diff options
62 files changed, 1954 insertions, 553 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 451adcc52b3c..024d0e5e3e5d 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..39eff9ff575c 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 |
@@ -1780,7 +1780,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, | |||
1780 | return ret; | 1780 | return ret; |
1781 | } | 1781 | } |
1782 | 1782 | ||
1783 | int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) | 1783 | static int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) |
1784 | { | 1784 | { |
1785 | unsigned long ofs, len; | 1785 | unsigned long ofs, len; |
1786 | int ret; | 1786 | int ret; |
@@ -1930,7 +1930,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", | 1930 | printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", |
1931 | __FUNCTION__, ofs, len); | 1931 | __FUNCTION__, ofs, len); |
1932 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, | 1932 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, |
1933 | ofs, len, 0); | 1933 | ofs, len, NULL); |
1934 | #endif | 1934 | #endif |
1935 | 1935 | ||
1936 | ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, | 1936 | ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, |
@@ -1940,7 +1940,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", | 1940 | printk(KERN_DEBUG "%s: lock status after, ret=%d\n", |
1941 | __FUNCTION__, ret); | 1941 | __FUNCTION__, ret); |
1942 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, | 1942 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, |
1943 | ofs, len, 0); | 1943 | ofs, len, NULL); |
1944 | #endif | 1944 | #endif |
1945 | 1945 | ||
1946 | return ret; | 1946 | return ret; |
@@ -1954,7 +1954,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", | 1954 | printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", |
1955 | __FUNCTION__, ofs, len); | 1955 | __FUNCTION__, ofs, len); |
1956 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, | 1956 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, |
1957 | ofs, len, 0); | 1957 | ofs, len, NULL); |
1958 | #endif | 1958 | #endif |
1959 | 1959 | ||
1960 | ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, | 1960 | ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, |
@@ -1964,7 +1964,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", | 1964 | printk(KERN_DEBUG "%s: lock status after, ret=%d\n", |
1965 | __FUNCTION__, ret); | 1965 | __FUNCTION__, ret); |
1966 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, | 1966 | cfi_varsize_frob(mtd, do_printlockstatus_oneblock, |
1967 | ofs, len, 0); | 1967 | ofs, len, NULL); |
1968 | #endif | 1968 | #endif |
1969 | 1969 | ||
1970 | return ret; | 1970 | return ret; |
@@ -2255,7 +2255,7 @@ static void cfi_intelext_save_locks(struct mtd_info *mtd) | |||
2255 | adr = region->offset + block * len; | 2255 | adr = region->offset + block * len; |
2256 | 2256 | ||
2257 | status = cfi_varsize_frob(mtd, | 2257 | status = cfi_varsize_frob(mtd, |
2258 | do_getlockstatus_oneblock, adr, len, 0); | 2258 | do_getlockstatus_oneblock, adr, len, NULL); |
2259 | if (status) | 2259 | if (status) |
2260 | set_bit(block, region->lockmap); | 2260 | set_bit(block, region->lockmap); |
2261 | else | 2261 | else |
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..b4ea64dc9360 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig | |||
@@ -69,12 +69,21 @@ config MTD_DATAFLASH26 | |||
69 | If you have such a board and such a DataFlash, say 'Y'. | 69 | If you have such a board and such a DataFlash, say 'Y'. |
70 | 70 | ||
71 | config MTD_M25P80 | 71 | config MTD_M25P80 |
72 | tristate "Support for M25 SPI Flash" | 72 | tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)" |
73 | depends on SPI_MASTER && EXPERIMENTAL | 73 | depends on SPI_MASTER && EXPERIMENTAL |
74 | help | 74 | help |
75 | This enables access to ST M25P80 and similar SPI flash chips, | 75 | This enables access to most modern SPI flash chips, used for |
76 | used for program and data storage. Set up your spi devices | 76 | program and data storage. Series supported include Atmel AT26DF, |
77 | with the right board-specific platform data. | 77 | Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X. Other chips |
78 | are supported as well. See the driver source for the current list, | ||
79 | or to add other chips. | ||
80 | |||
81 | Note that the original DataFlash chips (AT45 series, not AT26DF), | ||
82 | need an entirely different driver. | ||
83 | |||
84 | Set up your spi devices with the right board-specific platform data, | ||
85 | if you want to specify device partitioning or to use a device which | ||
86 | doesn't support the JEDEC ID instruction. | ||
78 | 87 | ||
79 | config MTD_SLRAM | 88 | config MTD_SLRAM |
80 | tristate "Uncached system RAM" | 89 | tristate "Uncached system RAM" |
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/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/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/physmap_of.c b/drivers/mtd/maps/physmap_of.c index bbb42c35b69b..fbd613968717 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c | |||
@@ -141,7 +141,6 @@ static int __devinit of_physmap_probe(struct of_device *dev, const struct of_dev | |||
141 | err = -ENOMEM; | 141 | err = -ENOMEM; |
142 | goto err_out; | 142 | goto err_out; |
143 | } | 143 | } |
144 | memset(info, 0, sizeof(*info)); | ||
145 | 144 | ||
146 | dev_set_drvdata(&dev->dev, info); | 145 | dev_set_drvdata(&dev->dev, info); |
147 | 146 | ||
@@ -213,10 +212,6 @@ static int __devinit of_physmap_probe(struct of_device *dev, const struct of_dev | |||
213 | err_out: | 212 | err_out: |
214 | of_physmap_remove(dev); | 213 | of_physmap_remove(dev); |
215 | return err; | 214 | return err; |
216 | |||
217 | return 0; | ||
218 | |||
219 | |||
220 | } | 215 | } |
221 | 216 | ||
222 | static struct of_device_id of_physmap_match[] = { | 217 | static struct of_device_id of_physmap_match[] = { |
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/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 8c86b802f212..942c88ec5b6a 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c | |||
@@ -135,7 +135,8 @@ static int mtd_close(struct inode *inode, struct file *file) | |||
135 | 135 | ||
136 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); | 136 | DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); |
137 | 137 | ||
138 | if (mtd->sync) | 138 | /* Only sync if opened RW */ |
139 | if ((file->f_mode & 2) && mtd->sync) | ||
139 | mtd->sync(mtd); | 140 | mtd->sync(mtd); |
140 | 141 | ||
141 | put_mtd_device(mtd); | 142 | 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..62ee2043d046 --- /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 (%d 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 (%d 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..df25cabb0481 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
@@ -134,10 +134,10 @@ config MTD_NAND_S3C2410_HWECC | |||
134 | 134 | ||
135 | config MTD_NAND_NDFC | 135 | config MTD_NAND_NDFC |
136 | tristate "NDFC NanD Flash Controller" | 136 | tristate "NDFC NanD Flash Controller" |
137 | depends on 44x | 137 | depends on 4xx |
138 | select MTD_NAND_ECC_SMC | 138 | select MTD_NAND_ECC_SMC |
139 | help | 139 | help |
140 | NDFC Nand Flash Controllers are integrated in EP44x SoCs | 140 | NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs |
141 | 141 | ||
142 | config MTD_NAND_S3C2410_CLKSTOP | 142 | config MTD_NAND_S3C2410_CLKSTOP |
143 | bool "S3C2410 NAND IDLE clock stop" | 143 | bool "S3C2410 NAND IDLE clock stop" |
@@ -237,7 +237,7 @@ config MTD_NAND_CAFE | |||
237 | select REED_SOLOMON | 237 | select REED_SOLOMON |
238 | select REED_SOLOMON_DEC16 | 238 | select REED_SOLOMON_DEC16 |
239 | help | 239 | help |
240 | Use NAND flash attached to the CAFÉ chip designed for the $100 | 240 | Use NAND flash attached to the CAFÉ chip designed for the OLPC |
241 | laptop. | 241 | laptop. |
242 | 242 | ||
243 | config MTD_NAND_CS553X | 243 | config MTD_NAND_CS553X |
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..d5691212058d 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) |
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/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/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..7d194cfdb873 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 | } |
@@ -445,8 +460,9 @@ static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) | |||
445 | struct onenand_chip *this = mtd->priv; | 460 | struct onenand_chip *this = mtd->priv; |
446 | 461 | ||
447 | if (ONENAND_CURRENT_BUFFERRAM(this)) { | 462 | if (ONENAND_CURRENT_BUFFERRAM(this)) { |
463 | /* Note: the 'this->writesize' is a real page size */ | ||
448 | if (area == ONENAND_DATARAM) | 464 | if (area == ONENAND_DATARAM) |
449 | return mtd->writesize; | 465 | return this->writesize; |
450 | if (area == ONENAND_SPARERAM) | 466 | if (area == ONENAND_SPARERAM) |
451 | return mtd->oobsize; | 467 | return mtd->oobsize; |
452 | } | 468 | } |
@@ -572,6 +588,30 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area, | |||
572 | } | 588 | } |
573 | 589 | ||
574 | /** | 590 | /** |
591 | * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode | ||
592 | * @param mtd MTD data structure | ||
593 | * @param addr address to check | ||
594 | * @return blockpage address | ||
595 | * | ||
596 | * Get blockpage address at 2x program mode | ||
597 | */ | ||
598 | static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr) | ||
599 | { | ||
600 | struct onenand_chip *this = mtd->priv; | ||
601 | int blockpage, block, page; | ||
602 | |||
603 | /* Calculate the even block number */ | ||
604 | block = (int) (addr >> this->erase_shift) & ~1; | ||
605 | /* Is it the odd plane? */ | ||
606 | if (addr & this->writesize) | ||
607 | block++; | ||
608 | page = (int) (addr >> (this->page_shift + 1)) & this->page_mask; | ||
609 | blockpage = (block << 7) | page; | ||
610 | |||
611 | return blockpage; | ||
612 | } | ||
613 | |||
614 | /** | ||
575 | * onenand_check_bufferram - [GENERIC] Check BufferRAM information | 615 | * onenand_check_bufferram - [GENERIC] Check BufferRAM information |
576 | * @param mtd MTD data structure | 616 | * @param mtd MTD data structure |
577 | * @param addr address to check | 617 | * @param addr address to check |
@@ -585,7 +625,10 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) | |||
585 | int blockpage, found = 0; | 625 | int blockpage, found = 0; |
586 | unsigned int i; | 626 | unsigned int i; |
587 | 627 | ||
588 | blockpage = (int) (addr >> this->page_shift); | 628 | if (ONENAND_IS_2PLANE(this)) |
629 | blockpage = onenand_get_2x_blockpage(mtd, addr); | ||
630 | else | ||
631 | blockpage = (int) (addr >> this->page_shift); | ||
589 | 632 | ||
590 | /* Is there valid data? */ | 633 | /* Is there valid data? */ |
591 | i = ONENAND_CURRENT_BUFFERRAM(this); | 634 | i = ONENAND_CURRENT_BUFFERRAM(this); |
@@ -625,7 +668,10 @@ static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, | |||
625 | int blockpage; | 668 | int blockpage; |
626 | unsigned int i; | 669 | unsigned int i; |
627 | 670 | ||
628 | blockpage = (int) (addr >> this->page_shift); | 671 | if (ONENAND_IS_2PLANE(this)) |
672 | blockpage = onenand_get_2x_blockpage(mtd, addr); | ||
673 | else | ||
674 | blockpage = (int) (addr >> this->page_shift); | ||
629 | 675 | ||
630 | /* Invalidate another BufferRAM */ | 676 | /* Invalidate another BufferRAM */ |
631 | i = ONENAND_NEXT_BUFFERRAM(this); | 677 | i = ONENAND_NEXT_BUFFERRAM(this); |
@@ -734,6 +780,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
734 | int read = 0, column; | 780 | int read = 0, column; |
735 | int thislen; | 781 | int thislen; |
736 | int ret = 0, boundary = 0; | 782 | int ret = 0, boundary = 0; |
783 | int writesize = this->writesize; | ||
737 | 784 | ||
738 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); | 785 | DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); |
739 | 786 | ||
@@ -754,22 +801,22 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
754 | /* Do first load to bufferRAM */ | 801 | /* Do first load to bufferRAM */ |
755 | if (read < len) { | 802 | if (read < len) { |
756 | if (!onenand_check_bufferram(mtd, from)) { | 803 | if (!onenand_check_bufferram(mtd, from)) { |
757 | this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize); | 804 | this->command(mtd, ONENAND_CMD_READ, from, writesize); |
758 | ret = this->wait(mtd, FL_READING); | 805 | ret = this->wait(mtd, FL_READING); |
759 | onenand_update_bufferram(mtd, from, !ret); | 806 | onenand_update_bufferram(mtd, from, !ret); |
760 | } | 807 | } |
761 | } | 808 | } |
762 | 809 | ||
763 | thislen = min_t(int, mtd->writesize, len - read); | 810 | thislen = min_t(int, writesize, len - read); |
764 | column = from & (mtd->writesize - 1); | 811 | column = from & (writesize - 1); |
765 | if (column + thislen > mtd->writesize) | 812 | if (column + thislen > writesize) |
766 | thislen = mtd->writesize - column; | 813 | thislen = writesize - column; |
767 | 814 | ||
768 | while (!ret) { | 815 | while (!ret) { |
769 | /* If there is more to load then start next load */ | 816 | /* If there is more to load then start next load */ |
770 | from += thislen; | 817 | from += thislen; |
771 | if (read + thislen < len) { | 818 | if (read + thislen < len) { |
772 | this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize); | 819 | this->command(mtd, ONENAND_CMD_READ, from, writesize); |
773 | /* | 820 | /* |
774 | * Chip boundary handling in DDP | 821 | * Chip boundary handling in DDP |
775 | * Now we issued chip 1 read and pointed chip 1 | 822 | * Now we issued chip 1 read and pointed chip 1 |
@@ -794,7 +841,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); | 841 | this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); |
795 | ONENAND_SET_NEXT_BUFFERRAM(this); | 842 | ONENAND_SET_NEXT_BUFFERRAM(this); |
796 | buf += thislen; | 843 | buf += thislen; |
797 | thislen = min_t(int, mtd->writesize, len - read); | 844 | thislen = min_t(int, writesize, len - read); |
798 | column = 0; | 845 | column = 0; |
799 | cond_resched(); | 846 | cond_resched(); |
800 | /* Now wait for load */ | 847 | /* Now wait for load */ |
@@ -1079,7 +1126,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, | |||
1079 | /* Read more? */ | 1126 | /* Read more? */ |
1080 | if (read < len) { | 1127 | if (read < len) { |
1081 | /* Update Page size */ | 1128 | /* Update Page size */ |
1082 | from += mtd->writesize; | 1129 | from += this->writesize; |
1083 | column = 0; | 1130 | column = 0; |
1084 | } | 1131 | } |
1085 | } | 1132 | } |
@@ -1135,12 +1182,12 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, | |||
1135 | int thislen, column; | 1182 | int thislen, column; |
1136 | 1183 | ||
1137 | while (len != 0) { | 1184 | while (len != 0) { |
1138 | thislen = min_t(int, mtd->writesize, len); | 1185 | thislen = min_t(int, this->writesize, len); |
1139 | column = addr & (mtd->writesize - 1); | 1186 | column = addr & (this->writesize - 1); |
1140 | if (column + thislen > mtd->writesize) | 1187 | if (column + thislen > this->writesize) |
1141 | thislen = mtd->writesize - column; | 1188 | thislen = this->writesize - column; |
1142 | 1189 | ||
1143 | this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); | 1190 | this->command(mtd, ONENAND_CMD_READ, addr, this->writesize); |
1144 | 1191 | ||
1145 | onenand_update_bufferram(mtd, addr, 0); | 1192 | onenand_update_bufferram(mtd, addr, 0); |
1146 | 1193 | ||
@@ -1236,6 +1283,10 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, | |||
1236 | 1283 | ||
1237 | /* In partial page write we don't update bufferram */ | 1284 | /* In partial page write we don't update bufferram */ |
1238 | onenand_update_bufferram(mtd, to, !ret && !subpage); | 1285 | onenand_update_bufferram(mtd, to, !ret && !subpage); |
1286 | if (ONENAND_IS_2PLANE(this)) { | ||
1287 | ONENAND_SET_BUFFERRAM1(this); | ||
1288 | onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage); | ||
1289 | } | ||
1239 | 1290 | ||
1240 | if (ret) { | 1291 | if (ret) { |
1241 | printk(KERN_ERR "onenand_write: write filaed %d\n", ret); | 1292 | printk(KERN_ERR "onenand_write: write filaed %d\n", ret); |
@@ -1384,6 +1435,10 @@ 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); | 1435 | this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); |
1385 | 1436 | ||
1386 | onenand_update_bufferram(mtd, to, 0); | 1437 | onenand_update_bufferram(mtd, to, 0); |
1438 | if (ONENAND_IS_2PLANE(this)) { | ||
1439 | ONENAND_SET_BUFFERRAM1(this); | ||
1440 | onenand_update_bufferram(mtd, to + this->writesize, 0); | ||
1441 | } | ||
1387 | 1442 | ||
1388 | ret = this->wait(mtd, FL_WRITING); | 1443 | ret = this->wait(mtd, FL_WRITING); |
1389 | if (ret) { | 1444 | if (ret) { |
@@ -2107,6 +2162,7 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, | |||
2107 | * | 2162 | * |
2108 | * Check and set OneNAND features | 2163 | * Check and set OneNAND features |
2109 | * - lock scheme | 2164 | * - lock scheme |
2165 | * - two plane | ||
2110 | */ | 2166 | */ |
2111 | static void onenand_check_features(struct mtd_info *mtd) | 2167 | static void onenand_check_features(struct mtd_info *mtd) |
2112 | { | 2168 | { |
@@ -2118,19 +2174,35 @@ static void onenand_check_features(struct mtd_info *mtd) | |||
2118 | process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; | 2174 | process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; |
2119 | 2175 | ||
2120 | /* Lock scheme */ | 2176 | /* Lock scheme */ |
2121 | if (density >= ONENAND_DEVICE_DENSITY_1Gb) { | 2177 | switch (density) { |
2178 | case ONENAND_DEVICE_DENSITY_4Gb: | ||
2179 | this->options |= ONENAND_HAS_2PLANE; | ||
2180 | |||
2181 | case ONENAND_DEVICE_DENSITY_2Gb: | ||
2182 | /* 2Gb DDP don't have 2 plane */ | ||
2183 | if (!ONENAND_IS_DDP(this)) | ||
2184 | this->options |= ONENAND_HAS_2PLANE; | ||
2185 | this->options |= ONENAND_HAS_UNLOCK_ALL; | ||
2186 | |||
2187 | case ONENAND_DEVICE_DENSITY_1Gb: | ||
2122 | /* A-Die has all block unlock */ | 2188 | /* A-Die has all block unlock */ |
2123 | if (process) { | 2189 | if (process) |
2124 | printk(KERN_DEBUG "Chip support all block unlock\n"); | ||
2125 | this->options |= ONENAND_HAS_UNLOCK_ALL; | 2190 | this->options |= ONENAND_HAS_UNLOCK_ALL; |
2126 | } | 2191 | break; |
2127 | } else { | 2192 | |
2128 | /* Some OneNAND has continues lock scheme */ | 2193 | default: |
2129 | if (!process) { | 2194 | /* Some OneNAND has continuous lock scheme */ |
2130 | printk(KERN_DEBUG "Lock scheme is Continues Lock\n"); | 2195 | if (!process) |
2131 | this->options |= ONENAND_HAS_CONT_LOCK; | 2196 | this->options |= ONENAND_HAS_CONT_LOCK; |
2132 | } | 2197 | break; |
2133 | } | 2198 | } |
2199 | |||
2200 | if (this->options & ONENAND_HAS_CONT_LOCK) | ||
2201 | printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); | ||
2202 | if (this->options & ONENAND_HAS_UNLOCK_ALL) | ||
2203 | printk(KERN_DEBUG "Chip support all block unlock\n"); | ||
2204 | if (this->options & ONENAND_HAS_2PLANE) | ||
2205 | printk(KERN_DEBUG "Chip has 2 plane\n"); | ||
2134 | } | 2206 | } |
2135 | 2207 | ||
2136 | /** | 2208 | /** |
@@ -2257,6 +2329,8 @@ static int onenand_probe(struct mtd_info *mtd) | |||
2257 | this->erase_shift = ffs(mtd->erasesize) - 1; | 2329 | this->erase_shift = ffs(mtd->erasesize) - 1; |
2258 | this->page_shift = ffs(mtd->writesize) - 1; | 2330 | this->page_shift = ffs(mtd->writesize) - 1; |
2259 | this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; | 2331 | this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; |
2332 | /* It's real page size */ | ||
2333 | this->writesize = mtd->writesize; | ||
2260 | 2334 | ||
2261 | /* REVIST: Multichip handling */ | 2335 | /* REVIST: Multichip handling */ |
2262 | 2336 | ||
@@ -2265,6 +2339,17 @@ static int onenand_probe(struct mtd_info *mtd) | |||
2265 | /* Check OneNAND features */ | 2339 | /* Check OneNAND features */ |
2266 | onenand_check_features(mtd); | 2340 | onenand_check_features(mtd); |
2267 | 2341 | ||
2342 | /* | ||
2343 | * We emulate the 4KiB page and 256KiB erase block size | ||
2344 | * But oobsize is still 64 bytes. | ||
2345 | * It is only valid if you turn on 2X program support, | ||
2346 | * Otherwise it will be ignored by compiler. | ||
2347 | */ | ||
2348 | if (ONENAND_IS_2PLANE(this)) { | ||
2349 | mtd->writesize <<= 1; | ||
2350 | mtd->erasesize <<= 1; | ||
2351 | } | ||
2352 | |||
2268 | return 0; | 2353 | return 0; |
2269 | } | 2354 | } |
2270 | 2355 | ||
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 58a0650293e1..84fb8428c023 100644 --- a/fs/Kconfig +++ b/fs/Kconfig | |||
@@ -1228,6 +1228,14 @@ config JFFS2_FS_WRITEBUFFER | |||
1228 | - NOR flash with transparent ECC | 1228 | - NOR flash with transparent ECC |
1229 | - DataFlash | 1229 | - DataFlash |
1230 | 1230 | ||
1231 | config JFFS2_FS_WBUF_VERIFY | ||
1232 | bool "Verify JFFS2 write-buffer reads" | ||
1233 | depends on JFFS2_FS_WRITEBUFFER | ||
1234 | default n | ||
1235 | help | ||
1236 | This causes JFFS2 to read back every page written through the | ||
1237 | write-buffer, and check for errors. | ||
1238 | |||
1231 | config JFFS2_SUMMARY | 1239 | config JFFS2_SUMMARY |
1232 | bool "JFFS2 summary support (EXPERIMENTAL)" | 1240 | bool "JFFS2 summary support (EXPERIMENTAL)" |
1233 | depends on JFFS2_FS && EXPERIMENTAL | 1241 | depends on JFFS2_FS && EXPERIMENTAL |
@@ -1298,52 +1306,71 @@ config JFFS2_ZLIB | |||
1298 | select ZLIB_DEFLATE | 1306 | select ZLIB_DEFLATE |
1299 | depends on JFFS2_FS | 1307 | depends on JFFS2_FS |
1300 | default y | 1308 | default y |
1301 | help | 1309 | help |
1302 | Zlib is designed to be a free, general-purpose, legally unencumbered, | 1310 | Zlib is designed to be a free, general-purpose, legally unencumbered, |
1303 | lossless data-compression library for use on virtually any computer | 1311 | lossless data-compression library for use on virtually any computer |
1304 | hardware and operating system. See <http://www.gzip.org/zlib/> for | 1312 | hardware and operating system. See <http://www.gzip.org/zlib/> for |
1305 | further information. | 1313 | further information. |
1306 | 1314 | ||
1307 | Say 'Y' if unsure. | 1315 | Say 'Y' if unsure. |
1316 | |||
1317 | config JFFS2_LZO | ||
1318 | bool "JFFS2 LZO compression support" if JFFS2_COMPRESSION_OPTIONS | ||
1319 | select LZO_COMPRESS | ||
1320 | select LZO_DECOMPRESS | ||
1321 | depends on JFFS2_FS | ||
1322 | default n | ||
1323 | help | ||
1324 | minilzo-based compression. Generally works better than Zlib. | ||
1325 | |||
1326 | This feature was added in July, 2007. Say 'N' if you need | ||
1327 | compatibility with older bootloaders or kernels. | ||
1308 | 1328 | ||
1309 | config JFFS2_RTIME | 1329 | config JFFS2_RTIME |
1310 | bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS | 1330 | bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS |
1311 | depends on JFFS2_FS | 1331 | depends on JFFS2_FS |
1312 | default y | 1332 | default y |
1313 | help | 1333 | help |
1314 | Rtime does manage to recompress already-compressed data. Say 'Y' if unsure. | 1334 | Rtime does manage to recompress already-compressed data. Say 'Y' if unsure. |
1315 | 1335 | ||
1316 | config JFFS2_RUBIN | 1336 | config JFFS2_RUBIN |
1317 | bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS | 1337 | bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS |
1318 | depends on JFFS2_FS | 1338 | depends on JFFS2_FS |
1319 | default n | 1339 | default n |
1320 | help | 1340 | help |
1321 | RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure. | 1341 | RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure. |
1322 | 1342 | ||
1323 | choice | 1343 | choice |
1324 | prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS | 1344 | prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS |
1325 | default JFFS2_CMODE_PRIORITY | 1345 | default JFFS2_CMODE_PRIORITY |
1326 | depends on JFFS2_FS | 1346 | depends on JFFS2_FS |
1327 | help | 1347 | help |
1328 | You can set here the default compression mode of JFFS2 from | 1348 | You can set here the default compression mode of JFFS2 from |
1329 | the available compression modes. Don't touch if unsure. | 1349 | the available compression modes. Don't touch if unsure. |
1330 | 1350 | ||
1331 | config JFFS2_CMODE_NONE | 1351 | config JFFS2_CMODE_NONE |
1332 | bool "no compression" | 1352 | bool "no compression" |
1333 | help | 1353 | help |
1334 | Uses no compression. | 1354 | Uses no compression. |
1335 | 1355 | ||
1336 | config JFFS2_CMODE_PRIORITY | 1356 | config JFFS2_CMODE_PRIORITY |
1337 | bool "priority" | 1357 | bool "priority" |
1338 | help | 1358 | help |
1339 | Tries the compressors in a predefined order and chooses the first | 1359 | Tries the compressors in a predefined order and chooses the first |
1340 | successful one. | 1360 | successful one. |
1341 | 1361 | ||
1342 | config JFFS2_CMODE_SIZE | 1362 | config JFFS2_CMODE_SIZE |
1343 | bool "size (EXPERIMENTAL)" | 1363 | bool "size (EXPERIMENTAL)" |
1344 | help | 1364 | help |
1345 | Tries all compressors and chooses the one which has the smallest | 1365 | Tries all compressors and chooses the one which has the smallest |
1346 | result. | 1366 | result. |
1367 | |||
1368 | config JFFS2_CMODE_FAVOURLZO | ||
1369 | bool "Favour LZO" | ||
1370 | help | ||
1371 | Tries all compressors and chooses the one which has the smallest | ||
1372 | result but gives some preference to LZO (which has faster | ||
1373 | decompression) at the expense of size. | ||
1347 | 1374 | ||
1348 | endchoice | 1375 | endchoice |
1349 | 1376 | ||
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/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/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..d293a1fad6d6 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 | { |
@@ -770,7 +770,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de | |||
770 | } | 770 | } |
771 | 771 | ||
772 | static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, | 772 | static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, |
773 | struct inode *new_dir_i, struct dentry *new_dentry) | 773 | struct inode *new_dir_i, struct dentry *new_dentry) |
774 | { | 774 | { |
775 | int ret; | 775 | int ret; |
776 | struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); | 776 | 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..efd83f33a806 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, c->sector_size); | ||
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%08x\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); |
@@ -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/gc.c b/fs/jffs2/gc.c index 2d99e06ab223..eded819df235 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c | |||
@@ -556,7 +556,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, | |||
556 | 556 | ||
557 | node = kmalloc(rawlen, GFP_KERNEL); | 557 | node = kmalloc(rawlen, GFP_KERNEL); |
558 | if (!node) | 558 | if (!node) |
559 | return -ENOMEM; | 559 | return -ENOMEM; |
560 | 560 | ||
561 | ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node); | 561 | ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node); |
562 | if (!ret && retlen != rawlen) | 562 | if (!ret && retlen != rawlen) |
@@ -624,7 +624,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, | |||
624 | 624 | ||
625 | if (ret || (retlen != rawlen)) { | 625 | if (ret || (retlen != rawlen)) { |
626 | printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", | 626 | printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", |
627 | rawlen, phys_ofs, ret, retlen); | 627 | rawlen, phys_ofs, ret, retlen); |
628 | if (retlen) { | 628 | if (retlen) { |
629 | jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, rawlen, NULL); | 629 | jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, rawlen, NULL); |
630 | } else { | 630 | } else { |
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index b13298a824ed..ae99cd7fd43b 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h | |||
@@ -106,6 +106,9 @@ struct jffs2_sb_info { | |||
106 | 106 | ||
107 | uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ | 107 | uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ |
108 | 108 | ||
109 | #ifdef CONFIG_JFFS2_FS_WBUF_VERIFY | ||
110 | unsigned char *wbuf_verify; /* read-back buffer for verification */ | ||
111 | #endif | ||
109 | #ifdef CONFIG_JFFS2_FS_WRITEBUFFER | 112 | #ifdef CONFIG_JFFS2_FS_WRITEBUFFER |
110 | unsigned char *wbuf; /* Write-behind buffer for NAND flash */ | 113 | unsigned char *wbuf; /* Write-behind buffer for NAND flash */ |
111 | uint32_t wbuf_ofs; | 114 | 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..5b49bff364b4 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 |
diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index b5baa356fed2..8d4319c56b17 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c | |||
@@ -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..59dd408e5432 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c | |||
@@ -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))))) |
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..2a77d3f93029 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 | * |
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..ec99c8ec83ae 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; |
@@ -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/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/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/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) |