diff options
Diffstat (limited to 'drivers/mtd')
36 files changed, 1371 insertions, 269 deletions
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/at91_nand.c b/drivers/mtd/nand/at91_nand.c index 512e999177f7..b2a5672df6e0 100644 --- a/drivers/mtd/nand/at91_nand.c +++ b/drivers/mtd/nand/at91_nand.c | |||
@@ -128,7 +128,10 @@ static int __init at91_nand_probe(struct platform_device *pdev) | |||
128 | nand_chip->IO_ADDR_R = host->io_base; | 128 | nand_chip->IO_ADDR_R = host->io_base; |
129 | nand_chip->IO_ADDR_W = host->io_base; | 129 | nand_chip->IO_ADDR_W = host->io_base; |
130 | nand_chip->cmd_ctrl = at91_nand_cmd_ctrl; | 130 | nand_chip->cmd_ctrl = at91_nand_cmd_ctrl; |
131 | nand_chip->dev_ready = at91_nand_device_ready; | 131 | |
132 | if (host->board->rdy_pin) | ||
133 | nand_chip->dev_ready = at91_nand_device_ready; | ||
134 | |||
132 | nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ | 135 | nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ |
133 | nand_chip->chip_delay = 20; /* 20us command delay time */ | 136 | nand_chip->chip_delay = 20; /* 20us command delay time */ |
134 | 137 | ||
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/edb7312.c b/drivers/mtd/nand/edb7312.c index 1daf8231aaef..0146cdc48039 100644 --- a/drivers/mtd/nand/edb7312.c +++ b/drivers/mtd/nand/edb7312.c | |||
@@ -74,7 +74,7 @@ static struct mtd_partition partition_info[] = { | |||
74 | /* | 74 | /* |
75 | * hardware specific access to control-lines | 75 | * hardware specific access to control-lines |
76 | * | 76 | * |
77 | * NAND_NCE: bit 0 -> bit 7 | 77 | * NAND_NCE: bit 0 -> bit 6 (bit 7 = 1) |
78 | * NAND_CLE: bit 1 -> bit 4 | 78 | * NAND_CLE: bit 1 -> bit 4 |
79 | * NAND_ALE: bit 2 -> bit 5 | 79 | * NAND_ALE: bit 2 -> bit 5 |
80 | */ | 80 | */ |
@@ -83,12 +83,12 @@ static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) | |||
83 | struct nand_chip *chip = mtd->priv; | 83 | struct nand_chip *chip = mtd->priv; |
84 | 84 | ||
85 | if (ctrl & NAND_CTRL_CHANGE) { | 85 | if (ctrl & NAND_CTRL_CHANGE) { |
86 | unsigned char bits; | 86 | unsigned char bits = 0x80; |
87 | 87 | ||
88 | bits = (ctrl & (NAND_CLE | NAND_ALE)) << 3; | 88 | bits |= (ctrl & (NAND_CLE | NAND_ALE)) << 3; |
89 | bits = (ctrl & NAND_NCE) << 7; | 89 | bits |= (ctrl & NAND_NCE) ? 0x00 : 0x40; |
90 | 90 | ||
91 | clps_writeb((clps_readb(ep7312_pxdr) & 0xB0) | 0x10, | 91 | clps_writeb((clps_readb(ep7312_pxdr) & 0xF0) | bits, |
92 | ep7312_pxdr); | 92 | ep7312_pxdr); |
93 | } | 93 | } |
94 | if (cmd != NAND_CMD_NONE) | 94 | if (cmd != NAND_CMD_NONE) |
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 7e68203fe1ba..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) |
@@ -24,6 +24,7 @@ | |||
24 | * if we have HW ecc support. | 24 | * if we have HW ecc support. |
25 | * The AG-AND chips have nice features for speed improvement, | 25 | * The AG-AND chips have nice features for speed improvement, |
26 | * which are not supported yet. Read / program 4 pages in one go. | 26 | * which are not supported yet. Read / program 4 pages in one go. |
27 | * BBT table is not serialized, has to be fixed | ||
27 | * | 28 | * |
28 | * This program is free software; you can redistribute it and/or modify | 29 | * This program is free software; you can redistribute it and/or modify |
29 | * it under the terms of the GNU General Public License version 2 as | 30 | * it under the terms of the GNU General Public License version 2 as |
@@ -360,6 +361,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
360 | /* We write two bytes, so we dont have to mess with 16 bit | 361 | /* We write two bytes, so we dont have to mess with 16 bit |
361 | * access | 362 | * access |
362 | */ | 363 | */ |
364 | nand_get_device(chip, mtd, FL_WRITING); | ||
363 | ofs += mtd->oobsize; | 365 | ofs += mtd->oobsize; |
364 | chip->ops.len = chip->ops.ooblen = 2; | 366 | chip->ops.len = chip->ops.ooblen = 2; |
365 | chip->ops.datbuf = NULL; | 367 | chip->ops.datbuf = NULL; |
@@ -367,9 +369,11 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
367 | chip->ops.ooboffs = chip->badblockpos & ~0x01; | 369 | chip->ops.ooboffs = chip->badblockpos & ~0x01; |
368 | 370 | ||
369 | ret = nand_do_write_oob(mtd, ofs, &chip->ops); | 371 | ret = nand_do_write_oob(mtd, ofs, &chip->ops); |
372 | nand_release_device(mtd); | ||
370 | } | 373 | } |
371 | if (!ret) | 374 | if (!ret) |
372 | mtd->ecc_stats.badblocks++; | 375 | mtd->ecc_stats.badblocks++; |
376 | |||
373 | return ret; | 377 | return ret; |
374 | } | 378 | } |
375 | 379 | ||
@@ -768,7 +772,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, | |||
768 | uint8_t *p = buf; | 772 | uint8_t *p = buf; |
769 | uint8_t *ecc_calc = chip->buffers->ecccalc; | 773 | uint8_t *ecc_calc = chip->buffers->ecccalc; |
770 | uint8_t *ecc_code = chip->buffers->ecccode; | 774 | uint8_t *ecc_code = chip->buffers->ecccode; |
771 | int *eccpos = chip->ecc.layout->eccpos; | 775 | uint32_t *eccpos = chip->ecc.layout->eccpos; |
772 | 776 | ||
773 | chip->ecc.read_page_raw(mtd, chip, buf); | 777 | chip->ecc.read_page_raw(mtd, chip, buf); |
774 | 778 | ||
@@ -810,7 +814,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, | |||
810 | uint8_t *p = buf; | 814 | uint8_t *p = buf; |
811 | uint8_t *ecc_calc = chip->buffers->ecccalc; | 815 | uint8_t *ecc_calc = chip->buffers->ecccalc; |
812 | uint8_t *ecc_code = chip->buffers->ecccode; | 816 | uint8_t *ecc_code = chip->buffers->ecccode; |
813 | int *eccpos = chip->ecc.layout->eccpos; | 817 | uint32_t *eccpos = chip->ecc.layout->eccpos; |
814 | 818 | ||
815 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { | 819 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { |
816 | chip->ecc.hwctl(mtd, NAND_ECC_READ); | 820 | chip->ecc.hwctl(mtd, NAND_ECC_READ); |
@@ -1416,7 +1420,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, | |||
1416 | int eccsteps = chip->ecc.steps; | 1420 | int eccsteps = chip->ecc.steps; |
1417 | uint8_t *ecc_calc = chip->buffers->ecccalc; | 1421 | uint8_t *ecc_calc = chip->buffers->ecccalc; |
1418 | const uint8_t *p = buf; | 1422 | const uint8_t *p = buf; |
1419 | int *eccpos = chip->ecc.layout->eccpos; | 1423 | uint32_t *eccpos = chip->ecc.layout->eccpos; |
1420 | 1424 | ||
1421 | /* Software ecc calculation */ | 1425 | /* Software ecc calculation */ |
1422 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) | 1426 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) |
@@ -1442,7 +1446,7 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, | |||
1442 | int eccsteps = chip->ecc.steps; | 1446 | int eccsteps = chip->ecc.steps; |
1443 | uint8_t *ecc_calc = chip->buffers->ecccalc; | 1447 | uint8_t *ecc_calc = chip->buffers->ecccalc; |
1444 | const uint8_t *p = buf; | 1448 | const uint8_t *p = buf; |
1445 | int *eccpos = chip->ecc.layout->eccpos; | 1449 | uint32_t *eccpos = chip->ecc.layout->eccpos; |
1446 | 1450 | ||
1447 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { | 1451 | for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { |
1448 | chip->ecc.hwctl(mtd, NAND_ECC_WRITE); | 1452 | chip->ecc.hwctl(mtd, NAND_ECC_WRITE); |
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 d4b1ba8f23ef..823fba4e6d2f 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c | |||
@@ -779,9 +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 | return; | 782 | goto out; |
783 | } | 783 | } else |
784 | else | ||
785 | part->block_size = mtd->erasesize; | 784 | part->block_size = mtd->erasesize; |
786 | } | 785 | } |
787 | 786 | ||
@@ -803,7 +802,7 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) | |||
803 | if (!add_mtd_blktrans_dev((void*)part)) | 802 | if (!add_mtd_blktrans_dev((void*)part)) |
804 | return; | 803 | return; |
805 | } | 804 | } |
806 | 805 | out: | |
807 | kfree(part); | 806 | kfree(part); |
808 | } | 807 | } |
809 | 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 | } |