diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-13 13:12:15 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-13 13:12:15 -0400 |
commit | c8c55bcb43d790d97790cfa319d80045a71fde39 (patch) | |
tree | b791d9478d3e7f1eb26e51bb999fde21847d4391 | |
parent | 3749c66c67fb5c257771815c186bc32290cacf44 (diff) | |
parent | ebf8889bd1fe3615991ff4494635d237280652a2 (diff) |
Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (91 commits)
[MTD] [NAND] Blackfin on-chip NAND Flash Controller driver
[MTD] [NOR] fix ctrl-alt-del can't reboot for intel flash bug
[MTD] [NAND] Fix compiler warning in Alauda driver
[JFFS2] Remove stray debugging printk
[JFFS2] Handle dirents on the flash with embedded zero bytes in names.
[JFFS2] Check for creation of dirents with embedded zero bytes in name.
[JFFS2] Don't count all 'very dirty' blocks except in debug mode
[JFFS2] Check whether garbage-collection actually obsoleted its victim.
[JFFS2] Relax threshold for triggering GC due to dirty blocks.
[MTD] [OneNAND] Fix typo related with recent commit
[JFFS2] Trigger garbage collection when very_dirty_list size becomes excessive
[MTD] [NAND] Avoid deadlock in erase callback; release chip lock first.
[MTD] [NAND] Resume method for CAFÉ NAND controller
[MTD] [NAND] Fix PCI ident table for CAFÉ NAND controller.
[MTD] [NAND] s3c2410: fix arch moves
[MTD] [OneNAND] fix numerous races
[MTD] map driver for NOR flash on the Intel Vermilion Range chipset
[JFFS2] Fix unpoint length
[MTD] fix CFI point method for discontiguous maps
[MTD] MAPS: Merge Lubbock and Mainstone drivers into common PXA2xx driver
...
91 files changed, 4671 insertions, 2226 deletions
diff --git a/Documentation/DocBook/mtdnand.tmpl b/Documentation/DocBook/mtdnand.tmpl index a8c8cce50633..6fbc41d98c1e 100644 --- a/Documentation/DocBook/mtdnand.tmpl +++ b/Documentation/DocBook/mtdnand.tmpl | |||
@@ -275,16 +275,13 @@ int __init board_init (void) | |||
275 | int err = 0; | 275 | int err = 0; |
276 | 276 | ||
277 | /* Allocate memory for MTD device structure and private data */ | 277 | /* Allocate memory for MTD device structure and private data */ |
278 | board_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); | 278 | board_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); |
279 | if (!board_mtd) { | 279 | if (!board_mtd) { |
280 | printk ("Unable to allocate NAND MTD device structure.\n"); | 280 | printk ("Unable to allocate NAND MTD device structure.\n"); |
281 | err = -ENOMEM; | 281 | err = -ENOMEM; |
282 | goto out; | 282 | goto out; |
283 | } | 283 | } |
284 | 284 | ||
285 | /* Initialize structures */ | ||
286 | memset ((char *) board_mtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip)); | ||
287 | |||
288 | /* map physical adress */ | 285 | /* map physical adress */ |
289 | baseaddr = (unsigned long)ioremap(CHIP_PHYSICAL_ADDRESS, 1024); | 286 | baseaddr = (unsigned long)ioremap(CHIP_PHYSICAL_ADDRESS, 1024); |
290 | if(!baseaddr){ | 287 | if(!baseaddr){ |
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index fbec8cd55e38..8848e8ac705d 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig | |||
@@ -278,6 +278,14 @@ config SSFDC | |||
278 | This enables read only access to SmartMedia formatted NAND | 278 | This enables read only access to SmartMedia formatted NAND |
279 | flash. You can mount it with FAT file system. | 279 | flash. You can mount it with FAT file system. |
280 | 280 | ||
281 | config MTD_OOPS | ||
282 | tristate "Log panic/oops to an MTD buffer" | ||
283 | depends on MTD | ||
284 | help | ||
285 | This enables panic and oops messages to be logged to a circular | ||
286 | buffer in a flash partition where it can be read back at some | ||
287 | later point. | ||
288 | |||
281 | source "drivers/mtd/chips/Kconfig" | 289 | source "drivers/mtd/chips/Kconfig" |
282 | 290 | ||
283 | source "drivers/mtd/maps/Kconfig" | 291 | source "drivers/mtd/maps/Kconfig" |
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 6d958a4566ff..7f0b04b4caa7 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile | |||
@@ -22,6 +22,7 @@ obj-$(CONFIG_NFTL) += nftl.o | |||
22 | obj-$(CONFIG_INFTL) += inftl.o | 22 | obj-$(CONFIG_INFTL) += inftl.o |
23 | obj-$(CONFIG_RFD_FTL) += rfd_ftl.o | 23 | obj-$(CONFIG_RFD_FTL) += rfd_ftl.o |
24 | obj-$(CONFIG_SSFDC) += ssfdc.o | 24 | obj-$(CONFIG_SSFDC) += ssfdc.o |
25 | obj-$(CONFIG_MTD_OOPS) += mtdoops.o | ||
25 | 26 | ||
26 | nftl-objs := nftlcore.o nftlmount.o | 27 | nftl-objs := nftlcore.o nftlmount.o |
27 | inftl-objs := inftlcore.o inftlmount.o | 28 | inftl-objs := inftlcore.o inftlmount.o |
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 2f19fa78d24a..3aa3dca56ae6 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c | |||
@@ -526,7 +526,7 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, | |||
526 | struct cfi_pri_intelext *extp = cfi->cmdset_priv; | 526 | struct cfi_pri_intelext *extp = cfi->cmdset_priv; |
527 | 527 | ||
528 | /* | 528 | /* |
529 | * Probing of multi-partition flash ships. | 529 | * Probing of multi-partition flash chips. |
530 | * | 530 | * |
531 | * To support multiple partitions when available, we simply arrange | 531 | * To support multiple partitions when available, we simply arrange |
532 | * for each of them to have their own flchip structure even if they | 532 | * for each of them to have their own flchip structure even if they |
@@ -653,7 +653,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr | |||
653 | resettime: | 653 | resettime: |
654 | timeo = jiffies + HZ; | 654 | timeo = jiffies + HZ; |
655 | retry: | 655 | retry: |
656 | if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) { | 656 | if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE || mode == FL_SHUTDOWN)) { |
657 | /* | 657 | /* |
658 | * OK. We have possibility for contension on the write/erase | 658 | * OK. We have possibility for contension on the write/erase |
659 | * operations which are global to the real chip and not per | 659 | * operations which are global to the real chip and not per |
@@ -798,6 +798,9 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr | |||
798 | if (mode == FL_READY && chip->oldstate == FL_READY) | 798 | if (mode == FL_READY && chip->oldstate == FL_READY) |
799 | return 0; | 799 | return 0; |
800 | 800 | ||
801 | case FL_SHUTDOWN: | ||
802 | /* The machine is rebooting now,so no one can get chip anymore */ | ||
803 | return -EIO; | ||
801 | default: | 804 | default: |
802 | sleep: | 805 | sleep: |
803 | set_current_state(TASK_UNINTERRUPTIBLE); | 806 | set_current_state(TASK_UNINTERRUPTIBLE); |
@@ -1166,28 +1169,34 @@ static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, si | |||
1166 | { | 1169 | { |
1167 | struct map_info *map = mtd->priv; | 1170 | struct map_info *map = mtd->priv; |
1168 | struct cfi_private *cfi = map->fldrv_priv; | 1171 | struct cfi_private *cfi = map->fldrv_priv; |
1169 | unsigned long ofs; | 1172 | unsigned long ofs, last_end = 0; |
1170 | int chipnum; | 1173 | int chipnum; |
1171 | int ret = 0; | 1174 | int ret = 0; |
1172 | 1175 | ||
1173 | if (!map->virt || (from + len > mtd->size)) | 1176 | if (!map->virt || (from + len > mtd->size)) |
1174 | return -EINVAL; | 1177 | return -EINVAL; |
1175 | 1178 | ||
1176 | *mtdbuf = (void *)map->virt + from; | ||
1177 | *retlen = 0; | ||
1178 | |||
1179 | /* Now lock the chip(s) to POINT state */ | 1179 | /* Now lock the chip(s) to POINT state */ |
1180 | 1180 | ||
1181 | /* ofs: offset within the first chip that the first read should start */ | 1181 | /* ofs: offset within the first chip that the first read should start */ |
1182 | chipnum = (from >> cfi->chipshift); | 1182 | chipnum = (from >> cfi->chipshift); |
1183 | ofs = from - (chipnum << cfi->chipshift); | 1183 | ofs = from - (chipnum << cfi->chipshift); |
1184 | 1184 | ||
1185 | *mtdbuf = (void *)map->virt + cfi->chips[chipnum].start + ofs; | ||
1186 | *retlen = 0; | ||
1187 | |||
1185 | while (len) { | 1188 | while (len) { |
1186 | unsigned long thislen; | 1189 | unsigned long thislen; |
1187 | 1190 | ||
1188 | if (chipnum >= cfi->numchips) | 1191 | if (chipnum >= cfi->numchips) |
1189 | break; | 1192 | break; |
1190 | 1193 | ||
1194 | /* We cannot point across chips that are virtually disjoint */ | ||
1195 | if (!last_end) | ||
1196 | last_end = cfi->chips[chipnum].start; | ||
1197 | else if (cfi->chips[chipnum].start != last_end) | ||
1198 | break; | ||
1199 | |||
1191 | if ((len + ofs -1) >> cfi->chipshift) | 1200 | if ((len + ofs -1) >> cfi->chipshift) |
1192 | thislen = (1<<cfi->chipshift) - ofs; | 1201 | thislen = (1<<cfi->chipshift) - ofs; |
1193 | else | 1202 | else |
@@ -1201,6 +1210,7 @@ static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, si | |||
1201 | len -= thislen; | 1210 | len -= thislen; |
1202 | 1211 | ||
1203 | ofs = 0; | 1212 | ofs = 0; |
1213 | last_end += 1 << cfi->chipshift; | ||
1204 | chipnum++; | 1214 | chipnum++; |
1205 | } | 1215 | } |
1206 | return 0; | 1216 | return 0; |
@@ -1780,7 +1790,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, | |||
1780 | return ret; | 1790 | return ret; |
1781 | } | 1791 | } |
1782 | 1792 | ||
1783 | int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) | 1793 | static int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) |
1784 | { | 1794 | { |
1785 | unsigned long ofs, len; | 1795 | unsigned long ofs, len; |
1786 | int ret; | 1796 | int ret; |
@@ -1930,7 +1940,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
1930 | printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", | 1940 | printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", |
1931 | __FUNCTION__, ofs, len); | 1941 | __FUNCTION__, ofs, len); |
1932 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, | 1942 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, |
1933 | ofs, len, 0); | 1943 | ofs, len, NULL); |
1934 | #endif | 1944 | #endif |
1935 | 1945 | ||
1936 | ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, | 1946 | ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, |
@@ -1940,7 +1950,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
1940 | printk(KERN_DEBUG "%s: lock status after, ret=%d\n", | 1950 | printk(KERN_DEBUG "%s: lock status after, ret=%d\n", |
1941 | __FUNCTION__, ret); | 1951 | __FUNCTION__, ret); |
1942 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, | 1952 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, |
1943 | ofs, len, 0); | 1953 | ofs, len, NULL); |
1944 | #endif | 1954 | #endif |
1945 | 1955 | ||
1946 | return ret; | 1956 | return ret; |
@@ -1954,7 +1964,7 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
1954 | printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", | 1964 | printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", |
1955 | __FUNCTION__, ofs, len); | 1965 | __FUNCTION__, ofs, len); |
1956 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, | 1966 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, |
1957 | ofs, len, 0); | 1967 | ofs, len, NULL); |
1958 | #endif | 1968 | #endif |
1959 | 1969 | ||
1960 | ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, | 1970 | ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, |
@@ -1964,7 +1974,7 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
1964 | printk(KERN_DEBUG "%s: lock status after, ret=%d\n", | 1974 | printk(KERN_DEBUG "%s: lock status after, ret=%d\n", |
1965 | __FUNCTION__, ret); | 1975 | __FUNCTION__, ret); |
1966 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, | 1976 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, |
1967 | ofs, len, 0); | 1977 | ofs, len, NULL); |
1968 | #endif | 1978 | #endif |
1969 | 1979 | ||
1970 | return ret; | 1980 | return ret; |
@@ -2255,7 +2265,7 @@ static void cfi_intelext_save_locks(struct mtd_info *mtd) | |||
2255 | adr = region->offset + block * len; | 2265 | adr = region->offset + block * len; |
2256 | 2266 | ||
2257 | status = cfi_varsize_frob(mtd, | 2267 | status = cfi_varsize_frob(mtd, |
2258 | do_getlockstatus_oneblock, adr, len, 0); | 2268 | do_getlockstatus_oneblock, adr, len, NULL); |
2259 | if (status) | 2269 | if (status) |
2260 | set_bit(block, region->lockmap); | 2270 | set_bit(block, region->lockmap); |
2261 | else | 2271 | else |
@@ -2402,10 +2412,10 @@ static int cfi_intelext_reset(struct mtd_info *mtd) | |||
2402 | and switch to array mode so any bootloader in | 2412 | and switch to array mode so any bootloader in |
2403 | flash is accessible for soft reboot. */ | 2413 | flash is accessible for soft reboot. */ |
2404 | spin_lock(chip->mutex); | 2414 | spin_lock(chip->mutex); |
2405 | ret = get_chip(map, chip, chip->start, FL_SYNCING); | 2415 | ret = get_chip(map, chip, chip->start, FL_SHUTDOWN); |
2406 | if (!ret) { | 2416 | if (!ret) { |
2407 | map_write(map, CMD(0xff), chip->start); | 2417 | map_write(map, CMD(0xff), chip->start); |
2408 | chip->state = FL_READY; | 2418 | chip->state = FL_SHUTDOWN; |
2409 | } | 2419 | } |
2410 | spin_unlock(chip->mutex); | 2420 | spin_unlock(chip->mutex); |
2411 | } | 2421 | } |
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 1f6445840461..389acc600f5e 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c | |||
@@ -1609,7 +1609,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, | |||
1609 | } | 1609 | } |
1610 | 1610 | ||
1611 | 1611 | ||
1612 | int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) | 1612 | static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) |
1613 | { | 1613 | { |
1614 | unsigned long ofs, len; | 1614 | unsigned long ofs, len; |
1615 | int ret; | 1615 | int ret; |
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 58e561e87699..a67b23b87fc0 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c | |||
@@ -17,7 +17,6 @@ | |||
17 | #include <linux/errno.h> | 17 | #include <linux/errno.h> |
18 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
19 | #include <linux/interrupt.h> | 19 | #include <linux/interrupt.h> |
20 | #include <linux/init.h> | ||
21 | 20 | ||
22 | #include <linux/mtd/mtd.h> | 21 | #include <linux/mtd/mtd.h> |
23 | #include <linux/mtd/map.h> | 22 | #include <linux/mtd/map.h> |
@@ -70,6 +69,7 @@ | |||
70 | 69 | ||
71 | /* Fujitsu */ | 70 | /* Fujitsu */ |
72 | #define MBM29F040C 0x00A4 | 71 | #define MBM29F040C 0x00A4 |
72 | #define MBM29F800BA 0x2258 | ||
73 | #define MBM29LV650UE 0x22D7 | 73 | #define MBM29LV650UE 0x22D7 |
74 | #define MBM29LV320TE 0x22F6 | 74 | #define MBM29LV320TE 0x22F6 |
75 | #define MBM29LV320BE 0x22F9 | 75 | #define MBM29LV320BE 0x22F9 |
@@ -129,6 +129,7 @@ | |||
129 | #define LH28F640BF 0x00b0 | 129 | #define LH28F640BF 0x00b0 |
130 | 130 | ||
131 | /* ST - www.st.com */ | 131 | /* ST - www.st.com */ |
132 | #define M29F800AB 0x0058 | ||
132 | #define M29W800DT 0x00D7 | 133 | #define M29W800DT 0x00D7 |
133 | #define M29W800DB 0x005B | 134 | #define M29W800DB 0x005B |
134 | #define M29W160DT 0x22C4 | 135 | #define M29W160DT 0x22C4 |
@@ -646,6 +647,23 @@ static const struct amd_flash_info jedec_table[] = { | |||
646 | } | 647 | } |
647 | }, { | 648 | }, { |
648 | .mfr_id = MANUFACTURER_FUJITSU, | 649 | .mfr_id = MANUFACTURER_FUJITSU, |
650 | .dev_id = MBM29F800BA, | ||
651 | .name = "Fujitsu MBM29F800BA", | ||
652 | .uaddr = { | ||
653 | [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ | ||
654 | [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ | ||
655 | }, | ||
656 | .DevSize = SIZE_1MiB, | ||
657 | .CmdSet = P_ID_AMD_STD, | ||
658 | .NumEraseRegions= 4, | ||
659 | .regions = { | ||
660 | ERASEINFO(0x04000,1), | ||
661 | ERASEINFO(0x02000,2), | ||
662 | ERASEINFO(0x08000,1), | ||
663 | ERASEINFO(0x10000,15), | ||
664 | } | ||
665 | }, { | ||
666 | .mfr_id = MANUFACTURER_FUJITSU, | ||
649 | .dev_id = MBM29LV650UE, | 667 | .dev_id = MBM29LV650UE, |
650 | .name = "Fujitsu MBM29LV650UE", | 668 | .name = "Fujitsu MBM29LV650UE", |
651 | .uaddr = { | 669 | .uaddr = { |
@@ -1510,6 +1528,23 @@ static const struct amd_flash_info jedec_table[] = { | |||
1510 | ERASEINFO(0x1000,256) | 1528 | ERASEINFO(0x1000,256) |
1511 | } | 1529 | } |
1512 | 1530 | ||
1531 | }, { | ||
1532 | .mfr_id = MANUFACTURER_ST, | ||
1533 | .dev_id = M29F800AB, | ||
1534 | .name = "ST M29F800AB", | ||
1535 | .uaddr = { | ||
1536 | [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ | ||
1537 | [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ | ||
1538 | }, | ||
1539 | .DevSize = SIZE_1MiB, | ||
1540 | .CmdSet = P_ID_AMD_STD, | ||
1541 | .NumEraseRegions= 4, | ||
1542 | .regions = { | ||
1543 | ERASEINFO(0x04000,1), | ||
1544 | ERASEINFO(0x02000,2), | ||
1545 | ERASEINFO(0x08000,1), | ||
1546 | ERASEINFO(0x10000,15), | ||
1547 | } | ||
1513 | }, { | 1548 | }, { |
1514 | .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ | 1549 | .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ |
1515 | .dev_id = M29W800DT, | 1550 | .dev_id = M29W800DT, |
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index ff642f8fbee7..811d56fd890f 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig | |||
@@ -60,21 +60,22 @@ config MTD_DATAFLASH | |||
60 | Sometimes DataFlash chips are packaged inside MMC-format | 60 | Sometimes DataFlash chips are packaged inside MMC-format |
61 | cards; at this writing, the MMC stack won't handle those. | 61 | cards; at this writing, the MMC stack won't handle those. |
62 | 62 | ||
63 | config MTD_DATAFLASH26 | ||
64 | tristate "AT91RM9200 DataFlash AT26xxx" | ||
65 | depends on MTD && ARCH_AT91RM9200 && AT91_SPI | ||
66 | help | ||
67 | This enables access to the DataFlash chip (AT26xxx) on an | ||
68 | AT91RM9200-based board. | ||
69 | If you have such a board and such a DataFlash, say 'Y'. | ||
70 | |||
71 | config MTD_M25P80 | 63 | config MTD_M25P80 |
72 | tristate "Support for M25 SPI Flash" | 64 | tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" |
73 | depends on SPI_MASTER && EXPERIMENTAL | 65 | depends on SPI_MASTER && EXPERIMENTAL |
74 | help | 66 | help |
75 | This enables access to ST M25P80 and similar SPI flash chips, | 67 | This enables access to most modern SPI flash chips, used for |
76 | used for program and data storage. Set up your spi devices | 68 | program and data storage. Series supported include Atmel AT26DF, |
77 | with the right board-specific platform data. | 69 | Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X. Other chips |
70 | are supported as well. See the driver source for the current list, | ||
71 | or to add other chips. | ||
72 | |||
73 | Note that the original DataFlash chips (AT45 series, not AT26DF), | ||
74 | need an entirely different driver. | ||
75 | |||
76 | Set up your spi devices with the right board-specific platform data, | ||
77 | if you want to specify device partitioning or to use a device which | ||
78 | doesn't support the JEDEC ID instruction. | ||
78 | 79 | ||
79 | config MTD_SLRAM | 80 | config MTD_SLRAM |
80 | tristate "Uncached system RAM" | 81 | tristate "Uncached system RAM" |
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 8ab568b3f533..0f788d5c4bf8 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile | |||
@@ -16,5 +16,4 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o | |||
16 | obj-$(CONFIG_MTD_LART) += lart.o | 16 | obj-$(CONFIG_MTD_LART) += lart.o |
17 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o | 17 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o |
18 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o | 18 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o |
19 | obj-$(CONFIG_MTD_DATAFLASH26) += at91_dataflash26.o | ||
20 | obj-$(CONFIG_MTD_M25P80) += m25p80.o | 19 | obj-$(CONFIG_MTD_M25P80) += m25p80.o |
diff --git a/drivers/mtd/devices/at91_dataflash26.c b/drivers/mtd/devices/at91_dataflash26.c deleted file mode 100644 index 64ce37f986fc..000000000000 --- a/drivers/mtd/devices/at91_dataflash26.c +++ /dev/null | |||
@@ -1,485 +0,0 @@ | |||
1 | /* | ||
2 | * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder) | ||
3 | * This is a largely modified version of at91_dataflash.c that | ||
4 | * supports AT26xxx dataflash chips. The original driver supports | ||
5 | * AT45xxx chips. | ||
6 | * | ||
7 | * Note: This driver was only tested with an AT26F004. It should be | ||
8 | * easy to make it work with other AT26xxx dataflash devices, though. | ||
9 | * | ||
10 | * Copyright (C) 2007 Hans J. Koch <hjk@linutronix.de> | ||
11 | * original Copyright (C) SAN People (Pty) Ltd | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or | ||
14 | * modify it under the terms of the GNU General Public License | ||
15 | * version 2 as published by the Free Software Foundation. | ||
16 | */ | ||
17 | |||
18 | #include <linux/config.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/mtd/mtd.h> | ||
22 | |||
23 | #include <asm/arch/at91_spi.h> | ||
24 | |||
25 | #define DATAFLASH_MAX_DEVICES 4 /* max number of dataflash devices */ | ||
26 | |||
27 | #define MANUFACTURER_ID_ATMEL 0x1F | ||
28 | |||
29 | /* command codes */ | ||
30 | |||
31 | #define AT26_OP_READ_STATUS 0x05 | ||
32 | #define AT26_OP_READ_DEV_ID 0x9F | ||
33 | #define AT26_OP_ERASE_PAGE_4K 0x20 | ||
34 | #define AT26_OP_READ_ARRAY_FAST 0x0B | ||
35 | #define AT26_OP_SEQUENTIAL_WRITE 0xAF | ||
36 | #define AT26_OP_WRITE_ENABLE 0x06 | ||
37 | #define AT26_OP_WRITE_DISABLE 0x04 | ||
38 | #define AT26_OP_SECTOR_PROTECT 0x36 | ||
39 | #define AT26_OP_SECTOR_UNPROTECT 0x39 | ||
40 | |||
41 | /* status register bits */ | ||
42 | |||
43 | #define AT26_STATUS_BUSY 0x01 | ||
44 | #define AT26_STATUS_WRITE_ENABLE 0x02 | ||
45 | |||
46 | struct dataflash_local | ||
47 | { | ||
48 | int spi; /* SPI chip-select number */ | ||
49 | unsigned int page_size; /* number of bytes per page */ | ||
50 | }; | ||
51 | |||
52 | |||
53 | /* Detected DataFlash devices */ | ||
54 | static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES]; | ||
55 | static int nr_devices = 0; | ||
56 | |||
57 | /* Allocate a single SPI transfer descriptor. We're assuming that if multiple | ||
58 | SPI transfers occur at the same time, spi_access_bus() will serialize them. | ||
59 | If this is not valid, then either (i) each dataflash 'priv' structure | ||
60 | needs it's own transfer descriptor, (ii) we lock this one, or (iii) use | ||
61 | another mechanism. */ | ||
62 | static struct spi_transfer_list* spi_transfer_desc; | ||
63 | |||
64 | /* | ||
65 | * Perform a SPI transfer to access the DataFlash device. | ||
66 | */ | ||
67 | static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len, | ||
68 | char* txnext, int txnext_len, char* rxnext, int rxnext_len) | ||
69 | { | ||
70 | struct spi_transfer_list* list = spi_transfer_desc; | ||
71 | |||
72 | list->tx[0] = tx; list->txlen[0] = tx_len; | ||
73 | list->rx[0] = rx; list->rxlen[0] = rx_len; | ||
74 | |||
75 | list->tx[1] = txnext; list->txlen[1] = txnext_len; | ||
76 | list->rx[1] = rxnext; list->rxlen[1] = rxnext_len; | ||
77 | |||
78 | list->nr_transfers = nr; | ||
79 | /* Note: spi_transfer() always returns 0, there are no error checks */ | ||
80 | return spi_transfer(list); | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * Return the status of the DataFlash device. | ||
85 | */ | ||
86 | static unsigned char at91_dataflash26_status(void) | ||
87 | { | ||
88 | unsigned char command[2]; | ||
89 | |||
90 | command[0] = AT26_OP_READ_STATUS; | ||
91 | command[1] = 0; | ||
92 | |||
93 | do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0); | ||
94 | |||
95 | return command[1]; | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | * Poll the DataFlash device until it is READY. | ||
100 | */ | ||
101 | static unsigned char at91_dataflash26_waitready(void) | ||
102 | { | ||
103 | unsigned char status; | ||
104 | |||
105 | while (1) { | ||
106 | status = at91_dataflash26_status(); | ||
107 | if (!(status & AT26_STATUS_BUSY)) | ||
108 | return status; | ||
109 | } | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * Enable/disable write access | ||
114 | */ | ||
115 | static void at91_dataflash26_write_enable(int enable) | ||
116 | { | ||
117 | unsigned char cmd[2]; | ||
118 | |||
119 | DEBUG(MTD_DEBUG_LEVEL3, "write_enable: enable=%i\n", enable); | ||
120 | |||
121 | if (enable) | ||
122 | cmd[0] = AT26_OP_WRITE_ENABLE; | ||
123 | else | ||
124 | cmd[0] = AT26_OP_WRITE_DISABLE; | ||
125 | cmd[1] = 0; | ||
126 | |||
127 | do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0); | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * Protect/unprotect sector | ||
132 | */ | ||
133 | static void at91_dataflash26_sector_protect(loff_t addr, int protect) | ||
134 | { | ||
135 | unsigned char cmd[4]; | ||
136 | |||
137 | DEBUG(MTD_DEBUG_LEVEL3, "sector_protect: addr=0x%06x prot=%d\n", | ||
138 | addr, protect); | ||
139 | |||
140 | if (protect) | ||
141 | cmd[0] = AT26_OP_SECTOR_PROTECT; | ||
142 | else | ||
143 | cmd[0] = AT26_OP_SECTOR_UNPROTECT; | ||
144 | cmd[1] = (addr & 0x00FF0000) >> 16; | ||
145 | cmd[2] = (addr & 0x0000FF00) >> 8; | ||
146 | cmd[3] = (addr & 0x000000FF); | ||
147 | |||
148 | do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0); | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * Erase blocks of flash. | ||
153 | */ | ||
154 | static int at91_dataflash26_erase(struct mtd_info *mtd, | ||
155 | struct erase_info *instr) | ||
156 | { | ||
157 | struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; | ||
158 | unsigned char cmd[4]; | ||
159 | |||
160 | DEBUG(MTD_DEBUG_LEVEL1, "dataflash_erase: addr=0x%06x len=%i\n", | ||
161 | instr->addr, instr->len); | ||
162 | |||
163 | /* Sanity checks */ | ||
164 | if (priv->page_size != 4096) | ||
165 | return -EINVAL; /* Can't handle other sizes at the moment */ | ||
166 | |||
167 | if ( ((instr->len % mtd->erasesize) != 0) | ||
168 | || ((instr->len % priv->page_size) != 0) | ||
169 | || ((instr->addr % priv->page_size) != 0) | ||
170 | || ((instr->addr + instr->len) > mtd->size)) | ||
171 | return -EINVAL; | ||
172 | |||
173 | spi_access_bus(priv->spi); | ||
174 | |||
175 | while (instr->len > 0) { | ||
176 | at91_dataflash26_write_enable(1); | ||
177 | at91_dataflash26_sector_protect(instr->addr, 0); | ||
178 | at91_dataflash26_write_enable(1); | ||
179 | cmd[0] = AT26_OP_ERASE_PAGE_4K; | ||
180 | cmd[1] = (instr->addr & 0x00FF0000) >> 16; | ||
181 | cmd[2] = (instr->addr & 0x0000FF00) >> 8; | ||
182 | cmd[3] = (instr->addr & 0x000000FF); | ||
183 | |||
184 | DEBUG(MTD_DEBUG_LEVEL3, "ERASE: (0x%02x) 0x%02x 0x%02x" | ||
185 | "0x%02x\n", | ||
186 | cmd[0], cmd[1], cmd[2], cmd[3]); | ||
187 | |||
188 | do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0); | ||
189 | at91_dataflash26_waitready(); | ||
190 | |||
191 | instr->addr += priv->page_size; /* next page */ | ||
192 | instr->len -= priv->page_size; | ||
193 | } | ||
194 | |||
195 | at91_dataflash26_write_enable(0); | ||
196 | spi_release_bus(priv->spi); | ||
197 | |||
198 | /* Inform MTD subsystem that erase is complete */ | ||
199 | instr->state = MTD_ERASE_DONE; | ||
200 | if (instr->callback) | ||
201 | instr->callback(instr); | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * Read from the DataFlash device. | ||
208 | * from : Start offset in flash device | ||
209 | * len : Number of bytes to read | ||
210 | * retlen : Number of bytes actually read | ||
211 | * buf : Buffer that will receive data | ||
212 | */ | ||
213 | static int at91_dataflash26_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
214 | size_t *retlen, u_char *buf) | ||
215 | { | ||
216 | struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; | ||
217 | unsigned char cmd[5]; | ||
218 | |||
219 | DEBUG(MTD_DEBUG_LEVEL1, "dataflash_read: %lli .. %lli\n", | ||
220 | from, from+len); | ||
221 | |||
222 | *retlen = 0; | ||
223 | |||
224 | /* Sanity checks */ | ||
225 | if (!len) | ||
226 | return 0; | ||
227 | if (from + len > mtd->size) | ||
228 | return -EINVAL; | ||
229 | |||
230 | cmd[0] = AT26_OP_READ_ARRAY_FAST; | ||
231 | cmd[1] = (from & 0x00FF0000) >> 16; | ||
232 | cmd[2] = (from & 0x0000FF00) >> 8; | ||
233 | cmd[3] = (from & 0x000000FF); | ||
234 | /* cmd[4] is a "Don't care" byte */ | ||
235 | |||
236 | DEBUG(MTD_DEBUG_LEVEL3, "READ: (0x%02x) 0x%02x 0x%02x 0x%02x\n", | ||
237 | cmd[0], cmd[1], cmd[2], cmd[3]); | ||
238 | |||
239 | spi_access_bus(priv->spi); | ||
240 | do_spi_transfer(2, cmd, 5, cmd, 5, buf, len, buf, len); | ||
241 | spi_release_bus(priv->spi); | ||
242 | |||
243 | *retlen = len; | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * Write to the DataFlash device. | ||
249 | * to : Start offset in flash device | ||
250 | * len : Number of bytes to write | ||
251 | * retlen : Number of bytes actually written | ||
252 | * buf : Buffer containing the data | ||
253 | */ | ||
254 | static int at91_dataflash26_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
255 | size_t *retlen, const u_char *buf) | ||
256 | { | ||
257 | struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; | ||
258 | unsigned int addr, buf_index = 0; | ||
259 | int ret = -EIO, sector, last_sector; | ||
260 | unsigned char status, cmd[5]; | ||
261 | |||
262 | DEBUG(MTD_DEBUG_LEVEL1, "dataflash_write: %lli .. %lli\n", to, to+len); | ||
263 | |||
264 | *retlen = 0; | ||
265 | |||
266 | /* Sanity checks */ | ||
267 | if (!len) | ||
268 | return 0; | ||
269 | if (to + len > mtd->size) | ||
270 | return -EINVAL; | ||
271 | |||
272 | spi_access_bus(priv->spi); | ||
273 | |||
274 | addr = to; | ||
275 | last_sector = -1; | ||
276 | |||
277 | while (buf_index < len) { | ||
278 | sector = addr / priv->page_size; | ||
279 | /* Write first byte if a new sector begins */ | ||
280 | if (sector != last_sector) { | ||
281 | at91_dataflash26_write_enable(1); | ||
282 | at91_dataflash26_sector_protect(addr, 0); | ||
283 | at91_dataflash26_write_enable(1); | ||
284 | |||
285 | /* Program first byte of a new sector */ | ||
286 | cmd[0] = AT26_OP_SEQUENTIAL_WRITE; | ||
287 | cmd[1] = (addr & 0x00FF0000) >> 16; | ||
288 | cmd[2] = (addr & 0x0000FF00) >> 8; | ||
289 | cmd[3] = (addr & 0x000000FF); | ||
290 | cmd[4] = buf[buf_index++]; | ||
291 | do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0); | ||
292 | status = at91_dataflash26_waitready(); | ||
293 | addr++; | ||
294 | /* On write errors, the chip resets the write enable | ||
295 | flag. This also happens after the last byte of a | ||
296 | sector is successfully programmed. */ | ||
297 | if ( ( !(status & AT26_STATUS_WRITE_ENABLE)) | ||
298 | && ((addr % priv->page_size) != 0) ) { | ||
299 | DEBUG(MTD_DEBUG_LEVEL1, | ||
300 | "write error1: addr=0x%06x, " | ||
301 | "status=0x%02x\n", addr, status); | ||
302 | goto write_err; | ||
303 | } | ||
304 | (*retlen)++; | ||
305 | last_sector = sector; | ||
306 | } | ||
307 | |||
308 | /* Write subsequent bytes in the same sector */ | ||
309 | cmd[0] = AT26_OP_SEQUENTIAL_WRITE; | ||
310 | cmd[1] = buf[buf_index++]; | ||
311 | do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0); | ||
312 | status = at91_dataflash26_waitready(); | ||
313 | addr++; | ||
314 | |||
315 | if ( ( !(status & AT26_STATUS_WRITE_ENABLE)) | ||
316 | && ((addr % priv->page_size) != 0) ) { | ||
317 | DEBUG(MTD_DEBUG_LEVEL1, "write error2: addr=0x%06x, " | ||
318 | "status=0x%02x\n", addr, status); | ||
319 | goto write_err; | ||
320 | } | ||
321 | |||
322 | (*retlen)++; | ||
323 | } | ||
324 | |||
325 | ret = 0; | ||
326 | at91_dataflash26_write_enable(0); | ||
327 | write_err: | ||
328 | spi_release_bus(priv->spi); | ||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | /* | ||
333 | * Initialize and register DataFlash device with MTD subsystem. | ||
334 | */ | ||
335 | static int __init add_dataflash(int channel, char *name, int nr_pages, | ||
336 | int pagesize) | ||
337 | { | ||
338 | struct mtd_info *device; | ||
339 | struct dataflash_local *priv; | ||
340 | |||
341 | if (nr_devices >= DATAFLASH_MAX_DEVICES) { | ||
342 | printk(KERN_ERR "at91_dataflash26: Too many devices " | ||
343 | "detected\n"); | ||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | device = kzalloc(sizeof(struct mtd_info) + strlen(name) + 8, | ||
348 | GFP_KERNEL); | ||
349 | if (!device) | ||
350 | return -ENOMEM; | ||
351 | |||
352 | device->name = (char *)&device[1]; | ||
353 | sprintf(device->name, "%s.spi%d", name, channel); | ||
354 | device->size = nr_pages * pagesize; | ||
355 | device->erasesize = pagesize; | ||
356 | device->owner = THIS_MODULE; | ||
357 | device->type = MTD_DATAFLASH; | ||
358 | device->flags = MTD_CAP_NORFLASH; | ||
359 | device->erase = at91_dataflash26_erase; | ||
360 | device->read = at91_dataflash26_read; | ||
361 | device->write = at91_dataflash26_write; | ||
362 | |||
363 | priv = (struct dataflash_local *)kzalloc(sizeof(struct dataflash_local), | ||
364 | GFP_KERNEL); | ||
365 | if (!priv) { | ||
366 | kfree(device); | ||
367 | return -ENOMEM; | ||
368 | } | ||
369 | |||
370 | priv->spi = channel; | ||
371 | priv->page_size = pagesize; | ||
372 | device->priv = priv; | ||
373 | |||
374 | mtd_devices[nr_devices] = device; | ||
375 | nr_devices++; | ||
376 | printk(KERN_INFO "at91_dataflash26: %s detected [spi%i] (%i bytes)\n", | ||
377 | name, channel, device->size); | ||
378 | |||
379 | return add_mtd_device(device); | ||
380 | } | ||
381 | |||
382 | /* | ||
383 | * Detect and initialize DataFlash device connected to specified SPI channel. | ||
384 | * | ||
385 | */ | ||
386 | |||
387 | struct dataflash26_types { | ||
388 | unsigned char id0; | ||
389 | unsigned char id1; | ||
390 | char *name; | ||
391 | int pagesize; | ||
392 | int nr_pages; | ||
393 | }; | ||
394 | |||
395 | struct dataflash26_types df26_types[] = { | ||
396 | { | ||
397 | .id0 = 0x04, | ||
398 | .id1 = 0x00, | ||
399 | .name = "AT26F004", | ||
400 | .pagesize = 4096, | ||
401 | .nr_pages = 128, | ||
402 | }, | ||
403 | { | ||
404 | .id0 = 0x45, | ||
405 | .id1 = 0x01, | ||
406 | .name = "AT26DF081A", /* Not tested ! */ | ||
407 | .pagesize = 4096, | ||
408 | .nr_pages = 256, | ||
409 | }, | ||
410 | }; | ||
411 | |||
412 | static int __init at91_dataflash26_detect(int channel) | ||
413 | { | ||
414 | unsigned char status, cmd[5]; | ||
415 | int i; | ||
416 | |||
417 | spi_access_bus(channel); | ||
418 | status = at91_dataflash26_status(); | ||
419 | |||
420 | if (status == 0 || status == 0xff) { | ||
421 | printk(KERN_ERR "at91_dataflash26_detect: status error %d\n", | ||
422 | status); | ||
423 | spi_release_bus(channel); | ||
424 | return -ENODEV; | ||
425 | } | ||
426 | |||
427 | cmd[0] = AT26_OP_READ_DEV_ID; | ||
428 | do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0); | ||
429 | spi_release_bus(channel); | ||
430 | |||
431 | if (cmd[1] != MANUFACTURER_ID_ATMEL) | ||
432 | return -ENODEV; | ||
433 | |||
434 | for (i = 0; i < ARRAY_SIZE(df26_types); i++) { | ||
435 | if ( cmd[2] == df26_types[i].id0 | ||
436 | && cmd[3] == df26_types[i].id1) | ||
437 | return add_dataflash(channel, | ||
438 | df26_types[i].name, | ||
439 | df26_types[i].nr_pages, | ||
440 | df26_types[i].pagesize); | ||
441 | } | ||
442 | |||
443 | printk(KERN_ERR "at91_dataflash26_detect: Unsupported device " | ||
444 | "(0x%02x/0x%02x)\n", cmd[2], cmd[3]); | ||
445 | return -ENODEV; | ||
446 | } | ||
447 | |||
448 | static int __init at91_dataflash26_init(void) | ||
449 | { | ||
450 | spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list), | ||
451 | GFP_KERNEL); | ||
452 | if (!spi_transfer_desc) | ||
453 | return -ENOMEM; | ||
454 | |||
455 | /* DataFlash (SPI chip select 0) */ | ||
456 | at91_dataflash26_detect(0); | ||
457 | |||
458 | #ifdef CONFIG_MTD_AT91_DATAFLASH_CARD | ||
459 | /* DataFlash card (SPI chip select 3) */ | ||
460 | at91_dataflash26_detect(3); | ||
461 | #endif | ||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | static void __exit at91_dataflash26_exit(void) | ||
466 | { | ||
467 | int i; | ||
468 | |||
469 | for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) { | ||
470 | if (mtd_devices[i]) { | ||
471 | del_mtd_device(mtd_devices[i]); | ||
472 | kfree(mtd_devices[i]->priv); | ||
473 | kfree(mtd_devices[i]); | ||
474 | } | ||
475 | } | ||
476 | nr_devices = 0; | ||
477 | kfree(spi_transfer_desc); | ||
478 | } | ||
479 | |||
480 | module_init(at91_dataflash26_init); | ||
481 | module_exit(at91_dataflash26_exit); | ||
482 | |||
483 | MODULE_LICENSE("GPL"); | ||
484 | MODULE_AUTHOR("Hans J. Koch"); | ||
485 | MODULE_DESCRIPTION("DataFlash AT26xxx driver for Atmel AT91RM9200"); | ||
diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c index 54aa75907640..d8cc94ec4e50 100644 --- a/drivers/mtd/devices/docprobe.c +++ b/drivers/mtd/devices/docprobe.c | |||
@@ -81,9 +81,7 @@ static unsigned long __initdata doc_locations[] = { | |||
81 | #endif /* CONFIG_MTD_DOCPROBE_HIGH */ | 81 | #endif /* CONFIG_MTD_DOCPROBE_HIGH */ |
82 | #elif defined(__PPC__) | 82 | #elif defined(__PPC__) |
83 | 0xe4000000, | 83 | 0xe4000000, |
84 | #elif defined(CONFIG_MOMENCO_OCELOT_G) | 84 | #else |
85 | 0xff000000, | ||
86 | ##else | ||
87 | #warning Unknown architecture for DiskOnChip. No default probe locations defined | 85 | #warning Unknown architecture for DiskOnChip. No default probe locations defined |
88 | #endif | 86 | #endif |
89 | 0xffffffff }; | 87 | 0xffffffff }; |
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 78c2511ae9e0..98df5bcc02f3 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * MTD SPI driver for ST M25Pxx flash chips | 2 | * MTD SPI driver for ST M25Pxx (and similar) serial flash chips |
3 | * | 3 | * |
4 | * Author: Mike Lavender, mike@steroidmicros.com | 4 | * Author: Mike Lavender, mike@steroidmicros.com |
5 | * | 5 | * |
@@ -19,33 +19,32 @@ | |||
19 | #include <linux/module.h> | 19 | #include <linux/module.h> |
20 | #include <linux/device.h> | 20 | #include <linux/device.h> |
21 | #include <linux/interrupt.h> | 21 | #include <linux/interrupt.h> |
22 | #include <linux/interrupt.h> | 22 | #include <linux/mutex.h> |
23 | |||
23 | #include <linux/mtd/mtd.h> | 24 | #include <linux/mtd/mtd.h> |
24 | #include <linux/mtd/partitions.h> | 25 | #include <linux/mtd/partitions.h> |
26 | |||
25 | #include <linux/spi/spi.h> | 27 | #include <linux/spi/spi.h> |
26 | #include <linux/spi/flash.h> | 28 | #include <linux/spi/flash.h> |
27 | 29 | ||
28 | #include <asm/semaphore.h> | ||
29 | |||
30 | |||
31 | /* NOTE: AT 25F and SST 25LF series are very similar, | ||
32 | * but commands for sector erase and chip id differ... | ||
33 | */ | ||
34 | 30 | ||
35 | #define FLASH_PAGESIZE 256 | 31 | #define FLASH_PAGESIZE 256 |
36 | 32 | ||
37 | /* Flash opcodes. */ | 33 | /* Flash opcodes. */ |
38 | #define OPCODE_WREN 6 /* Write enable */ | 34 | #define OPCODE_WREN 0x06 /* Write enable */ |
39 | #define OPCODE_RDSR 5 /* Read status register */ | 35 | #define OPCODE_RDSR 0x05 /* Read status register */ |
40 | #define OPCODE_READ 3 /* Read data bytes */ | 36 | #define OPCODE_READ 0x03 /* Read data bytes (low frequency) */ |
41 | #define OPCODE_PP 2 /* Page program */ | 37 | #define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ |
42 | #define OPCODE_SE 0xd8 /* Sector erase */ | 38 | #define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ |
43 | #define OPCODE_RES 0xab /* Read Electronic Signature */ | 39 | #define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ |
40 | #define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ | ||
41 | #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ | ||
44 | #define OPCODE_RDID 0x9f /* Read JEDEC ID */ | 42 | #define OPCODE_RDID 0x9f /* Read JEDEC ID */ |
45 | 43 | ||
46 | /* Status Register bits. */ | 44 | /* Status Register bits. */ |
47 | #define SR_WIP 1 /* Write in progress */ | 45 | #define SR_WIP 1 /* Write in progress */ |
48 | #define SR_WEL 2 /* Write enable latch */ | 46 | #define SR_WEL 2 /* Write enable latch */ |
47 | /* meaning of other SR_* bits may differ between vendors */ | ||
49 | #define SR_BP0 4 /* Block protect 0 */ | 48 | #define SR_BP0 4 /* Block protect 0 */ |
50 | #define SR_BP1 8 /* Block protect 1 */ | 49 | #define SR_BP1 8 /* Block protect 1 */ |
51 | #define SR_BP2 0x10 /* Block protect 2 */ | 50 | #define SR_BP2 0x10 /* Block protect 2 */ |
@@ -65,9 +64,10 @@ | |||
65 | 64 | ||
66 | struct m25p { | 65 | struct m25p { |
67 | struct spi_device *spi; | 66 | struct spi_device *spi; |
68 | struct semaphore lock; | 67 | struct mutex lock; |
69 | struct mtd_info mtd; | 68 | struct mtd_info mtd; |
70 | unsigned partitioned; | 69 | unsigned partitioned:1; |
70 | u8 erase_opcode; | ||
71 | u8 command[4]; | 71 | u8 command[4]; |
72 | }; | 72 | }; |
73 | 73 | ||
@@ -150,8 +150,9 @@ static int wait_till_ready(struct m25p *flash) | |||
150 | */ | 150 | */ |
151 | static int erase_sector(struct m25p *flash, u32 offset) | 151 | static int erase_sector(struct m25p *flash, u32 offset) |
152 | { | 152 | { |
153 | DEBUG(MTD_DEBUG_LEVEL3, "%s: %s at 0x%08x\n", flash->spi->dev.bus_id, | 153 | DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n", |
154 | __FUNCTION__, offset); | 154 | flash->spi->dev.bus_id, __FUNCTION__, |
155 | flash->mtd.erasesize / 1024, offset); | ||
155 | 156 | ||
156 | /* Wait until finished previous write command. */ | 157 | /* Wait until finished previous write command. */ |
157 | if (wait_till_ready(flash)) | 158 | if (wait_till_ready(flash)) |
@@ -161,7 +162,7 @@ static int erase_sector(struct m25p *flash, u32 offset) | |||
161 | write_enable(flash); | 162 | write_enable(flash); |
162 | 163 | ||
163 | /* Set up command buffer. */ | 164 | /* Set up command buffer. */ |
164 | flash->command[0] = OPCODE_SE; | 165 | flash->command[0] = flash->erase_opcode; |
165 | flash->command[1] = offset >> 16; | 166 | flash->command[1] = offset >> 16; |
166 | flash->command[2] = offset >> 8; | 167 | flash->command[2] = offset >> 8; |
167 | flash->command[3] = offset; | 168 | flash->command[3] = offset; |
@@ -201,13 +202,17 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
201 | addr = instr->addr; | 202 | addr = instr->addr; |
202 | len = instr->len; | 203 | len = instr->len; |
203 | 204 | ||
204 | down(&flash->lock); | 205 | mutex_lock(&flash->lock); |
206 | |||
207 | /* REVISIT in some cases we could speed up erasing large regions | ||
208 | * by using OPCODE_SE instead of OPCODE_BE_4K | ||
209 | */ | ||
205 | 210 | ||
206 | /* now erase those sectors */ | 211 | /* now erase those sectors */ |
207 | while (len) { | 212 | while (len) { |
208 | if (erase_sector(flash, addr)) { | 213 | if (erase_sector(flash, addr)) { |
209 | instr->state = MTD_ERASE_FAILED; | 214 | instr->state = MTD_ERASE_FAILED; |
210 | up(&flash->lock); | 215 | mutex_unlock(&flash->lock); |
211 | return -EIO; | 216 | return -EIO; |
212 | } | 217 | } |
213 | 218 | ||
@@ -215,7 +220,7 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
215 | len -= mtd->erasesize; | 220 | len -= mtd->erasesize; |
216 | } | 221 | } |
217 | 222 | ||
218 | up(&flash->lock); | 223 | mutex_unlock(&flash->lock); |
219 | 224 | ||
220 | instr->state = MTD_ERASE_DONE; | 225 | instr->state = MTD_ERASE_DONE; |
221 | mtd_erase_callback(instr); | 226 | mtd_erase_callback(instr); |
@@ -260,16 +265,19 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
260 | if (retlen) | 265 | if (retlen) |
261 | *retlen = 0; | 266 | *retlen = 0; |
262 | 267 | ||
263 | down(&flash->lock); | 268 | mutex_lock(&flash->lock); |
264 | 269 | ||
265 | /* Wait till previous write/erase is done. */ | 270 | /* Wait till previous write/erase is done. */ |
266 | if (wait_till_ready(flash)) { | 271 | if (wait_till_ready(flash)) { |
267 | /* REVISIT status return?? */ | 272 | /* REVISIT status return?? */ |
268 | up(&flash->lock); | 273 | mutex_unlock(&flash->lock); |
269 | return 1; | 274 | return 1; |
270 | } | 275 | } |
271 | 276 | ||
272 | /* NOTE: OPCODE_FAST_READ (if available) is faster... */ | 277 | /* FIXME switch to OPCODE_FAST_READ. It's required for higher |
278 | * clocks; and at this writing, every chip this driver handles | ||
279 | * supports that opcode. | ||
280 | */ | ||
273 | 281 | ||
274 | /* Set up the write data buffer. */ | 282 | /* Set up the write data buffer. */ |
275 | flash->command[0] = OPCODE_READ; | 283 | flash->command[0] = OPCODE_READ; |
@@ -281,7 +289,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
281 | 289 | ||
282 | *retlen = m.actual_length - sizeof(flash->command); | 290 | *retlen = m.actual_length - sizeof(flash->command); |
283 | 291 | ||
284 | up(&flash->lock); | 292 | mutex_unlock(&flash->lock); |
285 | 293 | ||
286 | return 0; | 294 | return 0; |
287 | } | 295 | } |
@@ -323,7 +331,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
323 | t[1].tx_buf = buf; | 331 | t[1].tx_buf = buf; |
324 | spi_message_add_tail(&t[1], &m); | 332 | spi_message_add_tail(&t[1], &m); |
325 | 333 | ||
326 | down(&flash->lock); | 334 | mutex_lock(&flash->lock); |
327 | 335 | ||
328 | /* Wait until finished previous write command. */ | 336 | /* Wait until finished previous write command. */ |
329 | if (wait_till_ready(flash)) | 337 | if (wait_till_ready(flash)) |
@@ -381,10 +389,10 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
381 | if (retlen) | 389 | if (retlen) |
382 | *retlen += m.actual_length | 390 | *retlen += m.actual_length |
383 | - sizeof(flash->command); | 391 | - sizeof(flash->command); |
384 | } | 392 | } |
385 | } | 393 | } |
386 | 394 | ||
387 | up(&flash->lock); | 395 | mutex_unlock(&flash->lock); |
388 | 396 | ||
389 | return 0; | 397 | return 0; |
390 | } | 398 | } |
@@ -398,24 +406,118 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
398 | 406 | ||
399 | struct flash_info { | 407 | struct flash_info { |
400 | char *name; | 408 | char *name; |
401 | u8 id; | 409 | |
402 | u16 jedec_id; | 410 | /* JEDEC id zero means "no ID" (most older chips); otherwise it has |
411 | * a high byte of zero plus three data bytes: the manufacturer id, | ||
412 | * then a two byte device id. | ||
413 | */ | ||
414 | u32 jedec_id; | ||
415 | |||
416 | /* The size listed here is what works with OPCODE_SE, which isn't | ||
417 | * necessarily called a "sector" by the vendor. | ||
418 | */ | ||
403 | unsigned sector_size; | 419 | unsigned sector_size; |
404 | unsigned n_sectors; | 420 | u16 n_sectors; |
421 | |||
422 | u16 flags; | ||
423 | #define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ | ||
405 | }; | 424 | }; |
406 | 425 | ||
426 | |||
427 | /* NOTE: double check command sets and memory organization when you add | ||
428 | * more flash chips. This current list focusses on newer chips, which | ||
429 | * have been converging on command sets which including JEDEC ID. | ||
430 | */ | ||
407 | static struct flash_info __devinitdata m25p_data [] = { | 431 | static struct flash_info __devinitdata m25p_data [] = { |
408 | /* REVISIT: fill in JEDEC ids, for parts that have them */ | 432 | |
409 | { "m25p05", 0x05, 0x2010, 32 * 1024, 2 }, | 433 | /* Atmel -- some are (confusingly) marketed as "DataFlash" */ |
410 | { "m25p10", 0x10, 0x2011, 32 * 1024, 4 }, | 434 | { "at25fs010", 0x1f6601, 32 * 1024, 4, SECT_4K, }, |
411 | { "m25p20", 0x11, 0x2012, 64 * 1024, 4 }, | 435 | { "at25fs040", 0x1f6604, 64 * 1024, 8, SECT_4K, }, |
412 | { "m25p40", 0x12, 0x2013, 64 * 1024, 8 }, | 436 | |
413 | { "m25p80", 0x13, 0x0000, 64 * 1024, 16 }, | 437 | { "at25df041a", 0x1f4401, 64 * 1024, 8, SECT_4K, }, |
414 | { "m25p16", 0x14, 0x2015, 64 * 1024, 32 }, | 438 | |
415 | { "m25p32", 0x15, 0x2016, 64 * 1024, 64 }, | 439 | { "at26f004", 0x1f0400, 64 * 1024, 8, SECT_4K, }, |
416 | { "m25p64", 0x16, 0x2017, 64 * 1024, 128 }, | 440 | { "at26df081a", 0x1f4501, 64 * 1024, 16, SECT_4K, }, |
441 | { "at26df161a", 0x1f4601, 64 * 1024, 32, SECT_4K, }, | ||
442 | { "at26df321", 0x1f4701, 64 * 1024, 64, SECT_4K, }, | ||
443 | |||
444 | /* Spansion -- single (large) sector size only, at least | ||
445 | * for the chips listed here (without boot sectors). | ||
446 | */ | ||
447 | { "s25sl004a", 0x010212, 64 * 1024, 8, }, | ||
448 | { "s25sl008a", 0x010213, 64 * 1024, 16, }, | ||
449 | { "s25sl016a", 0x010214, 64 * 1024, 32, }, | ||
450 | { "s25sl032a", 0x010215, 64 * 1024, 64, }, | ||
451 | { "s25sl064a", 0x010216, 64 * 1024, 128, }, | ||
452 | |||
453 | /* SST -- large erase sizes are "overlays", "sectors" are 4K */ | ||
454 | { "sst25vf040b", 0xbf258d, 64 * 1024, 8, SECT_4K, }, | ||
455 | { "sst25vf080b", 0xbf258e, 64 * 1024, 16, SECT_4K, }, | ||
456 | { "sst25vf016b", 0xbf2541, 64 * 1024, 32, SECT_4K, }, | ||
457 | { "sst25vf032b", 0xbf254a, 64 * 1024, 64, SECT_4K, }, | ||
458 | |||
459 | /* ST Microelectronics -- newer production may have feature updates */ | ||
460 | { "m25p05", 0x202010, 32 * 1024, 2, }, | ||
461 | { "m25p10", 0x202011, 32 * 1024, 4, }, | ||
462 | { "m25p20", 0x202012, 64 * 1024, 4, }, | ||
463 | { "m25p40", 0x202013, 64 * 1024, 8, }, | ||
464 | { "m25p80", 0, 64 * 1024, 16, }, | ||
465 | { "m25p16", 0x202015, 64 * 1024, 32, }, | ||
466 | { "m25p32", 0x202016, 64 * 1024, 64, }, | ||
467 | { "m25p64", 0x202017, 64 * 1024, 128, }, | ||
468 | { "m25p128", 0x202018, 256 * 1024, 64, }, | ||
469 | |||
470 | { "m45pe80", 0x204014, 64 * 1024, 16, }, | ||
471 | { "m45pe16", 0x204015, 64 * 1024, 32, }, | ||
472 | |||
473 | { "m25pe80", 0x208014, 64 * 1024, 16, }, | ||
474 | { "m25pe16", 0x208015, 64 * 1024, 32, SECT_4K, }, | ||
475 | |||
476 | /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ | ||
477 | { "w25x10", 0xef3011, 64 * 1024, 2, SECT_4K, }, | ||
478 | { "w25x20", 0xef3012, 64 * 1024, 4, SECT_4K, }, | ||
479 | { "w25x40", 0xef3013, 64 * 1024, 8, SECT_4K, }, | ||
480 | { "w25x80", 0xef3014, 64 * 1024, 16, SECT_4K, }, | ||
481 | { "w25x16", 0xef3015, 64 * 1024, 32, SECT_4K, }, | ||
482 | { "w25x32", 0xef3016, 64 * 1024, 64, SECT_4K, }, | ||
483 | { "w25x64", 0xef3017, 64 * 1024, 128, SECT_4K, }, | ||
417 | }; | 484 | }; |
418 | 485 | ||
486 | static struct flash_info *__devinit jedec_probe(struct spi_device *spi) | ||
487 | { | ||
488 | int tmp; | ||
489 | u8 code = OPCODE_RDID; | ||
490 | u8 id[3]; | ||
491 | u32 jedec; | ||
492 | struct flash_info *info; | ||
493 | |||
494 | /* JEDEC also defines an optional "extended device information" | ||
495 | * string for after vendor-specific data, after the three bytes | ||
496 | * we use here. Supporting some chips might require using it. | ||
497 | */ | ||
498 | tmp = spi_write_then_read(spi, &code, 1, id, 3); | ||
499 | if (tmp < 0) { | ||
500 | DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", | ||
501 | spi->dev.bus_id, tmp); | ||
502 | return NULL; | ||
503 | } | ||
504 | jedec = id[0]; | ||
505 | jedec = jedec << 8; | ||
506 | jedec |= id[1]; | ||
507 | jedec = jedec << 8; | ||
508 | jedec |= id[2]; | ||
509 | |||
510 | for (tmp = 0, info = m25p_data; | ||
511 | tmp < ARRAY_SIZE(m25p_data); | ||
512 | tmp++, info++) { | ||
513 | if (info->jedec_id == jedec) | ||
514 | return info; | ||
515 | } | ||
516 | dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec); | ||
517 | return NULL; | ||
518 | } | ||
519 | |||
520 | |||
419 | /* | 521 | /* |
420 | * board specific setup should have ensured the SPI clock used here | 522 | * board specific setup should have ensured the SPI clock used here |
421 | * matches what the READ command supports, at least until this driver | 523 | * matches what the READ command supports, at least until this driver |
@@ -429,37 +531,51 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
429 | unsigned i; | 531 | unsigned i; |
430 | 532 | ||
431 | /* Platform data helps sort out which chip type we have, as | 533 | /* Platform data helps sort out which chip type we have, as |
432 | * well as how this board partitions it. | 534 | * well as how this board partitions it. If we don't have |
535 | * a chip ID, try the JEDEC id commands; they'll work for most | ||
536 | * newer chips, even if we don't recognize the particular chip. | ||
433 | */ | 537 | */ |
434 | data = spi->dev.platform_data; | 538 | data = spi->dev.platform_data; |
435 | if (!data || !data->type) { | 539 | if (data && data->type) { |
436 | /* FIXME some chips can identify themselves with RES | 540 | for (i = 0, info = m25p_data; |
437 | * or JEDEC get-id commands. Try them ... | 541 | i < ARRAY_SIZE(m25p_data); |
438 | */ | 542 | i++, info++) { |
439 | DEBUG(MTD_DEBUG_LEVEL1, "%s: no chip id\n", | 543 | if (strcmp(data->type, info->name) == 0) |
440 | spi->dev.bus_id); | 544 | break; |
441 | return -ENODEV; | 545 | } |
442 | } | ||
443 | 546 | ||
444 | for (i = 0, info = m25p_data; i < ARRAY_SIZE(m25p_data); i++, info++) { | 547 | /* unrecognized chip? */ |
445 | if (strcmp(data->type, info->name) == 0) | 548 | if (i == ARRAY_SIZE(m25p_data)) { |
446 | break; | 549 | DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n", |
447 | } | 550 | spi->dev.bus_id, data->type); |
448 | if (i == ARRAY_SIZE(m25p_data)) { | 551 | info = NULL; |
449 | DEBUG(MTD_DEBUG_LEVEL1, "%s: unrecognized id %s\n", | 552 | |
450 | spi->dev.bus_id, data->type); | 553 | /* recognized; is that chip really what's there? */ |
554 | } else if (info->jedec_id) { | ||
555 | struct flash_info *chip = jedec_probe(spi); | ||
556 | |||
557 | if (!chip || chip != info) { | ||
558 | dev_warn(&spi->dev, "found %s, expected %s\n", | ||
559 | chip ? chip->name : "UNKNOWN", | ||
560 | info->name); | ||
561 | info = NULL; | ||
562 | } | ||
563 | } | ||
564 | } else | ||
565 | info = jedec_probe(spi); | ||
566 | |||
567 | if (!info) | ||
451 | return -ENODEV; | 568 | return -ENODEV; |
452 | } | ||
453 | 569 | ||
454 | flash = kzalloc(sizeof *flash, GFP_KERNEL); | 570 | flash = kzalloc(sizeof *flash, GFP_KERNEL); |
455 | if (!flash) | 571 | if (!flash) |
456 | return -ENOMEM; | 572 | return -ENOMEM; |
457 | 573 | ||
458 | flash->spi = spi; | 574 | flash->spi = spi; |
459 | init_MUTEX(&flash->lock); | 575 | mutex_init(&flash->lock); |
460 | dev_set_drvdata(&spi->dev, flash); | 576 | dev_set_drvdata(&spi->dev, flash); |
461 | 577 | ||
462 | if (data->name) | 578 | if (data && data->name) |
463 | flash->mtd.name = data->name; | 579 | flash->mtd.name = data->name; |
464 | else | 580 | else |
465 | flash->mtd.name = spi->dev.bus_id; | 581 | flash->mtd.name = spi->dev.bus_id; |
@@ -468,17 +584,25 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
468 | flash->mtd.writesize = 1; | 584 | flash->mtd.writesize = 1; |
469 | flash->mtd.flags = MTD_CAP_NORFLASH; | 585 | flash->mtd.flags = MTD_CAP_NORFLASH; |
470 | flash->mtd.size = info->sector_size * info->n_sectors; | 586 | flash->mtd.size = info->sector_size * info->n_sectors; |
471 | flash->mtd.erasesize = info->sector_size; | ||
472 | flash->mtd.erase = m25p80_erase; | 587 | flash->mtd.erase = m25p80_erase; |
473 | flash->mtd.read = m25p80_read; | 588 | flash->mtd.read = m25p80_read; |
474 | flash->mtd.write = m25p80_write; | 589 | flash->mtd.write = m25p80_write; |
475 | 590 | ||
591 | /* prefer "small sector" erase if possible */ | ||
592 | if (info->flags & SECT_4K) { | ||
593 | flash->erase_opcode = OPCODE_BE_4K; | ||
594 | flash->mtd.erasesize = 4096; | ||
595 | } else { | ||
596 | flash->erase_opcode = OPCODE_SE; | ||
597 | flash->mtd.erasesize = info->sector_size; | ||
598 | } | ||
599 | |||
476 | dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name, | 600 | dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name, |
477 | flash->mtd.size / 1024); | 601 | flash->mtd.size / 1024); |
478 | 602 | ||
479 | DEBUG(MTD_DEBUG_LEVEL2, | 603 | DEBUG(MTD_DEBUG_LEVEL2, |
480 | "mtd .name = %s, .size = 0x%.8x (%uM) " | 604 | "mtd .name = %s, .size = 0x%.8x (%uMiB) " |
481 | ".erasesize = 0x%.8x (%uK) .numeraseregions = %d\n", | 605 | ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", |
482 | flash->mtd.name, | 606 | flash->mtd.name, |
483 | flash->mtd.size, flash->mtd.size / (1024*1024), | 607 | flash->mtd.size, flash->mtd.size / (1024*1024), |
484 | flash->mtd.erasesize, flash->mtd.erasesize / 1024, | 608 | flash->mtd.erasesize, flash->mtd.erasesize / 1024, |
@@ -488,7 +612,7 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
488 | for (i = 0; i < flash->mtd.numeraseregions; i++) | 612 | for (i = 0; i < flash->mtd.numeraseregions; i++) |
489 | DEBUG(MTD_DEBUG_LEVEL2, | 613 | DEBUG(MTD_DEBUG_LEVEL2, |
490 | "mtd.eraseregions[%d] = { .offset = 0x%.8x, " | 614 | "mtd.eraseregions[%d] = { .offset = 0x%.8x, " |
491 | ".erasesize = 0x%.8x (%uK), " | 615 | ".erasesize = 0x%.8x (%uKiB), " |
492 | ".numblocks = %d }\n", | 616 | ".numblocks = %d }\n", |
493 | i, flash->mtd.eraseregions[i].offset, | 617 | i, flash->mtd.eraseregions[i].offset, |
494 | flash->mtd.eraseregions[i].erasesize, | 618 | flash->mtd.eraseregions[i].erasesize, |
@@ -516,14 +640,14 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
516 | } | 640 | } |
517 | 641 | ||
518 | if (nr_parts > 0) { | 642 | if (nr_parts > 0) { |
519 | for (i = 0; i < data->nr_parts; i++) { | 643 | for (i = 0; i < nr_parts; i++) { |
520 | DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " | 644 | DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " |
521 | "{.name = %s, .offset = 0x%.8x, " | 645 | "{.name = %s, .offset = 0x%.8x, " |
522 | ".size = 0x%.8x (%uK) }\n", | 646 | ".size = 0x%.8x (%uKiB) }\n", |
523 | i, data->parts[i].name, | 647 | i, parts[i].name, |
524 | data->parts[i].offset, | 648 | parts[i].offset, |
525 | data->parts[i].size, | 649 | parts[i].size, |
526 | data->parts[i].size / 1024); | 650 | parts[i].size / 1024); |
527 | } | 651 | } |
528 | flash->partitioned = 1; | 652 | flash->partitioned = 1; |
529 | return add_mtd_partitions(&flash->mtd, parts, nr_parts); | 653 | return add_mtd_partitions(&flash->mtd, parts, nr_parts); |
@@ -560,6 +684,11 @@ static struct spi_driver m25p80_driver = { | |||
560 | }, | 684 | }, |
561 | .probe = m25p_probe, | 685 | .probe = m25p_probe, |
562 | .remove = __devexit_p(m25p_remove), | 686 | .remove = __devexit_p(m25p_remove), |
687 | |||
688 | /* REVISIT: many of these chips have deep power-down modes, which | ||
689 | * should clearly be entered on suspend() to minimize power use. | ||
690 | * And also when they're otherwise idle... | ||
691 | */ | ||
563 | }; | 692 | }; |
564 | 693 | ||
565 | 694 | ||
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index a987e917f4e0..a5ed6d232c35 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/delay.h> | 15 | #include <linux/delay.h> |
16 | #include <linux/device.h> | 16 | #include <linux/device.h> |
17 | #include <linux/mutex.h> | ||
17 | #include <linux/spi/spi.h> | 18 | #include <linux/spi/spi.h> |
18 | #include <linux/spi/flash.h> | 19 | #include <linux/spi/flash.h> |
19 | 20 | ||
@@ -89,7 +90,7 @@ struct dataflash { | |||
89 | unsigned short page_offset; /* offset in flash address */ | 90 | unsigned short page_offset; /* offset in flash address */ |
90 | unsigned int page_size; /* of bytes per page */ | 91 | unsigned int page_size; /* of bytes per page */ |
91 | 92 | ||
92 | struct semaphore lock; | 93 | struct mutex lock; |
93 | struct spi_device *spi; | 94 | struct spi_device *spi; |
94 | 95 | ||
95 | struct mtd_info mtd; | 96 | struct mtd_info mtd; |
@@ -167,7 +168,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
167 | x.len = 4; | 168 | x.len = 4; |
168 | spi_message_add_tail(&x, &msg); | 169 | spi_message_add_tail(&x, &msg); |
169 | 170 | ||
170 | down(&priv->lock); | 171 | mutex_lock(&priv->lock); |
171 | while (instr->len > 0) { | 172 | while (instr->len > 0) { |
172 | unsigned int pageaddr; | 173 | unsigned int pageaddr; |
173 | int status; | 174 | int status; |
@@ -210,7 +211,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
210 | instr->len -= priv->page_size; | 211 | instr->len -= priv->page_size; |
211 | } | 212 | } |
212 | } | 213 | } |
213 | up(&priv->lock); | 214 | mutex_unlock(&priv->lock); |
214 | 215 | ||
215 | /* Inform MTD subsystem that erase is complete */ | 216 | /* Inform MTD subsystem that erase is complete */ |
216 | instr->state = MTD_ERASE_DONE; | 217 | instr->state = MTD_ERASE_DONE; |
@@ -266,7 +267,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
266 | x[1].len = len; | 267 | x[1].len = len; |
267 | spi_message_add_tail(&x[1], &msg); | 268 | spi_message_add_tail(&x[1], &msg); |
268 | 269 | ||
269 | down(&priv->lock); | 270 | mutex_lock(&priv->lock); |
270 | 271 | ||
271 | /* Continuous read, max clock = f(car) which may be less than | 272 | /* Continuous read, max clock = f(car) which may be less than |
272 | * the peak rate available. Some chips support commands with | 273 | * the peak rate available. Some chips support commands with |
@@ -279,7 +280,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
279 | /* plus 4 "don't care" bytes */ | 280 | /* plus 4 "don't care" bytes */ |
280 | 281 | ||
281 | status = spi_sync(priv->spi, &msg); | 282 | status = spi_sync(priv->spi, &msg); |
282 | up(&priv->lock); | 283 | mutex_unlock(&priv->lock); |
283 | 284 | ||
284 | if (status >= 0) { | 285 | if (status >= 0) { |
285 | *retlen = msg.actual_length - 8; | 286 | *retlen = msg.actual_length - 8; |
@@ -336,7 +337,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
336 | else | 337 | else |
337 | writelen = len; | 338 | writelen = len; |
338 | 339 | ||
339 | down(&priv->lock); | 340 | mutex_lock(&priv->lock); |
340 | while (remaining > 0) { | 341 | while (remaining > 0) { |
341 | DEBUG(MTD_DEBUG_LEVEL3, "write @ %i:%i len=%i\n", | 342 | DEBUG(MTD_DEBUG_LEVEL3, "write @ %i:%i len=%i\n", |
342 | pageaddr, offset, writelen); | 343 | pageaddr, offset, writelen); |
@@ -441,7 +442,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
441 | else | 442 | else |
442 | writelen = remaining; | 443 | writelen = remaining; |
443 | } | 444 | } |
444 | up(&priv->lock); | 445 | mutex_unlock(&priv->lock); |
445 | 446 | ||
446 | return status; | 447 | return status; |
447 | } | 448 | } |
@@ -463,7 +464,7 @@ add_dataflash(struct spi_device *spi, char *name, | |||
463 | if (!priv) | 464 | if (!priv) |
464 | return -ENOMEM; | 465 | return -ENOMEM; |
465 | 466 | ||
466 | init_MUTEX(&priv->lock); | 467 | mutex_init(&priv->lock); |
467 | priv->spi = spi; | 468 | priv->spi = spi; |
468 | priv->page_size = pagesize; | 469 | priv->page_size = pagesize; |
469 | priv->page_offset = pageoffset; | 470 | priv->page_offset = pageoffset; |
diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index e8f686f7a357..7060a0895ce2 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c | |||
@@ -30,8 +30,8 @@ | |||
30 | * | 30 | * |
31 | * Notes: | 31 | * Notes: |
32 | * Due to what I assume is more buggy SROM, the 64M PMC551 I | 32 | * Due to what I assume is more buggy SROM, the 64M PMC551 I |
33 | * have available claims that all 4 of it's DRAM banks have 64M | 33 | * have available claims that all 4 of its DRAM banks have 64MiB |
34 | * of ram configured (making a grand total of 256M onboard). | 34 | * of ram configured (making a grand total of 256MiB onboard). |
35 | * This is slightly annoying since the BAR0 size reflects the | 35 | * This is slightly annoying since the BAR0 size reflects the |
36 | * aperture size, not the dram size, and the V370PDC supplies no | 36 | * aperture size, not the dram size, and the V370PDC supplies no |
37 | * other method for memory size discovery. This problem is | 37 | * other method for memory size discovery. This problem is |
@@ -70,7 +70,7 @@ | |||
70 | * made the memory unusable, added a fix to code to touch up | 70 | * made the memory unusable, added a fix to code to touch up |
71 | * the DRAM some. | 71 | * the DRAM some. |
72 | * | 72 | * |
73 | * Bugs/FIXME's: | 73 | * Bugs/FIXMEs: |
74 | * * MUST fix the init function to not spin on a register | 74 | * * MUST fix the init function to not spin on a register |
75 | * waiting for it to set .. this does not safely handle busted | 75 | * waiting for it to set .. this does not safely handle busted |
76 | * devices that never reset the register correctly which will | 76 | * devices that never reset the register correctly which will |
@@ -562,10 +562,10 @@ static u32 fixup_pmc551(struct pci_dev *dev) | |||
562 | /* | 562 | /* |
563 | * Some screen fun | 563 | * Some screen fun |
564 | */ | 564 | */ |
565 | printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at " | 565 | printk(KERN_DEBUG "pmc551: %d%sB (0x%x) of %sprefetchable memory at " |
566 | "0x%llx\n", (size < 1024) ? size : (size < 1048576) ? | 566 | "0x%llx\n", (size < 1024) ? size : (size < 1048576) ? |
567 | size >> 10 : size >> 20, | 567 | size >> 10 : size >> 20, |
568 | (size < 1024) ? 'B' : (size < 1048576) ? 'K' : 'M', size, | 568 | (size < 1024) ? "" : (size < 1048576) ? "Ki" : "Mi", size, |
569 | ((dcmd & (0x1 << 3)) == 0) ? "non-" : "", | 569 | ((dcmd & (0x1 << 3)) == 0) ? "non-" : "", |
570 | (unsigned long long)pci_resource_start(dev, 0)); | 570 | (unsigned long long)pci_resource_start(dev, 0)); |
571 | 571 | ||
@@ -649,14 +649,10 @@ MODULE_DESCRIPTION(PMC551_VERSION); | |||
649 | * Stuff these outside the ifdef so as to not bust compiled in driver support | 649 | * Stuff these outside the ifdef so as to not bust compiled in driver support |
650 | */ | 650 | */ |
651 | static int msize = 0; | 651 | static int msize = 0; |
652 | #if defined(CONFIG_MTD_PMC551_APERTURE_SIZE) | ||
653 | static int asize = CONFIG_MTD_PMC551_APERTURE_SIZE; | ||
654 | #else | ||
655 | static int asize = 0; | 652 | static int asize = 0; |
656 | #endif | ||
657 | 653 | ||
658 | module_param(msize, int, 0); | 654 | module_param(msize, int, 0); |
659 | MODULE_PARM_DESC(msize, "memory size in Megabytes [1 - 1024]"); | 655 | MODULE_PARM_DESC(msize, "memory size in MiB [1 - 1024]"); |
660 | module_param(asize, int, 0); | 656 | module_param(asize, int, 0); |
661 | MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1-1024]"); | 657 | MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1-1024]"); |
662 | 658 | ||
@@ -799,8 +795,7 @@ static int __init init_pmc551(void) | |||
799 | mtd->owner = THIS_MODULE; | 795 | mtd->owner = THIS_MODULE; |
800 | 796 | ||
801 | if (add_mtd_device(mtd)) { | 797 | if (add_mtd_device(mtd)) { |
802 | printk(KERN_NOTICE "pmc551: Failed to register new " | 798 | printk(KERN_NOTICE "pmc551: Failed to register new device\n"); |
803 | "device\n"); | ||
804 | pci_iounmap(PCI_Device, priv->start); | 799 | pci_iounmap(PCI_Device, priv->start); |
805 | kfree(mtd->priv); | 800 | kfree(mtd->priv); |
806 | kfree(mtd); | 801 | kfree(mtd); |
@@ -811,13 +806,13 @@ static int __init init_pmc551(void) | |||
811 | pci_dev_get(PCI_Device); | 806 | pci_dev_get(PCI_Device); |
812 | 807 | ||
813 | printk(KERN_NOTICE "Registered pmc551 memory device.\n"); | 808 | printk(KERN_NOTICE "Registered pmc551 memory device.\n"); |
814 | printk(KERN_NOTICE "Mapped %dM of memory from 0x%p to 0x%p\n", | 809 | printk(KERN_NOTICE "Mapped %dMiB of memory from 0x%p to 0x%p\n", |
815 | priv->asize >> 20, | 810 | priv->asize >> 20, |
816 | priv->start, priv->start + priv->asize); | 811 | priv->start, priv->start + priv->asize); |
817 | printk(KERN_NOTICE "Total memory is %d%c\n", | 812 | printk(KERN_NOTICE "Total memory is %d%sB\n", |
818 | (length < 1024) ? length : | 813 | (length < 1024) ? length : |
819 | (length < 1048576) ? length >> 10 : length >> 20, | 814 | (length < 1048576) ? length >> 10 : length >> 20, |
820 | (length < 1024) ? 'B' : (length < 1048576) ? 'K' : 'M'); | 815 | (length < 1024) ? "" : (length < 1048576) ? "Ki" : "Mi"); |
821 | priv->nextpmc551 = pmc551list; | 816 | priv->nextpmc551 = pmc551list; |
822 | pmc551list = mtd; | 817 | pmc551list = mtd; |
823 | found++; | 818 | found++; |
@@ -850,7 +845,7 @@ static void __exit cleanup_pmc551(void) | |||
850 | pmc551list = priv->nextpmc551; | 845 | pmc551list = priv->nextpmc551; |
851 | 846 | ||
852 | if (priv->start) { | 847 | if (priv->start) { |
853 | printk(KERN_DEBUG "pmc551: unmapping %dM starting at " | 848 | printk(KERN_DEBUG "pmc551: unmapping %dMiB starting at " |
854 | "0x%p\n", priv->asize >> 20, priv->start); | 849 | "0x%p\n", priv->asize >> 20, priv->start); |
855 | pci_iounmap(priv->dev, priv->start); | 850 | pci_iounmap(priv->dev, priv->start); |
856 | } | 851 | } |
diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index ecac0e438f49..b8917beeb650 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c | |||
@@ -580,14 +580,13 @@ int INFTL_mount(struct INFTLrecord *s) | |||
580 | logical_block = block = BLOCK_NIL; | 580 | logical_block = block = BLOCK_NIL; |
581 | 581 | ||
582 | /* Temporary buffer to store ANAC numbers. */ | 582 | /* Temporary buffer to store ANAC numbers. */ |
583 | ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL); | 583 | ANACtable = kcalloc(s->nb_blocks, sizeof(u8), GFP_KERNEL); |
584 | if (!ANACtable) { | 584 | if (!ANACtable) { |
585 | printk(KERN_WARNING "INFTL: allocation of ANACtable " | 585 | printk(KERN_WARNING "INFTL: allocation of ANACtable " |
586 | "failed (%zd bytes)\n", | 586 | "failed (%zd bytes)\n", |
587 | s->nb_blocks * sizeof(u8)); | 587 | s->nb_blocks * sizeof(u8)); |
588 | return -ENOMEM; | 588 | return -ENOMEM; |
589 | } | 589 | } |
590 | memset(ANACtable, 0, s->nb_blocks); | ||
591 | 590 | ||
592 | /* | 591 | /* |
593 | * First pass is to explore each physical unit, and construct the | 592 | * First pass is to explore each physical unit, and construct the |
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 6cd132c75187..2a2a125b0c76 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig | |||
@@ -163,20 +163,12 @@ config MTD_SBC_GXX | |||
163 | More info at | 163 | More info at |
164 | <http://www.arcomcontrols.com/products/icp/pc104/processors/SBC_GX1.htm>. | 164 | <http://www.arcomcontrols.com/products/icp/pc104/processors/SBC_GX1.htm>. |
165 | 165 | ||
166 | config MTD_LUBBOCK | 166 | config MTD_PXA2XX |
167 | tristate "CFI Flash device mapped on Intel Lubbock XScale eval board" | 167 | tristate "CFI Flash device mapped on Intel XScale PXA2xx based boards" |
168 | depends on ARCH_LUBBOCK && MTD_CFI_INTELEXT && MTD_PARTITIONS | 168 | depends on (PXA25x || PXA27x) && MTD_CFI_INTELEXT |
169 | help | ||
170 | This provides a driver for the on-board flash of the Intel | ||
171 | 'Lubbock' XScale evaluation board. | ||
172 | |||
173 | config MTD_MAINSTONE | ||
174 | tristate "CFI Flash device mapped on Intel Mainstone XScale eval board" | ||
175 | depends on MACH_MAINSTONE && MTD_CFI_INTELEXT | ||
176 | select MTD_PARTITIONS | 169 | select MTD_PARTITIONS |
177 | help | 170 | help |
178 | This provides a driver for the on-board flash of the Intel | 171 | This provides a driver for the NOR flash attached to a PXA2xx chip. |
179 | 'Mainstone PXA27x evaluation board. | ||
180 | 172 | ||
181 | config MTD_OCTAGON | 173 | config MTD_OCTAGON |
182 | tristate "JEDEC Flash device mapped on Octagon 5066 SBC" | 174 | tristate "JEDEC Flash device mapped on Octagon 5066 SBC" |
@@ -354,7 +346,7 @@ config MTD_CFI_FLAGADM | |||
354 | 346 | ||
355 | config MTD_WALNUT | 347 | config MTD_WALNUT |
356 | tristate "Flash device mapped on IBM 405GP Walnut" | 348 | tristate "Flash device mapped on IBM 405GP Walnut" |
357 | depends on MTD_JEDECPROBE && WALNUT | 349 | depends on MTD_JEDECPROBE && WALNUT && !PPC_MERGE |
358 | help | 350 | help |
359 | This enables access routines for the flash chips on the IBM 405GP | 351 | This enables access routines for the flash chips on the IBM 405GP |
360 | Walnut board. If you have one of these boards and would like to | 352 | Walnut board. If you have one of these boards and would like to |
@@ -370,7 +362,7 @@ config MTD_EBONY | |||
370 | 362 | ||
371 | config MTD_OCOTEA | 363 | config MTD_OCOTEA |
372 | tristate "Flash devices mapped on IBM 440GX Ocotea" | 364 | tristate "Flash devices mapped on IBM 440GX Ocotea" |
373 | depends on MTD_CFI && OCOTEA | 365 | depends on MTD_CFI && OCOTEA && !PPC_MERGE |
374 | help | 366 | help |
375 | This enables access routines for the flash chips on the IBM 440GX | 367 | This enables access routines for the flash chips on the IBM 440GX |
376 | Ocotea board. If you have one of these boards and would like to | 368 | Ocotea board. If you have one of these boards and would like to |
@@ -384,22 +376,6 @@ config MTD_REDWOOD | |||
384 | Redwood board. If you have one of these boards and would like to | 376 | Redwood board. If you have one of these boards and would like to |
385 | use the flash chips on it, say 'Y'. | 377 | use the flash chips on it, say 'Y'. |
386 | 378 | ||
387 | config MTD_TQM834x | ||
388 | tristate "Flash device mapped on TQ Components TQM834x Boards" | ||
389 | depends on MTD_CFI && TQM834x | ||
390 | help | ||
391 | This enables access routines for the flash chips on the | ||
392 | TQ Components TQM834x boards. If you have one of these boards | ||
393 | and would like to use the flash chips on it, say 'Y'. | ||
394 | |||
395 | config MTD_OCELOT | ||
396 | tristate "Momenco Ocelot boot flash device" | ||
397 | depends on MOMENCO_OCELOT | ||
398 | help | ||
399 | This enables access routines for the boot flash device and for the | ||
400 | NVRAM on the Momenco Ocelot board. If you have one of these boards | ||
401 | and would like access to either of these, say 'Y'. | ||
402 | |||
403 | config MTD_SOLUTIONENGINE | 379 | config MTD_SOLUTIONENGINE |
404 | tristate "CFI Flash device mapped on Hitachi SolutionEngine" | 380 | tristate "CFI Flash device mapped on Hitachi SolutionEngine" |
405 | depends on SUPERH && MTD_CFI && MTD_REDBOOT_PARTS | 381 | depends on SUPERH && MTD_CFI && MTD_REDBOOT_PARTS |
@@ -605,6 +581,13 @@ config MTD_SHARP_SL | |||
605 | help | 581 | help |
606 | This enables access to the flash chip on the Sharp SL Series of PDAs. | 582 | This enables access to the flash chip on the Sharp SL Series of PDAs. |
607 | 583 | ||
584 | config MTD_INTEL_VR_NOR | ||
585 | tristate "NOR flash on Intel Vermilion Range Expansion Bus CS0" | ||
586 | depends on PCI | ||
587 | help | ||
588 | Map driver for a NOR flash bank located on the Expansion Bus of the | ||
589 | Intel Vermilion Range chipset. | ||
590 | |||
608 | config MTD_PLATRAM | 591 | config MTD_PLATRAM |
609 | tristate "Map driver for platform device RAM (mtd-ram)" | 592 | tristate "Map driver for platform device RAM (mtd-ram)" |
610 | select MTD_RAM | 593 | select MTD_RAM |
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 970b189271a2..316382a1401b 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile | |||
@@ -20,8 +20,7 @@ obj-$(CONFIG_MTD_ESB2ROM) += esb2rom.o | |||
20 | obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o | 20 | obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o |
21 | obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o | 21 | obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o |
22 | obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o | 22 | obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o |
23 | obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o | 23 | obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o |
24 | obj-$(CONFIG_MTD_MAINSTONE) += mainstone-flash.o | ||
25 | obj-$(CONFIG_MTD_MBX860) += mbx860.o | 24 | obj-$(CONFIG_MTD_MBX860) += mbx860.o |
26 | obj-$(CONFIG_MTD_CEIVA) += ceiva.o | 25 | obj-$(CONFIG_MTD_CEIVA) += ceiva.o |
27 | obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o | 26 | obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o |
@@ -43,7 +42,6 @@ obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o | |||
43 | obj-$(CONFIG_MTD_VMAX) += vmax301.o | 42 | obj-$(CONFIG_MTD_VMAX) += vmax301.o |
44 | obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o | 43 | obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o |
45 | obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o | 44 | obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o |
46 | obj-$(CONFIG_MTD_OCELOT) += ocelot.o | ||
47 | obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o | 45 | obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o |
48 | obj-$(CONFIG_MTD_PCI) += pci.o | 46 | obj-$(CONFIG_MTD_PCI) += pci.o |
49 | obj-$(CONFIG_MTD_ALCHEMY) += alchemy-flash.o | 47 | obj-$(CONFIG_MTD_ALCHEMY) += alchemy-flash.o |
@@ -70,4 +68,4 @@ obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o | |||
70 | obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o | 68 | obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o |
71 | obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o | 69 | obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o |
72 | obj-$(CONFIG_MTD_MTX1) += mtx-1_flash.o | 70 | obj-$(CONFIG_MTD_MTX1) += mtx-1_flash.o |
73 | obj-$(CONFIG_MTD_TQM834x) += tqm834x.o | 71 | obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o |
diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c index 84fbe0e8c47e..82811bcb0436 100644 --- a/drivers/mtd/maps/alchemy-flash.c +++ b/drivers/mtd/maps/alchemy-flash.c | |||
@@ -75,13 +75,6 @@ | |||
75 | #define BOARD_FLASH_WIDTH 2 /* 16-bits */ | 75 | #define BOARD_FLASH_WIDTH 2 /* 16-bits */ |
76 | #endif | 76 | #endif |
77 | 77 | ||
78 | #ifdef CONFIG_MIPS_HYDROGEN3 | ||
79 | #define BOARD_MAP_NAME "Hydrogen3 Flash" | ||
80 | #define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ | ||
81 | #define BOARD_FLASH_WIDTH 4 /* 32-bits */ | ||
82 | #define USE_LOCAL_ACCESSORS /* why? */ | ||
83 | #endif | ||
84 | |||
85 | #ifdef CONFIG_MIPS_BOSPORUS | 78 | #ifdef CONFIG_MIPS_BOSPORUS |
86 | #define BOARD_MAP_NAME "Bosporus Flash" | 79 | #define BOARD_MAP_NAME "Bosporus Flash" |
87 | #define BOARD_FLASH_SIZE 0x01000000 /* 16MB */ | 80 | #define BOARD_FLASH_SIZE 0x01000000 /* 16MB */ |
@@ -130,13 +123,6 @@ int __init alchemy_mtd_init(void) | |||
130 | 123 | ||
131 | window_addr = 0x20000000 - BOARD_FLASH_SIZE; | 124 | window_addr = 0x20000000 - BOARD_FLASH_SIZE; |
132 | window_size = BOARD_FLASH_SIZE; | 125 | window_size = BOARD_FLASH_SIZE; |
133 | #ifdef CONFIG_MIPS_MIRAGE_WHY | ||
134 | /* Boot ROM flash bank only; no user bank */ | ||
135 | window_addr = 0x1C000000; | ||
136 | window_size = 0x04000000; | ||
137 | /* USERFS from 0x1C00 0000 to 0x1FC00000 */ | ||
138 | alchemy_partitions[0].size = 0x03C00000; | ||
139 | #endif | ||
140 | 126 | ||
141 | /* | 127 | /* |
142 | * Static partition definition selection | 128 | * Static partition definition selection |
diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c new file mode 100644 index 000000000000..1e7814ae212a --- /dev/null +++ b/drivers/mtd/maps/intel_vr_nor.c | |||
@@ -0,0 +1,298 @@ | |||
1 | /* | ||
2 | * drivers/mtd/maps/intel_vr_nor.c | ||
3 | * | ||
4 | * An MTD map driver for a NOR flash bank on the Expansion Bus of the Intel | ||
5 | * Vermilion Range chipset. | ||
6 | * | ||
7 | * The Vermilion Range Expansion Bus supports four chip selects, each of which | ||
8 | * has 64MiB of address space. The 2nd BAR of the Expansion Bus PCI Device | ||
9 | * is a 256MiB memory region containing the address spaces for all four of the | ||
10 | * chip selects, with start addresses hardcoded on 64MiB boundaries. | ||
11 | * | ||
12 | * This map driver only supports NOR flash on chip select 0. The buswidth | ||
13 | * (either 8 bits or 16 bits) is determined by reading the Expansion Bus Timing | ||
14 | * and Control Register for Chip Select 0 (EXP_TIMING_CS0). This driver does | ||
15 | * not modify the value in the EXP_TIMING_CS0 register except to enable writing | ||
16 | * and disable boot acceleration. The timing parameters in the register are | ||
17 | * assumed to have been properly initialized by the BIOS. The reset default | ||
18 | * timing parameters are maximally conservative (slow), so access to the flash | ||
19 | * will be slower than it should be if the BIOS has not initialized the timing | ||
20 | * parameters. | ||
21 | * | ||
22 | * Author: Andy Lowe <alowe@mvista.com> | ||
23 | * | ||
24 | * 2006 (c) MontaVista Software, Inc. This file is licensed under | ||
25 | * the terms of the GNU General Public License version 2. This program | ||
26 | * is licensed "as is" without any warranty of any kind, whether express | ||
27 | * or implied. | ||
28 | */ | ||
29 | |||
30 | #include <linux/module.h> | ||
31 | #include <linux/kernel.h> | ||
32 | #include <linux/pci.h> | ||
33 | #include <linux/init.h> | ||
34 | #include <linux/mtd/mtd.h> | ||
35 | #include <linux/mtd/map.h> | ||
36 | #include <linux/mtd/partitions.h> | ||
37 | #include <linux/mtd/cfi.h> | ||
38 | #include <linux/mtd/flashchip.h> | ||
39 | |||
40 | #define DRV_NAME "vr_nor" | ||
41 | |||
42 | struct vr_nor_mtd { | ||
43 | void __iomem *csr_base; | ||
44 | struct map_info map; | ||
45 | struct mtd_info *info; | ||
46 | int nr_parts; | ||
47 | struct pci_dev *dev; | ||
48 | }; | ||
49 | |||
50 | /* Expansion Bus Configuration and Status Registers are in BAR 0 */ | ||
51 | #define EXP_CSR_MBAR 0 | ||
52 | /* Expansion Bus Memory Window is BAR 1 */ | ||
53 | #define EXP_WIN_MBAR 1 | ||
54 | /* Maximum address space for Chip Select 0 is 64MiB */ | ||
55 | #define CS0_SIZE 0x04000000 | ||
56 | /* Chip Select 0 is at offset 0 in the Memory Window */ | ||
57 | #define CS0_START 0x0 | ||
58 | /* Chip Select 0 Timing Register is at offset 0 in CSR */ | ||
59 | #define EXP_TIMING_CS0 0x00 | ||
60 | #define TIMING_CS_EN (1 << 31) /* Chip Select Enable */ | ||
61 | #define TIMING_BOOT_ACCEL_DIS (1 << 8) /* Boot Acceleration Disable */ | ||
62 | #define TIMING_WR_EN (1 << 1) /* Write Enable */ | ||
63 | #define TIMING_BYTE_EN (1 << 0) /* 8-bit vs 16-bit bus */ | ||
64 | #define TIMING_MASK 0x3FFF0000 | ||
65 | |||
66 | static void __devexit vr_nor_destroy_partitions(struct vr_nor_mtd *p) | ||
67 | { | ||
68 | if (p->nr_parts > 0) { | ||
69 | #if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE) | ||
70 | del_mtd_partitions(p->info); | ||
71 | #endif | ||
72 | } else | ||
73 | del_mtd_device(p->info); | ||
74 | } | ||
75 | |||
76 | static int __devinit vr_nor_init_partitions(struct vr_nor_mtd *p) | ||
77 | { | ||
78 | int err = 0; | ||
79 | #if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE) | ||
80 | struct mtd_partition *parts; | ||
81 | static const char *part_probes[] = { "cmdlinepart", NULL }; | ||
82 | #endif | ||
83 | |||
84 | /* register the flash bank */ | ||
85 | #if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE) | ||
86 | /* partition the flash bank */ | ||
87 | p->nr_parts = parse_mtd_partitions(p->info, part_probes, &parts, 0); | ||
88 | if (p->nr_parts > 0) | ||
89 | err = add_mtd_partitions(p->info, parts, p->nr_parts); | ||
90 | #endif | ||
91 | if (p->nr_parts <= 0) | ||
92 | err = add_mtd_device(p->info); | ||
93 | |||
94 | return err; | ||
95 | } | ||
96 | |||
97 | static void __devexit vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p) | ||
98 | { | ||
99 | map_destroy(p->info); | ||
100 | } | ||
101 | |||
102 | static int __devinit vr_nor_mtd_setup(struct vr_nor_mtd *p) | ||
103 | { | ||
104 | static const char *probe_types[] = | ||
105 | { "cfi_probe", "jedec_probe", NULL }; | ||
106 | const char **type; | ||
107 | |||
108 | for (type = probe_types; !p->info && *type; type++) | ||
109 | p->info = do_map_probe(*type, &p->map); | ||
110 | if (!p->info) | ||
111 | return -ENODEV; | ||
112 | |||
113 | p->info->owner = THIS_MODULE; | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static void __devexit vr_nor_destroy_maps(struct vr_nor_mtd *p) | ||
119 | { | ||
120 | unsigned int exp_timing_cs0; | ||
121 | |||
122 | /* write-protect the flash bank */ | ||
123 | exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0); | ||
124 | exp_timing_cs0 &= ~TIMING_WR_EN; | ||
125 | writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0); | ||
126 | |||
127 | /* unmap the flash window */ | ||
128 | iounmap(p->map.virt); | ||
129 | |||
130 | /* unmap the csr window */ | ||
131 | iounmap(p->csr_base); | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * Initialize the map_info structure and map the flash. | ||
136 | * Returns 0 on success, nonzero otherwise. | ||
137 | */ | ||
138 | static int __devinit vr_nor_init_maps(struct vr_nor_mtd *p) | ||
139 | { | ||
140 | unsigned long csr_phys, csr_len; | ||
141 | unsigned long win_phys, win_len; | ||
142 | unsigned int exp_timing_cs0; | ||
143 | int err; | ||
144 | |||
145 | csr_phys = pci_resource_start(p->dev, EXP_CSR_MBAR); | ||
146 | csr_len = pci_resource_len(p->dev, EXP_CSR_MBAR); | ||
147 | win_phys = pci_resource_start(p->dev, EXP_WIN_MBAR); | ||
148 | win_len = pci_resource_len(p->dev, EXP_WIN_MBAR); | ||
149 | |||
150 | if (!csr_phys || !csr_len || !win_phys || !win_len) | ||
151 | return -ENODEV; | ||
152 | |||
153 | if (win_len < (CS0_START + CS0_SIZE)) | ||
154 | return -ENXIO; | ||
155 | |||
156 | p->csr_base = ioremap_nocache(csr_phys, csr_len); | ||
157 | if (!p->csr_base) | ||
158 | return -ENOMEM; | ||
159 | |||
160 | exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0); | ||
161 | if (!(exp_timing_cs0 & TIMING_CS_EN)) { | ||
162 | dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 " | ||
163 | "is disabled.\n"); | ||
164 | err = -ENODEV; | ||
165 | goto release; | ||
166 | } | ||
167 | if ((exp_timing_cs0 & TIMING_MASK) == TIMING_MASK) { | ||
168 | dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 " | ||
169 | "is configured for maximally slow access times.\n"); | ||
170 | } | ||
171 | p->map.name = DRV_NAME; | ||
172 | p->map.bankwidth = (exp_timing_cs0 & TIMING_BYTE_EN) ? 1 : 2; | ||
173 | p->map.phys = win_phys + CS0_START; | ||
174 | p->map.size = CS0_SIZE; | ||
175 | p->map.virt = ioremap_nocache(p->map.phys, p->map.size); | ||
176 | if (!p->map.virt) { | ||
177 | err = -ENOMEM; | ||
178 | goto release; | ||
179 | } | ||
180 | simple_map_init(&p->map); | ||
181 | |||
182 | /* Enable writes to flash bank */ | ||
183 | exp_timing_cs0 |= TIMING_BOOT_ACCEL_DIS | TIMING_WR_EN; | ||
184 | writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0); | ||
185 | |||
186 | return 0; | ||
187 | |||
188 | release: | ||
189 | iounmap(p->csr_base); | ||
190 | return err; | ||
191 | } | ||
192 | |||
193 | static struct pci_device_id vr_nor_pci_ids[] = { | ||
194 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x500D)}, | ||
195 | {0,} | ||
196 | }; | ||
197 | |||
198 | static void __devexit vr_nor_pci_remove(struct pci_dev *dev) | ||
199 | { | ||
200 | struct vr_nor_mtd *p = pci_get_drvdata(dev); | ||
201 | |||
202 | pci_set_drvdata(dev, NULL); | ||
203 | vr_nor_destroy_partitions(p); | ||
204 | vr_nor_destroy_mtd_setup(p); | ||
205 | vr_nor_destroy_maps(p); | ||
206 | kfree(p); | ||
207 | pci_release_regions(dev); | ||
208 | pci_disable_device(dev); | ||
209 | } | ||
210 | |||
211 | static int __devinit | ||
212 | vr_nor_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) | ||
213 | { | ||
214 | struct vr_nor_mtd *p = NULL; | ||
215 | unsigned int exp_timing_cs0; | ||
216 | int err; | ||
217 | |||
218 | err = pci_enable_device(dev); | ||
219 | if (err) | ||
220 | goto out; | ||
221 | |||
222 | err = pci_request_regions(dev, DRV_NAME); | ||
223 | if (err) | ||
224 | goto disable_dev; | ||
225 | |||
226 | p = kzalloc(sizeof(*p), GFP_KERNEL); | ||
227 | err = -ENOMEM; | ||
228 | if (!p) | ||
229 | goto release; | ||
230 | |||
231 | p->dev = dev; | ||
232 | |||
233 | err = vr_nor_init_maps(p); | ||
234 | if (err) | ||
235 | goto release; | ||
236 | |||
237 | err = vr_nor_mtd_setup(p); | ||
238 | if (err) | ||
239 | goto destroy_maps; | ||
240 | |||
241 | err = vr_nor_init_partitions(p); | ||
242 | if (err) | ||
243 | goto destroy_mtd_setup; | ||
244 | |||
245 | pci_set_drvdata(dev, p); | ||
246 | |||
247 | return 0; | ||
248 | |||
249 | destroy_mtd_setup: | ||
250 | map_destroy(p->info); | ||
251 | |||
252 | destroy_maps: | ||
253 | /* write-protect the flash bank */ | ||
254 | exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0); | ||
255 | exp_timing_cs0 &= ~TIMING_WR_EN; | ||
256 | writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0); | ||
257 | |||
258 | /* unmap the flash window */ | ||
259 | iounmap(p->map.virt); | ||
260 | |||
261 | /* unmap the csr window */ | ||
262 | iounmap(p->csr_base); | ||
263 | |||
264 | release: | ||
265 | kfree(p); | ||
266 | pci_release_regions(dev); | ||
267 | |||
268 | disable_dev: | ||
269 | pci_disable_device(dev); | ||
270 | |||
271 | out: | ||
272 | return err; | ||
273 | } | ||
274 | |||
275 | static struct pci_driver vr_nor_pci_driver = { | ||
276 | .name = DRV_NAME, | ||
277 | .probe = vr_nor_pci_probe, | ||
278 | .remove = __devexit_p(vr_nor_pci_remove), | ||
279 | .id_table = vr_nor_pci_ids, | ||
280 | }; | ||
281 | |||
282 | static int __init vr_nor_mtd_init(void) | ||
283 | { | ||
284 | return pci_register_driver(&vr_nor_pci_driver); | ||
285 | } | ||
286 | |||
287 | static void __exit vr_nor_mtd_exit(void) | ||
288 | { | ||
289 | pci_unregister_driver(&vr_nor_pci_driver); | ||
290 | } | ||
291 | |||
292 | module_init(vr_nor_mtd_init); | ||
293 | module_exit(vr_nor_mtd_exit); | ||
294 | |||
295 | MODULE_AUTHOR("Andy Lowe"); | ||
296 | MODULE_DESCRIPTION("MTD map driver for NOR flash on Intel Vermilion Range"); | ||
297 | MODULE_LICENSE("GPL"); | ||
298 | MODULE_DEVICE_TABLE(pci, vr_nor_pci_ids); | ||
diff --git a/drivers/mtd/maps/lubbock-flash.c b/drivers/mtd/maps/lubbock-flash.c deleted file mode 100644 index e8560683b973..000000000000 --- a/drivers/mtd/maps/lubbock-flash.c +++ /dev/null | |||
@@ -1,168 +0,0 @@ | |||
1 | /* | ||
2 | * $Id: lubbock-flash.c,v 1.21 2005/11/07 11:14:27 gleixner Exp $ | ||
3 | * | ||
4 | * Map driver for the Lubbock developer platform. | ||
5 | * | ||
6 | * Author: Nicolas Pitre | ||
7 | * Copyright: (C) 2001 MontaVista Software Inc. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/init.h> | ||
18 | |||
19 | #include <linux/mtd/mtd.h> | ||
20 | #include <linux/mtd/map.h> | ||
21 | #include <linux/mtd/partitions.h> | ||
22 | |||
23 | #include <asm/io.h> | ||
24 | #include <asm/hardware.h> | ||
25 | #include <asm/arch/pxa-regs.h> | ||
26 | #include <asm/arch/lubbock.h> | ||
27 | #include <asm/cacheflush.h> | ||
28 | |||
29 | #define ROM_ADDR 0x00000000 | ||
30 | #define FLASH_ADDR 0x04000000 | ||
31 | |||
32 | #define WINDOW_SIZE 64*1024*1024 | ||
33 | |||
34 | static void lubbock_map_inval_cache(struct map_info *map, unsigned long from, ssize_t len) | ||
35 | { | ||
36 | flush_ioremap_region(map->phys, map->cached, from, len); | ||
37 | } | ||
38 | |||
39 | static struct map_info lubbock_maps[2] = { { | ||
40 | .size = WINDOW_SIZE, | ||
41 | .phys = 0x00000000, | ||
42 | .inval_cache = lubbock_map_inval_cache, | ||
43 | }, { | ||
44 | .size = WINDOW_SIZE, | ||
45 | .phys = 0x04000000, | ||
46 | .inval_cache = lubbock_map_inval_cache, | ||
47 | } }; | ||
48 | |||
49 | static struct mtd_partition lubbock_partitions[] = { | ||
50 | { | ||
51 | .name = "Bootloader", | ||
52 | .size = 0x00040000, | ||
53 | .offset = 0, | ||
54 | .mask_flags = MTD_WRITEABLE /* force read-only */ | ||
55 | },{ | ||
56 | .name = "Kernel", | ||
57 | .size = 0x00100000, | ||
58 | .offset = 0x00040000, | ||
59 | },{ | ||
60 | .name = "Filesystem", | ||
61 | .size = MTDPART_SIZ_FULL, | ||
62 | .offset = 0x00140000 | ||
63 | } | ||
64 | }; | ||
65 | |||
66 | static struct mtd_info *mymtds[2]; | ||
67 | static struct mtd_partition *parsed_parts[2]; | ||
68 | static int nr_parsed_parts[2]; | ||
69 | |||
70 | static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; | ||
71 | |||
72 | static int __init init_lubbock(void) | ||
73 | { | ||
74 | int flashboot = (LUB_CONF_SWITCHES & 1); | ||
75 | int ret = 0, i; | ||
76 | |||
77 | lubbock_maps[0].bankwidth = lubbock_maps[1].bankwidth = | ||
78 | (BOOT_DEF & 1) ? 2 : 4; | ||
79 | |||
80 | /* Compensate for the nROMBT switch which swaps the flash banks */ | ||
81 | printk(KERN_NOTICE "Lubbock configured to boot from %s (bank %d)\n", | ||
82 | flashboot?"Flash":"ROM", flashboot); | ||
83 | |||
84 | lubbock_maps[flashboot^1].name = "Lubbock Application Flash"; | ||
85 | lubbock_maps[flashboot].name = "Lubbock Boot ROM"; | ||
86 | |||
87 | for (i = 0; i < 2; i++) { | ||
88 | lubbock_maps[i].virt = ioremap(lubbock_maps[i].phys, WINDOW_SIZE); | ||
89 | if (!lubbock_maps[i].virt) { | ||
90 | printk(KERN_WARNING "Failed to ioremap %s\n", lubbock_maps[i].name); | ||
91 | if (!ret) | ||
92 | ret = -ENOMEM; | ||
93 | continue; | ||
94 | } | ||
95 | lubbock_maps[i].cached = ioremap_cached(lubbock_maps[i].phys, WINDOW_SIZE); | ||
96 | if (!lubbock_maps[i].cached) | ||
97 | printk(KERN_WARNING "Failed to ioremap cached %s\n", lubbock_maps[i].name); | ||
98 | simple_map_init(&lubbock_maps[i]); | ||
99 | |||
100 | printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit bankwidth)\n", | ||
101 | lubbock_maps[i].name, lubbock_maps[i].phys, | ||
102 | lubbock_maps[i].bankwidth * 8); | ||
103 | |||
104 | mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]); | ||
105 | |||
106 | if (!mymtds[i]) { | ||
107 | iounmap((void *)lubbock_maps[i].virt); | ||
108 | if (lubbock_maps[i].cached) | ||
109 | iounmap(lubbock_maps[i].cached); | ||
110 | if (!ret) | ||
111 | ret = -EIO; | ||
112 | continue; | ||
113 | } | ||
114 | mymtds[i]->owner = THIS_MODULE; | ||
115 | |||
116 | ret = parse_mtd_partitions(mymtds[i], probes, | ||
117 | &parsed_parts[i], 0); | ||
118 | |||
119 | if (ret > 0) | ||
120 | nr_parsed_parts[i] = ret; | ||
121 | } | ||
122 | |||
123 | if (!mymtds[0] && !mymtds[1]) | ||
124 | return ret; | ||
125 | |||
126 | for (i = 0; i < 2; i++) { | ||
127 | if (!mymtds[i]) { | ||
128 | printk(KERN_WARNING "%s is absent. Skipping\n", lubbock_maps[i].name); | ||
129 | } else if (nr_parsed_parts[i]) { | ||
130 | add_mtd_partitions(mymtds[i], parsed_parts[i], nr_parsed_parts[i]); | ||
131 | } else if (!i) { | ||
132 | printk("Using static partitions on %s\n", lubbock_maps[i].name); | ||
133 | add_mtd_partitions(mymtds[i], lubbock_partitions, ARRAY_SIZE(lubbock_partitions)); | ||
134 | } else { | ||
135 | printk("Registering %s as whole device\n", lubbock_maps[i].name); | ||
136 | add_mtd_device(mymtds[i]); | ||
137 | } | ||
138 | } | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static void __exit cleanup_lubbock(void) | ||
143 | { | ||
144 | int i; | ||
145 | for (i = 0; i < 2; i++) { | ||
146 | if (!mymtds[i]) | ||
147 | continue; | ||
148 | |||
149 | if (nr_parsed_parts[i] || !i) | ||
150 | del_mtd_partitions(mymtds[i]); | ||
151 | else | ||
152 | del_mtd_device(mymtds[i]); | ||
153 | |||
154 | map_destroy(mymtds[i]); | ||
155 | iounmap((void *)lubbock_maps[i].virt); | ||
156 | if (lubbock_maps[i].cached) | ||
157 | iounmap(lubbock_maps[i].cached); | ||
158 | |||
159 | kfree(parsed_parts[i]); | ||
160 | } | ||
161 | } | ||
162 | |||
163 | module_init(init_lubbock); | ||
164 | module_exit(cleanup_lubbock); | ||
165 | |||
166 | MODULE_LICENSE("GPL"); | ||
167 | MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>"); | ||
168 | MODULE_DESCRIPTION("MTD map driver for Intel Lubbock"); | ||
diff --git a/drivers/mtd/maps/mainstone-flash.c b/drivers/mtd/maps/mainstone-flash.c deleted file mode 100644 index d76487d82dcd..000000000000 --- a/drivers/mtd/maps/mainstone-flash.c +++ /dev/null | |||
@@ -1,180 +0,0 @@ | |||
1 | /* | ||
2 | * $Id: $ | ||
3 | * | ||
4 | * Map driver for the Mainstone developer platform. | ||
5 | * | ||
6 | * Author: Nicolas Pitre | ||
7 | * Copyright: (C) 2001 MontaVista Software Inc. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/init.h> | ||
18 | |||
19 | #include <linux/mtd/mtd.h> | ||
20 | #include <linux/mtd/map.h> | ||
21 | #include <linux/mtd/partitions.h> | ||
22 | |||
23 | #include <asm/io.h> | ||
24 | #include <asm/hardware.h> | ||
25 | #include <asm/arch/pxa-regs.h> | ||
26 | #include <asm/arch/mainstone.h> | ||
27 | #include <asm/cacheflush.h> | ||
28 | |||
29 | |||
30 | #define ROM_ADDR 0x00000000 | ||
31 | #define FLASH_ADDR 0x04000000 | ||
32 | |||
33 | #define WINDOW_SIZE 0x04000000 | ||
34 | |||
35 | static void mainstone_map_inval_cache(struct map_info *map, unsigned long from, | ||
36 | ssize_t len) | ||
37 | { | ||
38 | flush_ioremap_region(map->phys, map->cached, from, len); | ||
39 | } | ||
40 | |||
41 | static struct map_info mainstone_maps[2] = { { | ||
42 | .size = WINDOW_SIZE, | ||
43 | .phys = PXA_CS0_PHYS, | ||
44 | .inval_cache = mainstone_map_inval_cache, | ||
45 | }, { | ||
46 | .size = WINDOW_SIZE, | ||
47 | .phys = PXA_CS1_PHYS, | ||
48 | .inval_cache = mainstone_map_inval_cache, | ||
49 | } }; | ||
50 | |||
51 | static struct mtd_partition mainstone_partitions[] = { | ||
52 | { | ||
53 | .name = "Bootloader", | ||
54 | .size = 0x00040000, | ||
55 | .offset = 0, | ||
56 | .mask_flags = MTD_WRITEABLE /* force read-only */ | ||
57 | },{ | ||
58 | .name = "Kernel", | ||
59 | .size = 0x00400000, | ||
60 | .offset = 0x00040000, | ||
61 | },{ | ||
62 | .name = "Filesystem", | ||
63 | .size = MTDPART_SIZ_FULL, | ||
64 | .offset = 0x00440000 | ||
65 | } | ||
66 | }; | ||
67 | |||
68 | static struct mtd_info *mymtds[2]; | ||
69 | static struct mtd_partition *parsed_parts[2]; | ||
70 | static int nr_parsed_parts[2]; | ||
71 | |||
72 | static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; | ||
73 | |||
74 | static int __init init_mainstone(void) | ||
75 | { | ||
76 | int SW7 = 0; /* FIXME: get from SCR (Mst doc section 3.2.1.1) */ | ||
77 | int ret = 0, i; | ||
78 | |||
79 | mainstone_maps[0].bankwidth = (BOOT_DEF & 1) ? 2 : 4; | ||
80 | mainstone_maps[1].bankwidth = 4; | ||
81 | |||
82 | /* Compensate for SW7 which swaps the flash banks */ | ||
83 | mainstone_maps[SW7].name = "processor flash"; | ||
84 | mainstone_maps[SW7 ^ 1].name = "main board flash"; | ||
85 | |||
86 | printk(KERN_NOTICE "Mainstone configured to boot from %s\n", | ||
87 | mainstone_maps[0].name); | ||
88 | |||
89 | for (i = 0; i < 2; i++) { | ||
90 | mainstone_maps[i].virt = ioremap(mainstone_maps[i].phys, | ||
91 | WINDOW_SIZE); | ||
92 | if (!mainstone_maps[i].virt) { | ||
93 | printk(KERN_WARNING "Failed to ioremap %s\n", | ||
94 | mainstone_maps[i].name); | ||
95 | if (!ret) | ||
96 | ret = -ENOMEM; | ||
97 | continue; | ||
98 | } | ||
99 | mainstone_maps[i].cached = | ||
100 | ioremap_cached(mainstone_maps[i].phys, WINDOW_SIZE); | ||
101 | if (!mainstone_maps[i].cached) | ||
102 | printk(KERN_WARNING "Failed to ioremap cached %s\n", | ||
103 | mainstone_maps[i].name); | ||
104 | simple_map_init(&mainstone_maps[i]); | ||
105 | |||
106 | printk(KERN_NOTICE | ||
107 | "Probing %s at physical address 0x%08lx" | ||
108 | " (%d-bit bankwidth)\n", | ||
109 | mainstone_maps[i].name, mainstone_maps[i].phys, | ||
110 | mainstone_maps[i].bankwidth * 8); | ||
111 | |||
112 | mymtds[i] = do_map_probe("cfi_probe", &mainstone_maps[i]); | ||
113 | |||
114 | if (!mymtds[i]) { | ||
115 | iounmap((void *)mainstone_maps[i].virt); | ||
116 | if (mainstone_maps[i].cached) | ||
117 | iounmap(mainstone_maps[i].cached); | ||
118 | if (!ret) | ||
119 | ret = -EIO; | ||
120 | continue; | ||
121 | } | ||
122 | mymtds[i]->owner = THIS_MODULE; | ||
123 | |||
124 | ret = parse_mtd_partitions(mymtds[i], probes, | ||
125 | &parsed_parts[i], 0); | ||
126 | |||
127 | if (ret > 0) | ||
128 | nr_parsed_parts[i] = ret; | ||
129 | } | ||
130 | |||
131 | if (!mymtds[0] && !mymtds[1]) | ||
132 | return ret; | ||
133 | |||
134 | for (i = 0; i < 2; i++) { | ||
135 | if (!mymtds[i]) { | ||
136 | printk(KERN_WARNING "%s is absent. Skipping\n", | ||
137 | mainstone_maps[i].name); | ||
138 | } else if (nr_parsed_parts[i]) { | ||
139 | add_mtd_partitions(mymtds[i], parsed_parts[i], | ||
140 | nr_parsed_parts[i]); | ||
141 | } else if (!i) { | ||
142 | printk("Using static partitions on %s\n", | ||
143 | mainstone_maps[i].name); | ||
144 | add_mtd_partitions(mymtds[i], mainstone_partitions, | ||
145 | ARRAY_SIZE(mainstone_partitions)); | ||
146 | } else { | ||
147 | printk("Registering %s as whole device\n", | ||
148 | mainstone_maps[i].name); | ||
149 | add_mtd_device(mymtds[i]); | ||
150 | } | ||
151 | } | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static void __exit cleanup_mainstone(void) | ||
156 | { | ||
157 | int i; | ||
158 | for (i = 0; i < 2; i++) { | ||
159 | if (!mymtds[i]) | ||
160 | continue; | ||
161 | |||
162 | if (nr_parsed_parts[i] || !i) | ||
163 | del_mtd_partitions(mymtds[i]); | ||
164 | else | ||
165 | del_mtd_device(mymtds[i]); | ||
166 | |||
167 | map_destroy(mymtds[i]); | ||
168 | iounmap((void *)mainstone_maps[i].virt); | ||
169 | if (mainstone_maps[i].cached) | ||
170 | iounmap(mainstone_maps[i].cached); | ||
171 | kfree(parsed_parts[i]); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | module_init(init_mainstone); | ||
176 | module_exit(cleanup_mainstone); | ||
177 | |||
178 | MODULE_LICENSE("GPL"); | ||
179 | MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>"); | ||
180 | MODULE_DESCRIPTION("MTD map driver for Intel Mainstone"); | ||
diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 7b96cd02f82b..0c9b305a72e0 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c | |||
@@ -158,68 +158,11 @@ static struct notifier_block nettel_notifier_block = { | |||
158 | nettel_reboot_notifier, NULL, 0 | 158 | nettel_reboot_notifier, NULL, 0 |
159 | }; | 159 | }; |
160 | 160 | ||
161 | /* | ||
162 | * Erase the configuration file system. | ||
163 | * Used to support the software reset button. | ||
164 | */ | ||
165 | static void nettel_erasecallback(struct erase_info *done) | ||
166 | { | ||
167 | wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; | ||
168 | wake_up(wait_q); | ||
169 | } | ||
170 | |||
171 | static struct erase_info nettel_erase; | ||
172 | |||
173 | int nettel_eraseconfig(void) | ||
174 | { | ||
175 | struct mtd_info *mtd; | ||
176 | DECLARE_WAITQUEUE(wait, current); | ||
177 | wait_queue_head_t wait_q; | ||
178 | int ret; | ||
179 | |||
180 | init_waitqueue_head(&wait_q); | ||
181 | mtd = get_mtd_device(NULL, 2); | ||
182 | if (!IS_ERR(mtd)) { | ||
183 | nettel_erase.mtd = mtd; | ||
184 | nettel_erase.callback = nettel_erasecallback; | ||
185 | nettel_erase.callback = NULL; | ||
186 | nettel_erase.addr = 0; | ||
187 | nettel_erase.len = mtd->size; | ||
188 | nettel_erase.priv = (u_long) &wait_q; | ||
189 | nettel_erase.priv = 0; | ||
190 | |||
191 | set_current_state(TASK_INTERRUPTIBLE); | ||
192 | add_wait_queue(&wait_q, &wait); | ||
193 | |||
194 | ret = mtd->erase(mtd, &nettel_erase); | ||
195 | if (ret) { | ||
196 | set_current_state(TASK_RUNNING); | ||
197 | remove_wait_queue(&wait_q, &wait); | ||
198 | put_mtd_device(mtd); | ||
199 | return(ret); | ||
200 | } | ||
201 | |||
202 | schedule(); /* Wait for erase to finish. */ | ||
203 | remove_wait_queue(&wait_q, &wait); | ||
204 | |||
205 | put_mtd_device(mtd); | ||
206 | } | ||
207 | |||
208 | return(0); | ||
209 | } | ||
210 | |||
211 | #else | ||
212 | |||
213 | int nettel_eraseconfig(void) | ||
214 | { | ||
215 | return(0); | ||
216 | } | ||
217 | |||
218 | #endif | 161 | #endif |
219 | 162 | ||
220 | /****************************************************************************/ | 163 | /****************************************************************************/ |
221 | 164 | ||
222 | int __init nettel_init(void) | 165 | static int __init nettel_init(void) |
223 | { | 166 | { |
224 | volatile unsigned long *amdpar; | 167 | volatile unsigned long *amdpar; |
225 | unsigned long amdaddr, maxsize; | 168 | unsigned long amdaddr, maxsize; |
@@ -421,10 +364,6 @@ int __init nettel_init(void) | |||
421 | 364 | ||
422 | intel_mtd->owner = THIS_MODULE; | 365 | intel_mtd->owner = THIS_MODULE; |
423 | 366 | ||
424 | #ifndef CONFIG_BLK_DEV_INITRD | ||
425 | ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 1); | ||
426 | #endif | ||
427 | |||
428 | num_intel_partitions = sizeof(nettel_intel_partitions) / | 367 | num_intel_partitions = sizeof(nettel_intel_partitions) / |
429 | sizeof(nettel_intel_partitions[0]); | 368 | sizeof(nettel_intel_partitions[0]); |
430 | 369 | ||
@@ -477,7 +416,7 @@ out_unmap2: | |||
477 | 416 | ||
478 | /****************************************************************************/ | 417 | /****************************************************************************/ |
479 | 418 | ||
480 | void __exit nettel_cleanup(void) | 419 | static void __exit nettel_cleanup(void) |
481 | { | 420 | { |
482 | #ifdef CONFIG_MTD_CFI_INTELEXT | 421 | #ifdef CONFIG_MTD_CFI_INTELEXT |
483 | unregister_reboot_notifier(&nettel_notifier_block); | 422 | unregister_reboot_notifier(&nettel_notifier_block); |
diff --git a/drivers/mtd/maps/ocelot.c b/drivers/mtd/maps/ocelot.c deleted file mode 100644 index 6977963d7897..000000000000 --- a/drivers/mtd/maps/ocelot.c +++ /dev/null | |||
@@ -1,175 +0,0 @@ | |||
1 | /* | ||
2 | * $Id: ocelot.c,v 1.17 2005/11/07 11:14:27 gleixner Exp $ | ||
3 | * | ||
4 | * Flash on Momenco Ocelot | ||
5 | */ | ||
6 | |||
7 | #include <linux/module.h> | ||
8 | #include <linux/types.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <asm/io.h> | ||
12 | #include <linux/mtd/mtd.h> | ||
13 | #include <linux/mtd/map.h> | ||
14 | #include <linux/mtd/partitions.h> | ||
15 | |||
16 | #define OCELOT_PLD 0x2c000000 | ||
17 | #define FLASH_WINDOW_ADDR 0x2fc00000 | ||
18 | #define FLASH_WINDOW_SIZE 0x00080000 | ||
19 | #define FLASH_BUSWIDTH 1 | ||
20 | #define NVRAM_WINDOW_ADDR 0x2c800000 | ||
21 | #define NVRAM_WINDOW_SIZE 0x00007FF0 | ||
22 | #define NVRAM_BUSWIDTH 1 | ||
23 | |||
24 | static unsigned int cacheflush = 0; | ||
25 | |||
26 | static struct mtd_info *flash_mtd; | ||
27 | static struct mtd_info *nvram_mtd; | ||
28 | |||
29 | static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) | ||
30 | { | ||
31 | struct map_info *map = mtd->priv; | ||
32 | size_t done = 0; | ||
33 | |||
34 | /* If we use memcpy, it does word-wide writes. Even though we told the | ||
35 | GT64120A that it's an 8-bit wide region, word-wide writes don't work. | ||
36 | We end up just writing the first byte of the four to all four bytes. | ||
37 | So we have this loop instead */ | ||
38 | *retlen = len; | ||
39 | while(len) { | ||
40 | __raw_writeb(*(unsigned char *) from, map->virt + to); | ||
41 | from++; | ||
42 | to++; | ||
43 | len--; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | static struct mtd_partition *parsed_parts; | ||
48 | |||
49 | struct map_info ocelot_flash_map = { | ||
50 | .name = "Ocelot boot flash", | ||
51 | .size = FLASH_WINDOW_SIZE, | ||
52 | .bankwidth = FLASH_BUSWIDTH, | ||
53 | .phys = FLASH_WINDOW_ADDR, | ||
54 | }; | ||
55 | |||
56 | struct map_info ocelot_nvram_map = { | ||
57 | .name = "Ocelot NVRAM", | ||
58 | .size = NVRAM_WINDOW_SIZE, | ||
59 | .bankwidth = NVRAM_BUSWIDTH, | ||
60 | .phys = NVRAM_WINDOW_ADDR, | ||
61 | }; | ||
62 | |||
63 | static const char *probes[] = { "RedBoot", NULL }; | ||
64 | |||
65 | static int __init init_ocelot_maps(void) | ||
66 | { | ||
67 | void *pld; | ||
68 | int nr_parts; | ||
69 | unsigned char brd_status; | ||
70 | |||
71 | printk(KERN_INFO "Momenco Ocelot MTD mappings: Flash 0x%x at 0x%x, NVRAM 0x%x at 0x%x\n", | ||
72 | FLASH_WINDOW_SIZE, FLASH_WINDOW_ADDR, NVRAM_WINDOW_SIZE, NVRAM_WINDOW_ADDR); | ||
73 | |||
74 | /* First check whether the flash jumper is present */ | ||
75 | pld = ioremap(OCELOT_PLD, 0x10); | ||
76 | if (!pld) { | ||
77 | printk(KERN_NOTICE "Failed to ioremap Ocelot PLD\n"); | ||
78 | return -EIO; | ||
79 | } | ||
80 | brd_status = readb(pld+4); | ||
81 | iounmap(pld); | ||
82 | |||
83 | /* Now ioremap the NVRAM space */ | ||
84 | ocelot_nvram_map.virt = ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE); | ||
85 | if (!ocelot_nvram_map.virt) { | ||
86 | printk(KERN_NOTICE "Failed to ioremap Ocelot NVRAM space\n"); | ||
87 | return -EIO; | ||
88 | } | ||
89 | |||
90 | simple_map_init(&ocelot_nvram_map); | ||
91 | |||
92 | /* And do the RAM probe on it to get an MTD device */ | ||
93 | nvram_mtd = do_map_probe("map_ram", &ocelot_nvram_map); | ||
94 | if (!nvram_mtd) { | ||
95 | printk("NVRAM probe failed\n"); | ||
96 | goto fail_1; | ||
97 | } | ||
98 | nvram_mtd->owner = THIS_MODULE; | ||
99 | nvram_mtd->erasesize = 16; | ||
100 | /* Override the write() method */ | ||
101 | nvram_mtd->write = ocelot_ram_write; | ||
102 | |||
103 | /* Now map the flash space */ | ||
104 | ocelot_flash_map.virt = ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE); | ||
105 | if (!ocelot_flash_map.virt) { | ||
106 | printk(KERN_NOTICE "Failed to ioremap Ocelot flash space\n"); | ||
107 | goto fail_2; | ||
108 | } | ||
109 | /* Now the cached version */ | ||
110 | ocelot_flash_map.cached = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0); | ||
111 | |||
112 | simple_map_init(&ocelot_flash_map); | ||
113 | |||
114 | /* Only probe for flash if the write jumper is present */ | ||
115 | if (brd_status & 0x40) { | ||
116 | flash_mtd = do_map_probe("jedec", &ocelot_flash_map); | ||
117 | } else { | ||
118 | printk(KERN_NOTICE "Ocelot flash write jumper not present. Treating as ROM\n"); | ||
119 | } | ||
120 | /* If that failed or the jumper's absent, pretend it's ROM */ | ||
121 | if (!flash_mtd) { | ||
122 | flash_mtd = do_map_probe("map_rom", &ocelot_flash_map); | ||
123 | /* If we're treating it as ROM, set the erase size */ | ||
124 | if (flash_mtd) | ||
125 | flash_mtd->erasesize = 0x10000; | ||
126 | } | ||
127 | if (!flash_mtd) | ||
128 | goto fail3; | ||
129 | |||
130 | add_mtd_device(nvram_mtd); | ||
131 | |||
132 | flash_mtd->owner = THIS_MODULE; | ||
133 | nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0); | ||
134 | |||
135 | if (nr_parts > 0) | ||
136 | add_mtd_partitions(flash_mtd, parsed_parts, nr_parts); | ||
137 | else | ||
138 | add_mtd_device(flash_mtd); | ||
139 | |||
140 | return 0; | ||
141 | |||
142 | fail3: | ||
143 | iounmap((void *)ocelot_flash_map.virt); | ||
144 | if (ocelot_flash_map.cached) | ||
145 | iounmap((void *)ocelot_flash_map.cached); | ||
146 | fail_2: | ||
147 | map_destroy(nvram_mtd); | ||
148 | fail_1: | ||
149 | iounmap((void *)ocelot_nvram_map.virt); | ||
150 | |||
151 | return -ENXIO; | ||
152 | } | ||
153 | |||
154 | static void __exit cleanup_ocelot_maps(void) | ||
155 | { | ||
156 | del_mtd_device(nvram_mtd); | ||
157 | map_destroy(nvram_mtd); | ||
158 | iounmap((void *)ocelot_nvram_map.virt); | ||
159 | |||
160 | if (parsed_parts) | ||
161 | del_mtd_partitions(flash_mtd); | ||
162 | else | ||
163 | del_mtd_device(flash_mtd); | ||
164 | map_destroy(flash_mtd); | ||
165 | iounmap((void *)ocelot_flash_map.virt); | ||
166 | if (ocelot_flash_map.cached) | ||
167 | iounmap((void *)ocelot_flash_map.cached); | ||
168 | } | ||
169 | |||
170 | module_init(init_ocelot_maps); | ||
171 | module_exit(cleanup_ocelot_maps); | ||
172 | |||
173 | MODULE_LICENSE("GPL"); | ||
174 | MODULE_AUTHOR("Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>"); | ||
175 | MODULE_DESCRIPTION("MTD map driver for Momenco Ocelot board"); | ||
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index cf75a566442e..aeed9ea79714 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c | |||
@@ -232,7 +232,6 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
232 | info = kzalloc(sizeof(*info), GFP_KERNEL); | 232 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
233 | if (!info) | 233 | if (!info) |
234 | goto err_out; | 234 | goto err_out; |
235 | memset(info, 0, sizeof(*info)); | ||
236 | 235 | ||
237 | dev_set_drvdata(&dev->dev, info); | 236 | dev_set_drvdata(&dev->dev, info); |
238 | 237 | ||
diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c index 7e0377ec1c40..02bde8c982ec 100644 --- a/drivers/mtd/maps/pmcmsp-flash.c +++ b/drivers/mtd/maps/pmcmsp-flash.c | |||
@@ -73,13 +73,16 @@ int __init init_msp_flash(void) | |||
73 | return -ENXIO; | 73 | return -ENXIO; |
74 | 74 | ||
75 | printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt); | 75 | printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt); |
76 | msp_flash = (struct mtd_info **)kmalloc( | 76 | |
77 | fcnt * sizeof(struct map_info *), GFP_KERNEL); | 77 | msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL); |
78 | msp_parts = (struct mtd_partition **)kmalloc( | 78 | msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL); |
79 | fcnt * sizeof(struct mtd_partition *), GFP_KERNEL); | 79 | msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL); |
80 | msp_maps = (struct map_info *)kmalloc( | 80 | if (!msp_flash || !msp_parts || !msp_maps) { |
81 | fcnt * sizeof(struct mtd_info), GFP_KERNEL); | 81 | kfree(msp_maps); |
82 | memset(msp_maps, 0, fcnt * sizeof(struct mtd_info)); | 82 | kfree(msp_parts); |
83 | kfree(msp_flash); | ||
84 | return -ENOMEM; | ||
85 | } | ||
83 | 86 | ||
84 | /* loop over the flash devices, initializing each */ | 87 | /* loop over the flash devices, initializing each */ |
85 | for (i = 0; i < fcnt; i++) { | 88 | for (i = 0; i < fcnt; i++) { |
@@ -95,9 +98,8 @@ int __init init_msp_flash(void) | |||
95 | continue; | 98 | continue; |
96 | } | 99 | } |
97 | 100 | ||
98 | msp_parts[i] = (struct mtd_partition *)kmalloc( | 101 | msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition), |
99 | pcnt * sizeof(struct mtd_partition), GFP_KERNEL); | 102 | GFP_KERNEL); |
100 | memset(msp_parts[i], 0, pcnt * sizeof(struct mtd_partition)); | ||
101 | 103 | ||
102 | /* now initialize the devices proper */ | 104 | /* now initialize the devices proper */ |
103 | flash_name[5] = '0' + i; | 105 | flash_name[5] = '0' + i; |
diff --git a/drivers/mtd/maps/pmcmsp-ramroot.c b/drivers/mtd/maps/pmcmsp-ramroot.c index 18049bceba8d..30de5c0c09a9 100644 --- a/drivers/mtd/maps/pmcmsp-ramroot.c +++ b/drivers/mtd/maps/pmcmsp-ramroot.c | |||
@@ -79,7 +79,6 @@ static int __init init_rrmap(void) | |||
79 | rr_mtd->owner = THIS_MODULE; | 79 | rr_mtd->owner = THIS_MODULE; |
80 | 80 | ||
81 | add_mtd_device(rr_mtd); | 81 | add_mtd_device(rr_mtd); |
82 | ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, rr_mtd->index); | ||
83 | 82 | ||
84 | return 0; | 83 | return 0; |
85 | } | 84 | } |
diff --git a/drivers/mtd/maps/pq2fads.c b/drivers/mtd/maps/pq2fads.c deleted file mode 100644 index fb78d87cc130..000000000000 --- a/drivers/mtd/maps/pq2fads.c +++ /dev/null | |||
@@ -1,88 +0,0 @@ | |||
1 | /* | ||
2 | * drivers/mtd/maps/pq2fads.c | ||
3 | * | ||
4 | * Mapping for the flash SIMM on 8272ADS and PQ2FADS board | ||
5 | * | ||
6 | * Author: Vitaly Bordug <vbordug@ru.mvista.com> | ||
7 | * | ||
8 | * 2005 (c) MontaVista Software, Inc. This file is licensed under | ||
9 | * the terms of the GNU General Public License version 2. This program | ||
10 | * is licensed "as is" without any warranty of any kind, whether express | ||
11 | * or implied. | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <asm/io.h> | ||
19 | #include <asm/ppcboot.h> | ||
20 | #include <linux/mtd/mtd.h> | ||
21 | #include <linux/mtd/map.h> | ||
22 | #include <linux/mtd/partitions.h> | ||
23 | #include <linux/mtd/physmap.h> | ||
24 | |||
25 | /* | ||
26 | NOTE: bank width and interleave relative to the installed flash | ||
27 | should have been chosen within MTD_CFI_GEOMETRY options. | ||
28 | */ | ||
29 | #define PQ2FADS_BANK_WIDTH 4 | ||
30 | |||
31 | static struct mtd_partition pq2fads_partitions[] = { | ||
32 | { | ||
33 | #ifdef CONFIG_ADS8272 | ||
34 | .name = "HRCW", | ||
35 | .size = 0x40000, | ||
36 | .offset = 0, | ||
37 | .mask_flags = MTD_WRITEABLE, /* force read-only */ | ||
38 | }, { | ||
39 | .name = "User FS", | ||
40 | .size = 0x5c0000, | ||
41 | .offset = 0x40000, | ||
42 | #else | ||
43 | .name = "User FS", | ||
44 | .size = 0x600000, | ||
45 | .offset = 0, | ||
46 | #endif | ||
47 | }, { | ||
48 | .name = "uImage", | ||
49 | .size = 0x100000, | ||
50 | .offset = 0x600000, | ||
51 | .mask_flags = MTD_WRITEABLE, /* force read-only */ | ||
52 | }, { | ||
53 | .name = "bootloader", | ||
54 | .size = 0x40000, | ||
55 | .offset = 0x700000, | ||
56 | .mask_flags = MTD_WRITEABLE, /* force read-only */ | ||
57 | }, { | ||
58 | .name = "bootloader env", | ||
59 | .size = 0x40000, | ||
60 | .offset = 0x740000, | ||
61 | .mask_flags = MTD_WRITEABLE, /* force read-only */ | ||
62 | } | ||
63 | }; | ||
64 | |||
65 | |||
66 | /* pointer to MPC885ADS board info data */ | ||
67 | extern unsigned char __res[]; | ||
68 | |||
69 | static int __init init_pq2fads_mtd(void) | ||
70 | { | ||
71 | bd_t *bd = (bd_t *)__res; | ||
72 | physmap_configure(bd->bi_flashstart, bd->bi_flashsize, PQ2FADS_BANK_WIDTH, NULL); | ||
73 | |||
74 | physmap_set_partitions(pq2fads_partitions, | ||
75 | sizeof (pq2fads_partitions) / | ||
76 | sizeof (pq2fads_partitions[0])); | ||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static void __exit cleanup_pq2fads_mtd(void) | ||
81 | { | ||
82 | } | ||
83 | |||
84 | module_init(init_pq2fads_mtd); | ||
85 | module_exit(cleanup_pq2fads_mtd); | ||
86 | |||
87 | MODULE_LICENSE("GPL"); | ||
88 | MODULE_DESCRIPTION("MTD map and partitions for MPC8272ADS boards"); | ||
diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c new file mode 100644 index 000000000000..cb933ac475d5 --- /dev/null +++ b/drivers/mtd/maps/pxa2xx-flash.c | |||
@@ -0,0 +1,200 @@ | |||
1 | /* | ||
2 | * Map driver for Intel XScale PXA2xx platforms. | ||
3 | * | ||
4 | * Author: Nicolas Pitre | ||
5 | * Copyright: (C) 2001 MontaVista Software Inc. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/dma-mapping.h> | ||
18 | #include <linux/mtd/mtd.h> | ||
19 | #include <linux/mtd/map.h> | ||
20 | #include <linux/mtd/partitions.h> | ||
21 | |||
22 | #include <asm/io.h> | ||
23 | #include <asm/hardware.h> | ||
24 | |||
25 | #include <asm/mach/flash.h> | ||
26 | |||
27 | static void pxa2xx_map_inval_cache(struct map_info *map, unsigned long from, | ||
28 | ssize_t len) | ||
29 | { | ||
30 | consistent_sync((char *)map->cached + from, len, DMA_FROM_DEVICE); | ||
31 | } | ||
32 | |||
33 | struct pxa2xx_flash_info { | ||
34 | struct mtd_partition *parts; | ||
35 | int nr_parts; | ||
36 | struct mtd_info *mtd; | ||
37 | struct map_info map; | ||
38 | }; | ||
39 | |||
40 | |||
41 | static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; | ||
42 | |||
43 | |||
44 | static int __init pxa2xx_flash_probe(struct device *dev) | ||
45 | { | ||
46 | struct platform_device *pdev = to_platform_device(dev); | ||
47 | struct flash_platform_data *flash = pdev->dev.platform_data; | ||
48 | struct pxa2xx_flash_info *info; | ||
49 | struct mtd_partition *parts; | ||
50 | struct resource *res; | ||
51 | int ret = 0; | ||
52 | |||
53 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
54 | if (!res) | ||
55 | return -ENODEV; | ||
56 | |||
57 | info = kmalloc(sizeof(struct pxa2xx_flash_info), GFP_KERNEL); | ||
58 | if (!info) | ||
59 | return -ENOMEM; | ||
60 | |||
61 | memset(info, 0, sizeof(struct pxa2xx_flash_info)); | ||
62 | info->map.name = (char *) flash->name; | ||
63 | info->map.bankwidth = flash->width; | ||
64 | info->map.phys = res->start; | ||
65 | info->map.size = res->end - res->start + 1; | ||
66 | info->parts = flash->parts; | ||
67 | info->nr_parts = flash->nr_parts; | ||
68 | |||
69 | info->map.virt = ioremap(info->map.phys, info->map.size); | ||
70 | if (!info->map.virt) { | ||
71 | printk(KERN_WARNING "Failed to ioremap %s\n", | ||
72 | info->map.name); | ||
73 | return -ENOMEM; | ||
74 | } | ||
75 | info->map.cached = | ||
76 | ioremap_cached(info->map.phys, info->map.size); | ||
77 | if (!info->map.cached) | ||
78 | printk(KERN_WARNING "Failed to ioremap cached %s\n", | ||
79 | info->map.name); | ||
80 | info->map.inval_cache = pxa2xx_map_inval_cache; | ||
81 | simple_map_init(&info->map); | ||
82 | |||
83 | printk(KERN_NOTICE | ||
84 | "Probing %s at physical address 0x%08lx" | ||
85 | " (%d-bit bankwidth)\n", | ||
86 | info->map.name, (unsigned long)info->map.phys, | ||
87 | info->map.bankwidth * 8); | ||
88 | |||
89 | info->mtd = do_map_probe(flash->map_name, &info->map); | ||
90 | |||
91 | if (!info->mtd) { | ||
92 | iounmap((void *)info->map.virt); | ||
93 | if (info->map.cached) | ||
94 | iounmap(info->map.cached); | ||
95 | return -EIO; | ||
96 | } | ||
97 | info->mtd->owner = THIS_MODULE; | ||
98 | |||
99 | #ifdef CONFIG_MTD_PARTITIONS | ||
100 | ret = parse_mtd_partitions(info->mtd, probes, &parts, 0); | ||
101 | |||
102 | if (ret > 0) { | ||
103 | info->nr_parts = ret; | ||
104 | info->parts = parts; | ||
105 | } | ||
106 | #endif | ||
107 | |||
108 | if (info->nr_parts) { | ||
109 | add_mtd_partitions(info->mtd, info->parts, | ||
110 | info->nr_parts); | ||
111 | } else { | ||
112 | printk("Registering %s as whole device\n", | ||
113 | info->map.name); | ||
114 | add_mtd_device(info->mtd); | ||
115 | } | ||
116 | |||
117 | dev_set_drvdata(dev, info); | ||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | static int __exit pxa2xx_flash_remove(struct device *dev) | ||
122 | { | ||
123 | struct pxa2xx_flash_info *info = dev_get_drvdata(dev); | ||
124 | |||
125 | dev_set_drvdata(dev, NULL); | ||
126 | |||
127 | #ifdef CONFIG_MTD_PARTITIONS | ||
128 | if (info->nr_parts) | ||
129 | del_mtd_partitions(info->mtd); | ||
130 | else | ||
131 | #endif | ||
132 | del_mtd_device(info->mtd); | ||
133 | |||
134 | map_destroy(info->mtd); | ||
135 | iounmap(info->map.virt); | ||
136 | if (info->map.cached) | ||
137 | iounmap(info->map.cached); | ||
138 | kfree(info->parts); | ||
139 | kfree(info); | ||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | #ifdef CONFIG_PM | ||
144 | static int pxa2xx_flash_suspend(struct device *dev, pm_message_t state) | ||
145 | { | ||
146 | struct pxa2xx_flash_info *info = dev_get_drvdata(dev); | ||
147 | int ret = 0; | ||
148 | |||
149 | if (info->mtd && info->mtd->suspend) | ||
150 | ret = info->mtd->suspend(info->mtd); | ||
151 | return ret; | ||
152 | } | ||
153 | |||
154 | static int pxa2xx_flash_resume(struct device *dev) | ||
155 | { | ||
156 | struct pxa2xx_flash_info *info = dev_get_drvdata(dev); | ||
157 | |||
158 | if (info->mtd && info->mtd->resume) | ||
159 | info->mtd->resume(info->mtd); | ||
160 | return 0; | ||
161 | } | ||
162 | static void pxa2xx_flash_shutdown(struct device *dev) | ||
163 | { | ||
164 | struct pxa2xx_flash_info *info = dev_get_drvdata(dev); | ||
165 | |||
166 | if (info && info->mtd->suspend(info->mtd) == 0) | ||
167 | info->mtd->resume(info->mtd); | ||
168 | } | ||
169 | #else | ||
170 | #define pxa2xx_flash_suspend NULL | ||
171 | #define pxa2xx_flash_resume NULL | ||
172 | #define pxa2xx_flash_shutdown NULL | ||
173 | #endif | ||
174 | |||
175 | static struct device_driver pxa2xx_flash_driver = { | ||
176 | .name = "pxa2xx-flash", | ||
177 | .bus = &platform_bus_type, | ||
178 | .probe = pxa2xx_flash_probe, | ||
179 | .remove = __exit_p(pxa2xx_flash_remove), | ||
180 | .suspend = pxa2xx_flash_suspend, | ||
181 | .resume = pxa2xx_flash_resume, | ||
182 | .shutdown = pxa2xx_flash_shutdown, | ||
183 | }; | ||
184 | |||
185 | static int __init init_pxa2xx_flash(void) | ||
186 | { | ||
187 | return driver_register(&pxa2xx_flash_driver); | ||
188 | } | ||
189 | |||
190 | static void __exit cleanup_pxa2xx_flash(void) | ||
191 | { | ||
192 | driver_unregister(&pxa2xx_flash_driver); | ||
193 | } | ||
194 | |||
195 | module_init(init_pxa2xx_flash); | ||
196 | module_exit(cleanup_pxa2xx_flash); | ||
197 | |||
198 | MODULE_LICENSE("GPL"); | ||
199 | MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>"); | ||
200 | MODULE_DESCRIPTION("MTD map driver for Intel XScale PXA2xx"); | ||
diff --git a/drivers/mtd/maps/tqm834x.c b/drivers/mtd/maps/tqm834x.c deleted file mode 100644 index 9adc970e55e6..000000000000 --- a/drivers/mtd/maps/tqm834x.c +++ /dev/null | |||
@@ -1,286 +0,0 @@ | |||
1 | /* | ||
2 | * drivers/mtd/maps/tqm834x.c | ||
3 | * | ||
4 | * MTD mapping driver for TQM834x boards | ||
5 | * | ||
6 | * Copyright 2005 Wolfgang Denk, DENX Software Engineering, <wd@denx.de>. | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/init.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <asm/io.h> | ||
20 | #include <asm/ppcboot.h> | ||
21 | |||
22 | #include <linux/mtd/mtd.h> | ||
23 | #include <linux/mtd/map.h> | ||
24 | #include <linux/mtd/partitions.h> | ||
25 | |||
26 | #define FLASH_BANK_MAX 2 | ||
27 | |||
28 | extern unsigned char __res[]; | ||
29 | |||
30 | /* trivial struct to describe partition information */ | ||
31 | struct mtd_part_def | ||
32 | { | ||
33 | int nums; | ||
34 | unsigned char *type; | ||
35 | struct mtd_partition* mtd_part; | ||
36 | }; | ||
37 | |||
38 | static struct mtd_info* mtd_banks[FLASH_BANK_MAX]; | ||
39 | static struct map_info* map_banks[FLASH_BANK_MAX]; | ||
40 | static struct mtd_part_def part_banks[FLASH_BANK_MAX]; | ||
41 | |||
42 | static unsigned long num_banks; | ||
43 | static unsigned long start_scan_addr; | ||
44 | |||
45 | #ifdef CONFIG_MTD_PARTITIONS | ||
46 | /* | ||
47 | * The following defines the partition layout of TQM834x boards. | ||
48 | * | ||
49 | * See include/linux/mtd/partitions.h for definition of the | ||
50 | * mtd_partition structure. | ||
51 | * | ||
52 | * Assume minimal initial size of 4 MiB per bank, will be updated | ||
53 | * later in init_tqm834x_mtd() routine. | ||
54 | */ | ||
55 | |||
56 | /* Partition definition for the first flash bank which is always present. */ | ||
57 | static struct mtd_partition tqm834x_partitions_bank1[] = { | ||
58 | { | ||
59 | .name = "u-boot", /* u-boot firmware */ | ||
60 | .offset = 0x00000000, | ||
61 | .size = 0x00040000, /* 256 KiB */ | ||
62 | /*mask_flags: MTD_WRITEABLE, * force read-only */ | ||
63 | }, | ||
64 | { | ||
65 | .name = "env", /* u-boot environment */ | ||
66 | .offset = 0x00040000, | ||
67 | .size = 0x00020000, /* 128 KiB */ | ||
68 | /*mask_flags: MTD_WRITEABLE, * force read-only */ | ||
69 | }, | ||
70 | { | ||
71 | .name = "kernel", /* linux kernel image */ | ||
72 | .offset = 0x00060000, | ||
73 | .size = 0x00100000, /* 1 MiB */ | ||
74 | /*mask_flags: MTD_WRITEABLE, * force read-only */ | ||
75 | }, | ||
76 | { | ||
77 | .name = "initrd", /* ramdisk image */ | ||
78 | .offset = 0x00160000, | ||
79 | .size = 0x00200000, /* 2 MiB */ | ||
80 | }, | ||
81 | { | ||
82 | .name = "user", /* user data */ | ||
83 | .offset = 0x00360000, | ||
84 | .size = 0x000a0000, /* remaining space */ | ||
85 | /* NOTE: this parttion size is re-calcated in */ | ||
86 | /* init_tqm834x_mtd() to cover actual remaining space. */ | ||
87 | }, | ||
88 | }; | ||
89 | |||
90 | /* Partition definition for the second flash bank which may be present on some | ||
91 | * TQM834x boards. | ||
92 | */ | ||
93 | static struct mtd_partition tqm834x_partitions_bank2[] = { | ||
94 | { | ||
95 | .name = "jffs2", /* jffs2 filesystem */ | ||
96 | .offset = 0x00000000, | ||
97 | .size = 0x00400000, /* whole device */ | ||
98 | /* NOTE: this parttion size is re-calcated in */ | ||
99 | /* init_tqm834x_mtd() to cover actual device size. */ | ||
100 | }, | ||
101 | }; | ||
102 | |||
103 | #endif /* CONFIG_MTD_PARTITIONS */ | ||
104 | |||
105 | static int __init init_tqm834x_mtd(void) | ||
106 | { | ||
107 | int idx = 0, ret = 0; | ||
108 | unsigned long flash_addr, flash_size, mtd_size = 0; | ||
109 | |||
110 | /* pointer to TQM834x board info data */ | ||
111 | bd_t *bd = (bd_t *)__res; | ||
112 | #ifdef CONFIG_MTD_CMDLINE_PARTS | ||
113 | int n; | ||
114 | char mtdid[4]; | ||
115 | const char *part_probes[] = { "cmdlinepart", NULL }; | ||
116 | #endif | ||
117 | |||
118 | flash_addr = bd->bi_flashstart; | ||
119 | flash_size = bd->bi_flashsize; | ||
120 | |||
121 | /* request maximum flash size address space */ | ||
122 | start_scan_addr = (unsigned long)ioremap(flash_addr, flash_size); | ||
123 | if (!start_scan_addr) { | ||
124 | printk("%s: Failed to ioremap address: 0x%lx\n", | ||
125 | __FUNCTION__, flash_addr); | ||
126 | return -EIO; | ||
127 | } | ||
128 | |||
129 | for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { | ||
130 | if (mtd_size >= flash_size) | ||
131 | break; | ||
132 | |||
133 | pr_debug("%s: chip probing count %d\n", __FUNCTION__, idx); | ||
134 | |||
135 | map_banks[idx] = kzalloc(sizeof(struct map_info), GFP_KERNEL); | ||
136 | if (map_banks[idx] == NULL) { | ||
137 | ret = -ENOMEM; | ||
138 | goto error_mem; | ||
139 | } | ||
140 | map_banks[idx]->name = kzalloc(16, GFP_KERNEL); | ||
141 | if (map_banks[idx]->name == NULL) { | ||
142 | ret = -ENOMEM; | ||
143 | goto error_mem; | ||
144 | } | ||
145 | |||
146 | sprintf(map_banks[idx]->name, "TQM834x-%d", idx); | ||
147 | map_banks[idx]->size = flash_size; | ||
148 | map_banks[idx]->bankwidth = 4; | ||
149 | |||
150 | simple_map_init(map_banks[idx]); | ||
151 | |||
152 | map_banks[idx]->virt = (void __iomem *) | ||
153 | (start_scan_addr + ((idx > 0) ? | ||
154 | (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0)); | ||
155 | map_banks[idx]->phys = | ||
156 | flash_addr + ((idx > 0) ? | ||
157 | (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0); | ||
158 | |||
159 | /* start to probe flash chips */ | ||
160 | mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]); | ||
161 | if (mtd_banks[idx]) { | ||
162 | mtd_banks[idx]->owner = THIS_MODULE; | ||
163 | mtd_size += mtd_banks[idx]->size; | ||
164 | num_banks++; | ||
165 | pr_debug("%s: bank %ld, name: %s, size: %d bytes \n", | ||
166 | __FUNCTION__, num_banks, | ||
167 | mtd_banks[idx]->name, mtd_banks[idx]->size); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | /* no supported flash chips found */ | ||
172 | if (!num_banks) { | ||
173 | printk("TQM834x: No supported flash chips found!\n"); | ||
174 | ret = -ENXIO; | ||
175 | goto error_mem; | ||
176 | } | ||
177 | |||
178 | #ifdef CONFIG_MTD_PARTITIONS | ||
179 | /* | ||
180 | * Select static partition definitions | ||
181 | */ | ||
182 | n = ARRAY_SIZE(tqm834x_partitions_bank1); | ||
183 | part_banks[0].mtd_part = tqm834x_partitions_bank1; | ||
184 | part_banks[0].type = "static image bank1"; | ||
185 | part_banks[0].nums = n; | ||
186 | |||
187 | /* update last partition size to cover actual remaining space */ | ||
188 | tqm834x_partitions_bank1[n - 1].size = | ||
189 | mtd_banks[0]->size - | ||
190 | tqm834x_partitions_bank1[n - 1].offset; | ||
191 | |||
192 | /* check if we have second bank? */ | ||
193 | if (num_banks == 2) { | ||
194 | n = ARRAY_SIZE(tqm834x_partitions_bank2); | ||
195 | part_banks[1].mtd_part = tqm834x_partitions_bank2; | ||
196 | part_banks[1].type = "static image bank2"; | ||
197 | part_banks[1].nums = n; | ||
198 | |||
199 | /* update last partition size to cover actual remaining space */ | ||
200 | tqm834x_partitions_bank2[n - 1].size = | ||
201 | mtd_banks[1]->size - | ||
202 | tqm834x_partitions_bank2[n - 1].offset; | ||
203 | } | ||
204 | |||
205 | for(idx = 0; idx < num_banks ; idx++) { | ||
206 | #ifdef CONFIG_MTD_CMDLINE_PARTS | ||
207 | sprintf(mtdid, "%d", idx); | ||
208 | n = parse_mtd_partitions(mtd_banks[idx], | ||
209 | part_probes, | ||
210 | &part_banks[idx].mtd_part, | ||
211 | 0); | ||
212 | pr_debug("%s: %d command line partitions on bank %s\n", | ||
213 | __FUNCTION__, n, mtdid); | ||
214 | if (n > 0) { | ||
215 | part_banks[idx].type = "command line"; | ||
216 | part_banks[idx].nums = n; | ||
217 | } | ||
218 | #endif /* CONFIG_MTD_CMDLINE_PARTS */ | ||
219 | if (part_banks[idx].nums == 0) { | ||
220 | printk(KERN_NOTICE | ||
221 | "TQM834x flash bank %d: no partition info " | ||
222 | "available, registering whole device\n", idx); | ||
223 | add_mtd_device(mtd_banks[idx]); | ||
224 | } else { | ||
225 | printk(KERN_NOTICE | ||
226 | "TQM834x flash bank %d: Using %s partition " | ||
227 | "definition\n", idx, part_banks[idx].type); | ||
228 | add_mtd_partitions(mtd_banks[idx], | ||
229 | part_banks[idx].mtd_part, | ||
230 | part_banks[idx].nums); | ||
231 | } | ||
232 | } | ||
233 | #else /* ! CONFIG_MTD_PARTITIONS */ | ||
234 | printk(KERN_NOTICE "TQM834x flash: registering %d flash banks " | ||
235 | "at once\n", num_banks); | ||
236 | |||
237 | for(idx = 0 ; idx < num_banks ; idx++) | ||
238 | add_mtd_device(mtd_banks[idx]); | ||
239 | |||
240 | #endif /* CONFIG_MTD_PARTITIONS */ | ||
241 | |||
242 | return 0; | ||
243 | error_mem: | ||
244 | for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { | ||
245 | if (map_banks[idx] != NULL) { | ||
246 | if (map_banks[idx]->name != NULL) { | ||
247 | kfree(map_banks[idx]->name); | ||
248 | map_banks[idx]->name = NULL; | ||
249 | } | ||
250 | kfree(map_banks[idx]); | ||
251 | map_banks[idx] = NULL; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | iounmap((void *)start_scan_addr); | ||
256 | |||
257 | return ret; | ||
258 | } | ||
259 | |||
260 | static void __exit cleanup_tqm834x_mtd(void) | ||
261 | { | ||
262 | unsigned int idx = 0; | ||
263 | for(idx = 0 ; idx < num_banks ; idx++) { | ||
264 | /* destroy mtd_info previously allocated */ | ||
265 | if (mtd_banks[idx]) { | ||
266 | del_mtd_partitions(mtd_banks[idx]); | ||
267 | map_destroy(mtd_banks[idx]); | ||
268 | } | ||
269 | |||
270 | /* release map_info not used anymore */ | ||
271 | kfree(map_banks[idx]->name); | ||
272 | kfree(map_banks[idx]); | ||
273 | } | ||
274 | |||
275 | if (start_scan_addr) { | ||
276 | iounmap((void *)start_scan_addr); | ||
277 | start_scan_addr = 0; | ||
278 | } | ||
279 | } | ||
280 | |||
281 | module_init(init_tqm834x_mtd); | ||
282 | module_exit(cleanup_tqm834x_mtd); | ||
283 | |||
284 | MODULE_LICENSE("GPL"); | ||
285 | MODULE_AUTHOR("Wolfgang Denk <wd@denx.de>"); | ||
286 | MODULE_DESCRIPTION("MTD map driver for TQM834x boards"); | ||
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index ef89780eb9d6..74d9d30edabd 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c | |||
@@ -24,10 +24,9 @@ | |||
24 | #include <linux/kthread.h> | 24 | #include <linux/kthread.h> |
25 | #include <asm/uaccess.h> | 25 | #include <asm/uaccess.h> |
26 | 26 | ||
27 | static LIST_HEAD(blktrans_majors); | 27 | #include "mtdcore.h" |
28 | 28 | ||
29 | extern struct mutex mtd_table_mutex; | 29 | static LIST_HEAD(blktrans_majors); |
30 | extern struct mtd_info *mtd_table[]; | ||
31 | 30 | ||
32 | struct mtd_blkcore_priv { | 31 | struct mtd_blkcore_priv { |
33 | struct task_struct *thread; | 32 | struct task_struct *thread; |
@@ -202,7 +201,7 @@ static int blktrans_ioctl(struct inode *inode, struct file *file, | |||
202 | } | 201 | } |
203 | } | 202 | } |
204 | 203 | ||
205 | struct block_device_operations mtd_blktrans_ops = { | 204 | static struct block_device_operations mtd_blktrans_ops = { |
206 | .owner = THIS_MODULE, | 205 | .owner = THIS_MODULE, |
207 | .open = blktrans_open, | 206 | .open = blktrans_open, |
208 | .release = blktrans_release, | 207 | .release = blktrans_release, |
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index d091b2430b48..22ed96c4b7bd 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c | |||
@@ -136,7 +136,8 @@ static int mtd_close(struct inode *inode, struct file *file) | |||
136 | 136 | ||
137 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); | 137 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); |
138 | 138 | ||
139 | if (mtd->sync) | 139 | /* Only sync if opened RW */ |
140 | if ((file->f_mode & 2) && mtd->sync) | ||
140 | mtd->sync(mtd); | 141 | mtd->sync(mtd); |
141 | 142 | ||
142 | put_mtd_device(mtd); | 143 | put_mtd_device(mtd); |
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 41844ea02462..96be7ef62f35 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c | |||
@@ -178,7 +178,7 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs, | |||
178 | 178 | ||
179 | /* Check alignment */ | 179 | /* Check alignment */ |
180 | if (mtd->writesize > 1) { | 180 | if (mtd->writesize > 1) { |
181 | loff_t __to = to; | 181 | uint64_t __to = to; |
182 | if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize)) | 182 | if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize)) |
183 | return -EINVAL; | 183 | return -EINVAL; |
184 | } | 184 | } |
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index c153b64a8300..6c2645e28371 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c | |||
@@ -22,6 +22,8 @@ | |||
22 | 22 | ||
23 | #include <linux/mtd/mtd.h> | 23 | #include <linux/mtd/mtd.h> |
24 | 24 | ||
25 | #include "mtdcore.h" | ||
26 | |||
25 | /* These are exported solely for the purpose of mtd_blkdevs.c. You | 27 | /* These are exported solely for the purpose of mtd_blkdevs.c. You |
26 | should not use them for _anything_ else */ | 28 | should not use them for _anything_ else */ |
27 | DEFINE_MUTEX(mtd_table_mutex); | 29 | DEFINE_MUTEX(mtd_table_mutex); |
diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h new file mode 100644 index 000000000000..a33251f4b872 --- /dev/null +++ b/drivers/mtd/mtdcore.h | |||
@@ -0,0 +1,11 @@ | |||
1 | /* linux/drivers/mtd/mtdcore.h | ||
2 | * | ||
3 | * Header file for driver private mtdcore exports | ||
4 | * | ||
5 | */ | ||
6 | |||
7 | /* These are exported solely for the purpose of mtd_blkdevs.c. You | ||
8 | should not use them for _anything_ else */ | ||
9 | |||
10 | extern struct mutex mtd_table_mutex; | ||
11 | extern struct mtd_info *mtd_table[MAX_MTD_DEVICES]; | ||
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c new file mode 100644 index 000000000000..f8af627f0b98 --- /dev/null +++ b/drivers/mtd/mtdoops.c | |||
@@ -0,0 +1,376 @@ | |||
1 | /* | ||
2 | * MTD Oops/Panic logger | ||
3 | * | ||
4 | * Copyright (C) 2007 Nokia Corporation. All rights reserved. | ||
5 | * | ||
6 | * Author: Richard Purdie <rpurdie@openedhand.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * version 2 as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | ||
20 | * 02110-1301 USA | ||
21 | * | ||
22 | */ | ||
23 | |||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/console.h> | ||
27 | #include <linux/vmalloc.h> | ||
28 | #include <linux/workqueue.h> | ||
29 | #include <linux/sched.h> | ||
30 | #include <linux/wait.h> | ||
31 | #include <linux/mtd/mtd.h> | ||
32 | |||
33 | #define OOPS_PAGE_SIZE 4096 | ||
34 | |||
35 | static struct mtdoops_context { | ||
36 | int mtd_index; | ||
37 | struct work_struct work; | ||
38 | struct mtd_info *mtd; | ||
39 | int oops_pages; | ||
40 | int nextpage; | ||
41 | int nextcount; | ||
42 | |||
43 | void *oops_buf; | ||
44 | int ready; | ||
45 | int writecount; | ||
46 | } oops_cxt; | ||
47 | |||
48 | static void mtdoops_erase_callback(struct erase_info *done) | ||
49 | { | ||
50 | wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; | ||
51 | wake_up(wait_q); | ||
52 | } | ||
53 | |||
54 | static int mtdoops_erase_block(struct mtd_info *mtd, int offset) | ||
55 | { | ||
56 | struct erase_info erase; | ||
57 | DECLARE_WAITQUEUE(wait, current); | ||
58 | wait_queue_head_t wait_q; | ||
59 | int ret; | ||
60 | |||
61 | init_waitqueue_head(&wait_q); | ||
62 | erase.mtd = mtd; | ||
63 | erase.callback = mtdoops_erase_callback; | ||
64 | erase.addr = offset; | ||
65 | if (mtd->erasesize < OOPS_PAGE_SIZE) | ||
66 | erase.len = OOPS_PAGE_SIZE; | ||
67 | else | ||
68 | erase.len = mtd->erasesize; | ||
69 | erase.priv = (u_long)&wait_q; | ||
70 | |||
71 | set_current_state(TASK_INTERRUPTIBLE); | ||
72 | add_wait_queue(&wait_q, &wait); | ||
73 | |||
74 | ret = mtd->erase(mtd, &erase); | ||
75 | if (ret) { | ||
76 | set_current_state(TASK_RUNNING); | ||
77 | remove_wait_queue(&wait_q, &wait); | ||
78 | printk (KERN_WARNING "mtdoops: erase of region [0x%x, 0x%x] " | ||
79 | "on \"%s\" failed\n", | ||
80 | erase.addr, erase.len, mtd->name); | ||
81 | return ret; | ||
82 | } | ||
83 | |||
84 | schedule(); /* Wait for erase to finish. */ | ||
85 | remove_wait_queue(&wait_q, &wait); | ||
86 | |||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | static int mtdoops_inc_counter(struct mtdoops_context *cxt) | ||
91 | { | ||
92 | struct mtd_info *mtd = cxt->mtd; | ||
93 | size_t retlen; | ||
94 | u32 count; | ||
95 | int ret; | ||
96 | |||
97 | cxt->nextpage++; | ||
98 | if (cxt->nextpage > cxt->oops_pages) | ||
99 | cxt->nextpage = 0; | ||
100 | cxt->nextcount++; | ||
101 | if (cxt->nextcount == 0xffffffff) | ||
102 | cxt->nextcount = 0; | ||
103 | |||
104 | ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4, | ||
105 | &retlen, (u_char *) &count); | ||
106 | if ((retlen != 4) || (ret < 0)) { | ||
107 | printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)" | ||
108 | ", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE, | ||
109 | retlen, ret); | ||
110 | return 1; | ||
111 | } | ||
112 | |||
113 | /* See if we need to erase the next block */ | ||
114 | if (count != 0xffffffff) | ||
115 | return 1; | ||
116 | |||
117 | printk(KERN_DEBUG "mtdoops: Ready %d, %d (no erase)\n", | ||
118 | cxt->nextpage, cxt->nextcount); | ||
119 | cxt->ready = 1; | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static void mtdoops_prepare(struct mtdoops_context *cxt) | ||
124 | { | ||
125 | struct mtd_info *mtd = cxt->mtd; | ||
126 | int i = 0, j, ret, mod; | ||
127 | |||
128 | /* We were unregistered */ | ||
129 | if (!mtd) | ||
130 | return; | ||
131 | |||
132 | mod = (cxt->nextpage * OOPS_PAGE_SIZE) % mtd->erasesize; | ||
133 | if (mod != 0) { | ||
134 | cxt->nextpage = cxt->nextpage + ((mtd->erasesize - mod) / OOPS_PAGE_SIZE); | ||
135 | if (cxt->nextpage > cxt->oops_pages) | ||
136 | cxt->nextpage = 0; | ||
137 | } | ||
138 | |||
139 | while (mtd->block_isbad && | ||
140 | mtd->block_isbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE)) { | ||
141 | badblock: | ||
142 | printk(KERN_WARNING "mtdoops: Bad block at %08x\n", | ||
143 | cxt->nextpage * OOPS_PAGE_SIZE); | ||
144 | i++; | ||
145 | cxt->nextpage = cxt->nextpage + (mtd->erasesize / OOPS_PAGE_SIZE); | ||
146 | if (cxt->nextpage > cxt->oops_pages) | ||
147 | cxt->nextpage = 0; | ||
148 | if (i == (cxt->oops_pages / (mtd->erasesize / OOPS_PAGE_SIZE))) { | ||
149 | printk(KERN_ERR "mtdoops: All blocks bad!\n"); | ||
150 | return; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | for (j = 0, ret = -1; (j < 3) && (ret < 0); j++) | ||
155 | ret = mtdoops_erase_block(mtd, cxt->nextpage * OOPS_PAGE_SIZE); | ||
156 | |||
157 | if (ret < 0) { | ||
158 | if (mtd->block_markbad) | ||
159 | mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE); | ||
160 | goto badblock; | ||
161 | } | ||
162 | |||
163 | printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount); | ||
164 | |||
165 | cxt->ready = 1; | ||
166 | } | ||
167 | |||
168 | static void mtdoops_workfunc(struct work_struct *work) | ||
169 | { | ||
170 | struct mtdoops_context *cxt = | ||
171 | container_of(work, struct mtdoops_context, work); | ||
172 | |||
173 | mtdoops_prepare(cxt); | ||
174 | } | ||
175 | |||
176 | static int find_next_position(struct mtdoops_context *cxt) | ||
177 | { | ||
178 | struct mtd_info *mtd = cxt->mtd; | ||
179 | int page, maxpos = 0; | ||
180 | u32 count, maxcount = 0xffffffff; | ||
181 | size_t retlen; | ||
182 | |||
183 | for (page = 0; page < cxt->oops_pages; page++) { | ||
184 | mtd->read(mtd, page * OOPS_PAGE_SIZE, 4, &retlen, (u_char *) &count); | ||
185 | if (count == 0xffffffff) | ||
186 | continue; | ||
187 | if (maxcount == 0xffffffff) { | ||
188 | maxcount = count; | ||
189 | maxpos = page; | ||
190 | } else if ((count < 0x40000000) && (maxcount > 0xc0000000)) { | ||
191 | maxcount = count; | ||
192 | maxpos = page; | ||
193 | } else if ((count > maxcount) && (count < 0xc0000000)) { | ||
194 | maxcount = count; | ||
195 | maxpos = page; | ||
196 | } else if ((count > maxcount) && (count > 0xc0000000) | ||
197 | && (maxcount > 0x80000000)) { | ||
198 | maxcount = count; | ||
199 | maxpos = page; | ||
200 | } | ||
201 | } | ||
202 | if (maxcount == 0xffffffff) { | ||
203 | cxt->nextpage = 0; | ||
204 | cxt->nextcount = 1; | ||
205 | cxt->ready = 1; | ||
206 | printk(KERN_DEBUG "mtdoops: Ready %d, %d (first init)\n", | ||
207 | cxt->nextpage, cxt->nextcount); | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | cxt->nextpage = maxpos; | ||
212 | cxt->nextcount = maxcount; | ||
213 | |||
214 | return mtdoops_inc_counter(cxt); | ||
215 | } | ||
216 | |||
217 | |||
218 | static void mtdoops_notify_add(struct mtd_info *mtd) | ||
219 | { | ||
220 | struct mtdoops_context *cxt = &oops_cxt; | ||
221 | int ret; | ||
222 | |||
223 | if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) | ||
224 | return; | ||
225 | |||
226 | if (mtd->size < (mtd->erasesize * 2)) { | ||
227 | printk(KERN_ERR "MTD partition %d not big enough for mtdoops\n", | ||
228 | mtd->index); | ||
229 | return; | ||
230 | } | ||
231 | |||
232 | cxt->mtd = mtd; | ||
233 | cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE; | ||
234 | |||
235 | ret = find_next_position(cxt); | ||
236 | if (ret == 1) | ||
237 | mtdoops_prepare(cxt); | ||
238 | |||
239 | printk(KERN_DEBUG "mtdoops: Attached to MTD device %d\n", mtd->index); | ||
240 | } | ||
241 | |||
242 | static void mtdoops_notify_remove(struct mtd_info *mtd) | ||
243 | { | ||
244 | struct mtdoops_context *cxt = &oops_cxt; | ||
245 | |||
246 | if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) | ||
247 | return; | ||
248 | |||
249 | cxt->mtd = NULL; | ||
250 | flush_scheduled_work(); | ||
251 | } | ||
252 | |||
253 | static void mtdoops_console_sync(void) | ||
254 | { | ||
255 | struct mtdoops_context *cxt = &oops_cxt; | ||
256 | struct mtd_info *mtd = cxt->mtd; | ||
257 | size_t retlen; | ||
258 | int ret; | ||
259 | |||
260 | if (!cxt->ready || !mtd) | ||
261 | return; | ||
262 | |||
263 | if (cxt->writecount == 0) | ||
264 | return; | ||
265 | |||
266 | if (cxt->writecount < OOPS_PAGE_SIZE) | ||
267 | memset(cxt->oops_buf + cxt->writecount, 0xff, | ||
268 | OOPS_PAGE_SIZE - cxt->writecount); | ||
269 | |||
270 | ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, | ||
271 | OOPS_PAGE_SIZE, &retlen, cxt->oops_buf); | ||
272 | cxt->ready = 0; | ||
273 | cxt->writecount = 0; | ||
274 | |||
275 | if ((retlen != OOPS_PAGE_SIZE) || (ret < 0)) | ||
276 | printk(KERN_ERR "mtdoops: Write failure at %d (%td of %d written), err %d.\n", | ||
277 | cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); | ||
278 | |||
279 | ret = mtdoops_inc_counter(cxt); | ||
280 | if (ret == 1) | ||
281 | schedule_work(&cxt->work); | ||
282 | } | ||
283 | |||
284 | static void | ||
285 | mtdoops_console_write(struct console *co, const char *s, unsigned int count) | ||
286 | { | ||
287 | struct mtdoops_context *cxt = co->data; | ||
288 | struct mtd_info *mtd = cxt->mtd; | ||
289 | int i; | ||
290 | |||
291 | if (!oops_in_progress) { | ||
292 | mtdoops_console_sync(); | ||
293 | return; | ||
294 | } | ||
295 | |||
296 | if (!cxt->ready || !mtd) | ||
297 | return; | ||
298 | |||
299 | if (cxt->writecount == 0) { | ||
300 | u32 *stamp = cxt->oops_buf; | ||
301 | *stamp = cxt->nextcount; | ||
302 | cxt->writecount = 4; | ||
303 | } | ||
304 | |||
305 | if ((count + cxt->writecount) > OOPS_PAGE_SIZE) | ||
306 | count = OOPS_PAGE_SIZE - cxt->writecount; | ||
307 | |||
308 | for (i = 0; i < count; i++, s++) | ||
309 | *((char *)(cxt->oops_buf) + cxt->writecount + i) = *s; | ||
310 | |||
311 | cxt->writecount = cxt->writecount + count; | ||
312 | } | ||
313 | |||
314 | static int __init mtdoops_console_setup(struct console *co, char *options) | ||
315 | { | ||
316 | struct mtdoops_context *cxt = co->data; | ||
317 | |||
318 | if (cxt->mtd_index != -1) | ||
319 | return -EBUSY; | ||
320 | if (co->index == -1) | ||
321 | return -EINVAL; | ||
322 | |||
323 | cxt->mtd_index = co->index; | ||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | static struct mtd_notifier mtdoops_notifier = { | ||
328 | .add = mtdoops_notify_add, | ||
329 | .remove = mtdoops_notify_remove, | ||
330 | }; | ||
331 | |||
332 | static struct console mtdoops_console = { | ||
333 | .name = "ttyMTD", | ||
334 | .write = mtdoops_console_write, | ||
335 | .setup = mtdoops_console_setup, | ||
336 | .unblank = mtdoops_console_sync, | ||
337 | .flags = CON_PRINTBUFFER, | ||
338 | .index = -1, | ||
339 | .data = &oops_cxt, | ||
340 | }; | ||
341 | |||
342 | static int __init mtdoops_console_init(void) | ||
343 | { | ||
344 | struct mtdoops_context *cxt = &oops_cxt; | ||
345 | |||
346 | cxt->mtd_index = -1; | ||
347 | cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE); | ||
348 | |||
349 | if (!cxt->oops_buf) { | ||
350 | printk(KERN_ERR "Failed to allocate oops buffer workspace\n"); | ||
351 | return -ENOMEM; | ||
352 | } | ||
353 | |||
354 | INIT_WORK(&cxt->work, mtdoops_workfunc); | ||
355 | |||
356 | register_console(&mtdoops_console); | ||
357 | register_mtd_user(&mtdoops_notifier); | ||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | static void __exit mtdoops_console_exit(void) | ||
362 | { | ||
363 | struct mtdoops_context *cxt = &oops_cxt; | ||
364 | |||
365 | unregister_mtd_user(&mtdoops_notifier); | ||
366 | unregister_console(&mtdoops_console); | ||
367 | vfree(cxt->oops_buf); | ||
368 | } | ||
369 | |||
370 | |||
371 | subsys_initcall(mtdoops_console_init); | ||
372 | module_exit(mtdoops_console_exit); | ||
373 | |||
374 | MODULE_LICENSE("GPL"); | ||
375 | MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>"); | ||
376 | MODULE_DESCRIPTION("MTD Oops/Panic console logger/driver"); | ||
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index f1d60b6f048e..8f9c3baeb38e 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
@@ -91,6 +91,25 @@ config MTD_NAND_AU1550 | |||
91 | This enables the driver for the NAND flash controller on the | 91 | This enables the driver for the NAND flash controller on the |
92 | AMD/Alchemy 1550 SOC. | 92 | AMD/Alchemy 1550 SOC. |
93 | 93 | ||
94 | config MTD_NAND_BF5XX | ||
95 | tristate "Blackfin on-chip NAND Flash Controller driver" | ||
96 | depends on BF54x && MTD_NAND | ||
97 | help | ||
98 | This enables the Blackfin on-chip NAND flash controller | ||
99 | |||
100 | No board specific support is done by this driver, each board | ||
101 | must advertise a platform_device for the driver to attach. | ||
102 | |||
103 | This driver can also be built as a module. If so, the module | ||
104 | will be called bf5xx-nand. | ||
105 | |||
106 | config MTD_NAND_BF5XX_HWECC | ||
107 | bool "BF5XX NAND Hardware ECC" | ||
108 | depends on MTD_NAND_BF5XX | ||
109 | help | ||
110 | Enable the use of the BF5XX's internal ECC generator when | ||
111 | using NAND. | ||
112 | |||
94 | config MTD_NAND_RTC_FROM4 | 113 | config MTD_NAND_RTC_FROM4 |
95 | tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)" | 114 | tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)" |
96 | depends on SH_SOLUTION_ENGINE | 115 | depends on SH_SOLUTION_ENGINE |
@@ -134,10 +153,10 @@ config MTD_NAND_S3C2410_HWECC | |||
134 | 153 | ||
135 | config MTD_NAND_NDFC | 154 | config MTD_NAND_NDFC |
136 | tristate "NDFC NanD Flash Controller" | 155 | tristate "NDFC NanD Flash Controller" |
137 | depends on 44x | 156 | depends on 4xx && !PPC_MERGE |
138 | select MTD_NAND_ECC_SMC | 157 | select MTD_NAND_ECC_SMC |
139 | help | 158 | help |
140 | NDFC Nand Flash Controllers are integrated in EP44x SoCs | 159 | NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs |
141 | 160 | ||
142 | config MTD_NAND_S3C2410_CLKSTOP | 161 | config MTD_NAND_S3C2410_CLKSTOP |
143 | bool "S3C2410 NAND IDLE clock stop" | 162 | bool "S3C2410 NAND IDLE clock stop" |
@@ -237,7 +256,7 @@ config MTD_NAND_CAFE | |||
237 | select REED_SOLOMON | 256 | select REED_SOLOMON |
238 | select REED_SOLOMON_DEC16 | 257 | select REED_SOLOMON_DEC16 |
239 | help | 258 | help |
240 | Use NAND flash attached to the CAFÉ chip designed for the $100 | 259 | Use NAND flash attached to the CAFÉ chip designed for the OLPC |
241 | laptop. | 260 | laptop. |
242 | 261 | ||
243 | config MTD_NAND_CS553X | 262 | config MTD_NAND_CS553X |
@@ -280,5 +299,11 @@ config MTD_NAND_PLATFORM | |||
280 | devices. You will need to provide platform-specific functions | 299 | devices. You will need to provide platform-specific functions |
281 | via platform_data. | 300 | via platform_data. |
282 | 301 | ||
302 | config MTD_ALAUDA | ||
303 | tristate "MTD driver for Olympus MAUSB-10 and Fijufilm DPC-R1" | ||
304 | depends on MTD_NAND && USB | ||
305 | help | ||
306 | These two (and possibly other) Alauda-based cardreaders for | ||
307 | SmartMedia and xD allow raw flash access. | ||
283 | 308 | ||
284 | endif # MTD_NAND | 309 | endif # MTD_NAND |
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index edba1db14bfa..3ad6c0165da3 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile | |||
@@ -13,6 +13,7 @@ obj-$(CONFIG_MTD_NAND_TOTO) += toto.o | |||
13 | obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o | 13 | obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o |
14 | obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o | 14 | obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o |
15 | obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o | 15 | obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o |
16 | obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o | ||
16 | obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o | 17 | obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o |
17 | obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o | 18 | obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o |
18 | obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o | 19 | obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o |
@@ -27,5 +28,6 @@ obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o | |||
27 | obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o | 28 | obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o |
28 | obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o | 29 | obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o |
29 | obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o | 30 | obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o |
31 | obj-$(CONFIG_MTD_ALAUDA) += alauda.o | ||
30 | 32 | ||
31 | nand-objs := nand_base.o nand_bbt.o | 33 | nand-objs := nand_base.o nand_bbt.o |
diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c new file mode 100644 index 000000000000..257937cd99bf --- /dev/null +++ b/drivers/mtd/nand/alauda.c | |||
@@ -0,0 +1,742 @@ | |||
1 | /* | ||
2 | * MTD driver for Alauda chips | ||
3 | * | ||
4 | * Copyright (C) 2007 Joern Engel <joern@logfs.org> | ||
5 | * | ||
6 | * Based on drivers/usb/usb-skeleton.c which is: | ||
7 | * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) | ||
8 | * and on drivers/usb/storage/alauda.c, which is: | ||
9 | * (c) 2005 Daniel Drake <dsd@gentoo.org> | ||
10 | * | ||
11 | * Idea and initial work by Arnd Bergmann <arnd@arndb.de> | ||
12 | */ | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/kref.h> | ||
19 | #include <linux/usb.h> | ||
20 | #include <linux/mutex.h> | ||
21 | #include <linux/mtd/mtd.h> | ||
22 | #include <linux/mtd/nand_ecc.h> | ||
23 | |||
24 | /* Control commands */ | ||
25 | #define ALAUDA_GET_XD_MEDIA_STATUS 0x08 | ||
26 | #define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a | ||
27 | #define ALAUDA_GET_XD_MEDIA_SIG 0x86 | ||
28 | |||
29 | /* Common prefix */ | ||
30 | #define ALAUDA_BULK_CMD 0x40 | ||
31 | |||
32 | /* The two ports */ | ||
33 | #define ALAUDA_PORT_XD 0x00 | ||
34 | #define ALAUDA_PORT_SM 0x01 | ||
35 | |||
36 | /* Bulk commands */ | ||
37 | #define ALAUDA_BULK_READ_PAGE 0x84 | ||
38 | #define ALAUDA_BULK_READ_OOB 0x85 /* don't use, there's a chip bug */ | ||
39 | #define ALAUDA_BULK_READ_BLOCK 0x94 | ||
40 | #define ALAUDA_BULK_ERASE_BLOCK 0xa3 | ||
41 | #define ALAUDA_BULK_WRITE_PAGE 0xa4 | ||
42 | #define ALAUDA_BULK_WRITE_BLOCK 0xb4 | ||
43 | #define ALAUDA_BULK_RESET_MEDIA 0xe0 | ||
44 | |||
45 | /* Address shifting */ | ||
46 | #define PBA_LO(pba) ((pba & 0xF) << 5) | ||
47 | #define PBA_HI(pba) (pba >> 3) | ||
48 | #define PBA_ZONE(pba) (pba >> 11) | ||
49 | |||
50 | #define TIMEOUT HZ | ||
51 | |||
52 | static struct usb_device_id alauda_table [] = { | ||
53 | { USB_DEVICE(0x0584, 0x0008) }, /* Fujifilm DPC-R1 */ | ||
54 | { USB_DEVICE(0x07b4, 0x010a) }, /* Olympus MAUSB-10 */ | ||
55 | { } | ||
56 | }; | ||
57 | MODULE_DEVICE_TABLE(usb, alauda_table); | ||
58 | |||
59 | struct alauda_card { | ||
60 | u8 id; /* id byte */ | ||
61 | u8 chipshift; /* 1<<chipshift total size */ | ||
62 | u8 pageshift; /* 1<<pageshift page size */ | ||
63 | u8 blockshift; /* 1<<blockshift block size */ | ||
64 | }; | ||
65 | |||
66 | struct alauda { | ||
67 | struct usb_device *dev; | ||
68 | struct usb_interface *interface; | ||
69 | struct mtd_info *mtd; | ||
70 | struct alauda_card *card; | ||
71 | struct mutex card_mutex; | ||
72 | u32 pagemask; | ||
73 | u32 bytemask; | ||
74 | u32 blockmask; | ||
75 | unsigned int write_out; | ||
76 | unsigned int bulk_in; | ||
77 | unsigned int bulk_out; | ||
78 | u8 port; | ||
79 | struct kref kref; | ||
80 | }; | ||
81 | |||
82 | static struct alauda_card alauda_card_ids[] = { | ||
83 | /* NAND flash */ | ||
84 | { 0x6e, 20, 8, 12}, /* 1 MB */ | ||
85 | { 0xe8, 20, 8, 12}, /* 1 MB */ | ||
86 | { 0xec, 20, 8, 12}, /* 1 MB */ | ||
87 | { 0x64, 21, 8, 12}, /* 2 MB */ | ||
88 | { 0xea, 21, 8, 12}, /* 2 MB */ | ||
89 | { 0x6b, 22, 9, 13}, /* 4 MB */ | ||
90 | { 0xe3, 22, 9, 13}, /* 4 MB */ | ||
91 | { 0xe5, 22, 9, 13}, /* 4 MB */ | ||
92 | { 0xe6, 23, 9, 13}, /* 8 MB */ | ||
93 | { 0x73, 24, 9, 14}, /* 16 MB */ | ||
94 | { 0x75, 25, 9, 14}, /* 32 MB */ | ||
95 | { 0x76, 26, 9, 14}, /* 64 MB */ | ||
96 | { 0x79, 27, 9, 14}, /* 128 MB */ | ||
97 | { 0x71, 28, 9, 14}, /* 256 MB */ | ||
98 | |||
99 | /* MASK ROM */ | ||
100 | { 0x5d, 21, 9, 13}, /* 2 MB */ | ||
101 | { 0xd5, 22, 9, 13}, /* 4 MB */ | ||
102 | { 0xd6, 23, 9, 13}, /* 8 MB */ | ||
103 | { 0x57, 24, 9, 13}, /* 16 MB */ | ||
104 | { 0x58, 25, 9, 13}, /* 32 MB */ | ||
105 | { } | ||
106 | }; | ||
107 | |||
108 | static struct alauda_card *get_card(u8 id) | ||
109 | { | ||
110 | struct alauda_card *card; | ||
111 | |||
112 | for (card = alauda_card_ids; card->id; card++) | ||
113 | if (card->id == id) | ||
114 | return card; | ||
115 | return NULL; | ||
116 | } | ||
117 | |||
118 | static void alauda_delete(struct kref *kref) | ||
119 | { | ||
120 | struct alauda *al = container_of(kref, struct alauda, kref); | ||
121 | |||
122 | if (al->mtd) { | ||
123 | del_mtd_device(al->mtd); | ||
124 | kfree(al->mtd); | ||
125 | } | ||
126 | usb_put_dev(al->dev); | ||
127 | kfree(al); | ||
128 | } | ||
129 | |||
130 | static int alauda_get_media_status(struct alauda *al, void *buf) | ||
131 | { | ||
132 | int ret; | ||
133 | |||
134 | mutex_lock(&al->card_mutex); | ||
135 | ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0), | ||
136 | ALAUDA_GET_XD_MEDIA_STATUS, 0xc0, 0, 1, buf, 2, HZ); | ||
137 | mutex_unlock(&al->card_mutex); | ||
138 | return ret; | ||
139 | } | ||
140 | |||
141 | static int alauda_ack_media(struct alauda *al) | ||
142 | { | ||
143 | int ret; | ||
144 | |||
145 | mutex_lock(&al->card_mutex); | ||
146 | ret = usb_control_msg(al->dev, usb_sndctrlpipe(al->dev, 0), | ||
147 | ALAUDA_ACK_XD_MEDIA_CHANGE, 0x40, 0, 1, NULL, 0, HZ); | ||
148 | mutex_unlock(&al->card_mutex); | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | static int alauda_get_media_signatures(struct alauda *al, void *buf) | ||
153 | { | ||
154 | int ret; | ||
155 | |||
156 | mutex_lock(&al->card_mutex); | ||
157 | ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0), | ||
158 | ALAUDA_GET_XD_MEDIA_SIG, 0xc0, 0, 0, buf, 4, HZ); | ||
159 | mutex_unlock(&al->card_mutex); | ||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | static void alauda_reset(struct alauda *al) | ||
164 | { | ||
165 | u8 command[] = { | ||
166 | ALAUDA_BULK_CMD, ALAUDA_BULK_RESET_MEDIA, 0, 0, | ||
167 | 0, 0, 0, 0, al->port | ||
168 | }; | ||
169 | mutex_lock(&al->card_mutex); | ||
170 | usb_bulk_msg(al->dev, al->bulk_out, command, 9, NULL, HZ); | ||
171 | mutex_unlock(&al->card_mutex); | ||
172 | } | ||
173 | |||
174 | static void correct_data(void *buf, void *read_ecc, | ||
175 | int *corrected, int *uncorrected) | ||
176 | { | ||
177 | u8 calc_ecc[3]; | ||
178 | int err; | ||
179 | |||
180 | nand_calculate_ecc(NULL, buf, calc_ecc); | ||
181 | err = nand_correct_data(NULL, buf, read_ecc, calc_ecc); | ||
182 | if (err) { | ||
183 | if (err > 0) | ||
184 | (*corrected)++; | ||
185 | else | ||
186 | (*uncorrected)++; | ||
187 | } | ||
188 | } | ||
189 | |||
190 | struct alauda_sg_request { | ||
191 | struct urb *urb[3]; | ||
192 | struct completion comp; | ||
193 | }; | ||
194 | |||
195 | static void alauda_complete(struct urb *urb) | ||
196 | { | ||
197 | struct completion *comp = urb->context; | ||
198 | |||
199 | if (comp) | ||
200 | complete(comp); | ||
201 | } | ||
202 | |||
203 | static int __alauda_read_page(struct mtd_info *mtd, loff_t from, void *buf, | ||
204 | void *oob) | ||
205 | { | ||
206 | struct alauda_sg_request sg; | ||
207 | struct alauda *al = mtd->priv; | ||
208 | u32 pba = from >> al->card->blockshift; | ||
209 | u32 page = (from >> al->card->pageshift) & al->pagemask; | ||
210 | u8 command[] = { | ||
211 | ALAUDA_BULK_CMD, ALAUDA_BULK_READ_PAGE, PBA_HI(pba), | ||
212 | PBA_ZONE(pba), 0, PBA_LO(pba) + page, 1, 0, al->port | ||
213 | }; | ||
214 | int i, err; | ||
215 | |||
216 | for (i=0; i<3; i++) | ||
217 | sg.urb[i] = NULL; | ||
218 | |||
219 | err = -ENOMEM; | ||
220 | for (i=0; i<3; i++) { | ||
221 | sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); | ||
222 | if (!sg.urb[i]) | ||
223 | goto out; | ||
224 | } | ||
225 | init_completion(&sg.comp); | ||
226 | usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, | ||
227 | alauda_complete, NULL); | ||
228 | usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, mtd->writesize, | ||
229 | alauda_complete, NULL); | ||
230 | usb_fill_bulk_urb(sg.urb[2], al->dev, al->bulk_in, oob, 16, | ||
231 | alauda_complete, &sg.comp); | ||
232 | |||
233 | mutex_lock(&al->card_mutex); | ||
234 | for (i=0; i<3; i++) { | ||
235 | err = usb_submit_urb(sg.urb[i], GFP_NOIO); | ||
236 | if (err) | ||
237 | goto cancel; | ||
238 | } | ||
239 | if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { | ||
240 | err = -ETIMEDOUT; | ||
241 | cancel: | ||
242 | for (i=0; i<3; i++) { | ||
243 | usb_kill_urb(sg.urb[i]); | ||
244 | } | ||
245 | } | ||
246 | mutex_unlock(&al->card_mutex); | ||
247 | |||
248 | out: | ||
249 | usb_free_urb(sg.urb[0]); | ||
250 | usb_free_urb(sg.urb[1]); | ||
251 | usb_free_urb(sg.urb[2]); | ||
252 | return err; | ||
253 | } | ||
254 | |||
255 | static int alauda_read_page(struct mtd_info *mtd, loff_t from, | ||
256 | void *buf, u8 *oob, int *corrected, int *uncorrected) | ||
257 | { | ||
258 | int err; | ||
259 | |||
260 | err = __alauda_read_page(mtd, from, buf, oob); | ||
261 | if (err) | ||
262 | return err; | ||
263 | correct_data(buf, oob+13, corrected, uncorrected); | ||
264 | correct_data(buf+256, oob+8, corrected, uncorrected); | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | static int alauda_write_page(struct mtd_info *mtd, loff_t to, void *buf, | ||
269 | void *oob) | ||
270 | { | ||
271 | struct alauda_sg_request sg; | ||
272 | struct alauda *al = mtd->priv; | ||
273 | u32 pba = to >> al->card->blockshift; | ||
274 | u32 page = (to >> al->card->pageshift) & al->pagemask; | ||
275 | u8 command[] = { | ||
276 | ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_PAGE, PBA_HI(pba), | ||
277 | PBA_ZONE(pba), 0, PBA_LO(pba) + page, 32, 0, al->port | ||
278 | }; | ||
279 | int i, err; | ||
280 | |||
281 | for (i=0; i<3; i++) | ||
282 | sg.urb[i] = NULL; | ||
283 | |||
284 | err = -ENOMEM; | ||
285 | for (i=0; i<3; i++) { | ||
286 | sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); | ||
287 | if (!sg.urb[i]) | ||
288 | goto out; | ||
289 | } | ||
290 | init_completion(&sg.comp); | ||
291 | usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, | ||
292 | alauda_complete, NULL); | ||
293 | usb_fill_bulk_urb(sg.urb[1], al->dev, al->write_out, buf,mtd->writesize, | ||
294 | alauda_complete, NULL); | ||
295 | usb_fill_bulk_urb(sg.urb[2], al->dev, al->write_out, oob, 16, | ||
296 | alauda_complete, &sg.comp); | ||
297 | |||
298 | mutex_lock(&al->card_mutex); | ||
299 | for (i=0; i<3; i++) { | ||
300 | err = usb_submit_urb(sg.urb[i], GFP_NOIO); | ||
301 | if (err) | ||
302 | goto cancel; | ||
303 | } | ||
304 | if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { | ||
305 | err = -ETIMEDOUT; | ||
306 | cancel: | ||
307 | for (i=0; i<3; i++) { | ||
308 | usb_kill_urb(sg.urb[i]); | ||
309 | } | ||
310 | } | ||
311 | mutex_unlock(&al->card_mutex); | ||
312 | |||
313 | out: | ||
314 | usb_free_urb(sg.urb[0]); | ||
315 | usb_free_urb(sg.urb[1]); | ||
316 | usb_free_urb(sg.urb[2]); | ||
317 | return err; | ||
318 | } | ||
319 | |||
320 | static int alauda_erase_block(struct mtd_info *mtd, loff_t ofs) | ||
321 | { | ||
322 | struct alauda_sg_request sg; | ||
323 | struct alauda *al = mtd->priv; | ||
324 | u32 pba = ofs >> al->card->blockshift; | ||
325 | u8 command[] = { | ||
326 | ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba), | ||
327 | PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, al->port | ||
328 | }; | ||
329 | u8 buf[2]; | ||
330 | int i, err; | ||
331 | |||
332 | for (i=0; i<2; i++) | ||
333 | sg.urb[i] = NULL; | ||
334 | |||
335 | err = -ENOMEM; | ||
336 | for (i=0; i<2; i++) { | ||
337 | sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); | ||
338 | if (!sg.urb[i]) | ||
339 | goto out; | ||
340 | } | ||
341 | init_completion(&sg.comp); | ||
342 | usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, | ||
343 | alauda_complete, NULL); | ||
344 | usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, 2, | ||
345 | alauda_complete, &sg.comp); | ||
346 | |||
347 | mutex_lock(&al->card_mutex); | ||
348 | for (i=0; i<2; i++) { | ||
349 | err = usb_submit_urb(sg.urb[i], GFP_NOIO); | ||
350 | if (err) | ||
351 | goto cancel; | ||
352 | } | ||
353 | if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { | ||
354 | err = -ETIMEDOUT; | ||
355 | cancel: | ||
356 | for (i=0; i<2; i++) { | ||
357 | usb_kill_urb(sg.urb[i]); | ||
358 | } | ||
359 | } | ||
360 | mutex_unlock(&al->card_mutex); | ||
361 | |||
362 | out: | ||
363 | usb_free_urb(sg.urb[0]); | ||
364 | usb_free_urb(sg.urb[1]); | ||
365 | return err; | ||
366 | } | ||
367 | |||
368 | static int alauda_read_oob(struct mtd_info *mtd, loff_t from, void *oob) | ||
369 | { | ||
370 | static u8 ignore_buf[512]; /* write only */ | ||
371 | |||
372 | return __alauda_read_page(mtd, from, ignore_buf, oob); | ||
373 | } | ||
374 | |||
375 | static int popcount8(u8 c) | ||
376 | { | ||
377 | int ret = 0; | ||
378 | |||
379 | for ( ; c; c>>=1) | ||
380 | ret += c & 1; | ||
381 | return ret; | ||
382 | } | ||
383 | |||
384 | static int alauda_isbad(struct mtd_info *mtd, loff_t ofs) | ||
385 | { | ||
386 | u8 oob[16]; | ||
387 | int err; | ||
388 | |||
389 | err = alauda_read_oob(mtd, ofs, oob); | ||
390 | if (err) | ||
391 | return err; | ||
392 | |||
393 | /* A block is marked bad if two or more bits are zero */ | ||
394 | return popcount8(oob[5]) >= 7 ? 0 : 1; | ||
395 | } | ||
396 | |||
397 | static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
398 | size_t *retlen, u_char *buf) | ||
399 | { | ||
400 | struct alauda *al = mtd->priv; | ||
401 | void *bounce_buf; | ||
402 | int err, corrected=0, uncorrected=0; | ||
403 | |||
404 | bounce_buf = kmalloc(mtd->writesize, GFP_KERNEL); | ||
405 | if (!bounce_buf) | ||
406 | return -ENOMEM; | ||
407 | |||
408 | *retlen = len; | ||
409 | while (len) { | ||
410 | u8 oob[16]; | ||
411 | size_t byte = from & al->bytemask; | ||
412 | size_t cplen = min(len, mtd->writesize - byte); | ||
413 | |||
414 | err = alauda_read_page(mtd, from, bounce_buf, oob, | ||
415 | &corrected, &uncorrected); | ||
416 | if (err) | ||
417 | goto out; | ||
418 | |||
419 | memcpy(buf, bounce_buf + byte, cplen); | ||
420 | buf += cplen; | ||
421 | from += cplen; | ||
422 | len -= cplen; | ||
423 | } | ||
424 | err = 0; | ||
425 | if (corrected) | ||
426 | err = -EUCLEAN; | ||
427 | if (uncorrected) | ||
428 | err = -EBADMSG; | ||
429 | out: | ||
430 | kfree(bounce_buf); | ||
431 | return err; | ||
432 | } | ||
433 | |||
434 | static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
435 | size_t *retlen, u_char *buf) | ||
436 | { | ||
437 | struct alauda *al = mtd->priv; | ||
438 | int err, corrected=0, uncorrected=0; | ||
439 | |||
440 | if ((from & al->bytemask) || (len & al->bytemask)) | ||
441 | return alauda_bounce_read(mtd, from, len, retlen, buf); | ||
442 | |||
443 | *retlen = len; | ||
444 | while (len) { | ||
445 | u8 oob[16]; | ||
446 | |||
447 | err = alauda_read_page(mtd, from, buf, oob, | ||
448 | &corrected, &uncorrected); | ||
449 | if (err) | ||
450 | return err; | ||
451 | |||
452 | buf += mtd->writesize; | ||
453 | from += mtd->writesize; | ||
454 | len -= mtd->writesize; | ||
455 | } | ||
456 | err = 0; | ||
457 | if (corrected) | ||
458 | err = -EUCLEAN; | ||
459 | if (uncorrected) | ||
460 | err = -EBADMSG; | ||
461 | return err; | ||
462 | } | ||
463 | |||
464 | static int alauda_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
465 | size_t *retlen, const u_char *buf) | ||
466 | { | ||
467 | struct alauda *al = mtd->priv; | ||
468 | int err; | ||
469 | |||
470 | if ((to & al->bytemask) || (len & al->bytemask)) | ||
471 | return -EINVAL; | ||
472 | |||
473 | *retlen = len; | ||
474 | while (len) { | ||
475 | u32 page = (to >> al->card->pageshift) & al->pagemask; | ||
476 | u8 oob[16] = { 'h', 'e', 'l', 'l', 'o', 0xff, 0xff, 0xff, | ||
477 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | ||
478 | |||
479 | /* don't write to bad blocks */ | ||
480 | if (page == 0) { | ||
481 | err = alauda_isbad(mtd, to); | ||
482 | if (err) { | ||
483 | return -EIO; | ||
484 | } | ||
485 | } | ||
486 | nand_calculate_ecc(mtd, buf, &oob[13]); | ||
487 | nand_calculate_ecc(mtd, buf+256, &oob[8]); | ||
488 | |||
489 | err = alauda_write_page(mtd, to, (void*)buf, oob); | ||
490 | if (err) | ||
491 | return err; | ||
492 | |||
493 | buf += mtd->writesize; | ||
494 | to += mtd->writesize; | ||
495 | len -= mtd->writesize; | ||
496 | } | ||
497 | return 0; | ||
498 | } | ||
499 | |||
500 | static int __alauda_erase(struct mtd_info *mtd, struct erase_info *instr) | ||
501 | { | ||
502 | struct alauda *al = mtd->priv; | ||
503 | u32 ofs = instr->addr; | ||
504 | u32 len = instr->len; | ||
505 | int err; | ||
506 | |||
507 | if ((ofs & al->blockmask) || (len & al->blockmask)) | ||
508 | return -EINVAL; | ||
509 | |||
510 | while (len) { | ||
511 | /* don't erase bad blocks */ | ||
512 | err = alauda_isbad(mtd, ofs); | ||
513 | if (err > 0) | ||
514 | err = -EIO; | ||
515 | if (err < 0) | ||
516 | return err; | ||
517 | |||
518 | err = alauda_erase_block(mtd, ofs); | ||
519 | if (err < 0) | ||
520 | return err; | ||
521 | |||
522 | ofs += mtd->erasesize; | ||
523 | len -= mtd->erasesize; | ||
524 | } | ||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | static int alauda_erase(struct mtd_info *mtd, struct erase_info *instr) | ||
529 | { | ||
530 | int err; | ||
531 | |||
532 | err = __alauda_erase(mtd, instr); | ||
533 | instr->state = err ? MTD_ERASE_FAILED : MTD_ERASE_DONE; | ||
534 | mtd_erase_callback(instr); | ||
535 | return err; | ||
536 | } | ||
537 | |||
538 | static int alauda_init_media(struct alauda *al) | ||
539 | { | ||
540 | u8 buf[4], *b0=buf, *b1=buf+1; | ||
541 | struct alauda_card *card; | ||
542 | struct mtd_info *mtd; | ||
543 | int err; | ||
544 | |||
545 | mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); | ||
546 | if (!mtd) | ||
547 | return -ENOMEM; | ||
548 | |||
549 | for (;;) { | ||
550 | err = alauda_get_media_status(al, buf); | ||
551 | if (err < 0) | ||
552 | goto error; | ||
553 | if (*b0 & 0x10) | ||
554 | break; | ||
555 | msleep(20); | ||
556 | } | ||
557 | |||
558 | err = alauda_ack_media(al); | ||
559 | if (err) | ||
560 | goto error; | ||
561 | |||
562 | msleep(10); | ||
563 | |||
564 | err = alauda_get_media_status(al, buf); | ||
565 | if (err < 0) | ||
566 | goto error; | ||
567 | |||
568 | if (*b0 != 0x14) { | ||
569 | /* media not ready */ | ||
570 | err = -EIO; | ||
571 | goto error; | ||
572 | } | ||
573 | err = alauda_get_media_signatures(al, buf); | ||
574 | if (err < 0) | ||
575 | goto error; | ||
576 | |||
577 | card = get_card(*b1); | ||
578 | if (!card) { | ||
579 | printk(KERN_ERR"Alauda: unknown card id %02x\n", *b1); | ||
580 | err = -EIO; | ||
581 | goto error; | ||
582 | } | ||
583 | printk(KERN_INFO"pagesize=%x\nerasesize=%x\nsize=%xMiB\n", | ||
584 | 1<<card->pageshift, 1<<card->blockshift, | ||
585 | 1<<(card->chipshift-20)); | ||
586 | al->card = card; | ||
587 | al->pagemask = (1 << (card->blockshift - card->pageshift)) - 1; | ||
588 | al->bytemask = (1 << card->pageshift) - 1; | ||
589 | al->blockmask = (1 << card->blockshift) - 1; | ||
590 | |||
591 | mtd->name = "alauda"; | ||
592 | mtd->size = 1<<card->chipshift; | ||
593 | mtd->erasesize = 1<<card->blockshift; | ||
594 | mtd->writesize = 1<<card->pageshift; | ||
595 | mtd->type = MTD_NANDFLASH; | ||
596 | mtd->flags = MTD_CAP_NANDFLASH; | ||
597 | mtd->read = alauda_read; | ||
598 | mtd->write = alauda_write; | ||
599 | mtd->erase = alauda_erase; | ||
600 | mtd->block_isbad = alauda_isbad; | ||
601 | mtd->priv = al; | ||
602 | mtd->owner = THIS_MODULE; | ||
603 | |||
604 | err = add_mtd_device(mtd); | ||
605 | if (err) { | ||
606 | err = -ENFILE; | ||
607 | goto error; | ||
608 | } | ||
609 | |||
610 | al->mtd = mtd; | ||
611 | alauda_reset(al); /* no clue whether this is necessary */ | ||
612 | return 0; | ||
613 | error: | ||
614 | kfree(mtd); | ||
615 | return err; | ||
616 | } | ||
617 | |||
618 | static int alauda_check_media(struct alauda *al) | ||
619 | { | ||
620 | u8 buf[2], *b0 = buf, *b1 = buf+1; | ||
621 | int err; | ||
622 | |||
623 | err = alauda_get_media_status(al, buf); | ||
624 | if (err < 0) | ||
625 | return err; | ||
626 | |||
627 | if ((*b1 & 0x01) == 0) { | ||
628 | /* door open */ | ||
629 | return -EIO; | ||
630 | } | ||
631 | if ((*b0 & 0x80) || ((*b0 & 0x1F) == 0x10)) { | ||
632 | /* no media ? */ | ||
633 | return -EIO; | ||
634 | } | ||
635 | if (*b0 & 0x08) { | ||
636 | /* media change ? */ | ||
637 | return alauda_init_media(al); | ||
638 | } | ||
639 | return 0; | ||
640 | } | ||
641 | |||
642 | static int alauda_probe(struct usb_interface *interface, | ||
643 | const struct usb_device_id *id) | ||
644 | { | ||
645 | struct alauda *al; | ||
646 | struct usb_host_interface *iface; | ||
647 | struct usb_endpoint_descriptor *ep, | ||
648 | *ep_in=NULL, *ep_out=NULL, *ep_wr=NULL; | ||
649 | int i, err = -ENOMEM; | ||
650 | |||
651 | al = kzalloc(2*sizeof(*al), GFP_KERNEL); | ||
652 | if (!al) | ||
653 | goto error; | ||
654 | |||
655 | kref_init(&al->kref); | ||
656 | usb_set_intfdata(interface, al); | ||
657 | |||
658 | al->dev = usb_get_dev(interface_to_usbdev(interface)); | ||
659 | al->interface = interface; | ||
660 | |||
661 | iface = interface->cur_altsetting; | ||
662 | for (i = 0; i < iface->desc.bNumEndpoints; ++i) { | ||
663 | ep = &iface->endpoint[i].desc; | ||
664 | |||
665 | if (usb_endpoint_is_bulk_in(ep)) { | ||
666 | ep_in = ep; | ||
667 | } else if (usb_endpoint_is_bulk_out(ep)) { | ||
668 | if (i==0) | ||
669 | ep_wr = ep; | ||
670 | else | ||
671 | ep_out = ep; | ||
672 | } | ||
673 | } | ||
674 | err = -EIO; | ||
675 | if (!ep_wr || !ep_in || !ep_out) | ||
676 | goto error; | ||
677 | |||
678 | al->write_out = usb_sndbulkpipe(al->dev, | ||
679 | ep_wr->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); | ||
680 | al->bulk_in = usb_rcvbulkpipe(al->dev, | ||
681 | ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); | ||
682 | al->bulk_out = usb_sndbulkpipe(al->dev, | ||
683 | ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); | ||
684 | |||
685 | /* second device is identical up to now */ | ||
686 | memcpy(al+1, al, sizeof(*al)); | ||
687 | |||
688 | mutex_init(&al[0].card_mutex); | ||
689 | mutex_init(&al[1].card_mutex); | ||
690 | |||
691 | al[0].port = ALAUDA_PORT_XD; | ||
692 | al[1].port = ALAUDA_PORT_SM; | ||
693 | |||
694 | info("alauda probed"); | ||
695 | alauda_check_media(al); | ||
696 | alauda_check_media(al+1); | ||
697 | |||
698 | return 0; | ||
699 | |||
700 | error: | ||
701 | if (al) | ||
702 | kref_put(&al->kref, alauda_delete); | ||
703 | return err; | ||
704 | } | ||
705 | |||
706 | static void alauda_disconnect(struct usb_interface *interface) | ||
707 | { | ||
708 | struct alauda *al; | ||
709 | |||
710 | al = usb_get_intfdata(interface); | ||
711 | usb_set_intfdata(interface, NULL); | ||
712 | |||
713 | /* FIXME: prevent more I/O from starting */ | ||
714 | |||
715 | /* decrement our usage count */ | ||
716 | if (al) | ||
717 | kref_put(&al->kref, alauda_delete); | ||
718 | |||
719 | info("alauda gone"); | ||
720 | } | ||
721 | |||
722 | static struct usb_driver alauda_driver = { | ||
723 | .name = "alauda", | ||
724 | .probe = alauda_probe, | ||
725 | .disconnect = alauda_disconnect, | ||
726 | .id_table = alauda_table, | ||
727 | }; | ||
728 | |||
729 | static int __init alauda_init(void) | ||
730 | { | ||
731 | return usb_register(&alauda_driver); | ||
732 | } | ||
733 | |||
734 | static void __exit alauda_exit(void) | ||
735 | { | ||
736 | usb_deregister(&alauda_driver); | ||
737 | } | ||
738 | |||
739 | module_init(alauda_init); | ||
740 | module_exit(alauda_exit); | ||
741 | |||
742 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c new file mode 100644 index 000000000000..1657ecd74881 --- /dev/null +++ b/drivers/mtd/nand/bf5xx_nand.c | |||
@@ -0,0 +1,788 @@ | |||
1 | /* linux/drivers/mtd/nand/bf5xx_nand.c | ||
2 | * | ||
3 | * Copyright 2006-2007 Analog Devices Inc. | ||
4 | * http://blackfin.uclinux.org/ | ||
5 | * Bryan Wu <bryan.wu@analog.com> | ||
6 | * | ||
7 | * Blackfin BF5xx on-chip NAND flash controler driver | ||
8 | * | ||
9 | * Derived from drivers/mtd/nand/s3c2410.c | ||
10 | * Copyright (c) 2007 Ben Dooks <ben@simtec.co.uk> | ||
11 | * | ||
12 | * Derived from drivers/mtd/nand/cafe.c | ||
13 | * Copyright © 2006 Red Hat, Inc. | ||
14 | * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> | ||
15 | * | ||
16 | * Changelog: | ||
17 | * 12-Jun-2007 Bryan Wu: Initial version | ||
18 | * 18-Jul-2007 Bryan Wu: | ||
19 | * - ECC_HW and ECC_SW supported | ||
20 | * - DMA supported in ECC_HW | ||
21 | * - YAFFS tested as rootfs in both ECC_HW and ECC_SW | ||
22 | * | ||
23 | * TODO: | ||
24 | * Enable JFFS2 over NAND as rootfs | ||
25 | * | ||
26 | * This program is free software; you can redistribute it and/or modify | ||
27 | * it under the terms of the GNU General Public License as published by | ||
28 | * the Free Software Foundation; either version 2 of the License, or | ||
29 | * (at your option) any later version. | ||
30 | * | ||
31 | * This program is distributed in the hope that it will be useful, | ||
32 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
33 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
34 | * GNU General Public License for more details. | ||
35 | * | ||
36 | * You should have received a copy of the GNU General Public License | ||
37 | * along with this program; if not, write to the Free Software | ||
38 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
39 | */ | ||
40 | |||
41 | #include <linux/module.h> | ||
42 | #include <linux/types.h> | ||
43 | #include <linux/init.h> | ||
44 | #include <linux/kernel.h> | ||
45 | #include <linux/string.h> | ||
46 | #include <linux/ioport.h> | ||
47 | #include <linux/platform_device.h> | ||
48 | #include <linux/delay.h> | ||
49 | #include <linux/dma-mapping.h> | ||
50 | #include <linux/err.h> | ||
51 | #include <linux/slab.h> | ||
52 | #include <linux/io.h> | ||
53 | #include <linux/bitops.h> | ||
54 | |||
55 | #include <linux/mtd/mtd.h> | ||
56 | #include <linux/mtd/nand.h> | ||
57 | #include <linux/mtd/nand_ecc.h> | ||
58 | #include <linux/mtd/partitions.h> | ||
59 | |||
60 | #include <asm/blackfin.h> | ||
61 | #include <asm/dma.h> | ||
62 | #include <asm/cacheflush.h> | ||
63 | #include <asm/nand.h> | ||
64 | #include <asm/portmux.h> | ||
65 | |||
66 | #define DRV_NAME "bf5xx-nand" | ||
67 | #define DRV_VERSION "1.2" | ||
68 | #define DRV_AUTHOR "Bryan Wu <bryan.wu@analog.com>" | ||
69 | #define DRV_DESC "BF5xx on-chip NAND FLash Controller Driver" | ||
70 | |||
71 | #ifdef CONFIG_MTD_NAND_BF5XX_HWECC | ||
72 | static int hardware_ecc = 1; | ||
73 | #else | ||
74 | static int hardware_ecc; | ||
75 | #endif | ||
76 | |||
77 | static unsigned short bfin_nfc_pin_req[] = {P_NAND_CE, P_NAND_RB, 0}; | ||
78 | |||
79 | /* | ||
80 | * Data structures for bf5xx nand flash controller driver | ||
81 | */ | ||
82 | |||
83 | /* bf5xx nand info */ | ||
84 | struct bf5xx_nand_info { | ||
85 | /* mtd info */ | ||
86 | struct nand_hw_control controller; | ||
87 | struct mtd_info mtd; | ||
88 | struct nand_chip chip; | ||
89 | |||
90 | /* platform info */ | ||
91 | struct bf5xx_nand_platform *platform; | ||
92 | |||
93 | /* device info */ | ||
94 | struct device *device; | ||
95 | |||
96 | /* DMA stuff */ | ||
97 | struct completion dma_completion; | ||
98 | }; | ||
99 | |||
100 | /* | ||
101 | * Conversion functions | ||
102 | */ | ||
103 | static struct bf5xx_nand_info *mtd_to_nand_info(struct mtd_info *mtd) | ||
104 | { | ||
105 | return container_of(mtd, struct bf5xx_nand_info, mtd); | ||
106 | } | ||
107 | |||
108 | static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev) | ||
109 | { | ||
110 | return platform_get_drvdata(pdev); | ||
111 | } | ||
112 | |||
113 | static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev) | ||
114 | { | ||
115 | return pdev->dev.platform_data; | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * struct nand_chip interface function pointers | ||
120 | */ | ||
121 | |||
122 | /* | ||
123 | * bf5xx_nand_hwcontrol | ||
124 | * | ||
125 | * Issue command and address cycles to the chip | ||
126 | */ | ||
127 | static void bf5xx_nand_hwcontrol(struct mtd_info *mtd, int cmd, | ||
128 | unsigned int ctrl) | ||
129 | { | ||
130 | if (cmd == NAND_CMD_NONE) | ||
131 | return; | ||
132 | |||
133 | while (bfin_read_NFC_STAT() & WB_FULL) | ||
134 | cpu_relax(); | ||
135 | |||
136 | if (ctrl & NAND_CLE) | ||
137 | bfin_write_NFC_CMD(cmd); | ||
138 | else | ||
139 | bfin_write_NFC_ADDR(cmd); | ||
140 | SSYNC(); | ||
141 | } | ||
142 | |||
143 | /* | ||
144 | * bf5xx_nand_devready() | ||
145 | * | ||
146 | * returns 0 if the nand is busy, 1 if it is ready | ||
147 | */ | ||
148 | static int bf5xx_nand_devready(struct mtd_info *mtd) | ||
149 | { | ||
150 | unsigned short val = bfin_read_NFC_IRQSTAT(); | ||
151 | |||
152 | if ((val & NBUSYIRQ) == NBUSYIRQ) | ||
153 | return 1; | ||
154 | else | ||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * ECC functions | ||
160 | * These allow the bf5xx to use the controller's ECC | ||
161 | * generator block to ECC the data as it passes through | ||
162 | */ | ||
163 | |||
164 | /* | ||
165 | * ECC error correction function | ||
166 | */ | ||
167 | static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat, | ||
168 | u_char *read_ecc, u_char *calc_ecc) | ||
169 | { | ||
170 | struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); | ||
171 | u32 syndrome[5]; | ||
172 | u32 calced, stored; | ||
173 | int i; | ||
174 | unsigned short failing_bit, failing_byte; | ||
175 | u_char data; | ||
176 | |||
177 | calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16); | ||
178 | stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16); | ||
179 | |||
180 | syndrome[0] = (calced ^ stored); | ||
181 | |||
182 | /* | ||
183 | * syndrome 0: all zero | ||
184 | * No error in data | ||
185 | * No action | ||
186 | */ | ||
187 | if (!syndrome[0] || !calced || !stored) | ||
188 | return 0; | ||
189 | |||
190 | /* | ||
191 | * sysdrome 0: only one bit is one | ||
192 | * ECC data was incorrect | ||
193 | * No action | ||
194 | */ | ||
195 | if (hweight32(syndrome[0]) == 1) { | ||
196 | dev_err(info->device, "ECC data was incorrect!\n"); | ||
197 | return 1; | ||
198 | } | ||
199 | |||
200 | syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF); | ||
201 | syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF); | ||
202 | syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF); | ||
203 | syndrome[4] = syndrome[2] ^ syndrome[3]; | ||
204 | |||
205 | for (i = 0; i < 5; i++) | ||
206 | dev_info(info->device, "syndrome[%d] 0x%08x\n", i, syndrome[i]); | ||
207 | |||
208 | dev_info(info->device, | ||
209 | "calced[0x%08x], stored[0x%08x]\n", | ||
210 | calced, stored); | ||
211 | |||
212 | /* | ||
213 | * sysdrome 0: exactly 11 bits are one, each parity | ||
214 | * and parity' pair is 1 & 0 or 0 & 1. | ||
215 | * 1-bit correctable error | ||
216 | * Correct the error | ||
217 | */ | ||
218 | if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) { | ||
219 | dev_info(info->device, | ||
220 | "1-bit correctable error, correct it.\n"); | ||
221 | dev_info(info->device, | ||
222 | "syndrome[1] 0x%08x\n", syndrome[1]); | ||
223 | |||
224 | failing_bit = syndrome[1] & 0x7; | ||
225 | failing_byte = syndrome[1] >> 0x3; | ||
226 | data = *(dat + failing_byte); | ||
227 | data = data ^ (0x1 << failing_bit); | ||
228 | *(dat + failing_byte) = data; | ||
229 | |||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | /* | ||
234 | * sysdrome 0: random data | ||
235 | * More than 1-bit error, non-correctable error | ||
236 | * Discard data, mark bad block | ||
237 | */ | ||
238 | dev_err(info->device, | ||
239 | "More than 1-bit error, non-correctable error.\n"); | ||
240 | dev_err(info->device, | ||
241 | "Please discard data, mark bad block\n"); | ||
242 | |||
243 | return 1; | ||
244 | } | ||
245 | |||
246 | static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat, | ||
247 | u_char *read_ecc, u_char *calc_ecc) | ||
248 | { | ||
249 | struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); | ||
250 | struct bf5xx_nand_platform *plat = info->platform; | ||
251 | unsigned short page_size = (plat->page_size ? 512 : 256); | ||
252 | int ret; | ||
253 | |||
254 | ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc); | ||
255 | |||
256 | /* If page size is 512, correct second 256 bytes */ | ||
257 | if (page_size == 512) { | ||
258 | dat += 256; | ||
259 | read_ecc += 8; | ||
260 | calc_ecc += 8; | ||
261 | ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc); | ||
262 | } | ||
263 | |||
264 | return ret; | ||
265 | } | ||
266 | |||
267 | static void bf5xx_nand_enable_hwecc(struct mtd_info *mtd, int mode) | ||
268 | { | ||
269 | return; | ||
270 | } | ||
271 | |||
272 | static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd, | ||
273 | const u_char *dat, u_char *ecc_code) | ||
274 | { | ||
275 | struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); | ||
276 | struct bf5xx_nand_platform *plat = info->platform; | ||
277 | u16 page_size = (plat->page_size ? 512 : 256); | ||
278 | u16 ecc0, ecc1; | ||
279 | u32 code[2]; | ||
280 | u8 *p; | ||
281 | int bytes = 3, i; | ||
282 | |||
283 | /* first 4 bytes ECC code for 256 page size */ | ||
284 | ecc0 = bfin_read_NFC_ECC0(); | ||
285 | ecc1 = bfin_read_NFC_ECC1(); | ||
286 | |||
287 | code[0] = (ecc0 & 0x3FF) | ((ecc1 & 0x3FF) << 11); | ||
288 | |||
289 | dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]); | ||
290 | |||
291 | /* second 4 bytes ECC code for 512 page size */ | ||
292 | if (page_size == 512) { | ||
293 | ecc0 = bfin_read_NFC_ECC2(); | ||
294 | ecc1 = bfin_read_NFC_ECC3(); | ||
295 | code[1] = (ecc0 & 0x3FF) | ((ecc1 & 0x3FF) << 11); | ||
296 | bytes = 6; | ||
297 | dev_dbg(info->device, "returning ecc 0x%08x\n", code[1]); | ||
298 | } | ||
299 | |||
300 | p = (u8 *)code; | ||
301 | for (i = 0; i < bytes; i++) | ||
302 | ecc_code[i] = p[i]; | ||
303 | |||
304 | return 0; | ||
305 | } | ||
306 | |||
307 | /* | ||
308 | * PIO mode for buffer writing and reading | ||
309 | */ | ||
310 | static void bf5xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) | ||
311 | { | ||
312 | int i; | ||
313 | unsigned short val; | ||
314 | |||
315 | /* | ||
316 | * Data reads are requested by first writing to NFC_DATA_RD | ||
317 | * and then reading back from NFC_READ. | ||
318 | */ | ||
319 | for (i = 0; i < len; i++) { | ||
320 | while (bfin_read_NFC_STAT() & WB_FULL) | ||
321 | cpu_relax(); | ||
322 | |||
323 | /* Contents do not matter */ | ||
324 | bfin_write_NFC_DATA_RD(0x0000); | ||
325 | SSYNC(); | ||
326 | |||
327 | while ((bfin_read_NFC_IRQSTAT() & RD_RDY) != RD_RDY) | ||
328 | cpu_relax(); | ||
329 | |||
330 | buf[i] = bfin_read_NFC_READ(); | ||
331 | |||
332 | val = bfin_read_NFC_IRQSTAT(); | ||
333 | val |= RD_RDY; | ||
334 | bfin_write_NFC_IRQSTAT(val); | ||
335 | SSYNC(); | ||
336 | } | ||
337 | } | ||
338 | |||
339 | static uint8_t bf5xx_nand_read_byte(struct mtd_info *mtd) | ||
340 | { | ||
341 | uint8_t val; | ||
342 | |||
343 | bf5xx_nand_read_buf(mtd, &val, 1); | ||
344 | |||
345 | return val; | ||
346 | } | ||
347 | |||
348 | static void bf5xx_nand_write_buf(struct mtd_info *mtd, | ||
349 | const uint8_t *buf, int len) | ||
350 | { | ||
351 | int i; | ||
352 | |||
353 | for (i = 0; i < len; i++) { | ||
354 | while (bfin_read_NFC_STAT() & WB_FULL) | ||
355 | cpu_relax(); | ||
356 | |||
357 | bfin_write_NFC_DATA_WR(buf[i]); | ||
358 | SSYNC(); | ||
359 | } | ||
360 | } | ||
361 | |||
362 | static void bf5xx_nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) | ||
363 | { | ||
364 | int i; | ||
365 | u16 *p = (u16 *) buf; | ||
366 | len >>= 1; | ||
367 | |||
368 | /* | ||
369 | * Data reads are requested by first writing to NFC_DATA_RD | ||
370 | * and then reading back from NFC_READ. | ||
371 | */ | ||
372 | bfin_write_NFC_DATA_RD(0x5555); | ||
373 | |||
374 | SSYNC(); | ||
375 | |||
376 | for (i = 0; i < len; i++) | ||
377 | p[i] = bfin_read_NFC_READ(); | ||
378 | } | ||
379 | |||
380 | static void bf5xx_nand_write_buf16(struct mtd_info *mtd, | ||
381 | const uint8_t *buf, int len) | ||
382 | { | ||
383 | int i; | ||
384 | u16 *p = (u16 *) buf; | ||
385 | len >>= 1; | ||
386 | |||
387 | for (i = 0; i < len; i++) | ||
388 | bfin_write_NFC_DATA_WR(p[i]); | ||
389 | |||
390 | SSYNC(); | ||
391 | } | ||
392 | |||
393 | /* | ||
394 | * DMA functions for buffer writing and reading | ||
395 | */ | ||
396 | static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id) | ||
397 | { | ||
398 | struct bf5xx_nand_info *info = dev_id; | ||
399 | |||
400 | clear_dma_irqstat(CH_NFC); | ||
401 | disable_dma(CH_NFC); | ||
402 | complete(&info->dma_completion); | ||
403 | |||
404 | return IRQ_HANDLED; | ||
405 | } | ||
406 | |||
407 | static int bf5xx_nand_dma_rw(struct mtd_info *mtd, | ||
408 | uint8_t *buf, int is_read) | ||
409 | { | ||
410 | struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); | ||
411 | struct bf5xx_nand_platform *plat = info->platform; | ||
412 | unsigned short page_size = (plat->page_size ? 512 : 256); | ||
413 | unsigned short val; | ||
414 | |||
415 | dev_dbg(info->device, " mtd->%p, buf->%p, is_read %d\n", | ||
416 | mtd, buf, is_read); | ||
417 | |||
418 | /* | ||
419 | * Before starting a dma transfer, be sure to invalidate/flush | ||
420 | * the cache over the address range of your DMA buffer to | ||
421 | * prevent cache coherency problems. Otherwise very subtle bugs | ||
422 | * can be introduced to your driver. | ||
423 | */ | ||
424 | if (is_read) | ||
425 | invalidate_dcache_range((unsigned int)buf, | ||
426 | (unsigned int)(buf + page_size)); | ||
427 | else | ||
428 | flush_dcache_range((unsigned int)buf, | ||
429 | (unsigned int)(buf + page_size)); | ||
430 | |||
431 | /* | ||
432 | * This register must be written before each page is | ||
433 | * transferred to generate the correct ECC register | ||
434 | * values. | ||
435 | */ | ||
436 | bfin_write_NFC_RST(0x1); | ||
437 | SSYNC(); | ||
438 | |||
439 | disable_dma(CH_NFC); | ||
440 | clear_dma_irqstat(CH_NFC); | ||
441 | |||
442 | /* setup DMA register with Blackfin DMA API */ | ||
443 | set_dma_config(CH_NFC, 0x0); | ||
444 | set_dma_start_addr(CH_NFC, (unsigned long) buf); | ||
445 | set_dma_x_count(CH_NFC, (page_size >> 2)); | ||
446 | set_dma_x_modify(CH_NFC, 4); | ||
447 | |||
448 | /* setup write or read operation */ | ||
449 | val = DI_EN | WDSIZE_32; | ||
450 | if (is_read) | ||
451 | val |= WNR; | ||
452 | set_dma_config(CH_NFC, val); | ||
453 | enable_dma(CH_NFC); | ||
454 | |||
455 | /* Start PAGE read/write operation */ | ||
456 | if (is_read) | ||
457 | bfin_write_NFC_PGCTL(0x1); | ||
458 | else | ||
459 | bfin_write_NFC_PGCTL(0x2); | ||
460 | wait_for_completion(&info->dma_completion); | ||
461 | |||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd, | ||
466 | uint8_t *buf, int len) | ||
467 | { | ||
468 | struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); | ||
469 | struct bf5xx_nand_platform *plat = info->platform; | ||
470 | unsigned short page_size = (plat->page_size ? 512 : 256); | ||
471 | |||
472 | dev_dbg(info->device, "mtd->%p, buf->%p, int %d\n", mtd, buf, len); | ||
473 | |||
474 | if (len == page_size) | ||
475 | bf5xx_nand_dma_rw(mtd, buf, 1); | ||
476 | else | ||
477 | bf5xx_nand_read_buf(mtd, buf, len); | ||
478 | } | ||
479 | |||
480 | static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd, | ||
481 | const uint8_t *buf, int len) | ||
482 | { | ||
483 | struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); | ||
484 | struct bf5xx_nand_platform *plat = info->platform; | ||
485 | unsigned short page_size = (plat->page_size ? 512 : 256); | ||
486 | |||
487 | dev_dbg(info->device, "mtd->%p, buf->%p, len %d\n", mtd, buf, len); | ||
488 | |||
489 | if (len == page_size) | ||
490 | bf5xx_nand_dma_rw(mtd, (uint8_t *)buf, 0); | ||
491 | else | ||
492 | bf5xx_nand_write_buf(mtd, buf, len); | ||
493 | } | ||
494 | |||
495 | /* | ||
496 | * System initialization functions | ||
497 | */ | ||
498 | |||
499 | static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) | ||
500 | { | ||
501 | int ret; | ||
502 | unsigned short val; | ||
503 | |||
504 | /* Do not use dma */ | ||
505 | if (!hardware_ecc) | ||
506 | return 0; | ||
507 | |||
508 | init_completion(&info->dma_completion); | ||
509 | |||
510 | /* Setup DMAC1 channel mux for NFC which shared with SDH */ | ||
511 | val = bfin_read_DMAC1_PERIMUX(); | ||
512 | val &= 0xFFFE; | ||
513 | bfin_write_DMAC1_PERIMUX(val); | ||
514 | SSYNC(); | ||
515 | |||
516 | /* Request NFC DMA channel */ | ||
517 | ret = request_dma(CH_NFC, "BF5XX NFC driver"); | ||
518 | if (ret < 0) { | ||
519 | dev_err(info->device, " unable to get DMA channel\n"); | ||
520 | return ret; | ||
521 | } | ||
522 | |||
523 | set_dma_callback(CH_NFC, (void *) bf5xx_nand_dma_irq, (void *) info); | ||
524 | |||
525 | /* Turn off the DMA channel first */ | ||
526 | disable_dma(CH_NFC); | ||
527 | return 0; | ||
528 | } | ||
529 | |||
530 | /* | ||
531 | * BF5XX NFC hardware initialization | ||
532 | * - pin mux setup | ||
533 | * - clear interrupt status | ||
534 | */ | ||
535 | static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info) | ||
536 | { | ||
537 | int err = 0; | ||
538 | unsigned short val; | ||
539 | struct bf5xx_nand_platform *plat = info->platform; | ||
540 | |||
541 | /* setup NFC_CTL register */ | ||
542 | dev_info(info->device, | ||
543 | "page_size=%d, data_width=%d, wr_dly=%d, rd_dly=%d\n", | ||
544 | (plat->page_size ? 512 : 256), | ||
545 | (plat->data_width ? 16 : 8), | ||
546 | plat->wr_dly, plat->rd_dly); | ||
547 | |||
548 | val = (plat->page_size << NFC_PG_SIZE_OFFSET) | | ||
549 | (plat->data_width << NFC_NWIDTH_OFFSET) | | ||
550 | (plat->rd_dly << NFC_RDDLY_OFFSET) | | ||
551 | (plat->rd_dly << NFC_WRDLY_OFFSET); | ||
552 | dev_dbg(info->device, "NFC_CTL is 0x%04x\n", val); | ||
553 | |||
554 | bfin_write_NFC_CTL(val); | ||
555 | SSYNC(); | ||
556 | |||
557 | /* clear interrupt status */ | ||
558 | bfin_write_NFC_IRQMASK(0x0); | ||
559 | SSYNC(); | ||
560 | val = bfin_read_NFC_IRQSTAT(); | ||
561 | bfin_write_NFC_IRQSTAT(val); | ||
562 | SSYNC(); | ||
563 | |||
564 | if (peripheral_request_list(bfin_nfc_pin_req, DRV_NAME)) { | ||
565 | printk(KERN_ERR DRV_NAME | ||
566 | ": Requesting Peripherals failed\n"); | ||
567 | return -EFAULT; | ||
568 | } | ||
569 | |||
570 | /* DMA initialization */ | ||
571 | if (bf5xx_nand_dma_init(info)) | ||
572 | err = -ENXIO; | ||
573 | |||
574 | return err; | ||
575 | } | ||
576 | |||
577 | /* | ||
578 | * Device management interface | ||
579 | */ | ||
580 | static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info) | ||
581 | { | ||
582 | struct mtd_info *mtd = &info->mtd; | ||
583 | |||
584 | #ifdef CONFIG_MTD_PARTITIONS | ||
585 | struct mtd_partition *parts = info->platform->partitions; | ||
586 | int nr = info->platform->nr_partitions; | ||
587 | |||
588 | return add_mtd_partitions(mtd, parts, nr); | ||
589 | #else | ||
590 | return add_mtd_device(mtd); | ||
591 | #endif | ||
592 | } | ||
593 | |||
594 | static int bf5xx_nand_remove(struct platform_device *pdev) | ||
595 | { | ||
596 | struct bf5xx_nand_info *info = to_nand_info(pdev); | ||
597 | struct mtd_info *mtd = NULL; | ||
598 | |||
599 | platform_set_drvdata(pdev, NULL); | ||
600 | |||
601 | /* first thing we need to do is release all our mtds | ||
602 | * and their partitions, then go through freeing the | ||
603 | * resources used | ||
604 | */ | ||
605 | mtd = &info->mtd; | ||
606 | if (mtd) { | ||
607 | nand_release(mtd); | ||
608 | kfree(mtd); | ||
609 | } | ||
610 | |||
611 | peripheral_free_list(bfin_nfc_pin_req); | ||
612 | |||
613 | /* free the common resources */ | ||
614 | kfree(info); | ||
615 | |||
616 | return 0; | ||
617 | } | ||
618 | |||
619 | /* | ||
620 | * bf5xx_nand_probe | ||
621 | * | ||
622 | * called by device layer when it finds a device matching | ||
623 | * one our driver can handled. This code checks to see if | ||
624 | * it can allocate all necessary resources then calls the | ||
625 | * nand layer to look for devices | ||
626 | */ | ||
627 | static int bf5xx_nand_probe(struct platform_device *pdev) | ||
628 | { | ||
629 | struct bf5xx_nand_platform *plat = to_nand_plat(pdev); | ||
630 | struct bf5xx_nand_info *info = NULL; | ||
631 | struct nand_chip *chip = NULL; | ||
632 | struct mtd_info *mtd = NULL; | ||
633 | int err = 0; | ||
634 | |||
635 | dev_dbg(&pdev->dev, "(%p)\n", pdev); | ||
636 | |||
637 | if (!plat) { | ||
638 | dev_err(&pdev->dev, "no platform specific information\n"); | ||
639 | goto exit_error; | ||
640 | } | ||
641 | |||
642 | info = kzalloc(sizeof(*info), GFP_KERNEL); | ||
643 | if (info == NULL) { | ||
644 | dev_err(&pdev->dev, "no memory for flash info\n"); | ||
645 | err = -ENOMEM; | ||
646 | goto exit_error; | ||
647 | } | ||
648 | |||
649 | platform_set_drvdata(pdev, info); | ||
650 | |||
651 | spin_lock_init(&info->controller.lock); | ||
652 | init_waitqueue_head(&info->controller.wq); | ||
653 | |||
654 | info->device = &pdev->dev; | ||
655 | info->platform = plat; | ||
656 | |||
657 | /* initialise chip data struct */ | ||
658 | chip = &info->chip; | ||
659 | |||
660 | if (plat->data_width) | ||
661 | chip->options |= NAND_BUSWIDTH_16; | ||
662 | |||
663 | chip->options |= NAND_CACHEPRG | NAND_SKIP_BBTSCAN; | ||
664 | |||
665 | chip->read_buf = (plat->data_width) ? | ||
666 | bf5xx_nand_read_buf16 : bf5xx_nand_read_buf; | ||
667 | chip->write_buf = (plat->data_width) ? | ||
668 | bf5xx_nand_write_buf16 : bf5xx_nand_write_buf; | ||
669 | |||
670 | chip->read_byte = bf5xx_nand_read_byte; | ||
671 | |||
672 | chip->cmd_ctrl = bf5xx_nand_hwcontrol; | ||
673 | chip->dev_ready = bf5xx_nand_devready; | ||
674 | |||
675 | chip->priv = &info->mtd; | ||
676 | chip->controller = &info->controller; | ||
677 | |||
678 | chip->IO_ADDR_R = (void __iomem *) NFC_READ; | ||
679 | chip->IO_ADDR_W = (void __iomem *) NFC_DATA_WR; | ||
680 | |||
681 | chip->chip_delay = 0; | ||
682 | |||
683 | /* initialise mtd info data struct */ | ||
684 | mtd = &info->mtd; | ||
685 | mtd->priv = chip; | ||
686 | mtd->owner = THIS_MODULE; | ||
687 | |||
688 | /* initialise the hardware */ | ||
689 | err = bf5xx_nand_hw_init(info); | ||
690 | if (err != 0) | ||
691 | goto exit_error; | ||
692 | |||
693 | /* setup hardware ECC data struct */ | ||
694 | if (hardware_ecc) { | ||
695 | if (plat->page_size == NFC_PG_SIZE_256) { | ||
696 | chip->ecc.bytes = 3; | ||
697 | chip->ecc.size = 256; | ||
698 | } else if (plat->page_size == NFC_PG_SIZE_512) { | ||
699 | chip->ecc.bytes = 6; | ||
700 | chip->ecc.size = 512; | ||
701 | } | ||
702 | |||
703 | chip->read_buf = bf5xx_nand_dma_read_buf; | ||
704 | chip->write_buf = bf5xx_nand_dma_write_buf; | ||
705 | chip->ecc.calculate = bf5xx_nand_calculate_ecc; | ||
706 | chip->ecc.correct = bf5xx_nand_correct_data; | ||
707 | chip->ecc.mode = NAND_ECC_HW; | ||
708 | chip->ecc.hwctl = bf5xx_nand_enable_hwecc; | ||
709 | } else { | ||
710 | chip->ecc.mode = NAND_ECC_SOFT; | ||
711 | } | ||
712 | |||
713 | /* scan hardware nand chip and setup mtd info data struct */ | ||
714 | if (nand_scan(mtd, 1)) { | ||
715 | err = -ENXIO; | ||
716 | goto exit_error; | ||
717 | } | ||
718 | |||
719 | /* add NAND partition */ | ||
720 | bf5xx_nand_add_partition(info); | ||
721 | |||
722 | dev_dbg(&pdev->dev, "initialised ok\n"); | ||
723 | return 0; | ||
724 | |||
725 | exit_error: | ||
726 | bf5xx_nand_remove(pdev); | ||
727 | |||
728 | if (err == 0) | ||
729 | err = -EINVAL; | ||
730 | return err; | ||
731 | } | ||
732 | |||
733 | /* PM Support */ | ||
734 | #ifdef CONFIG_PM | ||
735 | |||
736 | static int bf5xx_nand_suspend(struct platform_device *dev, pm_message_t pm) | ||
737 | { | ||
738 | struct bf5xx_nand_info *info = platform_get_drvdata(dev); | ||
739 | |||
740 | return 0; | ||
741 | } | ||
742 | |||
743 | static int bf5xx_nand_resume(struct platform_device *dev) | ||
744 | { | ||
745 | struct bf5xx_nand_info *info = platform_get_drvdata(dev); | ||
746 | |||
747 | if (info) | ||
748 | bf5xx_nand_hw_init(info); | ||
749 | |||
750 | return 0; | ||
751 | } | ||
752 | |||
753 | #else | ||
754 | #define bf5xx_nand_suspend NULL | ||
755 | #define bf5xx_nand_resume NULL | ||
756 | #endif | ||
757 | |||
758 | /* driver device registration */ | ||
759 | static struct platform_driver bf5xx_nand_driver = { | ||
760 | .probe = bf5xx_nand_probe, | ||
761 | .remove = bf5xx_nand_remove, | ||
762 | .suspend = bf5xx_nand_suspend, | ||
763 | .resume = bf5xx_nand_resume, | ||
764 | .driver = { | ||
765 | .name = DRV_NAME, | ||
766 | .owner = THIS_MODULE, | ||
767 | }, | ||
768 | }; | ||
769 | |||
770 | static int __init bf5xx_nand_init(void) | ||
771 | { | ||
772 | printk(KERN_INFO "%s, Version %s (c) 2007 Analog Devices, Inc.\n", | ||
773 | DRV_DESC, DRV_VERSION); | ||
774 | |||
775 | return platform_driver_register(&bf5xx_nand_driver); | ||
776 | } | ||
777 | |||
778 | static void __exit bf5xx_nand_exit(void) | ||
779 | { | ||
780 | platform_driver_unregister(&bf5xx_nand_driver); | ||
781 | } | ||
782 | |||
783 | module_init(bf5xx_nand_init); | ||
784 | module_exit(bf5xx_nand_exit); | ||
785 | |||
786 | MODULE_LICENSE("GPL"); | ||
787 | MODULE_AUTHOR(DRV_AUTHOR); | ||
788 | MODULE_DESCRIPTION(DRV_DESC); | ||
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 6f32a35eb106..e2832d0b9899 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c | |||
@@ -623,6 +623,11 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, | |||
623 | uint32_t ctrl; | 623 | uint32_t ctrl; |
624 | int err = 0; | 624 | int err = 0; |
625 | 625 | ||
626 | /* Very old versions shared the same PCI ident for all three | ||
627 | functions on the chip. Verify the class too... */ | ||
628 | if ((pdev->class >> 8) != PCI_CLASS_MEMORY_FLASH) | ||
629 | return -ENODEV; | ||
630 | |||
626 | err = pci_enable_device(pdev); | 631 | err = pci_enable_device(pdev); |
627 | if (err) | 632 | if (err) |
628 | return err; | 633 | return err; |
@@ -816,21 +821,57 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev) | |||
816 | } | 821 | } |
817 | 822 | ||
818 | static struct pci_device_id cafe_nand_tbl[] = { | 823 | static struct pci_device_id cafe_nand_tbl[] = { |
819 | { 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MEMORY_FLASH << 8, 0xFFFF0 }, | 824 | { 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID }, |
820 | { 0, } | 825 | { } |
821 | }; | 826 | }; |
822 | 827 | ||
823 | MODULE_DEVICE_TABLE(pci, cafe_nand_tbl); | 828 | MODULE_DEVICE_TABLE(pci, cafe_nand_tbl); |
824 | 829 | ||
830 | static int cafe_nand_resume(struct pci_dev *pdev) | ||
831 | { | ||
832 | uint32_t ctrl; | ||
833 | struct mtd_info *mtd = pci_get_drvdata(pdev); | ||
834 | struct cafe_priv *cafe = mtd->priv; | ||
835 | |||
836 | /* Start off by resetting the NAND controller completely */ | ||
837 | cafe_writel(cafe, 1, NAND_RESET); | ||
838 | cafe_writel(cafe, 0, NAND_RESET); | ||
839 | cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); | ||
840 | |||
841 | /* Restore timing configuration */ | ||
842 | cafe_writel(cafe, timing[0], NAND_TIMING1); | ||
843 | cafe_writel(cafe, timing[1], NAND_TIMING2); | ||
844 | cafe_writel(cafe, timing[2], NAND_TIMING3); | ||
845 | |||
846 | /* Disable master reset, enable NAND clock */ | ||
847 | ctrl = cafe_readl(cafe, GLOBAL_CTRL); | ||
848 | ctrl &= 0xffffeff0; | ||
849 | ctrl |= 0x00007000; | ||
850 | cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL); | ||
851 | cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL); | ||
852 | cafe_writel(cafe, 0, NAND_DMA_CTRL); | ||
853 | cafe_writel(cafe, 0x7006, GLOBAL_CTRL); | ||
854 | cafe_writel(cafe, 0x700a, GLOBAL_CTRL); | ||
855 | |||
856 | /* Set up DMA address */ | ||
857 | cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0); | ||
858 | if (sizeof(cafe->dmaaddr) > 4) | ||
859 | /* Shift in two parts to shut the compiler up */ | ||
860 | cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1); | ||
861 | else | ||
862 | cafe_writel(cafe, 0, NAND_DMA_ADDR1); | ||
863 | |||
864 | /* Enable NAND IRQ in global IRQ mask register */ | ||
865 | cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); | ||
866 | return 0; | ||
867 | } | ||
868 | |||
825 | static struct pci_driver cafe_nand_pci_driver = { | 869 | static struct pci_driver cafe_nand_pci_driver = { |
826 | .name = "CAFÉ NAND", | 870 | .name = "CAFÉ NAND", |
827 | .id_table = cafe_nand_tbl, | 871 | .id_table = cafe_nand_tbl, |
828 | .probe = cafe_nand_probe, | 872 | .probe = cafe_nand_probe, |
829 | .remove = __devexit_p(cafe_nand_remove), | 873 | .remove = __devexit_p(cafe_nand_remove), |
830 | #ifdef CONFIG_PMx | ||
831 | .suspend = cafe_nand_suspend, | ||
832 | .resume = cafe_nand_resume, | 874 | .resume = cafe_nand_resume, |
833 | #endif | ||
834 | }; | 875 | }; |
835 | 876 | ||
836 | static int cafe_nand_init(void) | 877 | static int cafe_nand_init(void) |
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index e96259f22cca..ab9f5c5db38d 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c | |||
@@ -56,8 +56,6 @@ static unsigned long __initdata doc_locations[] = { | |||
56 | #endif /* CONFIG_MTD_DOCPROBE_HIGH */ | 56 | #endif /* CONFIG_MTD_DOCPROBE_HIGH */ |
57 | #elif defined(__PPC__) | 57 | #elif defined(__PPC__) |
58 | 0xe4000000, | 58 | 0xe4000000, |
59 | #elif defined(CONFIG_MOMENCO_OCELOT_G) | ||
60 | 0xff000000, | ||
61 | #else | 59 | #else |
62 | #warning Unknown architecture for DiskOnChip. No default probe locations defined | 60 | #warning Unknown architecture for DiskOnChip. No default probe locations defined |
63 | #endif | 61 | #endif |
diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c index 7e9afc4c7757..bed87290decc 100644 --- a/drivers/mtd/nand/excite_nandflash.c +++ b/drivers/mtd/nand/excite_nandflash.c | |||
@@ -27,7 +27,6 @@ | |||
27 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
28 | #include <linux/delay.h> | 28 | #include <linux/delay.h> |
29 | #include <linux/err.h> | 29 | #include <linux/err.h> |
30 | #include <linux/kernel.h> | ||
31 | 30 | ||
32 | #include <linux/mtd/mtd.h> | 31 | #include <linux/mtd/mtd.h> |
33 | #include <linux/mtd/nand.h> | 32 | #include <linux/mtd/nand.h> |
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 24ac6778b1a8..b4e0e7723894 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * Basic support for AG-AND chips is provided. | 7 | * Basic support for AG-AND chips is provided. |
8 | * | 8 | * |
9 | * Additional technical information is available on | 9 | * Additional technical information is available on |
10 | * http://www.linux-mtd.infradead.org/tech/nand.html | 10 | * http://www.linux-mtd.infradead.org/doc/nand.html |
11 | * | 11 | * |
12 | * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) | 12 | * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) |
13 | * 2002-2006 Thomas Gleixner (tglx@linutronix.de) | 13 | * 2002-2006 Thomas Gleixner (tglx@linutronix.de) |
@@ -2069,13 +2069,14 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, | |||
2069 | erase_exit: | 2069 | erase_exit: |
2070 | 2070 | ||
2071 | ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; | 2071 | ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO; |
2072 | /* Do call back function */ | ||
2073 | if (!ret) | ||
2074 | mtd_erase_callback(instr); | ||
2075 | 2072 | ||
2076 | /* Deselect and wake up anyone waiting on the device */ | 2073 | /* Deselect and wake up anyone waiting on the device */ |
2077 | nand_release_device(mtd); | 2074 | nand_release_device(mtd); |
2078 | 2075 | ||
2076 | /* Do call back function */ | ||
2077 | if (!ret) | ||
2078 | mtd_erase_callback(instr); | ||
2079 | |||
2079 | /* | 2080 | /* |
2080 | * If BBT requires refresh and erase was successful, rewrite any | 2081 | * If BBT requires refresh and erase was successful, rewrite any |
2081 | * selected bad block tables | 2082 | * selected bad block tables |
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 2fc674a190cf..a3e3ab0185d5 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c | |||
@@ -141,6 +141,7 @@ struct nand_manufacturers nand_manuf_ids[] = { | |||
141 | {NAND_MFR_STMICRO, "ST Micro"}, | 141 | {NAND_MFR_STMICRO, "ST Micro"}, |
142 | {NAND_MFR_HYNIX, "Hynix"}, | 142 | {NAND_MFR_HYNIX, "Hynix"}, |
143 | {NAND_MFR_MICRON, "Micron"}, | 143 | {NAND_MFR_MICRON, "Micron"}, |
144 | {NAND_MFR_AMD, "AMD"}, | ||
144 | {0x0, "Unknown"} | 145 | {0x0, "Unknown"} |
145 | }; | 146 | }; |
146 | 147 | ||
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 205df0f771fe..a7574807dc46 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c | |||
@@ -1272,7 +1272,13 @@ static int prog_page(struct nandsim *ns, int num) | |||
1272 | mypage = NS_GET_PAGE(ns); | 1272 | mypage = NS_GET_PAGE(ns); |
1273 | if (mypage->byte == NULL) { | 1273 | if (mypage->byte == NULL) { |
1274 | NS_DBG("prog_page: allocating page %d\n", ns->regs.row); | 1274 | NS_DBG("prog_page: allocating page %d\n", ns->regs.row); |
1275 | mypage->byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); | 1275 | /* |
1276 | * We allocate memory with GFP_NOFS because a flash FS may | ||
1277 | * utilize this. If it is holding an FS lock, then gets here, | ||
1278 | * then kmalloc runs writeback which goes to the FS again | ||
1279 | * and deadlocks. This was seen in practice. | ||
1280 | */ | ||
1281 | mypage->byte = kmalloc(ns->geom.pgszoob, GFP_NOFS); | ||
1276 | if (mypage->byte == NULL) { | 1282 | if (mypage->byte == NULL) { |
1277 | NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row); | 1283 | NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row); |
1278 | return -1; | 1284 | return -1; |
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index fd7a8d5ba29a..1c0e89f00e8d 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c | |||
@@ -24,7 +24,11 @@ | |||
24 | #include <linux/platform_device.h> | 24 | #include <linux/platform_device.h> |
25 | 25 | ||
26 | #include <asm/io.h> | 26 | #include <asm/io.h> |
27 | #ifdef CONFIG_40x | ||
28 | #include <asm/ibm405.h> | ||
29 | #else | ||
27 | #include <asm/ibm44x.h> | 30 | #include <asm/ibm44x.h> |
31 | #endif | ||
28 | 32 | ||
29 | struct ndfc_nand_mtd { | 33 | struct ndfc_nand_mtd { |
30 | struct mtd_info mtd; | 34 | struct mtd_info mtd; |
@@ -230,7 +234,11 @@ static int ndfc_nand_probe(struct platform_device *pdev) | |||
230 | struct ndfc_controller *ndfc = &ndfc_ctrl; | 234 | struct ndfc_controller *ndfc = &ndfc_ctrl; |
231 | unsigned long long phys = settings->ndfc_erpn | res->start; | 235 | unsigned long long phys = settings->ndfc_erpn | res->start; |
232 | 236 | ||
237 | #ifndef CONFIG_PHYS_64BIT | ||
238 | ndfc->ndfcbase = ioremap((phys_addr_t)phys, res->end - res->start + 1); | ||
239 | #else | ||
233 | ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1); | 240 | ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1); |
241 | #endif | ||
234 | if (!ndfc->ndfcbase) { | 242 | if (!ndfc->ndfcbase) { |
235 | printk(KERN_ERR "NDFC: ioremap failed\n"); | 243 | printk(KERN_ERR "NDFC: ioremap failed\n"); |
236 | return -EIO; | 244 | return -EIO; |
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 5fac4c421a20..b79a9cf2d162 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c | |||
@@ -60,8 +60,8 @@ | |||
60 | 60 | ||
61 | #include <asm/io.h> | 61 | #include <asm/io.h> |
62 | 62 | ||
63 | #include <asm/arch/regs-nand.h> | 63 | #include <asm/plat-s3c/regs-nand.h> |
64 | #include <asm/arch/nand.h> | 64 | #include <asm/plat-s3c/nand.h> |
65 | 65 | ||
66 | #ifdef CONFIG_MTD_NAND_S3C2410_HWECC | 66 | #ifdef CONFIG_MTD_NAND_S3C2410_HWECC |
67 | static int hardware_ecc = 1; | 67 | static int hardware_ecc = 1; |
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index c257d397d08a..cb41cbca64f7 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig | |||
@@ -40,4 +40,27 @@ config MTD_ONENAND_OTP | |||
40 | 40 | ||
41 | OTP block is fully-guaranteed to be a valid block. | 41 | OTP block is fully-guaranteed to be a valid block. |
42 | 42 | ||
43 | config MTD_ONENAND_2X_PROGRAM | ||
44 | bool "OneNAND 2X program support" | ||
45 | help | ||
46 | The 2X Program is an extension of Program Operation. | ||
47 | Since the device is equipped with two DataRAMs, and two-plane NAND | ||
48 | Flash memory array, these two component enables simultaneous program | ||
49 | of 4KiB. Plane1 has only even blocks such as block0, block2, block4 | ||
50 | while Plane2 has only odd blocks such as block1, block3, block5. | ||
51 | So MTD regards it as 4KiB page size and 256KiB block size | ||
52 | |||
53 | Now the following chips support it. (KFXXX16Q2M) | ||
54 | Demux: KFG2G16Q2M, KFH4G16Q2M, KFW8G16Q2M, | ||
55 | Mux: KFM2G16Q2M, KFN4G16Q2M, | ||
56 | |||
57 | And more recent chips | ||
58 | |||
59 | config MTD_ONENAND_SIM | ||
60 | tristate "OneNAND simulator support" | ||
61 | depends on MTD_PARTITIONS | ||
62 | help | ||
63 | The simulator may simulate various OneNAND flash chips for the | ||
64 | OneNAND MTD layer. | ||
65 | |||
43 | endif # MTD_ONENAND | 66 | endif # MTD_ONENAND |
diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile index 269cfe467345..4d2eacfd7e11 100644 --- a/drivers/mtd/onenand/Makefile +++ b/drivers/mtd/onenand/Makefile | |||
@@ -8,4 +8,7 @@ obj-$(CONFIG_MTD_ONENAND) += onenand.o | |||
8 | # Board specific. | 8 | # Board specific. |
9 | obj-$(CONFIG_MTD_ONENAND_GENERIC) += generic.o | 9 | obj-$(CONFIG_MTD_ONENAND_GENERIC) += generic.o |
10 | 10 | ||
11 | # Simulator | ||
12 | obj-$(CONFIG_MTD_ONENAND_SIM) += onenand_sim.o | ||
13 | |||
11 | onenand-objs = onenand_base.o onenand_bbt.o | 14 | onenand-objs = onenand_base.o onenand_bbt.o |
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 0537fac8de74..b2c40f67db83 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
@@ -206,6 +206,15 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
206 | default: | 206 | default: |
207 | block = (int) (addr >> this->erase_shift); | 207 | block = (int) (addr >> this->erase_shift); |
208 | page = (int) (addr >> this->page_shift); | 208 | page = (int) (addr >> this->page_shift); |
209 | |||
210 | if (ONENAND_IS_2PLANE(this)) { | ||
211 | /* Make the even block number */ | ||
212 | block &= ~1; | ||
213 | /* Is it the odd plane? */ | ||
214 | if (addr & this->writesize) | ||
215 | block++; | ||
216 | page >>= 1; | ||
217 | } | ||
209 | page &= this->page_mask; | 218 | page &= this->page_mask; |
210 | break; | 219 | break; |
211 | } | 220 | } |
@@ -216,8 +225,12 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
216 | value = onenand_bufferram_address(this, block); | 225 | value = onenand_bufferram_address(this, block); |
217 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); | 226 | this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); |
218 | 227 | ||
219 | /* Switch to the next data buffer */ | 228 | if (ONENAND_IS_2PLANE(this)) |
220 | ONENAND_SET_NEXT_BUFFERRAM(this); | 229 | /* It is always BufferRAM0 */ |
230 | ONENAND_SET_BUFFERRAM0(this); | ||
231 | else | ||
232 | /* Switch to the next data buffer */ | ||
233 | ONENAND_SET_NEXT_BUFFERRAM(this); | ||
221 | 234 | ||
222 | return 0; | 235 | return 0; |
223 | } | 236 | } |
@@ -247,6 +260,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le | |||
247 | break; | 260 | break; |
248 | 261 | ||
249 | default: | 262 | default: |
263 | if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG) | ||
264 | cmd = ONENAND_CMD_2X_PROG; | ||
250 | dataram = ONENAND_CURRENT_BUFFERRAM(this); | 265 | dataram = ONENAND_CURRENT_BUFFERRAM(this); |
251 | break; | 266 | break; |
252 | } | 267 | } |
@@ -318,12 +333,14 @@ static int onenand_wait(struct mtd_info *mtd, int state) | |||
318 | if (interrupt & ONENAND_INT_READ) { | 333 | if (interrupt & ONENAND_INT_READ) { |
319 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); | 334 | int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); |
320 | if (ecc) { | 335 | if (ecc) { |
321 | printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); | ||
322 | if (ecc & ONENAND_ECC_2BIT_ALL) { | 336 | if (ecc & ONENAND_ECC_2BIT_ALL) { |
337 | printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); | ||
323 | mtd->ecc_stats.failed++; | 338 | mtd->ecc_stats.failed++; |
324 | return ecc; | 339 | return ecc; |
325 | } else if (ecc & ONENAND_ECC_1BIT_ALL) | 340 | } else if (ecc & ONENAND_ECC_1BIT_ALL) { |
341 | printk(KERN_INFO "onenand_wait: correctable ECC error = 0x%04x\n", ecc); | ||
326 | mtd->ecc_stats.corrected++; | 342 | mtd->ecc_stats.corrected++; |
343 | } | ||
327 | } | 344 | } |
328 | } else if (state == FL_READING) { | 345 | } else if (state == FL_READING) { |
329 | printk(KERN_ERR "onenand_wait: read timeout! ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); | 346 | printk(KERN_ERR "onenand_wait: read timeout! ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); |
@@ -445,8 +462,9 @@ static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) | |||
445 | struct onenand_chip *this = mtd->priv; | 462 | struct onenand_chip *this = mtd->priv; |
446 | 463 | ||
447 | if (ONENAND_CURRENT_BUFFERRAM(this)) { | 464 | if (ONENAND_CURRENT_BUFFERRAM(this)) { |
465 | /* Note: the 'this->writesize' is a real page size */ | ||
448 | if (area == ONENAND_DATARAM) | 466 | if (area == ONENAND_DATARAM) |
449 | return mtd->writesize; | 467 | return this->writesize; |
450 | if (area == ONENAND_SPARERAM) | 468 | if (area == ONENAND_SPARERAM) |
451 | return mtd->oobsize; | 469 | return mtd->oobsize; |
452 | } | 470 | } |
@@ -572,6 +590,30 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area, | |||
572 | } | 590 | } |
573 | 591 | ||
574 | /** | 592 | /** |
593 | * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode | ||
594 | * @param mtd MTD data structure | ||
595 | * @param addr address to check | ||
596 | * @return blockpage address | ||
597 | * | ||
598 | * Get blockpage address at 2x program mode | ||
599 | */ | ||
600 | static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr) | ||
601 | { | ||
602 | struct onenand_chip *this = mtd->priv; | ||
603 | int blockpage, block, page; | ||
604 | |||
605 | /* Calculate the even block number */ | ||
606 | block = (int) (addr >> this->erase_shift) & ~1; | ||
607 | /* Is it the odd plane? */ | ||
608 | if (addr & this->writesize) | ||
609 | block++; | ||
610 | page = (int) (addr >> (this->page_shift + 1)) & this->page_mask; | ||
611 | blockpage = (block << 7) | page; | ||
612 | |||
613 | return blockpage; | ||
614 | } | ||
615 | |||
616 | /** | ||
575 | * onenand_check_bufferram - [GENERIC] Check BufferRAM information | 617 | * onenand_check_bufferram - [GENERIC] Check BufferRAM information |
576 | * @param mtd MTD data structure | 618 | * @param mtd MTD data structure |
577 | * @param addr address to check | 619 | * @param addr address to check |
@@ -585,7 +627,10 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) | |||
585 | int blockpage, found = 0; | 627 | int blockpage, found = 0; |
586 | unsigned int i; | 628 | unsigned int i; |
587 | 629 | ||
588 | blockpage = (int) (addr >> this->page_shift); | 630 | if (ONENAND_IS_2PLANE(this)) |
631 | blockpage = onenand_get_2x_blockpage(mtd, addr); | ||
632 | else | ||
633 | blockpage = (int) (addr >> this->page_shift); | ||
589 | 634 | ||
590 | /* Is there valid data? */ | 635 | /* Is there valid data? */ |
591 | i = ONENAND_CURRENT_BUFFERRAM(this); | 636 | i = ONENAND_CURRENT_BUFFERRAM(this); |
@@ -625,7 +670,10 @@ static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, | |||
625 | int blockpage; | 670 | int blockpage; |
626 | unsigned int i; | 671 | unsigned int i; |
627 | 672 | ||
628 | blockpage = (int) (addr >> this->page_shift); | 673 | if (ONENAND_IS_2PLANE(this)) |
674 | blockpage = onenand_get_2x_blockpage(mtd, addr); | ||
675 | else | ||
676 | blockpage = (int) (addr >> this->page_shift); | ||
629 | 677 | ||
630 | /* Invalidate another BufferRAM */ | 678 | /* Invalidate another BufferRAM */ |
631 | i = ONENAND_NEXT_BUFFERRAM(this); | 679 | i = ONENAND_NEXT_BUFFERRAM(this); |
@@ -717,36 +765,86 @@ static void onenand_release_device(struct mtd_info *mtd) | |||
717 | } | 765 | } |
718 | 766 | ||
719 | /** | 767 | /** |
720 | * onenand_read - [MTD Interface] Read data from flash | 768 | * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer |
769 | * @param mtd MTD device structure | ||
770 | * @param buf destination address | ||
771 | * @param column oob offset to read from | ||
772 | * @param thislen oob length to read | ||
773 | */ | ||
774 | static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column, | ||
775 | int thislen) | ||
776 | { | ||
777 | struct onenand_chip *this = mtd->priv; | ||
778 | struct nand_oobfree *free; | ||
779 | int readcol = column; | ||
780 | int readend = column + thislen; | ||
781 | int lastgap = 0; | ||
782 | unsigned int i; | ||
783 | uint8_t *oob_buf = this->oob_buf; | ||
784 | |||
785 | free = this->ecclayout->oobfree; | ||
786 | for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { | ||
787 | if (readcol >= lastgap) | ||
788 | readcol += free->offset - lastgap; | ||
789 | if (readend >= lastgap) | ||
790 | readend += free->offset - lastgap; | ||
791 | lastgap = free->offset + free->length; | ||
792 | } | ||
793 | this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); | ||
794 | free = this->ecclayout->oobfree; | ||
795 | for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { | ||
796 | int free_end = free->offset + free->length; | ||
797 | if (free->offset < readend && free_end > readcol) { | ||
798 | int st = max_t(int,free->offset,readcol); | ||
799 | int ed = min_t(int,free_end,readend); | ||
800 | int n = ed - st; | ||
801 | memcpy(buf, oob_buf + st, n); | ||
802 | buf += n; | ||
803 | } else if (column == 0) | ||
804 | break; | ||
805 | } | ||
806 | return 0; | ||
807 | } | ||
808 | |||
809 | /** | ||
810 | * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band | ||
721 | * @param mtd MTD device structure | 811 | * @param mtd MTD device structure |
722 | * @param from offset to read from | 812 | * @param from offset to read from |
723 | * @param len number of bytes to read | 813 | * @param ops: oob operation description structure |
724 | * @param retlen pointer to variable to store the number of read bytes | ||
725 | * @param buf the databuffer to put data | ||
726 | * | 814 | * |
727 | * Read with ecc | 815 | * OneNAND read main and/or out-of-band data |
728 | */ | 816 | */ |
729 | static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | 817 | static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, |
730 | size_t *retlen, u_char *buf) | 818 | struct mtd_oob_ops *ops) |
731 | { | 819 | { |
732 | struct onenand_chip *this = mtd->priv; | 820 | struct onenand_chip *this = mtd->priv; |
733 | struct mtd_ecc_stats stats; | 821 | struct mtd_ecc_stats stats; |
734 | int read = 0, column; | 822 | size_t len = ops->len; |
735 | int thislen; | 823 | size_t ooblen = ops->ooblen; |
824 | u_char *buf = ops->datbuf; | ||
825 | u_char *oobbuf = ops->oobbuf; | ||
826 | int read = 0, column, thislen; | ||
827 | int oobread = 0, oobcolumn, thisooblen, oobsize; | ||
736 | int ret = 0, boundary = 0; | 828 | int ret = 0, boundary = 0; |
829 | int writesize = this->writesize; | ||
737 | 830 | ||
738 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); | 831 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); |
832 | |||
833 | if (ops->mode == MTD_OOB_AUTO) | ||
834 | oobsize = this->ecclayout->oobavail; | ||
835 | else | ||
836 | oobsize = mtd->oobsize; | ||
837 | |||
838 | oobcolumn = from & (mtd->oobsize - 1); | ||
739 | 839 | ||
740 | /* Do not allow reads past end of device */ | 840 | /* Do not allow reads past end of device */ |
741 | if ((from + len) > mtd->size) { | 841 | if ((from + len) > mtd->size) { |
742 | printk(KERN_ERR "onenand_read: Attempt read beyond end of device\n"); | 842 | printk(KERN_ERR "onenand_read_ops_nolock: Attempt read beyond end of device\n"); |
743 | *retlen = 0; | 843 | ops->retlen = 0; |
844 | ops->oobretlen = 0; | ||
744 | return -EINVAL; | 845 | return -EINVAL; |
745 | } | 846 | } |
746 | 847 | ||
747 | /* Grab the lock and see if the device is available */ | ||
748 | onenand_get_device(mtd, FL_READING); | ||
749 | |||
750 | stats = mtd->ecc_stats; | 848 | stats = mtd->ecc_stats; |
751 | 849 | ||
752 | /* Read-while-load method */ | 850 | /* Read-while-load method */ |
@@ -754,22 +852,22 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
754 | /* Do first load to bufferRAM */ | 852 | /* Do first load to bufferRAM */ |
755 | if (read < len) { | 853 | if (read < len) { |
756 | if (!onenand_check_bufferram(mtd, from)) { | 854 | if (!onenand_check_bufferram(mtd, from)) { |
757 | this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize); | 855 | this->command(mtd, ONENAND_CMD_READ, from, writesize); |
758 | ret = this->wait(mtd, FL_READING); | 856 | ret = this->wait(mtd, FL_READING); |
759 | onenand_update_bufferram(mtd, from, !ret); | 857 | onenand_update_bufferram(mtd, from, !ret); |
760 | } | 858 | } |
761 | } | 859 | } |
762 | 860 | ||
763 | thislen = min_t(int, mtd->writesize, len - read); | 861 | thislen = min_t(int, writesize, len - read); |
764 | column = from & (mtd->writesize - 1); | 862 | column = from & (writesize - 1); |
765 | if (column + thislen > mtd->writesize) | 863 | if (column + thislen > writesize) |
766 | thislen = mtd->writesize - column; | 864 | thislen = writesize - column; |
767 | 865 | ||
768 | while (!ret) { | 866 | while (!ret) { |
769 | /* If there is more to load then start next load */ | 867 | /* If there is more to load then start next load */ |
770 | from += thislen; | 868 | from += thislen; |
771 | if (read + thislen < len) { | 869 | if (read + thislen < len) { |
772 | this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize); | 870 | this->command(mtd, ONENAND_CMD_READ, from, writesize); |
773 | /* | 871 | /* |
774 | * Chip boundary handling in DDP | 872 | * Chip boundary handling in DDP |
775 | * Now we issued chip 1 read and pointed chip 1 | 873 | * Now we issued chip 1 read and pointed chip 1 |
@@ -785,6 +883,21 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
785 | } | 883 | } |
786 | /* While load is going, read from last bufferRAM */ | 884 | /* While load is going, read from last bufferRAM */ |
787 | this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); | 885 | this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); |
886 | |||
887 | /* Read oob area if needed */ | ||
888 | if (oobbuf) { | ||
889 | thisooblen = oobsize - oobcolumn; | ||
890 | thisooblen = min_t(int, thisooblen, ooblen - oobread); | ||
891 | |||
892 | if (ops->mode == MTD_OOB_AUTO) | ||
893 | onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); | ||
894 | else | ||
895 | this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); | ||
896 | oobread += thisooblen; | ||
897 | oobbuf += thisooblen; | ||
898 | oobcolumn = 0; | ||
899 | } | ||
900 | |||
788 | /* See if we are done */ | 901 | /* See if we are done */ |
789 | read += thislen; | 902 | read += thislen; |
790 | if (read == len) | 903 | if (read == len) |
@@ -794,7 +907,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
794 | this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); | 907 | this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); |
795 | ONENAND_SET_NEXT_BUFFERRAM(this); | 908 | ONENAND_SET_NEXT_BUFFERRAM(this); |
796 | buf += thislen; | 909 | buf += thislen; |
797 | thislen = min_t(int, mtd->writesize, len - read); | 910 | thislen = min_t(int, writesize, len - read); |
798 | column = 0; | 911 | column = 0; |
799 | cond_resched(); | 912 | cond_resched(); |
800 | /* Now wait for load */ | 913 | /* Now wait for load */ |
@@ -802,15 +915,13 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
802 | onenand_update_bufferram(mtd, from, !ret); | 915 | onenand_update_bufferram(mtd, from, !ret); |
803 | } | 916 | } |
804 | 917 | ||
805 | /* Deselect and wake up anyone waiting on the device */ | ||
806 | onenand_release_device(mtd); | ||
807 | |||
808 | /* | 918 | /* |
809 | * Return success, if no ECC failures, else -EBADMSG | 919 | * Return success, if no ECC failures, else -EBADMSG |
810 | * fs driver will take care of that, because | 920 | * fs driver will take care of that, because |
811 | * retlen == desired len and result == -EBADMSG | 921 | * retlen == desired len and result == -EBADMSG |
812 | */ | 922 | */ |
813 | *retlen = read; | 923 | ops->retlen = read; |
924 | ops->oobretlen = oobread; | ||
814 | 925 | ||
815 | if (mtd->ecc_stats.failed - stats.failed) | 926 | if (mtd->ecc_stats.failed - stats.failed) |
816 | return -EBADMSG; | 927 | return -EBADMSG; |
@@ -822,69 +933,29 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
822 | } | 933 | } |
823 | 934 | ||
824 | /** | 935 | /** |
825 | * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer | 936 | * onenand_read_oob_nolock - [MTD Interface] OneNAND read out-of-band |
826 | * @param mtd MTD device structure | ||
827 | * @param buf destination address | ||
828 | * @param column oob offset to read from | ||
829 | * @param thislen oob length to read | ||
830 | */ | ||
831 | static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column, | ||
832 | int thislen) | ||
833 | { | ||
834 | struct onenand_chip *this = mtd->priv; | ||
835 | struct nand_oobfree *free; | ||
836 | int readcol = column; | ||
837 | int readend = column + thislen; | ||
838 | int lastgap = 0; | ||
839 | unsigned int i; | ||
840 | uint8_t *oob_buf = this->oob_buf; | ||
841 | |||
842 | free = this->ecclayout->oobfree; | ||
843 | for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { | ||
844 | if (readcol >= lastgap) | ||
845 | readcol += free->offset - lastgap; | ||
846 | if (readend >= lastgap) | ||
847 | readend += free->offset - lastgap; | ||
848 | lastgap = free->offset + free->length; | ||
849 | } | ||
850 | this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); | ||
851 | free = this->ecclayout->oobfree; | ||
852 | for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { | ||
853 | int free_end = free->offset + free->length; | ||
854 | if (free->offset < readend && free_end > readcol) { | ||
855 | int st = max_t(int,free->offset,readcol); | ||
856 | int ed = min_t(int,free_end,readend); | ||
857 | int n = ed - st; | ||
858 | memcpy(buf, oob_buf + st, n); | ||
859 | buf += n; | ||
860 | } else if (column == 0) | ||
861 | break; | ||
862 | } | ||
863 | return 0; | ||
864 | } | ||
865 | |||
866 | /** | ||
867 | * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band | ||
868 | * @param mtd MTD device structure | 937 | * @param mtd MTD device structure |
869 | * @param from offset to read from | 938 | * @param from offset to read from |
870 | * @param len number of bytes to read | 939 | * @param ops: oob operation description structure |
871 | * @param retlen pointer to variable to store the number of read bytes | ||
872 | * @param buf the databuffer to put data | ||
873 | * @param mode operation mode | ||
874 | * | 940 | * |
875 | * OneNAND read out-of-band data from the spare area | 941 | * OneNAND read out-of-band data from the spare area |
876 | */ | 942 | */ |
877 | static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | 943 | static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, |
878 | size_t *retlen, u_char *buf, mtd_oob_mode_t mode) | 944 | struct mtd_oob_ops *ops) |
879 | { | 945 | { |
880 | struct onenand_chip *this = mtd->priv; | 946 | struct onenand_chip *this = mtd->priv; |
881 | int read = 0, thislen, column, oobsize; | 947 | int read = 0, thislen, column, oobsize; |
948 | size_t len = ops->ooblen; | ||
949 | mtd_oob_mode_t mode = ops->mode; | ||
950 | u_char *buf = ops->oobbuf; | ||
882 | int ret = 0; | 951 | int ret = 0; |
883 | 952 | ||
884 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); | 953 | from += ops->ooboffs; |
954 | |||
955 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); | ||
885 | 956 | ||
886 | /* Initialize return length value */ | 957 | /* Initialize return length value */ |
887 | *retlen = 0; | 958 | ops->oobretlen = 0; |
888 | 959 | ||
889 | if (mode == MTD_OOB_AUTO) | 960 | if (mode == MTD_OOB_AUTO) |
890 | oobsize = this->ecclayout->oobavail; | 961 | oobsize = this->ecclayout->oobavail; |
@@ -894,7 +965,7 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
894 | column = from & (mtd->oobsize - 1); | 965 | column = from & (mtd->oobsize - 1); |
895 | 966 | ||
896 | if (unlikely(column >= oobsize)) { | 967 | if (unlikely(column >= oobsize)) { |
897 | printk(KERN_ERR "onenand_read_oob: Attempted to start read outside oob\n"); | 968 | printk(KERN_ERR "onenand_read_oob_nolock: Attempted to start read outside oob\n"); |
898 | return -EINVAL; | 969 | return -EINVAL; |
899 | } | 970 | } |
900 | 971 | ||
@@ -902,13 +973,10 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
902 | if (unlikely(from >= mtd->size || | 973 | if (unlikely(from >= mtd->size || |
903 | column + len > ((mtd->size >> this->page_shift) - | 974 | column + len > ((mtd->size >> this->page_shift) - |
904 | (from >> this->page_shift)) * oobsize)) { | 975 | (from >> this->page_shift)) * oobsize)) { |
905 | printk(KERN_ERR "onenand_read_oob: Attempted to read beyond end of device\n"); | 976 | printk(KERN_ERR "onenand_read_oob_nolock: Attempted to read beyond end of device\n"); |
906 | return -EINVAL; | 977 | return -EINVAL; |
907 | } | 978 | } |
908 | 979 | ||
909 | /* Grab the lock and see if the device is available */ | ||
910 | onenand_get_device(mtd, FL_READING); | ||
911 | |||
912 | while (read < len) { | 980 | while (read < len) { |
913 | cond_resched(); | 981 | cond_resched(); |
914 | 982 | ||
@@ -928,7 +996,7 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
928 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); | 996 | this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); |
929 | 997 | ||
930 | if (ret) { | 998 | if (ret) { |
931 | printk(KERN_ERR "onenand_read_oob: read failed = 0x%x\n", ret); | 999 | printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); |
932 | break; | 1000 | break; |
933 | } | 1001 | } |
934 | 1002 | ||
@@ -947,22 +1015,52 @@ static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, | |||
947 | } | 1015 | } |
948 | } | 1016 | } |
949 | 1017 | ||
950 | /* Deselect and wake up anyone waiting on the device */ | 1018 | ops->oobretlen = read; |
1019 | return ret; | ||
1020 | } | ||
1021 | |||
1022 | /** | ||
1023 | * onenand_read - [MTD Interface] Read data from flash | ||
1024 | * @param mtd MTD device structure | ||
1025 | * @param from offset to read from | ||
1026 | * @param len number of bytes to read | ||
1027 | * @param retlen pointer to variable to store the number of read bytes | ||
1028 | * @param buf the databuffer to put data | ||
1029 | * | ||
1030 | * Read with ecc | ||
1031 | */ | ||
1032 | static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
1033 | size_t *retlen, u_char *buf) | ||
1034 | { | ||
1035 | struct mtd_oob_ops ops = { | ||
1036 | .len = len, | ||
1037 | .ooblen = 0, | ||
1038 | .datbuf = buf, | ||
1039 | .oobbuf = NULL, | ||
1040 | }; | ||
1041 | int ret; | ||
1042 | |||
1043 | onenand_get_device(mtd, FL_READING); | ||
1044 | ret = onenand_read_ops_nolock(mtd, from, &ops); | ||
951 | onenand_release_device(mtd); | 1045 | onenand_release_device(mtd); |
952 | 1046 | ||
953 | *retlen = read; | 1047 | *retlen = ops.retlen; |
954 | return ret; | 1048 | return ret; |
955 | } | 1049 | } |
956 | 1050 | ||
957 | /** | 1051 | /** |
958 | * onenand_read_oob - [MTD Interface] NAND write data and/or out-of-band | 1052 | * onenand_read_oob - [MTD Interface] Read main and/or out-of-band |
959 | * @param mtd: MTD device structure | 1053 | * @param mtd: MTD device structure |
960 | * @param from: offset to read from | 1054 | * @param from: offset to read from |
961 | * @param ops: oob operation description structure | 1055 | * @param ops: oob operation description structure |
1056 | |||
1057 | * Read main and/or out-of-band | ||
962 | */ | 1058 | */ |
963 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, | 1059 | static int onenand_read_oob(struct mtd_info *mtd, loff_t from, |
964 | struct mtd_oob_ops *ops) | 1060 | struct mtd_oob_ops *ops) |
965 | { | 1061 | { |
1062 | int ret; | ||
1063 | |||
966 | switch (ops->mode) { | 1064 | switch (ops->mode) { |
967 | case MTD_OOB_PLACE: | 1065 | case MTD_OOB_PLACE: |
968 | case MTD_OOB_AUTO: | 1066 | case MTD_OOB_AUTO: |
@@ -972,8 +1070,15 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, | |||
972 | default: | 1070 | default: |
973 | return -EINVAL; | 1071 | return -EINVAL; |
974 | } | 1072 | } |
975 | return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen, | 1073 | |
976 | &ops->oobretlen, ops->oobbuf, ops->mode); | 1074 | onenand_get_device(mtd, FL_READING); |
1075 | if (ops->datbuf) | ||
1076 | ret = onenand_read_ops_nolock(mtd, from, ops); | ||
1077 | else | ||
1078 | ret = onenand_read_oob_nolock(mtd, from, ops); | ||
1079 | onenand_release_device(mtd); | ||
1080 | |||
1081 | return ret; | ||
977 | } | 1082 | } |
978 | 1083 | ||
979 | /** | 1084 | /** |
@@ -1079,7 +1184,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, | |||
1079 | /* Read more? */ | 1184 | /* Read more? */ |
1080 | if (read < len) { | 1185 | if (read < len) { |
1081 | /* Update Page size */ | 1186 | /* Update Page size */ |
1082 | from += mtd->writesize; | 1187 | from += this->writesize; |
1083 | column = 0; | 1188 | column = 0; |
1084 | } | 1189 | } |
1085 | } | 1190 | } |
@@ -1097,7 +1202,6 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, | |||
1097 | * @param mtd MTD device structure | 1202 | * @param mtd MTD device structure |
1098 | * @param buf the databuffer to verify | 1203 | * @param buf the databuffer to verify |
1099 | * @param to offset to read from | 1204 | * @param to offset to read from |
1100 | * | ||
1101 | */ | 1205 | */ |
1102 | static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) | 1206 | static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) |
1103 | { | 1207 | { |
@@ -1125,7 +1229,6 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to | |||
1125 | * @param buf the databuffer to verify | 1229 | * @param buf the databuffer to verify |
1126 | * @param addr offset to read from | 1230 | * @param addr offset to read from |
1127 | * @param len number of bytes to read and compare | 1231 | * @param len number of bytes to read and compare |
1128 | * | ||
1129 | */ | 1232 | */ |
1130 | static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) | 1233 | static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) |
1131 | { | 1234 | { |
@@ -1135,12 +1238,12 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, | |||
1135 | int thislen, column; | 1238 | int thislen, column; |
1136 | 1239 | ||
1137 | while (len != 0) { | 1240 | while (len != 0) { |
1138 | thislen = min_t(int, mtd->writesize, len); | 1241 | thislen = min_t(int, this->writesize, len); |
1139 | column = addr & (mtd->writesize - 1); | 1242 | column = addr & (this->writesize - 1); |
1140 | if (column + thislen > mtd->writesize) | 1243 | if (column + thislen > this->writesize) |
1141 | thislen = mtd->writesize - column; | 1244 | thislen = this->writesize - column; |
1142 | 1245 | ||
1143 | this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); | 1246 | this->command(mtd, ONENAND_CMD_READ, addr, this->writesize); |
1144 | 1247 | ||
1145 | onenand_update_bufferram(mtd, addr, 0); | 1248 | onenand_update_bufferram(mtd, addr, 0); |
1146 | 1249 | ||
@@ -1171,50 +1274,101 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, | |||
1171 | #define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0) | 1274 | #define NOTALIGNED(x) ((x & (this->subpagesize - 1)) != 0) |
1172 | 1275 | ||
1173 | /** | 1276 | /** |
1174 | * onenand_write - [MTD Interface] write buffer to FLASH | 1277 | * onenand_fill_auto_oob - [Internal] oob auto-placement transfer |
1278 | * @param mtd MTD device structure | ||
1279 | * @param oob_buf oob buffer | ||
1280 | * @param buf source address | ||
1281 | * @param column oob offset to write to | ||
1282 | * @param thislen oob length to write | ||
1283 | */ | ||
1284 | static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, | ||
1285 | const u_char *buf, int column, int thislen) | ||
1286 | { | ||
1287 | struct onenand_chip *this = mtd->priv; | ||
1288 | struct nand_oobfree *free; | ||
1289 | int writecol = column; | ||
1290 | int writeend = column + thislen; | ||
1291 | int lastgap = 0; | ||
1292 | unsigned int i; | ||
1293 | |||
1294 | free = this->ecclayout->oobfree; | ||
1295 | for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { | ||
1296 | if (writecol >= lastgap) | ||
1297 | writecol += free->offset - lastgap; | ||
1298 | if (writeend >= lastgap) | ||
1299 | writeend += free->offset - lastgap; | ||
1300 | lastgap = free->offset + free->length; | ||
1301 | } | ||
1302 | free = this->ecclayout->oobfree; | ||
1303 | for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { | ||
1304 | int free_end = free->offset + free->length; | ||
1305 | if (free->offset < writeend && free_end > writecol) { | ||
1306 | int st = max_t(int,free->offset,writecol); | ||
1307 | int ed = min_t(int,free_end,writeend); | ||
1308 | int n = ed - st; | ||
1309 | memcpy(oob_buf + st, buf, n); | ||
1310 | buf += n; | ||
1311 | } else if (column == 0) | ||
1312 | break; | ||
1313 | } | ||
1314 | return 0; | ||
1315 | } | ||
1316 | |||
1317 | /** | ||
1318 | * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band | ||
1175 | * @param mtd MTD device structure | 1319 | * @param mtd MTD device structure |
1176 | * @param to offset to write to | 1320 | * @param to offset to write to |
1177 | * @param len number of bytes to write | 1321 | * @param ops oob operation description structure |
1178 | * @param retlen pointer to variable to store the number of written bytes | ||
1179 | * @param buf the data to write | ||
1180 | * | 1322 | * |
1181 | * Write with ECC | 1323 | * Write main and/or oob with ECC |
1182 | */ | 1324 | */ |
1183 | static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | 1325 | static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, |
1184 | size_t *retlen, const u_char *buf) | 1326 | struct mtd_oob_ops *ops) |
1185 | { | 1327 | { |
1186 | struct onenand_chip *this = mtd->priv; | 1328 | struct onenand_chip *this = mtd->priv; |
1187 | int written = 0; | 1329 | int written = 0, column, thislen, subpage; |
1330 | int oobwritten = 0, oobcolumn, thisooblen, oobsize; | ||
1331 | size_t len = ops->len; | ||
1332 | size_t ooblen = ops->ooblen; | ||
1333 | const u_char *buf = ops->datbuf; | ||
1334 | const u_char *oob = ops->oobbuf; | ||
1335 | u_char *oobbuf; | ||
1188 | int ret = 0; | 1336 | int ret = 0; |
1189 | int column, subpage; | ||
1190 | 1337 | ||
1191 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); | 1338 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); |
1192 | 1339 | ||
1193 | /* Initialize retlen, in case of early exit */ | 1340 | /* Initialize retlen, in case of early exit */ |
1194 | *retlen = 0; | 1341 | ops->retlen = 0; |
1342 | ops->oobretlen = 0; | ||
1195 | 1343 | ||
1196 | /* Do not allow writes past end of device */ | 1344 | /* Do not allow writes past end of device */ |
1197 | if (unlikely((to + len) > mtd->size)) { | 1345 | if (unlikely((to + len) > mtd->size)) { |
1198 | printk(KERN_ERR "onenand_write: Attempt write to past end of device\n"); | 1346 | printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n"); |
1199 | return -EINVAL; | 1347 | return -EINVAL; |
1200 | } | 1348 | } |
1201 | 1349 | ||
1202 | /* Reject writes, which are not page aligned */ | 1350 | /* Reject writes, which are not page aligned */ |
1203 | if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { | 1351 | if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { |
1204 | printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n"); | 1352 | printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n"); |
1205 | return -EINVAL; | 1353 | return -EINVAL; |
1206 | } | 1354 | } |
1207 | 1355 | ||
1208 | column = to & (mtd->writesize - 1); | 1356 | if (ops->mode == MTD_OOB_AUTO) |
1357 | oobsize = this->ecclayout->oobavail; | ||
1358 | else | ||
1359 | oobsize = mtd->oobsize; | ||
1209 | 1360 | ||
1210 | /* Grab the lock and see if the device is available */ | 1361 | oobcolumn = to & (mtd->oobsize - 1); |
1211 | onenand_get_device(mtd, FL_WRITING); | 1362 | |
1363 | column = to & (mtd->writesize - 1); | ||
1212 | 1364 | ||
1213 | /* Loop until all data write */ | 1365 | /* Loop until all data write */ |
1214 | while (written < len) { | 1366 | while (written < len) { |
1215 | int thislen = min_t(int, mtd->writesize - column, len - written); | ||
1216 | u_char *wbuf = (u_char *) buf; | 1367 | u_char *wbuf = (u_char *) buf; |
1217 | 1368 | ||
1369 | thislen = min_t(int, mtd->writesize - column, len - written); | ||
1370 | thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten); | ||
1371 | |||
1218 | cond_resched(); | 1372 | cond_resched(); |
1219 | 1373 | ||
1220 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); | 1374 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); |
@@ -1228,7 +1382,25 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
1228 | } | 1382 | } |
1229 | 1383 | ||
1230 | this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); | 1384 | this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); |
1231 | this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); | 1385 | |
1386 | if (oob) { | ||
1387 | oobbuf = this->oob_buf; | ||
1388 | |||
1389 | /* We send data to spare ram with oobsize | ||
1390 | * to prevent byte access */ | ||
1391 | memset(oobbuf, 0xff, mtd->oobsize); | ||
1392 | if (ops->mode == MTD_OOB_AUTO) | ||
1393 | onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen); | ||
1394 | else | ||
1395 | memcpy(oobbuf + oobcolumn, oob, thisooblen); | ||
1396 | |||
1397 | oobwritten += thisooblen; | ||
1398 | oob += thisooblen; | ||
1399 | oobcolumn = 0; | ||
1400 | } else | ||
1401 | oobbuf = (u_char *) ffchars; | ||
1402 | |||
1403 | this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); | ||
1232 | 1404 | ||
1233 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); | 1405 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); |
1234 | 1406 | ||
@@ -1236,16 +1408,20 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
1236 | 1408 | ||
1237 | /* In partial page write we don't update bufferram */ | 1409 | /* In partial page write we don't update bufferram */ |
1238 | onenand_update_bufferram(mtd, to, !ret && !subpage); | 1410 | onenand_update_bufferram(mtd, to, !ret && !subpage); |
1411 | if (ONENAND_IS_2PLANE(this)) { | ||
1412 | ONENAND_SET_BUFFERRAM1(this); | ||
1413 | onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage); | ||
1414 | } | ||
1239 | 1415 | ||
1240 | if (ret) { | 1416 | if (ret) { |
1241 | printk(KERN_ERR "onenand_write: write filaed %d\n", ret); | 1417 | printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); |
1242 | break; | 1418 | break; |
1243 | } | 1419 | } |
1244 | 1420 | ||
1245 | /* Only check verify write turn on */ | 1421 | /* Only check verify write turn on */ |
1246 | ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen); | 1422 | ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen); |
1247 | if (ret) { | 1423 | if (ret) { |
1248 | printk(KERN_ERR "onenand_write: verify failed %d\n", ret); | 1424 | printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); |
1249 | break; | 1425 | break; |
1250 | } | 1426 | } |
1251 | 1427 | ||
@@ -1262,54 +1438,14 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
1262 | /* Deselect and wake up anyone waiting on the device */ | 1438 | /* Deselect and wake up anyone waiting on the device */ |
1263 | onenand_release_device(mtd); | 1439 | onenand_release_device(mtd); |
1264 | 1440 | ||
1265 | *retlen = written; | 1441 | ops->retlen = written; |
1266 | 1442 | ||
1267 | return ret; | 1443 | return ret; |
1268 | } | 1444 | } |
1269 | 1445 | ||
1270 | /** | ||
1271 | * onenand_fill_auto_oob - [Internal] oob auto-placement transfer | ||
1272 | * @param mtd MTD device structure | ||
1273 | * @param oob_buf oob buffer | ||
1274 | * @param buf source address | ||
1275 | * @param column oob offset to write to | ||
1276 | * @param thislen oob length to write | ||
1277 | */ | ||
1278 | static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, | ||
1279 | const u_char *buf, int column, int thislen) | ||
1280 | { | ||
1281 | struct onenand_chip *this = mtd->priv; | ||
1282 | struct nand_oobfree *free; | ||
1283 | int writecol = column; | ||
1284 | int writeend = column + thislen; | ||
1285 | int lastgap = 0; | ||
1286 | unsigned int i; | ||
1287 | |||
1288 | free = this->ecclayout->oobfree; | ||
1289 | for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { | ||
1290 | if (writecol >= lastgap) | ||
1291 | writecol += free->offset - lastgap; | ||
1292 | if (writeend >= lastgap) | ||
1293 | writeend += free->offset - lastgap; | ||
1294 | lastgap = free->offset + free->length; | ||
1295 | } | ||
1296 | free = this->ecclayout->oobfree; | ||
1297 | for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { | ||
1298 | int free_end = free->offset + free->length; | ||
1299 | if (free->offset < writeend && free_end > writecol) { | ||
1300 | int st = max_t(int,free->offset,writecol); | ||
1301 | int ed = min_t(int,free_end,writeend); | ||
1302 | int n = ed - st; | ||
1303 | memcpy(oob_buf + st, buf, n); | ||
1304 | buf += n; | ||
1305 | } else if (column == 0) | ||
1306 | break; | ||
1307 | } | ||
1308 | return 0; | ||
1309 | } | ||
1310 | 1446 | ||
1311 | /** | 1447 | /** |
1312 | * onenand_do_write_oob - [Internal] OneNAND write out-of-band | 1448 | * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band |
1313 | * @param mtd MTD device structure | 1449 | * @param mtd MTD device structure |
1314 | * @param to offset to write to | 1450 | * @param to offset to write to |
1315 | * @param len number of bytes to write | 1451 | * @param len number of bytes to write |
@@ -1319,18 +1455,23 @@ static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, | |||
1319 | * | 1455 | * |
1320 | * OneNAND write out-of-band | 1456 | * OneNAND write out-of-band |
1321 | */ | 1457 | */ |
1322 | static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | 1458 | static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, |
1323 | size_t *retlen, const u_char *buf, mtd_oob_mode_t mode) | 1459 | struct mtd_oob_ops *ops) |
1324 | { | 1460 | { |
1325 | struct onenand_chip *this = mtd->priv; | 1461 | struct onenand_chip *this = mtd->priv; |
1326 | int column, ret = 0, oobsize; | 1462 | int column, ret = 0, oobsize; |
1327 | int written = 0; | 1463 | int written = 0; |
1328 | u_char *oobbuf; | 1464 | u_char *oobbuf; |
1465 | size_t len = ops->ooblen; | ||
1466 | const u_char *buf = ops->oobbuf; | ||
1467 | mtd_oob_mode_t mode = ops->mode; | ||
1329 | 1468 | ||
1330 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); | 1469 | to += ops->ooboffs; |
1470 | |||
1471 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); | ||
1331 | 1472 | ||
1332 | /* Initialize retlen, in case of early exit */ | 1473 | /* Initialize retlen, in case of early exit */ |
1333 | *retlen = 0; | 1474 | ops->oobretlen = 0; |
1334 | 1475 | ||
1335 | if (mode == MTD_OOB_AUTO) | 1476 | if (mode == MTD_OOB_AUTO) |
1336 | oobsize = this->ecclayout->oobavail; | 1477 | oobsize = this->ecclayout->oobavail; |
@@ -1340,13 +1481,13 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1340 | column = to & (mtd->oobsize - 1); | 1481 | column = to & (mtd->oobsize - 1); |
1341 | 1482 | ||
1342 | if (unlikely(column >= oobsize)) { | 1483 | if (unlikely(column >= oobsize)) { |
1343 | printk(KERN_ERR "onenand_write_oob: Attempted to start write outside oob\n"); | 1484 | printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n"); |
1344 | return -EINVAL; | 1485 | return -EINVAL; |
1345 | } | 1486 | } |
1346 | 1487 | ||
1347 | /* For compatibility with NAND: Do not allow write past end of page */ | 1488 | /* For compatibility with NAND: Do not allow write past end of page */ |
1348 | if (unlikely(column + len > oobsize)) { | 1489 | if (unlikely(column + len > oobsize)) { |
1349 | printk(KERN_ERR "onenand_write_oob: " | 1490 | printk(KERN_ERR "onenand_write_oob_nolock: " |
1350 | "Attempt to write past end of page\n"); | 1491 | "Attempt to write past end of page\n"); |
1351 | return -EINVAL; | 1492 | return -EINVAL; |
1352 | } | 1493 | } |
@@ -1355,13 +1496,10 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1355 | if (unlikely(to >= mtd->size || | 1496 | if (unlikely(to >= mtd->size || |
1356 | column + len > ((mtd->size >> this->page_shift) - | 1497 | column + len > ((mtd->size >> this->page_shift) - |
1357 | (to >> this->page_shift)) * oobsize)) { | 1498 | (to >> this->page_shift)) * oobsize)) { |
1358 | printk(KERN_ERR "onenand_write_oob: Attempted to write past end of device\n"); | 1499 | printk(KERN_ERR "onenand_write_oob_nolock: Attempted to write past end of device\n"); |
1359 | return -EINVAL; | 1500 | return -EINVAL; |
1360 | } | 1501 | } |
1361 | 1502 | ||
1362 | /* Grab the lock and see if the device is available */ | ||
1363 | onenand_get_device(mtd, FL_WRITING); | ||
1364 | |||
1365 | oobbuf = this->oob_buf; | 1503 | oobbuf = this->oob_buf; |
1366 | 1504 | ||
1367 | /* Loop until all data write */ | 1505 | /* Loop until all data write */ |
@@ -1384,16 +1522,20 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1384 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); | 1522 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); |
1385 | 1523 | ||
1386 | onenand_update_bufferram(mtd, to, 0); | 1524 | onenand_update_bufferram(mtd, to, 0); |
1525 | if (ONENAND_IS_2PLANE(this)) { | ||
1526 | ONENAND_SET_BUFFERRAM1(this); | ||
1527 | onenand_update_bufferram(mtd, to + this->writesize, 0); | ||
1528 | } | ||
1387 | 1529 | ||
1388 | ret = this->wait(mtd, FL_WRITING); | 1530 | ret = this->wait(mtd, FL_WRITING); |
1389 | if (ret) { | 1531 | if (ret) { |
1390 | printk(KERN_ERR "onenand_write_oob: write failed %d\n", ret); | 1532 | printk(KERN_ERR "onenand_write_oob_nolock: write failed %d\n", ret); |
1391 | break; | 1533 | break; |
1392 | } | 1534 | } |
1393 | 1535 | ||
1394 | ret = onenand_verify_oob(mtd, oobbuf, to); | 1536 | ret = onenand_verify_oob(mtd, oobbuf, to); |
1395 | if (ret) { | 1537 | if (ret) { |
1396 | printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret); | 1538 | printk(KERN_ERR "onenand_write_oob_nolock: verify failed %d\n", ret); |
1397 | break; | 1539 | break; |
1398 | } | 1540 | } |
1399 | 1541 | ||
@@ -1406,11 +1548,37 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1406 | column = 0; | 1548 | column = 0; |
1407 | } | 1549 | } |
1408 | 1550 | ||
1409 | /* Deselect and wake up anyone waiting on the device */ | 1551 | ops->oobretlen = written; |
1410 | onenand_release_device(mtd); | 1552 | |
1553 | return ret; | ||
1554 | } | ||
1555 | |||
1556 | /** | ||
1557 | * onenand_write - [MTD Interface] write buffer to FLASH | ||
1558 | * @param mtd MTD device structure | ||
1559 | * @param to offset to write to | ||
1560 | * @param len number of bytes to write | ||
1561 | * @param retlen pointer to variable to store the number of written bytes | ||
1562 | * @param buf the data to write | ||
1563 | * | ||
1564 | * Write with ECC | ||
1565 | */ | ||
1566 | static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
1567 | size_t *retlen, const u_char *buf) | ||
1568 | { | ||
1569 | struct mtd_oob_ops ops = { | ||
1570 | .len = len, | ||
1571 | .ooblen = 0, | ||
1572 | .datbuf = (u_char *) buf, | ||
1573 | .oobbuf = NULL, | ||
1574 | }; | ||
1575 | int ret; | ||
1411 | 1576 | ||
1412 | *retlen = written; | 1577 | onenand_get_device(mtd, FL_WRITING); |
1578 | ret = onenand_write_ops_nolock(mtd, to, &ops); | ||
1579 | onenand_release_device(mtd); | ||
1413 | 1580 | ||
1581 | *retlen = ops.retlen; | ||
1414 | return ret; | 1582 | return ret; |
1415 | } | 1583 | } |
1416 | 1584 | ||
@@ -1423,6 +1591,8 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, | |||
1423 | static int onenand_write_oob(struct mtd_info *mtd, loff_t to, | 1591 | static int onenand_write_oob(struct mtd_info *mtd, loff_t to, |
1424 | struct mtd_oob_ops *ops) | 1592 | struct mtd_oob_ops *ops) |
1425 | { | 1593 | { |
1594 | int ret; | ||
1595 | |||
1426 | switch (ops->mode) { | 1596 | switch (ops->mode) { |
1427 | case MTD_OOB_PLACE: | 1597 | case MTD_OOB_PLACE: |
1428 | case MTD_OOB_AUTO: | 1598 | case MTD_OOB_AUTO: |
@@ -1432,21 +1602,27 @@ static int onenand_write_oob(struct mtd_info *mtd, loff_t to, | |||
1432 | default: | 1602 | default: |
1433 | return -EINVAL; | 1603 | return -EINVAL; |
1434 | } | 1604 | } |
1435 | return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen, | 1605 | |
1436 | &ops->oobretlen, ops->oobbuf, ops->mode); | 1606 | onenand_get_device(mtd, FL_WRITING); |
1607 | if (ops->datbuf) | ||
1608 | ret = onenand_write_ops_nolock(mtd, to, ops); | ||
1609 | else | ||
1610 | ret = onenand_write_oob_nolock(mtd, to, ops); | ||
1611 | onenand_release_device(mtd); | ||
1612 | |||
1613 | return ret; | ||
1437 | } | 1614 | } |
1438 | 1615 | ||
1439 | /** | 1616 | /** |
1440 | * onenand_block_checkbad - [GENERIC] Check if a block is marked bad | 1617 | * onenand_block_isbad_nolock - [GENERIC] Check if a block is marked bad |
1441 | * @param mtd MTD device structure | 1618 | * @param mtd MTD device structure |
1442 | * @param ofs offset from device start | 1619 | * @param ofs offset from device start |
1443 | * @param getchip 0, if the chip is already selected | ||
1444 | * @param allowbbt 1, if its allowed to access the bbt area | 1620 | * @param allowbbt 1, if its allowed to access the bbt area |
1445 | * | 1621 | * |
1446 | * Check, if the block is bad. Either by reading the bad block table or | 1622 | * Check, if the block is bad. Either by reading the bad block table or |
1447 | * calling of the scan function. | 1623 | * calling of the scan function. |
1448 | */ | 1624 | */ |
1449 | static int onenand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) | 1625 | static int onenand_block_isbad_nolock(struct mtd_info *mtd, loff_t ofs, int allowbbt) |
1450 | { | 1626 | { |
1451 | struct onenand_chip *this = mtd->priv; | 1627 | struct onenand_chip *this = mtd->priv; |
1452 | struct bbm_info *bbm = this->bbm; | 1628 | struct bbm_info *bbm = this->bbm; |
@@ -1507,7 +1683,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) | |||
1507 | cond_resched(); | 1683 | cond_resched(); |
1508 | 1684 | ||
1509 | /* Check if we have a bad block, we do not erase bad blocks */ | 1685 | /* Check if we have a bad block, we do not erase bad blocks */ |
1510 | if (onenand_block_checkbad(mtd, addr, 0, 0)) { | 1686 | if (onenand_block_isbad_nolock(mtd, addr, 0)) { |
1511 | printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%08x\n", (unsigned int) addr); | 1687 | printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%08x\n", (unsigned int) addr); |
1512 | instr->state = MTD_ERASE_FAILED; | 1688 | instr->state = MTD_ERASE_FAILED; |
1513 | goto erase_exit; | 1689 | goto erase_exit; |
@@ -1571,11 +1747,16 @@ static void onenand_sync(struct mtd_info *mtd) | |||
1571 | */ | 1747 | */ |
1572 | static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) | 1748 | static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) |
1573 | { | 1749 | { |
1750 | int ret; | ||
1751 | |||
1574 | /* Check for invalid offset */ | 1752 | /* Check for invalid offset */ |
1575 | if (ofs > mtd->size) | 1753 | if (ofs > mtd->size) |
1576 | return -EINVAL; | 1754 | return -EINVAL; |
1577 | 1755 | ||
1578 | return onenand_block_checkbad(mtd, ofs, 1, 0); | 1756 | onenand_get_device(mtd, FL_READING); |
1757 | ret = onenand_block_isbad_nolock(mtd, ofs, 0); | ||
1758 | onenand_release_device(mtd); | ||
1759 | return ret; | ||
1579 | } | 1760 | } |
1580 | 1761 | ||
1581 | /** | 1762 | /** |
@@ -1591,7 +1772,12 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
1591 | struct onenand_chip *this = mtd->priv; | 1772 | struct onenand_chip *this = mtd->priv; |
1592 | struct bbm_info *bbm = this->bbm; | 1773 | struct bbm_info *bbm = this->bbm; |
1593 | u_char buf[2] = {0, 0}; | 1774 | u_char buf[2] = {0, 0}; |
1594 | size_t retlen; | 1775 | struct mtd_oob_ops ops = { |
1776 | .mode = MTD_OOB_PLACE, | ||
1777 | .ooblen = 2, | ||
1778 | .oobbuf = buf, | ||
1779 | .ooboffs = 0, | ||
1780 | }; | ||
1595 | int block; | 1781 | int block; |
1596 | 1782 | ||
1597 | /* Get block number */ | 1783 | /* Get block number */ |
@@ -1601,7 +1787,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
1601 | 1787 | ||
1602 | /* We write two bytes, so we dont have to mess with 16 bit access */ | 1788 | /* We write two bytes, so we dont have to mess with 16 bit access */ |
1603 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); | 1789 | ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); |
1604 | return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE); | 1790 | return onenand_write_oob_nolock(mtd, ofs, &ops); |
1605 | } | 1791 | } |
1606 | 1792 | ||
1607 | /** | 1793 | /** |
@@ -1624,7 +1810,10 @@ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
1624 | return ret; | 1810 | return ret; |
1625 | } | 1811 | } |
1626 | 1812 | ||
1627 | return this->block_markbad(mtd, ofs); | 1813 | onenand_get_device(mtd, FL_WRITING); |
1814 | ret = this->block_markbad(mtd, ofs); | ||
1815 | onenand_release_device(mtd); | ||
1816 | return ret; | ||
1628 | } | 1817 | } |
1629 | 1818 | ||
1630 | /** | 1819 | /** |
@@ -1823,13 +2012,19 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
1823 | size_t *retlen, u_char *buf) | 2012 | size_t *retlen, u_char *buf) |
1824 | { | 2013 | { |
1825 | struct onenand_chip *this = mtd->priv; | 2014 | struct onenand_chip *this = mtd->priv; |
2015 | struct mtd_oob_ops ops = { | ||
2016 | .len = len, | ||
2017 | .ooblen = 0, | ||
2018 | .datbuf = buf, | ||
2019 | .oobbuf = NULL, | ||
2020 | }; | ||
1826 | int ret; | 2021 | int ret; |
1827 | 2022 | ||
1828 | /* Enter OTP access mode */ | 2023 | /* Enter OTP access mode */ |
1829 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); | 2024 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); |
1830 | this->wait(mtd, FL_OTPING); | 2025 | this->wait(mtd, FL_OTPING); |
1831 | 2026 | ||
1832 | ret = mtd->read(mtd, from, len, retlen, buf); | 2027 | ret = onenand_read_ops_nolock(mtd, from, &ops); |
1833 | 2028 | ||
1834 | /* Exit OTP access mode */ | 2029 | /* Exit OTP access mode */ |
1835 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); | 2030 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); |
@@ -1841,19 +2036,20 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
1841 | /** | 2036 | /** |
1842 | * do_otp_write - [DEFAULT] Write OTP block area | 2037 | * do_otp_write - [DEFAULT] Write OTP block area |
1843 | * @param mtd MTD device structure | 2038 | * @param mtd MTD device structure |
1844 | * @param from The offset to write | 2039 | * @param to The offset to write |
1845 | * @param len number of bytes to write | 2040 | * @param len number of bytes to write |
1846 | * @param retlen pointer to variable to store the number of write bytes | 2041 | * @param retlen pointer to variable to store the number of write bytes |
1847 | * @param buf the databuffer to put/get data | 2042 | * @param buf the databuffer to put/get data |
1848 | * | 2043 | * |
1849 | * Write OTP block area. | 2044 | * Write OTP block area. |
1850 | */ | 2045 | */ |
1851 | static int do_otp_write(struct mtd_info *mtd, loff_t from, size_t len, | 2046 | static int do_otp_write(struct mtd_info *mtd, loff_t to, size_t len, |
1852 | size_t *retlen, u_char *buf) | 2047 | size_t *retlen, u_char *buf) |
1853 | { | 2048 | { |
1854 | struct onenand_chip *this = mtd->priv; | 2049 | struct onenand_chip *this = mtd->priv; |
1855 | unsigned char *pbuf = buf; | 2050 | unsigned char *pbuf = buf; |
1856 | int ret; | 2051 | int ret; |
2052 | struct mtd_oob_ops ops; | ||
1857 | 2053 | ||
1858 | /* Force buffer page aligned */ | 2054 | /* Force buffer page aligned */ |
1859 | if (len < mtd->writesize) { | 2055 | if (len < mtd->writesize) { |
@@ -1867,7 +2063,12 @@ static int do_otp_write(struct mtd_info *mtd, loff_t from, size_t len, | |||
1867 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); | 2063 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); |
1868 | this->wait(mtd, FL_OTPING); | 2064 | this->wait(mtd, FL_OTPING); |
1869 | 2065 | ||
1870 | ret = mtd->write(mtd, from, len, retlen, pbuf); | 2066 | ops.len = len; |
2067 | ops.ooblen = 0; | ||
2068 | ops.datbuf = pbuf; | ||
2069 | ops.oobbuf = NULL; | ||
2070 | ret = onenand_write_ops_nolock(mtd, to, &ops); | ||
2071 | *retlen = ops.retlen; | ||
1871 | 2072 | ||
1872 | /* Exit OTP access mode */ | 2073 | /* Exit OTP access mode */ |
1873 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); | 2074 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); |
@@ -1890,13 +2091,21 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, | |||
1890 | size_t *retlen, u_char *buf) | 2091 | size_t *retlen, u_char *buf) |
1891 | { | 2092 | { |
1892 | struct onenand_chip *this = mtd->priv; | 2093 | struct onenand_chip *this = mtd->priv; |
2094 | struct mtd_oob_ops ops = { | ||
2095 | .mode = MTD_OOB_PLACE, | ||
2096 | .ooblen = len, | ||
2097 | .oobbuf = buf, | ||
2098 | .ooboffs = 0, | ||
2099 | }; | ||
1893 | int ret; | 2100 | int ret; |
1894 | 2101 | ||
1895 | /* Enter OTP access mode */ | 2102 | /* Enter OTP access mode */ |
1896 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); | 2103 | this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); |
1897 | this->wait(mtd, FL_OTPING); | 2104 | this->wait(mtd, FL_OTPING); |
1898 | 2105 | ||
1899 | ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE); | 2106 | ret = onenand_write_oob_nolock(mtd, from, &ops); |
2107 | |||
2108 | *retlen = ops.oobretlen; | ||
1900 | 2109 | ||
1901 | /* Exit OTP access mode */ | 2110 | /* Exit OTP access mode */ |
1902 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); | 2111 | this->command(mtd, ONENAND_CMD_RESET, 0, 0); |
@@ -1943,13 +2152,16 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, | |||
1943 | if (((mtd->writesize * otp_pages) - (from + len)) < 0) | 2152 | if (((mtd->writesize * otp_pages) - (from + len)) < 0) |
1944 | return 0; | 2153 | return 0; |
1945 | 2154 | ||
2155 | onenand_get_device(mtd, FL_OTPING); | ||
1946 | while (len > 0 && otp_pages > 0) { | 2156 | while (len > 0 && otp_pages > 0) { |
1947 | if (!action) { /* OTP Info functions */ | 2157 | if (!action) { /* OTP Info functions */ |
1948 | struct otp_info *otpinfo; | 2158 | struct otp_info *otpinfo; |
1949 | 2159 | ||
1950 | len -= sizeof(struct otp_info); | 2160 | len -= sizeof(struct otp_info); |
1951 | if (len <= 0) | 2161 | if (len <= 0) { |
1952 | return -ENOSPC; | 2162 | ret = -ENOSPC; |
2163 | break; | ||
2164 | } | ||
1953 | 2165 | ||
1954 | otpinfo = (struct otp_info *) buf; | 2166 | otpinfo = (struct otp_info *) buf; |
1955 | otpinfo->start = from; | 2167 | otpinfo->start = from; |
@@ -1969,13 +2181,14 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, | |||
1969 | len -= size; | 2181 | len -= size; |
1970 | *retlen += size; | 2182 | *retlen += size; |
1971 | 2183 | ||
1972 | if (ret < 0) | 2184 | if (ret) |
1973 | return ret; | 2185 | break; |
1974 | } | 2186 | } |
1975 | otp_pages--; | 2187 | otp_pages--; |
1976 | } | 2188 | } |
2189 | onenand_release_device(mtd); | ||
1977 | 2190 | ||
1978 | return 0; | 2191 | return ret; |
1979 | } | 2192 | } |
1980 | 2193 | ||
1981 | /** | 2194 | /** |
@@ -2107,6 +2320,7 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, | |||
2107 | * | 2320 | * |
2108 | * Check and set OneNAND features | 2321 | * Check and set OneNAND features |
2109 | * - lock scheme | 2322 | * - lock scheme |
2323 | * - two plane | ||
2110 | */ | 2324 | */ |
2111 | static void onenand_check_features(struct mtd_info *mtd) | 2325 | static void onenand_check_features(struct mtd_info *mtd) |
2112 | { | 2326 | { |
@@ -2118,19 +2332,35 @@ static void onenand_check_features(struct mtd_info *mtd) | |||
2118 | process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; | 2332 | process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; |
2119 | 2333 | ||
2120 | /* Lock scheme */ | 2334 | /* Lock scheme */ |
2121 | if (density >= ONENAND_DEVICE_DENSITY_1Gb) { | 2335 | switch (density) { |
2336 | case ONENAND_DEVICE_DENSITY_4Gb: | ||
2337 | this->options |= ONENAND_HAS_2PLANE; | ||
2338 | |||
2339 | case ONENAND_DEVICE_DENSITY_2Gb: | ||
2340 | /* 2Gb DDP don't have 2 plane */ | ||
2341 | if (!ONENAND_IS_DDP(this)) | ||
2342 | this->options |= ONENAND_HAS_2PLANE; | ||
2343 | this->options |= ONENAND_HAS_UNLOCK_ALL; | ||
2344 | |||
2345 | case ONENAND_DEVICE_DENSITY_1Gb: | ||
2122 | /* A-Die has all block unlock */ | 2346 | /* A-Die has all block unlock */ |
2123 | if (process) { | 2347 | if (process) |
2124 | printk(KERN_DEBUG "Chip support all block unlock\n"); | ||
2125 | this->options |= ONENAND_HAS_UNLOCK_ALL; | 2348 | this->options |= ONENAND_HAS_UNLOCK_ALL; |
2126 | } | 2349 | break; |
2127 | } else { | 2350 | |
2128 | /* Some OneNAND has continues lock scheme */ | 2351 | default: |
2129 | if (!process) { | 2352 | /* Some OneNAND has continuous lock scheme */ |
2130 | printk(KERN_DEBUG "Lock scheme is Continues Lock\n"); | 2353 | if (!process) |
2131 | this->options |= ONENAND_HAS_CONT_LOCK; | 2354 | this->options |= ONENAND_HAS_CONT_LOCK; |
2132 | } | 2355 | break; |
2133 | } | 2356 | } |
2357 | |||
2358 | if (this->options & ONENAND_HAS_CONT_LOCK) | ||
2359 | printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); | ||
2360 | if (this->options & ONENAND_HAS_UNLOCK_ALL) | ||
2361 | printk(KERN_DEBUG "Chip support all block unlock\n"); | ||
2362 | if (this->options & ONENAND_HAS_2PLANE) | ||
2363 | printk(KERN_DEBUG "Chip has 2 plane\n"); | ||
2134 | } | 2364 | } |
2135 | 2365 | ||
2136 | /** | 2366 | /** |
@@ -2154,7 +2384,7 @@ static void onenand_print_device_info(int device, int version) | |||
2154 | (16 << density), | 2384 | (16 << density), |
2155 | vcc ? "2.65/3.3" : "1.8", | 2385 | vcc ? "2.65/3.3" : "1.8", |
2156 | device); | 2386 | device); |
2157 | printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version); | 2387 | printk(KERN_INFO "OneNAND version = 0x%04x\n", version); |
2158 | } | 2388 | } |
2159 | 2389 | ||
2160 | static const struct onenand_manufacturers onenand_manuf_ids[] = { | 2390 | static const struct onenand_manufacturers onenand_manuf_ids[] = { |
@@ -2257,6 +2487,8 @@ static int onenand_probe(struct mtd_info *mtd) | |||
2257 | this->erase_shift = ffs(mtd->erasesize) - 1; | 2487 | this->erase_shift = ffs(mtd->erasesize) - 1; |
2258 | this->page_shift = ffs(mtd->writesize) - 1; | 2488 | this->page_shift = ffs(mtd->writesize) - 1; |
2259 | this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; | 2489 | this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; |
2490 | /* It's real page size */ | ||
2491 | this->writesize = mtd->writesize; | ||
2260 | 2492 | ||
2261 | /* REVIST: Multichip handling */ | 2493 | /* REVIST: Multichip handling */ |
2262 | 2494 | ||
@@ -2265,6 +2497,17 @@ static int onenand_probe(struct mtd_info *mtd) | |||
2265 | /* Check OneNAND features */ | 2497 | /* Check OneNAND features */ |
2266 | onenand_check_features(mtd); | 2498 | onenand_check_features(mtd); |
2267 | 2499 | ||
2500 | /* | ||
2501 | * We emulate the 4KiB page and 256KiB erase block size | ||
2502 | * But oobsize is still 64 bytes. | ||
2503 | * It is only valid if you turn on 2X program support, | ||
2504 | * Otherwise it will be ignored by compiler. | ||
2505 | */ | ||
2506 | if (ONENAND_IS_2PLANE(this)) { | ||
2507 | mtd->writesize <<= 1; | ||
2508 | mtd->erasesize <<= 1; | ||
2509 | } | ||
2510 | |||
2268 | return 0; | 2511 | return 0; |
2269 | } | 2512 | } |
2270 | 2513 | ||
diff --git a/drivers/mtd/onenand/onenand_sim.c b/drivers/mtd/onenand/onenand_sim.c new file mode 100644 index 000000000000..0d89ad5776fa --- /dev/null +++ b/drivers/mtd/onenand/onenand_sim.c | |||
@@ -0,0 +1,495 @@ | |||
1 | /* | ||
2 | * linux/drivers/mtd/onenand/onenand_sim.c | ||
3 | * | ||
4 | * The OneNAND simulator | ||
5 | * | ||
6 | * Copyright © 2005-2007 Samsung Electronics | ||
7 | * Kyungmin Park <kyungmin.park@samsung.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/vmalloc.h> | ||
18 | #include <linux/mtd/mtd.h> | ||
19 | #include <linux/mtd/partitions.h> | ||
20 | #include <linux/mtd/onenand.h> | ||
21 | |||
22 | #include <linux/io.h> | ||
23 | |||
24 | #ifndef CONFIG_ONENAND_SIM_MANUFACTURER | ||
25 | #define CONFIG_ONENAND_SIM_MANUFACTURER 0xec | ||
26 | #endif | ||
27 | #ifndef CONFIG_ONENAND_SIM_DEVICE_ID | ||
28 | #define CONFIG_ONENAND_SIM_DEVICE_ID 0x04 | ||
29 | #endif | ||
30 | #ifndef CONFIG_ONENAND_SIM_VERSION_ID | ||
31 | #define CONFIG_ONENAND_SIM_VERSION_ID 0x1e | ||
32 | #endif | ||
33 | |||
34 | static int manuf_id = CONFIG_ONENAND_SIM_MANUFACTURER; | ||
35 | static int device_id = CONFIG_ONENAND_SIM_DEVICE_ID; | ||
36 | static int version_id = CONFIG_ONENAND_SIM_VERSION_ID; | ||
37 | |||
38 | struct onenand_flash { | ||
39 | void __iomem *base; | ||
40 | void __iomem *data; | ||
41 | }; | ||
42 | |||
43 | #define ONENAND_CORE(flash) (flash->data) | ||
44 | #define ONENAND_CORE_SPARE(flash, this, offset) \ | ||
45 | ((flash->data) + (this->chipsize) + (offset >> 5)) | ||
46 | |||
47 | #define ONENAND_MAIN_AREA(this, offset) \ | ||
48 | (this->base + ONENAND_DATARAM + offset) | ||
49 | |||
50 | #define ONENAND_SPARE_AREA(this, offset) \ | ||
51 | (this->base + ONENAND_SPARERAM + offset) | ||
52 | |||
53 | #define ONENAND_GET_WP_STATUS(this) \ | ||
54 | (readw(this->base + ONENAND_REG_WP_STATUS)) | ||
55 | |||
56 | #define ONENAND_SET_WP_STATUS(v, this) \ | ||
57 | (writew(v, this->base + ONENAND_REG_WP_STATUS)) | ||
58 | |||
59 | /* It has all 0xff chars */ | ||
60 | #define MAX_ONENAND_PAGESIZE (2048 + 64) | ||
61 | static unsigned char *ffchars; | ||
62 | |||
63 | static struct mtd_partition os_partitions[] = { | ||
64 | { | ||
65 | .name = "OneNAND simulator partition", | ||
66 | .offset = 0, | ||
67 | .size = MTDPART_SIZ_FULL, | ||
68 | }, | ||
69 | }; | ||
70 | |||
71 | /* | ||
72 | * OneNAND simulator mtd | ||
73 | */ | ||
74 | struct onenand_info { | ||
75 | struct mtd_info mtd; | ||
76 | struct mtd_partition *parts; | ||
77 | struct onenand_chip onenand; | ||
78 | struct onenand_flash flash; | ||
79 | }; | ||
80 | |||
81 | static struct onenand_info *info; | ||
82 | |||
83 | #define DPRINTK(format, args...) \ | ||
84 | do { \ | ||
85 | printk(KERN_DEBUG "%s[%d]: " format "\n", __func__, \ | ||
86 | __LINE__, ##args); \ | ||
87 | } while (0) | ||
88 | |||
89 | /** | ||
90 | * onenand_lock_handle - Handle Lock scheme | ||
91 | * @param this OneNAND device structure | ||
92 | * @param cmd The command to be sent | ||
93 | * | ||
94 | * Send lock command to OneNAND device. | ||
95 | * The lock scheme is depends on chip type. | ||
96 | */ | ||
97 | static void onenand_lock_handle(struct onenand_chip *this, int cmd) | ||
98 | { | ||
99 | int block_lock_scheme; | ||
100 | int status; | ||
101 | |||
102 | status = ONENAND_GET_WP_STATUS(this); | ||
103 | block_lock_scheme = !(this->options & ONENAND_HAS_CONT_LOCK); | ||
104 | |||
105 | switch (cmd) { | ||
106 | case ONENAND_CMD_UNLOCK: | ||
107 | if (block_lock_scheme) | ||
108 | ONENAND_SET_WP_STATUS(ONENAND_WP_US, this); | ||
109 | else | ||
110 | ONENAND_SET_WP_STATUS(status | ONENAND_WP_US, this); | ||
111 | break; | ||
112 | |||
113 | case ONENAND_CMD_LOCK: | ||
114 | if (block_lock_scheme) | ||
115 | ONENAND_SET_WP_STATUS(ONENAND_WP_LS, this); | ||
116 | else | ||
117 | ONENAND_SET_WP_STATUS(status | ONENAND_WP_LS, this); | ||
118 | break; | ||
119 | |||
120 | case ONENAND_CMD_LOCK_TIGHT: | ||
121 | if (block_lock_scheme) | ||
122 | ONENAND_SET_WP_STATUS(ONENAND_WP_LTS, this); | ||
123 | else | ||
124 | ONENAND_SET_WP_STATUS(status | ONENAND_WP_LTS, this); | ||
125 | break; | ||
126 | |||
127 | default: | ||
128 | break; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * onenand_bootram_handle - Handle BootRAM area | ||
134 | * @param this OneNAND device structure | ||
135 | * @param cmd The command to be sent | ||
136 | * | ||
137 | * Emulate BootRAM area. It is possible to do basic operation using BootRAM. | ||
138 | */ | ||
139 | static void onenand_bootram_handle(struct onenand_chip *this, int cmd) | ||
140 | { | ||
141 | switch (cmd) { | ||
142 | case ONENAND_CMD_READID: | ||
143 | writew(manuf_id, this->base); | ||
144 | writew(device_id, this->base + 2); | ||
145 | writew(version_id, this->base + 4); | ||
146 | break; | ||
147 | |||
148 | default: | ||
149 | /* REVIST: Handle other commands */ | ||
150 | break; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | * onenand_update_interrupt - Set interrupt register | ||
156 | * @param this OneNAND device structure | ||
157 | * @param cmd The command to be sent | ||
158 | * | ||
159 | * Update interrupt register. The status is depends on command. | ||
160 | */ | ||
161 | static void onenand_update_interrupt(struct onenand_chip *this, int cmd) | ||
162 | { | ||
163 | int interrupt = ONENAND_INT_MASTER; | ||
164 | |||
165 | switch (cmd) { | ||
166 | case ONENAND_CMD_READ: | ||
167 | case ONENAND_CMD_READOOB: | ||
168 | interrupt |= ONENAND_INT_READ; | ||
169 | break; | ||
170 | |||
171 | case ONENAND_CMD_PROG: | ||
172 | case ONENAND_CMD_PROGOOB: | ||
173 | interrupt |= ONENAND_INT_WRITE; | ||
174 | break; | ||
175 | |||
176 | case ONENAND_CMD_ERASE: | ||
177 | interrupt |= ONENAND_INT_ERASE; | ||
178 | break; | ||
179 | |||
180 | case ONENAND_CMD_RESET: | ||
181 | interrupt |= ONENAND_INT_RESET; | ||
182 | break; | ||
183 | |||
184 | default: | ||
185 | break; | ||
186 | } | ||
187 | |||
188 | writew(interrupt, this->base + ONENAND_REG_INTERRUPT); | ||
189 | } | ||
190 | |||
191 | /** | ||
192 | * onenand_check_overwrite - Check over-write if happend | ||
193 | * @param dest The destination pointer | ||
194 | * @param src The source pointer | ||
195 | * @param count The length to be check | ||
196 | * @return 0 on same, otherwise 1 | ||
197 | * | ||
198 | * Compare the source with destination | ||
199 | */ | ||
200 | static int onenand_check_overwrite(void *dest, void *src, size_t count) | ||
201 | { | ||
202 | unsigned int *s = (unsigned int *) src; | ||
203 | unsigned int *d = (unsigned int *) dest; | ||
204 | int i; | ||
205 | |||
206 | count >>= 2; | ||
207 | for (i = 0; i < count; i++) | ||
208 | if ((*s++ ^ *d++) != 0) | ||
209 | return 1; | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | /** | ||
215 | * onenand_data_handle - Handle OneNAND Core and DataRAM | ||
216 | * @param this OneNAND device structure | ||
217 | * @param cmd The command to be sent | ||
218 | * @param dataram Which dataram used | ||
219 | * @param offset The offset to OneNAND Core | ||
220 | * | ||
221 | * Copy data from OneNAND Core to DataRAM (read) | ||
222 | * Copy data from DataRAM to OneNAND Core (write) | ||
223 | * Erase the OneNAND Core (erase) | ||
224 | */ | ||
225 | static void onenand_data_handle(struct onenand_chip *this, int cmd, | ||
226 | int dataram, unsigned int offset) | ||
227 | { | ||
228 | struct mtd_info *mtd = &info->mtd; | ||
229 | struct onenand_flash *flash = this->priv; | ||
230 | int main_offset, spare_offset; | ||
231 | void __iomem *src; | ||
232 | void __iomem *dest; | ||
233 | unsigned int i; | ||
234 | |||
235 | if (dataram) { | ||
236 | main_offset = mtd->writesize; | ||
237 | spare_offset = mtd->oobsize; | ||
238 | } else { | ||
239 | main_offset = 0; | ||
240 | spare_offset = 0; | ||
241 | } | ||
242 | |||
243 | switch (cmd) { | ||
244 | case ONENAND_CMD_READ: | ||
245 | src = ONENAND_CORE(flash) + offset; | ||
246 | dest = ONENAND_MAIN_AREA(this, main_offset); | ||
247 | memcpy(dest, src, mtd->writesize); | ||
248 | /* Fall through */ | ||
249 | |||
250 | case ONENAND_CMD_READOOB: | ||
251 | src = ONENAND_CORE_SPARE(flash, this, offset); | ||
252 | dest = ONENAND_SPARE_AREA(this, spare_offset); | ||
253 | memcpy(dest, src, mtd->oobsize); | ||
254 | break; | ||
255 | |||
256 | case ONENAND_CMD_PROG: | ||
257 | src = ONENAND_MAIN_AREA(this, main_offset); | ||
258 | dest = ONENAND_CORE(flash) + offset; | ||
259 | /* To handle partial write */ | ||
260 | for (i = 0; i < (1 << mtd->subpage_sft); i++) { | ||
261 | int off = i * this->subpagesize; | ||
262 | if (!memcmp(src + off, ffchars, this->subpagesize)) | ||
263 | continue; | ||
264 | if (memcmp(dest + off, ffchars, this->subpagesize) && | ||
265 | onenand_check_overwrite(dest + off, src + off, this->subpagesize)) | ||
266 | printk(KERN_ERR "over-write happend at 0x%08x\n", offset); | ||
267 | memcpy(dest + off, src + off, this->subpagesize); | ||
268 | } | ||
269 | /* Fall through */ | ||
270 | |||
271 | case ONENAND_CMD_PROGOOB: | ||
272 | src = ONENAND_SPARE_AREA(this, spare_offset); | ||
273 | /* Check all data is 0xff chars */ | ||
274 | if (!memcmp(src, ffchars, mtd->oobsize)) | ||
275 | break; | ||
276 | |||
277 | dest = ONENAND_CORE_SPARE(flash, this, offset); | ||
278 | if (memcmp(dest, ffchars, mtd->oobsize) && | ||
279 | onenand_check_overwrite(dest, src, mtd->oobsize)) | ||
280 | printk(KERN_ERR "OOB: over-write happend at 0x%08x\n", | ||
281 | offset); | ||
282 | memcpy(dest, src, mtd->oobsize); | ||
283 | break; | ||
284 | |||
285 | case ONENAND_CMD_ERASE: | ||
286 | memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize); | ||
287 | memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff, | ||
288 | (mtd->erasesize >> 5)); | ||
289 | break; | ||
290 | |||
291 | default: | ||
292 | break; | ||
293 | } | ||
294 | } | ||
295 | |||
296 | /** | ||
297 | * onenand_command_handle - Handle command | ||
298 | * @param this OneNAND device structure | ||
299 | * @param cmd The command to be sent | ||
300 | * | ||
301 | * Emulate OneNAND command. | ||
302 | */ | ||
303 | static void onenand_command_handle(struct onenand_chip *this, int cmd) | ||
304 | { | ||
305 | unsigned long offset = 0; | ||
306 | int block = -1, page = -1, bufferram = -1; | ||
307 | int dataram = 0; | ||
308 | |||
309 | switch (cmd) { | ||
310 | case ONENAND_CMD_UNLOCK: | ||
311 | case ONENAND_CMD_LOCK: | ||
312 | case ONENAND_CMD_LOCK_TIGHT: | ||
313 | case ONENAND_CMD_UNLOCK_ALL: | ||
314 | onenand_lock_handle(this, cmd); | ||
315 | break; | ||
316 | |||
317 | case ONENAND_CMD_BUFFERRAM: | ||
318 | /* Do nothing */ | ||
319 | return; | ||
320 | |||
321 | default: | ||
322 | block = (int) readw(this->base + ONENAND_REG_START_ADDRESS1); | ||
323 | if (block & (1 << ONENAND_DDP_SHIFT)) { | ||
324 | block &= ~(1 << ONENAND_DDP_SHIFT); | ||
325 | /* The half of chip block */ | ||
326 | block += this->chipsize >> (this->erase_shift + 1); | ||
327 | } | ||
328 | if (cmd == ONENAND_CMD_ERASE) | ||
329 | break; | ||
330 | |||
331 | page = (int) readw(this->base + ONENAND_REG_START_ADDRESS8); | ||
332 | page = (page >> ONENAND_FPA_SHIFT); | ||
333 | bufferram = (int) readw(this->base + ONENAND_REG_START_BUFFER); | ||
334 | bufferram >>= ONENAND_BSA_SHIFT; | ||
335 | bufferram &= ONENAND_BSA_DATARAM1; | ||
336 | dataram = (bufferram == ONENAND_BSA_DATARAM1) ? 1 : 0; | ||
337 | break; | ||
338 | } | ||
339 | |||
340 | if (block != -1) | ||
341 | offset += block << this->erase_shift; | ||
342 | |||
343 | if (page != -1) | ||
344 | offset += page << this->page_shift; | ||
345 | |||
346 | onenand_data_handle(this, cmd, dataram, offset); | ||
347 | |||
348 | onenand_update_interrupt(this, cmd); | ||
349 | } | ||
350 | |||
351 | /** | ||
352 | * onenand_writew - [OneNAND Interface] Emulate write operation | ||
353 | * @param value value to write | ||
354 | * @param addr address to write | ||
355 | * | ||
356 | * Write OneNAND register with value | ||
357 | */ | ||
358 | static void onenand_writew(unsigned short value, void __iomem * addr) | ||
359 | { | ||
360 | struct onenand_chip *this = info->mtd.priv; | ||
361 | |||
362 | /* BootRAM handling */ | ||
363 | if (addr < this->base + ONENAND_DATARAM) { | ||
364 | onenand_bootram_handle(this, value); | ||
365 | return; | ||
366 | } | ||
367 | /* Command handling */ | ||
368 | if (addr == this->base + ONENAND_REG_COMMAND) | ||
369 | onenand_command_handle(this, value); | ||
370 | |||
371 | writew(value, addr); | ||
372 | } | ||
373 | |||
374 | /** | ||
375 | * flash_init - Initialize OneNAND simulator | ||
376 | * @param flash OneNAND simulaotr data strucutres | ||
377 | * | ||
378 | * Initialize OneNAND simulator. | ||
379 | */ | ||
380 | static int __init flash_init(struct onenand_flash *flash) | ||
381 | { | ||
382 | int density, size; | ||
383 | int buffer_size; | ||
384 | |||
385 | flash->base = kzalloc(131072, GFP_KERNEL); | ||
386 | if (!flash->base) { | ||
387 | printk(KERN_ERR "Unable to allocate base address.\n"); | ||
388 | return -ENOMEM; | ||
389 | } | ||
390 | |||
391 | density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT; | ||
392 | size = ((16 << 20) << density); | ||
393 | |||
394 | ONENAND_CORE(flash) = vmalloc(size + (size >> 5)); | ||
395 | if (!ONENAND_CORE(flash)) { | ||
396 | printk(KERN_ERR "Unable to allocate nand core address.\n"); | ||
397 | kfree(flash->base); | ||
398 | return -ENOMEM; | ||
399 | } | ||
400 | |||
401 | memset(ONENAND_CORE(flash), 0xff, size + (size >> 5)); | ||
402 | |||
403 | /* Setup registers */ | ||
404 | writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID); | ||
405 | writew(device_id, flash->base + ONENAND_REG_DEVICE_ID); | ||
406 | writew(version_id, flash->base + ONENAND_REG_VERSION_ID); | ||
407 | |||
408 | if (density < 2) | ||
409 | buffer_size = 0x0400; /* 1KiB page */ | ||
410 | else | ||
411 | buffer_size = 0x0800; /* 2KiB page */ | ||
412 | writew(buffer_size, flash->base + ONENAND_REG_DATA_BUFFER_SIZE); | ||
413 | |||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | /** | ||
418 | * flash_exit - Clean up OneNAND simulator | ||
419 | * @param flash OneNAND simulaotr data strucutres | ||
420 | * | ||
421 | * Clean up OneNAND simulator. | ||
422 | */ | ||
423 | static void flash_exit(struct onenand_flash *flash) | ||
424 | { | ||
425 | vfree(ONENAND_CORE(flash)); | ||
426 | kfree(flash->base); | ||
427 | kfree(flash); | ||
428 | } | ||
429 | |||
430 | static int __init onenand_sim_init(void) | ||
431 | { | ||
432 | /* Allocate all 0xff chars pointer */ | ||
433 | ffchars = kmalloc(MAX_ONENAND_PAGESIZE, GFP_KERNEL); | ||
434 | if (!ffchars) { | ||
435 | printk(KERN_ERR "Unable to allocate ff chars.\n"); | ||
436 | return -ENOMEM; | ||
437 | } | ||
438 | memset(ffchars, 0xff, MAX_ONENAND_PAGESIZE); | ||
439 | |||
440 | /* Allocate OneNAND simulator mtd pointer */ | ||
441 | info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL); | ||
442 | if (!info) { | ||
443 | printk(KERN_ERR "Unable to allocate core structures.\n"); | ||
444 | kfree(ffchars); | ||
445 | return -ENOMEM; | ||
446 | } | ||
447 | |||
448 | /* Override write_word function */ | ||
449 | info->onenand.write_word = onenand_writew; | ||
450 | |||
451 | if (flash_init(&info->flash)) { | ||
452 | printk(KERN_ERR "Unable to allocat flash.\n"); | ||
453 | kfree(ffchars); | ||
454 | kfree(info); | ||
455 | return -ENOMEM; | ||
456 | } | ||
457 | |||
458 | info->parts = os_partitions; | ||
459 | |||
460 | info->onenand.base = info->flash.base; | ||
461 | info->onenand.priv = &info->flash; | ||
462 | |||
463 | info->mtd.name = "OneNAND simulator"; | ||
464 | info->mtd.priv = &info->onenand; | ||
465 | info->mtd.owner = THIS_MODULE; | ||
466 | |||
467 | if (onenand_scan(&info->mtd, 1)) { | ||
468 | flash_exit(&info->flash); | ||
469 | kfree(ffchars); | ||
470 | kfree(info); | ||
471 | return -ENXIO; | ||
472 | } | ||
473 | |||
474 | add_mtd_partitions(&info->mtd, info->parts, ARRAY_SIZE(os_partitions)); | ||
475 | |||
476 | return 0; | ||
477 | } | ||
478 | |||
479 | static void __exit onenand_sim_exit(void) | ||
480 | { | ||
481 | struct onenand_chip *this = info->mtd.priv; | ||
482 | struct onenand_flash *flash = this->priv; | ||
483 | |||
484 | onenand_release(&info->mtd); | ||
485 | flash_exit(flash); | ||
486 | kfree(ffchars); | ||
487 | kfree(info); | ||
488 | } | ||
489 | |||
490 | module_init(onenand_sim_init); | ||
491 | module_exit(onenand_sim_exit); | ||
492 | |||
493 | MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); | ||
494 | MODULE_DESCRIPTION("The OneNAND flash simulator"); | ||
495 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index 006c03aacb55..823fba4e6d2f 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c | |||
@@ -779,10 +779,8 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) | |||
779 | else { | 779 | else { |
780 | if (!mtd->erasesize) { | 780 | if (!mtd->erasesize) { |
781 | printk(KERN_WARNING PREFIX "please provide block_size"); | 781 | printk(KERN_WARNING PREFIX "please provide block_size"); |
782 | kfree(part); | 782 | goto out; |
783 | return; | 783 | } else |
784 | } | ||
785 | else | ||
786 | part->block_size = mtd->erasesize; | 784 | part->block_size = mtd->erasesize; |
787 | } | 785 | } |
788 | 786 | ||
@@ -804,7 +802,7 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) | |||
804 | if (!add_mtd_blktrans_dev((void*)part)) | 802 | if (!add_mtd_blktrans_dev((void*)part)) |
805 | return; | 803 | return; |
806 | } | 804 | } |
807 | 805 | out: | |
808 | kfree(part); | 806 | kfree(part); |
809 | } | 807 | } |
810 | 808 | ||
diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 94ee54934411..29c41eeb09fe 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c | |||
@@ -1314,11 +1314,10 @@ static int paranoid_check_si(const struct ubi_device *ubi, | |||
1314 | * Make sure that all the physical eraseblocks are in one of the lists | 1314 | * Make sure that all the physical eraseblocks are in one of the lists |
1315 | * or trees. | 1315 | * or trees. |
1316 | */ | 1316 | */ |
1317 | buf = kmalloc(ubi->peb_count, GFP_KERNEL); | 1317 | buf = kzalloc(ubi->peb_count, GFP_KERNEL); |
1318 | if (!buf) | 1318 | if (!buf) |
1319 | return -ENOMEM; | 1319 | return -ENOMEM; |
1320 | 1320 | ||
1321 | memset(buf, 1, ubi->peb_count); | ||
1322 | for (pnum = 0; pnum < ubi->peb_count; pnum++) { | 1321 | for (pnum = 0; pnum < ubi->peb_count; pnum++) { |
1323 | err = ubi_io_is_bad(ubi, pnum); | 1322 | err = ubi_io_is_bad(ubi, pnum); |
1324 | if (err < 0) { | 1323 | if (err < 0) { |
@@ -1326,28 +1325,28 @@ static int paranoid_check_si(const struct ubi_device *ubi, | |||
1326 | return err; | 1325 | return err; |
1327 | } | 1326 | } |
1328 | else if (err) | 1327 | else if (err) |
1329 | buf[pnum] = 0; | 1328 | buf[pnum] = 1; |
1330 | } | 1329 | } |
1331 | 1330 | ||
1332 | ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) | 1331 | ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) |
1333 | ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) | 1332 | ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) |
1334 | buf[seb->pnum] = 0; | 1333 | buf[seb->pnum] = 1; |
1335 | 1334 | ||
1336 | list_for_each_entry(seb, &si->free, u.list) | 1335 | list_for_each_entry(seb, &si->free, u.list) |
1337 | buf[seb->pnum] = 0; | 1336 | buf[seb->pnum] = 1; |
1338 | 1337 | ||
1339 | list_for_each_entry(seb, &si->corr, u.list) | 1338 | list_for_each_entry(seb, &si->corr, u.list) |
1340 | buf[seb->pnum] = 0; | 1339 | buf[seb->pnum] = 1; |
1341 | 1340 | ||
1342 | list_for_each_entry(seb, &si->erase, u.list) | 1341 | list_for_each_entry(seb, &si->erase, u.list) |
1343 | buf[seb->pnum] = 0; | 1342 | buf[seb->pnum] = 1; |
1344 | 1343 | ||
1345 | list_for_each_entry(seb, &si->alien, u.list) | 1344 | list_for_each_entry(seb, &si->alien, u.list) |
1346 | buf[seb->pnum] = 0; | 1345 | buf[seb->pnum] = 1; |
1347 | 1346 | ||
1348 | err = 0; | 1347 | err = 0; |
1349 | for (pnum = 0; pnum < ubi->peb_count; pnum++) | 1348 | for (pnum = 0; pnum < ubi->peb_count; pnum++) |
1350 | if (buf[pnum]) { | 1349 | if (!buf[pnum]) { |
1351 | ubi_err("PEB %d is not referred", pnum); | 1350 | ubi_err("PEB %d is not referred", pnum); |
1352 | err = 1; | 1351 | err = 1; |
1353 | } | 1352 | } |
diff --git a/fs/Kconfig b/fs/Kconfig index f9eed6d79066..bb02b39380a3 100644 --- a/fs/Kconfig +++ b/fs/Kconfig | |||
@@ -1225,6 +1225,14 @@ config JFFS2_FS_WRITEBUFFER | |||
1225 | - NOR flash with transparent ECC | 1225 | - NOR flash with transparent ECC |
1226 | - DataFlash | 1226 | - DataFlash |
1227 | 1227 | ||
1228 | config JFFS2_FS_WBUF_VERIFY | ||
1229 | bool "Verify JFFS2 write-buffer reads" | ||
1230 | depends on JFFS2_FS_WRITEBUFFER | ||
1231 | default n | ||
1232 | help | ||
1233 | This causes JFFS2 to read back every page written through the | ||
1234 | write-buffer, and check for errors. | ||
1235 | |||
1228 | config JFFS2_SUMMARY | 1236 | config JFFS2_SUMMARY |
1229 | bool "JFFS2 summary support (EXPERIMENTAL)" | 1237 | bool "JFFS2 summary support (EXPERIMENTAL)" |
1230 | depends on JFFS2_FS && EXPERIMENTAL | 1238 | depends on JFFS2_FS && EXPERIMENTAL |
@@ -1295,52 +1303,71 @@ config JFFS2_ZLIB | |||
1295 | select ZLIB_DEFLATE | 1303 | select ZLIB_DEFLATE |
1296 | depends on JFFS2_FS | 1304 | depends on JFFS2_FS |
1297 | default y | 1305 | default y |
1298 | help | 1306 | help |
1299 | Zlib is designed to be a free, general-purpose, legally unencumbered, | 1307 | Zlib is designed to be a free, general-purpose, legally unencumbered, |
1300 | lossless data-compression library for use on virtually any computer | 1308 | lossless data-compression library for use on virtually any computer |
1301 | hardware and operating system. See <http://www.gzip.org/zlib/> for | 1309 | hardware and operating system. See <http://www.gzip.org/zlib/> for |
1302 | further information. | 1310 | further information. |
1303 | 1311 | ||
1304 | Say 'Y' if unsure. | 1312 | Say 'Y' if unsure. |
1313 | |||
1314 | config JFFS2_LZO | ||
1315 | bool "JFFS2 LZO compression support" if JFFS2_COMPRESSION_OPTIONS | ||
1316 | select LZO_COMPRESS | ||
1317 | select LZO_DECOMPRESS | ||
1318 | depends on JFFS2_FS | ||
1319 | default n | ||
1320 | help | ||
1321 | minilzo-based compression. Generally works better than Zlib. | ||
1322 | |||
1323 | This feature was added in July, 2007. Say 'N' if you need | ||
1324 | compatibility with older bootloaders or kernels. | ||
1305 | 1325 | ||
1306 | config JFFS2_RTIME | 1326 | config JFFS2_RTIME |
1307 | bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS | 1327 | bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS |
1308 | depends on JFFS2_FS | 1328 | depends on JFFS2_FS |
1309 | default y | 1329 | default y |
1310 | help | 1330 | help |
1311 | Rtime does manage to recompress already-compressed data. Say 'Y' if unsure. | 1331 | Rtime does manage to recompress already-compressed data. Say 'Y' if unsure. |
1312 | 1332 | ||
1313 | config JFFS2_RUBIN | 1333 | config JFFS2_RUBIN |
1314 | bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS | 1334 | bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS |
1315 | depends on JFFS2_FS | 1335 | depends on JFFS2_FS |
1316 | default n | 1336 | default n |
1317 | help | 1337 | help |
1318 | RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure. | 1338 | RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure. |
1319 | 1339 | ||
1320 | choice | 1340 | choice |
1321 | prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS | 1341 | prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS |
1322 | default JFFS2_CMODE_PRIORITY | 1342 | default JFFS2_CMODE_PRIORITY |
1323 | depends on JFFS2_FS | 1343 | depends on JFFS2_FS |
1324 | help | 1344 | help |
1325 | You can set here the default compression mode of JFFS2 from | 1345 | You can set here the default compression mode of JFFS2 from |
1326 | the available compression modes. Don't touch if unsure. | 1346 | the available compression modes. Don't touch if unsure. |
1327 | 1347 | ||
1328 | config JFFS2_CMODE_NONE | 1348 | config JFFS2_CMODE_NONE |
1329 | bool "no compression" | 1349 | bool "no compression" |
1330 | help | 1350 | help |
1331 | Uses no compression. | 1351 | Uses no compression. |
1332 | 1352 | ||
1333 | config JFFS2_CMODE_PRIORITY | 1353 | config JFFS2_CMODE_PRIORITY |
1334 | bool "priority" | 1354 | bool "priority" |
1335 | help | 1355 | help |
1336 | Tries the compressors in a predefined order and chooses the first | 1356 | Tries the compressors in a predefined order and chooses the first |
1337 | successful one. | 1357 | successful one. |
1338 | 1358 | ||
1339 | config JFFS2_CMODE_SIZE | 1359 | config JFFS2_CMODE_SIZE |
1340 | bool "size (EXPERIMENTAL)" | 1360 | bool "size (EXPERIMENTAL)" |
1341 | help | 1361 | help |
1342 | Tries all compressors and chooses the one which has the smallest | 1362 | Tries all compressors and chooses the one which has the smallest |
1343 | result. | 1363 | result. |
1364 | |||
1365 | config JFFS2_CMODE_FAVOURLZO | ||
1366 | bool "Favour LZO" | ||
1367 | help | ||
1368 | Tries all compressors and chooses the one which has the smallest | ||
1369 | result but gives some preference to LZO (which has faster | ||
1370 | decompression) at the expense of size. | ||
1344 | 1371 | ||
1345 | endchoice | 1372 | endchoice |
1346 | 1373 | ||
diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile index c32b241e3d91..60e5d49ca03e 100644 --- a/fs/jffs2/Makefile +++ b/fs/jffs2/Makefile | |||
@@ -17,4 +17,5 @@ jffs2-$(CONFIG_JFFS2_FS_POSIX_ACL) += acl.o | |||
17 | jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o | 17 | jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o |
18 | jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o | 18 | jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o |
19 | jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o | 19 | jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o |
20 | jffs2-$(CONFIG_JFFS2_LZO) += compr_lzo.o | ||
20 | jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o | 21 | jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o |
diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index 65b3a1b5b88d..8ec9323e830a 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c | |||
@@ -176,7 +176,7 @@ static void jffs2_iset_acl(struct inode *inode, struct posix_acl **i_acl, struct | |||
176 | spin_unlock(&inode->i_lock); | 176 | spin_unlock(&inode->i_lock); |
177 | } | 177 | } |
178 | 178 | ||
179 | static struct posix_acl *jffs2_get_acl(struct inode *inode, int type) | 179 | struct posix_acl *jffs2_get_acl(struct inode *inode, int type) |
180 | { | 180 | { |
181 | struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); | 181 | struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); |
182 | struct posix_acl *acl; | 182 | struct posix_acl *acl; |
@@ -247,8 +247,13 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl) | |||
247 | if (rc < 0) | 247 | if (rc < 0) |
248 | return rc; | 248 | return rc; |
249 | if (inode->i_mode != mode) { | 249 | if (inode->i_mode != mode) { |
250 | inode->i_mode = mode; | 250 | struct iattr attr; |
251 | jffs2_dirty_inode(inode); | 251 | |
252 | attr.ia_valid = ATTR_MODE; | ||
253 | attr.ia_mode = mode; | ||
254 | rc = jffs2_do_setattr(inode, &attr); | ||
255 | if (rc < 0) | ||
256 | return rc; | ||
252 | } | 257 | } |
253 | if (rc == 0) | 258 | if (rc == 0) |
254 | acl = NULL; | 259 | acl = NULL; |
@@ -307,22 +312,16 @@ int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd) | |||
307 | return generic_permission(inode, mask, jffs2_check_acl); | 312 | return generic_permission(inode, mask, jffs2_check_acl); |
308 | } | 313 | } |
309 | 314 | ||
310 | int jffs2_init_acl(struct inode *inode, struct inode *dir) | 315 | int jffs2_init_acl(struct inode *inode, struct posix_acl *acl) |
311 | { | 316 | { |
312 | struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); | 317 | struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); |
313 | struct posix_acl *acl = NULL, *clone; | 318 | struct posix_acl *clone; |
314 | mode_t mode; | 319 | mode_t mode; |
315 | int rc = 0; | 320 | int rc = 0; |
316 | 321 | ||
317 | f->i_acl_access = JFFS2_ACL_NOT_CACHED; | 322 | f->i_acl_access = JFFS2_ACL_NOT_CACHED; |
318 | f->i_acl_default = JFFS2_ACL_NOT_CACHED; | 323 | f->i_acl_default = JFFS2_ACL_NOT_CACHED; |
319 | if (!S_ISLNK(inode->i_mode)) { | 324 | |
320 | acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT); | ||
321 | if (IS_ERR(acl)) | ||
322 | return PTR_ERR(acl); | ||
323 | if (!acl) | ||
324 | inode->i_mode &= ~current->fs->umask; | ||
325 | } | ||
326 | if (acl) { | 325 | if (acl) { |
327 | if (S_ISDIR(inode->i_mode)) { | 326 | if (S_ISDIR(inode->i_mode)) { |
328 | rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl); | 327 | rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl); |
diff --git a/fs/jffs2/acl.h b/fs/jffs2/acl.h index c84378cee82a..90a2dbf59051 100644 --- a/fs/jffs2/acl.h +++ b/fs/jffs2/acl.h | |||
@@ -28,9 +28,10 @@ struct jffs2_acl_header { | |||
28 | 28 | ||
29 | #define JFFS2_ACL_NOT_CACHED ((void *)-1) | 29 | #define JFFS2_ACL_NOT_CACHED ((void *)-1) |
30 | 30 | ||
31 | extern struct posix_acl *jffs2_get_acl(struct inode *inode, int type); | ||
31 | extern int jffs2_permission(struct inode *, int, struct nameidata *); | 32 | extern int jffs2_permission(struct inode *, int, struct nameidata *); |
32 | extern int jffs2_acl_chmod(struct inode *); | 33 | extern int jffs2_acl_chmod(struct inode *); |
33 | extern int jffs2_init_acl(struct inode *, struct inode *); | 34 | extern int jffs2_init_acl(struct inode *, struct posix_acl *); |
34 | extern void jffs2_clear_acl(struct jffs2_inode_info *); | 35 | extern void jffs2_clear_acl(struct jffs2_inode_info *); |
35 | 36 | ||
36 | extern struct xattr_handler jffs2_acl_access_xattr_handler; | 37 | extern struct xattr_handler jffs2_acl_access_xattr_handler; |
@@ -38,6 +39,7 @@ extern struct xattr_handler jffs2_acl_default_xattr_handler; | |||
38 | 39 | ||
39 | #else | 40 | #else |
40 | 41 | ||
42 | #define jffs2_get_acl(inode, type) (NULL) | ||
41 | #define jffs2_permission NULL | 43 | #define jffs2_permission NULL |
42 | #define jffs2_acl_chmod(inode) (0) | 44 | #define jffs2_acl_chmod(inode) (0) |
43 | #define jffs2_init_acl(inode,dir) (0) | 45 | #define jffs2_init_acl(inode,dir) (0) |
diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index 504643f2e98b..d568ae846741 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c | |||
@@ -23,8 +23,8 @@ static int jffs2_garbage_collect_thread(void *); | |||
23 | void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) | 23 | void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) |
24 | { | 24 | { |
25 | spin_lock(&c->erase_completion_lock); | 25 | spin_lock(&c->erase_completion_lock); |
26 | if (c->gc_task && jffs2_thread_should_wake(c)) | 26 | if (c->gc_task && jffs2_thread_should_wake(c)) |
27 | send_sig(SIGHUP, c->gc_task, 1); | 27 | send_sig(SIGHUP, c->gc_task, 1); |
28 | spin_unlock(&c->erase_completion_lock); | 28 | spin_unlock(&c->erase_completion_lock); |
29 | } | 29 | } |
30 | 30 | ||
diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 0ca2fff2617f..722a6b682951 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c | |||
@@ -285,6 +285,14 @@ static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c) | |||
285 | than actually making progress? */ | 285 | than actually making progress? */ |
286 | c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2; | 286 | c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2; |
287 | 287 | ||
288 | /* What number of 'very dirty' eraseblocks do we allow before we | ||
289 | trigger the GC thread even if we don't _need_ the space. When we | ||
290 | can't mark nodes obsolete on the medium, the old dirty nodes cause | ||
291 | performance problems because we have to inspect and discard them. */ | ||
292 | c->vdirty_blocks_gctrigger = c->resv_blocks_gctrigger; | ||
293 | if (jffs2_can_mark_obsolete(c)) | ||
294 | c->vdirty_blocks_gctrigger *= 10; | ||
295 | |||
288 | /* If there's less than this amount of dirty space, don't bother | 296 | /* If there's less than this amount of dirty space, don't bother |
289 | trying to GC to make more space. It'll be a fruitless task */ | 297 | trying to GC to make more space. It'll be a fruitless task */ |
290 | c->nospc_dirty_size = c->sector_size + (c->flash_size / 100); | 298 | c->nospc_dirty_size = c->sector_size + (c->flash_size / 100); |
@@ -303,6 +311,8 @@ static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c) | |||
303 | c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024); | 311 | c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024); |
304 | dbg_fsbuild("Amount of dirty space required to GC: %d bytes\n", | 312 | dbg_fsbuild("Amount of dirty space required to GC: %d bytes\n", |
305 | c->nospc_dirty_size); | 313 | c->nospc_dirty_size); |
314 | dbg_fsbuild("Very dirty blocks before GC triggered: %d\n", | ||
315 | c->vdirty_blocks_gctrigger); | ||
306 | } | 316 | } |
307 | 317 | ||
308 | int jffs2_do_mount_fs(struct jffs2_sb_info *c) | 318 | int jffs2_do_mount_fs(struct jffs2_sb_info *c) |
diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c index 485d065de41f..86739ee53b37 100644 --- a/fs/jffs2/compr.c +++ b/fs/jffs2/compr.c | |||
@@ -5,7 +5,7 @@ | |||
5 | * Created by Arjan van de Ven <arjanv@redhat.com> | 5 | * Created by Arjan van de Ven <arjanv@redhat.com> |
6 | * | 6 | * |
7 | * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, | 7 | * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, |
8 | * University of Szeged, Hungary | 8 | * University of Szeged, Hungary |
9 | * | 9 | * |
10 | * For licensing information, see the file 'LICENCE' in this directory. | 10 | * For licensing information, see the file 'LICENCE' in this directory. |
11 | * | 11 | * |
@@ -24,6 +24,34 @@ static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY; | |||
24 | /* Statistics for blocks stored without compression */ | 24 | /* Statistics for blocks stored without compression */ |
25 | static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; | 25 | static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0; |
26 | 26 | ||
27 | |||
28 | /* | ||
29 | * Return 1 to use this compression | ||
30 | */ | ||
31 | static int jffs2_is_best_compression(struct jffs2_compressor *this, | ||
32 | struct jffs2_compressor *best, uint32_t size, uint32_t bestsize) | ||
33 | { | ||
34 | switch (jffs2_compression_mode) { | ||
35 | case JFFS2_COMPR_MODE_SIZE: | ||
36 | if (bestsize > size) | ||
37 | return 1; | ||
38 | return 0; | ||
39 | case JFFS2_COMPR_MODE_FAVOURLZO: | ||
40 | if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size)) | ||
41 | return 1; | ||
42 | if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size)) | ||
43 | return 1; | ||
44 | if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100))) | ||
45 | return 1; | ||
46 | if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size) | ||
47 | return 1; | ||
48 | |||
49 | return 0; | ||
50 | } | ||
51 | /* Shouldn't happen */ | ||
52 | return 0; | ||
53 | } | ||
54 | |||
27 | /* jffs2_compress: | 55 | /* jffs2_compress: |
28 | * @data: Pointer to uncompressed data | 56 | * @data: Pointer to uncompressed data |
29 | * @cdata: Pointer to returned pointer to buffer for compressed data | 57 | * @cdata: Pointer to returned pointer to buffer for compressed data |
@@ -43,121 +71,124 @@ static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_co | |||
43 | * *datalen accordingly to show the amount of data which were compressed. | 71 | * *datalen accordingly to show the amount of data which were compressed. |
44 | */ | 72 | */ |
45 | uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, | 73 | uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, |
46 | unsigned char *data_in, unsigned char **cpage_out, | 74 | unsigned char *data_in, unsigned char **cpage_out, |
47 | uint32_t *datalen, uint32_t *cdatalen) | 75 | uint32_t *datalen, uint32_t *cdatalen) |
48 | { | 76 | { |
49 | int ret = JFFS2_COMPR_NONE; | 77 | int ret = JFFS2_COMPR_NONE; |
50 | int compr_ret; | 78 | int compr_ret; |
51 | struct jffs2_compressor *this, *best=NULL; | 79 | struct jffs2_compressor *this, *best=NULL; |
52 | unsigned char *output_buf = NULL, *tmp_buf; | 80 | unsigned char *output_buf = NULL, *tmp_buf; |
53 | uint32_t orig_slen, orig_dlen; | 81 | uint32_t orig_slen, orig_dlen; |
54 | uint32_t best_slen=0, best_dlen=0; | 82 | uint32_t best_slen=0, best_dlen=0; |
55 | 83 | ||
56 | switch (jffs2_compression_mode) { | 84 | switch (jffs2_compression_mode) { |
57 | case JFFS2_COMPR_MODE_NONE: | 85 | case JFFS2_COMPR_MODE_NONE: |
58 | break; | 86 | break; |
59 | case JFFS2_COMPR_MODE_PRIORITY: | 87 | case JFFS2_COMPR_MODE_PRIORITY: |
60 | output_buf = kmalloc(*cdatalen,GFP_KERNEL); | 88 | output_buf = kmalloc(*cdatalen,GFP_KERNEL); |
61 | if (!output_buf) { | 89 | if (!output_buf) { |
62 | printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n"); | 90 | printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n"); |
63 | goto out; | 91 | goto out; |
64 | } | 92 | } |
65 | orig_slen = *datalen; | 93 | orig_slen = *datalen; |
66 | orig_dlen = *cdatalen; | 94 | orig_dlen = *cdatalen; |
67 | spin_lock(&jffs2_compressor_list_lock); | 95 | spin_lock(&jffs2_compressor_list_lock); |
68 | list_for_each_entry(this, &jffs2_compressor_list, list) { | 96 | list_for_each_entry(this, &jffs2_compressor_list, list) { |
69 | /* Skip decompress-only backwards-compatibility and disabled modules */ | 97 | /* Skip decompress-only backwards-compatibility and disabled modules */ |
70 | if ((!this->compress)||(this->disabled)) | 98 | if ((!this->compress)||(this->disabled)) |
71 | continue; | 99 | continue; |
72 | 100 | ||
73 | this->usecount++; | 101 | this->usecount++; |
74 | spin_unlock(&jffs2_compressor_list_lock); | 102 | spin_unlock(&jffs2_compressor_list_lock); |
75 | *datalen = orig_slen; | 103 | *datalen = orig_slen; |
76 | *cdatalen = orig_dlen; | 104 | *cdatalen = orig_dlen; |
77 | compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL); | 105 | compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL); |
78 | spin_lock(&jffs2_compressor_list_lock); | 106 | spin_lock(&jffs2_compressor_list_lock); |
79 | this->usecount--; | 107 | this->usecount--; |
80 | if (!compr_ret) { | 108 | if (!compr_ret) { |
81 | ret = this->compr; | 109 | ret = this->compr; |
82 | this->stat_compr_blocks++; | 110 | this->stat_compr_blocks++; |
83 | this->stat_compr_orig_size += *datalen; | 111 | this->stat_compr_orig_size += *datalen; |
84 | this->stat_compr_new_size += *cdatalen; | 112 | this->stat_compr_new_size += *cdatalen; |
85 | break; | 113 | break; |
86 | } | 114 | } |
87 | } | 115 | } |
88 | spin_unlock(&jffs2_compressor_list_lock); | 116 | spin_unlock(&jffs2_compressor_list_lock); |
89 | if (ret == JFFS2_COMPR_NONE) kfree(output_buf); | 117 | if (ret == JFFS2_COMPR_NONE) |
90 | break; | 118 | kfree(output_buf); |
91 | case JFFS2_COMPR_MODE_SIZE: | 119 | break; |
92 | orig_slen = *datalen; | 120 | case JFFS2_COMPR_MODE_SIZE: |
93 | orig_dlen = *cdatalen; | 121 | case JFFS2_COMPR_MODE_FAVOURLZO: |
94 | spin_lock(&jffs2_compressor_list_lock); | 122 | orig_slen = *datalen; |
95 | list_for_each_entry(this, &jffs2_compressor_list, list) { | 123 | orig_dlen = *cdatalen; |
96 | /* Skip decompress-only backwards-compatibility and disabled modules */ | 124 | spin_lock(&jffs2_compressor_list_lock); |
97 | if ((!this->compress)||(this->disabled)) | 125 | list_for_each_entry(this, &jffs2_compressor_list, list) { |
98 | continue; | 126 | /* Skip decompress-only backwards-compatibility and disabled modules */ |
99 | /* Allocating memory for output buffer if necessary */ | 127 | if ((!this->compress)||(this->disabled)) |
100 | if ((this->compr_buf_size<orig_dlen)&&(this->compr_buf)) { | 128 | continue; |
101 | spin_unlock(&jffs2_compressor_list_lock); | 129 | /* Allocating memory for output buffer if necessary */ |
102 | kfree(this->compr_buf); | 130 | if ((this->compr_buf_size < orig_slen) && (this->compr_buf)) { |
103 | spin_lock(&jffs2_compressor_list_lock); | 131 | spin_unlock(&jffs2_compressor_list_lock); |
104 | this->compr_buf_size=0; | 132 | kfree(this->compr_buf); |
105 | this->compr_buf=NULL; | 133 | spin_lock(&jffs2_compressor_list_lock); |
106 | } | 134 | this->compr_buf_size=0; |
107 | if (!this->compr_buf) { | 135 | this->compr_buf=NULL; |
108 | spin_unlock(&jffs2_compressor_list_lock); | 136 | } |
109 | tmp_buf = kmalloc(orig_dlen,GFP_KERNEL); | 137 | if (!this->compr_buf) { |
110 | spin_lock(&jffs2_compressor_list_lock); | 138 | spin_unlock(&jffs2_compressor_list_lock); |
111 | if (!tmp_buf) { | 139 | tmp_buf = kmalloc(orig_slen, GFP_KERNEL); |
112 | printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen); | 140 | spin_lock(&jffs2_compressor_list_lock); |
113 | continue; | 141 | if (!tmp_buf) { |
114 | } | 142 | printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n", orig_slen); |
115 | else { | 143 | continue; |
116 | this->compr_buf = tmp_buf; | 144 | } |
117 | this->compr_buf_size = orig_dlen; | 145 | else { |
118 | } | 146 | this->compr_buf = tmp_buf; |
119 | } | 147 | this->compr_buf_size = orig_slen; |
120 | this->usecount++; | 148 | } |
121 | spin_unlock(&jffs2_compressor_list_lock); | 149 | } |
122 | *datalen = orig_slen; | 150 | this->usecount++; |
123 | *cdatalen = orig_dlen; | 151 | spin_unlock(&jffs2_compressor_list_lock); |
124 | compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL); | 152 | *datalen = orig_slen; |
125 | spin_lock(&jffs2_compressor_list_lock); | 153 | *cdatalen = orig_dlen; |
126 | this->usecount--; | 154 | compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL); |
127 | if (!compr_ret) { | 155 | spin_lock(&jffs2_compressor_list_lock); |
128 | if ((!best_dlen)||(best_dlen>*cdatalen)) { | 156 | this->usecount--; |
129 | best_dlen = *cdatalen; | 157 | if (!compr_ret) { |
130 | best_slen = *datalen; | 158 | if (((!best_dlen) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen)) |
131 | best = this; | 159 | && (*cdatalen < *datalen)) { |
132 | } | 160 | best_dlen = *cdatalen; |
133 | } | 161 | best_slen = *datalen; |
134 | } | 162 | best = this; |
135 | if (best_dlen) { | 163 | } |
136 | *cdatalen = best_dlen; | 164 | } |
137 | *datalen = best_slen; | 165 | } |
138 | output_buf = best->compr_buf; | 166 | if (best_dlen) { |
139 | best->compr_buf = NULL; | 167 | *cdatalen = best_dlen; |
140 | best->compr_buf_size = 0; | 168 | *datalen = best_slen; |
141 | best->stat_compr_blocks++; | 169 | output_buf = best->compr_buf; |
142 | best->stat_compr_orig_size += best_slen; | 170 | best->compr_buf = NULL; |
143 | best->stat_compr_new_size += best_dlen; | 171 | best->compr_buf_size = 0; |
144 | ret = best->compr; | 172 | best->stat_compr_blocks++; |
145 | } | 173 | best->stat_compr_orig_size += best_slen; |
146 | spin_unlock(&jffs2_compressor_list_lock); | 174 | best->stat_compr_new_size += best_dlen; |
147 | break; | 175 | ret = best->compr; |
148 | default: | 176 | } |
149 | printk(KERN_ERR "JFFS2: unknow compression mode.\n"); | 177 | spin_unlock(&jffs2_compressor_list_lock); |
150 | } | 178 | break; |
179 | default: | ||
180 | printk(KERN_ERR "JFFS2: unknow compression mode.\n"); | ||
181 | } | ||
151 | out: | 182 | out: |
152 | if (ret == JFFS2_COMPR_NONE) { | 183 | if (ret == JFFS2_COMPR_NONE) { |
153 | *cpage_out = data_in; | 184 | *cpage_out = data_in; |
154 | *datalen = *cdatalen; | 185 | *datalen = *cdatalen; |
155 | none_stat_compr_blocks++; | 186 | none_stat_compr_blocks++; |
156 | none_stat_compr_size += *datalen; | 187 | none_stat_compr_size += *datalen; |
157 | } | 188 | } |
158 | else { | 189 | else { |
159 | *cpage_out = output_buf; | 190 | *cpage_out = output_buf; |
160 | } | 191 | } |
161 | return ret; | 192 | return ret; |
162 | } | 193 | } |
163 | 194 | ||
@@ -165,8 +196,8 @@ int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, | |||
165 | uint16_t comprtype, unsigned char *cdata_in, | 196 | uint16_t comprtype, unsigned char *cdata_in, |
166 | unsigned char *data_out, uint32_t cdatalen, uint32_t datalen) | 197 | unsigned char *data_out, uint32_t cdatalen, uint32_t datalen) |
167 | { | 198 | { |
168 | struct jffs2_compressor *this; | 199 | struct jffs2_compressor *this; |
169 | int ret; | 200 | int ret; |
170 | 201 | ||
171 | /* Older code had a bug where it would write non-zero 'usercompr' | 202 | /* Older code had a bug where it would write non-zero 'usercompr' |
172 | fields. Deal with it. */ | 203 | fields. Deal with it. */ |
@@ -177,32 +208,32 @@ int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, | |||
177 | case JFFS2_COMPR_NONE: | 208 | case JFFS2_COMPR_NONE: |
178 | /* This should be special-cased elsewhere, but we might as well deal with it */ | 209 | /* This should be special-cased elsewhere, but we might as well deal with it */ |
179 | memcpy(data_out, cdata_in, datalen); | 210 | memcpy(data_out, cdata_in, datalen); |
180 | none_stat_decompr_blocks++; | 211 | none_stat_decompr_blocks++; |
181 | break; | 212 | break; |
182 | case JFFS2_COMPR_ZERO: | 213 | case JFFS2_COMPR_ZERO: |
183 | memset(data_out, 0, datalen); | 214 | memset(data_out, 0, datalen); |
184 | break; | 215 | break; |
185 | default: | 216 | default: |
186 | spin_lock(&jffs2_compressor_list_lock); | 217 | spin_lock(&jffs2_compressor_list_lock); |
187 | list_for_each_entry(this, &jffs2_compressor_list, list) { | 218 | list_for_each_entry(this, &jffs2_compressor_list, list) { |
188 | if (comprtype == this->compr) { | 219 | if (comprtype == this->compr) { |
189 | this->usecount++; | 220 | this->usecount++; |
190 | spin_unlock(&jffs2_compressor_list_lock); | 221 | spin_unlock(&jffs2_compressor_list_lock); |
191 | ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL); | 222 | ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL); |
192 | spin_lock(&jffs2_compressor_list_lock); | 223 | spin_lock(&jffs2_compressor_list_lock); |
193 | if (ret) { | 224 | if (ret) { |
194 | printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret); | 225 | printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret); |
195 | } | 226 | } |
196 | else { | 227 | else { |
197 | this->stat_decompr_blocks++; | 228 | this->stat_decompr_blocks++; |
198 | } | 229 | } |
199 | this->usecount--; | 230 | this->usecount--; |
200 | spin_unlock(&jffs2_compressor_list_lock); | 231 | spin_unlock(&jffs2_compressor_list_lock); |
201 | return ret; | 232 | return ret; |
202 | } | 233 | } |
203 | } | 234 | } |
204 | printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype); | 235 | printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype); |
205 | spin_unlock(&jffs2_compressor_list_lock); | 236 | spin_unlock(&jffs2_compressor_list_lock); |
206 | return -EIO; | 237 | return -EIO; |
207 | } | 238 | } |
208 | return 0; | 239 | return 0; |
@@ -210,108 +241,119 @@ int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, | |||
210 | 241 | ||
211 | int jffs2_register_compressor(struct jffs2_compressor *comp) | 242 | int jffs2_register_compressor(struct jffs2_compressor *comp) |
212 | { | 243 | { |
213 | struct jffs2_compressor *this; | 244 | struct jffs2_compressor *this; |
214 | 245 | ||
215 | if (!comp->name) { | 246 | if (!comp->name) { |
216 | printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n"); | 247 | printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n"); |
217 | return -1; | 248 | return -1; |
218 | } | 249 | } |
219 | comp->compr_buf_size=0; | 250 | comp->compr_buf_size=0; |
220 | comp->compr_buf=NULL; | 251 | comp->compr_buf=NULL; |
221 | comp->usecount=0; | 252 | comp->usecount=0; |
222 | comp->stat_compr_orig_size=0; | 253 | comp->stat_compr_orig_size=0; |
223 | comp->stat_compr_new_size=0; | 254 | comp->stat_compr_new_size=0; |
224 | comp->stat_compr_blocks=0; | 255 | comp->stat_compr_blocks=0; |
225 | comp->stat_decompr_blocks=0; | 256 | comp->stat_decompr_blocks=0; |
226 | D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name)); | 257 | D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name)); |
227 | 258 | ||
228 | spin_lock(&jffs2_compressor_list_lock); | 259 | spin_lock(&jffs2_compressor_list_lock); |
229 | 260 | ||
230 | list_for_each_entry(this, &jffs2_compressor_list, list) { | 261 | list_for_each_entry(this, &jffs2_compressor_list, list) { |
231 | if (this->priority < comp->priority) { | 262 | if (this->priority < comp->priority) { |
232 | list_add(&comp->list, this->list.prev); | 263 | list_add(&comp->list, this->list.prev); |
233 | goto out; | 264 | goto out; |
234 | } | 265 | } |
235 | } | 266 | } |
236 | list_add_tail(&comp->list, &jffs2_compressor_list); | 267 | list_add_tail(&comp->list, &jffs2_compressor_list); |
237 | out: | 268 | out: |
238 | D2(list_for_each_entry(this, &jffs2_compressor_list, list) { | 269 | D2(list_for_each_entry(this, &jffs2_compressor_list, list) { |
239 | printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); | 270 | printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); |
240 | }) | 271 | }) |
241 | 272 | ||
242 | spin_unlock(&jffs2_compressor_list_lock); | 273 | spin_unlock(&jffs2_compressor_list_lock); |
243 | 274 | ||
244 | return 0; | 275 | return 0; |
245 | } | 276 | } |
246 | 277 | ||
247 | int jffs2_unregister_compressor(struct jffs2_compressor *comp) | 278 | int jffs2_unregister_compressor(struct jffs2_compressor *comp) |
248 | { | 279 | { |
249 | D2(struct jffs2_compressor *this;) | 280 | D2(struct jffs2_compressor *this;) |
250 | 281 | ||
251 | D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name)); | 282 | D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name)); |
252 | 283 | ||
253 | spin_lock(&jffs2_compressor_list_lock); | 284 | spin_lock(&jffs2_compressor_list_lock); |
254 | 285 | ||
255 | if (comp->usecount) { | 286 | if (comp->usecount) { |
256 | spin_unlock(&jffs2_compressor_list_lock); | 287 | spin_unlock(&jffs2_compressor_list_lock); |
257 | printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n"); | 288 | printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n"); |
258 | return -1; | 289 | return -1; |
259 | } | 290 | } |
260 | list_del(&comp->list); | 291 | list_del(&comp->list); |
261 | 292 | ||
262 | D2(list_for_each_entry(this, &jffs2_compressor_list, list) { | 293 | D2(list_for_each_entry(this, &jffs2_compressor_list, list) { |
263 | printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); | 294 | printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority); |
264 | }) | 295 | }) |
265 | spin_unlock(&jffs2_compressor_list_lock); | 296 | spin_unlock(&jffs2_compressor_list_lock); |
266 | return 0; | 297 | return 0; |
267 | } | 298 | } |
268 | 299 | ||
269 | void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig) | 300 | void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig) |
270 | { | 301 | { |
271 | if (orig != comprbuf) | 302 | if (orig != comprbuf) |
272 | kfree(comprbuf); | 303 | kfree(comprbuf); |
273 | } | 304 | } |
274 | 305 | ||
275 | int __init jffs2_compressors_init(void) | 306 | int __init jffs2_compressors_init(void) |
276 | { | 307 | { |
277 | /* Registering compressors */ | 308 | /* Registering compressors */ |
278 | #ifdef CONFIG_JFFS2_ZLIB | 309 | #ifdef CONFIG_JFFS2_ZLIB |
279 | jffs2_zlib_init(); | 310 | jffs2_zlib_init(); |
280 | #endif | 311 | #endif |
281 | #ifdef CONFIG_JFFS2_RTIME | 312 | #ifdef CONFIG_JFFS2_RTIME |
282 | jffs2_rtime_init(); | 313 | jffs2_rtime_init(); |
283 | #endif | 314 | #endif |
284 | #ifdef CONFIG_JFFS2_RUBIN | 315 | #ifdef CONFIG_JFFS2_RUBIN |
285 | jffs2_rubinmips_init(); | 316 | jffs2_rubinmips_init(); |
286 | jffs2_dynrubin_init(); | 317 | jffs2_dynrubin_init(); |
318 | #endif | ||
319 | #ifdef CONFIG_JFFS2_LZO | ||
320 | jffs2_lzo_init(); | ||
287 | #endif | 321 | #endif |
288 | /* Setting default compression mode */ | 322 | /* Setting default compression mode */ |
289 | #ifdef CONFIG_JFFS2_CMODE_NONE | 323 | #ifdef CONFIG_JFFS2_CMODE_NONE |
290 | jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; | 324 | jffs2_compression_mode = JFFS2_COMPR_MODE_NONE; |
291 | D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");) | 325 | D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");) |
292 | #else | 326 | #else |
293 | #ifdef CONFIG_JFFS2_CMODE_SIZE | 327 | #ifdef CONFIG_JFFS2_CMODE_SIZE |
294 | jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; | 328 | jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE; |
295 | D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");) | 329 | D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");) |
330 | #else | ||
331 | #ifdef CONFIG_JFFS2_CMODE_FAVOURLZO | ||
332 | jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO; | ||
333 | D1(printk(KERN_INFO "JFFS2: default compression mode: favourlzo\n");) | ||
296 | #else | 334 | #else |
297 | D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");) | 335 | D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");) |
336 | #endif | ||
298 | #endif | 337 | #endif |
299 | #endif | 338 | #endif |
300 | return 0; | 339 | return 0; |
301 | } | 340 | } |
302 | 341 | ||
303 | int jffs2_compressors_exit(void) | 342 | int jffs2_compressors_exit(void) |
304 | { | 343 | { |
305 | /* Unregistering compressors */ | 344 | /* Unregistering compressors */ |
345 | #ifdef CONFIG_JFFS2_LZO | ||
346 | jffs2_lzo_exit(); | ||
347 | #endif | ||
306 | #ifdef CONFIG_JFFS2_RUBIN | 348 | #ifdef CONFIG_JFFS2_RUBIN |
307 | jffs2_dynrubin_exit(); | 349 | jffs2_dynrubin_exit(); |
308 | jffs2_rubinmips_exit(); | 350 | jffs2_rubinmips_exit(); |
309 | #endif | 351 | #endif |
310 | #ifdef CONFIG_JFFS2_RTIME | 352 | #ifdef CONFIG_JFFS2_RTIME |
311 | jffs2_rtime_exit(); | 353 | jffs2_rtime_exit(); |
312 | #endif | 354 | #endif |
313 | #ifdef CONFIG_JFFS2_ZLIB | 355 | #ifdef CONFIG_JFFS2_ZLIB |
314 | jffs2_zlib_exit(); | 356 | jffs2_zlib_exit(); |
315 | #endif | 357 | #endif |
316 | return 0; | 358 | return 0; |
317 | } | 359 | } |
diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h index 68cc7010dbdf..7d1d72faa774 100644 --- a/fs/jffs2/compr.h +++ b/fs/jffs2/compr.h | |||
@@ -2,7 +2,7 @@ | |||
2 | * JFFS2 -- Journalling Flash File System, Version 2. | 2 | * JFFS2 -- Journalling Flash File System, Version 2. |
3 | * | 3 | * |
4 | * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, | 4 | * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, |
5 | * University of Szeged, Hungary | 5 | * University of Szeged, Hungary |
6 | * | 6 | * |
7 | * For licensing information, see the file 'LICENCE' in this directory. | 7 | * For licensing information, see the file 'LICENCE' in this directory. |
8 | * | 8 | * |
@@ -27,34 +27,38 @@ | |||
27 | #define JFFS2_RUBINMIPS_PRIORITY 10 | 27 | #define JFFS2_RUBINMIPS_PRIORITY 10 |
28 | #define JFFS2_DYNRUBIN_PRIORITY 20 | 28 | #define JFFS2_DYNRUBIN_PRIORITY 20 |
29 | #define JFFS2_LZARI_PRIORITY 30 | 29 | #define JFFS2_LZARI_PRIORITY 30 |
30 | #define JFFS2_LZO_PRIORITY 40 | ||
31 | #define JFFS2_RTIME_PRIORITY 50 | 30 | #define JFFS2_RTIME_PRIORITY 50 |
32 | #define JFFS2_ZLIB_PRIORITY 60 | 31 | #define JFFS2_ZLIB_PRIORITY 60 |
32 | #define JFFS2_LZO_PRIORITY 80 | ||
33 | |||
33 | 34 | ||
34 | #define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */ | 35 | #define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */ |
35 | #define JFFS2_DYNRUBIN_DISABLED /* for decompression */ | 36 | #define JFFS2_DYNRUBIN_DISABLED /* for decompression */ |
36 | 37 | ||
37 | #define JFFS2_COMPR_MODE_NONE 0 | 38 | #define JFFS2_COMPR_MODE_NONE 0 |
38 | #define JFFS2_COMPR_MODE_PRIORITY 1 | 39 | #define JFFS2_COMPR_MODE_PRIORITY 1 |
39 | #define JFFS2_COMPR_MODE_SIZE 2 | 40 | #define JFFS2_COMPR_MODE_SIZE 2 |
41 | #define JFFS2_COMPR_MODE_FAVOURLZO 3 | ||
42 | |||
43 | #define FAVOUR_LZO_PERCENT 80 | ||
40 | 44 | ||
41 | struct jffs2_compressor { | 45 | struct jffs2_compressor { |
42 | struct list_head list; | 46 | struct list_head list; |
43 | int priority; /* used by prirority comr. mode */ | 47 | int priority; /* used by prirority comr. mode */ |
44 | char *name; | 48 | char *name; |
45 | char compr; /* JFFS2_COMPR_XXX */ | 49 | char compr; /* JFFS2_COMPR_XXX */ |
46 | int (*compress)(unsigned char *data_in, unsigned char *cpage_out, | 50 | int (*compress)(unsigned char *data_in, unsigned char *cpage_out, |
47 | uint32_t *srclen, uint32_t *destlen, void *model); | 51 | uint32_t *srclen, uint32_t *destlen, void *model); |
48 | int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, | 52 | int (*decompress)(unsigned char *cdata_in, unsigned char *data_out, |
49 | uint32_t cdatalen, uint32_t datalen, void *model); | 53 | uint32_t cdatalen, uint32_t datalen, void *model); |
50 | int usecount; | 54 | int usecount; |
51 | int disabled; /* if seted the compressor won't compress */ | 55 | int disabled; /* if set the compressor won't compress */ |
52 | unsigned char *compr_buf; /* used by size compr. mode */ | 56 | unsigned char *compr_buf; /* used by size compr. mode */ |
53 | uint32_t compr_buf_size; /* used by size compr. mode */ | 57 | uint32_t compr_buf_size; /* used by size compr. mode */ |
54 | uint32_t stat_compr_orig_size; | 58 | uint32_t stat_compr_orig_size; |
55 | uint32_t stat_compr_new_size; | 59 | uint32_t stat_compr_new_size; |
56 | uint32_t stat_compr_blocks; | 60 | uint32_t stat_compr_blocks; |
57 | uint32_t stat_decompr_blocks; | 61 | uint32_t stat_decompr_blocks; |
58 | }; | 62 | }; |
59 | 63 | ||
60 | int jffs2_register_compressor(struct jffs2_compressor *comp); | 64 | int jffs2_register_compressor(struct jffs2_compressor *comp); |
@@ -64,12 +68,12 @@ int jffs2_compressors_init(void); | |||
64 | int jffs2_compressors_exit(void); | 68 | int jffs2_compressors_exit(void); |
65 | 69 | ||
66 | uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, | 70 | uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, |
67 | unsigned char *data_in, unsigned char **cpage_out, | 71 | unsigned char *data_in, unsigned char **cpage_out, |
68 | uint32_t *datalen, uint32_t *cdatalen); | 72 | uint32_t *datalen, uint32_t *cdatalen); |
69 | 73 | ||
70 | int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, | 74 | int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f, |
71 | uint16_t comprtype, unsigned char *cdata_in, | 75 | uint16_t comprtype, unsigned char *cdata_in, |
72 | unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); | 76 | unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); |
73 | 77 | ||
74 | void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig); | 78 | void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig); |
75 | 79 | ||
@@ -90,5 +94,9 @@ void jffs2_rtime_exit(void); | |||
90 | int jffs2_zlib_init(void); | 94 | int jffs2_zlib_init(void); |
91 | void jffs2_zlib_exit(void); | 95 | void jffs2_zlib_exit(void); |
92 | #endif | 96 | #endif |
97 | #ifdef CONFIG_JFFS2_LZO | ||
98 | int jffs2_lzo_init(void); | ||
99 | void jffs2_lzo_exit(void); | ||
100 | #endif | ||
93 | 101 | ||
94 | #endif /* __JFFS2_COMPR_H__ */ | 102 | #endif /* __JFFS2_COMPR_H__ */ |
diff --git a/fs/jffs2/compr_lzo.c b/fs/jffs2/compr_lzo.c new file mode 100644 index 000000000000..47b045797e42 --- /dev/null +++ b/fs/jffs2/compr_lzo.c | |||
@@ -0,0 +1,108 @@ | |||
1 | /* | ||
2 | * JFFS2 -- Journalling Flash File System, Version 2. | ||
3 | * | ||
4 | * Copyright © 2007 Nokia Corporation. All rights reserved. | ||
5 | * | ||
6 | * Created by Richard Purdie <rpurdie@openedhand.com> | ||
7 | * | ||
8 | * For licensing information, see the file 'LICENCE' in this directory. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/vmalloc.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/lzo.h> | ||
18 | #include "compr.h" | ||
19 | |||
20 | static void *lzo_mem; | ||
21 | static void *lzo_compress_buf; | ||
22 | static DEFINE_MUTEX(deflate_mutex); | ||
23 | |||
24 | static void free_workspace(void) | ||
25 | { | ||
26 | vfree(lzo_mem); | ||
27 | vfree(lzo_compress_buf); | ||
28 | } | ||
29 | |||
30 | static int __init alloc_workspace(void) | ||
31 | { | ||
32 | lzo_mem = vmalloc(LZO1X_MEM_COMPRESS); | ||
33 | lzo_compress_buf = vmalloc(lzo1x_worst_compress(PAGE_SIZE)); | ||
34 | |||
35 | if (!lzo_mem || !lzo_compress_buf) { | ||
36 | printk(KERN_WARNING "Failed to allocate lzo deflate workspace\n"); | ||
37 | free_workspace(); | ||
38 | return -ENOMEM; | ||
39 | } | ||
40 | |||
41 | return 0; | ||
42 | } | ||
43 | |||
44 | static int jffs2_lzo_compress(unsigned char *data_in, unsigned char *cpage_out, | ||
45 | uint32_t *sourcelen, uint32_t *dstlen, void *model) | ||
46 | { | ||
47 | size_t compress_size; | ||
48 | int ret; | ||
49 | |||
50 | mutex_lock(&deflate_mutex); | ||
51 | ret = lzo1x_1_compress(data_in, *sourcelen, lzo_compress_buf, &compress_size, lzo_mem); | ||
52 | mutex_unlock(&deflate_mutex); | ||
53 | |||
54 | if (ret != LZO_E_OK) | ||
55 | return -1; | ||
56 | |||
57 | if (compress_size > *dstlen) | ||
58 | return -1; | ||
59 | |||
60 | memcpy(cpage_out, lzo_compress_buf, compress_size); | ||
61 | *dstlen = compress_size; | ||
62 | |||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out, | ||
67 | uint32_t srclen, uint32_t destlen, void *model) | ||
68 | { | ||
69 | size_t dl = destlen; | ||
70 | int ret; | ||
71 | |||
72 | ret = lzo1x_decompress_safe(data_in, srclen, cpage_out, &dl); | ||
73 | |||
74 | if (ret != LZO_E_OK || dl != destlen) | ||
75 | return -1; | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static struct jffs2_compressor jffs2_lzo_comp = { | ||
81 | .priority = JFFS2_LZO_PRIORITY, | ||
82 | .name = "lzo", | ||
83 | .compr = JFFS2_COMPR_LZO, | ||
84 | .compress = &jffs2_lzo_compress, | ||
85 | .decompress = &jffs2_lzo_decompress, | ||
86 | .disabled = 0, | ||
87 | }; | ||
88 | |||
89 | int __init jffs2_lzo_init(void) | ||
90 | { | ||
91 | int ret; | ||
92 | |||
93 | ret = alloc_workspace(); | ||
94 | if (ret < 0) | ||
95 | return ret; | ||
96 | |||
97 | ret = jffs2_register_compressor(&jffs2_lzo_comp); | ||
98 | if (ret) | ||
99 | free_workspace(); | ||
100 | |||
101 | return ret; | ||
102 | } | ||
103 | |||
104 | void jffs2_lzo_exit(void) | ||
105 | { | ||
106 | jffs2_unregister_compressor(&jffs2_lzo_comp); | ||
107 | free_workspace(); | ||
108 | } | ||
diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c index 0d0bfd2e4e0d..546d1538d076 100644 --- a/fs/jffs2/compr_rtime.c +++ b/fs/jffs2/compr_rtime.c | |||
@@ -104,7 +104,7 @@ static int jffs2_rtime_decompress(unsigned char *data_in, | |||
104 | } | 104 | } |
105 | } | 105 | } |
106 | } | 106 | } |
107 | return 0; | 107 | return 0; |
108 | } | 108 | } |
109 | 109 | ||
110 | static struct jffs2_compressor jffs2_rtime_comp = { | 110 | static struct jffs2_compressor jffs2_rtime_comp = { |
diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c index ea0431e047d5..c73fa89b5f8a 100644 --- a/fs/jffs2/compr_rubin.c +++ b/fs/jffs2/compr_rubin.c | |||
@@ -384,7 +384,7 @@ static int jffs2_rubinmips_decompress(unsigned char *data_in, | |||
384 | void *model) | 384 | void *model) |
385 | { | 385 | { |
386 | rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); | 386 | rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); |
387 | return 0; | 387 | return 0; |
388 | } | 388 | } |
389 | 389 | ||
390 | static int jffs2_dynrubin_decompress(unsigned char *data_in, | 390 | static int jffs2_dynrubin_decompress(unsigned char *data_in, |
@@ -399,7 +399,7 @@ static int jffs2_dynrubin_decompress(unsigned char *data_in, | |||
399 | bits[c] = data_in[c]; | 399 | bits[c] = data_in[c]; |
400 | 400 | ||
401 | rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen); | 401 | rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen); |
402 | return 0; | 402 | return 0; |
403 | } | 403 | } |
404 | 404 | ||
405 | static struct jffs2_compressor jffs2_rubinmips_comp = { | 405 | static struct jffs2_compressor jffs2_rubinmips_comp = { |
diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c index 2b87fccc1557..cfd301a5edfc 100644 --- a/fs/jffs2/compr_zlib.c +++ b/fs/jffs2/compr_zlib.c | |||
@@ -181,7 +181,7 @@ static int jffs2_zlib_decompress(unsigned char *data_in, | |||
181 | } | 181 | } |
182 | zlib_inflateEnd(&inf_strm); | 182 | zlib_inflateEnd(&inf_strm); |
183 | mutex_unlock(&inflate_mutex); | 183 | mutex_unlock(&inflate_mutex); |
184 | return 0; | 184 | return 0; |
185 | } | 185 | } |
186 | 186 | ||
187 | static struct jffs2_compressor jffs2_zlib_comp = { | 187 | static struct jffs2_compressor jffs2_zlib_comp = { |
@@ -203,11 +203,11 @@ int __init jffs2_zlib_init(void) | |||
203 | 203 | ||
204 | ret = alloc_workspaces(); | 204 | ret = alloc_workspaces(); |
205 | if (ret) | 205 | if (ret) |
206 | return ret; | 206 | return ret; |
207 | 207 | ||
208 | ret = jffs2_register_compressor(&jffs2_zlib_comp); | 208 | ret = jffs2_register_compressor(&jffs2_zlib_comp); |
209 | if (ret) | 209 | if (ret) |
210 | free_workspaces(); | 210 | free_workspaces(); |
211 | 211 | ||
212 | return ret; | 212 | return ret; |
213 | } | 213 | } |
diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index c1dfca310dd6..8353eb9c1799 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c | |||
@@ -32,7 +32,7 @@ static int jffs2_mkdir (struct inode *,struct dentry *,int); | |||
32 | static int jffs2_rmdir (struct inode *,struct dentry *); | 32 | static int jffs2_rmdir (struct inode *,struct dentry *); |
33 | static int jffs2_mknod (struct inode *,struct dentry *,int,dev_t); | 33 | static int jffs2_mknod (struct inode *,struct dentry *,int,dev_t); |
34 | static int jffs2_rename (struct inode *, struct dentry *, | 34 | static int jffs2_rename (struct inode *, struct dentry *, |
35 | struct inode *, struct dentry *); | 35 | struct inode *, struct dentry *); |
36 | 36 | ||
37 | const struct file_operations jffs2_dir_operations = | 37 | const struct file_operations jffs2_dir_operations = |
38 | { | 38 | { |
@@ -182,6 +182,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, | |||
182 | struct jffs2_inode_info *f, *dir_f; | 182 | struct jffs2_inode_info *f, *dir_f; |
183 | struct jffs2_sb_info *c; | 183 | struct jffs2_sb_info *c; |
184 | struct inode *inode; | 184 | struct inode *inode; |
185 | struct posix_acl *acl; | ||
185 | int ret; | 186 | int ret; |
186 | 187 | ||
187 | ri = jffs2_alloc_raw_inode(); | 188 | ri = jffs2_alloc_raw_inode(); |
@@ -192,7 +193,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, | |||
192 | 193 | ||
193 | D1(printk(KERN_DEBUG "jffs2_create()\n")); | 194 | D1(printk(KERN_DEBUG "jffs2_create()\n")); |
194 | 195 | ||
195 | inode = jffs2_new_inode(dir_i, mode, ri); | 196 | inode = jffs2_new_inode(dir_i, mode, ri, &acl); |
196 | 197 | ||
197 | if (IS_ERR(inode)) { | 198 | if (IS_ERR(inode)) { |
198 | D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n")); | 199 | D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n")); |
@@ -212,12 +213,12 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, | |||
212 | dentry->d_name.name, dentry->d_name.len); | 213 | dentry->d_name.name, dentry->d_name.len); |
213 | 214 | ||
214 | if (ret) | 215 | if (ret) |
215 | goto fail; | 216 | goto fail_acl; |
216 | 217 | ||
217 | ret = jffs2_init_security(inode, dir_i); | 218 | ret = jffs2_init_security(inode, dir_i); |
218 | if (ret) | 219 | if (ret) |
219 | goto fail; | 220 | goto fail_acl; |
220 | ret = jffs2_init_acl(inode, dir_i); | 221 | ret = jffs2_init_acl(inode, acl); |
221 | if (ret) | 222 | if (ret) |
222 | goto fail; | 223 | goto fail; |
223 | 224 | ||
@@ -230,6 +231,8 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, | |||
230 | inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages)); | 231 | inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages)); |
231 | return 0; | 232 | return 0; |
232 | 233 | ||
234 | fail_acl: | ||
235 | posix_acl_release(acl); | ||
233 | fail: | 236 | fail: |
234 | make_bad_inode(inode); | 237 | make_bad_inode(inode); |
235 | iput(inode); | 238 | iput(inode); |
@@ -306,6 +309,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char | |||
306 | struct jffs2_full_dirent *fd; | 309 | struct jffs2_full_dirent *fd; |
307 | int namelen; | 310 | int namelen; |
308 | uint32_t alloclen; | 311 | uint32_t alloclen; |
312 | struct posix_acl *acl; | ||
309 | int ret, targetlen = strlen(target); | 313 | int ret, targetlen = strlen(target); |
310 | 314 | ||
311 | /* FIXME: If you care. We'd need to use frags for the target | 315 | /* FIXME: If you care. We'd need to use frags for the target |
@@ -332,7 +336,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char | |||
332 | return ret; | 336 | return ret; |
333 | } | 337 | } |
334 | 338 | ||
335 | inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri); | 339 | inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri, &acl); |
336 | 340 | ||
337 | if (IS_ERR(inode)) { | 341 | if (IS_ERR(inode)) { |
338 | jffs2_free_raw_inode(ri); | 342 | jffs2_free_raw_inode(ri); |
@@ -362,6 +366,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char | |||
362 | up(&f->sem); | 366 | up(&f->sem); |
363 | jffs2_complete_reservation(c); | 367 | jffs2_complete_reservation(c); |
364 | jffs2_clear_inode(inode); | 368 | jffs2_clear_inode(inode); |
369 | posix_acl_release(acl); | ||
365 | return PTR_ERR(fn); | 370 | return PTR_ERR(fn); |
366 | } | 371 | } |
367 | 372 | ||
@@ -372,6 +377,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char | |||
372 | up(&f->sem); | 377 | up(&f->sem); |
373 | jffs2_complete_reservation(c); | 378 | jffs2_complete_reservation(c); |
374 | jffs2_clear_inode(inode); | 379 | jffs2_clear_inode(inode); |
380 | posix_acl_release(acl); | ||
375 | return -ENOMEM; | 381 | return -ENOMEM; |
376 | } | 382 | } |
377 | 383 | ||
@@ -389,9 +395,10 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char | |||
389 | ret = jffs2_init_security(inode, dir_i); | 395 | ret = jffs2_init_security(inode, dir_i); |
390 | if (ret) { | 396 | if (ret) { |
391 | jffs2_clear_inode(inode); | 397 | jffs2_clear_inode(inode); |
398 | posix_acl_release(acl); | ||
392 | return ret; | 399 | return ret; |
393 | } | 400 | } |
394 | ret = jffs2_init_acl(inode, dir_i); | 401 | ret = jffs2_init_acl(inode, acl); |
395 | if (ret) { | 402 | if (ret) { |
396 | jffs2_clear_inode(inode); | 403 | jffs2_clear_inode(inode); |
397 | return ret; | 404 | return ret; |
@@ -469,6 +476,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) | |||
469 | struct jffs2_full_dirent *fd; | 476 | struct jffs2_full_dirent *fd; |
470 | int namelen; | 477 | int namelen; |
471 | uint32_t alloclen; | 478 | uint32_t alloclen; |
479 | struct posix_acl *acl; | ||
472 | int ret; | 480 | int ret; |
473 | 481 | ||
474 | mode |= S_IFDIR; | 482 | mode |= S_IFDIR; |
@@ -491,7 +499,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) | |||
491 | return ret; | 499 | return ret; |
492 | } | 500 | } |
493 | 501 | ||
494 | inode = jffs2_new_inode(dir_i, mode, ri); | 502 | inode = jffs2_new_inode(dir_i, mode, ri, &acl); |
495 | 503 | ||
496 | if (IS_ERR(inode)) { | 504 | if (IS_ERR(inode)) { |
497 | jffs2_free_raw_inode(ri); | 505 | jffs2_free_raw_inode(ri); |
@@ -518,6 +526,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) | |||
518 | up(&f->sem); | 526 | up(&f->sem); |
519 | jffs2_complete_reservation(c); | 527 | jffs2_complete_reservation(c); |
520 | jffs2_clear_inode(inode); | 528 | jffs2_clear_inode(inode); |
529 | posix_acl_release(acl); | ||
521 | return PTR_ERR(fn); | 530 | return PTR_ERR(fn); |
522 | } | 531 | } |
523 | /* No data here. Only a metadata node, which will be | 532 | /* No data here. Only a metadata node, which will be |
@@ -531,9 +540,10 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) | |||
531 | ret = jffs2_init_security(inode, dir_i); | 540 | ret = jffs2_init_security(inode, dir_i); |
532 | if (ret) { | 541 | if (ret) { |
533 | jffs2_clear_inode(inode); | 542 | jffs2_clear_inode(inode); |
543 | posix_acl_release(acl); | ||
534 | return ret; | 544 | return ret; |
535 | } | 545 | } |
536 | ret = jffs2_init_acl(inode, dir_i); | 546 | ret = jffs2_init_acl(inode, acl); |
537 | if (ret) { | 547 | if (ret) { |
538 | jffs2_clear_inode(inode); | 548 | jffs2_clear_inode(inode); |
539 | return ret; | 549 | return ret; |
@@ -629,6 +639,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de | |||
629 | union jffs2_device_node dev; | 639 | union jffs2_device_node dev; |
630 | int devlen = 0; | 640 | int devlen = 0; |
631 | uint32_t alloclen; | 641 | uint32_t alloclen; |
642 | struct posix_acl *acl; | ||
632 | int ret; | 643 | int ret; |
633 | 644 | ||
634 | if (!new_valid_dev(rdev)) | 645 | if (!new_valid_dev(rdev)) |
@@ -655,7 +666,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de | |||
655 | return ret; | 666 | return ret; |
656 | } | 667 | } |
657 | 668 | ||
658 | inode = jffs2_new_inode(dir_i, mode, ri); | 669 | inode = jffs2_new_inode(dir_i, mode, ri, &acl); |
659 | 670 | ||
660 | if (IS_ERR(inode)) { | 671 | if (IS_ERR(inode)) { |
661 | jffs2_free_raw_inode(ri); | 672 | jffs2_free_raw_inode(ri); |
@@ -684,6 +695,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de | |||
684 | up(&f->sem); | 695 | up(&f->sem); |
685 | jffs2_complete_reservation(c); | 696 | jffs2_complete_reservation(c); |
686 | jffs2_clear_inode(inode); | 697 | jffs2_clear_inode(inode); |
698 | posix_acl_release(acl); | ||
687 | return PTR_ERR(fn); | 699 | return PTR_ERR(fn); |
688 | } | 700 | } |
689 | /* No data here. Only a metadata node, which will be | 701 | /* No data here. Only a metadata node, which will be |
@@ -697,9 +709,10 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de | |||
697 | ret = jffs2_init_security(inode, dir_i); | 709 | ret = jffs2_init_security(inode, dir_i); |
698 | if (ret) { | 710 | if (ret) { |
699 | jffs2_clear_inode(inode); | 711 | jffs2_clear_inode(inode); |
712 | posix_acl_release(acl); | ||
700 | return ret; | 713 | return ret; |
701 | } | 714 | } |
702 | ret = jffs2_init_acl(inode, dir_i); | 715 | ret = jffs2_init_acl(inode, acl); |
703 | if (ret) { | 716 | if (ret) { |
704 | jffs2_clear_inode(inode); | 717 | jffs2_clear_inode(inode); |
705 | return ret; | 718 | return ret; |
@@ -770,7 +783,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de | |||
770 | } | 783 | } |
771 | 784 | ||
772 | static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, | 785 | static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, |
773 | struct inode *new_dir_i, struct dentry *new_dentry) | 786 | struct inode *new_dir_i, struct dentry *new_dentry) |
774 | { | 787 | { |
775 | int ret; | 788 | int ret; |
776 | struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); | 789 | struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); |
diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index 66e7c2f1e644..a1db9180633f 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c | |||
@@ -38,8 +38,8 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, | |||
38 | #ifdef __ECOS | 38 | #ifdef __ECOS |
39 | ret = jffs2_flash_erase(c, jeb); | 39 | ret = jffs2_flash_erase(c, jeb); |
40 | if (!ret) { | 40 | if (!ret) { |
41 | jffs2_erase_succeeded(c, jeb); | 41 | jffs2_erase_succeeded(c, jeb); |
42 | return; | 42 | return; |
43 | } | 43 | } |
44 | bad_offset = jeb->offset; | 44 | bad_offset = jeb->offset; |
45 | #else /* Linux */ | 45 | #else /* Linux */ |
@@ -50,12 +50,14 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, | |||
50 | instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL); | 50 | instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL); |
51 | if (!instr) { | 51 | if (!instr) { |
52 | printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); | 52 | printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); |
53 | down(&c->erase_free_sem); | ||
53 | spin_lock(&c->erase_completion_lock); | 54 | spin_lock(&c->erase_completion_lock); |
54 | list_move(&jeb->list, &c->erase_pending_list); | 55 | list_move(&jeb->list, &c->erase_pending_list); |
55 | c->erasing_size -= c->sector_size; | 56 | c->erasing_size -= c->sector_size; |
56 | c->dirty_size += c->sector_size; | 57 | c->dirty_size += c->sector_size; |
57 | jeb->dirty_size = c->sector_size; | 58 | jeb->dirty_size = c->sector_size; |
58 | spin_unlock(&c->erase_completion_lock); | 59 | spin_unlock(&c->erase_completion_lock); |
60 | up(&c->erase_free_sem); | ||
59 | return; | 61 | return; |
60 | } | 62 | } |
61 | 63 | ||
@@ -82,12 +84,14 @@ static void jffs2_erase_block(struct jffs2_sb_info *c, | |||
82 | if (ret == -ENOMEM || ret == -EAGAIN) { | 84 | if (ret == -ENOMEM || ret == -EAGAIN) { |
83 | /* Erase failed immediately. Refile it on the list */ | 85 | /* Erase failed immediately. Refile it on the list */ |
84 | D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret)); | 86 | D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret)); |
87 | down(&c->erase_free_sem); | ||
85 | spin_lock(&c->erase_completion_lock); | 88 | spin_lock(&c->erase_completion_lock); |
86 | list_move(&jeb->list, &c->erase_pending_list); | 89 | list_move(&jeb->list, &c->erase_pending_list); |
87 | c->erasing_size -= c->sector_size; | 90 | c->erasing_size -= c->sector_size; |
88 | c->dirty_size += c->sector_size; | 91 | c->dirty_size += c->sector_size; |
89 | jeb->dirty_size = c->sector_size; | 92 | jeb->dirty_size = c->sector_size; |
90 | spin_unlock(&c->erase_completion_lock); | 93 | spin_unlock(&c->erase_completion_lock); |
94 | up(&c->erase_free_sem); | ||
91 | return; | 95 | return; |
92 | } | 96 | } |
93 | 97 | ||
@@ -114,6 +118,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) | |||
114 | jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); | 118 | jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); |
115 | list_del(&jeb->list); | 119 | list_del(&jeb->list); |
116 | spin_unlock(&c->erase_completion_lock); | 120 | spin_unlock(&c->erase_completion_lock); |
121 | up(&c->erase_free_sem); | ||
117 | jffs2_mark_erased_block(c, jeb); | 122 | jffs2_mark_erased_block(c, jeb); |
118 | 123 | ||
119 | if (!--count) { | 124 | if (!--count) { |
@@ -134,6 +139,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) | |||
134 | jffs2_free_jeb_node_refs(c, jeb); | 139 | jffs2_free_jeb_node_refs(c, jeb); |
135 | list_add(&jeb->list, &c->erasing_list); | 140 | list_add(&jeb->list, &c->erasing_list); |
136 | spin_unlock(&c->erase_completion_lock); | 141 | spin_unlock(&c->erase_completion_lock); |
142 | up(&c->erase_free_sem); | ||
137 | 143 | ||
138 | jffs2_erase_block(c, jeb); | 144 | jffs2_erase_block(c, jeb); |
139 | 145 | ||
@@ -142,23 +148,25 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) | |||
142 | } | 148 | } |
143 | 149 | ||
144 | /* Be nice */ | 150 | /* Be nice */ |
145 | cond_resched(); | 151 | yield(); |
152 | down(&c->erase_free_sem); | ||
146 | spin_lock(&c->erase_completion_lock); | 153 | spin_lock(&c->erase_completion_lock); |
147 | } | 154 | } |
148 | 155 | ||
149 | spin_unlock(&c->erase_completion_lock); | 156 | spin_unlock(&c->erase_completion_lock); |
157 | up(&c->erase_free_sem); | ||
150 | done: | 158 | done: |
151 | D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); | 159 | D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); |
152 | |||
153 | up(&c->erase_free_sem); | ||
154 | } | 160 | } |
155 | 161 | ||
156 | static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) | 162 | static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) |
157 | { | 163 | { |
158 | D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset)); | 164 | D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset)); |
165 | down(&c->erase_free_sem); | ||
159 | spin_lock(&c->erase_completion_lock); | 166 | spin_lock(&c->erase_completion_lock); |
160 | list_move_tail(&jeb->list, &c->erase_complete_list); | 167 | list_move_tail(&jeb->list, &c->erase_complete_list); |
161 | spin_unlock(&c->erase_completion_lock); | 168 | spin_unlock(&c->erase_completion_lock); |
169 | up(&c->erase_free_sem); | ||
162 | /* Ensure that kupdated calls us again to mark them clean */ | 170 | /* Ensure that kupdated calls us again to mark them clean */ |
163 | jffs2_erase_pending_trigger(c); | 171 | jffs2_erase_pending_trigger(c); |
164 | } | 172 | } |
@@ -172,22 +180,26 @@ static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock | |||
172 | failed too many times. */ | 180 | failed too many times. */ |
173 | if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) { | 181 | if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) { |
174 | /* We'd like to give this block another try. */ | 182 | /* We'd like to give this block another try. */ |
183 | down(&c->erase_free_sem); | ||
175 | spin_lock(&c->erase_completion_lock); | 184 | spin_lock(&c->erase_completion_lock); |
176 | list_move(&jeb->list, &c->erase_pending_list); | 185 | list_move(&jeb->list, &c->erase_pending_list); |
177 | c->erasing_size -= c->sector_size; | 186 | c->erasing_size -= c->sector_size; |
178 | c->dirty_size += c->sector_size; | 187 | c->dirty_size += c->sector_size; |
179 | jeb->dirty_size = c->sector_size; | 188 | jeb->dirty_size = c->sector_size; |
180 | spin_unlock(&c->erase_completion_lock); | 189 | spin_unlock(&c->erase_completion_lock); |
190 | up(&c->erase_free_sem); | ||
181 | return; | 191 | return; |
182 | } | 192 | } |
183 | } | 193 | } |
184 | 194 | ||
195 | down(&c->erase_free_sem); | ||
185 | spin_lock(&c->erase_completion_lock); | 196 | spin_lock(&c->erase_completion_lock); |
186 | c->erasing_size -= c->sector_size; | 197 | c->erasing_size -= c->sector_size; |
187 | c->bad_size += c->sector_size; | 198 | c->bad_size += c->sector_size; |
188 | list_move(&jeb->list, &c->bad_list); | 199 | list_move(&jeb->list, &c->bad_list); |
189 | c->nr_erasing_blocks--; | 200 | c->nr_erasing_blocks--; |
190 | spin_unlock(&c->erase_completion_lock); | 201 | spin_unlock(&c->erase_completion_lock); |
202 | up(&c->erase_free_sem); | ||
191 | wake_up(&c->erase_wait); | 203 | wake_up(&c->erase_wait); |
192 | } | 204 | } |
193 | 205 | ||
@@ -317,6 +329,33 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl | |||
317 | size_t retlen; | 329 | size_t retlen; |
318 | int ret = -EIO; | 330 | int ret = -EIO; |
319 | 331 | ||
332 | if (c->mtd->point) { | ||
333 | unsigned long *wordebuf; | ||
334 | |||
335 | ret = c->mtd->point(c->mtd, jeb->offset, c->sector_size, &retlen, (unsigned char **)&ebuf); | ||
336 | if (ret) { | ||
337 | D1(printk(KERN_DEBUG "MTD point failed %d\n", ret)); | ||
338 | goto do_flash_read; | ||
339 | } | ||
340 | if (retlen < c->sector_size) { | ||
341 | /* Don't muck about if it won't let us point to the whole erase sector */ | ||
342 | D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen)); | ||
343 | c->mtd->unpoint(c->mtd, ebuf, jeb->offset, retlen); | ||
344 | goto do_flash_read; | ||
345 | } | ||
346 | wordebuf = ebuf-sizeof(*wordebuf); | ||
347 | retlen /= sizeof(*wordebuf); | ||
348 | do { | ||
349 | if (*++wordebuf != ~0) | ||
350 | break; | ||
351 | } while(--retlen); | ||
352 | c->mtd->unpoint(c->mtd, ebuf, jeb->offset, c->sector_size); | ||
353 | if (retlen) | ||
354 | printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08tx\n", | ||
355 | *wordebuf, jeb->offset + c->sector_size-retlen*sizeof(*wordebuf)); | ||
356 | return 0; | ||
357 | } | ||
358 | do_flash_read: | ||
320 | ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL); | 359 | ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL); |
321 | if (!ebuf) { | 360 | if (!ebuf) { |
322 | printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", jeb->offset); | 361 | printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", jeb->offset); |
@@ -362,7 +401,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb | |||
362 | { | 401 | { |
363 | size_t retlen; | 402 | size_t retlen; |
364 | int ret; | 403 | int ret; |
365 | uint32_t bad_offset; | 404 | uint32_t uninitialized_var(bad_offset); |
366 | 405 | ||
367 | switch (jffs2_block_check_erase(c, jeb, &bad_offset)) { | 406 | switch (jffs2_block_check_erase(c, jeb, &bad_offset)) { |
368 | case -EAGAIN: goto refile; | 407 | case -EAGAIN: goto refile; |
@@ -417,6 +456,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb | |||
417 | jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL); | 456 | jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL); |
418 | } | 457 | } |
419 | 458 | ||
459 | down(&c->erase_free_sem); | ||
420 | spin_lock(&c->erase_completion_lock); | 460 | spin_lock(&c->erase_completion_lock); |
421 | c->erasing_size -= c->sector_size; | 461 | c->erasing_size -= c->sector_size; |
422 | c->free_size += jeb->free_size; | 462 | c->free_size += jeb->free_size; |
@@ -429,23 +469,28 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb | |||
429 | c->nr_erasing_blocks--; | 469 | c->nr_erasing_blocks--; |
430 | c->nr_free_blocks++; | 470 | c->nr_free_blocks++; |
431 | spin_unlock(&c->erase_completion_lock); | 471 | spin_unlock(&c->erase_completion_lock); |
472 | up(&c->erase_free_sem); | ||
432 | wake_up(&c->erase_wait); | 473 | wake_up(&c->erase_wait); |
433 | return; | 474 | return; |
434 | 475 | ||
435 | filebad: | 476 | filebad: |
477 | down(&c->erase_free_sem); | ||
436 | spin_lock(&c->erase_completion_lock); | 478 | spin_lock(&c->erase_completion_lock); |
437 | /* Stick it on a list (any list) so erase_failed can take it | 479 | /* Stick it on a list (any list) so erase_failed can take it |
438 | right off again. Silly, but shouldn't happen often. */ | 480 | right off again. Silly, but shouldn't happen often. */ |
439 | list_add(&jeb->list, &c->erasing_list); | 481 | list_add(&jeb->list, &c->erasing_list); |
440 | spin_unlock(&c->erase_completion_lock); | 482 | spin_unlock(&c->erase_completion_lock); |
483 | up(&c->erase_free_sem); | ||
441 | jffs2_erase_failed(c, jeb, bad_offset); | 484 | jffs2_erase_failed(c, jeb, bad_offset); |
442 | return; | 485 | return; |
443 | 486 | ||
444 | refile: | 487 | refile: |
445 | /* Stick it back on the list from whence it came and come back later */ | 488 | /* Stick it back on the list from whence it came and come back later */ |
446 | jffs2_erase_pending_trigger(c); | 489 | jffs2_erase_pending_trigger(c); |
490 | down(&c->erase_free_sem); | ||
447 | spin_lock(&c->erase_completion_lock); | 491 | spin_lock(&c->erase_completion_lock); |
448 | list_add(&jeb->list, &c->erase_complete_list); | 492 | list_add(&jeb->list, &c->erase_complete_list); |
449 | spin_unlock(&c->erase_completion_lock); | 493 | spin_unlock(&c->erase_completion_lock); |
494 | up(&c->erase_free_sem); | ||
450 | return; | 495 | return; |
451 | } | 496 | } |
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 8bc727b71696..ed85f9afdbc8 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c | |||
@@ -24,7 +24,7 @@ | |||
24 | 24 | ||
25 | static int jffs2_flash_setup(struct jffs2_sb_info *c); | 25 | static int jffs2_flash_setup(struct jffs2_sb_info *c); |
26 | 26 | ||
27 | static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) | 27 | int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) |
28 | { | 28 | { |
29 | struct jffs2_full_dnode *old_metadata, *new_metadata; | 29 | struct jffs2_full_dnode *old_metadata, *new_metadata; |
30 | struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); | 30 | struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); |
@@ -36,10 +36,8 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) | |||
36 | unsigned int ivalid; | 36 | unsigned int ivalid; |
37 | uint32_t alloclen; | 37 | uint32_t alloclen; |
38 | int ret; | 38 | int ret; |
39 | |||
39 | D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino)); | 40 | D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino)); |
40 | ret = inode_change_ok(inode, iattr); | ||
41 | if (ret) | ||
42 | return ret; | ||
43 | 41 | ||
44 | /* Special cases - we don't want more than one data node | 42 | /* Special cases - we don't want more than one data node |
45 | for these types on the medium at any time. So setattr | 43 | for these types on the medium at any time. So setattr |
@@ -183,9 +181,14 @@ int jffs2_setattr(struct dentry *dentry, struct iattr *iattr) | |||
183 | { | 181 | { |
184 | int rc; | 182 | int rc; |
185 | 183 | ||
184 | rc = inode_change_ok(dentry->d_inode, iattr); | ||
185 | if (rc) | ||
186 | return rc; | ||
187 | |||
186 | rc = jffs2_do_setattr(dentry->d_inode, iattr); | 188 | rc = jffs2_do_setattr(dentry->d_inode, iattr); |
187 | if (!rc && (iattr->ia_valid & ATTR_MODE)) | 189 | if (!rc && (iattr->ia_valid & ATTR_MODE)) |
188 | rc = jffs2_acl_chmod(dentry->d_inode); | 190 | rc = jffs2_acl_chmod(dentry->d_inode); |
191 | |||
189 | return rc; | 192 | return rc; |
190 | } | 193 | } |
191 | 194 | ||
@@ -399,7 +402,8 @@ void jffs2_write_super (struct super_block *sb) | |||
399 | 402 | ||
400 | /* jffs2_new_inode: allocate a new inode and inocache, add it to the hash, | 403 | /* jffs2_new_inode: allocate a new inode and inocache, add it to the hash, |
401 | fill in the raw_inode while you're at it. */ | 404 | fill in the raw_inode while you're at it. */ |
402 | struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri) | 405 | struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri, |
406 | struct posix_acl **acl) | ||
403 | { | 407 | { |
404 | struct inode *inode; | 408 | struct inode *inode; |
405 | struct super_block *sb = dir_i->i_sb; | 409 | struct super_block *sb = dir_i->i_sb; |
@@ -431,7 +435,23 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i | |||
431 | } else { | 435 | } else { |
432 | ri->gid = cpu_to_je16(current->fsgid); | 436 | ri->gid = cpu_to_je16(current->fsgid); |
433 | } | 437 | } |
434 | ri->mode = cpu_to_jemode(mode); | 438 | |
439 | /* POSIX ACLs have to be processed now, at least partly. | ||
440 | The umask is only applied if there's no default ACL */ | ||
441 | if (!S_ISLNK(mode)) { | ||
442 | *acl = jffs2_get_acl(dir_i, ACL_TYPE_DEFAULT); | ||
443 | if (IS_ERR(*acl)) { | ||
444 | make_bad_inode(inode); | ||
445 | iput(inode); | ||
446 | inode = (void *)*acl; | ||
447 | *acl = NULL; | ||
448 | return inode; | ||
449 | } | ||
450 | if (!(*acl)) | ||
451 | mode &= ~current->fs->umask; | ||
452 | } else { | ||
453 | *acl = NULL; | ||
454 | } | ||
435 | ret = jffs2_do_new_inode (c, f, mode, ri); | 455 | ret = jffs2_do_new_inode (c, f, mode, ri); |
436 | if (ret) { | 456 | if (ret) { |
437 | make_bad_inode(inode); | 457 | make_bad_inode(inode); |
diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 2d99e06ab223..32ff0373aa04 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c | |||
@@ -122,6 +122,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) | |||
122 | struct jffs2_inode_cache *ic; | 122 | struct jffs2_inode_cache *ic; |
123 | struct jffs2_eraseblock *jeb; | 123 | struct jffs2_eraseblock *jeb; |
124 | struct jffs2_raw_node_ref *raw; | 124 | struct jffs2_raw_node_ref *raw; |
125 | uint32_t gcblock_dirty; | ||
125 | int ret = 0, inum, nlink; | 126 | int ret = 0, inum, nlink; |
126 | int xattr = 0; | 127 | int xattr = 0; |
127 | 128 | ||
@@ -236,6 +237,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) | |||
236 | } | 237 | } |
237 | 238 | ||
238 | raw = jeb->gc_node; | 239 | raw = jeb->gc_node; |
240 | gcblock_dirty = jeb->dirty_size; | ||
239 | 241 | ||
240 | while(ref_obsolete(raw)) { | 242 | while(ref_obsolete(raw)) { |
241 | D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw))); | 243 | D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw))); |
@@ -282,7 +284,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) | |||
282 | } else { | 284 | } else { |
283 | ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic, raw); | 285 | ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic, raw); |
284 | } | 286 | } |
285 | goto release_sem; | 287 | goto test_gcnode; |
286 | } | 288 | } |
287 | #endif | 289 | #endif |
288 | 290 | ||
@@ -376,7 +378,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) | |||
376 | 378 | ||
377 | if (ret != -EBADFD) { | 379 | if (ret != -EBADFD) { |
378 | spin_unlock(&c->inocache_lock); | 380 | spin_unlock(&c->inocache_lock); |
379 | goto release_sem; | 381 | goto test_gcnode; |
380 | } | 382 | } |
381 | 383 | ||
382 | /* Fall through if it wanted us to, with inocache_lock held */ | 384 | /* Fall through if it wanted us to, with inocache_lock held */ |
@@ -407,6 +409,12 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) | |||
407 | 409 | ||
408 | jffs2_gc_release_inode(c, f); | 410 | jffs2_gc_release_inode(c, f); |
409 | 411 | ||
412 | test_gcnode: | ||
413 | if (jeb->dirty_size == gcblock_dirty && !ref_obsolete(jeb->gc_node)) { | ||
414 | /* Eep. This really should never happen. GC is broken */ | ||
415 | printk(KERN_ERR "Error garbage collecting node at %08x!\n", ref_offset(jeb->gc_node)); | ||
416 | ret = -ENOSPC; | ||
417 | } | ||
410 | release_sem: | 418 | release_sem: |
411 | up(&c->alloc_sem); | 419 | up(&c->alloc_sem); |
412 | 420 | ||
@@ -556,7 +564,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, | |||
556 | 564 | ||
557 | node = kmalloc(rawlen, GFP_KERNEL); | 565 | node = kmalloc(rawlen, GFP_KERNEL); |
558 | if (!node) | 566 | if (!node) |
559 | return -ENOMEM; | 567 | return -ENOMEM; |
560 | 568 | ||
561 | ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node); | 569 | ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node); |
562 | if (!ret && retlen != rawlen) | 570 | if (!ret && retlen != rawlen) |
@@ -598,10 +606,15 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, | |||
598 | goto bail; | 606 | goto bail; |
599 | } | 607 | } |
600 | 608 | ||
609 | if (strnlen(node->d.name, node->d.nsize) != node->d.nsize) { | ||
610 | printk(KERN_WARNING "Name in dirent node at 0x%08x contains zeroes\n", ref_offset(raw)); | ||
611 | goto bail; | ||
612 | } | ||
613 | |||
601 | if (node->d.nsize) { | 614 | if (node->d.nsize) { |
602 | crc = crc32(0, node->d.name, node->d.nsize); | 615 | crc = crc32(0, node->d.name, node->d.nsize); |
603 | if (je32_to_cpu(node->d.name_crc) != crc) { | 616 | if (je32_to_cpu(node->d.name_crc) != crc) { |
604 | printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n", | 617 | printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", |
605 | ref_offset(raw), je32_to_cpu(node->d.name_crc), crc); | 618 | ref_offset(raw), je32_to_cpu(node->d.name_crc), crc); |
606 | goto bail; | 619 | goto bail; |
607 | } | 620 | } |
@@ -624,7 +637,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, | |||
624 | 637 | ||
625 | if (ret || (retlen != rawlen)) { | 638 | if (ret || (retlen != rawlen)) { |
626 | printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", | 639 | printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", |
627 | rawlen, phys_ofs, ret, retlen); | 640 | rawlen, phys_ofs, ret, retlen); |
628 | if (retlen) { | 641 | if (retlen) { |
629 | jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, rawlen, NULL); | 642 | jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, rawlen, NULL); |
630 | } else { | 643 | } else { |
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index b13298a824ed..3a2197f3c812 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h | |||
@@ -69,6 +69,8 @@ struct jffs2_sb_info { | |||
69 | uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */ | 69 | uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */ |
70 | uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */ | 70 | uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */ |
71 | uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */ | 71 | uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */ |
72 | /* Number of 'very dirty' blocks before we trigger immediate GC */ | ||
73 | uint8_t vdirty_blocks_gctrigger; | ||
72 | 74 | ||
73 | uint32_t nospc_dirty_size; | 75 | uint32_t nospc_dirty_size; |
74 | 76 | ||
@@ -106,6 +108,9 @@ struct jffs2_sb_info { | |||
106 | 108 | ||
107 | uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ | 109 | uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ |
108 | 110 | ||
111 | #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY | ||
112 | unsigned char *wbuf_verify; /* read-back buffer for verification */ | ||
113 | #endif | ||
109 | #ifdef CONFIG_JFFS2_FS_WRITEBUFFER | 114 | #ifdef CONFIG_JFFS2_FS_WRITEBUFFER |
110 | unsigned char *wbuf; /* Write-behind buffer for NAND flash */ | 115 | unsigned char *wbuf; /* Write-behind buffer for NAND flash */ |
111 | uint32_t wbuf_ofs; | 116 | uint32_t wbuf_ofs; |
diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index bc5509fe577b..ec1aae9e695e 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h | |||
@@ -127,7 +127,7 @@ static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_nod | |||
127 | return ((struct jffs2_inode_cache *)raw); | 127 | return ((struct jffs2_inode_cache *)raw); |
128 | } | 128 | } |
129 | 129 | ||
130 | /* flash_offset & 3 always has to be zero, because nodes are | 130 | /* flash_offset & 3 always has to be zero, because nodes are |
131 | always aligned at 4 bytes. So we have a couple of extra bits | 131 | always aligned at 4 bytes. So we have a couple of extra bits |
132 | to play with, which indicate the node's status; see below: */ | 132 | to play with, which indicate the node's status; see below: */ |
133 | #define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */ | 133 | #define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */ |
diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index dbc908ad622b..a0313fa8748e 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c | |||
@@ -154,7 +154,7 @@ int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, | |||
154 | while(ret == -EAGAIN) { | 154 | while(ret == -EAGAIN) { |
155 | ret = jffs2_do_reserve_space(c, minsize, len, sumsize); | 155 | ret = jffs2_do_reserve_space(c, minsize, len, sumsize); |
156 | if (ret) { | 156 | if (ret) { |
157 | D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret)); | 157 | D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret)); |
158 | } | 158 | } |
159 | } | 159 | } |
160 | spin_unlock(&c->erase_completion_lock); | 160 | spin_unlock(&c->erase_completion_lock); |
@@ -423,7 +423,12 @@ struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c, | |||
423 | even after refiling c->nextblock */ | 423 | even after refiling c->nextblock */ |
424 | if ((c->nextblock || ((ofs & 3) != REF_OBSOLETE)) | 424 | if ((c->nextblock || ((ofs & 3) != REF_OBSOLETE)) |
425 | && (jeb != c->nextblock || (ofs & ~3) != jeb->offset + (c->sector_size - jeb->free_size))) { | 425 | && (jeb != c->nextblock || (ofs & ~3) != jeb->offset + (c->sector_size - jeb->free_size))) { |
426 | printk(KERN_WARNING "argh. node added in wrong place\n"); | 426 | printk(KERN_WARNING "argh. node added in wrong place at 0x%08x(%d)\n", ofs & ~3, ofs & 3); |
427 | if (c->nextblock) | ||
428 | printk(KERN_WARNING "nextblock 0x%08x", c->nextblock->offset); | ||
429 | else | ||
430 | printk(KERN_WARNING "No nextblock"); | ||
431 | printk(", expected at %08x\n", jeb->offset + (c->sector_size - jeb->free_size)); | ||
427 | return ERR_PTR(-EINVAL); | 432 | return ERR_PTR(-EINVAL); |
428 | } | 433 | } |
429 | #endif | 434 | #endif |
@@ -717,6 +722,8 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c) | |||
717 | { | 722 | { |
718 | int ret = 0; | 723 | int ret = 0; |
719 | uint32_t dirty; | 724 | uint32_t dirty; |
725 | int nr_very_dirty = 0; | ||
726 | struct jffs2_eraseblock *jeb; | ||
720 | 727 | ||
721 | if (c->unchecked_size) { | 728 | if (c->unchecked_size) { |
722 | D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", | 729 | D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", |
@@ -738,8 +745,18 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c) | |||
738 | (dirty > c->nospc_dirty_size)) | 745 | (dirty > c->nospc_dirty_size)) |
739 | ret = 1; | 746 | ret = 1; |
740 | 747 | ||
741 | D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", | 748 | list_for_each_entry(jeb, &c->very_dirty_list, list) { |
742 | c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no")); | 749 | nr_very_dirty++; |
750 | if (nr_very_dirty == c->vdirty_blocks_gctrigger) { | ||
751 | ret = 1; | ||
752 | /* In debug mode, actually go through and count them all */ | ||
753 | D1(continue); | ||
754 | break; | ||
755 | } | ||
756 | } | ||
757 | |||
758 | D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x, vdirty_blocks %d: %s\n", | ||
759 | c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, nr_very_dirty, ret?"yes":"no")); | ||
743 | 760 | ||
744 | return ret; | 761 | return ret; |
745 | } | 762 | } |
diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index 80daea96bbc2..f6743a915cf3 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h | |||
@@ -173,12 +173,15 @@ int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long); | |||
173 | extern const struct inode_operations jffs2_symlink_inode_operations; | 173 | extern const struct inode_operations jffs2_symlink_inode_operations; |
174 | 174 | ||
175 | /* fs.c */ | 175 | /* fs.c */ |
176 | struct posix_acl; | ||
177 | |||
176 | int jffs2_setattr (struct dentry *, struct iattr *); | 178 | int jffs2_setattr (struct dentry *, struct iattr *); |
179 | int jffs2_do_setattr (struct inode *, struct iattr *); | ||
177 | void jffs2_read_inode (struct inode *); | 180 | void jffs2_read_inode (struct inode *); |
178 | void jffs2_clear_inode (struct inode *); | 181 | void jffs2_clear_inode (struct inode *); |
179 | void jffs2_dirty_inode(struct inode *inode); | 182 | void jffs2_dirty_inode(struct inode *inode); |
180 | struct inode *jffs2_new_inode (struct inode *dir_i, int mode, | 183 | struct inode *jffs2_new_inode (struct inode *dir_i, int mode, |
181 | struct jffs2_raw_inode *ri); | 184 | struct jffs2_raw_inode *ri, struct posix_acl **acl); |
182 | int jffs2_statfs (struct dentry *, struct kstatfs *); | 185 | int jffs2_statfs (struct dentry *, struct kstatfs *); |
183 | void jffs2_write_super (struct super_block *); | 186 | void jffs2_write_super (struct super_block *); |
184 | int jffs2_remount_fs (struct super_block *, int *, char *); | 187 | int jffs2_remount_fs (struct super_block *, int *, char *); |
diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index b5baa356fed2..2eae5d2dbebe 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c | |||
@@ -65,7 +65,7 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info | |||
65 | err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer); | 65 | err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer); |
66 | if (!err && retlen < tn->csize) { | 66 | if (!err && retlen < tn->csize) { |
67 | JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize); | 67 | JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize); |
68 | c->mtd->unpoint(c->mtd, buffer, ofs, len); | 68 | c->mtd->unpoint(c->mtd, buffer, ofs, retlen); |
69 | } else if (err) | 69 | } else if (err) |
70 | JFFS2_WARNING("MTD point failed: error code %d.\n", err); | 70 | JFFS2_WARNING("MTD point failed: error code %d.\n", err); |
71 | else | 71 | else |
@@ -211,7 +211,7 @@ static void jffs2_kill_tn(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info * | |||
211 | * ordering. | 211 | * ordering. |
212 | * | 212 | * |
213 | * Returns 0 if the node was handled (including marking it obsolete) | 213 | * Returns 0 if the node was handled (including marking it obsolete) |
214 | * < 0 an if error occurred | 214 | * < 0 an if error occurred |
215 | */ | 215 | */ |
216 | static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, | 216 | static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c, |
217 | struct jffs2_readinode_info *rii, | 217 | struct jffs2_readinode_info *rii, |
@@ -862,8 +862,8 @@ static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_re | |||
862 | JFFS2_ERROR("REF_UNCHECKED but unknown node at %#08x\n", | 862 | JFFS2_ERROR("REF_UNCHECKED but unknown node at %#08x\n", |
863 | ref_offset(ref)); | 863 | ref_offset(ref)); |
864 | JFFS2_ERROR("Node is {%04x,%04x,%08x,%08x}. Please report this error.\n", | 864 | JFFS2_ERROR("Node is {%04x,%04x,%08x,%08x}. Please report this error.\n", |
865 | je16_to_cpu(un->magic), je16_to_cpu(un->nodetype), | 865 | je16_to_cpu(un->magic), je16_to_cpu(un->nodetype), |
866 | je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc)); | 866 | je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc)); |
867 | jffs2_mark_node_obsolete(c, ref); | 867 | jffs2_mark_node_obsolete(c, ref); |
868 | return 0; | 868 | return 0; |
869 | } | 869 | } |
diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 6c75cd433342..272872d27fd5 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c | |||
@@ -101,7 +101,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) | |||
101 | if (!ret && pointlen < c->mtd->size) { | 101 | if (!ret && pointlen < c->mtd->size) { |
102 | /* Don't muck about if it won't let us point to the whole flash */ | 102 | /* Don't muck about if it won't let us point to the whole flash */ |
103 | D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen)); | 103 | D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen)); |
104 | c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); | 104 | c->mtd->unpoint(c->mtd, flashbuf, 0, pointlen); |
105 | flashbuf = NULL; | 105 | flashbuf = NULL; |
106 | } | 106 | } |
107 | if (ret) | 107 | if (ret) |
@@ -863,7 +863,7 @@ scan_more: | |||
863 | switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) { | 863 | switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) { |
864 | case JFFS2_FEATURE_ROCOMPAT: | 864 | case JFFS2_FEATURE_ROCOMPAT: |
865 | printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs); | 865 | printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs); |
866 | c->flags |= JFFS2_SB_FLAG_RO; | 866 | c->flags |= JFFS2_SB_FLAG_RO; |
867 | if (!(jffs2_is_readonly(c))) | 867 | if (!(jffs2_is_readonly(c))) |
868 | return -EROFS; | 868 | return -EROFS; |
869 | if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen))))) | 869 | if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen))))) |
@@ -1004,6 +1004,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo | |||
1004 | { | 1004 | { |
1005 | struct jffs2_full_dirent *fd; | 1005 | struct jffs2_full_dirent *fd; |
1006 | struct jffs2_inode_cache *ic; | 1006 | struct jffs2_inode_cache *ic; |
1007 | uint32_t checkedlen; | ||
1007 | uint32_t crc; | 1008 | uint32_t crc; |
1008 | int err; | 1009 | int err; |
1009 | 1010 | ||
@@ -1024,12 +1025,18 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo | |||
1024 | 1025 | ||
1025 | pseudo_random += je32_to_cpu(rd->version); | 1026 | pseudo_random += je32_to_cpu(rd->version); |
1026 | 1027 | ||
1027 | fd = jffs2_alloc_full_dirent(rd->nsize+1); | 1028 | /* Should never happen. Did. (OLPC trac #4184)*/ |
1029 | checkedlen = strnlen(rd->name, rd->nsize); | ||
1030 | if (checkedlen < rd->nsize) { | ||
1031 | printk(KERN_ERR "Dirent at %08x has zeroes in name. Truncating to %d chars\n", | ||
1032 | ofs, checkedlen); | ||
1033 | } | ||
1034 | fd = jffs2_alloc_full_dirent(checkedlen+1); | ||
1028 | if (!fd) { | 1035 | if (!fd) { |
1029 | return -ENOMEM; | 1036 | return -ENOMEM; |
1030 | } | 1037 | } |
1031 | memcpy(&fd->name, rd->name, rd->nsize); | 1038 | memcpy(&fd->name, rd->name, checkedlen); |
1032 | fd->name[rd->nsize] = 0; | 1039 | fd->name[checkedlen] = 0; |
1033 | 1040 | ||
1034 | crc = crc32(0, fd->name, rd->nsize); | 1041 | crc = crc32(0, fd->name, rd->nsize); |
1035 | if (crc != je32_to_cpu(rd->name_crc)) { | 1042 | if (crc != je32_to_cpu(rd->name_crc)) { |
@@ -1055,7 +1062,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo | |||
1055 | fd->next = NULL; | 1062 | fd->next = NULL; |
1056 | fd->version = je32_to_cpu(rd->version); | 1063 | fd->version = je32_to_cpu(rd->version); |
1057 | fd->ino = je32_to_cpu(rd->ino); | 1064 | fd->ino = je32_to_cpu(rd->ino); |
1058 | fd->nhash = full_name_hash(fd->name, rd->nsize); | 1065 | fd->nhash = full_name_hash(fd->name, checkedlen); |
1059 | fd->type = rd->type; | 1066 | fd->type = rd->type; |
1060 | jffs2_add_fd_to_list(c, fd, &ic->scan_dents); | 1067 | jffs2_add_fd_to_list(c, fd, &ic->scan_dents); |
1061 | 1068 | ||
diff --git a/fs/jffs2/security.c b/fs/jffs2/security.c index bc9f6ba10823..02c39c64ecb3 100644 --- a/fs/jffs2/security.c +++ b/fs/jffs2/security.c | |||
@@ -38,9 +38,9 @@ int jffs2_init_security(struct inode *inode, struct inode *dir) | |||
38 | } | 38 | } |
39 | rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0); | 39 | rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0); |
40 | 40 | ||
41 | kfree(name); | 41 | kfree(name); |
42 | kfree(value); | 42 | kfree(value); |
43 | return rc; | 43 | return rc; |
44 | } | 44 | } |
45 | 45 | ||
46 | /* ---- XATTR Handler for "security.*" ----------------- */ | 46 | /* ---- XATTR Handler for "security.*" ----------------- */ |
diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c index d828b296392a..629af01e5ade 100644 --- a/fs/jffs2/summary.c +++ b/fs/jffs2/summary.c | |||
@@ -2,10 +2,10 @@ | |||
2 | * JFFS2 -- Journalling Flash File System, Version 2. | 2 | * JFFS2 -- Journalling Flash File System, Version 2. |
3 | * | 3 | * |
4 | * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, | 4 | * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, |
5 | * Zoltan Sogor <weth@inf.u-szeged.hu>, | 5 | * Zoltan Sogor <weth@inf.u-szeged.hu>, |
6 | * Patrik Kluba <pajko@halom.u-szeged.hu>, | 6 | * Patrik Kluba <pajko@halom.u-szeged.hu>, |
7 | * University of Szeged, Hungary | 7 | * University of Szeged, Hungary |
8 | * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com> | 8 | * 2006 KaiGai Kohei <kaigai@ak.jp.nec.com> |
9 | * | 9 | * |
10 | * For licensing information, see the file 'LICENCE' in this directory. | 10 | * For licensing information, see the file 'LICENCE' in this directory. |
11 | * | 11 | * |
@@ -429,6 +429,7 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras | |||
429 | 429 | ||
430 | case JFFS2_NODETYPE_DIRENT: { | 430 | case JFFS2_NODETYPE_DIRENT: { |
431 | struct jffs2_sum_dirent_flash *spd; | 431 | struct jffs2_sum_dirent_flash *spd; |
432 | int checkedlen; | ||
432 | spd = sp; | 433 | spd = sp; |
433 | 434 | ||
434 | dbg_summary("Dirent at 0x%08x-0x%08x\n", | 435 | dbg_summary("Dirent at 0x%08x-0x%08x\n", |
@@ -436,12 +437,25 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras | |||
436 | jeb->offset + je32_to_cpu(spd->offset) + je32_to_cpu(spd->totlen)); | 437 | jeb->offset + je32_to_cpu(spd->offset) + je32_to_cpu(spd->totlen)); |
437 | 438 | ||
438 | 439 | ||
439 | fd = jffs2_alloc_full_dirent(spd->nsize+1); | 440 | /* This should never happen, but https://dev.laptop.org/ticket/4184 */ |
441 | checkedlen = strnlen(spd->name, spd->nsize); | ||
442 | if (!checkedlen) { | ||
443 | printk(KERN_ERR "Dirent at %08x has zero at start of name. Aborting mount.\n", | ||
444 | jeb->offset + je32_to_cpu(spd->offset)); | ||
445 | return -EIO; | ||
446 | } | ||
447 | if (checkedlen < spd->nsize) { | ||
448 | printk(KERN_ERR "Dirent at %08x has zeroes in name. Truncating to %d chars\n", | ||
449 | jeb->offset + je32_to_cpu(spd->offset), checkedlen); | ||
450 | } | ||
451 | |||
452 | |||
453 | fd = jffs2_alloc_full_dirent(checkedlen+1); | ||
440 | if (!fd) | 454 | if (!fd) |
441 | return -ENOMEM; | 455 | return -ENOMEM; |
442 | 456 | ||
443 | memcpy(&fd->name, spd->name, spd->nsize); | 457 | memcpy(&fd->name, spd->name, checkedlen); |
444 | fd->name[spd->nsize] = 0; | 458 | fd->name[checkedlen] = 0; |
445 | 459 | ||
446 | ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino)); | 460 | ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino)); |
447 | if (!ic) { | 461 | if (!ic) { |
@@ -455,7 +469,7 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras | |||
455 | fd->next = NULL; | 469 | fd->next = NULL; |
456 | fd->version = je32_to_cpu(spd->version); | 470 | fd->version = je32_to_cpu(spd->version); |
457 | fd->ino = je32_to_cpu(spd->ino); | 471 | fd->ino = je32_to_cpu(spd->ino); |
458 | fd->nhash = full_name_hash(fd->name, spd->nsize); | 472 | fd->nhash = full_name_hash(fd->name, checkedlen); |
459 | fd->type = spd->type; | 473 | fd->type = spd->type; |
460 | 474 | ||
461 | jffs2_add_fd_to_list(c, fd, &ic->scan_dents); | 475 | jffs2_add_fd_to_list(c, fd, &ic->scan_dents); |
diff --git a/fs/jffs2/summary.h b/fs/jffs2/summary.h index 0c6669e21390..8bf34f2fa5ce 100644 --- a/fs/jffs2/summary.h +++ b/fs/jffs2/summary.h | |||
@@ -2,9 +2,9 @@ | |||
2 | * JFFS2 -- Journalling Flash File System, Version 2. | 2 | * JFFS2 -- Journalling Flash File System, Version 2. |
3 | * | 3 | * |
4 | * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, | 4 | * Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>, |
5 | * Zoltan Sogor <weth@inf.u-szeged.hu>, | 5 | * Zoltan Sogor <weth@inf.u-szeged.hu>, |
6 | * Patrik Kluba <pajko@halom.u-szeged.hu>, | 6 | * Patrik Kluba <pajko@halom.u-szeged.hu>, |
7 | * University of Szeged, Hungary | 7 | * University of Szeged, Hungary |
8 | * | 8 | * |
9 | * For licensing information, see the file 'LICENCE' in this directory. | 9 | * For licensing information, see the file 'LICENCE' in this directory. |
10 | * | 10 | * |
diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 91d1d0f1c66c..d1d4f27464ba 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c | |||
@@ -220,6 +220,47 @@ static struct jffs2_raw_node_ref **jffs2_incore_replace_raw(struct jffs2_sb_info | |||
220 | return NULL; | 220 | return NULL; |
221 | } | 221 | } |
222 | 222 | ||
223 | #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY | ||
224 | static int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf, | ||
225 | uint32_t ofs) | ||
226 | { | ||
227 | int ret; | ||
228 | size_t retlen; | ||
229 | char *eccstr; | ||
230 | |||
231 | ret = c->mtd->read(c->mtd, ofs, c->wbuf_pagesize, &retlen, c->wbuf_verify); | ||
232 | if (ret && ret != -EUCLEAN && ret != -EBADMSG) { | ||
233 | printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x failed: %d\n", c->wbuf_ofs, ret); | ||
234 | return ret; | ||
235 | } else if (retlen != c->wbuf_pagesize) { | ||
236 | printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x gave short read: %zd not %d.\n", ofs, retlen, c->wbuf_pagesize); | ||
237 | return -EIO; | ||
238 | } | ||
239 | if (!memcmp(buf, c->wbuf_verify, c->wbuf_pagesize)) | ||
240 | return 0; | ||
241 | |||
242 | if (ret == -EUCLEAN) | ||
243 | eccstr = "corrected"; | ||
244 | else if (ret == -EBADMSG) | ||
245 | eccstr = "correction failed"; | ||
246 | else | ||
247 | eccstr = "OK or unused"; | ||
248 | |||
249 | printk(KERN_WARNING "Write verify error (ECC %s) at %08x. Wrote:\n", | ||
250 | eccstr, c->wbuf_ofs); | ||
251 | print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1, | ||
252 | c->wbuf, c->wbuf_pagesize, 0); | ||
253 | |||
254 | printk(KERN_WARNING "Read back:\n"); | ||
255 | print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1, | ||
256 | c->wbuf_verify, c->wbuf_pagesize, 0); | ||
257 | |||
258 | return -EIO; | ||
259 | } | ||
260 | #else | ||
261 | #define jffs2_verify_write(c,b,o) (0) | ||
262 | #endif | ||
263 | |||
223 | /* Recover from failure to write wbuf. Recover the nodes up to the | 264 | /* Recover from failure to write wbuf. Recover the nodes up to the |
224 | * wbuf, not the one which we were starting to try to write. */ | 265 | * wbuf, not the one which we were starting to try to write. */ |
225 | 266 | ||
@@ -380,7 +421,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) | |||
380 | ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, | 421 | ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, |
381 | rewrite_buf); | 422 | rewrite_buf); |
382 | 423 | ||
383 | if (ret || retlen != towrite) { | 424 | if (ret || retlen != towrite || jffs2_verify_write(c, rewrite_buf, ofs)) { |
384 | /* Argh. We tried. Really we did. */ | 425 | /* Argh. We tried. Really we did. */ |
385 | printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n"); | 426 | printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n"); |
386 | kfree(buf); | 427 | kfree(buf); |
@@ -587,15 +628,16 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) | |||
587 | 628 | ||
588 | ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf); | 629 | ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf); |
589 | 630 | ||
590 | if (ret || retlen != c->wbuf_pagesize) { | 631 | if (ret) { |
591 | if (ret) | 632 | printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n", ret); |
592 | printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret); | 633 | goto wfail; |
593 | else { | 634 | } else if (retlen != c->wbuf_pagesize) { |
594 | printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n", | 635 | printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n", |
595 | retlen, c->wbuf_pagesize); | 636 | retlen, c->wbuf_pagesize); |
596 | ret = -EIO; | 637 | ret = -EIO; |
597 | } | 638 | goto wfail; |
598 | 639 | } else if ((ret = jffs2_verify_write(c, c->wbuf, c->wbuf_ofs))) { | |
640 | wfail: | ||
599 | jffs2_wbuf_recover(c); | 641 | jffs2_wbuf_recover(c); |
600 | 642 | ||
601 | return ret; | 643 | return ret; |
@@ -966,8 +1008,8 @@ exit: | |||
966 | 1008 | ||
967 | #define NR_OOB_SCAN_PAGES 4 | 1009 | #define NR_OOB_SCAN_PAGES 4 |
968 | 1010 | ||
969 | /* For historical reasons we use only 12 bytes for OOB clean marker */ | 1011 | /* For historical reasons we use only 8 bytes for OOB clean marker */ |
970 | #define OOB_CM_SIZE 12 | 1012 | #define OOB_CM_SIZE 8 |
971 | 1013 | ||
972 | static const struct jffs2_unknown_node oob_cleanmarker = | 1014 | static const struct jffs2_unknown_node oob_cleanmarker = |
973 | { | 1015 | { |
@@ -1021,8 +1063,8 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c, | |||
1021 | /* | 1063 | /* |
1022 | * Check for a valid cleanmarker. | 1064 | * Check for a valid cleanmarker. |
1023 | * Returns: 0 if a valid cleanmarker was found | 1065 | * Returns: 0 if a valid cleanmarker was found |
1024 | * 1 if no cleanmarker was found | 1066 | * 1 if no cleanmarker was found |
1025 | * negative error code if an error occurred | 1067 | * negative error code if an error occurred |
1026 | */ | 1068 | */ |
1027 | int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, | 1069 | int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, |
1028 | struct jffs2_eraseblock *jeb) | 1070 | struct jffs2_eraseblock *jeb) |
@@ -1138,11 +1180,22 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) | |||
1138 | return -ENOMEM; | 1180 | return -ENOMEM; |
1139 | } | 1181 | } |
1140 | 1182 | ||
1183 | #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY | ||
1184 | c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL); | ||
1185 | if (!c->wbuf_verify) { | ||
1186 | kfree(c->oobbuf); | ||
1187 | kfree(c->wbuf); | ||
1188 | return -ENOMEM; | ||
1189 | } | ||
1190 | #endif | ||
1141 | return 0; | 1191 | return 0; |
1142 | } | 1192 | } |
1143 | 1193 | ||
1144 | void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) | 1194 | void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) |
1145 | { | 1195 | { |
1196 | #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY | ||
1197 | kfree(c->wbuf_verify); | ||
1198 | #endif | ||
1146 | kfree(c->wbuf); | 1199 | kfree(c->wbuf); |
1147 | kfree(c->oobbuf); | 1200 | kfree(c->oobbuf); |
1148 | } | 1201 | } |
diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 664c164aa67c..2f5695446d0f 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c | |||
@@ -215,6 +215,17 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff | |||
215 | BUG(); | 215 | BUG(); |
216 | }); | 216 | }); |
217 | 217 | ||
218 | if (strnlen(name, namelen) != namelen) { | ||
219 | /* This should never happen, but seems to have done on at least one | ||
220 | occasion: https://dev.laptop.org/ticket/4184 */ | ||
221 | printk(KERN_CRIT "Error in jffs2_write_dirent() -- name contains zero bytes!\n"); | ||
222 | printk(KERN_CRIT "Directory inode #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x\n", | ||
223 | je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino), | ||
224 | je32_to_cpu(rd->name_crc)); | ||
225 | WARN_ON(1); | ||
226 | return ERR_PTR(-EIO); | ||
227 | } | ||
228 | |||
218 | vecs[0].iov_base = rd; | 229 | vecs[0].iov_base = rd; |
219 | vecs[0].iov_len = sizeof(*rd); | 230 | vecs[0].iov_len = sizeof(*rd); |
220 | vecs[1].iov_base = (unsigned char *)name; | 231 | vecs[1].iov_base = (unsigned char *)name; |
@@ -226,7 +237,7 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff | |||
226 | 237 | ||
227 | fd->version = je32_to_cpu(rd->version); | 238 | fd->version = je32_to_cpu(rd->version); |
228 | fd->ino = je32_to_cpu(rd->ino); | 239 | fd->ino = je32_to_cpu(rd->ino); |
229 | fd->nhash = full_name_hash(name, strlen(name)); | 240 | fd->nhash = full_name_hash(name, namelen); |
230 | fd->type = rd->type; | 241 | fd->type = rd->type; |
231 | memcpy(fd->name, name, namelen); | 242 | memcpy(fd->name, name, namelen); |
232 | fd->name[namelen]=0; | 243 | fd->name[namelen]=0; |
diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h index 3b0ff2925937..6e3b5ddfb7ab 100644 --- a/fs/jffs2/xattr.h +++ b/fs/jffs2/xattr.h | |||
@@ -75,7 +75,7 @@ extern void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c); | |||
75 | extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c); | 75 | extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c); |
76 | 76 | ||
77 | extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c, | 77 | extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c, |
78 | uint32_t xid, uint32_t version); | 78 | uint32_t xid, uint32_t version); |
79 | 79 | ||
80 | extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); | 80 | extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); |
81 | extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); | 81 | extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); |
diff --git a/fs/jffs2/xattr_user.c b/fs/jffs2/xattr_user.c index 40942bc516bb..8bbeab90ada1 100644 --- a/fs/jffs2/xattr_user.c +++ b/fs/jffs2/xattr_user.c | |||
@@ -17,7 +17,7 @@ | |||
17 | #include "nodelist.h" | 17 | #include "nodelist.h" |
18 | 18 | ||
19 | static int jffs2_user_getxattr(struct inode *inode, const char *name, | 19 | static int jffs2_user_getxattr(struct inode *inode, const char *name, |
20 | void *buffer, size_t size) | 20 | void *buffer, size_t size) |
21 | { | 21 | { |
22 | if (!strcmp(name, "")) | 22 | if (!strcmp(name, "")) |
23 | return -EINVAL; | 23 | return -EINVAL; |
@@ -25,7 +25,7 @@ static int jffs2_user_getxattr(struct inode *inode, const char *name, | |||
25 | } | 25 | } |
26 | 26 | ||
27 | static int jffs2_user_setxattr(struct inode *inode, const char *name, const void *buffer, | 27 | static int jffs2_user_setxattr(struct inode *inode, const char *name, const void *buffer, |
28 | size_t size, int flags) | 28 | size_t size, int flags) |
29 | { | 29 | { |
30 | if (!strcmp(name, "")) | 30 | if (!strcmp(name, "")) |
31 | return -EINVAL; | 31 | return -EINVAL; |
diff --git a/include/asm-blackfin/mach-bf548/dma.h b/include/asm-blackfin/mach-bf548/dma.h index fcc8b4c34c6a..14cb10cc24ae 100644 --- a/include/asm-blackfin/mach-bf548/dma.h +++ b/include/asm-blackfin/mach-bf548/dma.h | |||
@@ -55,6 +55,7 @@ | |||
55 | #define CH_SPORT3_RX 20 | 55 | #define CH_SPORT3_RX 20 |
56 | #define CH_SPORT3_TX 21 | 56 | #define CH_SPORT3_TX 21 |
57 | #define CH_SDH 22 | 57 | #define CH_SDH 22 |
58 | #define CH_NFC 22 | ||
58 | #define CH_SPI2 23 | 59 | #define CH_SPI2 23 |
59 | 60 | ||
60 | #define CH_MEM_STREAM0_DEST 24 | 61 | #define CH_MEM_STREAM0_DEST 24 |
diff --git a/include/asm-blackfin/nand.h b/include/asm-blackfin/nand.h new file mode 100644 index 000000000000..afbaafa793f1 --- /dev/null +++ b/include/asm-blackfin/nand.h | |||
@@ -0,0 +1,47 @@ | |||
1 | /* linux/include/asm-blackfin/nand.h | ||
2 | * | ||
3 | * Copyright (c) 2007 Analog Devices, Inc. | ||
4 | * Bryan Wu <bryan.wu@analog.com> | ||
5 | * | ||
6 | * BF5XX - NAND flash controller platfrom_device info | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | /* struct bf5xx_nand_platform | ||
14 | * | ||
15 | * define a interface between platfrom board specific code and | ||
16 | * bf54x NFC driver. | ||
17 | * | ||
18 | * nr_partitions = number of partitions pointed to be partitoons (or zero) | ||
19 | * partitions = mtd partition list | ||
20 | */ | ||
21 | |||
22 | #define NFC_PG_SIZE_256 0 | ||
23 | #define NFC_PG_SIZE_512 1 | ||
24 | #define NFC_PG_SIZE_OFFSET 9 | ||
25 | |||
26 | #define NFC_NWIDTH_8 0 | ||
27 | #define NFC_NWIDTH_16 1 | ||
28 | #define NFC_NWIDTH_OFFSET 8 | ||
29 | |||
30 | #define NFC_RDDLY_OFFSET 4 | ||
31 | #define NFC_WRDLY_OFFSET 0 | ||
32 | |||
33 | #define NFC_STAT_NBUSY 1 | ||
34 | |||
35 | struct bf5xx_nand_platform { | ||
36 | /* NAND chip information */ | ||
37 | unsigned short page_size; | ||
38 | unsigned short data_width; | ||
39 | |||
40 | /* RD/WR strobe delay timing information, all times in SCLK cycles */ | ||
41 | unsigned short rd_dly; | ||
42 | unsigned short wr_dly; | ||
43 | |||
44 | /* NAND MTD partition information */ | ||
45 | int nr_partitions; | ||
46 | struct mtd_partition *partitions; | ||
47 | }; | ||
diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index 840631fa5ff1..6b563cae23df 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h | |||
@@ -46,6 +46,7 @@ | |||
46 | #define JFFS2_COMPR_COPY 0x04 | 46 | #define JFFS2_COMPR_COPY 0x04 |
47 | #define JFFS2_COMPR_DYNRUBIN 0x05 | 47 | #define JFFS2_COMPR_DYNRUBIN 0x05 |
48 | #define JFFS2_COMPR_ZLIB 0x06 | 48 | #define JFFS2_COMPR_ZLIB 0x06 |
49 | #define JFFS2_COMPR_LZO 0x07 | ||
49 | /* Compatibility flags. */ | 50 | /* Compatibility flags. */ |
50 | #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ | 51 | #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */ |
51 | #define JFFS2_NODE_ACCURATE 0x2000 | 52 | #define JFFS2_NODE_ACCURATE 0x2000 |
diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index 123948b14547..e17c5343cf51 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h | |||
@@ -57,6 +57,15 @@ | |||
57 | #define cfi_interleave_is_8(cfi) (0) | 57 | #define cfi_interleave_is_8(cfi) (0) |
58 | #endif | 58 | #endif |
59 | 59 | ||
60 | #ifndef cfi_interleave | ||
61 | #warning No CONFIG_MTD_CFI_Ix selected. No NOR chip support can work. | ||
62 | static inline int cfi_interleave(void *cfi) | ||
63 | { | ||
64 | BUG(); | ||
65 | return 0; | ||
66 | } | ||
67 | #endif | ||
68 | |||
60 | static inline int cfi_interleave_supported(int i) | 69 | static inline int cfi_interleave_supported(int i) |
61 | { | 70 | { |
62 | switch (i) { | 71 | switch (i) { |
diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h index a293a3b78e05..39e7d2a1be9a 100644 --- a/include/linux/mtd/flashchip.h +++ b/include/linux/mtd/flashchip.h | |||
@@ -40,6 +40,7 @@ typedef enum { | |||
40 | FL_POINT, | 40 | FL_POINT, |
41 | FL_XIP_WHILE_ERASING, | 41 | FL_XIP_WHILE_ERASING, |
42 | FL_XIP_WHILE_WRITING, | 42 | FL_XIP_WHILE_WRITING, |
43 | FL_SHUTDOWN, | ||
43 | FL_UNKNOWN | 44 | FL_UNKNOWN |
44 | } flstate_t; | 45 | } flstate_t; |
45 | 46 | ||
diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index 81f3a314dd76..a9fae032ba81 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h | |||
@@ -125,7 +125,15 @@ | |||
125 | #endif | 125 | #endif |
126 | 126 | ||
127 | #ifndef map_bankwidth | 127 | #ifndef map_bankwidth |
128 | #error "No bus width supported. What's the point?" | 128 | #warning "No CONFIG_MTD_MAP_BANK_WIDTH_xx selected. No NOR chip support can work" |
129 | static inline int map_bankwidth(void *map) | ||
130 | { | ||
131 | BUG(); | ||
132 | return 0; | ||
133 | } | ||
134 | #define map_bankwidth_is_large(map) (0) | ||
135 | #define map_words(map) (0) | ||
136 | #define MAX_MAP_BANKWIDTH 1 | ||
129 | #endif | 137 | #endif |
130 | 138 | ||
131 | static inline int map_bankwidth_supported(int w) | 139 | static inline int map_bankwidth_supported(int w) |
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index fd64ccfbce02..783fc983417c 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h | |||
@@ -133,6 +133,13 @@ struct mtd_info { | |||
133 | int numeraseregions; | 133 | int numeraseregions; |
134 | struct mtd_erase_region_info *eraseregions; | 134 | struct mtd_erase_region_info *eraseregions; |
135 | 135 | ||
136 | /* | ||
137 | * Erase is an asynchronous operation. Device drivers are supposed | ||
138 | * to call instr->callback() whenever the operation completes, even | ||
139 | * if it completes with a failure. | ||
140 | * Callers are supposed to pass a callback function and wait for it | ||
141 | * to be called before writing to the block. | ||
142 | */ | ||
136 | int (*erase) (struct mtd_info *mtd, struct erase_info *instr); | 143 | int (*erase) (struct mtd_info *mtd, struct erase_info *instr); |
137 | 144 | ||
138 | /* This stuff for eXecute-In-Place */ | 145 | /* This stuff for eXecute-In-Place */ |
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index d2365c8dcacc..c42bc7f533a5 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h | |||
@@ -432,6 +432,7 @@ struct nand_chip { | |||
432 | #define NAND_MFR_STMICRO 0x20 | 432 | #define NAND_MFR_STMICRO 0x20 |
433 | #define NAND_MFR_HYNIX 0xad | 433 | #define NAND_MFR_HYNIX 0xad |
434 | #define NAND_MFR_MICRON 0x2c | 434 | #define NAND_MFR_MICRON 0x2c |
435 | #define NAND_MFR_AMD 0x01 | ||
435 | 436 | ||
436 | /** | 437 | /** |
437 | * struct nand_flash_dev - NAND Flash Device ID Structure | 438 | * struct nand_flash_dev - NAND Flash Device ID Structure |
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index a56d24ada505..fd0a260e070b 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h | |||
@@ -60,6 +60,7 @@ struct onenand_bufferram { | |||
60 | * @erase_shift: [INTERN] number of address bits in a block | 60 | * @erase_shift: [INTERN] number of address bits in a block |
61 | * @page_shift: [INTERN] number of address bits in a page | 61 | * @page_shift: [INTERN] number of address bits in a page |
62 | * @page_mask: [INTERN] a page per block mask | 62 | * @page_mask: [INTERN] a page per block mask |
63 | * @writesize: [INTERN] a real page size | ||
63 | * @bufferram_index: [INTERN] BufferRAM index | 64 | * @bufferram_index: [INTERN] BufferRAM index |
64 | * @bufferram: [INTERN] BufferRAM info | 65 | * @bufferram: [INTERN] BufferRAM info |
65 | * @readw: [REPLACEABLE] hardware specific function for read short | 66 | * @readw: [REPLACEABLE] hardware specific function for read short |
@@ -100,6 +101,7 @@ struct onenand_chip { | |||
100 | unsigned int erase_shift; | 101 | unsigned int erase_shift; |
101 | unsigned int page_shift; | 102 | unsigned int page_shift; |
102 | unsigned int page_mask; | 103 | unsigned int page_mask; |
104 | unsigned int writesize; | ||
103 | 105 | ||
104 | unsigned int bufferram_index; | 106 | unsigned int bufferram_index; |
105 | struct onenand_bufferram bufferram[MAX_BUFFERRAM]; | 107 | struct onenand_bufferram bufferram[MAX_BUFFERRAM]; |
@@ -140,6 +142,8 @@ struct onenand_chip { | |||
140 | #define ONENAND_NEXT_BUFFERRAM(this) (this->bufferram_index ^ 1) | 142 | #define ONENAND_NEXT_BUFFERRAM(this) (this->bufferram_index ^ 1) |
141 | #define ONENAND_SET_NEXT_BUFFERRAM(this) (this->bufferram_index ^= 1) | 143 | #define ONENAND_SET_NEXT_BUFFERRAM(this) (this->bufferram_index ^= 1) |
142 | #define ONENAND_SET_PREV_BUFFERRAM(this) (this->bufferram_index ^= 1) | 144 | #define ONENAND_SET_PREV_BUFFERRAM(this) (this->bufferram_index ^= 1) |
145 | #define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0) | ||
146 | #define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1) | ||
143 | 147 | ||
144 | #define ONENAND_GET_SYS_CFG1(this) \ | 148 | #define ONENAND_GET_SYS_CFG1(this) \ |
145 | (this->read_word(this->base + ONENAND_REG_SYS_CFG1)) | 149 | (this->read_word(this->base + ONENAND_REG_SYS_CFG1)) |
@@ -149,6 +153,13 @@ struct onenand_chip { | |||
149 | #define ONENAND_IS_DDP(this) \ | 153 | #define ONENAND_IS_DDP(this) \ |
150 | (this->device_id & ONENAND_DEVICE_IS_DDP) | 154 | (this->device_id & ONENAND_DEVICE_IS_DDP) |
151 | 155 | ||
156 | #ifdef CONFIG_MTD_ONENAND_2X_PROGRAM | ||
157 | #define ONENAND_IS_2PLANE(this) \ | ||
158 | (this->options & ONENAND_HAS_2PLANE) | ||
159 | #else | ||
160 | #define ONENAND_IS_2PLANE(this) (0) | ||
161 | #endif | ||
162 | |||
152 | /* Check byte access in OneNAND */ | 163 | /* Check byte access in OneNAND */ |
153 | #define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1) | 164 | #define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1) |
154 | 165 | ||
@@ -157,6 +168,7 @@ struct onenand_chip { | |||
157 | */ | 168 | */ |
158 | #define ONENAND_HAS_CONT_LOCK (0x0001) | 169 | #define ONENAND_HAS_CONT_LOCK (0x0001) |
159 | #define ONENAND_HAS_UNLOCK_ALL (0x0002) | 170 | #define ONENAND_HAS_UNLOCK_ALL (0x0002) |
171 | #define ONENAND_HAS_2PLANE (0x0004) | ||
160 | #define ONENAND_PAGEBUF_ALLOC (0x1000) | 172 | #define ONENAND_PAGEBUF_ALLOC (0x1000) |
161 | #define ONENAND_OOBBUF_ALLOC (0x2000) | 173 | #define ONENAND_OOBBUF_ALLOC (0x2000) |
162 | 174 | ||
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index af94719890e7..c46161f4eee3 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h | |||
@@ -74,6 +74,8 @@ | |||
74 | 74 | ||
75 | #define ONENAND_DEVICE_DENSITY_512Mb (0x002) | 75 | #define ONENAND_DEVICE_DENSITY_512Mb (0x002) |
76 | #define ONENAND_DEVICE_DENSITY_1Gb (0x003) | 76 | #define ONENAND_DEVICE_DENSITY_1Gb (0x003) |
77 | #define ONENAND_DEVICE_DENSITY_2Gb (0x004) | ||
78 | #define ONENAND_DEVICE_DENSITY_4Gb (0x005) | ||
77 | 79 | ||
78 | /* | 80 | /* |
79 | * Version ID Register F002h (R) | 81 | * Version ID Register F002h (R) |
@@ -111,6 +113,8 @@ | |||
111 | #define ONENAND_CMD_READOOB (0x13) | 113 | #define ONENAND_CMD_READOOB (0x13) |
112 | #define ONENAND_CMD_PROG (0x80) | 114 | #define ONENAND_CMD_PROG (0x80) |
113 | #define ONENAND_CMD_PROGOOB (0x1A) | 115 | #define ONENAND_CMD_PROGOOB (0x1A) |
116 | #define ONENAND_CMD_2X_PROG (0x7D) | ||
117 | #define ONENAND_CMD_2X_CACHE_PROG (0x7F) | ||
114 | #define ONENAND_CMD_UNLOCK (0x23) | 118 | #define ONENAND_CMD_UNLOCK (0x23) |
115 | #define ONENAND_CMD_LOCK (0x2A) | 119 | #define ONENAND_CMD_LOCK (0x2A) |
116 | #define ONENAND_CMD_LOCK_TIGHT (0x2C) | 120 | #define ONENAND_CMD_LOCK_TIGHT (0x2C) |