diff options
Diffstat (limited to 'drivers/mtd')
70 files changed, 3529 insertions, 1187 deletions
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 4521b1ecce45..82d1e4de475b 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile | |||
@@ -4,7 +4,7 @@ | |||
4 | 4 | ||
5 | # Core functionality. | 5 | # Core functionality. |
6 | obj-$(CONFIG_MTD) += mtd.o | 6 | obj-$(CONFIG_MTD) += mtd.o |
7 | mtd-y := mtdcore.o mtdsuper.o | 7 | mtd-y := mtdcore.o mtdsuper.o mtdbdi.o |
8 | mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o | 8 | mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o |
9 | 9 | ||
10 | obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o | 10 | obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o |
diff --git a/drivers/mtd/ar7part.c b/drivers/mtd/ar7part.c index ecf170b55c32..6697a1ec72d0 100644 --- a/drivers/mtd/ar7part.c +++ b/drivers/mtd/ar7part.c | |||
@@ -44,8 +44,6 @@ struct ar7_bin_rec { | |||
44 | unsigned int address; | 44 | unsigned int address; |
45 | }; | 45 | }; |
46 | 46 | ||
47 | static struct mtd_partition ar7_parts[AR7_PARTS]; | ||
48 | |||
49 | static int create_mtd_partitions(struct mtd_info *master, | 47 | static int create_mtd_partitions(struct mtd_info *master, |
50 | struct mtd_partition **pparts, | 48 | struct mtd_partition **pparts, |
51 | unsigned long origin) | 49 | unsigned long origin) |
@@ -57,7 +55,11 @@ static int create_mtd_partitions(struct mtd_info *master, | |||
57 | unsigned int root_offset = ROOT_OFFSET; | 55 | unsigned int root_offset = ROOT_OFFSET; |
58 | 56 | ||
59 | int retries = 10; | 57 | int retries = 10; |
58 | struct mtd_partition *ar7_parts; | ||
60 | 59 | ||
60 | ar7_parts = kzalloc(sizeof(*ar7_parts) * AR7_PARTS, GFP_KERNEL); | ||
61 | if (!ar7_parts) | ||
62 | return -ENOMEM; | ||
61 | ar7_parts[0].name = "loader"; | 63 | ar7_parts[0].name = "loader"; |
62 | ar7_parts[0].offset = 0; | 64 | ar7_parts[0].offset = 0; |
63 | ar7_parts[0].size = master->erasesize; | 65 | ar7_parts[0].size = master->erasesize; |
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index f5ab6fa1057b..c240454fd113 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c | |||
@@ -1236,10 +1236,14 @@ static int inval_cache_and_wait_for_operation( | |||
1236 | remove_wait_queue(&chip->wq, &wait); | 1236 | remove_wait_queue(&chip->wq, &wait); |
1237 | spin_lock(chip->mutex); | 1237 | spin_lock(chip->mutex); |
1238 | } | 1238 | } |
1239 | if (chip->erase_suspended || chip->write_suspended) { | 1239 | if (chip->erase_suspended && chip_state == FL_ERASING) { |
1240 | /* Suspend has occured while sleep: reset timeout */ | 1240 | /* Erase suspend occured while sleep: reset timeout */ |
1241 | timeo = reset_timeo; | 1241 | timeo = reset_timeo; |
1242 | chip->erase_suspended = 0; | 1242 | chip->erase_suspended = 0; |
1243 | } | ||
1244 | if (chip->write_suspended && chip_state == FL_WRITING) { | ||
1245 | /* Write suspend occured while sleep: reset timeout */ | ||
1246 | timeo = reset_timeo; | ||
1243 | chip->write_suspended = 0; | 1247 | chip->write_suspended = 0; |
1244 | } | 1248 | } |
1245 | } | 1249 | } |
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 94bb61e19047..61ea833e0908 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c | |||
@@ -282,6 +282,16 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param) | |||
282 | } | 282 | } |
283 | } | 283 | } |
284 | 284 | ||
285 | static void fixup_M29W128G_write_buffer(struct mtd_info *mtd, void *param) | ||
286 | { | ||
287 | struct map_info *map = mtd->priv; | ||
288 | struct cfi_private *cfi = map->fldrv_priv; | ||
289 | if (cfi->cfiq->BufWriteTimeoutTyp) { | ||
290 | pr_warning("Don't use write buffer on ST flash M29W128G\n"); | ||
291 | cfi->cfiq->BufWriteTimeoutTyp = 0; | ||
292 | } | ||
293 | } | ||
294 | |||
285 | static struct cfi_fixup cfi_fixup_table[] = { | 295 | static struct cfi_fixup cfi_fixup_table[] = { |
286 | { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, | 296 | { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, |
287 | #ifdef AMD_BOOTLOC_BUG | 297 | #ifdef AMD_BOOTLOC_BUG |
@@ -298,6 +308,7 @@ static struct cfi_fixup cfi_fixup_table[] = { | |||
298 | { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, }, | 308 | { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, }, |
299 | { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, }, | 309 | { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, }, |
300 | { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, }, | 310 | { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, }, |
311 | { CFI_MFR_ST, 0x227E, fixup_M29W128G_write_buffer, NULL, }, | ||
301 | #if !FORCE_WORD_WRITE | 312 | #if !FORCE_WORD_WRITE |
302 | { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, | 313 | { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, |
303 | #endif | 314 | #endif |
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 2f3f2f719ba4..e824b9b9b056 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c | |||
@@ -159,6 +159,7 @@ | |||
159 | #define SST39LF800 0x2781 | 159 | #define SST39LF800 0x2781 |
160 | #define SST39LF160 0x2782 | 160 | #define SST39LF160 0x2782 |
161 | #define SST39VF1601 0x234b | 161 | #define SST39VF1601 0x234b |
162 | #define SST39VF3201 0x235b | ||
162 | #define SST39LF512 0x00D4 | 163 | #define SST39LF512 0x00D4 |
163 | #define SST39LF010 0x00D5 | 164 | #define SST39LF010 0x00D5 |
164 | #define SST39LF020 0x00D6 | 165 | #define SST39LF020 0x00D6 |
@@ -1490,6 +1491,21 @@ static const struct amd_flash_info jedec_table[] = { | |||
1490 | ERASEINFO(0x1000,256) | 1491 | ERASEINFO(0x1000,256) |
1491 | } | 1492 | } |
1492 | }, { | 1493 | }, { |
1494 | .mfr_id = MANUFACTURER_SST, /* should be CFI */ | ||
1495 | .dev_id = SST39VF3201, | ||
1496 | .name = "SST 39VF3201", | ||
1497 | .devtypes = CFI_DEVICETYPE_X16, | ||
1498 | .uaddr = MTD_UADDR_0xAAAA_0x5555, | ||
1499 | .dev_size = SIZE_4MiB, | ||
1500 | .cmd_set = P_ID_AMD_STD, | ||
1501 | .nr_regions = 4, | ||
1502 | .regions = { | ||
1503 | ERASEINFO(0x1000,256), | ||
1504 | ERASEINFO(0x1000,256), | ||
1505 | ERASEINFO(0x1000,256), | ||
1506 | ERASEINFO(0x1000,256) | ||
1507 | } | ||
1508 | }, { | ||
1493 | .mfr_id = MANUFACTURER_SST, | 1509 | .mfr_id = MANUFACTURER_SST, |
1494 | .dev_id = SST36VF3203, | 1510 | .dev_id = SST36VF3203, |
1495 | .name = "SST 36VF3203", | 1511 | .name = "SST 36VF3203", |
diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index 072dd8abf33a..6bdc50c727e7 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c | |||
@@ -21,6 +21,8 @@ static int mapram_write (struct mtd_info *, loff_t, size_t, size_t *, const u_ch | |||
21 | static int mapram_erase (struct mtd_info *, struct erase_info *); | 21 | static int mapram_erase (struct mtd_info *, struct erase_info *); |
22 | static void mapram_nop (struct mtd_info *); | 22 | static void mapram_nop (struct mtd_info *); |
23 | static struct mtd_info *map_ram_probe(struct map_info *map); | 23 | static struct mtd_info *map_ram_probe(struct map_info *map); |
24 | static unsigned long mapram_unmapped_area(struct mtd_info *, unsigned long, | ||
25 | unsigned long, unsigned long); | ||
24 | 26 | ||
25 | 27 | ||
26 | static struct mtd_chip_driver mapram_chipdrv = { | 28 | static struct mtd_chip_driver mapram_chipdrv = { |
@@ -64,6 +66,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map) | |||
64 | mtd->type = MTD_RAM; | 66 | mtd->type = MTD_RAM; |
65 | mtd->size = map->size; | 67 | mtd->size = map->size; |
66 | mtd->erase = mapram_erase; | 68 | mtd->erase = mapram_erase; |
69 | mtd->get_unmapped_area = mapram_unmapped_area; | ||
67 | mtd->read = mapram_read; | 70 | mtd->read = mapram_read; |
68 | mtd->write = mapram_write; | 71 | mtd->write = mapram_write; |
69 | mtd->sync = mapram_nop; | 72 | mtd->sync = mapram_nop; |
@@ -79,6 +82,20 @@ static struct mtd_info *map_ram_probe(struct map_info *map) | |||
79 | } | 82 | } |
80 | 83 | ||
81 | 84 | ||
85 | /* | ||
86 | * Allow NOMMU mmap() to directly map the device (if not NULL) | ||
87 | * - return the address to which the offset maps | ||
88 | * - return -ENOSYS to indicate refusal to do the mapping | ||
89 | */ | ||
90 | static unsigned long mapram_unmapped_area(struct mtd_info *mtd, | ||
91 | unsigned long len, | ||
92 | unsigned long offset, | ||
93 | unsigned long flags) | ||
94 | { | ||
95 | struct map_info *map = mtd->priv; | ||
96 | return (unsigned long) map->virt + offset; | ||
97 | } | ||
98 | |||
82 | static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) | 99 | static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) |
83 | { | 100 | { |
84 | struct map_info *map = mtd->priv; | 101 | struct map_info *map = mtd->priv; |
diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index 821d0ed6bae3..076090a67b90 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c | |||
@@ -19,6 +19,9 @@ static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); | |||
19 | static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); | 19 | static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); |
20 | static void maprom_nop (struct mtd_info *); | 20 | static void maprom_nop (struct mtd_info *); |
21 | static struct mtd_info *map_rom_probe(struct map_info *map); | 21 | static struct mtd_info *map_rom_probe(struct map_info *map); |
22 | static int maprom_erase (struct mtd_info *mtd, struct erase_info *info); | ||
23 | static unsigned long maprom_unmapped_area(struct mtd_info *, unsigned long, | ||
24 | unsigned long, unsigned long); | ||
22 | 25 | ||
23 | static struct mtd_chip_driver maprom_chipdrv = { | 26 | static struct mtd_chip_driver maprom_chipdrv = { |
24 | .probe = map_rom_probe, | 27 | .probe = map_rom_probe, |
@@ -39,9 +42,11 @@ static struct mtd_info *map_rom_probe(struct map_info *map) | |||
39 | mtd->name = map->name; | 42 | mtd->name = map->name; |
40 | mtd->type = MTD_ROM; | 43 | mtd->type = MTD_ROM; |
41 | mtd->size = map->size; | 44 | mtd->size = map->size; |
45 | mtd->get_unmapped_area = maprom_unmapped_area; | ||
42 | mtd->read = maprom_read; | 46 | mtd->read = maprom_read; |
43 | mtd->write = maprom_write; | 47 | mtd->write = maprom_write; |
44 | mtd->sync = maprom_nop; | 48 | mtd->sync = maprom_nop; |
49 | mtd->erase = maprom_erase; | ||
45 | mtd->flags = MTD_CAP_ROM; | 50 | mtd->flags = MTD_CAP_ROM; |
46 | mtd->erasesize = map->size; | 51 | mtd->erasesize = map->size; |
47 | mtd->writesize = 1; | 52 | mtd->writesize = 1; |
@@ -51,6 +56,20 @@ static struct mtd_info *map_rom_probe(struct map_info *map) | |||
51 | } | 56 | } |
52 | 57 | ||
53 | 58 | ||
59 | /* | ||
60 | * Allow NOMMU mmap() to directly map the device (if not NULL) | ||
61 | * - return the address to which the offset maps | ||
62 | * - return -ENOSYS to indicate refusal to do the mapping | ||
63 | */ | ||
64 | static unsigned long maprom_unmapped_area(struct mtd_info *mtd, | ||
65 | unsigned long len, | ||
66 | unsigned long offset, | ||
67 | unsigned long flags) | ||
68 | { | ||
69 | struct map_info *map = mtd->priv; | ||
70 | return (unsigned long) map->virt + offset; | ||
71 | } | ||
72 | |||
54 | static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) | 73 | static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) |
55 | { | 74 | { |
56 | struct map_info *map = mtd->priv; | 75 | struct map_info *map = mtd->priv; |
@@ -71,6 +90,12 @@ static int maprom_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *re | |||
71 | return -EIO; | 90 | return -EIO; |
72 | } | 91 | } |
73 | 92 | ||
93 | static int maprom_erase (struct mtd_info *mtd, struct erase_info *info) | ||
94 | { | ||
95 | /* We do our best 8) */ | ||
96 | return -EROFS; | ||
97 | } | ||
98 | |||
74 | static int __init map_rom_init(void) | 99 | static int __init map_rom_init(void) |
75 | { | 100 | { |
76 | register_mtd_chip_driver(&maprom_chipdrv); | 101 | register_mtd_chip_driver(&maprom_chipdrv); |
diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index 50a340388e74..5011fa73f918 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c | |||
@@ -335,7 +335,11 @@ static int parse_cmdline_partitions(struct mtd_info *master, | |||
335 | } | 335 | } |
336 | offset += part->parts[i].size; | 336 | offset += part->parts[i].size; |
337 | } | 337 | } |
338 | *pparts = part->parts; | 338 | *pparts = kmemdup(part->parts, |
339 | sizeof(*part->parts) * part->num_parts, | ||
340 | GFP_KERNEL); | ||
341 | if (!*pparts) | ||
342 | return -ENOMEM; | ||
339 | return part->num_parts; | 343 | return part->num_parts; |
340 | } | 344 | } |
341 | } | 345 | } |
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index bc33200535fc..6fde0a2e3567 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig | |||
@@ -120,13 +120,6 @@ config MTD_PHRAM | |||
120 | doesn't have access to, memory beyond the mem=xxx limit, nvram, | 120 | doesn't have access to, memory beyond the mem=xxx limit, nvram, |
121 | memory on the video card, etc... | 121 | memory on the video card, etc... |
122 | 122 | ||
123 | config MTD_PS3VRAM | ||
124 | tristate "PS3 video RAM" | ||
125 | depends on FB_PS3 | ||
126 | help | ||
127 | This driver allows you to use excess PS3 video RAM as volatile | ||
128 | storage or system swap. | ||
129 | |||
130 | config MTD_LART | 123 | config MTD_LART |
131 | tristate "28F160xx flash driver for LART" | 124 | tristate "28F160xx flash driver for LART" |
132 | depends on SA1100_LART | 125 | depends on SA1100_LART |
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index e51521df4e40..0993d5cf3923 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile | |||
@@ -16,4 +16,3 @@ obj-$(CONFIG_MTD_LART) += lart.o | |||
16 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o | 16 | obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o |
17 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o | 17 | obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o |
18 | obj-$(CONFIG_MTD_M25P80) += m25p80.o | 18 | obj-$(CONFIG_MTD_M25P80) += m25p80.o |
19 | obj-$(CONFIG_MTD_PS3VRAM) += ps3vram.o | ||
diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index 50de839c77a9..5bf5f460e132 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c | |||
@@ -10,7 +10,6 @@ | |||
10 | #include <asm/errno.h> | 10 | #include <asm/errno.h> |
11 | #include <asm/io.h> | 11 | #include <asm/io.h> |
12 | #include <asm/uaccess.h> | 12 | #include <asm/uaccess.h> |
13 | #include <linux/miscdevice.h> | ||
14 | #include <linux/delay.h> | 13 | #include <linux/delay.h> |
15 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
16 | #include <linux/sched.h> | 15 | #include <linux/sched.h> |
diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index e32c568c1145..0990f7803628 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c | |||
@@ -10,7 +10,6 @@ | |||
10 | #include <asm/errno.h> | 10 | #include <asm/errno.h> |
11 | #include <asm/io.h> | 11 | #include <asm/io.h> |
12 | #include <asm/uaccess.h> | 12 | #include <asm/uaccess.h> |
13 | #include <linux/miscdevice.h> | ||
14 | #include <linux/delay.h> | 13 | #include <linux/delay.h> |
15 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
16 | #include <linux/init.h> | 15 | #include <linux/init.h> |
diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index d853f891b586..719b2915dc3a 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c | |||
@@ -14,7 +14,6 @@ | |||
14 | #include <asm/errno.h> | 14 | #include <asm/errno.h> |
15 | #include <asm/io.h> | 15 | #include <asm/io.h> |
16 | #include <asm/uaccess.h> | 16 | #include <asm/uaccess.h> |
17 | #include <linux/miscdevice.h> | ||
18 | #include <linux/delay.h> | 17 | #include <linux/delay.h> |
19 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
20 | #include <linux/init.h> | 19 | #include <linux/init.h> |
diff --git a/drivers/mtd/devices/docecc.c b/drivers/mtd/devices/docecc.c index 874e51b110a2..a19cda52da5c 100644 --- a/drivers/mtd/devices/docecc.c +++ b/drivers/mtd/devices/docecc.c | |||
@@ -26,7 +26,6 @@ | |||
26 | #include <asm/errno.h> | 26 | #include <asm/errno.h> |
27 | #include <asm/io.h> | 27 | #include <asm/io.h> |
28 | #include <asm/uaccess.h> | 28 | #include <asm/uaccess.h> |
29 | #include <linux/miscdevice.h> | ||
30 | #include <linux/delay.h> | 29 | #include <linux/delay.h> |
31 | #include <linux/slab.h> | 30 | #include <linux/slab.h> |
32 | #include <linux/init.h> | 31 | #include <linux/init.h> |
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 7c3fc766dcf1..8185b1f3e5e6 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c | |||
@@ -65,12 +65,6 @@ | |||
65 | #define FAST_READ_DUMMY_BYTE 0 | 65 | #define FAST_READ_DUMMY_BYTE 0 |
66 | #endif | 66 | #endif |
67 | 67 | ||
68 | #ifdef CONFIG_MTD_PARTITIONS | ||
69 | #define mtd_has_partitions() (1) | ||
70 | #else | ||
71 | #define mtd_has_partitions() (0) | ||
72 | #endif | ||
73 | |||
74 | /****************************************************************************/ | 68 | /****************************************************************************/ |
75 | 69 | ||
76 | struct m25p { | 70 | struct m25p { |
@@ -678,6 +672,8 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
678 | flash->mtd.erasesize = info->sector_size; | 672 | flash->mtd.erasesize = info->sector_size; |
679 | } | 673 | } |
680 | 674 | ||
675 | flash->mtd.dev.parent = &spi->dev; | ||
676 | |||
681 | dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name, | 677 | dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name, |
682 | (long long)flash->mtd.size >> 10); | 678 | (long long)flash->mtd.size >> 10); |
683 | 679 | ||
@@ -708,12 +704,13 @@ static int __devinit m25p_probe(struct spi_device *spi) | |||
708 | struct mtd_partition *parts = NULL; | 704 | struct mtd_partition *parts = NULL; |
709 | int nr_parts = 0; | 705 | int nr_parts = 0; |
710 | 706 | ||
711 | #ifdef CONFIG_MTD_CMDLINE_PARTS | 707 | if (mtd_has_cmdlinepart()) { |
712 | static const char *part_probes[] = { "cmdlinepart", NULL, }; | 708 | static const char *part_probes[] |
709 | = { "cmdlinepart", NULL, }; | ||
713 | 710 | ||
714 | nr_parts = parse_mtd_partitions(&flash->mtd, | 711 | nr_parts = parse_mtd_partitions(&flash->mtd, |
715 | part_probes, &parts, 0); | 712 | part_probes, &parts, 0); |
716 | #endif | 713 | } |
717 | 714 | ||
718 | if (nr_parts <= 0 && data && data->parts) { | 715 | if (nr_parts <= 0 && data && data->parts) { |
719 | parts = data->parts; | 716 | parts = data->parts; |
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index d44f741ae229..62dee54af0a5 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c | |||
@@ -98,12 +98,6 @@ struct dataflash { | |||
98 | struct mtd_info mtd; | 98 | struct mtd_info mtd; |
99 | }; | 99 | }; |
100 | 100 | ||
101 | #ifdef CONFIG_MTD_PARTITIONS | ||
102 | #define mtd_has_partitions() (1) | ||
103 | #else | ||
104 | #define mtd_has_partitions() (0) | ||
105 | #endif | ||
106 | |||
107 | /* ......................................................................... */ | 101 | /* ......................................................................... */ |
108 | 102 | ||
109 | /* | 103 | /* |
@@ -670,6 +664,8 @@ add_dataflash_otp(struct spi_device *spi, char *name, | |||
670 | device->write = dataflash_write; | 664 | device->write = dataflash_write; |
671 | device->priv = priv; | 665 | device->priv = priv; |
672 | 666 | ||
667 | device->dev.parent = &spi->dev; | ||
668 | |||
673 | if (revision >= 'c') | 669 | if (revision >= 'c') |
674 | otp_tag = otp_setup(device, revision); | 670 | otp_tag = otp_setup(device, revision); |
675 | 671 | ||
@@ -682,11 +678,13 @@ add_dataflash_otp(struct spi_device *spi, char *name, | |||
682 | struct mtd_partition *parts; | 678 | struct mtd_partition *parts; |
683 | int nr_parts = 0; | 679 | int nr_parts = 0; |
684 | 680 | ||
685 | #ifdef CONFIG_MTD_CMDLINE_PARTS | 681 | if (mtd_has_cmdlinepart()) { |
686 | static const char *part_probes[] = { "cmdlinepart", NULL, }; | 682 | static const char *part_probes[] |
683 | = { "cmdlinepart", NULL, }; | ||
687 | 684 | ||
688 | nr_parts = parse_mtd_partitions(device, part_probes, &parts, 0); | 685 | nr_parts = parse_mtd_partitions(device, |
689 | #endif | 686 | part_probes, &parts, 0); |
687 | } | ||
690 | 688 | ||
691 | if (nr_parts <= 0 && pdata && pdata->parts) { | 689 | if (nr_parts <= 0 && pdata && pdata->parts) { |
692 | parts = pdata->parts; | 690 | parts = pdata->parts; |
@@ -821,7 +819,8 @@ static struct flash_info *__devinit jedec_probe(struct spi_device *spi) | |||
821 | if (!(info->flags & IS_POW2PS)) | 819 | if (!(info->flags & IS_POW2PS)) |
822 | return info; | 820 | return info; |
823 | } | 821 | } |
824 | } | 822 | } else |
823 | return info; | ||
825 | } | 824 | } |
826 | } | 825 | } |
827 | 826 | ||
diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index 3aaca88847d3..fce5ff7589aa 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c | |||
@@ -65,6 +65,19 @@ static void ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len) | |||
65 | { | 65 | { |
66 | } | 66 | } |
67 | 67 | ||
68 | /* | ||
69 | * Allow NOMMU mmap() to directly map the device (if not NULL) | ||
70 | * - return the address to which the offset maps | ||
71 | * - return -ENOSYS to indicate refusal to do the mapping | ||
72 | */ | ||
73 | static unsigned long ram_get_unmapped_area(struct mtd_info *mtd, | ||
74 | unsigned long len, | ||
75 | unsigned long offset, | ||
76 | unsigned long flags) | ||
77 | { | ||
78 | return (unsigned long) mtd->priv + offset; | ||
79 | } | ||
80 | |||
68 | static int ram_read(struct mtd_info *mtd, loff_t from, size_t len, | 81 | static int ram_read(struct mtd_info *mtd, loff_t from, size_t len, |
69 | size_t *retlen, u_char *buf) | 82 | size_t *retlen, u_char *buf) |
70 | { | 83 | { |
@@ -116,6 +129,7 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, | |||
116 | mtd->erase = ram_erase; | 129 | mtd->erase = ram_erase; |
117 | mtd->point = ram_point; | 130 | mtd->point = ram_point; |
118 | mtd->unpoint = ram_unpoint; | 131 | mtd->unpoint = ram_unpoint; |
132 | mtd->get_unmapped_area = ram_get_unmapped_area; | ||
119 | mtd->read = ram_read; | 133 | mtd->read = ram_read; |
120 | mtd->write = ram_write; | 134 | mtd->write = ram_write; |
121 | 135 | ||
diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c deleted file mode 100644 index d21e9beb7ed2..000000000000 --- a/drivers/mtd/devices/ps3vram.c +++ /dev/null | |||
@@ -1,768 +0,0 @@ | |||
1 | /** | ||
2 | * ps3vram - Use extra PS3 video ram as MTD block device. | ||
3 | * | ||
4 | * Copyright (c) 2007-2008 Jim Paris <jim@jtan.com> | ||
5 | * Added support RSX DMA Vivien Chappelier <vivien.chappelier@free.fr> | ||
6 | */ | ||
7 | |||
8 | #include <linux/io.h> | ||
9 | #include <linux/mm.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/list.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/moduleparam.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/version.h> | ||
17 | #include <linux/gfp.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/mtd/mtd.h> | ||
20 | |||
21 | #include <asm/lv1call.h> | ||
22 | #include <asm/ps3.h> | ||
23 | |||
24 | #define DEVICE_NAME "ps3vram" | ||
25 | |||
26 | #define XDR_BUF_SIZE (2 * 1024 * 1024) /* XDR buffer (must be 1MiB aligned) */ | ||
27 | #define XDR_IOIF 0x0c000000 | ||
28 | |||
29 | #define FIFO_BASE XDR_IOIF | ||
30 | #define FIFO_SIZE (64 * 1024) | ||
31 | |||
32 | #define DMA_PAGE_SIZE (4 * 1024) | ||
33 | |||
34 | #define CACHE_PAGE_SIZE (256 * 1024) | ||
35 | #define CACHE_PAGE_COUNT ((XDR_BUF_SIZE - FIFO_SIZE) / CACHE_PAGE_SIZE) | ||
36 | |||
37 | #define CACHE_OFFSET CACHE_PAGE_SIZE | ||
38 | #define FIFO_OFFSET 0 | ||
39 | |||
40 | #define CTRL_PUT 0x10 | ||
41 | #define CTRL_GET 0x11 | ||
42 | #define CTRL_TOP 0x15 | ||
43 | |||
44 | #define UPLOAD_SUBCH 1 | ||
45 | #define DOWNLOAD_SUBCH 2 | ||
46 | |||
47 | #define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c | ||
48 | #define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104 | ||
49 | |||
50 | #define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601 | ||
51 | |||
52 | struct mtd_info ps3vram_mtd; | ||
53 | |||
54 | #define CACHE_PAGE_PRESENT 1 | ||
55 | #define CACHE_PAGE_DIRTY 2 | ||
56 | |||
57 | struct ps3vram_tag { | ||
58 | unsigned int address; | ||
59 | unsigned int flags; | ||
60 | }; | ||
61 | |||
62 | struct ps3vram_cache { | ||
63 | unsigned int page_count; | ||
64 | unsigned int page_size; | ||
65 | struct ps3vram_tag *tags; | ||
66 | }; | ||
67 | |||
68 | struct ps3vram_priv { | ||
69 | u64 memory_handle; | ||
70 | u64 context_handle; | ||
71 | u32 *ctrl; | ||
72 | u32 *reports; | ||
73 | u8 __iomem *ddr_base; | ||
74 | u8 *xdr_buf; | ||
75 | |||
76 | u32 *fifo_base; | ||
77 | u32 *fifo_ptr; | ||
78 | |||
79 | struct device *dev; | ||
80 | struct ps3vram_cache cache; | ||
81 | |||
82 | /* Used to serialize cache/DMA operations */ | ||
83 | struct mutex lock; | ||
84 | }; | ||
85 | |||
86 | #define DMA_NOTIFIER_HANDLE_BASE 0x66604200 /* first DMA notifier handle */ | ||
87 | #define DMA_NOTIFIER_OFFSET_BASE 0x1000 /* first DMA notifier offset */ | ||
88 | #define DMA_NOTIFIER_SIZE 0x40 | ||
89 | #define NOTIFIER 7 /* notifier used for completion report */ | ||
90 | |||
91 | /* A trailing '-' means to subtract off ps3fb_videomemory.size */ | ||
92 | char *size = "256M-"; | ||
93 | module_param(size, charp, 0); | ||
94 | MODULE_PARM_DESC(size, "memory size"); | ||
95 | |||
96 | static u32 *ps3vram_get_notifier(u32 *reports, int notifier) | ||
97 | { | ||
98 | return (void *) reports + | ||
99 | DMA_NOTIFIER_OFFSET_BASE + | ||
100 | DMA_NOTIFIER_SIZE * notifier; | ||
101 | } | ||
102 | |||
103 | static void ps3vram_notifier_reset(struct mtd_info *mtd) | ||
104 | { | ||
105 | int i; | ||
106 | |||
107 | struct ps3vram_priv *priv = mtd->priv; | ||
108 | u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); | ||
109 | for (i = 0; i < 4; i++) | ||
110 | notify[i] = 0xffffffff; | ||
111 | } | ||
112 | |||
113 | static int ps3vram_notifier_wait(struct mtd_info *mtd, unsigned int timeout_ms) | ||
114 | { | ||
115 | struct ps3vram_priv *priv = mtd->priv; | ||
116 | u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); | ||
117 | unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); | ||
118 | |||
119 | do { | ||
120 | if (!notify[3]) | ||
121 | return 0; | ||
122 | msleep(1); | ||
123 | } while (time_before(jiffies, timeout)); | ||
124 | |||
125 | return -ETIMEDOUT; | ||
126 | } | ||
127 | |||
128 | static void ps3vram_init_ring(struct mtd_info *mtd) | ||
129 | { | ||
130 | struct ps3vram_priv *priv = mtd->priv; | ||
131 | |||
132 | priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET; | ||
133 | priv->ctrl[CTRL_GET] = FIFO_BASE + FIFO_OFFSET; | ||
134 | } | ||
135 | |||
136 | static int ps3vram_wait_ring(struct mtd_info *mtd, unsigned int timeout_ms) | ||
137 | { | ||
138 | struct ps3vram_priv *priv = mtd->priv; | ||
139 | unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); | ||
140 | |||
141 | do { | ||
142 | if (priv->ctrl[CTRL_PUT] == priv->ctrl[CTRL_GET]) | ||
143 | return 0; | ||
144 | msleep(1); | ||
145 | } while (time_before(jiffies, timeout)); | ||
146 | |||
147 | dev_dbg(priv->dev, "%s:%d: FIFO timeout (%08x/%08x/%08x)\n", __func__, | ||
148 | __LINE__, priv->ctrl[CTRL_PUT], priv->ctrl[CTRL_GET], | ||
149 | priv->ctrl[CTRL_TOP]); | ||
150 | |||
151 | return -ETIMEDOUT; | ||
152 | } | ||
153 | |||
154 | static void ps3vram_out_ring(struct ps3vram_priv *priv, u32 data) | ||
155 | { | ||
156 | *(priv->fifo_ptr)++ = data; | ||
157 | } | ||
158 | |||
159 | static void ps3vram_begin_ring(struct ps3vram_priv *priv, u32 chan, | ||
160 | u32 tag, u32 size) | ||
161 | { | ||
162 | ps3vram_out_ring(priv, (size << 18) | (chan << 13) | tag); | ||
163 | } | ||
164 | |||
165 | static void ps3vram_rewind_ring(struct mtd_info *mtd) | ||
166 | { | ||
167 | struct ps3vram_priv *priv = mtd->priv; | ||
168 | u64 status; | ||
169 | |||
170 | ps3vram_out_ring(priv, 0x20000000 | (FIFO_BASE + FIFO_OFFSET)); | ||
171 | |||
172 | priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET; | ||
173 | |||
174 | /* asking the HV for a blit will kick the fifo */ | ||
175 | status = lv1_gpu_context_attribute(priv->context_handle, | ||
176 | L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, | ||
177 | 0, 0, 0, 0); | ||
178 | if (status) | ||
179 | dev_err(priv->dev, "%s:%d: lv1_gpu_context_attribute failed\n", | ||
180 | __func__, __LINE__); | ||
181 | |||
182 | priv->fifo_ptr = priv->fifo_base; | ||
183 | } | ||
184 | |||
185 | static void ps3vram_fire_ring(struct mtd_info *mtd) | ||
186 | { | ||
187 | struct ps3vram_priv *priv = mtd->priv; | ||
188 | u64 status; | ||
189 | |||
190 | mutex_lock(&ps3_gpu_mutex); | ||
191 | |||
192 | priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET + | ||
193 | (priv->fifo_ptr - priv->fifo_base) * sizeof(u32); | ||
194 | |||
195 | /* asking the HV for a blit will kick the fifo */ | ||
196 | status = lv1_gpu_context_attribute(priv->context_handle, | ||
197 | L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, | ||
198 | 0, 0, 0, 0); | ||
199 | if (status) | ||
200 | dev_err(priv->dev, "%s:%d: lv1_gpu_context_attribute failed\n", | ||
201 | __func__, __LINE__); | ||
202 | |||
203 | if ((priv->fifo_ptr - priv->fifo_base) * sizeof(u32) > | ||
204 | FIFO_SIZE - 1024) { | ||
205 | dev_dbg(priv->dev, "%s:%d: fifo full, rewinding\n", __func__, | ||
206 | __LINE__); | ||
207 | ps3vram_wait_ring(mtd, 200); | ||
208 | ps3vram_rewind_ring(mtd); | ||
209 | } | ||
210 | |||
211 | mutex_unlock(&ps3_gpu_mutex); | ||
212 | } | ||
213 | |||
214 | static void ps3vram_bind(struct mtd_info *mtd) | ||
215 | { | ||
216 | struct ps3vram_priv *priv = mtd->priv; | ||
217 | |||
218 | ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0, 1); | ||
219 | ps3vram_out_ring(priv, 0x31337303); | ||
220 | ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x180, 3); | ||
221 | ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER); | ||
222 | ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */ | ||
223 | ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */ | ||
224 | |||
225 | ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0, 1); | ||
226 | ps3vram_out_ring(priv, 0x3137c0de); | ||
227 | ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x180, 3); | ||
228 | ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER); | ||
229 | ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */ | ||
230 | ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */ | ||
231 | |||
232 | ps3vram_fire_ring(mtd); | ||
233 | } | ||
234 | |||
235 | static int ps3vram_upload(struct mtd_info *mtd, unsigned int src_offset, | ||
236 | unsigned int dst_offset, int len, int count) | ||
237 | { | ||
238 | struct ps3vram_priv *priv = mtd->priv; | ||
239 | |||
240 | ps3vram_begin_ring(priv, UPLOAD_SUBCH, | ||
241 | NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8); | ||
242 | ps3vram_out_ring(priv, XDR_IOIF + src_offset); | ||
243 | ps3vram_out_ring(priv, dst_offset); | ||
244 | ps3vram_out_ring(priv, len); | ||
245 | ps3vram_out_ring(priv, len); | ||
246 | ps3vram_out_ring(priv, len); | ||
247 | ps3vram_out_ring(priv, count); | ||
248 | ps3vram_out_ring(priv, (1 << 8) | 1); | ||
249 | ps3vram_out_ring(priv, 0); | ||
250 | |||
251 | ps3vram_notifier_reset(mtd); | ||
252 | ps3vram_begin_ring(priv, UPLOAD_SUBCH, | ||
253 | NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1); | ||
254 | ps3vram_out_ring(priv, 0); | ||
255 | ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x100, 1); | ||
256 | ps3vram_out_ring(priv, 0); | ||
257 | ps3vram_fire_ring(mtd); | ||
258 | if (ps3vram_notifier_wait(mtd, 200) < 0) { | ||
259 | dev_dbg(priv->dev, "%s:%d: notifier timeout\n", __func__, | ||
260 | __LINE__); | ||
261 | return -1; | ||
262 | } | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static int ps3vram_download(struct mtd_info *mtd, unsigned int src_offset, | ||
268 | unsigned int dst_offset, int len, int count) | ||
269 | { | ||
270 | struct ps3vram_priv *priv = mtd->priv; | ||
271 | |||
272 | ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, | ||
273 | NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8); | ||
274 | ps3vram_out_ring(priv, src_offset); | ||
275 | ps3vram_out_ring(priv, XDR_IOIF + dst_offset); | ||
276 | ps3vram_out_ring(priv, len); | ||
277 | ps3vram_out_ring(priv, len); | ||
278 | ps3vram_out_ring(priv, len); | ||
279 | ps3vram_out_ring(priv, count); | ||
280 | ps3vram_out_ring(priv, (1 << 8) | 1); | ||
281 | ps3vram_out_ring(priv, 0); | ||
282 | |||
283 | ps3vram_notifier_reset(mtd); | ||
284 | ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, | ||
285 | NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1); | ||
286 | ps3vram_out_ring(priv, 0); | ||
287 | ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x100, 1); | ||
288 | ps3vram_out_ring(priv, 0); | ||
289 | ps3vram_fire_ring(mtd); | ||
290 | if (ps3vram_notifier_wait(mtd, 200) < 0) { | ||
291 | dev_dbg(priv->dev, "%s:%d: notifier timeout\n", __func__, | ||
292 | __LINE__); | ||
293 | return -1; | ||
294 | } | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static void ps3vram_cache_evict(struct mtd_info *mtd, int entry) | ||
300 | { | ||
301 | struct ps3vram_priv *priv = mtd->priv; | ||
302 | struct ps3vram_cache *cache = &priv->cache; | ||
303 | |||
304 | if (cache->tags[entry].flags & CACHE_PAGE_DIRTY) { | ||
305 | dev_dbg(priv->dev, "%s:%d: flushing %d : 0x%08x\n", __func__, | ||
306 | __LINE__, entry, cache->tags[entry].address); | ||
307 | if (ps3vram_upload(mtd, | ||
308 | CACHE_OFFSET + entry * cache->page_size, | ||
309 | cache->tags[entry].address, | ||
310 | DMA_PAGE_SIZE, | ||
311 | cache->page_size / DMA_PAGE_SIZE) < 0) { | ||
312 | dev_dbg(priv->dev, "%s:%d: failed to upload from " | ||
313 | "0x%x to 0x%x size 0x%x\n", __func__, __LINE__, | ||
314 | entry * cache->page_size, | ||
315 | cache->tags[entry].address, cache->page_size); | ||
316 | } | ||
317 | cache->tags[entry].flags &= ~CACHE_PAGE_DIRTY; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | static void ps3vram_cache_load(struct mtd_info *mtd, int entry, | ||
322 | unsigned int address) | ||
323 | { | ||
324 | struct ps3vram_priv *priv = mtd->priv; | ||
325 | struct ps3vram_cache *cache = &priv->cache; | ||
326 | |||
327 | dev_dbg(priv->dev, "%s:%d: fetching %d : 0x%08x\n", __func__, __LINE__, | ||
328 | entry, address); | ||
329 | if (ps3vram_download(mtd, | ||
330 | address, | ||
331 | CACHE_OFFSET + entry * cache->page_size, | ||
332 | DMA_PAGE_SIZE, | ||
333 | cache->page_size / DMA_PAGE_SIZE) < 0) { | ||
334 | dev_err(priv->dev, "%s:%d: failed to download from " | ||
335 | "0x%x to 0x%x size 0x%x\n", __func__, __LINE__, address, | ||
336 | entry * cache->page_size, cache->page_size); | ||
337 | } | ||
338 | |||
339 | cache->tags[entry].address = address; | ||
340 | cache->tags[entry].flags |= CACHE_PAGE_PRESENT; | ||
341 | } | ||
342 | |||
343 | |||
344 | static void ps3vram_cache_flush(struct mtd_info *mtd) | ||
345 | { | ||
346 | struct ps3vram_priv *priv = mtd->priv; | ||
347 | struct ps3vram_cache *cache = &priv->cache; | ||
348 | int i; | ||
349 | |||
350 | dev_dbg(priv->dev, "%s:%d: FLUSH\n", __func__, __LINE__); | ||
351 | for (i = 0; i < cache->page_count; i++) { | ||
352 | ps3vram_cache_evict(mtd, i); | ||
353 | cache->tags[i].flags = 0; | ||
354 | } | ||
355 | } | ||
356 | |||
357 | static unsigned int ps3vram_cache_match(struct mtd_info *mtd, loff_t address) | ||
358 | { | ||
359 | struct ps3vram_priv *priv = mtd->priv; | ||
360 | struct ps3vram_cache *cache = &priv->cache; | ||
361 | unsigned int base; | ||
362 | unsigned int offset; | ||
363 | int i; | ||
364 | static int counter; | ||
365 | |||
366 | offset = (unsigned int) (address & (cache->page_size - 1)); | ||
367 | base = (unsigned int) (address - offset); | ||
368 | |||
369 | /* fully associative check */ | ||
370 | for (i = 0; i < cache->page_count; i++) { | ||
371 | if ((cache->tags[i].flags & CACHE_PAGE_PRESENT) && | ||
372 | cache->tags[i].address == base) { | ||
373 | dev_dbg(priv->dev, "%s:%d: found entry %d : 0x%08x\n", | ||
374 | __func__, __LINE__, i, cache->tags[i].address); | ||
375 | return i; | ||
376 | } | ||
377 | } | ||
378 | |||
379 | /* choose a random entry */ | ||
380 | i = (jiffies + (counter++)) % cache->page_count; | ||
381 | dev_dbg(priv->dev, "%s:%d: using entry %d\n", __func__, __LINE__, i); | ||
382 | |||
383 | ps3vram_cache_evict(mtd, i); | ||
384 | ps3vram_cache_load(mtd, i, base); | ||
385 | |||
386 | return i; | ||
387 | } | ||
388 | |||
389 | static int ps3vram_cache_init(struct mtd_info *mtd) | ||
390 | { | ||
391 | struct ps3vram_priv *priv = mtd->priv; | ||
392 | |||
393 | priv->cache.page_count = CACHE_PAGE_COUNT; | ||
394 | priv->cache.page_size = CACHE_PAGE_SIZE; | ||
395 | priv->cache.tags = kzalloc(sizeof(struct ps3vram_tag) * | ||
396 | CACHE_PAGE_COUNT, GFP_KERNEL); | ||
397 | if (priv->cache.tags == NULL) { | ||
398 | dev_err(priv->dev, "%s:%d: could not allocate cache tags\n", | ||
399 | __func__, __LINE__); | ||
400 | return -ENOMEM; | ||
401 | } | ||
402 | |||
403 | dev_info(priv->dev, "created ram cache: %d entries, %d KiB each\n", | ||
404 | CACHE_PAGE_COUNT, CACHE_PAGE_SIZE / 1024); | ||
405 | |||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | static void ps3vram_cache_cleanup(struct mtd_info *mtd) | ||
410 | { | ||
411 | struct ps3vram_priv *priv = mtd->priv; | ||
412 | |||
413 | ps3vram_cache_flush(mtd); | ||
414 | kfree(priv->cache.tags); | ||
415 | } | ||
416 | |||
417 | static int ps3vram_erase(struct mtd_info *mtd, struct erase_info *instr) | ||
418 | { | ||
419 | struct ps3vram_priv *priv = mtd->priv; | ||
420 | |||
421 | if (instr->addr + instr->len > mtd->size) | ||
422 | return -EINVAL; | ||
423 | |||
424 | mutex_lock(&priv->lock); | ||
425 | |||
426 | ps3vram_cache_flush(mtd); | ||
427 | |||
428 | /* Set bytes to 0xFF */ | ||
429 | memset_io(priv->ddr_base + instr->addr, 0xFF, instr->len); | ||
430 | |||
431 | mutex_unlock(&priv->lock); | ||
432 | |||
433 | instr->state = MTD_ERASE_DONE; | ||
434 | mtd_erase_callback(instr); | ||
435 | |||
436 | return 0; | ||
437 | } | ||
438 | |||
439 | static int ps3vram_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
440 | size_t *retlen, u_char *buf) | ||
441 | { | ||
442 | struct ps3vram_priv *priv = mtd->priv; | ||
443 | unsigned int cached, count; | ||
444 | |||
445 | dev_dbg(priv->dev, "%s:%d: from=0x%08x len=0x%zx\n", __func__, __LINE__, | ||
446 | (unsigned int)from, len); | ||
447 | |||
448 | if (from >= mtd->size) | ||
449 | return -EINVAL; | ||
450 | |||
451 | if (len > mtd->size - from) | ||
452 | len = mtd->size - from; | ||
453 | |||
454 | /* Copy from vram to buf */ | ||
455 | count = len; | ||
456 | while (count) { | ||
457 | unsigned int offset, avail; | ||
458 | unsigned int entry; | ||
459 | |||
460 | offset = (unsigned int) (from & (priv->cache.page_size - 1)); | ||
461 | avail = priv->cache.page_size - offset; | ||
462 | |||
463 | mutex_lock(&priv->lock); | ||
464 | |||
465 | entry = ps3vram_cache_match(mtd, from); | ||
466 | cached = CACHE_OFFSET + entry * priv->cache.page_size + offset; | ||
467 | |||
468 | dev_dbg(priv->dev, "%s:%d: from=%08x cached=%08x offset=%08x " | ||
469 | "avail=%08x count=%08x\n", __func__, __LINE__, | ||
470 | (unsigned int)from, cached, offset, avail, count); | ||
471 | |||
472 | if (avail > count) | ||
473 | avail = count; | ||
474 | memcpy(buf, priv->xdr_buf + cached, avail); | ||
475 | |||
476 | mutex_unlock(&priv->lock); | ||
477 | |||
478 | buf += avail; | ||
479 | count -= avail; | ||
480 | from += avail; | ||
481 | } | ||
482 | |||
483 | *retlen = len; | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | static int ps3vram_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
488 | size_t *retlen, const u_char *buf) | ||
489 | { | ||
490 | struct ps3vram_priv *priv = mtd->priv; | ||
491 | unsigned int cached, count; | ||
492 | |||
493 | if (to >= mtd->size) | ||
494 | return -EINVAL; | ||
495 | |||
496 | if (len > mtd->size - to) | ||
497 | len = mtd->size - to; | ||
498 | |||
499 | /* Copy from buf to vram */ | ||
500 | count = len; | ||
501 | while (count) { | ||
502 | unsigned int offset, avail; | ||
503 | unsigned int entry; | ||
504 | |||
505 | offset = (unsigned int) (to & (priv->cache.page_size - 1)); | ||
506 | avail = priv->cache.page_size - offset; | ||
507 | |||
508 | mutex_lock(&priv->lock); | ||
509 | |||
510 | entry = ps3vram_cache_match(mtd, to); | ||
511 | cached = CACHE_OFFSET + entry * priv->cache.page_size + offset; | ||
512 | |||
513 | dev_dbg(priv->dev, "%s:%d: to=%08x cached=%08x offset=%08x " | ||
514 | "avail=%08x count=%08x\n", __func__, __LINE__, | ||
515 | (unsigned int)to, cached, offset, avail, count); | ||
516 | |||
517 | if (avail > count) | ||
518 | avail = count; | ||
519 | memcpy(priv->xdr_buf + cached, buf, avail); | ||
520 | |||
521 | priv->cache.tags[entry].flags |= CACHE_PAGE_DIRTY; | ||
522 | |||
523 | mutex_unlock(&priv->lock); | ||
524 | |||
525 | buf += avail; | ||
526 | count -= avail; | ||
527 | to += avail; | ||
528 | } | ||
529 | |||
530 | *retlen = len; | ||
531 | return 0; | ||
532 | } | ||
533 | |||
534 | static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) | ||
535 | { | ||
536 | struct ps3vram_priv *priv; | ||
537 | int status; | ||
538 | u64 ddr_lpar; | ||
539 | u64 ctrl_lpar; | ||
540 | u64 info_lpar; | ||
541 | u64 reports_lpar; | ||
542 | u64 ddr_size; | ||
543 | u64 reports_size; | ||
544 | int ret = -ENOMEM; | ||
545 | char *rest; | ||
546 | |||
547 | ret = -EIO; | ||
548 | ps3vram_mtd.priv = kzalloc(sizeof(struct ps3vram_priv), GFP_KERNEL); | ||
549 | if (!ps3vram_mtd.priv) | ||
550 | goto out; | ||
551 | priv = ps3vram_mtd.priv; | ||
552 | |||
553 | mutex_init(&priv->lock); | ||
554 | priv->dev = &dev->core; | ||
555 | |||
556 | /* Allocate XDR buffer (1MiB aligned) */ | ||
557 | priv->xdr_buf = (void *)__get_free_pages(GFP_KERNEL, | ||
558 | get_order(XDR_BUF_SIZE)); | ||
559 | if (priv->xdr_buf == NULL) { | ||
560 | dev_dbg(&dev->core, "%s:%d: could not allocate XDR buffer\n", | ||
561 | __func__, __LINE__); | ||
562 | ret = -ENOMEM; | ||
563 | goto out_free_priv; | ||
564 | } | ||
565 | |||
566 | /* Put FIFO at begginning of XDR buffer */ | ||
567 | priv->fifo_base = (u32 *) (priv->xdr_buf + FIFO_OFFSET); | ||
568 | priv->fifo_ptr = priv->fifo_base; | ||
569 | |||
570 | /* XXX: Need to open GPU, in case ps3fb or snd_ps3 aren't loaded */ | ||
571 | if (ps3_open_hv_device(dev)) { | ||
572 | dev_err(&dev->core, "%s:%d: ps3_open_hv_device failed\n", | ||
573 | __func__, __LINE__); | ||
574 | ret = -EAGAIN; | ||
575 | goto out_close_gpu; | ||
576 | } | ||
577 | |||
578 | /* Request memory */ | ||
579 | status = -1; | ||
580 | ddr_size = memparse(size, &rest); | ||
581 | if (*rest == '-') | ||
582 | ddr_size -= ps3fb_videomemory.size; | ||
583 | ddr_size = ALIGN(ddr_size, 1024*1024); | ||
584 | if (ddr_size <= 0) { | ||
585 | dev_err(&dev->core, "%s:%d: specified size is too small\n", | ||
586 | __func__, __LINE__); | ||
587 | ret = -EINVAL; | ||
588 | goto out_close_gpu; | ||
589 | } | ||
590 | |||
591 | while (ddr_size > 0) { | ||
592 | status = lv1_gpu_memory_allocate(ddr_size, 0, 0, 0, 0, | ||
593 | &priv->memory_handle, | ||
594 | &ddr_lpar); | ||
595 | if (!status) | ||
596 | break; | ||
597 | ddr_size -= 1024*1024; | ||
598 | } | ||
599 | if (status || ddr_size <= 0) { | ||
600 | dev_err(&dev->core, "%s:%d: lv1_gpu_memory_allocate failed\n", | ||
601 | __func__, __LINE__); | ||
602 | ret = -ENOMEM; | ||
603 | goto out_free_xdr_buf; | ||
604 | } | ||
605 | |||
606 | /* Request context */ | ||
607 | status = lv1_gpu_context_allocate(priv->memory_handle, | ||
608 | 0, | ||
609 | &priv->context_handle, | ||
610 | &ctrl_lpar, | ||
611 | &info_lpar, | ||
612 | &reports_lpar, | ||
613 | &reports_size); | ||
614 | if (status) { | ||
615 | dev_err(&dev->core, "%s:%d: lv1_gpu_context_allocate failed\n", | ||
616 | __func__, __LINE__); | ||
617 | ret = -ENOMEM; | ||
618 | goto out_free_memory; | ||
619 | } | ||
620 | |||
621 | /* Map XDR buffer to RSX */ | ||
622 | status = lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF, | ||
623 | ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)), | ||
624 | XDR_BUF_SIZE, 0); | ||
625 | if (status) { | ||
626 | dev_err(&dev->core, "%s:%d: lv1_gpu_context_iomap failed\n", | ||
627 | __func__, __LINE__); | ||
628 | ret = -ENOMEM; | ||
629 | goto out_free_context; | ||
630 | } | ||
631 | |||
632 | priv->ddr_base = ioremap_flags(ddr_lpar, ddr_size, _PAGE_NO_CACHE); | ||
633 | |||
634 | if (!priv->ddr_base) { | ||
635 | dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__, | ||
636 | __LINE__); | ||
637 | ret = -ENOMEM; | ||
638 | goto out_free_context; | ||
639 | } | ||
640 | |||
641 | priv->ctrl = ioremap(ctrl_lpar, 64 * 1024); | ||
642 | if (!priv->ctrl) { | ||
643 | dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__, | ||
644 | __LINE__); | ||
645 | ret = -ENOMEM; | ||
646 | goto out_unmap_vram; | ||
647 | } | ||
648 | |||
649 | priv->reports = ioremap(reports_lpar, reports_size); | ||
650 | if (!priv->reports) { | ||
651 | dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__, | ||
652 | __LINE__); | ||
653 | ret = -ENOMEM; | ||
654 | goto out_unmap_ctrl; | ||
655 | } | ||
656 | |||
657 | mutex_lock(&ps3_gpu_mutex); | ||
658 | ps3vram_init_ring(&ps3vram_mtd); | ||
659 | mutex_unlock(&ps3_gpu_mutex); | ||
660 | |||
661 | ps3vram_mtd.name = "ps3vram"; | ||
662 | ps3vram_mtd.size = ddr_size; | ||
663 | ps3vram_mtd.flags = MTD_CAP_RAM; | ||
664 | ps3vram_mtd.erase = ps3vram_erase; | ||
665 | ps3vram_mtd.point = NULL; | ||
666 | ps3vram_mtd.unpoint = NULL; | ||
667 | ps3vram_mtd.read = ps3vram_read; | ||
668 | ps3vram_mtd.write = ps3vram_write; | ||
669 | ps3vram_mtd.owner = THIS_MODULE; | ||
670 | ps3vram_mtd.type = MTD_RAM; | ||
671 | ps3vram_mtd.erasesize = CACHE_PAGE_SIZE; | ||
672 | ps3vram_mtd.writesize = 1; | ||
673 | |||
674 | ps3vram_bind(&ps3vram_mtd); | ||
675 | |||
676 | mutex_lock(&ps3_gpu_mutex); | ||
677 | ret = ps3vram_wait_ring(&ps3vram_mtd, 100); | ||
678 | mutex_unlock(&ps3_gpu_mutex); | ||
679 | if (ret < 0) { | ||
680 | dev_err(&dev->core, "%s:%d: failed to initialize channels\n", | ||
681 | __func__, __LINE__); | ||
682 | ret = -ETIMEDOUT; | ||
683 | goto out_unmap_reports; | ||
684 | } | ||
685 | |||
686 | ps3vram_cache_init(&ps3vram_mtd); | ||
687 | |||
688 | if (add_mtd_device(&ps3vram_mtd)) { | ||
689 | dev_err(&dev->core, "%s:%d: add_mtd_device failed\n", | ||
690 | __func__, __LINE__); | ||
691 | ret = -EAGAIN; | ||
692 | goto out_cache_cleanup; | ||
693 | } | ||
694 | |||
695 | dev_info(&dev->core, "reserved %u MiB of gpu memory\n", | ||
696 | (unsigned int)(ddr_size / 1024 / 1024)); | ||
697 | |||
698 | return 0; | ||
699 | |||
700 | out_cache_cleanup: | ||
701 | ps3vram_cache_cleanup(&ps3vram_mtd); | ||
702 | out_unmap_reports: | ||
703 | iounmap(priv->reports); | ||
704 | out_unmap_ctrl: | ||
705 | iounmap(priv->ctrl); | ||
706 | out_unmap_vram: | ||
707 | iounmap(priv->ddr_base); | ||
708 | out_free_context: | ||
709 | lv1_gpu_context_free(priv->context_handle); | ||
710 | out_free_memory: | ||
711 | lv1_gpu_memory_free(priv->memory_handle); | ||
712 | out_close_gpu: | ||
713 | ps3_close_hv_device(dev); | ||
714 | out_free_xdr_buf: | ||
715 | free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE)); | ||
716 | out_free_priv: | ||
717 | kfree(ps3vram_mtd.priv); | ||
718 | ps3vram_mtd.priv = NULL; | ||
719 | out: | ||
720 | return ret; | ||
721 | } | ||
722 | |||
723 | static int ps3vram_shutdown(struct ps3_system_bus_device *dev) | ||
724 | { | ||
725 | struct ps3vram_priv *priv; | ||
726 | |||
727 | priv = ps3vram_mtd.priv; | ||
728 | |||
729 | del_mtd_device(&ps3vram_mtd); | ||
730 | ps3vram_cache_cleanup(&ps3vram_mtd); | ||
731 | iounmap(priv->reports); | ||
732 | iounmap(priv->ctrl); | ||
733 | iounmap(priv->ddr_base); | ||
734 | lv1_gpu_context_free(priv->context_handle); | ||
735 | lv1_gpu_memory_free(priv->memory_handle); | ||
736 | ps3_close_hv_device(dev); | ||
737 | free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE)); | ||
738 | kfree(priv); | ||
739 | return 0; | ||
740 | } | ||
741 | |||
742 | static struct ps3_system_bus_driver ps3vram_driver = { | ||
743 | .match_id = PS3_MATCH_ID_GPU, | ||
744 | .match_sub_id = PS3_MATCH_SUB_ID_GPU_RAMDISK, | ||
745 | .core.name = DEVICE_NAME, | ||
746 | .core.owner = THIS_MODULE, | ||
747 | .probe = ps3vram_probe, | ||
748 | .remove = ps3vram_shutdown, | ||
749 | .shutdown = ps3vram_shutdown, | ||
750 | }; | ||
751 | |||
752 | static int __init ps3vram_init(void) | ||
753 | { | ||
754 | return ps3_system_bus_driver_register(&ps3vram_driver); | ||
755 | } | ||
756 | |||
757 | static void __exit ps3vram_exit(void) | ||
758 | { | ||
759 | ps3_system_bus_driver_unregister(&ps3vram_driver); | ||
760 | } | ||
761 | |||
762 | module_init(ps3vram_init); | ||
763 | module_exit(ps3vram_exit); | ||
764 | |||
765 | MODULE_LICENSE("GPL"); | ||
766 | MODULE_AUTHOR("Jim Paris <jim@jtan.com>"); | ||
767 | MODULE_DESCRIPTION("MTD driver for PS3 video RAM"); | ||
768 | MODULE_ALIAS(PS3_MODULE_ALIAS_GPU_RAMDISK); | ||
diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index a425d09f35a0..00248e81ecd5 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c | |||
@@ -267,22 +267,28 @@ static int parse_cmdline(char *devname, char *szstart, char *szlength) | |||
267 | if (*(szlength) != '+') { | 267 | if (*(szlength) != '+') { |
268 | devlength = simple_strtoul(szlength, &buffer, 0); | 268 | devlength = simple_strtoul(szlength, &buffer, 0); |
269 | devlength = handle_unit(devlength, buffer) - devstart; | 269 | devlength = handle_unit(devlength, buffer) - devstart; |
270 | if (devlength < devstart) | ||
271 | goto err_out; | ||
272 | |||
273 | devlength -= devstart; | ||
270 | } else { | 274 | } else { |
271 | devlength = simple_strtoul(szlength + 1, &buffer, 0); | 275 | devlength = simple_strtoul(szlength + 1, &buffer, 0); |
272 | devlength = handle_unit(devlength, buffer); | 276 | devlength = handle_unit(devlength, buffer); |
273 | } | 277 | } |
274 | T("slram: devname=%s, devstart=0x%lx, devlength=0x%lx\n", | 278 | T("slram: devname=%s, devstart=0x%lx, devlength=0x%lx\n", |
275 | devname, devstart, devlength); | 279 | devname, devstart, devlength); |
276 | if ((devstart < 0) || (devlength < 0) || (devlength % SLRAM_BLK_SZ != 0)) { | 280 | if (devlength % SLRAM_BLK_SZ != 0) |
277 | E("slram: Illegal start / length parameter.\n"); | 281 | goto err_out; |
278 | return(-EINVAL); | ||
279 | } | ||
280 | 282 | ||
281 | if ((devstart = register_device(devname, devstart, devlength))){ | 283 | if ((devstart = register_device(devname, devstart, devlength))){ |
282 | unregister_devices(); | 284 | unregister_devices(); |
283 | return((int)devstart); | 285 | return((int)devstart); |
284 | } | 286 | } |
285 | return(0); | 287 | return(0); |
288 | |||
289 | err_out: | ||
290 | E("slram: Illegal length parameter.\n"); | ||
291 | return(-EINVAL); | ||
286 | } | 292 | } |
287 | 293 | ||
288 | #ifndef MODULE | 294 | #ifndef MODULE |
diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index f751dd97c549..32e82aef3e53 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c | |||
@@ -28,7 +28,6 @@ | |||
28 | #include <asm/errno.h> | 28 | #include <asm/errno.h> |
29 | #include <asm/io.h> | 29 | #include <asm/io.h> |
30 | #include <asm/uaccess.h> | 30 | #include <asm/uaccess.h> |
31 | #include <linux/miscdevice.h> | ||
32 | #include <linux/delay.h> | 31 | #include <linux/delay.h> |
33 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
34 | #include <linux/init.h> | 33 | #include <linux/init.h> |
diff --git a/drivers/mtd/internal.h b/drivers/mtd/internal.h new file mode 100644 index 000000000000..c658fe7216b5 --- /dev/null +++ b/drivers/mtd/internal.h | |||
@@ -0,0 +1,17 @@ | |||
1 | /* Internal MTD definitions | ||
2 | * | ||
3 | * Copyright © 2006 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | * mtdbdi.c | ||
14 | */ | ||
15 | extern struct backing_dev_info mtd_bdi_unmappable; | ||
16 | extern struct backing_dev_info mtd_bdi_ro_mappable; | ||
17 | extern struct backing_dev_info mtd_bdi_rw_mappable; | ||
diff --git a/drivers/mtd/lpddr/Kconfig b/drivers/mtd/lpddr/Kconfig index acd4ea9b2278..5a401d8047ab 100644 --- a/drivers/mtd/lpddr/Kconfig +++ b/drivers/mtd/lpddr/Kconfig | |||
@@ -12,6 +12,7 @@ config MTD_LPDDR | |||
12 | DDR memories, intended for battery-operated systems. | 12 | DDR memories, intended for battery-operated systems. |
13 | 13 | ||
14 | config MTD_QINFO_PROBE | 14 | config MTD_QINFO_PROBE |
15 | depends on MTD_LPDDR | ||
15 | tristate "Detect flash chips by QINFO probe" | 16 | tristate "Detect flash chips by QINFO probe" |
16 | help | 17 | help |
17 | Device Information for LPDDR chips is offered through the Overlay | 18 | Device Information for LPDDR chips is offered through the Overlay |
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 0225cbbf22de..82923bd2d9c5 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig | |||
@@ -491,7 +491,7 @@ config MTD_PCMCIA_ANONYMOUS | |||
491 | 491 | ||
492 | config MTD_BFIN_ASYNC | 492 | config MTD_BFIN_ASYNC |
493 | tristate "Blackfin BF533-STAMP Flash Chip Support" | 493 | tristate "Blackfin BF533-STAMP Flash Chip Support" |
494 | depends on BFIN533_STAMP && MTD_CFI | 494 | depends on BFIN533_STAMP && MTD_CFI && MTD_COMPLEX_MAPPINGS |
495 | select MTD_PARTITIONS | 495 | select MTD_PARTITIONS |
496 | default y | 496 | default y |
497 | help | 497 | help |
@@ -529,12 +529,6 @@ config MTD_DMV182 | |||
529 | help | 529 | help |
530 | Map driver for Dy-4 SVME/DMV-182 board. | 530 | Map driver for Dy-4 SVME/DMV-182 board. |
531 | 531 | ||
532 | config MTD_SHARP_SL | ||
533 | tristate "ROM mapped on Sharp SL Series" | ||
534 | depends on ARCH_PXA | ||
535 | help | ||
536 | This enables access to the flash chip on the Sharp SL Series of PDAs. | ||
537 | |||
538 | config MTD_INTEL_VR_NOR | 532 | config MTD_INTEL_VR_NOR |
539 | tristate "NOR flash on Intel Vermilion Range Expansion Bus CS0" | 533 | tristate "NOR flash on Intel Vermilion Range Expansion Bus CS0" |
540 | depends on PCI | 534 | depends on PCI |
@@ -542,6 +536,12 @@ config MTD_INTEL_VR_NOR | |||
542 | Map driver for a NOR flash bank located on the Expansion Bus of the | 536 | Map driver for a NOR flash bank located on the Expansion Bus of the |
543 | Intel Vermilion Range chipset. | 537 | Intel Vermilion Range chipset. |
544 | 538 | ||
539 | config MTD_RBTX4939 | ||
540 | tristate "Map driver for RBTX4939 board" | ||
541 | depends on TOSHIBA_RBTX4939 && MTD_CFI && MTD_COMPLEX_MAPPINGS | ||
542 | help | ||
543 | Map driver for NOR flash chips on RBTX4939 board. | ||
544 | |||
545 | config MTD_PLATRAM | 545 | config MTD_PLATRAM |
546 | tristate "Map driver for platform device RAM (mtd-ram)" | 546 | tristate "Map driver for platform device RAM (mtd-ram)" |
547 | select MTD_RAM | 547 | select MTD_RAM |
@@ -551,5 +551,15 @@ config MTD_PLATRAM | |||
551 | 551 | ||
552 | This selection automatically selects the map_ram driver. | 552 | This selection automatically selects the map_ram driver. |
553 | 553 | ||
554 | endmenu | 554 | config MTD_VMU |
555 | tristate "Map driver for Dreamcast VMU" | ||
556 | depends on MAPLE | ||
557 | help | ||
558 | This driver enables access to the Dreamcast Visual Memory Unit (VMU). | ||
559 | |||
560 | Most Dreamcast users will want to say Y here. | ||
555 | 561 | ||
562 | To build this as a module select M here, the module will be called | ||
563 | vmu-flash. | ||
564 | |||
565 | endmenu | ||
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 6d9ba35caf11..2dbc1bec8488 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile | |||
@@ -56,8 +56,9 @@ obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o | |||
56 | obj-$(CONFIG_MTD_IXP2000) += ixp2000.o | 56 | obj-$(CONFIG_MTD_IXP2000) += ixp2000.o |
57 | obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o | 57 | obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o |
58 | obj-$(CONFIG_MTD_DMV182) += dmv182.o | 58 | obj-$(CONFIG_MTD_DMV182) += dmv182.o |
59 | obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o | ||
60 | obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o | 59 | obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o |
61 | obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o | 60 | obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o |
62 | obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o | 61 | obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o |
63 | obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o | 62 | obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o |
63 | obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o | ||
64 | obj-$(CONFIG_MTD_VMU) += vmu-flash.o | ||
diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c index 6fec86aaed7e..576611f605db 100644 --- a/drivers/mtd/maps/bfin-async-flash.c +++ b/drivers/mtd/maps/bfin-async-flash.c | |||
@@ -152,14 +152,18 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev) | |||
152 | 152 | ||
153 | if (gpio_request(state->enet_flash_pin, DRIVER_NAME)) { | 153 | if (gpio_request(state->enet_flash_pin, DRIVER_NAME)) { |
154 | pr_devinit(KERN_ERR DRIVER_NAME ": Failed to request gpio %d\n", state->enet_flash_pin); | 154 | pr_devinit(KERN_ERR DRIVER_NAME ": Failed to request gpio %d\n", state->enet_flash_pin); |
155 | kfree(state); | ||
155 | return -EBUSY; | 156 | return -EBUSY; |
156 | } | 157 | } |
157 | gpio_direction_output(state->enet_flash_pin, 1); | 158 | gpio_direction_output(state->enet_flash_pin, 1); |
158 | 159 | ||
159 | pr_devinit(KERN_NOTICE DRIVER_NAME ": probing %d-bit flash bus\n", state->map.bankwidth * 8); | 160 | pr_devinit(KERN_NOTICE DRIVER_NAME ": probing %d-bit flash bus\n", state->map.bankwidth * 8); |
160 | state->mtd = do_map_probe(memory->name, &state->map); | 161 | state->mtd = do_map_probe(memory->name, &state->map); |
161 | if (!state->mtd) | 162 | if (!state->mtd) { |
163 | gpio_free(state->enet_flash_pin); | ||
164 | kfree(state); | ||
162 | return -ENXIO; | 165 | return -ENXIO; |
166 | } | ||
163 | 167 | ||
164 | #ifdef CONFIG_MTD_PARTITIONS | 168 | #ifdef CONFIG_MTD_PARTITIONS |
165 | ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0); | 169 | ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0); |
diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 5f7a245ed132..424f17d6ffd1 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c | |||
@@ -342,9 +342,9 @@ static struct pci_device_id ck804xrom_pci_tbl[] = { | |||
342 | { 0, } | 342 | { 0, } |
343 | }; | 343 | }; |
344 | 344 | ||
345 | #if 0 | ||
345 | MODULE_DEVICE_TABLE(pci, ck804xrom_pci_tbl); | 346 | MODULE_DEVICE_TABLE(pci, ck804xrom_pci_tbl); |
346 | 347 | ||
347 | #if 0 | ||
348 | static struct pci_driver ck804xrom_driver = { | 348 | static struct pci_driver ck804xrom_driver = { |
349 | .name = MOD_NAME, | 349 | .name = MOD_NAME, |
350 | .id_table = ck804xrom_pci_tbl, | 350 | .id_table = ck804xrom_pci_tbl, |
diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index d2ec262666c7..c9681a339a59 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/ioport.h> | 31 | #include <linux/ioport.h> |
32 | #include <linux/platform_device.h> | 32 | #include <linux/platform_device.h> |
33 | #include <linux/init.h> | 33 | #include <linux/init.h> |
34 | #include <linux/io.h> | ||
34 | 35 | ||
35 | #include <linux/mtd/mtd.h> | 36 | #include <linux/mtd/mtd.h> |
36 | #include <linux/mtd/map.h> | 37 | #include <linux/mtd/map.h> |
@@ -38,7 +39,6 @@ | |||
38 | 39 | ||
39 | #include <asm/mach/flash.h> | 40 | #include <asm/mach/flash.h> |
40 | #include <mach/hardware.h> | 41 | #include <mach/hardware.h> |
41 | #include <asm/io.h> | ||
42 | #include <asm/system.h> | 42 | #include <asm/system.h> |
43 | 43 | ||
44 | #ifdef CONFIG_ARCH_P720T | 44 | #ifdef CONFIG_ARCH_P720T |
diff --git a/drivers/mtd/maps/omap_nor.c b/drivers/mtd/maps/omap_nor.c index 7e50e9b1b781..a24478102b11 100644 --- a/drivers/mtd/maps/omap_nor.c +++ b/drivers/mtd/maps/omap_nor.c | |||
@@ -115,6 +115,8 @@ static int __init omapflash_probe(struct platform_device *pdev) | |||
115 | } | 115 | } |
116 | info->mtd->owner = THIS_MODULE; | 116 | info->mtd->owner = THIS_MODULE; |
117 | 117 | ||
118 | info->mtd->dev.parent = &pdev->dev; | ||
119 | |||
118 | #ifdef CONFIG_MTD_PARTITIONS | 120 | #ifdef CONFIG_MTD_PARTITIONS |
119 | err = parse_mtd_partitions(info->mtd, part_probes, &info->parts, 0); | 121 | err = parse_mtd_partitions(info->mtd, part_probes, &info->parts, 0); |
120 | if (err > 0) | 122 | if (err > 0) |
diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 87743661d48e..29a901157352 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c | |||
@@ -29,6 +29,7 @@ struct physmap_flash_info { | |||
29 | struct map_info map[MAX_RESOURCES]; | 29 | struct map_info map[MAX_RESOURCES]; |
30 | #ifdef CONFIG_MTD_PARTITIONS | 30 | #ifdef CONFIG_MTD_PARTITIONS |
31 | int nr_parts; | 31 | int nr_parts; |
32 | struct mtd_partition *parts; | ||
32 | #endif | 33 | #endif |
33 | }; | 34 | }; |
34 | 35 | ||
@@ -45,25 +46,29 @@ static int physmap_flash_remove(struct platform_device *dev) | |||
45 | 46 | ||
46 | physmap_data = dev->dev.platform_data; | 47 | physmap_data = dev->dev.platform_data; |
47 | 48 | ||
48 | #ifdef CONFIG_MTD_CONCAT | 49 | if (info->cmtd) { |
49 | if (info->cmtd != info->mtd[0]) { | 50 | #ifdef CONFIG_MTD_PARTITIONS |
51 | if (info->nr_parts || physmap_data->nr_parts) | ||
52 | del_mtd_partitions(info->cmtd); | ||
53 | else | ||
54 | del_mtd_device(info->cmtd); | ||
55 | #else | ||
50 | del_mtd_device(info->cmtd); | 56 | del_mtd_device(info->cmtd); |
51 | mtd_concat_destroy(info->cmtd); | 57 | #endif |
52 | } | 58 | } |
59 | #ifdef CONFIG_MTD_PARTITIONS | ||
60 | if (info->nr_parts) | ||
61 | kfree(info->parts); | ||
53 | #endif | 62 | #endif |
54 | 63 | ||
55 | for (i = 0; i < MAX_RESOURCES; i++) { | 64 | #ifdef CONFIG_MTD_CONCAT |
56 | if (info->mtd[i] != NULL) { | 65 | if (info->cmtd != info->mtd[0]) |
57 | #ifdef CONFIG_MTD_PARTITIONS | 66 | mtd_concat_destroy(info->cmtd); |
58 | if (info->nr_parts || physmap_data->nr_parts) | ||
59 | del_mtd_partitions(info->mtd[i]); | ||
60 | else | ||
61 | del_mtd_device(info->mtd[i]); | ||
62 | #else | ||
63 | del_mtd_device(info->mtd[i]); | ||
64 | #endif | 67 | #endif |
68 | |||
69 | for (i = 0; i < MAX_RESOURCES; i++) { | ||
70 | if (info->mtd[i] != NULL) | ||
65 | map_destroy(info->mtd[i]); | 71 | map_destroy(info->mtd[i]); |
66 | } | ||
67 | } | 72 | } |
68 | return 0; | 73 | return 0; |
69 | } | 74 | } |
@@ -86,9 +91,6 @@ static int physmap_flash_probe(struct platform_device *dev) | |||
86 | int err = 0; | 91 | int err = 0; |
87 | int i; | 92 | int i; |
88 | int devices_found = 0; | 93 | int devices_found = 0; |
89 | #ifdef CONFIG_MTD_PARTITIONS | ||
90 | struct mtd_partition *parts; | ||
91 | #endif | ||
92 | 94 | ||
93 | physmap_data = dev->dev.platform_data; | 95 | physmap_data = dev->dev.platform_data; |
94 | if (physmap_data == NULL) | 96 | if (physmap_data == NULL) |
@@ -145,6 +147,7 @@ static int physmap_flash_probe(struct platform_device *dev) | |||
145 | devices_found++; | 147 | devices_found++; |
146 | } | 148 | } |
147 | info->mtd[i]->owner = THIS_MODULE; | 149 | info->mtd[i]->owner = THIS_MODULE; |
150 | info->mtd[i]->dev.parent = &dev->dev; | ||
148 | } | 151 | } |
149 | 152 | ||
150 | if (devices_found == 1) { | 153 | if (devices_found == 1) { |
@@ -167,10 +170,11 @@ static int physmap_flash_probe(struct platform_device *dev) | |||
167 | goto err_out; | 170 | goto err_out; |
168 | 171 | ||
169 | #ifdef CONFIG_MTD_PARTITIONS | 172 | #ifdef CONFIG_MTD_PARTITIONS |
170 | err = parse_mtd_partitions(info->cmtd, part_probe_types, &parts, 0); | 173 | err = parse_mtd_partitions(info->cmtd, part_probe_types, |
174 | &info->parts, 0); | ||
171 | if (err > 0) { | 175 | if (err > 0) { |
172 | add_mtd_partitions(info->cmtd, parts, err); | 176 | add_mtd_partitions(info->cmtd, info->parts, err); |
173 | kfree(parts); | 177 | info->nr_parts = err; |
174 | return 0; | 178 | return 0; |
175 | } | 179 | } |
176 | 180 | ||
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index fbf0ca939d72..c83a60fada53 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c | |||
@@ -219,6 +219,7 @@ static int __devinit of_flash_probe(struct of_device *dev, | |||
219 | goto err_out; | 219 | goto err_out; |
220 | } | 220 | } |
221 | info->mtd->owner = THIS_MODULE; | 221 | info->mtd->owner = THIS_MODULE; |
222 | info->mtd->dev.parent = &dev->dev; | ||
222 | 223 | ||
223 | #ifdef CONFIG_MTD_PARTITIONS | 224 | #ifdef CONFIG_MTD_PARTITIONS |
224 | /* First look for RedBoot table or partitions on the command | 225 | /* First look for RedBoot table or partitions on the command |
diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index e7dd9c8a965e..49c9ece76477 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c | |||
@@ -224,6 +224,7 @@ static int platram_probe(struct platform_device *pdev) | |||
224 | } | 224 | } |
225 | 225 | ||
226 | info->mtd->owner = THIS_MODULE; | 226 | info->mtd->owner = THIS_MODULE; |
227 | info->mtd->dev.parent = &pdev->dev; | ||
227 | 228 | ||
228 | platram_setrw(info, PLATRAM_RW); | 229 | platram_setrw(info, PLATRAM_RW); |
229 | 230 | ||
diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index 771139c5bf87..572d32fdf38a 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c | |||
@@ -41,9 +41,8 @@ struct pxa2xx_flash_info { | |||
41 | static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; | 41 | static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; |
42 | 42 | ||
43 | 43 | ||
44 | static int __init pxa2xx_flash_probe(struct device *dev) | 44 | static int __init pxa2xx_flash_probe(struct platform_device *pdev) |
45 | { | 45 | { |
46 | struct platform_device *pdev = to_platform_device(dev); | ||
47 | struct flash_platform_data *flash = pdev->dev.platform_data; | 46 | struct flash_platform_data *flash = pdev->dev.platform_data; |
48 | struct pxa2xx_flash_info *info; | 47 | struct pxa2xx_flash_info *info; |
49 | struct mtd_partition *parts; | 48 | struct mtd_partition *parts; |
@@ -114,15 +113,15 @@ static int __init pxa2xx_flash_probe(struct device *dev) | |||
114 | add_mtd_device(info->mtd); | 113 | add_mtd_device(info->mtd); |
115 | } | 114 | } |
116 | 115 | ||
117 | dev_set_drvdata(dev, info); | 116 | platform_set_drvdata(pdev, info); |
118 | return 0; | 117 | return 0; |
119 | } | 118 | } |
120 | 119 | ||
121 | static int __exit pxa2xx_flash_remove(struct device *dev) | 120 | static int __devexit pxa2xx_flash_remove(struct platform_device *dev) |
122 | { | 121 | { |
123 | struct pxa2xx_flash_info *info = dev_get_drvdata(dev); | 122 | struct pxa2xx_flash_info *info = platform_get_drvdata(dev); |
124 | 123 | ||
125 | dev_set_drvdata(dev, NULL); | 124 | platform_set_drvdata(dev, NULL); |
126 | 125 | ||
127 | #ifdef CONFIG_MTD_PARTITIONS | 126 | #ifdef CONFIG_MTD_PARTITIONS |
128 | if (info->nr_parts) | 127 | if (info->nr_parts) |
@@ -141,9 +140,9 @@ static int __exit pxa2xx_flash_remove(struct device *dev) | |||
141 | } | 140 | } |
142 | 141 | ||
143 | #ifdef CONFIG_PM | 142 | #ifdef CONFIG_PM |
144 | static int pxa2xx_flash_suspend(struct device *dev, pm_message_t state) | 143 | static int pxa2xx_flash_suspend(struct platform_device *dev, pm_message_t state) |
145 | { | 144 | { |
146 | struct pxa2xx_flash_info *info = dev_get_drvdata(dev); | 145 | struct pxa2xx_flash_info *info = platform_get_drvdata(dev); |
147 | int ret = 0; | 146 | int ret = 0; |
148 | 147 | ||
149 | if (info->mtd && info->mtd->suspend) | 148 | if (info->mtd && info->mtd->suspend) |
@@ -151,17 +150,17 @@ static int pxa2xx_flash_suspend(struct device *dev, pm_message_t state) | |||
151 | return ret; | 150 | return ret; |
152 | } | 151 | } |
153 | 152 | ||
154 | static int pxa2xx_flash_resume(struct device *dev) | 153 | static int pxa2xx_flash_resume(struct platform_device *dev) |
155 | { | 154 | { |
156 | struct pxa2xx_flash_info *info = dev_get_drvdata(dev); | 155 | struct pxa2xx_flash_info *info = platform_get_drvdata(dev); |
157 | 156 | ||
158 | if (info->mtd && info->mtd->resume) | 157 | if (info->mtd && info->mtd->resume) |
159 | info->mtd->resume(info->mtd); | 158 | info->mtd->resume(info->mtd); |
160 | return 0; | 159 | return 0; |
161 | } | 160 | } |
162 | static void pxa2xx_flash_shutdown(struct device *dev) | 161 | static void pxa2xx_flash_shutdown(struct platform_device *dev) |
163 | { | 162 | { |
164 | struct pxa2xx_flash_info *info = dev_get_drvdata(dev); | 163 | struct pxa2xx_flash_info *info = platform_get_drvdata(dev); |
165 | 164 | ||
166 | if (info && info->mtd->suspend(info->mtd) == 0) | 165 | if (info && info->mtd->suspend(info->mtd) == 0) |
167 | info->mtd->resume(info->mtd); | 166 | info->mtd->resume(info->mtd); |
@@ -172,11 +171,13 @@ static void pxa2xx_flash_shutdown(struct device *dev) | |||
172 | #define pxa2xx_flash_shutdown NULL | 171 | #define pxa2xx_flash_shutdown NULL |
173 | #endif | 172 | #endif |
174 | 173 | ||
175 | static struct device_driver pxa2xx_flash_driver = { | 174 | static struct platform_driver pxa2xx_flash_driver = { |
176 | .name = "pxa2xx-flash", | 175 | .driver = { |
177 | .bus = &platform_bus_type, | 176 | .name = "pxa2xx-flash", |
177 | .owner = THIS_MODULE, | ||
178 | }, | ||
178 | .probe = pxa2xx_flash_probe, | 179 | .probe = pxa2xx_flash_probe, |
179 | .remove = __exit_p(pxa2xx_flash_remove), | 180 | .remove = __devexit_p(pxa2xx_flash_remove), |
180 | .suspend = pxa2xx_flash_suspend, | 181 | .suspend = pxa2xx_flash_suspend, |
181 | .resume = pxa2xx_flash_resume, | 182 | .resume = pxa2xx_flash_resume, |
182 | .shutdown = pxa2xx_flash_shutdown, | 183 | .shutdown = pxa2xx_flash_shutdown, |
@@ -184,12 +185,12 @@ static struct device_driver pxa2xx_flash_driver = { | |||
184 | 185 | ||
185 | static int __init init_pxa2xx_flash(void) | 186 | static int __init init_pxa2xx_flash(void) |
186 | { | 187 | { |
187 | return driver_register(&pxa2xx_flash_driver); | 188 | return platform_driver_register(&pxa2xx_flash_driver); |
188 | } | 189 | } |
189 | 190 | ||
190 | static void __exit cleanup_pxa2xx_flash(void) | 191 | static void __exit cleanup_pxa2xx_flash(void) |
191 | { | 192 | { |
192 | driver_unregister(&pxa2xx_flash_driver); | 193 | platform_driver_unregister(&pxa2xx_flash_driver); |
193 | } | 194 | } |
194 | 195 | ||
195 | module_init(init_pxa2xx_flash); | 196 | module_init(init_pxa2xx_flash); |
diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c new file mode 100644 index 000000000000..d39f0adac846 --- /dev/null +++ b/drivers/mtd/maps/rbtx4939-flash.c | |||
@@ -0,0 +1,208 @@ | |||
1 | /* | ||
2 | * rbtx4939-flash (based on physmap.c) | ||
3 | * | ||
4 | * This is a simplified physmap driver with map_init callback function. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * Copyright (C) 2009 Atsushi Nemoto <anemo@mba.ocn.ne.jp> | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/types.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/platform_device.h> | ||
20 | #include <linux/mtd/mtd.h> | ||
21 | #include <linux/mtd/map.h> | ||
22 | #include <linux/mtd/partitions.h> | ||
23 | #include <asm/txx9/rbtx4939.h> | ||
24 | |||
25 | struct rbtx4939_flash_info { | ||
26 | struct mtd_info *mtd; | ||
27 | struct map_info map; | ||
28 | #ifdef CONFIG_MTD_PARTITIONS | ||
29 | int nr_parts; | ||
30 | struct mtd_partition *parts; | ||
31 | #endif | ||
32 | }; | ||
33 | |||
34 | static int rbtx4939_flash_remove(struct platform_device *dev) | ||
35 | { | ||
36 | struct rbtx4939_flash_info *info; | ||
37 | |||
38 | info = platform_get_drvdata(dev); | ||
39 | if (!info) | ||
40 | return 0; | ||
41 | platform_set_drvdata(dev, NULL); | ||
42 | |||
43 | if (info->mtd) { | ||
44 | #ifdef CONFIG_MTD_PARTITIONS | ||
45 | struct rbtx4939_flash_data *pdata = dev->dev.platform_data; | ||
46 | |||
47 | if (info->nr_parts) { | ||
48 | del_mtd_partitions(info->mtd); | ||
49 | kfree(info->parts); | ||
50 | } else if (pdata->nr_parts) | ||
51 | del_mtd_partitions(info->mtd); | ||
52 | else | ||
53 | del_mtd_device(info->mtd); | ||
54 | #else | ||
55 | del_mtd_device(info->mtd); | ||
56 | #endif | ||
57 | map_destroy(info->mtd); | ||
58 | } | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; | ||
63 | #ifdef CONFIG_MTD_PARTITIONS | ||
64 | static const char *part_probe_types[] = { "cmdlinepart", NULL }; | ||
65 | #endif | ||
66 | |||
67 | static int rbtx4939_flash_probe(struct platform_device *dev) | ||
68 | { | ||
69 | struct rbtx4939_flash_data *pdata; | ||
70 | struct rbtx4939_flash_info *info; | ||
71 | struct resource *res; | ||
72 | const char **probe_type; | ||
73 | int err = 0; | ||
74 | unsigned long size; | ||
75 | |||
76 | pdata = dev->dev.platform_data; | ||
77 | if (!pdata) | ||
78 | return -ENODEV; | ||
79 | |||
80 | res = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
81 | if (!res) | ||
82 | return -ENODEV; | ||
83 | info = devm_kzalloc(&dev->dev, sizeof(struct rbtx4939_flash_info), | ||
84 | GFP_KERNEL); | ||
85 | if (!info) | ||
86 | return -ENOMEM; | ||
87 | |||
88 | platform_set_drvdata(dev, info); | ||
89 | |||
90 | size = resource_size(res); | ||
91 | pr_notice("rbtx4939 platform flash device: %pR\n", res); | ||
92 | |||
93 | if (!devm_request_mem_region(&dev->dev, res->start, size, | ||
94 | dev_name(&dev->dev))) | ||
95 | return -EBUSY; | ||
96 | |||
97 | info->map.name = dev_name(&dev->dev); | ||
98 | info->map.phys = res->start; | ||
99 | info->map.size = size; | ||
100 | info->map.bankwidth = pdata->width; | ||
101 | |||
102 | info->map.virt = devm_ioremap(&dev->dev, info->map.phys, size); | ||
103 | if (!info->map.virt) | ||
104 | return -EBUSY; | ||
105 | |||
106 | if (pdata->map_init) | ||
107 | (*pdata->map_init)(&info->map); | ||
108 | else | ||
109 | simple_map_init(&info->map); | ||
110 | |||
111 | probe_type = rom_probe_types; | ||
112 | for (; !info->mtd && *probe_type; probe_type++) | ||
113 | info->mtd = do_map_probe(*probe_type, &info->map); | ||
114 | if (!info->mtd) { | ||
115 | dev_err(&dev->dev, "map_probe failed\n"); | ||
116 | err = -ENXIO; | ||
117 | goto err_out; | ||
118 | } | ||
119 | info->mtd->owner = THIS_MODULE; | ||
120 | if (err) | ||
121 | goto err_out; | ||
122 | |||
123 | #ifdef CONFIG_MTD_PARTITIONS | ||
124 | err = parse_mtd_partitions(info->mtd, part_probe_types, | ||
125 | &info->parts, 0); | ||
126 | if (err > 0) { | ||
127 | add_mtd_partitions(info->mtd, info->parts, err); | ||
128 | info->nr_parts = err; | ||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | if (pdata->nr_parts) { | ||
133 | pr_notice("Using rbtx4939 partition information\n"); | ||
134 | add_mtd_partitions(info->mtd, pdata->parts, pdata->nr_parts); | ||
135 | return 0; | ||
136 | } | ||
137 | #endif | ||
138 | |||
139 | add_mtd_device(info->mtd); | ||
140 | return 0; | ||
141 | |||
142 | err_out: | ||
143 | rbtx4939_flash_remove(dev); | ||
144 | return err; | ||
145 | } | ||
146 | |||
147 | #ifdef CONFIG_PM | ||
148 | static int rbtx4939_flash_suspend(struct platform_device *dev, | ||
149 | pm_message_t state) | ||
150 | { | ||
151 | struct rbtx4939_flash_info *info = platform_get_drvdata(dev); | ||
152 | |||
153 | if (info->mtd->suspend) | ||
154 | return info->mtd->suspend(info->mtd); | ||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static int rbtx4939_flash_resume(struct platform_device *dev) | ||
159 | { | ||
160 | struct rbtx4939_flash_info *info = platform_get_drvdata(dev); | ||
161 | |||
162 | if (info->mtd->resume) | ||
163 | info->mtd->resume(info->mtd); | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static void rbtx4939_flash_shutdown(struct platform_device *dev) | ||
168 | { | ||
169 | struct rbtx4939_flash_info *info = platform_get_drvdata(dev); | ||
170 | |||
171 | if (info->mtd->suspend && info->mtd->resume) | ||
172 | if (info->mtd->suspend(info->mtd) == 0) | ||
173 | info->mtd->resume(info->mtd); | ||
174 | } | ||
175 | #else | ||
176 | #define rbtx4939_flash_suspend NULL | ||
177 | #define rbtx4939_flash_resume NULL | ||
178 | #define rbtx4939_flash_shutdown NULL | ||
179 | #endif | ||
180 | |||
181 | static struct platform_driver rbtx4939_flash_driver = { | ||
182 | .probe = rbtx4939_flash_probe, | ||
183 | .remove = rbtx4939_flash_remove, | ||
184 | .suspend = rbtx4939_flash_suspend, | ||
185 | .resume = rbtx4939_flash_resume, | ||
186 | .shutdown = rbtx4939_flash_shutdown, | ||
187 | .driver = { | ||
188 | .name = "rbtx4939-flash", | ||
189 | .owner = THIS_MODULE, | ||
190 | }, | ||
191 | }; | ||
192 | |||
193 | static int __init rbtx4939_flash_init(void) | ||
194 | { | ||
195 | return platform_driver_register(&rbtx4939_flash_driver); | ||
196 | } | ||
197 | |||
198 | static void __exit rbtx4939_flash_exit(void) | ||
199 | { | ||
200 | platform_driver_unregister(&rbtx4939_flash_driver); | ||
201 | } | ||
202 | |||
203 | module_init(rbtx4939_flash_init); | ||
204 | module_exit(rbtx4939_flash_exit); | ||
205 | |||
206 | MODULE_LICENSE("GPL"); | ||
207 | MODULE_DESCRIPTION("RBTX4939 MTD map driver"); | ||
208 | MODULE_ALIAS("platform:rbtx4939-flash"); | ||
diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 6f6a0f6dafd6..05e9362dc7f0 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/slab.h> | 12 | #include <linux/slab.h> |
13 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
14 | #include <linux/err.h> | 14 | #include <linux/err.h> |
15 | #include <linux/io.h> | ||
15 | 16 | ||
16 | #include <linux/mtd/mtd.h> | 17 | #include <linux/mtd/mtd.h> |
17 | #include <linux/mtd/map.h> | 18 | #include <linux/mtd/map.h> |
@@ -19,7 +20,6 @@ | |||
19 | #include <linux/mtd/concat.h> | 20 | #include <linux/mtd/concat.h> |
20 | 21 | ||
21 | #include <mach/hardware.h> | 22 | #include <mach/hardware.h> |
22 | #include <asm/io.h> | ||
23 | #include <asm/sizes.h> | 23 | #include <asm/sizes.h> |
24 | #include <asm/mach/flash.h> | 24 | #include <asm/mach/flash.h> |
25 | 25 | ||
@@ -351,7 +351,7 @@ sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat) | |||
351 | 351 | ||
352 | static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; | 352 | static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; |
353 | 353 | ||
354 | static int __init sa1100_mtd_probe(struct platform_device *pdev) | 354 | static int __devinit sa1100_mtd_probe(struct platform_device *pdev) |
355 | { | 355 | { |
356 | struct flash_platform_data *plat = pdev->dev.platform_data; | 356 | struct flash_platform_data *plat = pdev->dev.platform_data; |
357 | struct mtd_partition *parts; | 357 | struct mtd_partition *parts; |
diff --git a/drivers/mtd/maps/sharpsl-flash.c b/drivers/mtd/maps/sharpsl-flash.c deleted file mode 100644 index b392f096c706..000000000000 --- a/drivers/mtd/maps/sharpsl-flash.c +++ /dev/null | |||
@@ -1,116 +0,0 @@ | |||
1 | /* | ||
2 | * sharpsl-flash.c | ||
3 | * | ||
4 | * Copyright (C) 2001 Lineo Japan, Inc. | ||
5 | * Copyright (C) 2002 SHARP | ||
6 | * | ||
7 | * based on rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp | ||
8 | * Handle mapping of the flash on the RPX Lite and CLLF boards | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | */ | ||
21 | |||
22 | #include <linux/module.h> | ||
23 | #include <linux/types.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/mtd/mtd.h> | ||
26 | #include <linux/mtd/map.h> | ||
27 | #include <linux/mtd/partitions.h> | ||
28 | #include <asm/io.h> | ||
29 | #include <asm/mach-types.h> | ||
30 | |||
31 | #define WINDOW_ADDR 0x00000000 | ||
32 | #define WINDOW_SIZE 0x00800000 | ||
33 | #define BANK_WIDTH 2 | ||
34 | |||
35 | static struct mtd_info *mymtd; | ||
36 | |||
37 | struct map_info sharpsl_map = { | ||
38 | .name = "sharpsl-flash", | ||
39 | .size = WINDOW_SIZE, | ||
40 | .bankwidth = BANK_WIDTH, | ||
41 | .phys = WINDOW_ADDR | ||
42 | }; | ||
43 | |||
44 | static struct mtd_partition sharpsl_partitions[1] = { | ||
45 | { | ||
46 | name: "Boot PROM Filesystem", | ||
47 | } | ||
48 | }; | ||
49 | |||
50 | static int __init init_sharpsl(void) | ||
51 | { | ||
52 | struct mtd_partition *parts; | ||
53 | int nb_parts = 0; | ||
54 | char *part_type = "static"; | ||
55 | |||
56 | printk(KERN_NOTICE "Sharp SL series flash device: %x at %x\n", | ||
57 | WINDOW_SIZE, WINDOW_ADDR); | ||
58 | sharpsl_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); | ||
59 | if (!sharpsl_map.virt) { | ||
60 | printk("Failed to ioremap\n"); | ||
61 | return -EIO; | ||
62 | } | ||
63 | |||
64 | simple_map_init(&sharpsl_map); | ||
65 | |||
66 | mymtd = do_map_probe("map_rom", &sharpsl_map); | ||
67 | if (!mymtd) { | ||
68 | iounmap(sharpsl_map.virt); | ||
69 | return -ENXIO; | ||
70 | } | ||
71 | |||
72 | mymtd->owner = THIS_MODULE; | ||
73 | |||
74 | if (machine_is_corgi() || machine_is_shepherd() || machine_is_husky() | ||
75 | || machine_is_poodle()) { | ||
76 | sharpsl_partitions[0].size=0x006d0000; | ||
77 | sharpsl_partitions[0].offset=0x00120000; | ||
78 | } else if (machine_is_tosa()) { | ||
79 | sharpsl_partitions[0].size=0x006a0000; | ||
80 | sharpsl_partitions[0].offset=0x00160000; | ||
81 | } else if (machine_is_spitz() || machine_is_akita() || machine_is_borzoi()) { | ||
82 | sharpsl_partitions[0].size=0x006b0000; | ||
83 | sharpsl_partitions[0].offset=0x00140000; | ||
84 | } else { | ||
85 | map_destroy(mymtd); | ||
86 | iounmap(sharpsl_map.virt); | ||
87 | return -ENODEV; | ||
88 | } | ||
89 | |||
90 | parts = sharpsl_partitions; | ||
91 | nb_parts = ARRAY_SIZE(sharpsl_partitions); | ||
92 | |||
93 | printk(KERN_NOTICE "Using %s partition definition\n", part_type); | ||
94 | add_mtd_partitions(mymtd, parts, nb_parts); | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static void __exit cleanup_sharpsl(void) | ||
100 | { | ||
101 | if (mymtd) { | ||
102 | del_mtd_partitions(mymtd); | ||
103 | map_destroy(mymtd); | ||
104 | } | ||
105 | if (sharpsl_map.virt) { | ||
106 | iounmap(sharpsl_map.virt); | ||
107 | sharpsl_map.virt = 0; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | module_init(init_sharpsl); | ||
112 | module_exit(cleanup_sharpsl); | ||
113 | |||
114 | MODULE_LICENSE("GPL"); | ||
115 | MODULE_AUTHOR("SHARP (Original: Arnold Christensen <AKC@pel.dk>)"); | ||
116 | MODULE_DESCRIPTION("MTD map driver for SHARP SL series"); | ||
diff --git a/drivers/mtd/maps/vmu-flash.c b/drivers/mtd/maps/vmu-flash.c new file mode 100644 index 000000000000..1f73297e7776 --- /dev/null +++ b/drivers/mtd/maps/vmu-flash.c | |||
@@ -0,0 +1,832 @@ | |||
1 | /* vmu-flash.c | ||
2 | * Driver for SEGA Dreamcast Visual Memory Unit | ||
3 | * | ||
4 | * Copyright (c) Adrian McMenamin 2002 - 2009 | ||
5 | * Copyright (c) Paul Mundt 2001 | ||
6 | * | ||
7 | * Licensed under version 2 of the | ||
8 | * GNU General Public Licence | ||
9 | */ | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/delay.h> | ||
13 | #include <linux/maple.h> | ||
14 | #include <linux/mtd/mtd.h> | ||
15 | #include <linux/mtd/map.h> | ||
16 | |||
17 | struct vmu_cache { | ||
18 | unsigned char *buffer; /* Cache */ | ||
19 | unsigned int block; /* Which block was cached */ | ||
20 | unsigned long jiffies_atc; /* When was it cached? */ | ||
21 | int valid; | ||
22 | }; | ||
23 | |||
24 | struct mdev_part { | ||
25 | struct maple_device *mdev; | ||
26 | int partition; | ||
27 | }; | ||
28 | |||
29 | struct vmupart { | ||
30 | u16 user_blocks; | ||
31 | u16 root_block; | ||
32 | u16 numblocks; | ||
33 | char *name; | ||
34 | struct vmu_cache *pcache; | ||
35 | }; | ||
36 | |||
37 | struct memcard { | ||
38 | u16 tempA; | ||
39 | u16 tempB; | ||
40 | u32 partitions; | ||
41 | u32 blocklen; | ||
42 | u32 writecnt; | ||
43 | u32 readcnt; | ||
44 | u32 removeable; | ||
45 | int partition; | ||
46 | int read; | ||
47 | unsigned char *blockread; | ||
48 | struct vmupart *parts; | ||
49 | struct mtd_info *mtd; | ||
50 | }; | ||
51 | |||
52 | struct vmu_block { | ||
53 | unsigned int num; /* block number */ | ||
54 | unsigned int ofs; /* block offset */ | ||
55 | }; | ||
56 | |||
57 | static struct vmu_block *ofs_to_block(unsigned long src_ofs, | ||
58 | struct mtd_info *mtd, int partition) | ||
59 | { | ||
60 | struct vmu_block *vblock; | ||
61 | struct maple_device *mdev; | ||
62 | struct memcard *card; | ||
63 | struct mdev_part *mpart; | ||
64 | int num; | ||
65 | |||
66 | mpart = mtd->priv; | ||
67 | mdev = mpart->mdev; | ||
68 | card = maple_get_drvdata(mdev); | ||
69 | |||
70 | if (src_ofs >= card->parts[partition].numblocks * card->blocklen) | ||
71 | goto failed; | ||
72 | |||
73 | num = src_ofs / card->blocklen; | ||
74 | if (num > card->parts[partition].numblocks) | ||
75 | goto failed; | ||
76 | |||
77 | vblock = kmalloc(sizeof(struct vmu_block), GFP_KERNEL); | ||
78 | if (!vblock) | ||
79 | goto failed; | ||
80 | |||
81 | vblock->num = num; | ||
82 | vblock->ofs = src_ofs % card->blocklen; | ||
83 | return vblock; | ||
84 | |||
85 | failed: | ||
86 | return NULL; | ||
87 | } | ||
88 | |||
89 | /* Maple bus callback function for reads */ | ||
90 | static void vmu_blockread(struct mapleq *mq) | ||
91 | { | ||
92 | struct maple_device *mdev; | ||
93 | struct memcard *card; | ||
94 | |||
95 | mdev = mq->dev; | ||
96 | card = maple_get_drvdata(mdev); | ||
97 | /* copy the read in data */ | ||
98 | |||
99 | if (unlikely(!card->blockread)) | ||
100 | return; | ||
101 | |||
102 | memcpy(card->blockread, mq->recvbuf->buf + 12, | ||
103 | card->blocklen/card->readcnt); | ||
104 | |||
105 | } | ||
106 | |||
107 | /* Interface with maple bus to read blocks | ||
108 | * caching the results so that other parts | ||
109 | * of the driver can access block reads */ | ||
110 | static int maple_vmu_read_block(unsigned int num, unsigned char *buf, | ||
111 | struct mtd_info *mtd) | ||
112 | { | ||
113 | struct memcard *card; | ||
114 | struct mdev_part *mpart; | ||
115 | struct maple_device *mdev; | ||
116 | int partition, error = 0, x, wait; | ||
117 | unsigned char *blockread = NULL; | ||
118 | struct vmu_cache *pcache; | ||
119 | __be32 sendbuf; | ||
120 | |||
121 | mpart = mtd->priv; | ||
122 | mdev = mpart->mdev; | ||
123 | partition = mpart->partition; | ||
124 | card = maple_get_drvdata(mdev); | ||
125 | pcache = card->parts[partition].pcache; | ||
126 | pcache->valid = 0; | ||
127 | |||
128 | /* prepare the cache for this block */ | ||
129 | if (!pcache->buffer) { | ||
130 | pcache->buffer = kmalloc(card->blocklen, GFP_KERNEL); | ||
131 | if (!pcache->buffer) { | ||
132 | dev_err(&mdev->dev, "VMU at (%d, %d) - read fails due" | ||
133 | " to lack of memory\n", mdev->port, | ||
134 | mdev->unit); | ||
135 | error = -ENOMEM; | ||
136 | goto outB; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | /* | ||
141 | * Reads may be phased - again the hardware spec | ||
142 | * supports this - though may not be any devices in | ||
143 | * the wild that implement it, but we will here | ||
144 | */ | ||
145 | for (x = 0; x < card->readcnt; x++) { | ||
146 | sendbuf = cpu_to_be32(partition << 24 | x << 16 | num); | ||
147 | |||
148 | if (atomic_read(&mdev->busy) == 1) { | ||
149 | wait_event_interruptible_timeout(mdev->maple_wait, | ||
150 | atomic_read(&mdev->busy) == 0, HZ); | ||
151 | if (atomic_read(&mdev->busy) == 1) { | ||
152 | dev_notice(&mdev->dev, "VMU at (%d, %d)" | ||
153 | " is busy\n", mdev->port, mdev->unit); | ||
154 | error = -EAGAIN; | ||
155 | goto outB; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | atomic_set(&mdev->busy, 1); | ||
160 | blockread = kmalloc(card->blocklen/card->readcnt, GFP_KERNEL); | ||
161 | if (!blockread) { | ||
162 | error = -ENOMEM; | ||
163 | atomic_set(&mdev->busy, 0); | ||
164 | goto outB; | ||
165 | } | ||
166 | card->blockread = blockread; | ||
167 | |||
168 | maple_getcond_callback(mdev, vmu_blockread, 0, | ||
169 | MAPLE_FUNC_MEMCARD); | ||
170 | error = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD, | ||
171 | MAPLE_COMMAND_BREAD, 2, &sendbuf); | ||
172 | /* Very long timeouts seem to be needed when box is stressed */ | ||
173 | wait = wait_event_interruptible_timeout(mdev->maple_wait, | ||
174 | (atomic_read(&mdev->busy) == 0 || | ||
175 | atomic_read(&mdev->busy) == 2), HZ * 3); | ||
176 | /* | ||
177 | * MTD layer does not handle hotplugging well | ||
178 | * so have to return errors when VMU is unplugged | ||
179 | * in the middle of a read (busy == 2) | ||
180 | */ | ||
181 | if (error || atomic_read(&mdev->busy) == 2) { | ||
182 | if (atomic_read(&mdev->busy) == 2) | ||
183 | error = -ENXIO; | ||
184 | atomic_set(&mdev->busy, 0); | ||
185 | card->blockread = NULL; | ||
186 | goto outA; | ||
187 | } | ||
188 | if (wait == 0 || wait == -ERESTARTSYS) { | ||
189 | card->blockread = NULL; | ||
190 | atomic_set(&mdev->busy, 0); | ||
191 | error = -EIO; | ||
192 | list_del_init(&(mdev->mq->list)); | ||
193 | kfree(mdev->mq->sendbuf); | ||
194 | mdev->mq->sendbuf = NULL; | ||
195 | if (wait == -ERESTARTSYS) { | ||
196 | dev_warn(&mdev->dev, "VMU read on (%d, %d)" | ||
197 | " interrupted on block 0x%X\n", | ||
198 | mdev->port, mdev->unit, num); | ||
199 | } else | ||
200 | dev_notice(&mdev->dev, "VMU read on (%d, %d)" | ||
201 | " timed out on block 0x%X\n", | ||
202 | mdev->port, mdev->unit, num); | ||
203 | goto outA; | ||
204 | } | ||
205 | |||
206 | memcpy(buf + (card->blocklen/card->readcnt) * x, blockread, | ||
207 | card->blocklen/card->readcnt); | ||
208 | |||
209 | memcpy(pcache->buffer + (card->blocklen/card->readcnt) * x, | ||
210 | card->blockread, card->blocklen/card->readcnt); | ||
211 | card->blockread = NULL; | ||
212 | pcache->block = num; | ||
213 | pcache->jiffies_atc = jiffies; | ||
214 | pcache->valid = 1; | ||
215 | kfree(blockread); | ||
216 | } | ||
217 | |||
218 | return error; | ||
219 | |||
220 | outA: | ||
221 | kfree(blockread); | ||
222 | outB: | ||
223 | return error; | ||
224 | } | ||
225 | |||
226 | /* communicate with maple bus for phased writing */ | ||
227 | static int maple_vmu_write_block(unsigned int num, const unsigned char *buf, | ||
228 | struct mtd_info *mtd) | ||
229 | { | ||
230 | struct memcard *card; | ||
231 | struct mdev_part *mpart; | ||
232 | struct maple_device *mdev; | ||
233 | int partition, error, locking, x, phaselen, wait; | ||
234 | __be32 *sendbuf; | ||
235 | |||
236 | mpart = mtd->priv; | ||
237 | mdev = mpart->mdev; | ||
238 | partition = mpart->partition; | ||
239 | card = maple_get_drvdata(mdev); | ||
240 | |||
241 | phaselen = card->blocklen/card->writecnt; | ||
242 | |||
243 | sendbuf = kmalloc(phaselen + 4, GFP_KERNEL); | ||
244 | if (!sendbuf) { | ||
245 | error = -ENOMEM; | ||
246 | goto fail_nosendbuf; | ||
247 | } | ||
248 | for (x = 0; x < card->writecnt; x++) { | ||
249 | sendbuf[0] = cpu_to_be32(partition << 24 | x << 16 | num); | ||
250 | memcpy(&sendbuf[1], buf + phaselen * x, phaselen); | ||
251 | /* wait until the device is not busy doing something else | ||
252 | * or 1 second - which ever is longer */ | ||
253 | if (atomic_read(&mdev->busy) == 1) { | ||
254 | wait_event_interruptible_timeout(mdev->maple_wait, | ||
255 | atomic_read(&mdev->busy) == 0, HZ); | ||
256 | if (atomic_read(&mdev->busy) == 1) { | ||
257 | error = -EBUSY; | ||
258 | dev_notice(&mdev->dev, "VMU write at (%d, %d)" | ||
259 | "failed - device is busy\n", | ||
260 | mdev->port, mdev->unit); | ||
261 | goto fail_nolock; | ||
262 | } | ||
263 | } | ||
264 | atomic_set(&mdev->busy, 1); | ||
265 | |||
266 | locking = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD, | ||
267 | MAPLE_COMMAND_BWRITE, phaselen / 4 + 2, sendbuf); | ||
268 | wait = wait_event_interruptible_timeout(mdev->maple_wait, | ||
269 | atomic_read(&mdev->busy) == 0, HZ/10); | ||
270 | if (locking) { | ||
271 | error = -EIO; | ||
272 | atomic_set(&mdev->busy, 0); | ||
273 | goto fail_nolock; | ||
274 | } | ||
275 | if (atomic_read(&mdev->busy) == 2) { | ||
276 | atomic_set(&mdev->busy, 0); | ||
277 | } else if (wait == 0 || wait == -ERESTARTSYS) { | ||
278 | error = -EIO; | ||
279 | dev_warn(&mdev->dev, "Write at (%d, %d) of block" | ||
280 | " 0x%X at phase %d failed: could not" | ||
281 | " communicate with VMU", mdev->port, | ||
282 | mdev->unit, num, x); | ||
283 | atomic_set(&mdev->busy, 0); | ||
284 | kfree(mdev->mq->sendbuf); | ||
285 | mdev->mq->sendbuf = NULL; | ||
286 | list_del_init(&(mdev->mq->list)); | ||
287 | goto fail_nolock; | ||
288 | } | ||
289 | } | ||
290 | kfree(sendbuf); | ||
291 | |||
292 | return card->blocklen; | ||
293 | |||
294 | fail_nolock: | ||
295 | kfree(sendbuf); | ||
296 | fail_nosendbuf: | ||
297 | dev_err(&mdev->dev, "VMU (%d, %d): write failed\n", mdev->port, | ||
298 | mdev->unit); | ||
299 | return error; | ||
300 | } | ||
301 | |||
302 | /* mtd function to simulate reading byte by byte */ | ||
303 | static unsigned char vmu_flash_read_char(unsigned long ofs, int *retval, | ||
304 | struct mtd_info *mtd) | ||
305 | { | ||
306 | struct vmu_block *vblock; | ||
307 | struct memcard *card; | ||
308 | struct mdev_part *mpart; | ||
309 | struct maple_device *mdev; | ||
310 | unsigned char *buf, ret; | ||
311 | int partition, error; | ||
312 | |||
313 | mpart = mtd->priv; | ||
314 | mdev = mpart->mdev; | ||
315 | partition = mpart->partition; | ||
316 | card = maple_get_drvdata(mdev); | ||
317 | *retval = 0; | ||
318 | |||
319 | buf = kmalloc(card->blocklen, GFP_KERNEL); | ||
320 | if (!buf) { | ||
321 | *retval = 1; | ||
322 | ret = -ENOMEM; | ||
323 | goto finish; | ||
324 | } | ||
325 | |||
326 | vblock = ofs_to_block(ofs, mtd, partition); | ||
327 | if (!vblock) { | ||
328 | *retval = 3; | ||
329 | ret = -ENOMEM; | ||
330 | goto out_buf; | ||
331 | } | ||
332 | |||
333 | error = maple_vmu_read_block(vblock->num, buf, mtd); | ||
334 | if (error) { | ||
335 | ret = error; | ||
336 | *retval = 2; | ||
337 | goto out_vblock; | ||
338 | } | ||
339 | |||
340 | ret = buf[vblock->ofs]; | ||
341 | |||
342 | out_vblock: | ||
343 | kfree(vblock); | ||
344 | out_buf: | ||
345 | kfree(buf); | ||
346 | finish: | ||
347 | return ret; | ||
348 | } | ||
349 | |||
350 | /* mtd higher order function to read flash */ | ||
351 | static int vmu_flash_read(struct mtd_info *mtd, loff_t from, size_t len, | ||
352 | size_t *retlen, u_char *buf) | ||
353 | { | ||
354 | struct maple_device *mdev; | ||
355 | struct memcard *card; | ||
356 | struct mdev_part *mpart; | ||
357 | struct vmu_cache *pcache; | ||
358 | struct vmu_block *vblock; | ||
359 | int index = 0, retval, partition, leftover, numblocks; | ||
360 | unsigned char cx; | ||
361 | |||
362 | if (len < 1) | ||
363 | return -EIO; | ||
364 | |||
365 | mpart = mtd->priv; | ||
366 | mdev = mpart->mdev; | ||
367 | partition = mpart->partition; | ||
368 | card = maple_get_drvdata(mdev); | ||
369 | |||
370 | numblocks = card->parts[partition].numblocks; | ||
371 | if (from + len > numblocks * card->blocklen) | ||
372 | len = numblocks * card->blocklen - from; | ||
373 | if (len == 0) | ||
374 | return -EIO; | ||
375 | /* Have we cached this bit already? */ | ||
376 | pcache = card->parts[partition].pcache; | ||
377 | do { | ||
378 | vblock = ofs_to_block(from + index, mtd, partition); | ||
379 | if (!vblock) | ||
380 | return -ENOMEM; | ||
381 | /* Have we cached this and is the cache valid and timely? */ | ||
382 | if (pcache->valid && | ||
383 | time_before(jiffies, pcache->jiffies_atc + HZ) && | ||
384 | (pcache->block == vblock->num)) { | ||
385 | /* we have cached it, so do necessary copying */ | ||
386 | leftover = card->blocklen - vblock->ofs; | ||
387 | if (vblock->ofs + len - index < card->blocklen) { | ||
388 | /* only a bit of this block to copy */ | ||
389 | memcpy(buf + index, | ||
390 | pcache->buffer + vblock->ofs, | ||
391 | len - index); | ||
392 | index = len; | ||
393 | } else { | ||
394 | /* otherwise copy remainder of whole block */ | ||
395 | memcpy(buf + index, pcache->buffer + | ||
396 | vblock->ofs, leftover); | ||
397 | index += leftover; | ||
398 | } | ||
399 | } else { | ||
400 | /* | ||
401 | * Not cached so read one byte - | ||
402 | * but cache the rest of the block | ||
403 | */ | ||
404 | cx = vmu_flash_read_char(from + index, &retval, mtd); | ||
405 | if (retval) { | ||
406 | *retlen = index; | ||
407 | kfree(vblock); | ||
408 | return cx; | ||
409 | } | ||
410 | memset(buf + index, cx, 1); | ||
411 | index++; | ||
412 | } | ||
413 | kfree(vblock); | ||
414 | } while (len > index); | ||
415 | *retlen = index; | ||
416 | |||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | static int vmu_flash_write(struct mtd_info *mtd, loff_t to, size_t len, | ||
421 | size_t *retlen, const u_char *buf) | ||
422 | { | ||
423 | struct maple_device *mdev; | ||
424 | struct memcard *card; | ||
425 | struct mdev_part *mpart; | ||
426 | int index = 0, partition, error = 0, numblocks; | ||
427 | struct vmu_cache *pcache; | ||
428 | struct vmu_block *vblock; | ||
429 | unsigned char *buffer; | ||
430 | |||
431 | mpart = mtd->priv; | ||
432 | mdev = mpart->mdev; | ||
433 | partition = mpart->partition; | ||
434 | card = maple_get_drvdata(mdev); | ||
435 | |||
436 | /* simple sanity checks */ | ||
437 | if (len < 1) { | ||
438 | error = -EIO; | ||
439 | goto failed; | ||
440 | } | ||
441 | numblocks = card->parts[partition].numblocks; | ||
442 | if (to + len > numblocks * card->blocklen) | ||
443 | len = numblocks * card->blocklen - to; | ||
444 | if (len == 0) { | ||
445 | error = -EIO; | ||
446 | goto failed; | ||
447 | } | ||
448 | |||
449 | vblock = ofs_to_block(to, mtd, partition); | ||
450 | if (!vblock) { | ||
451 | error = -ENOMEM; | ||
452 | goto failed; | ||
453 | } | ||
454 | |||
455 | buffer = kmalloc(card->blocklen, GFP_KERNEL); | ||
456 | if (!buffer) { | ||
457 | error = -ENOMEM; | ||
458 | goto fail_buffer; | ||
459 | } | ||
460 | |||
461 | do { | ||
462 | /* Read in the block we are to write to */ | ||
463 | error = maple_vmu_read_block(vblock->num, buffer, mtd); | ||
464 | if (error) | ||
465 | goto fail_io; | ||
466 | |||
467 | do { | ||
468 | buffer[vblock->ofs] = buf[index]; | ||
469 | vblock->ofs++; | ||
470 | index++; | ||
471 | if (index >= len) | ||
472 | break; | ||
473 | } while (vblock->ofs < card->blocklen); | ||
474 | |||
475 | /* write out new buffer */ | ||
476 | error = maple_vmu_write_block(vblock->num, buffer, mtd); | ||
477 | /* invalidate the cache */ | ||
478 | pcache = card->parts[partition].pcache; | ||
479 | pcache->valid = 0; | ||
480 | |||
481 | if (error != card->blocklen) | ||
482 | goto fail_io; | ||
483 | |||
484 | vblock->num++; | ||
485 | vblock->ofs = 0; | ||
486 | } while (len > index); | ||
487 | |||
488 | kfree(buffer); | ||
489 | *retlen = index; | ||
490 | kfree(vblock); | ||
491 | return 0; | ||
492 | |||
493 | fail_io: | ||
494 | kfree(buffer); | ||
495 | fail_buffer: | ||
496 | kfree(vblock); | ||
497 | failed: | ||
498 | dev_err(&mdev->dev, "VMU write failing with error %d\n", error); | ||
499 | return error; | ||
500 | } | ||
501 | |||
502 | static void vmu_flash_sync(struct mtd_info *mtd) | ||
503 | { | ||
504 | /* Do nothing here */ | ||
505 | } | ||
506 | |||
507 | /* Maple bus callback function to recursively query hardware details */ | ||
508 | static void vmu_queryblocks(struct mapleq *mq) | ||
509 | { | ||
510 | struct maple_device *mdev; | ||
511 | unsigned short *res; | ||
512 | struct memcard *card; | ||
513 | __be32 partnum; | ||
514 | struct vmu_cache *pcache; | ||
515 | struct mdev_part *mpart; | ||
516 | struct mtd_info *mtd_cur; | ||
517 | struct vmupart *part_cur; | ||
518 | int error; | ||
519 | |||
520 | mdev = mq->dev; | ||
521 | card = maple_get_drvdata(mdev); | ||
522 | res = (unsigned short *) (mq->recvbuf->buf); | ||
523 | card->tempA = res[12]; | ||
524 | card->tempB = res[6]; | ||
525 | |||
526 | dev_info(&mdev->dev, "VMU device at partition %d has %d user " | ||
527 | "blocks with a root block at %d\n", card->partition, | ||
528 | card->tempA, card->tempB); | ||
529 | |||
530 | part_cur = &card->parts[card->partition]; | ||
531 | part_cur->user_blocks = card->tempA; | ||
532 | part_cur->root_block = card->tempB; | ||
533 | part_cur->numblocks = card->tempB + 1; | ||
534 | part_cur->name = kmalloc(12, GFP_KERNEL); | ||
535 | if (!part_cur->name) | ||
536 | goto fail_name; | ||
537 | |||
538 | sprintf(part_cur->name, "vmu%d.%d.%d", | ||
539 | mdev->port, mdev->unit, card->partition); | ||
540 | mtd_cur = &card->mtd[card->partition]; | ||
541 | mtd_cur->name = part_cur->name; | ||
542 | mtd_cur->type = 8; | ||
543 | mtd_cur->flags = MTD_WRITEABLE|MTD_NO_ERASE; | ||
544 | mtd_cur->size = part_cur->numblocks * card->blocklen; | ||
545 | mtd_cur->erasesize = card->blocklen; | ||
546 | mtd_cur->write = vmu_flash_write; | ||
547 | mtd_cur->read = vmu_flash_read; | ||
548 | mtd_cur->sync = vmu_flash_sync; | ||
549 | mtd_cur->writesize = card->blocklen; | ||
550 | |||
551 | mpart = kmalloc(sizeof(struct mdev_part), GFP_KERNEL); | ||
552 | if (!mpart) | ||
553 | goto fail_mpart; | ||
554 | |||
555 | mpart->mdev = mdev; | ||
556 | mpart->partition = card->partition; | ||
557 | mtd_cur->priv = mpart; | ||
558 | mtd_cur->owner = THIS_MODULE; | ||
559 | |||
560 | pcache = kzalloc(sizeof(struct vmu_cache), GFP_KERNEL); | ||
561 | if (!pcache) | ||
562 | goto fail_cache_create; | ||
563 | part_cur->pcache = pcache; | ||
564 | |||
565 | error = add_mtd_device(mtd_cur); | ||
566 | if (error) | ||
567 | goto fail_mtd_register; | ||
568 | |||
569 | maple_getcond_callback(mdev, NULL, 0, | ||
570 | MAPLE_FUNC_MEMCARD); | ||
571 | |||
572 | /* | ||
573 | * Set up a recursive call to the (probably theoretical) | ||
574 | * second or more partition | ||
575 | */ | ||
576 | if (++card->partition < card->partitions) { | ||
577 | partnum = cpu_to_be32(card->partition << 24); | ||
578 | maple_getcond_callback(mdev, vmu_queryblocks, 0, | ||
579 | MAPLE_FUNC_MEMCARD); | ||
580 | maple_add_packet(mdev, MAPLE_FUNC_MEMCARD, | ||
581 | MAPLE_COMMAND_GETMINFO, 2, &partnum); | ||
582 | } | ||
583 | return; | ||
584 | |||
585 | fail_mtd_register: | ||
586 | dev_err(&mdev->dev, "Could not register maple device at (%d, %d)" | ||
587 | "error is 0x%X\n", mdev->port, mdev->unit, error); | ||
588 | for (error = 0; error <= card->partition; error++) { | ||
589 | kfree(((card->parts)[error]).pcache); | ||
590 | ((card->parts)[error]).pcache = NULL; | ||
591 | } | ||
592 | fail_cache_create: | ||
593 | fail_mpart: | ||
594 | for (error = 0; error <= card->partition; error++) { | ||
595 | kfree(((card->mtd)[error]).priv); | ||
596 | ((card->mtd)[error]).priv = NULL; | ||
597 | } | ||
598 | maple_getcond_callback(mdev, NULL, 0, | ||
599 | MAPLE_FUNC_MEMCARD); | ||
600 | kfree(part_cur->name); | ||
601 | fail_name: | ||
602 | return; | ||
603 | } | ||
604 | |||
605 | /* Handles very basic info about the flash, queries for details */ | ||
606 | static int __devinit vmu_connect(struct maple_device *mdev) | ||
607 | { | ||
608 | unsigned long test_flash_data, basic_flash_data; | ||
609 | int c, error; | ||
610 | struct memcard *card; | ||
611 | u32 partnum = 0; | ||
612 | |||
613 | test_flash_data = be32_to_cpu(mdev->devinfo.function); | ||
614 | /* Need to count how many bits are set - to find out which | ||
615 | * function_data element has details of the memory card: | ||
616 | * using Brian Kernighan's/Peter Wegner's method */ | ||
617 | for (c = 0; test_flash_data; c++) | ||
618 | test_flash_data &= test_flash_data - 1; | ||
619 | |||
620 | basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]); | ||
621 | |||
622 | card = kmalloc(sizeof(struct memcard), GFP_KERNEL); | ||
623 | if (!card) { | ||
624 | error = ENOMEM; | ||
625 | goto fail_nomem; | ||
626 | } | ||
627 | |||
628 | card->partitions = (basic_flash_data >> 24 & 0xFF) + 1; | ||
629 | card->blocklen = ((basic_flash_data >> 16 & 0xFF) + 1) << 5; | ||
630 | card->writecnt = basic_flash_data >> 12 & 0xF; | ||
631 | card->readcnt = basic_flash_data >> 8 & 0xF; | ||
632 | card->removeable = basic_flash_data >> 7 & 1; | ||
633 | |||
634 | card->partition = 0; | ||
635 | |||
636 | /* | ||
637 | * Not sure there are actually any multi-partition devices in the | ||
638 | * real world, but the hardware supports them, so, so will we | ||
639 | */ | ||
640 | card->parts = kmalloc(sizeof(struct vmupart) * card->partitions, | ||
641 | GFP_KERNEL); | ||
642 | if (!card->parts) { | ||
643 | error = -ENOMEM; | ||
644 | goto fail_partitions; | ||
645 | } | ||
646 | |||
647 | card->mtd = kmalloc(sizeof(struct mtd_info) * card->partitions, | ||
648 | GFP_KERNEL); | ||
649 | if (!card->mtd) { | ||
650 | error = -ENOMEM; | ||
651 | goto fail_mtd_info; | ||
652 | } | ||
653 | |||
654 | maple_set_drvdata(mdev, card); | ||
655 | |||
656 | /* | ||
657 | * We want to trap meminfo not get cond | ||
658 | * so set interval to zero, but rely on maple bus | ||
659 | * driver to pass back the results of the meminfo | ||
660 | */ | ||
661 | maple_getcond_callback(mdev, vmu_queryblocks, 0, | ||
662 | MAPLE_FUNC_MEMCARD); | ||
663 | |||
664 | /* Make sure we are clear to go */ | ||
665 | if (atomic_read(&mdev->busy) == 1) { | ||
666 | wait_event_interruptible_timeout(mdev->maple_wait, | ||
667 | atomic_read(&mdev->busy) == 0, HZ); | ||
668 | if (atomic_read(&mdev->busy) == 1) { | ||
669 | dev_notice(&mdev->dev, "VMU at (%d, %d) is busy\n", | ||
670 | mdev->port, mdev->unit); | ||
671 | error = -EAGAIN; | ||
672 | goto fail_device_busy; | ||
673 | } | ||
674 | } | ||
675 | |||
676 | atomic_set(&mdev->busy, 1); | ||
677 | |||
678 | /* | ||
679 | * Set up the minfo call: vmu_queryblocks will handle | ||
680 | * the information passed back | ||
681 | */ | ||
682 | error = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD, | ||
683 | MAPLE_COMMAND_GETMINFO, 2, &partnum); | ||
684 | if (error) { | ||
685 | dev_err(&mdev->dev, "Could not lock VMU at (%d, %d)" | ||
686 | " error is 0x%X\n", mdev->port, mdev->unit, error); | ||
687 | goto fail_mtd_info; | ||
688 | } | ||
689 | return 0; | ||
690 | |||
691 | fail_device_busy: | ||
692 | kfree(card->mtd); | ||
693 | fail_mtd_info: | ||
694 | kfree(card->parts); | ||
695 | fail_partitions: | ||
696 | kfree(card); | ||
697 | fail_nomem: | ||
698 | return error; | ||
699 | } | ||
700 | |||
701 | static void __devexit vmu_disconnect(struct maple_device *mdev) | ||
702 | { | ||
703 | struct memcard *card; | ||
704 | struct mdev_part *mpart; | ||
705 | int x; | ||
706 | |||
707 | mdev->callback = NULL; | ||
708 | card = maple_get_drvdata(mdev); | ||
709 | for (x = 0; x < card->partitions; x++) { | ||
710 | mpart = ((card->mtd)[x]).priv; | ||
711 | mpart->mdev = NULL; | ||
712 | del_mtd_device(&((card->mtd)[x])); | ||
713 | kfree(((card->parts)[x]).name); | ||
714 | } | ||
715 | kfree(card->parts); | ||
716 | kfree(card->mtd); | ||
717 | kfree(card); | ||
718 | } | ||
719 | |||
720 | /* Callback to handle eccentricities of both mtd subsystem | ||
721 | * and general flakyness of Dreamcast VMUs | ||
722 | */ | ||
723 | static int vmu_can_unload(struct maple_device *mdev) | ||
724 | { | ||
725 | struct memcard *card; | ||
726 | int x; | ||
727 | struct mtd_info *mtd; | ||
728 | |||
729 | card = maple_get_drvdata(mdev); | ||
730 | for (x = 0; x < card->partitions; x++) { | ||
731 | mtd = &((card->mtd)[x]); | ||
732 | if (mtd->usecount > 0) | ||
733 | return 0; | ||
734 | } | ||
735 | return 1; | ||
736 | } | ||
737 | |||
738 | #define ERRSTR "VMU at (%d, %d) file error -" | ||
739 | |||
740 | static void vmu_file_error(struct maple_device *mdev, void *recvbuf) | ||
741 | { | ||
742 | enum maple_file_errors error = ((int *)recvbuf)[1]; | ||
743 | |||
744 | switch (error) { | ||
745 | |||
746 | case MAPLE_FILEERR_INVALID_PARTITION: | ||
747 | dev_notice(&mdev->dev, ERRSTR " invalid partition number\n", | ||
748 | mdev->port, mdev->unit); | ||
749 | break; | ||
750 | |||
751 | case MAPLE_FILEERR_PHASE_ERROR: | ||
752 | dev_notice(&mdev->dev, ERRSTR " phase error\n", | ||
753 | mdev->port, mdev->unit); | ||
754 | break; | ||
755 | |||
756 | case MAPLE_FILEERR_INVALID_BLOCK: | ||
757 | dev_notice(&mdev->dev, ERRSTR " invalid block number\n", | ||
758 | mdev->port, mdev->unit); | ||
759 | break; | ||
760 | |||
761 | case MAPLE_FILEERR_WRITE_ERROR: | ||
762 | dev_notice(&mdev->dev, ERRSTR " write error\n", | ||
763 | mdev->port, mdev->unit); | ||
764 | break; | ||
765 | |||
766 | case MAPLE_FILEERR_INVALID_WRITE_LENGTH: | ||
767 | dev_notice(&mdev->dev, ERRSTR " invalid write length\n", | ||
768 | mdev->port, mdev->unit); | ||
769 | break; | ||
770 | |||
771 | case MAPLE_FILEERR_BAD_CRC: | ||
772 | dev_notice(&mdev->dev, ERRSTR " bad CRC\n", | ||
773 | mdev->port, mdev->unit); | ||
774 | break; | ||
775 | |||
776 | default: | ||
777 | dev_notice(&mdev->dev, ERRSTR " 0x%X\n", | ||
778 | mdev->port, mdev->unit, error); | ||
779 | } | ||
780 | } | ||
781 | |||
782 | |||
783 | static int __devinit probe_maple_vmu(struct device *dev) | ||
784 | { | ||
785 | int error; | ||
786 | struct maple_device *mdev = to_maple_dev(dev); | ||
787 | struct maple_driver *mdrv = to_maple_driver(dev->driver); | ||
788 | |||
789 | mdev->can_unload = vmu_can_unload; | ||
790 | mdev->fileerr_handler = vmu_file_error; | ||
791 | mdev->driver = mdrv; | ||
792 | |||
793 | error = vmu_connect(mdev); | ||
794 | if (error) | ||
795 | return error; | ||
796 | |||
797 | return 0; | ||
798 | } | ||
799 | |||
800 | static int __devexit remove_maple_vmu(struct device *dev) | ||
801 | { | ||
802 | struct maple_device *mdev = to_maple_dev(dev); | ||
803 | |||
804 | vmu_disconnect(mdev); | ||
805 | return 0; | ||
806 | } | ||
807 | |||
808 | static struct maple_driver vmu_flash_driver = { | ||
809 | .function = MAPLE_FUNC_MEMCARD, | ||
810 | .drv = { | ||
811 | .name = "Dreamcast_visual_memory", | ||
812 | .probe = probe_maple_vmu, | ||
813 | .remove = __devexit_p(remove_maple_vmu), | ||
814 | }, | ||
815 | }; | ||
816 | |||
817 | static int __init vmu_flash_map_init(void) | ||
818 | { | ||
819 | return maple_driver_register(&vmu_flash_driver); | ||
820 | } | ||
821 | |||
822 | static void __exit vmu_flash_map_exit(void) | ||
823 | { | ||
824 | maple_driver_unregister(&vmu_flash_driver); | ||
825 | } | ||
826 | |||
827 | module_init(vmu_flash_map_init); | ||
828 | module_exit(vmu_flash_map_exit); | ||
829 | |||
830 | MODULE_LICENSE("GPL"); | ||
831 | MODULE_AUTHOR("Adrian McMenamin"); | ||
832 | MODULE_DESCRIPTION("Flash mapping for Sega Dreamcast visual memory"); | ||
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 1409f01406f6..a49a9c8f2cb1 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c | |||
@@ -286,6 +286,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) | |||
286 | gd->private_data = new; | 286 | gd->private_data = new; |
287 | new->blkcore_priv = gd; | 287 | new->blkcore_priv = gd; |
288 | gd->queue = tr->blkcore_priv->rq; | 288 | gd->queue = tr->blkcore_priv->rq; |
289 | gd->driverfs_dev = new->mtd->dev.parent; | ||
289 | 290 | ||
290 | if (new->readonly) | 291 | if (new->readonly) |
291 | set_disk_ro(gd, 1); | 292 | set_disk_ro(gd, 1); |
@@ -382,11 +383,12 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) | |||
382 | tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr, | 383 | tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr, |
383 | "%sd", tr->name); | 384 | "%sd", tr->name); |
384 | if (IS_ERR(tr->blkcore_priv->thread)) { | 385 | if (IS_ERR(tr->blkcore_priv->thread)) { |
386 | int ret = PTR_ERR(tr->blkcore_priv->thread); | ||
385 | blk_cleanup_queue(tr->blkcore_priv->rq); | 387 | blk_cleanup_queue(tr->blkcore_priv->rq); |
386 | unregister_blkdev(tr->major, tr->name); | 388 | unregister_blkdev(tr->major, tr->name); |
387 | kfree(tr->blkcore_priv); | 389 | kfree(tr->blkcore_priv); |
388 | mutex_unlock(&mtd_table_mutex); | 390 | mutex_unlock(&mtd_table_mutex); |
389 | return PTR_ERR(tr->blkcore_priv->thread); | 391 | return ret; |
390 | } | 392 | } |
391 | 393 | ||
392 | INIT_LIST_HEAD(&tr->devs); | 394 | INIT_LIST_HEAD(&tr->devs); |
diff --git a/drivers/mtd/mtdbdi.c b/drivers/mtd/mtdbdi.c new file mode 100644 index 000000000000..5ca5aed0b225 --- /dev/null +++ b/drivers/mtd/mtdbdi.c | |||
@@ -0,0 +1,43 @@ | |||
1 | /* MTD backing device capabilities | ||
2 | * | ||
3 | * Copyright © 2006 Red Hat, Inc. All Rights Reserved. | ||
4 | * Written by David Howells (dhowells@redhat.com) | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/backing-dev.h> | ||
13 | #include <linux/mtd/mtd.h> | ||
14 | #include "internal.h" | ||
15 | |||
16 | /* | ||
17 | * backing device capabilities for non-mappable devices (such as NAND flash) | ||
18 | * - permits private mappings, copies are taken of the data | ||
19 | */ | ||
20 | struct backing_dev_info mtd_bdi_unmappable = { | ||
21 | .capabilities = BDI_CAP_MAP_COPY, | ||
22 | }; | ||
23 | |||
24 | /* | ||
25 | * backing device capabilities for R/O mappable devices (such as ROM) | ||
26 | * - permits private mappings, copies are taken of the data | ||
27 | * - permits non-writable shared mappings | ||
28 | */ | ||
29 | struct backing_dev_info mtd_bdi_ro_mappable = { | ||
30 | .capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT | | ||
31 | BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP), | ||
32 | }; | ||
33 | |||
34 | /* | ||
35 | * backing device capabilities for writable mappable devices (such as RAM) | ||
36 | * - permits private mappings, copies are taken of the data | ||
37 | * - permits non-writable shared mappings | ||
38 | */ | ||
39 | struct backing_dev_info mtd_bdi_rw_mappable = { | ||
40 | .capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT | | ||
41 | BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP | | ||
42 | BDI_CAP_WRITE_MAP), | ||
43 | }; | ||
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index e9ec59e9a566..763d3f0a1f42 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c | |||
@@ -13,39 +13,13 @@ | |||
13 | #include <linux/slab.h> | 13 | #include <linux/slab.h> |
14 | #include <linux/sched.h> | 14 | #include <linux/sched.h> |
15 | #include <linux/smp_lock.h> | 15 | #include <linux/smp_lock.h> |
16 | #include <linux/backing-dev.h> | ||
16 | 17 | ||
17 | #include <linux/mtd/mtd.h> | 18 | #include <linux/mtd/mtd.h> |
18 | #include <linux/mtd/compatmac.h> | 19 | #include <linux/mtd/compatmac.h> |
19 | 20 | ||
20 | #include <asm/uaccess.h> | 21 | #include <asm/uaccess.h> |
21 | 22 | ||
22 | static struct class *mtd_class; | ||
23 | |||
24 | static void mtd_notify_add(struct mtd_info* mtd) | ||
25 | { | ||
26 | if (!mtd) | ||
27 | return; | ||
28 | |||
29 | device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), | ||
30 | NULL, "mtd%d", mtd->index); | ||
31 | |||
32 | device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), | ||
33 | NULL, "mtd%dro", mtd->index); | ||
34 | } | ||
35 | |||
36 | static void mtd_notify_remove(struct mtd_info* mtd) | ||
37 | { | ||
38 | if (!mtd) | ||
39 | return; | ||
40 | |||
41 | device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2)); | ||
42 | device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1)); | ||
43 | } | ||
44 | |||
45 | static struct mtd_notifier notifier = { | ||
46 | .add = mtd_notify_add, | ||
47 | .remove = mtd_notify_remove, | ||
48 | }; | ||
49 | 23 | ||
50 | /* | 24 | /* |
51 | * Data structure to hold the pointer to the mtd device as well | 25 | * Data structure to hold the pointer to the mtd device as well |
@@ -107,12 +81,15 @@ static int mtd_open(struct inode *inode, struct file *file) | |||
107 | goto out; | 81 | goto out; |
108 | } | 82 | } |
109 | 83 | ||
110 | if (MTD_ABSENT == mtd->type) { | 84 | if (mtd->type == MTD_ABSENT) { |
111 | put_mtd_device(mtd); | 85 | put_mtd_device(mtd); |
112 | ret = -ENODEV; | 86 | ret = -ENODEV; |
113 | goto out; | 87 | goto out; |
114 | } | 88 | } |
115 | 89 | ||
90 | if (mtd->backing_dev_info) | ||
91 | file->f_mapping->backing_dev_info = mtd->backing_dev_info; | ||
92 | |||
116 | /* You can't open it RW if it's not a writeable device */ | 93 | /* You can't open it RW if it's not a writeable device */ |
117 | if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) { | 94 | if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) { |
118 | put_mtd_device(mtd); | 95 | put_mtd_device(mtd); |
@@ -781,6 +758,59 @@ static int mtd_ioctl(struct inode *inode, struct file *file, | |||
781 | return ret; | 758 | return ret; |
782 | } /* memory_ioctl */ | 759 | } /* memory_ioctl */ |
783 | 760 | ||
761 | /* | ||
762 | * try to determine where a shared mapping can be made | ||
763 | * - only supported for NOMMU at the moment (MMU can't doesn't copy private | ||
764 | * mappings) | ||
765 | */ | ||
766 | #ifndef CONFIG_MMU | ||
767 | static unsigned long mtd_get_unmapped_area(struct file *file, | ||
768 | unsigned long addr, | ||
769 | unsigned long len, | ||
770 | unsigned long pgoff, | ||
771 | unsigned long flags) | ||
772 | { | ||
773 | struct mtd_file_info *mfi = file->private_data; | ||
774 | struct mtd_info *mtd = mfi->mtd; | ||
775 | |||
776 | if (mtd->get_unmapped_area) { | ||
777 | unsigned long offset; | ||
778 | |||
779 | if (addr != 0) | ||
780 | return (unsigned long) -EINVAL; | ||
781 | |||
782 | if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT)) | ||
783 | return (unsigned long) -EINVAL; | ||
784 | |||
785 | offset = pgoff << PAGE_SHIFT; | ||
786 | if (offset > mtd->size - len) | ||
787 | return (unsigned long) -EINVAL; | ||
788 | |||
789 | return mtd->get_unmapped_area(mtd, len, offset, flags); | ||
790 | } | ||
791 | |||
792 | /* can't map directly */ | ||
793 | return (unsigned long) -ENOSYS; | ||
794 | } | ||
795 | #endif | ||
796 | |||
797 | /* | ||
798 | * set up a mapping for shared memory segments | ||
799 | */ | ||
800 | static int mtd_mmap(struct file *file, struct vm_area_struct *vma) | ||
801 | { | ||
802 | #ifdef CONFIG_MMU | ||
803 | struct mtd_file_info *mfi = file->private_data; | ||
804 | struct mtd_info *mtd = mfi->mtd; | ||
805 | |||
806 | if (mtd->type == MTD_RAM || mtd->type == MTD_ROM) | ||
807 | return 0; | ||
808 | return -ENOSYS; | ||
809 | #else | ||
810 | return vma->vm_flags & VM_SHARED ? 0 : -ENOSYS; | ||
811 | #endif | ||
812 | } | ||
813 | |||
784 | static const struct file_operations mtd_fops = { | 814 | static const struct file_operations mtd_fops = { |
785 | .owner = THIS_MODULE, | 815 | .owner = THIS_MODULE, |
786 | .llseek = mtd_lseek, | 816 | .llseek = mtd_lseek, |
@@ -789,39 +819,36 @@ static const struct file_operations mtd_fops = { | |||
789 | .ioctl = mtd_ioctl, | 819 | .ioctl = mtd_ioctl, |
790 | .open = mtd_open, | 820 | .open = mtd_open, |
791 | .release = mtd_close, | 821 | .release = mtd_close, |
822 | .mmap = mtd_mmap, | ||
823 | #ifndef CONFIG_MMU | ||
824 | .get_unmapped_area = mtd_get_unmapped_area, | ||
825 | #endif | ||
792 | }; | 826 | }; |
793 | 827 | ||
794 | static int __init init_mtdchar(void) | 828 | static int __init init_mtdchar(void) |
795 | { | 829 | { |
796 | if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) { | 830 | int status; |
831 | |||
832 | status = register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops); | ||
833 | if (status < 0) { | ||
797 | printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", | 834 | printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", |
798 | MTD_CHAR_MAJOR); | 835 | MTD_CHAR_MAJOR); |
799 | return -EAGAIN; | ||
800 | } | 836 | } |
801 | 837 | ||
802 | mtd_class = class_create(THIS_MODULE, "mtd"); | 838 | return status; |
803 | |||
804 | if (IS_ERR(mtd_class)) { | ||
805 | printk(KERN_ERR "Error creating mtd class.\n"); | ||
806 | unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); | ||
807 | return PTR_ERR(mtd_class); | ||
808 | } | ||
809 | |||
810 | register_mtd_user(¬ifier); | ||
811 | return 0; | ||
812 | } | 839 | } |
813 | 840 | ||
814 | static void __exit cleanup_mtdchar(void) | 841 | static void __exit cleanup_mtdchar(void) |
815 | { | 842 | { |
816 | unregister_mtd_user(¬ifier); | ||
817 | class_destroy(mtd_class); | ||
818 | unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); | 843 | unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); |
819 | } | 844 | } |
820 | 845 | ||
821 | module_init(init_mtdchar); | 846 | module_init(init_mtdchar); |
822 | module_exit(cleanup_mtdchar); | 847 | module_exit(cleanup_mtdchar); |
823 | 848 | ||
849 | MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR); | ||
824 | 850 | ||
825 | MODULE_LICENSE("GPL"); | 851 | MODULE_LICENSE("GPL"); |
826 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | 852 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); |
827 | MODULE_DESCRIPTION("Direct character-device access to MTD devices"); | 853 | MODULE_DESCRIPTION("Direct character-device access to MTD devices"); |
854 | MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR); | ||
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 3dbb1b38db66..792b547786b8 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/slab.h> | 13 | #include <linux/slab.h> |
14 | #include <linux/sched.h> | 14 | #include <linux/sched.h> |
15 | #include <linux/types.h> | 15 | #include <linux/types.h> |
16 | #include <linux/backing-dev.h> | ||
16 | 17 | ||
17 | #include <linux/mtd/mtd.h> | 18 | #include <linux/mtd/mtd.h> |
18 | #include <linux/mtd/concat.h> | 19 | #include <linux/mtd/concat.h> |
@@ -684,6 +685,40 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs) | |||
684 | } | 685 | } |
685 | 686 | ||
686 | /* | 687 | /* |
688 | * try to support NOMMU mmaps on concatenated devices | ||
689 | * - we don't support subdev spanning as we can't guarantee it'll work | ||
690 | */ | ||
691 | static unsigned long concat_get_unmapped_area(struct mtd_info *mtd, | ||
692 | unsigned long len, | ||
693 | unsigned long offset, | ||
694 | unsigned long flags) | ||
695 | { | ||
696 | struct mtd_concat *concat = CONCAT(mtd); | ||
697 | int i; | ||
698 | |||
699 | for (i = 0; i < concat->num_subdev; i++) { | ||
700 | struct mtd_info *subdev = concat->subdev[i]; | ||
701 | |||
702 | if (offset >= subdev->size) { | ||
703 | offset -= subdev->size; | ||
704 | continue; | ||
705 | } | ||
706 | |||
707 | /* we've found the subdev over which the mapping will reside */ | ||
708 | if (offset + len > subdev->size) | ||
709 | return (unsigned long) -EINVAL; | ||
710 | |||
711 | if (subdev->get_unmapped_area) | ||
712 | return subdev->get_unmapped_area(subdev, len, offset, | ||
713 | flags); | ||
714 | |||
715 | break; | ||
716 | } | ||
717 | |||
718 | return (unsigned long) -ENOSYS; | ||
719 | } | ||
720 | |||
721 | /* | ||
687 | * This function constructs a virtual MTD device by concatenating | 722 | * This function constructs a virtual MTD device by concatenating |
688 | * num_devs MTD devices. A pointer to the new device object is | 723 | * num_devs MTD devices. A pointer to the new device object is |
689 | * stored to *new_dev upon success. This function does _not_ | 724 | * stored to *new_dev upon success. This function does _not_ |
@@ -740,6 +775,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c | |||
740 | 775 | ||
741 | concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks; | 776 | concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks; |
742 | 777 | ||
778 | concat->mtd.backing_dev_info = subdev[0]->backing_dev_info; | ||
779 | |||
743 | concat->subdev[0] = subdev[0]; | 780 | concat->subdev[0] = subdev[0]; |
744 | 781 | ||
745 | for (i = 1; i < num_devs; i++) { | 782 | for (i = 1; i < num_devs; i++) { |
@@ -766,6 +803,15 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c | |||
766 | concat->mtd.flags |= | 803 | concat->mtd.flags |= |
767 | subdev[i]->flags & MTD_WRITEABLE; | 804 | subdev[i]->flags & MTD_WRITEABLE; |
768 | } | 805 | } |
806 | |||
807 | /* only permit direct mapping if the BDIs are all the same | ||
808 | * - copy-mapping is still permitted | ||
809 | */ | ||
810 | if (concat->mtd.backing_dev_info != | ||
811 | subdev[i]->backing_dev_info) | ||
812 | concat->mtd.backing_dev_info = | ||
813 | &default_backing_dev_info; | ||
814 | |||
769 | concat->mtd.size += subdev[i]->size; | 815 | concat->mtd.size += subdev[i]->size; |
770 | concat->mtd.ecc_stats.badblocks += | 816 | concat->mtd.ecc_stats.badblocks += |
771 | subdev[i]->ecc_stats.badblocks; | 817 | subdev[i]->ecc_stats.badblocks; |
@@ -796,6 +842,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c | |||
796 | concat->mtd.unlock = concat_unlock; | 842 | concat->mtd.unlock = concat_unlock; |
797 | concat->mtd.suspend = concat_suspend; | 843 | concat->mtd.suspend = concat_suspend; |
798 | concat->mtd.resume = concat_resume; | 844 | concat->mtd.resume = concat_resume; |
845 | concat->mtd.get_unmapped_area = concat_get_unmapped_area; | ||
799 | 846 | ||
800 | /* | 847 | /* |
801 | * Combine the erase block size info of the subdevices: | 848 | * Combine the erase block size info of the subdevices: |
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 76fe0a1e7a5e..fdd6ae859397 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c | |||
@@ -19,9 +19,13 @@ | |||
19 | #include <linux/proc_fs.h> | 19 | #include <linux/proc_fs.h> |
20 | 20 | ||
21 | #include <linux/mtd/mtd.h> | 21 | #include <linux/mtd/mtd.h> |
22 | #include "internal.h" | ||
22 | 23 | ||
23 | #include "mtdcore.h" | 24 | #include "mtdcore.h" |
24 | 25 | ||
26 | |||
27 | static struct class *mtd_class; | ||
28 | |||
25 | /* These are exported solely for the purpose of mtd_blkdevs.c. You | 29 | /* These are exported solely for the purpose of mtd_blkdevs.c. You |
26 | should not use them for _anything_ else */ | 30 | should not use them for _anything_ else */ |
27 | DEFINE_MUTEX(mtd_table_mutex); | 31 | DEFINE_MUTEX(mtd_table_mutex); |
@@ -32,6 +36,160 @@ EXPORT_SYMBOL_GPL(mtd_table); | |||
32 | 36 | ||
33 | static LIST_HEAD(mtd_notifiers); | 37 | static LIST_HEAD(mtd_notifiers); |
34 | 38 | ||
39 | |||
40 | #if defined(CONFIG_MTD_CHAR) || defined(CONFIG_MTD_CHAR_MODULE) | ||
41 | #define MTD_DEVT(index) MKDEV(MTD_CHAR_MAJOR, (index)*2) | ||
42 | #else | ||
43 | #define MTD_DEVT(index) 0 | ||
44 | #endif | ||
45 | |||
46 | /* REVISIT once MTD uses the driver model better, whoever allocates | ||
47 | * the mtd_info will probably want to use the release() hook... | ||
48 | */ | ||
49 | static void mtd_release(struct device *dev) | ||
50 | { | ||
51 | struct mtd_info *mtd = dev_to_mtd(dev); | ||
52 | |||
53 | /* remove /dev/mtdXro node if needed */ | ||
54 | if (MTD_DEVT(mtd->index)) | ||
55 | device_destroy(mtd_class, MTD_DEVT(mtd->index) + 1); | ||
56 | } | ||
57 | |||
58 | static ssize_t mtd_type_show(struct device *dev, | ||
59 | struct device_attribute *attr, char *buf) | ||
60 | { | ||
61 | struct mtd_info *mtd = dev_to_mtd(dev); | ||
62 | char *type; | ||
63 | |||
64 | switch (mtd->type) { | ||
65 | case MTD_ABSENT: | ||
66 | type = "absent"; | ||
67 | break; | ||
68 | case MTD_RAM: | ||
69 | type = "ram"; | ||
70 | break; | ||
71 | case MTD_ROM: | ||
72 | type = "rom"; | ||
73 | break; | ||
74 | case MTD_NORFLASH: | ||
75 | type = "nor"; | ||
76 | break; | ||
77 | case MTD_NANDFLASH: | ||
78 | type = "nand"; | ||
79 | break; | ||
80 | case MTD_DATAFLASH: | ||
81 | type = "dataflash"; | ||
82 | break; | ||
83 | case MTD_UBIVOLUME: | ||
84 | type = "ubi"; | ||
85 | break; | ||
86 | default: | ||
87 | type = "unknown"; | ||
88 | } | ||
89 | |||
90 | return snprintf(buf, PAGE_SIZE, "%s\n", type); | ||
91 | } | ||
92 | static DEVICE_ATTR(type, S_IRUGO, mtd_type_show, NULL); | ||
93 | |||
94 | static ssize_t mtd_flags_show(struct device *dev, | ||
95 | struct device_attribute *attr, char *buf) | ||
96 | { | ||
97 | struct mtd_info *mtd = dev_to_mtd(dev); | ||
98 | |||
99 | return snprintf(buf, PAGE_SIZE, "0x%lx\n", (unsigned long)mtd->flags); | ||
100 | |||
101 | } | ||
102 | static DEVICE_ATTR(flags, S_IRUGO, mtd_flags_show, NULL); | ||
103 | |||
104 | static ssize_t mtd_size_show(struct device *dev, | ||
105 | struct device_attribute *attr, char *buf) | ||
106 | { | ||
107 | struct mtd_info *mtd = dev_to_mtd(dev); | ||
108 | |||
109 | return snprintf(buf, PAGE_SIZE, "%llu\n", | ||
110 | (unsigned long long)mtd->size); | ||
111 | |||
112 | } | ||
113 | static DEVICE_ATTR(size, S_IRUGO, mtd_size_show, NULL); | ||
114 | |||
115 | static ssize_t mtd_erasesize_show(struct device *dev, | ||
116 | struct device_attribute *attr, char *buf) | ||
117 | { | ||
118 | struct mtd_info *mtd = dev_to_mtd(dev); | ||
119 | |||
120 | return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->erasesize); | ||
121 | |||
122 | } | ||
123 | static DEVICE_ATTR(erasesize, S_IRUGO, mtd_erasesize_show, NULL); | ||
124 | |||
125 | static ssize_t mtd_writesize_show(struct device *dev, | ||
126 | struct device_attribute *attr, char *buf) | ||
127 | { | ||
128 | struct mtd_info *mtd = dev_to_mtd(dev); | ||
129 | |||
130 | return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->writesize); | ||
131 | |||
132 | } | ||
133 | static DEVICE_ATTR(writesize, S_IRUGO, mtd_writesize_show, NULL); | ||
134 | |||
135 | static ssize_t mtd_oobsize_show(struct device *dev, | ||
136 | struct device_attribute *attr, char *buf) | ||
137 | { | ||
138 | struct mtd_info *mtd = dev_to_mtd(dev); | ||
139 | |||
140 | return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->oobsize); | ||
141 | |||
142 | } | ||
143 | static DEVICE_ATTR(oobsize, S_IRUGO, mtd_oobsize_show, NULL); | ||
144 | |||
145 | static ssize_t mtd_numeraseregions_show(struct device *dev, | ||
146 | struct device_attribute *attr, char *buf) | ||
147 | { | ||
148 | struct mtd_info *mtd = dev_to_mtd(dev); | ||
149 | |||
150 | return snprintf(buf, PAGE_SIZE, "%u\n", mtd->numeraseregions); | ||
151 | |||
152 | } | ||
153 | static DEVICE_ATTR(numeraseregions, S_IRUGO, mtd_numeraseregions_show, | ||
154 | NULL); | ||
155 | |||
156 | static ssize_t mtd_name_show(struct device *dev, | ||
157 | struct device_attribute *attr, char *buf) | ||
158 | { | ||
159 | struct mtd_info *mtd = dev_to_mtd(dev); | ||
160 | |||
161 | return snprintf(buf, PAGE_SIZE, "%s\n", mtd->name); | ||
162 | |||
163 | } | ||
164 | static DEVICE_ATTR(name, S_IRUGO, mtd_name_show, NULL); | ||
165 | |||
166 | static struct attribute *mtd_attrs[] = { | ||
167 | &dev_attr_type.attr, | ||
168 | &dev_attr_flags.attr, | ||
169 | &dev_attr_size.attr, | ||
170 | &dev_attr_erasesize.attr, | ||
171 | &dev_attr_writesize.attr, | ||
172 | &dev_attr_oobsize.attr, | ||
173 | &dev_attr_numeraseregions.attr, | ||
174 | &dev_attr_name.attr, | ||
175 | NULL, | ||
176 | }; | ||
177 | |||
178 | struct attribute_group mtd_group = { | ||
179 | .attrs = mtd_attrs, | ||
180 | }; | ||
181 | |||
182 | struct attribute_group *mtd_groups[] = { | ||
183 | &mtd_group, | ||
184 | NULL, | ||
185 | }; | ||
186 | |||
187 | static struct device_type mtd_devtype = { | ||
188 | .name = "mtd", | ||
189 | .groups = mtd_groups, | ||
190 | .release = mtd_release, | ||
191 | }; | ||
192 | |||
35 | /** | 193 | /** |
36 | * add_mtd_device - register an MTD device | 194 | * add_mtd_device - register an MTD device |
37 | * @mtd: pointer to new MTD device info structure | 195 | * @mtd: pointer to new MTD device info structure |
@@ -40,12 +198,27 @@ static LIST_HEAD(mtd_notifiers); | |||
40 | * notify each currently active MTD 'user' of its arrival. Returns | 198 | * notify each currently active MTD 'user' of its arrival. Returns |
41 | * zero on success or 1 on failure, which currently will only happen | 199 | * zero on success or 1 on failure, which currently will only happen |
42 | * if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16) | 200 | * if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16) |
201 | * or there's a sysfs error. | ||
43 | */ | 202 | */ |
44 | 203 | ||
45 | int add_mtd_device(struct mtd_info *mtd) | 204 | int add_mtd_device(struct mtd_info *mtd) |
46 | { | 205 | { |
47 | int i; | 206 | int i; |
48 | 207 | ||
208 | if (!mtd->backing_dev_info) { | ||
209 | switch (mtd->type) { | ||
210 | case MTD_RAM: | ||
211 | mtd->backing_dev_info = &mtd_bdi_rw_mappable; | ||
212 | break; | ||
213 | case MTD_ROM: | ||
214 | mtd->backing_dev_info = &mtd_bdi_ro_mappable; | ||
215 | break; | ||
216 | default: | ||
217 | mtd->backing_dev_info = &mtd_bdi_unmappable; | ||
218 | break; | ||
219 | } | ||
220 | } | ||
221 | |||
49 | BUG_ON(mtd->writesize == 0); | 222 | BUG_ON(mtd->writesize == 0); |
50 | mutex_lock(&mtd_table_mutex); | 223 | mutex_lock(&mtd_table_mutex); |
51 | 224 | ||
@@ -80,6 +253,23 @@ int add_mtd_device(struct mtd_info *mtd) | |||
80 | mtd->name); | 253 | mtd->name); |
81 | } | 254 | } |
82 | 255 | ||
256 | /* Caller should have set dev.parent to match the | ||
257 | * physical device. | ||
258 | */ | ||
259 | mtd->dev.type = &mtd_devtype; | ||
260 | mtd->dev.class = mtd_class; | ||
261 | mtd->dev.devt = MTD_DEVT(i); | ||
262 | dev_set_name(&mtd->dev, "mtd%d", i); | ||
263 | if (device_register(&mtd->dev) != 0) { | ||
264 | mtd_table[i] = NULL; | ||
265 | break; | ||
266 | } | ||
267 | |||
268 | if (MTD_DEVT(i)) | ||
269 | device_create(mtd_class, mtd->dev.parent, | ||
270 | MTD_DEVT(i) + 1, | ||
271 | NULL, "mtd%dro", i); | ||
272 | |||
83 | DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); | 273 | DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); |
84 | /* No need to get a refcount on the module containing | 274 | /* No need to get a refcount on the module containing |
85 | the notifier, since we hold the mtd_table_mutex */ | 275 | the notifier, since we hold the mtd_table_mutex */ |
@@ -124,6 +314,8 @@ int del_mtd_device (struct mtd_info *mtd) | |||
124 | } else { | 314 | } else { |
125 | struct mtd_notifier *not; | 315 | struct mtd_notifier *not; |
126 | 316 | ||
317 | device_unregister(&mtd->dev); | ||
318 | |||
127 | /* No need to get a refcount on the module containing | 319 | /* No need to get a refcount on the module containing |
128 | the notifier, since we hold the mtd_table_mutex */ | 320 | the notifier, since we hold the mtd_table_mutex */ |
129 | list_for_each_entry(not, &mtd_notifiers, list) | 321 | list_for_each_entry(not, &mtd_notifiers, list) |
@@ -393,28 +585,38 @@ done: | |||
393 | return ((count < begin+len-off) ? count : begin+len-off); | 585 | return ((count < begin+len-off) ? count : begin+len-off); |
394 | } | 586 | } |
395 | 587 | ||
588 | #endif /* CONFIG_PROC_FS */ | ||
589 | |||
396 | /*====================================================================*/ | 590 | /*====================================================================*/ |
397 | /* Init code */ | 591 | /* Init code */ |
398 | 592 | ||
399 | static int __init init_mtd(void) | 593 | static int __init init_mtd(void) |
400 | { | 594 | { |
595 | mtd_class = class_create(THIS_MODULE, "mtd"); | ||
596 | |||
597 | if (IS_ERR(mtd_class)) { | ||
598 | pr_err("Error creating mtd class.\n"); | ||
599 | return PTR_ERR(mtd_class); | ||
600 | } | ||
601 | #ifdef CONFIG_PROC_FS | ||
401 | if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) | 602 | if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) |
402 | proc_mtd->read_proc = mtd_read_proc; | 603 | proc_mtd->read_proc = mtd_read_proc; |
604 | #endif /* CONFIG_PROC_FS */ | ||
403 | return 0; | 605 | return 0; |
404 | } | 606 | } |
405 | 607 | ||
406 | static void __exit cleanup_mtd(void) | 608 | static void __exit cleanup_mtd(void) |
407 | { | 609 | { |
610 | #ifdef CONFIG_PROC_FS | ||
408 | if (proc_mtd) | 611 | if (proc_mtd) |
409 | remove_proc_entry( "mtd", NULL); | 612 | remove_proc_entry( "mtd", NULL); |
613 | #endif /* CONFIG_PROC_FS */ | ||
614 | class_destroy(mtd_class); | ||
410 | } | 615 | } |
411 | 616 | ||
412 | module_init(init_mtd); | 617 | module_init(init_mtd); |
413 | module_exit(cleanup_mtd); | 618 | module_exit(cleanup_mtd); |
414 | 619 | ||
415 | #endif /* CONFIG_PROC_FS */ | ||
416 | |||
417 | |||
418 | MODULE_LICENSE("GPL"); | 620 | MODULE_LICENSE("GPL"); |
419 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | 621 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); |
420 | MODULE_DESCRIPTION("Core MTD registration and access routines"); | 622 | MODULE_DESCRIPTION("Core MTD registration and access routines"); |
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 1a6b3beabe8d..1060337c06df 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c | |||
@@ -44,6 +44,7 @@ static struct mtdoops_context { | |||
44 | int oops_pages; | 44 | int oops_pages; |
45 | int nextpage; | 45 | int nextpage; |
46 | int nextcount; | 46 | int nextcount; |
47 | char *name; | ||
47 | 48 | ||
48 | void *oops_buf; | 49 | void *oops_buf; |
49 | 50 | ||
@@ -273,6 +274,9 @@ static void mtdoops_notify_add(struct mtd_info *mtd) | |||
273 | { | 274 | { |
274 | struct mtdoops_context *cxt = &oops_cxt; | 275 | struct mtdoops_context *cxt = &oops_cxt; |
275 | 276 | ||
277 | if (cxt->name && !strcmp(mtd->name, cxt->name)) | ||
278 | cxt->mtd_index = mtd->index; | ||
279 | |||
276 | if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) | 280 | if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) |
277 | return; | 281 | return; |
278 | 282 | ||
@@ -357,8 +361,10 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count) | |||
357 | spin_lock_irqsave(&cxt->writecount_lock, flags); | 361 | spin_lock_irqsave(&cxt->writecount_lock, flags); |
358 | 362 | ||
359 | /* Check ready status didn't change whilst waiting for the lock */ | 363 | /* Check ready status didn't change whilst waiting for the lock */ |
360 | if (!cxt->ready) | 364 | if (!cxt->ready) { |
365 | spin_unlock_irqrestore(&cxt->writecount_lock, flags); | ||
361 | return; | 366 | return; |
367 | } | ||
362 | 368 | ||
363 | if (cxt->writecount == 0) { | 369 | if (cxt->writecount == 0) { |
364 | u32 *stamp = cxt->oops_buf; | 370 | u32 *stamp = cxt->oops_buf; |
@@ -383,8 +389,12 @@ static int __init mtdoops_console_setup(struct console *co, char *options) | |||
383 | { | 389 | { |
384 | struct mtdoops_context *cxt = co->data; | 390 | struct mtdoops_context *cxt = co->data; |
385 | 391 | ||
386 | if (cxt->mtd_index != -1) | 392 | if (cxt->mtd_index != -1 || cxt->name) |
387 | return -EBUSY; | 393 | return -EBUSY; |
394 | if (options) { | ||
395 | cxt->name = kstrdup(options, GFP_KERNEL); | ||
396 | return 0; | ||
397 | } | ||
388 | if (co->index == -1) | 398 | if (co->index == -1) |
389 | return -EINVAL; | 399 | return -EINVAL; |
390 | 400 | ||
@@ -412,6 +422,7 @@ static int __init mtdoops_console_init(void) | |||
412 | 422 | ||
413 | cxt->mtd_index = -1; | 423 | cxt->mtd_index = -1; |
414 | cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE); | 424 | cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE); |
425 | spin_lock_init(&cxt->writecount_lock); | ||
415 | 426 | ||
416 | if (!cxt->oops_buf) { | 427 | if (!cxt->oops_buf) { |
417 | printk(KERN_ERR "Failed to allocate mtdoops buffer workspace\n"); | 428 | printk(KERN_ERR "Failed to allocate mtdoops buffer workspace\n"); |
@@ -432,6 +443,7 @@ static void __exit mtdoops_console_exit(void) | |||
432 | 443 | ||
433 | unregister_mtd_user(&mtdoops_notifier); | 444 | unregister_mtd_user(&mtdoops_notifier); |
434 | unregister_console(&mtdoops_console); | 445 | unregister_console(&mtdoops_console); |
446 | kfree(cxt->name); | ||
435 | vfree(cxt->oops_buf); | 447 | vfree(cxt->oops_buf); |
436 | } | 448 | } |
437 | 449 | ||
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 144e6b613a77..29675edb44b4 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c | |||
@@ -48,8 +48,11 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
48 | size_t *retlen, u_char *buf) | 48 | size_t *retlen, u_char *buf) |
49 | { | 49 | { |
50 | struct mtd_part *part = PART(mtd); | 50 | struct mtd_part *part = PART(mtd); |
51 | struct mtd_ecc_stats stats; | ||
51 | int res; | 52 | int res; |
52 | 53 | ||
54 | stats = part->master->ecc_stats; | ||
55 | |||
53 | if (from >= mtd->size) | 56 | if (from >= mtd->size) |
54 | len = 0; | 57 | len = 0; |
55 | else if (from + len > mtd->size) | 58 | else if (from + len > mtd->size) |
@@ -58,9 +61,9 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
58 | len, retlen, buf); | 61 | len, retlen, buf); |
59 | if (unlikely(res)) { | 62 | if (unlikely(res)) { |
60 | if (res == -EUCLEAN) | 63 | if (res == -EUCLEAN) |
61 | mtd->ecc_stats.corrected++; | 64 | mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected; |
62 | if (res == -EBADMSG) | 65 | if (res == -EBADMSG) |
63 | mtd->ecc_stats.failed++; | 66 | mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed; |
64 | } | 67 | } |
65 | return res; | 68 | return res; |
66 | } | 69 | } |
@@ -84,6 +87,18 @@ static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len) | |||
84 | part->master->unpoint(part->master, from + part->offset, len); | 87 | part->master->unpoint(part->master, from + part->offset, len); |
85 | } | 88 | } |
86 | 89 | ||
90 | static unsigned long part_get_unmapped_area(struct mtd_info *mtd, | ||
91 | unsigned long len, | ||
92 | unsigned long offset, | ||
93 | unsigned long flags) | ||
94 | { | ||
95 | struct mtd_part *part = PART(mtd); | ||
96 | |||
97 | offset += part->offset; | ||
98 | return part->master->get_unmapped_area(part->master, len, offset, | ||
99 | flags); | ||
100 | } | ||
101 | |||
87 | static int part_read_oob(struct mtd_info *mtd, loff_t from, | 102 | static int part_read_oob(struct mtd_info *mtd, loff_t from, |
88 | struct mtd_oob_ops *ops) | 103 | struct mtd_oob_ops *ops) |
89 | { | 104 | { |
@@ -342,6 +357,12 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, | |||
342 | 357 | ||
343 | slave->mtd.name = part->name; | 358 | slave->mtd.name = part->name; |
344 | slave->mtd.owner = master->owner; | 359 | slave->mtd.owner = master->owner; |
360 | slave->mtd.backing_dev_info = master->backing_dev_info; | ||
361 | |||
362 | /* NOTE: we don't arrange MTDs as a tree; it'd be error-prone | ||
363 | * to have the same data be in two different partitions. | ||
364 | */ | ||
365 | slave->mtd.dev.parent = master->dev.parent; | ||
345 | 366 | ||
346 | slave->mtd.read = part_read; | 367 | slave->mtd.read = part_read; |
347 | slave->mtd.write = part_write; | 368 | slave->mtd.write = part_write; |
@@ -354,6 +375,8 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, | |||
354 | slave->mtd.unpoint = part_unpoint; | 375 | slave->mtd.unpoint = part_unpoint; |
355 | } | 376 | } |
356 | 377 | ||
378 | if (master->get_unmapped_area) | ||
379 | slave->mtd.get_unmapped_area = part_get_unmapped_area; | ||
357 | if (master->read_oob) | 380 | if (master->read_oob) |
358 | slave->mtd.read_oob = part_read_oob; | 381 | slave->mtd.read_oob = part_read_oob; |
359 | if (master->write_oob) | 382 | if (master->write_oob) |
@@ -493,7 +516,9 @@ out_register: | |||
493 | * This function, given a master MTD object and a partition table, creates | 516 | * This function, given a master MTD object and a partition table, creates |
494 | * and registers slave MTD objects which are bound to the master according to | 517 | * and registers slave MTD objects which are bound to the master according to |
495 | * the partition definitions. | 518 | * the partition definitions. |
496 | * (Q: should we register the master MTD object as well?) | 519 | * |
520 | * We don't register the master, or expect the caller to have done so, | ||
521 | * for reasons of data integrity. | ||
497 | */ | 522 | */ |
498 | 523 | ||
499 | int add_mtd_partitions(struct mtd_info *master, | 524 | int add_mtd_partitions(struct mtd_info *master, |
diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c index 00d46e137b2a..92285d0089c2 100644 --- a/drivers/mtd/mtdsuper.c +++ b/drivers/mtd/mtdsuper.c | |||
@@ -81,13 +81,16 @@ static int get_sb_mtd_aux(struct file_system_type *fs_type, int flags, | |||
81 | 81 | ||
82 | /* go */ | 82 | /* go */ |
83 | sb->s_flags |= MS_ACTIVE; | 83 | sb->s_flags |= MS_ACTIVE; |
84 | return simple_set_mnt(mnt, sb); | 84 | simple_set_mnt(mnt, sb); |
85 | |||
86 | return 0; | ||
85 | 87 | ||
86 | /* new mountpoint for an already mounted superblock */ | 88 | /* new mountpoint for an already mounted superblock */ |
87 | already_mounted: | 89 | already_mounted: |
88 | DEBUG(1, "MTDSB: Device %d (\"%s\") is already mounted\n", | 90 | DEBUG(1, "MTDSB: Device %d (\"%s\") is already mounted\n", |
89 | mtd->index, mtd->name); | 91 | mtd->index, mtd->name); |
90 | ret = simple_set_mnt(mnt, sb); | 92 | simple_set_mnt(mnt, sb); |
93 | ret = 0; | ||
91 | goto out_put; | 94 | goto out_put; |
92 | 95 | ||
93 | out_error: | 96 | out_error: |
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 8b12e6e109d3..890936d0275e 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
@@ -273,7 +273,7 @@ config MTD_NAND_CAFE | |||
273 | 273 | ||
274 | config MTD_NAND_CS553X | 274 | config MTD_NAND_CS553X |
275 | tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)" | 275 | tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)" |
276 | depends on X86_32 && (X86_PC || X86_GENERICARCH) | 276 | depends on X86_32 |
277 | help | 277 | help |
278 | The CS553x companion chips for the AMD Geode processor | 278 | The CS553x companion chips for the AMD Geode processor |
279 | include NAND flash controllers with built-in hardware ECC | 279 | include NAND flash controllers with built-in hardware ECC |
@@ -334,7 +334,7 @@ config MTD_NAND_ATMEL_ECC_NONE | |||
334 | endchoice | 334 | endchoice |
335 | 335 | ||
336 | config MTD_NAND_PXA3xx | 336 | config MTD_NAND_PXA3xx |
337 | bool "Support for NAND flash devices on PXA3xx" | 337 | tristate "Support for NAND flash devices on PXA3xx" |
338 | depends on MTD_NAND && PXA3xx | 338 | depends on MTD_NAND && PXA3xx |
339 | help | 339 | help |
340 | This enables the driver for the NAND flash device found on | 340 | This enables the driver for the NAND flash device found on |
@@ -427,4 +427,23 @@ config MTD_NAND_SH_FLCTL | |||
427 | Several Renesas SuperH CPU has FLCTL. This option enables support | 427 | Several Renesas SuperH CPU has FLCTL. This option enables support |
428 | for NAND Flash using FLCTL. This driver support SH7723. | 428 | for NAND Flash using FLCTL. This driver support SH7723. |
429 | 429 | ||
430 | config MTD_NAND_DAVINCI | ||
431 | tristate "Support NAND on DaVinci SoC" | ||
432 | depends on ARCH_DAVINCI | ||
433 | help | ||
434 | Enable the driver for NAND flash chips on Texas Instruments | ||
435 | DaVinci processors. | ||
436 | |||
437 | config MTD_NAND_TXX9NDFMC | ||
438 | tristate "NAND Flash support for TXx9 SoC" | ||
439 | depends on SOC_TX4938 || SOC_TX4939 | ||
440 | help | ||
441 | This enables the NAND flash controller on the TXx9 SoCs. | ||
442 | |||
443 | config MTD_NAND_SOCRATES | ||
444 | tristate "Support for NAND on Socrates board" | ||
445 | depends on MTD_NAND && SOCRATES | ||
446 | help | ||
447 | Enables support for NAND Flash chips wired onto Socrates board. | ||
448 | |||
430 | endif # MTD_NAND | 449 | endif # MTD_NAND |
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index b661586afbfc..d33860ac42c3 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile | |||
@@ -14,6 +14,7 @@ obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o | |||
14 | obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o | 14 | obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o |
15 | obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o | 15 | obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o |
16 | obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o | 16 | obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o |
17 | obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o | ||
17 | obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o | 18 | obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o |
18 | obj-$(CONFIG_MTD_NAND_H1900) += h1910.o | 19 | obj-$(CONFIG_MTD_NAND_H1900) += h1910.o |
19 | obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o | 20 | obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o |
@@ -36,5 +37,7 @@ obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o | |||
36 | obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o | 37 | obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o |
37 | obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o | 38 | obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o |
38 | obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o | 39 | obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o |
40 | obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o | ||
41 | obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o | ||
39 | 42 | ||
40 | nand-objs := nand_base.o nand_bbt.o | 43 | nand-objs := nand_base.o nand_bbt.o |
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index c98c1570a40b..47a33cec3793 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c | |||
@@ -139,7 +139,8 @@ static int atmel_nand_device_ready(struct mtd_info *mtd) | |||
139 | struct nand_chip *nand_chip = mtd->priv; | 139 | struct nand_chip *nand_chip = mtd->priv; |
140 | struct atmel_nand_host *host = nand_chip->priv; | 140 | struct atmel_nand_host *host = nand_chip->priv; |
141 | 141 | ||
142 | return gpio_get_value(host->board->rdy_pin); | 142 | return gpio_get_value(host->board->rdy_pin) ^ |
143 | !!host->board->rdy_pin_active_low; | ||
143 | } | 144 | } |
144 | 145 | ||
145 | /* | 146 | /* |
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 9af2a2cc1153..4c2a67ca801e 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c | |||
@@ -552,7 +552,6 @@ static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd, | |||
552 | static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) | 552 | static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) |
553 | { | 553 | { |
554 | int ret; | 554 | int ret; |
555 | unsigned short val; | ||
556 | 555 | ||
557 | /* Do not use dma */ | 556 | /* Do not use dma */ |
558 | if (!hardware_ecc) | 557 | if (!hardware_ecc) |
@@ -560,13 +559,6 @@ static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) | |||
560 | 559 | ||
561 | init_completion(&info->dma_completion); | 560 | init_completion(&info->dma_completion); |
562 | 561 | ||
563 | #ifdef CONFIG_BF54x | ||
564 | /* Setup DMAC1 channel mux for NFC which shared with SDH */ | ||
565 | val = bfin_read_DMAC1_PERIMUX(); | ||
566 | val &= 0xFFFE; | ||
567 | bfin_write_DMAC1_PERIMUX(val); | ||
568 | SSYNC(); | ||
569 | #endif | ||
570 | /* Request NFC DMA channel */ | 562 | /* Request NFC DMA channel */ |
571 | ret = request_dma(CH_NFC, "BF5XX NFC driver"); | 563 | ret = request_dma(CH_NFC, "BF5XX NFC driver"); |
572 | if (ret < 0) { | 564 | if (ret < 0) { |
@@ -574,7 +566,13 @@ static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) | |||
574 | return ret; | 566 | return ret; |
575 | } | 567 | } |
576 | 568 | ||
577 | set_dma_callback(CH_NFC, (void *) bf5xx_nand_dma_irq, (void *) info); | 569 | #ifdef CONFIG_BF54x |
570 | /* Setup DMAC1 channel mux for NFC which shared with SDH */ | ||
571 | bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() & ~1); | ||
572 | SSYNC(); | ||
573 | #endif | ||
574 | |||
575 | set_dma_callback(CH_NFC, bf5xx_nand_dma_irq, info); | ||
578 | 576 | ||
579 | /* Turn off the DMA channel first */ | 577 | /* Turn off the DMA channel first */ |
580 | disable_dma(CH_NFC); | 578 | disable_dma(CH_NFC); |
@@ -632,7 +630,7 @@ static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info) | |||
632 | /* | 630 | /* |
633 | * Device management interface | 631 | * Device management interface |
634 | */ | 632 | */ |
635 | static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info) | 633 | static int __devinit bf5xx_nand_add_partition(struct bf5xx_nand_info *info) |
636 | { | 634 | { |
637 | struct mtd_info *mtd = &info->mtd; | 635 | struct mtd_info *mtd = &info->mtd; |
638 | 636 | ||
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 22a6b2e50e91..7c5b257ce8e4 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c | |||
@@ -654,6 +654,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, | |||
654 | } | 654 | } |
655 | cafe = (void *)(&mtd[1]); | 655 | cafe = (void *)(&mtd[1]); |
656 | 656 | ||
657 | mtd->dev.parent = &pdev->dev; | ||
657 | mtd->priv = cafe; | 658 | mtd->priv = cafe; |
658 | mtd->owner = THIS_MODULE; | 659 | mtd->owner = THIS_MODULE; |
659 | 660 | ||
diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c index fa129c09bca8..10081e656a6f 100644 --- a/drivers/mtd/nand/cmx270_nand.c +++ b/drivers/mtd/nand/cmx270_nand.c | |||
@@ -26,8 +26,7 @@ | |||
26 | #include <asm/irq.h> | 26 | #include <asm/irq.h> |
27 | #include <asm/mach-types.h> | 27 | #include <asm/mach-types.h> |
28 | 28 | ||
29 | #include <mach/hardware.h> | 29 | #include <mach/pxa2xx-regs.h> |
30 | #include <mach/pxa-regs.h> | ||
31 | 30 | ||
32 | #define GPIO_NAND_CS (11) | 31 | #define GPIO_NAND_CS (11) |
33 | #define GPIO_NAND_RB (89) | 32 | #define GPIO_NAND_RB (89) |
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c new file mode 100644 index 000000000000..0119220de7d0 --- /dev/null +++ b/drivers/mtd/nand/davinci_nand.c | |||
@@ -0,0 +1,570 @@ | |||
1 | /* | ||
2 | * davinci_nand.c - NAND Flash Driver for DaVinci family chips | ||
3 | * | ||
4 | * Copyright © 2006 Texas Instruments. | ||
5 | * | ||
6 | * Port to 2.6.23 Copyright © 2008 by: | ||
7 | * Sander Huijsen <Shuijsen@optelecom-nkf.com> | ||
8 | * Troy Kisky <troy.kisky@boundarydevices.com> | ||
9 | * Dirk Behme <Dirk.Behme@gmail.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | */ | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/platform_device.h> | ||
30 | #include <linux/err.h> | ||
31 | #include <linux/clk.h> | ||
32 | #include <linux/io.h> | ||
33 | #include <linux/mtd/nand.h> | ||
34 | #include <linux/mtd/partitions.h> | ||
35 | |||
36 | #include <mach/nand.h> | ||
37 | |||
38 | #include <asm/mach-types.h> | ||
39 | |||
40 | |||
41 | /* | ||
42 | * This is a device driver for the NAND flash controller found on the | ||
43 | * various DaVinci family chips. It handles up to four SoC chipselects, | ||
44 | * and some flavors of secondary chipselect (e.g. based on A12) as used | ||
45 | * with multichip packages. | ||
46 | * | ||
47 | * The 1-bit ECC hardware is supported, but not yet the newer 4-bit ECC | ||
48 | * available on chips like the DM355 and OMAP-L137 and needed with the | ||
49 | * more error-prone MLC NAND chips. | ||
50 | * | ||
51 | * This driver assumes EM_WAIT connects all the NAND devices' RDY/nBUSY | ||
52 | * outputs in a "wire-AND" configuration, with no per-chip signals. | ||
53 | */ | ||
54 | struct davinci_nand_info { | ||
55 | struct mtd_info mtd; | ||
56 | struct nand_chip chip; | ||
57 | |||
58 | struct device *dev; | ||
59 | struct clk *clk; | ||
60 | bool partitioned; | ||
61 | |||
62 | void __iomem *base; | ||
63 | void __iomem *vaddr; | ||
64 | |||
65 | uint32_t ioaddr; | ||
66 | uint32_t current_cs; | ||
67 | |||
68 | uint32_t mask_chipsel; | ||
69 | uint32_t mask_ale; | ||
70 | uint32_t mask_cle; | ||
71 | |||
72 | uint32_t core_chipsel; | ||
73 | }; | ||
74 | |||
75 | static DEFINE_SPINLOCK(davinci_nand_lock); | ||
76 | |||
77 | #define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd) | ||
78 | |||
79 | |||
80 | static inline unsigned int davinci_nand_readl(struct davinci_nand_info *info, | ||
81 | int offset) | ||
82 | { | ||
83 | return __raw_readl(info->base + offset); | ||
84 | } | ||
85 | |||
86 | static inline void davinci_nand_writel(struct davinci_nand_info *info, | ||
87 | int offset, unsigned long value) | ||
88 | { | ||
89 | __raw_writel(value, info->base + offset); | ||
90 | } | ||
91 | |||
92 | /*----------------------------------------------------------------------*/ | ||
93 | |||
94 | /* | ||
95 | * Access to hardware control lines: ALE, CLE, secondary chipselect. | ||
96 | */ | ||
97 | |||
98 | static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd, | ||
99 | unsigned int ctrl) | ||
100 | { | ||
101 | struct davinci_nand_info *info = to_davinci_nand(mtd); | ||
102 | uint32_t addr = info->current_cs; | ||
103 | struct nand_chip *nand = mtd->priv; | ||
104 | |||
105 | /* Did the control lines change? */ | ||
106 | if (ctrl & NAND_CTRL_CHANGE) { | ||
107 | if ((ctrl & NAND_CTRL_CLE) == NAND_CTRL_CLE) | ||
108 | addr |= info->mask_cle; | ||
109 | else if ((ctrl & NAND_CTRL_ALE) == NAND_CTRL_ALE) | ||
110 | addr |= info->mask_ale; | ||
111 | |||
112 | nand->IO_ADDR_W = (void __iomem __force *)addr; | ||
113 | } | ||
114 | |||
115 | if (cmd != NAND_CMD_NONE) | ||
116 | iowrite8(cmd, nand->IO_ADDR_W); | ||
117 | } | ||
118 | |||
119 | static void nand_davinci_select_chip(struct mtd_info *mtd, int chip) | ||
120 | { | ||
121 | struct davinci_nand_info *info = to_davinci_nand(mtd); | ||
122 | uint32_t addr = info->ioaddr; | ||
123 | |||
124 | /* maybe kick in a second chipselect */ | ||
125 | if (chip > 0) | ||
126 | addr |= info->mask_chipsel; | ||
127 | info->current_cs = addr; | ||
128 | |||
129 | info->chip.IO_ADDR_W = (void __iomem __force *)addr; | ||
130 | info->chip.IO_ADDR_R = info->chip.IO_ADDR_W; | ||
131 | } | ||
132 | |||
133 | /*----------------------------------------------------------------------*/ | ||
134 | |||
135 | /* | ||
136 | * 1-bit hardware ECC ... context maintained for each core chipselect | ||
137 | */ | ||
138 | |||
139 | static inline uint32_t nand_davinci_readecc_1bit(struct mtd_info *mtd) | ||
140 | { | ||
141 | struct davinci_nand_info *info = to_davinci_nand(mtd); | ||
142 | |||
143 | return davinci_nand_readl(info, NANDF1ECC_OFFSET | ||
144 | + 4 * info->core_chipsel); | ||
145 | } | ||
146 | |||
147 | static void nand_davinci_hwctl_1bit(struct mtd_info *mtd, int mode) | ||
148 | { | ||
149 | struct davinci_nand_info *info; | ||
150 | uint32_t nandcfr; | ||
151 | unsigned long flags; | ||
152 | |||
153 | info = to_davinci_nand(mtd); | ||
154 | |||
155 | /* Reset ECC hardware */ | ||
156 | nand_davinci_readecc_1bit(mtd); | ||
157 | |||
158 | spin_lock_irqsave(&davinci_nand_lock, flags); | ||
159 | |||
160 | /* Restart ECC hardware */ | ||
161 | nandcfr = davinci_nand_readl(info, NANDFCR_OFFSET); | ||
162 | nandcfr |= BIT(8 + info->core_chipsel); | ||
163 | davinci_nand_writel(info, NANDFCR_OFFSET, nandcfr); | ||
164 | |||
165 | spin_unlock_irqrestore(&davinci_nand_lock, flags); | ||
166 | } | ||
167 | |||
168 | /* | ||
169 | * Read hardware ECC value and pack into three bytes | ||
170 | */ | ||
171 | static int nand_davinci_calculate_1bit(struct mtd_info *mtd, | ||
172 | const u_char *dat, u_char *ecc_code) | ||
173 | { | ||
174 | unsigned int ecc_val = nand_davinci_readecc_1bit(mtd); | ||
175 | unsigned int ecc24 = (ecc_val & 0x0fff) | ((ecc_val & 0x0fff0000) >> 4); | ||
176 | |||
177 | /* invert so that erased block ecc is correct */ | ||
178 | ecc24 = ~ecc24; | ||
179 | ecc_code[0] = (u_char)(ecc24); | ||
180 | ecc_code[1] = (u_char)(ecc24 >> 8); | ||
181 | ecc_code[2] = (u_char)(ecc24 >> 16); | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat, | ||
187 | u_char *read_ecc, u_char *calc_ecc) | ||
188 | { | ||
189 | struct nand_chip *chip = mtd->priv; | ||
190 | uint32_t eccNand = read_ecc[0] | (read_ecc[1] << 8) | | ||
191 | (read_ecc[2] << 16); | ||
192 | uint32_t eccCalc = calc_ecc[0] | (calc_ecc[1] << 8) | | ||
193 | (calc_ecc[2] << 16); | ||
194 | uint32_t diff = eccCalc ^ eccNand; | ||
195 | |||
196 | if (diff) { | ||
197 | if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) { | ||
198 | /* Correctable error */ | ||
199 | if ((diff >> (12 + 3)) < chip->ecc.size) { | ||
200 | dat[diff >> (12 + 3)] ^= BIT((diff >> 12) & 7); | ||
201 | return 1; | ||
202 | } else { | ||
203 | return -1; | ||
204 | } | ||
205 | } else if (!(diff & (diff - 1))) { | ||
206 | /* Single bit ECC error in the ECC itself, | ||
207 | * nothing to fix */ | ||
208 | return 1; | ||
209 | } else { | ||
210 | /* Uncorrectable error */ | ||
211 | return -1; | ||
212 | } | ||
213 | |||
214 | } | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | /*----------------------------------------------------------------------*/ | ||
219 | |||
220 | /* | ||
221 | * NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's | ||
222 | * how these chips are normally wired. This translates to both 8 and 16 | ||
223 | * bit busses using ALE == BIT(3) in byte addresses, and CLE == BIT(4). | ||
224 | * | ||
225 | * For now we assume that configuration, or any other one which ignores | ||
226 | * the two LSBs for NAND access ... so we can issue 32-bit reads/writes | ||
227 | * and have that transparently morphed into multiple NAND operations. | ||
228 | */ | ||
229 | static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) | ||
230 | { | ||
231 | struct nand_chip *chip = mtd->priv; | ||
232 | |||
233 | if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0) | ||
234 | ioread32_rep(chip->IO_ADDR_R, buf, len >> 2); | ||
235 | else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0) | ||
236 | ioread16_rep(chip->IO_ADDR_R, buf, len >> 1); | ||
237 | else | ||
238 | ioread8_rep(chip->IO_ADDR_R, buf, len); | ||
239 | } | ||
240 | |||
241 | static void nand_davinci_write_buf(struct mtd_info *mtd, | ||
242 | const uint8_t *buf, int len) | ||
243 | { | ||
244 | struct nand_chip *chip = mtd->priv; | ||
245 | |||
246 | if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0) | ||
247 | iowrite32_rep(chip->IO_ADDR_R, buf, len >> 2); | ||
248 | else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0) | ||
249 | iowrite16_rep(chip->IO_ADDR_R, buf, len >> 1); | ||
250 | else | ||
251 | iowrite8_rep(chip->IO_ADDR_R, buf, len); | ||
252 | } | ||
253 | |||
254 | /* | ||
255 | * Check hardware register for wait status. Returns 1 if device is ready, | ||
256 | * 0 if it is still busy. | ||
257 | */ | ||
258 | static int nand_davinci_dev_ready(struct mtd_info *mtd) | ||
259 | { | ||
260 | struct davinci_nand_info *info = to_davinci_nand(mtd); | ||
261 | |||
262 | return davinci_nand_readl(info, NANDFSR_OFFSET) & BIT(0); | ||
263 | } | ||
264 | |||
265 | static void __init nand_dm6446evm_flash_init(struct davinci_nand_info *info) | ||
266 | { | ||
267 | uint32_t regval, a1cr; | ||
268 | |||
269 | /* | ||
270 | * NAND FLASH timings @ PLL1 == 459 MHz | ||
271 | * - AEMIF.CLK freq = PLL1/6 = 459/6 = 76.5 MHz | ||
272 | * - AEMIF.CLK period = 1/76.5 MHz = 13.1 ns | ||
273 | */ | ||
274 | regval = 0 | ||
275 | | (0 << 31) /* selectStrobe */ | ||
276 | | (0 << 30) /* extWait (never with NAND) */ | ||
277 | | (1 << 26) /* writeSetup 10 ns */ | ||
278 | | (3 << 20) /* writeStrobe 40 ns */ | ||
279 | | (1 << 17) /* writeHold 10 ns */ | ||
280 | | (0 << 13) /* readSetup 10 ns */ | ||
281 | | (3 << 7) /* readStrobe 60 ns */ | ||
282 | | (0 << 4) /* readHold 10 ns */ | ||
283 | | (3 << 2) /* turnAround ?? ns */ | ||
284 | | (0 << 0) /* asyncSize 8-bit bus */ | ||
285 | ; | ||
286 | a1cr = davinci_nand_readl(info, A1CR_OFFSET); | ||
287 | if (a1cr != regval) { | ||
288 | dev_dbg(info->dev, "Warning: NAND config: Set A1CR " \ | ||
289 | "reg to 0x%08x, was 0x%08x, should be done by " \ | ||
290 | "bootloader.\n", regval, a1cr); | ||
291 | davinci_nand_writel(info, A1CR_OFFSET, regval); | ||
292 | } | ||
293 | } | ||
294 | |||
295 | /*----------------------------------------------------------------------*/ | ||
296 | |||
297 | static int __init nand_davinci_probe(struct platform_device *pdev) | ||
298 | { | ||
299 | struct davinci_nand_pdata *pdata = pdev->dev.platform_data; | ||
300 | struct davinci_nand_info *info; | ||
301 | struct resource *res1; | ||
302 | struct resource *res2; | ||
303 | void __iomem *vaddr; | ||
304 | void __iomem *base; | ||
305 | int ret; | ||
306 | uint32_t val; | ||
307 | nand_ecc_modes_t ecc_mode; | ||
308 | |||
309 | /* which external chipselect will we be managing? */ | ||
310 | if (pdev->id < 0 || pdev->id > 3) | ||
311 | return -ENODEV; | ||
312 | |||
313 | info = kzalloc(sizeof(*info), GFP_KERNEL); | ||
314 | if (!info) { | ||
315 | dev_err(&pdev->dev, "unable to allocate memory\n"); | ||
316 | ret = -ENOMEM; | ||
317 | goto err_nomem; | ||
318 | } | ||
319 | |||
320 | platform_set_drvdata(pdev, info); | ||
321 | |||
322 | res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
323 | res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
324 | if (!res1 || !res2) { | ||
325 | dev_err(&pdev->dev, "resource missing\n"); | ||
326 | ret = -EINVAL; | ||
327 | goto err_nomem; | ||
328 | } | ||
329 | |||
330 | vaddr = ioremap(res1->start, res1->end - res1->start); | ||
331 | base = ioremap(res2->start, res2->end - res2->start); | ||
332 | if (!vaddr || !base) { | ||
333 | dev_err(&pdev->dev, "ioremap failed\n"); | ||
334 | ret = -EINVAL; | ||
335 | goto err_ioremap; | ||
336 | } | ||
337 | |||
338 | info->dev = &pdev->dev; | ||
339 | info->base = base; | ||
340 | info->vaddr = vaddr; | ||
341 | |||
342 | info->mtd.priv = &info->chip; | ||
343 | info->mtd.name = dev_name(&pdev->dev); | ||
344 | info->mtd.owner = THIS_MODULE; | ||
345 | |||
346 | info->mtd.dev.parent = &pdev->dev; | ||
347 | |||
348 | info->chip.IO_ADDR_R = vaddr; | ||
349 | info->chip.IO_ADDR_W = vaddr; | ||
350 | info->chip.chip_delay = 0; | ||
351 | info->chip.select_chip = nand_davinci_select_chip; | ||
352 | |||
353 | /* options such as NAND_USE_FLASH_BBT or 16-bit widths */ | ||
354 | info->chip.options = pdata ? pdata->options : 0; | ||
355 | |||
356 | info->ioaddr = (uint32_t __force) vaddr; | ||
357 | |||
358 | info->current_cs = info->ioaddr; | ||
359 | info->core_chipsel = pdev->id; | ||
360 | info->mask_chipsel = pdata->mask_chipsel; | ||
361 | |||
362 | /* use nandboot-capable ALE/CLE masks by default */ | ||
363 | if (pdata && pdata->mask_ale) | ||
364 | info->mask_ale = pdata->mask_cle; | ||
365 | else | ||
366 | info->mask_ale = MASK_ALE; | ||
367 | if (pdata && pdata->mask_cle) | ||
368 | info->mask_cle = pdata->mask_cle; | ||
369 | else | ||
370 | info->mask_cle = MASK_CLE; | ||
371 | |||
372 | /* Set address of hardware control function */ | ||
373 | info->chip.cmd_ctrl = nand_davinci_hwcontrol; | ||
374 | info->chip.dev_ready = nand_davinci_dev_ready; | ||
375 | |||
376 | /* Speed up buffer I/O */ | ||
377 | info->chip.read_buf = nand_davinci_read_buf; | ||
378 | info->chip.write_buf = nand_davinci_write_buf; | ||
379 | |||
380 | /* use board-specific ECC config; else, the best available */ | ||
381 | if (pdata) | ||
382 | ecc_mode = pdata->ecc_mode; | ||
383 | else | ||
384 | ecc_mode = NAND_ECC_HW; | ||
385 | |||
386 | switch (ecc_mode) { | ||
387 | case NAND_ECC_NONE: | ||
388 | case NAND_ECC_SOFT: | ||
389 | break; | ||
390 | case NAND_ECC_HW: | ||
391 | info->chip.ecc.calculate = nand_davinci_calculate_1bit; | ||
392 | info->chip.ecc.correct = nand_davinci_correct_1bit; | ||
393 | info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; | ||
394 | info->chip.ecc.size = 512; | ||
395 | info->chip.ecc.bytes = 3; | ||
396 | break; | ||
397 | case NAND_ECC_HW_SYNDROME: | ||
398 | /* FIXME implement */ | ||
399 | info->chip.ecc.size = 512; | ||
400 | info->chip.ecc.bytes = 10; | ||
401 | |||
402 | dev_warn(&pdev->dev, "4-bit ECC nyet supported\n"); | ||
403 | /* FALL THROUGH */ | ||
404 | default: | ||
405 | ret = -EINVAL; | ||
406 | goto err_ecc; | ||
407 | } | ||
408 | info->chip.ecc.mode = ecc_mode; | ||
409 | |||
410 | info->clk = clk_get(&pdev->dev, "AEMIFCLK"); | ||
411 | if (IS_ERR(info->clk)) { | ||
412 | ret = PTR_ERR(info->clk); | ||
413 | dev_dbg(&pdev->dev, "unable to get AEMIFCLK, err %d\n", ret); | ||
414 | goto err_clk; | ||
415 | } | ||
416 | |||
417 | ret = clk_enable(info->clk); | ||
418 | if (ret < 0) { | ||
419 | dev_dbg(&pdev->dev, "unable to enable AEMIFCLK, err %d\n", ret); | ||
420 | goto err_clk_enable; | ||
421 | } | ||
422 | |||
423 | /* EMIF timings should normally be set by the boot loader, | ||
424 | * especially after boot-from-NAND. The *only* reason to | ||
425 | * have this special casing for the DM6446 EVM is to work | ||
426 | * with boot-from-NOR ... with CS0 manually re-jumpered | ||
427 | * (after startup) so it addresses the NAND flash, not NOR. | ||
428 | * Even for dev boards, that's unusually rude... | ||
429 | */ | ||
430 | if (machine_is_davinci_evm()) | ||
431 | nand_dm6446evm_flash_init(info); | ||
432 | |||
433 | spin_lock_irq(&davinci_nand_lock); | ||
434 | |||
435 | /* put CSxNAND into NAND mode */ | ||
436 | val = davinci_nand_readl(info, NANDFCR_OFFSET); | ||
437 | val |= BIT(info->core_chipsel); | ||
438 | davinci_nand_writel(info, NANDFCR_OFFSET, val); | ||
439 | |||
440 | spin_unlock_irq(&davinci_nand_lock); | ||
441 | |||
442 | /* Scan to find existence of the device(s) */ | ||
443 | ret = nand_scan(&info->mtd, pdata->mask_chipsel ? 2 : 1); | ||
444 | if (ret < 0) { | ||
445 | dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); | ||
446 | goto err_scan; | ||
447 | } | ||
448 | |||
449 | if (mtd_has_partitions()) { | ||
450 | struct mtd_partition *mtd_parts = NULL; | ||
451 | int mtd_parts_nb = 0; | ||
452 | |||
453 | if (mtd_has_cmdlinepart()) { | ||
454 | static const char *probes[] __initconst = | ||
455 | { "cmdlinepart", NULL }; | ||
456 | |||
457 | const char *master_name; | ||
458 | |||
459 | /* Set info->mtd.name = 0 temporarily */ | ||
460 | master_name = info->mtd.name; | ||
461 | info->mtd.name = (char *)0; | ||
462 | |||
463 | /* info->mtd.name == 0, means: don't bother checking | ||
464 | <mtd-id> */ | ||
465 | mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes, | ||
466 | &mtd_parts, 0); | ||
467 | |||
468 | /* Restore info->mtd.name */ | ||
469 | info->mtd.name = master_name; | ||
470 | } | ||
471 | |||
472 | if (mtd_parts_nb <= 0 && pdata) { | ||
473 | mtd_parts = pdata->parts; | ||
474 | mtd_parts_nb = pdata->nr_parts; | ||
475 | } | ||
476 | |||
477 | /* Register any partitions */ | ||
478 | if (mtd_parts_nb > 0) { | ||
479 | ret = add_mtd_partitions(&info->mtd, | ||
480 | mtd_parts, mtd_parts_nb); | ||
481 | if (ret == 0) | ||
482 | info->partitioned = true; | ||
483 | } | ||
484 | |||
485 | } else if (pdata && pdata->nr_parts) { | ||
486 | dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n", | ||
487 | pdata->nr_parts, info->mtd.name); | ||
488 | } | ||
489 | |||
490 | /* If there's no partition info, just package the whole chip | ||
491 | * as a single MTD device. | ||
492 | */ | ||
493 | if (!info->partitioned) | ||
494 | ret = add_mtd_device(&info->mtd) ? -ENODEV : 0; | ||
495 | |||
496 | if (ret < 0) | ||
497 | goto err_scan; | ||
498 | |||
499 | val = davinci_nand_readl(info, NRCSR_OFFSET); | ||
500 | dev_info(&pdev->dev, "controller rev. %d.%d\n", | ||
501 | (val >> 8) & 0xff, val & 0xff); | ||
502 | |||
503 | return 0; | ||
504 | |||
505 | err_scan: | ||
506 | clk_disable(info->clk); | ||
507 | |||
508 | err_clk_enable: | ||
509 | clk_put(info->clk); | ||
510 | |||
511 | err_ecc: | ||
512 | err_clk: | ||
513 | err_ioremap: | ||
514 | if (base) | ||
515 | iounmap(base); | ||
516 | if (vaddr) | ||
517 | iounmap(vaddr); | ||
518 | |||
519 | err_nomem: | ||
520 | kfree(info); | ||
521 | return ret; | ||
522 | } | ||
523 | |||
524 | static int __exit nand_davinci_remove(struct platform_device *pdev) | ||
525 | { | ||
526 | struct davinci_nand_info *info = platform_get_drvdata(pdev); | ||
527 | int status; | ||
528 | |||
529 | if (mtd_has_partitions() && info->partitioned) | ||
530 | status = del_mtd_partitions(&info->mtd); | ||
531 | else | ||
532 | status = del_mtd_device(&info->mtd); | ||
533 | |||
534 | iounmap(info->base); | ||
535 | iounmap(info->vaddr); | ||
536 | |||
537 | nand_release(&info->mtd); | ||
538 | |||
539 | clk_disable(info->clk); | ||
540 | clk_put(info->clk); | ||
541 | |||
542 | kfree(info); | ||
543 | |||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | static struct platform_driver nand_davinci_driver = { | ||
548 | .remove = __exit_p(nand_davinci_remove), | ||
549 | .driver = { | ||
550 | .name = "davinci_nand", | ||
551 | }, | ||
552 | }; | ||
553 | MODULE_ALIAS("platform:davinci_nand"); | ||
554 | |||
555 | static int __init nand_davinci_init(void) | ||
556 | { | ||
557 | return platform_driver_probe(&nand_davinci_driver, nand_davinci_probe); | ||
558 | } | ||
559 | module_init(nand_davinci_init); | ||
560 | |||
561 | static void __exit nand_davinci_exit(void) | ||
562 | { | ||
563 | platform_driver_unregister(&nand_davinci_driver); | ||
564 | } | ||
565 | module_exit(nand_davinci_exit); | ||
566 | |||
567 | MODULE_LICENSE("GPL"); | ||
568 | MODULE_AUTHOR("Texas Instruments"); | ||
569 | MODULE_DESCRIPTION("Davinci NAND flash driver"); | ||
570 | |||
diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index e4226e02d63e..e51c1ed7ac18 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c | |||
@@ -1773,4 +1773,4 @@ module_exit(cleanup_nanddoc); | |||
1773 | 1773 | ||
1774 | MODULE_LICENSE("GPL"); | 1774 | MODULE_LICENSE("GPL"); |
1775 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | 1775 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); |
1776 | MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver\n"); | 1776 | MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver"); |
diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c index ced14b5294d5..72446fb48d4b 100644 --- a/drivers/mtd/nand/excite_nandflash.c +++ b/drivers/mtd/nand/excite_nandflash.c | |||
@@ -128,11 +128,11 @@ static int excite_nand_devready(struct mtd_info *mtd) | |||
128 | * The binding to the mtd and all allocated | 128 | * The binding to the mtd and all allocated |
129 | * resources are released. | 129 | * resources are released. |
130 | */ | 130 | */ |
131 | static int __exit excite_nand_remove(struct device *dev) | 131 | static int __exit excite_nand_remove(struct platform_device *dev) |
132 | { | 132 | { |
133 | struct excite_nand_drvdata * const this = dev_get_drvdata(dev); | 133 | struct excite_nand_drvdata * const this = platform_get_drvdata(dev); |
134 | 134 | ||
135 | dev_set_drvdata(dev, NULL); | 135 | platform_set_drvdata(dev, NULL); |
136 | 136 | ||
137 | if (unlikely(!this)) { | 137 | if (unlikely(!this)) { |
138 | printk(KERN_ERR "%s: called %s without private data!!", | 138 | printk(KERN_ERR "%s: called %s without private data!!", |
@@ -159,9 +159,8 @@ static int __exit excite_nand_remove(struct device *dev) | |||
159 | * it can allocate all necessary resources then calls the | 159 | * it can allocate all necessary resources then calls the |
160 | * nand layer to look for devices. | 160 | * nand layer to look for devices. |
161 | */ | 161 | */ |
162 | static int __init excite_nand_probe(struct device *dev) | 162 | static int __init excite_nand_probe(struct platform_device *pdev) |
163 | { | 163 | { |
164 | struct platform_device * const pdev = to_platform_device(dev); | ||
165 | struct excite_nand_drvdata *drvdata; /* private driver data */ | 164 | struct excite_nand_drvdata *drvdata; /* private driver data */ |
166 | struct nand_chip *board_chip; /* private flash chip data */ | 165 | struct nand_chip *board_chip; /* private flash chip data */ |
167 | struct mtd_info *board_mtd; /* mtd info for this board */ | 166 | struct mtd_info *board_mtd; /* mtd info for this board */ |
@@ -175,7 +174,7 @@ static int __init excite_nand_probe(struct device *dev) | |||
175 | } | 174 | } |
176 | 175 | ||
177 | /* bind private data into driver */ | 176 | /* bind private data into driver */ |
178 | dev_set_drvdata(dev, drvdata); | 177 | platform_set_drvdata(pdev, drvdata); |
179 | 178 | ||
180 | /* allocate and map the resource */ | 179 | /* allocate and map the resource */ |
181 | drvdata->regs = | 180 | drvdata->regs = |
@@ -219,23 +218,25 @@ static int __init excite_nand_probe(struct device *dev) | |||
219 | return 0; | 218 | return 0; |
220 | } | 219 | } |
221 | 220 | ||
222 | static struct device_driver excite_nand_driver = { | 221 | static struct platform_driver excite_nand_driver = { |
223 | .name = "excite_nand", | 222 | .driver = { |
224 | .bus = &platform_bus_type, | 223 | .name = "excite_nand", |
224 | .owner = THIS_MODULE, | ||
225 | }, | ||
225 | .probe = excite_nand_probe, | 226 | .probe = excite_nand_probe, |
226 | .remove = __exit_p(excite_nand_remove) | 227 | .remove = __devexit_p(excite_nand_remove) |
227 | }; | 228 | }; |
228 | 229 | ||
229 | static int __init excite_nand_init(void) | 230 | static int __init excite_nand_init(void) |
230 | { | 231 | { |
231 | pr_info("Basler eXcite nand flash driver Version " | 232 | pr_info("Basler eXcite nand flash driver Version " |
232 | EXCITE_NANDFLASH_VERSION "\n"); | 233 | EXCITE_NANDFLASH_VERSION "\n"); |
233 | return driver_register(&excite_nand_driver); | 234 | return platform_driver_register(&excite_nand_driver); |
234 | } | 235 | } |
235 | 236 | ||
236 | static void __exit excite_nand_exit(void) | 237 | static void __exit excite_nand_exit(void) |
237 | { | 238 | { |
238 | driver_unregister(&excite_nand_driver); | 239 | platform_driver_unregister(&excite_nand_driver); |
239 | } | 240 | } |
240 | 241 | ||
241 | module_init(excite_nand_init); | 242 | module_init(excite_nand_init); |
diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 7815a404a632..d120cd8d7267 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c | |||
@@ -23,6 +23,10 @@ | |||
23 | #include <linux/io.h> | 23 | #include <linux/io.h> |
24 | #include <asm/fsl_lbc.h> | 24 | #include <asm/fsl_lbc.h> |
25 | 25 | ||
26 | #define FSL_UPM_WAIT_RUN_PATTERN 0x1 | ||
27 | #define FSL_UPM_WAIT_WRITE_BYTE 0x2 | ||
28 | #define FSL_UPM_WAIT_WRITE_BUFFER 0x4 | ||
29 | |||
26 | struct fsl_upm_nand { | 30 | struct fsl_upm_nand { |
27 | struct device *dev; | 31 | struct device *dev; |
28 | struct mtd_info mtd; | 32 | struct mtd_info mtd; |
@@ -36,8 +40,12 @@ struct fsl_upm_nand { | |||
36 | uint8_t upm_addr_offset; | 40 | uint8_t upm_addr_offset; |
37 | uint8_t upm_cmd_offset; | 41 | uint8_t upm_cmd_offset; |
38 | void __iomem *io_base; | 42 | void __iomem *io_base; |
39 | int rnb_gpio; | 43 | int rnb_gpio[NAND_MAX_CHIPS]; |
44 | uint32_t mchip_offsets[NAND_MAX_CHIPS]; | ||
45 | uint32_t mchip_count; | ||
46 | uint32_t mchip_number; | ||
40 | int chip_delay; | 47 | int chip_delay; |
48 | uint32_t wait_flags; | ||
41 | }; | 49 | }; |
42 | 50 | ||
43 | #define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd) | 51 | #define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd) |
@@ -46,7 +54,7 @@ static int fun_chip_ready(struct mtd_info *mtd) | |||
46 | { | 54 | { |
47 | struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); | 55 | struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); |
48 | 56 | ||
49 | if (gpio_get_value(fun->rnb_gpio)) | 57 | if (gpio_get_value(fun->rnb_gpio[fun->mchip_number])) |
50 | return 1; | 58 | return 1; |
51 | 59 | ||
52 | dev_vdbg(fun->dev, "busy\n"); | 60 | dev_vdbg(fun->dev, "busy\n"); |
@@ -55,9 +63,9 @@ static int fun_chip_ready(struct mtd_info *mtd) | |||
55 | 63 | ||
56 | static void fun_wait_rnb(struct fsl_upm_nand *fun) | 64 | static void fun_wait_rnb(struct fsl_upm_nand *fun) |
57 | { | 65 | { |
58 | int cnt = 1000000; | 66 | if (fun->rnb_gpio[fun->mchip_number] >= 0) { |
67 | int cnt = 1000000; | ||
59 | 68 | ||
60 | if (fun->rnb_gpio >= 0) { | ||
61 | while (--cnt && !fun_chip_ready(&fun->mtd)) | 69 | while (--cnt && !fun_chip_ready(&fun->mtd)) |
62 | cpu_relax(); | 70 | cpu_relax(); |
63 | if (!cnt) | 71 | if (!cnt) |
@@ -69,7 +77,9 @@ static void fun_wait_rnb(struct fsl_upm_nand *fun) | |||
69 | 77 | ||
70 | static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) | 78 | static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) |
71 | { | 79 | { |
80 | struct nand_chip *chip = mtd->priv; | ||
72 | struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); | 81 | struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); |
82 | u32 mar; | ||
73 | 83 | ||
74 | if (!(ctrl & fun->last_ctrl)) { | 84 | if (!(ctrl & fun->last_ctrl)) { |
75 | fsl_upm_end_pattern(&fun->upm); | 85 | fsl_upm_end_pattern(&fun->upm); |
@@ -87,9 +97,28 @@ static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) | |||
87 | fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); | 97 | fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); |
88 | } | 98 | } |
89 | 99 | ||
90 | fsl_upm_run_pattern(&fun->upm, fun->io_base, cmd); | 100 | mar = (cmd << (32 - fun->upm.width)) | |
101 | fun->mchip_offsets[fun->mchip_number]; | ||
102 | fsl_upm_run_pattern(&fun->upm, chip->IO_ADDR_R, mar); | ||
103 | |||
104 | if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN) | ||
105 | fun_wait_rnb(fun); | ||
106 | } | ||
107 | |||
108 | static void fun_select_chip(struct mtd_info *mtd, int mchip_nr) | ||
109 | { | ||
110 | struct nand_chip *chip = mtd->priv; | ||
111 | struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); | ||
91 | 112 | ||
92 | fun_wait_rnb(fun); | 113 | if (mchip_nr == -1) { |
114 | chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); | ||
115 | } else if (mchip_nr >= 0) { | ||
116 | fun->mchip_number = mchip_nr; | ||
117 | chip->IO_ADDR_R = fun->io_base + fun->mchip_offsets[mchip_nr]; | ||
118 | chip->IO_ADDR_W = chip->IO_ADDR_R; | ||
119 | } else { | ||
120 | BUG(); | ||
121 | } | ||
93 | } | 122 | } |
94 | 123 | ||
95 | static uint8_t fun_read_byte(struct mtd_info *mtd) | 124 | static uint8_t fun_read_byte(struct mtd_info *mtd) |
@@ -115,8 +144,11 @@ static void fun_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) | |||
115 | 144 | ||
116 | for (i = 0; i < len; i++) { | 145 | for (i = 0; i < len; i++) { |
117 | out_8(fun->chip.IO_ADDR_W, buf[i]); | 146 | out_8(fun->chip.IO_ADDR_W, buf[i]); |
118 | fun_wait_rnb(fun); | 147 | if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE) |
148 | fun_wait_rnb(fun); | ||
119 | } | 149 | } |
150 | if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER) | ||
151 | fun_wait_rnb(fun); | ||
120 | } | 152 | } |
121 | 153 | ||
122 | static int __devinit fun_chip_init(struct fsl_upm_nand *fun, | 154 | static int __devinit fun_chip_init(struct fsl_upm_nand *fun, |
@@ -137,8 +169,10 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun, | |||
137 | fun->chip.read_buf = fun_read_buf; | 169 | fun->chip.read_buf = fun_read_buf; |
138 | fun->chip.write_buf = fun_write_buf; | 170 | fun->chip.write_buf = fun_write_buf; |
139 | fun->chip.ecc.mode = NAND_ECC_SOFT; | 171 | fun->chip.ecc.mode = NAND_ECC_SOFT; |
172 | if (fun->mchip_count > 1) | ||
173 | fun->chip.select_chip = fun_select_chip; | ||
140 | 174 | ||
141 | if (fun->rnb_gpio >= 0) | 175 | if (fun->rnb_gpio[0] >= 0) |
142 | fun->chip.dev_ready = fun_chip_ready; | 176 | fun->chip.dev_ready = fun_chip_ready; |
143 | 177 | ||
144 | fun->mtd.priv = &fun->chip; | 178 | fun->mtd.priv = &fun->chip; |
@@ -155,7 +189,7 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun, | |||
155 | goto err; | 189 | goto err; |
156 | } | 190 | } |
157 | 191 | ||
158 | ret = nand_scan(&fun->mtd, 1); | 192 | ret = nand_scan(&fun->mtd, fun->mchip_count); |
159 | if (ret) | 193 | if (ret) |
160 | goto err; | 194 | goto err; |
161 | 195 | ||
@@ -185,8 +219,10 @@ static int __devinit fun_probe(struct of_device *ofdev, | |||
185 | struct fsl_upm_nand *fun; | 219 | struct fsl_upm_nand *fun; |
186 | struct resource io_res; | 220 | struct resource io_res; |
187 | const uint32_t *prop; | 221 | const uint32_t *prop; |
222 | int rnb_gpio; | ||
188 | int ret; | 223 | int ret; |
189 | int size; | 224 | int size; |
225 | int i; | ||
190 | 226 | ||
191 | fun = kzalloc(sizeof(*fun), GFP_KERNEL); | 227 | fun = kzalloc(sizeof(*fun), GFP_KERNEL); |
192 | if (!fun) | 228 | if (!fun) |
@@ -208,7 +244,7 @@ static int __devinit fun_probe(struct of_device *ofdev, | |||
208 | if (!prop || size != sizeof(uint32_t)) { | 244 | if (!prop || size != sizeof(uint32_t)) { |
209 | dev_err(&ofdev->dev, "can't get UPM address offset\n"); | 245 | dev_err(&ofdev->dev, "can't get UPM address offset\n"); |
210 | ret = -EINVAL; | 246 | ret = -EINVAL; |
211 | goto err2; | 247 | goto err1; |
212 | } | 248 | } |
213 | fun->upm_addr_offset = *prop; | 249 | fun->upm_addr_offset = *prop; |
214 | 250 | ||
@@ -216,21 +252,40 @@ static int __devinit fun_probe(struct of_device *ofdev, | |||
216 | if (!prop || size != sizeof(uint32_t)) { | 252 | if (!prop || size != sizeof(uint32_t)) { |
217 | dev_err(&ofdev->dev, "can't get UPM command offset\n"); | 253 | dev_err(&ofdev->dev, "can't get UPM command offset\n"); |
218 | ret = -EINVAL; | 254 | ret = -EINVAL; |
219 | goto err2; | 255 | goto err1; |
220 | } | 256 | } |
221 | fun->upm_cmd_offset = *prop; | 257 | fun->upm_cmd_offset = *prop; |
222 | 258 | ||
223 | fun->rnb_gpio = of_get_gpio(ofdev->node, 0); | 259 | prop = of_get_property(ofdev->node, |
224 | if (fun->rnb_gpio >= 0) { | 260 | "fsl,upm-addr-line-cs-offsets", &size); |
225 | ret = gpio_request(fun->rnb_gpio, dev_name(&ofdev->dev)); | 261 | if (prop && (size / sizeof(uint32_t)) > 0) { |
226 | if (ret) { | 262 | fun->mchip_count = size / sizeof(uint32_t); |
227 | dev_err(&ofdev->dev, "can't request RNB gpio\n"); | 263 | if (fun->mchip_count >= NAND_MAX_CHIPS) { |
264 | dev_err(&ofdev->dev, "too much multiple chips\n"); | ||
265 | goto err1; | ||
266 | } | ||
267 | for (i = 0; i < fun->mchip_count; i++) | ||
268 | fun->mchip_offsets[i] = prop[i]; | ||
269 | } else { | ||
270 | fun->mchip_count = 1; | ||
271 | } | ||
272 | |||
273 | for (i = 0; i < fun->mchip_count; i++) { | ||
274 | fun->rnb_gpio[i] = -1; | ||
275 | rnb_gpio = of_get_gpio(ofdev->node, i); | ||
276 | if (rnb_gpio >= 0) { | ||
277 | ret = gpio_request(rnb_gpio, dev_name(&ofdev->dev)); | ||
278 | if (ret) { | ||
279 | dev_err(&ofdev->dev, | ||
280 | "can't request RNB gpio #%d\n", i); | ||
281 | goto err2; | ||
282 | } | ||
283 | gpio_direction_input(rnb_gpio); | ||
284 | fun->rnb_gpio[i] = rnb_gpio; | ||
285 | } else if (rnb_gpio == -EINVAL) { | ||
286 | dev_err(&ofdev->dev, "RNB gpio #%d is invalid\n", i); | ||
228 | goto err2; | 287 | goto err2; |
229 | } | 288 | } |
230 | gpio_direction_input(fun->rnb_gpio); | ||
231 | } else if (fun->rnb_gpio == -EINVAL) { | ||
232 | dev_err(&ofdev->dev, "specified RNB gpio is invalid\n"); | ||
233 | goto err2; | ||
234 | } | 289 | } |
235 | 290 | ||
236 | prop = of_get_property(ofdev->node, "chip-delay", NULL); | 291 | prop = of_get_property(ofdev->node, "chip-delay", NULL); |
@@ -239,8 +294,15 @@ static int __devinit fun_probe(struct of_device *ofdev, | |||
239 | else | 294 | else |
240 | fun->chip_delay = 50; | 295 | fun->chip_delay = 50; |
241 | 296 | ||
297 | prop = of_get_property(ofdev->node, "fsl,upm-wait-flags", &size); | ||
298 | if (prop && size == sizeof(uint32_t)) | ||
299 | fun->wait_flags = *prop; | ||
300 | else | ||
301 | fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN | | ||
302 | FSL_UPM_WAIT_WRITE_BYTE; | ||
303 | |||
242 | fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start, | 304 | fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start, |
243 | io_res.end - io_res.start + 1); | 305 | io_res.end - io_res.start + 1); |
244 | if (!fun->io_base) { | 306 | if (!fun->io_base) { |
245 | ret = -ENOMEM; | 307 | ret = -ENOMEM; |
246 | goto err2; | 308 | goto err2; |
@@ -257,8 +319,11 @@ static int __devinit fun_probe(struct of_device *ofdev, | |||
257 | 319 | ||
258 | return 0; | 320 | return 0; |
259 | err2: | 321 | err2: |
260 | if (fun->rnb_gpio >= 0) | 322 | for (i = 0; i < fun->mchip_count; i++) { |
261 | gpio_free(fun->rnb_gpio); | 323 | if (fun->rnb_gpio[i] < 0) |
324 | break; | ||
325 | gpio_free(fun->rnb_gpio[i]); | ||
326 | } | ||
262 | err1: | 327 | err1: |
263 | kfree(fun); | 328 | kfree(fun); |
264 | 329 | ||
@@ -268,12 +333,16 @@ err1: | |||
268 | static int __devexit fun_remove(struct of_device *ofdev) | 333 | static int __devexit fun_remove(struct of_device *ofdev) |
269 | { | 334 | { |
270 | struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev); | 335 | struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev); |
336 | int i; | ||
271 | 337 | ||
272 | nand_release(&fun->mtd); | 338 | nand_release(&fun->mtd); |
273 | kfree(fun->mtd.name); | 339 | kfree(fun->mtd.name); |
274 | 340 | ||
275 | if (fun->rnb_gpio >= 0) | 341 | for (i = 0; i < fun->mchip_count; i++) { |
276 | gpio_free(fun->rnb_gpio); | 342 | if (fun->rnb_gpio[i] < 0) |
343 | break; | ||
344 | gpio_free(fun->rnb_gpio[i]); | ||
345 | } | ||
277 | 346 | ||
278 | kfree(fun); | 347 | kfree(fun); |
279 | 348 | ||
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 21fd4f1c4806..f3548d048014 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c | |||
@@ -866,6 +866,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) | |||
866 | mtd = &host->mtd; | 866 | mtd = &host->mtd; |
867 | mtd->priv = this; | 867 | mtd->priv = this; |
868 | mtd->owner = THIS_MODULE; | 868 | mtd->owner = THIS_MODULE; |
869 | mtd->dev.parent = &pdev->dev; | ||
869 | 870 | ||
870 | /* 50 us command delay time */ | 871 | /* 50 us command delay time */ |
871 | this->chip_delay = 5; | 872 | this->chip_delay = 5; |
@@ -880,7 +881,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) | |||
880 | this->read_buf = mxc_nand_read_buf; | 881 | this->read_buf = mxc_nand_read_buf; |
881 | this->verify_buf = mxc_nand_verify_buf; | 882 | this->verify_buf = mxc_nand_verify_buf; |
882 | 883 | ||
883 | host->clk = clk_get(&pdev->dev, "nfc_clk"); | 884 | host->clk = clk_get(&pdev->dev, "nfc"); |
884 | if (IS_ERR(host->clk)) | 885 | if (IS_ERR(host->clk)) |
885 | goto eclk; | 886 | goto eclk; |
886 | 887 | ||
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 0c3afccde8a2..3d7ed432fa41 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
@@ -82,6 +82,20 @@ static struct nand_ecclayout nand_oob_64 = { | |||
82 | .length = 38}} | 82 | .length = 38}} |
83 | }; | 83 | }; |
84 | 84 | ||
85 | static struct nand_ecclayout nand_oob_128 = { | ||
86 | .eccbytes = 48, | ||
87 | .eccpos = { | ||
88 | 80, 81, 82, 83, 84, 85, 86, 87, | ||
89 | 88, 89, 90, 91, 92, 93, 94, 95, | ||
90 | 96, 97, 98, 99, 100, 101, 102, 103, | ||
91 | 104, 105, 106, 107, 108, 109, 110, 111, | ||
92 | 112, 113, 114, 115, 116, 117, 118, 119, | ||
93 | 120, 121, 122, 123, 124, 125, 126, 127}, | ||
94 | .oobfree = { | ||
95 | {.offset = 2, | ||
96 | .length = 78}} | ||
97 | }; | ||
98 | |||
85 | static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, | 99 | static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, |
86 | int new_state); | 100 | int new_state); |
87 | 101 | ||
@@ -748,6 +762,8 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) | |||
748 | * @mtd: mtd info structure | 762 | * @mtd: mtd info structure |
749 | * @chip: nand chip info structure | 763 | * @chip: nand chip info structure |
750 | * @buf: buffer to store read data | 764 | * @buf: buffer to store read data |
765 | * | ||
766 | * Not for syndrome calculating ecc controllers, which use a special oob layout | ||
751 | */ | 767 | */ |
752 | static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, | 768 | static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, |
753 | uint8_t *buf) | 769 | uint8_t *buf) |
@@ -758,6 +774,47 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, | |||
758 | } | 774 | } |
759 | 775 | ||
760 | /** | 776 | /** |
777 | * nand_read_page_raw_syndrome - [Intern] read raw page data without ecc | ||
778 | * @mtd: mtd info structure | ||
779 | * @chip: nand chip info structure | ||
780 | * @buf: buffer to store read data | ||
781 | * | ||
782 | * We need a special oob layout and handling even when OOB isn't used. | ||
783 | */ | ||
784 | static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, | ||
785 | uint8_t *buf) | ||
786 | { | ||
787 | int eccsize = chip->ecc.size; | ||
788 | int eccbytes = chip->ecc.bytes; | ||
789 | uint8_t *oob = chip->oob_poi; | ||
790 | int steps, size; | ||
791 | |||
792 | for (steps = chip->ecc.steps; steps > 0; steps--) { | ||
793 | chip->read_buf(mtd, buf, eccsize); | ||
794 | buf += eccsize; | ||
795 | |||
796 | if (chip->ecc.prepad) { | ||
797 | chip->read_buf(mtd, oob, chip->ecc.prepad); | ||
798 | oob += chip->ecc.prepad; | ||
799 | } | ||
800 | |||
801 | chip->read_buf(mtd, oob, eccbytes); | ||
802 | oob += eccbytes; | ||
803 | |||
804 | if (chip->ecc.postpad) { | ||
805 | chip->read_buf(mtd, oob, chip->ecc.postpad); | ||
806 | oob += chip->ecc.postpad; | ||
807 | } | ||
808 | } | ||
809 | |||
810 | size = mtd->oobsize - (oob - chip->oob_poi); | ||
811 | if (size) | ||
812 | chip->read_buf(mtd, oob, size); | ||
813 | |||
814 | return 0; | ||
815 | } | ||
816 | |||
817 | /** | ||
761 | * nand_read_page_swecc - [REPLACABLE] software ecc based page read function | 818 | * nand_read_page_swecc - [REPLACABLE] software ecc based page read function |
762 | * @mtd: mtd info structure | 819 | * @mtd: mtd info structure |
763 | * @chip: nand chip info structure | 820 | * @chip: nand chip info structure |
@@ -1482,6 +1539,8 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, | |||
1482 | * @mtd: mtd info structure | 1539 | * @mtd: mtd info structure |
1483 | * @chip: nand chip info structure | 1540 | * @chip: nand chip info structure |
1484 | * @buf: data buffer | 1541 | * @buf: data buffer |
1542 | * | ||
1543 | * Not for syndrome calculating ecc controllers, which use a special oob layout | ||
1485 | */ | 1544 | */ |
1486 | static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, | 1545 | static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, |
1487 | const uint8_t *buf) | 1546 | const uint8_t *buf) |
@@ -1491,6 +1550,44 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, | |||
1491 | } | 1550 | } |
1492 | 1551 | ||
1493 | /** | 1552 | /** |
1553 | * nand_write_page_raw_syndrome - [Intern] raw page write function | ||
1554 | * @mtd: mtd info structure | ||
1555 | * @chip: nand chip info structure | ||
1556 | * @buf: data buffer | ||
1557 | * | ||
1558 | * We need a special oob layout and handling even when ECC isn't checked. | ||
1559 | */ | ||
1560 | static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, | ||
1561 | const uint8_t *buf) | ||
1562 | { | ||
1563 | int eccsize = chip->ecc.size; | ||
1564 | int eccbytes = chip->ecc.bytes; | ||
1565 | uint8_t *oob = chip->oob_poi; | ||
1566 | int steps, size; | ||
1567 | |||
1568 | for (steps = chip->ecc.steps; steps > 0; steps--) { | ||
1569 | chip->write_buf(mtd, buf, eccsize); | ||
1570 | buf += eccsize; | ||
1571 | |||
1572 | if (chip->ecc.prepad) { | ||
1573 | chip->write_buf(mtd, oob, chip->ecc.prepad); | ||
1574 | oob += chip->ecc.prepad; | ||
1575 | } | ||
1576 | |||
1577 | chip->read_buf(mtd, oob, eccbytes); | ||
1578 | oob += eccbytes; | ||
1579 | |||
1580 | if (chip->ecc.postpad) { | ||
1581 | chip->write_buf(mtd, oob, chip->ecc.postpad); | ||
1582 | oob += chip->ecc.postpad; | ||
1583 | } | ||
1584 | } | ||
1585 | |||
1586 | size = mtd->oobsize - (oob - chip->oob_poi); | ||
1587 | if (size) | ||
1588 | chip->write_buf(mtd, oob, size); | ||
1589 | } | ||
1590 | /** | ||
1494 | * nand_write_page_swecc - [REPLACABLE] software ecc based page write function | 1591 | * nand_write_page_swecc - [REPLACABLE] software ecc based page write function |
1495 | * @mtd: mtd info structure | 1592 | * @mtd: mtd info structure |
1496 | * @chip: nand chip info structure | 1593 | * @chip: nand chip info structure |
@@ -1863,7 +1960,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, | |||
1863 | } | 1960 | } |
1864 | 1961 | ||
1865 | if (unlikely(ops->ooboffs >= len)) { | 1962 | if (unlikely(ops->ooboffs >= len)) { |
1866 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | 1963 | DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: " |
1867 | "Attempt to start write outside oob\n"); | 1964 | "Attempt to start write outside oob\n"); |
1868 | return -EINVAL; | 1965 | return -EINVAL; |
1869 | } | 1966 | } |
@@ -1873,7 +1970,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, | |||
1873 | ops->ooboffs + ops->ooblen > | 1970 | ops->ooboffs + ops->ooblen > |
1874 | ((mtd->size >> chip->page_shift) - | 1971 | ((mtd->size >> chip->page_shift) - |
1875 | (to >> chip->page_shift)) * len)) { | 1972 | (to >> chip->page_shift)) * len)) { |
1876 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | 1973 | DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: " |
1877 | "Attempt write beyond end of device\n"); | 1974 | "Attempt write beyond end of device\n"); |
1878 | return -EINVAL; | 1975 | return -EINVAL; |
1879 | } | 1976 | } |
@@ -1929,8 +2026,8 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, | |||
1929 | 2026 | ||
1930 | /* Do not allow writes past end of device */ | 2027 | /* Do not allow writes past end of device */ |
1931 | if (ops->datbuf && (to + ops->len) > mtd->size) { | 2028 | if (ops->datbuf && (to + ops->len) > mtd->size) { |
1932 | DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " | 2029 | DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " |
1933 | "Attempt read beyond end of device\n"); | 2030 | "Attempt write beyond end of device\n"); |
1934 | return -EINVAL; | 2031 | return -EINVAL; |
1935 | } | 2032 | } |
1936 | 2033 | ||
@@ -2555,6 +2652,9 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
2555 | case 64: | 2652 | case 64: |
2556 | chip->ecc.layout = &nand_oob_64; | 2653 | chip->ecc.layout = &nand_oob_64; |
2557 | break; | 2654 | break; |
2655 | case 128: | ||
2656 | chip->ecc.layout = &nand_oob_128; | ||
2657 | break; | ||
2558 | default: | 2658 | default: |
2559 | printk(KERN_WARNING "No oob scheme defined for " | 2659 | printk(KERN_WARNING "No oob scheme defined for " |
2560 | "oobsize %d\n", mtd->oobsize); | 2660 | "oobsize %d\n", mtd->oobsize); |
@@ -2569,10 +2669,6 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
2569 | * check ECC mode, default to software if 3byte/512byte hardware ECC is | 2669 | * check ECC mode, default to software if 3byte/512byte hardware ECC is |
2570 | * selected and we have 256 byte pagesize fallback to software ECC | 2670 | * selected and we have 256 byte pagesize fallback to software ECC |
2571 | */ | 2671 | */ |
2572 | if (!chip->ecc.read_page_raw) | ||
2573 | chip->ecc.read_page_raw = nand_read_page_raw; | ||
2574 | if (!chip->ecc.write_page_raw) | ||
2575 | chip->ecc.write_page_raw = nand_write_page_raw; | ||
2576 | 2672 | ||
2577 | switch (chip->ecc.mode) { | 2673 | switch (chip->ecc.mode) { |
2578 | case NAND_ECC_HW: | 2674 | case NAND_ECC_HW: |
@@ -2581,6 +2677,10 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
2581 | chip->ecc.read_page = nand_read_page_hwecc; | 2677 | chip->ecc.read_page = nand_read_page_hwecc; |
2582 | if (!chip->ecc.write_page) | 2678 | if (!chip->ecc.write_page) |
2583 | chip->ecc.write_page = nand_write_page_hwecc; | 2679 | chip->ecc.write_page = nand_write_page_hwecc; |
2680 | if (!chip->ecc.read_page_raw) | ||
2681 | chip->ecc.read_page_raw = nand_read_page_raw; | ||
2682 | if (!chip->ecc.write_page_raw) | ||
2683 | chip->ecc.write_page_raw = nand_write_page_raw; | ||
2584 | if (!chip->ecc.read_oob) | 2684 | if (!chip->ecc.read_oob) |
2585 | chip->ecc.read_oob = nand_read_oob_std; | 2685 | chip->ecc.read_oob = nand_read_oob_std; |
2586 | if (!chip->ecc.write_oob) | 2686 | if (!chip->ecc.write_oob) |
@@ -2602,6 +2702,10 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
2602 | chip->ecc.read_page = nand_read_page_syndrome; | 2702 | chip->ecc.read_page = nand_read_page_syndrome; |
2603 | if (!chip->ecc.write_page) | 2703 | if (!chip->ecc.write_page) |
2604 | chip->ecc.write_page = nand_write_page_syndrome; | 2704 | chip->ecc.write_page = nand_write_page_syndrome; |
2705 | if (!chip->ecc.read_page_raw) | ||
2706 | chip->ecc.read_page_raw = nand_read_page_raw_syndrome; | ||
2707 | if (!chip->ecc.write_page_raw) | ||
2708 | chip->ecc.write_page_raw = nand_write_page_raw_syndrome; | ||
2605 | if (!chip->ecc.read_oob) | 2709 | if (!chip->ecc.read_oob) |
2606 | chip->ecc.read_oob = nand_read_oob_syndrome; | 2710 | chip->ecc.read_oob = nand_read_oob_syndrome; |
2607 | if (!chip->ecc.write_oob) | 2711 | if (!chip->ecc.write_oob) |
@@ -2620,6 +2724,8 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
2620 | chip->ecc.read_page = nand_read_page_swecc; | 2724 | chip->ecc.read_page = nand_read_page_swecc; |
2621 | chip->ecc.read_subpage = nand_read_subpage; | 2725 | chip->ecc.read_subpage = nand_read_subpage; |
2622 | chip->ecc.write_page = nand_write_page_swecc; | 2726 | chip->ecc.write_page = nand_write_page_swecc; |
2727 | chip->ecc.read_page_raw = nand_read_page_raw; | ||
2728 | chip->ecc.write_page_raw = nand_write_page_raw; | ||
2623 | chip->ecc.read_oob = nand_read_oob_std; | 2729 | chip->ecc.read_oob = nand_read_oob_std; |
2624 | chip->ecc.write_oob = nand_write_oob_std; | 2730 | chip->ecc.write_oob = nand_write_oob_std; |
2625 | chip->ecc.size = 256; | 2731 | chip->ecc.size = 256; |
@@ -2632,6 +2738,8 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
2632 | chip->ecc.read_page = nand_read_page_raw; | 2738 | chip->ecc.read_page = nand_read_page_raw; |
2633 | chip->ecc.write_page = nand_write_page_raw; | 2739 | chip->ecc.write_page = nand_write_page_raw; |
2634 | chip->ecc.read_oob = nand_read_oob_std; | 2740 | chip->ecc.read_oob = nand_read_oob_std; |
2741 | chip->ecc.read_page_raw = nand_read_page_raw; | ||
2742 | chip->ecc.write_page_raw = nand_write_page_raw; | ||
2635 | chip->ecc.write_oob = nand_write_oob_std; | 2743 | chip->ecc.write_oob = nand_write_oob_std; |
2636 | chip->ecc.size = mtd->writesize; | 2744 | chip->ecc.size = mtd->writesize; |
2637 | chip->ecc.bytes = 0; | 2745 | chip->ecc.bytes = 0; |
@@ -2676,6 +2784,7 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
2676 | break; | 2784 | break; |
2677 | case 4: | 2785 | case 4: |
2678 | case 8: | 2786 | case 8: |
2787 | case 16: | ||
2679 | mtd->subpage_sft = 2; | 2788 | mtd->subpage_sft = 2; |
2680 | break; | 2789 | break; |
2681 | } | 2790 | } |
@@ -2720,14 +2829,14 @@ int nand_scan_tail(struct mtd_info *mtd) | |||
2720 | return chip->scan_bbt(mtd); | 2829 | return chip->scan_bbt(mtd); |
2721 | } | 2830 | } |
2722 | 2831 | ||
2723 | /* module_text_address() isn't exported, and it's mostly a pointless | 2832 | /* is_module_text_address() isn't exported, and it's mostly a pointless |
2724 | test if this is a module _anyway_ -- they'd have to try _really_ hard | 2833 | test if this is a module _anyway_ -- they'd have to try _really_ hard |
2725 | to call us from in-kernel code if the core NAND support is modular. */ | 2834 | to call us from in-kernel code if the core NAND support is modular. */ |
2726 | #ifdef MODULE | 2835 | #ifdef MODULE |
2727 | #define caller_is_module() (1) | 2836 | #define caller_is_module() (1) |
2728 | #else | 2837 | #else |
2729 | #define caller_is_module() \ | 2838 | #define caller_is_module() \ |
2730 | module_text_address((unsigned long)__builtin_return_address(0)) | 2839 | is_module_text_address((unsigned long)__builtin_return_address(0)) |
2731 | #endif | 2840 | #endif |
2732 | 2841 | ||
2733 | /** | 2842 | /** |
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 582cf80f555a..89bf85af642c 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c | |||
@@ -187,7 +187,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, | |||
187 | return -ENODEV; | 187 | return -ENODEV; |
188 | 188 | ||
189 | ndfc->mtd.name = kasprintf(GFP_KERNEL, "%s.%s", | 189 | ndfc->mtd.name = kasprintf(GFP_KERNEL, "%s.%s", |
190 | ndfc->ofdev->dev.bus_id, flash_np->name); | 190 | dev_name(&ndfc->ofdev->dev), flash_np->name); |
191 | if (!ndfc->mtd.name) { | 191 | if (!ndfc->mtd.name) { |
192 | ret = -ENOMEM; | 192 | ret = -ENOMEM; |
193 | goto err; | 193 | goto err; |
diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 917cf8d3ae95..c2dfd3ea353d 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c | |||
@@ -149,7 +149,7 @@ static int __devexit orion_nand_remove(struct platform_device *pdev) | |||
149 | 149 | ||
150 | static struct platform_driver orion_nand_driver = { | 150 | static struct platform_driver orion_nand_driver = { |
151 | .probe = orion_nand_probe, | 151 | .probe = orion_nand_probe, |
152 | .remove = orion_nand_remove, | 152 | .remove = __devexit_p(orion_nand_remove), |
153 | .driver = { | 153 | .driver = { |
154 | .name = "orion_nand", | 154 | .name = "orion_nand", |
155 | .owner = THIS_MODULE, | 155 | .owner = THIS_MODULE, |
diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 75f9f4874ecf..86e1d08eee00 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c | |||
@@ -30,7 +30,7 @@ struct plat_nand_data { | |||
30 | /* | 30 | /* |
31 | * Probe for the NAND device. | 31 | * Probe for the NAND device. |
32 | */ | 32 | */ |
33 | static int __init plat_nand_probe(struct platform_device *pdev) | 33 | static int __devinit plat_nand_probe(struct platform_device *pdev) |
34 | { | 34 | { |
35 | struct platform_nand_data *pdata = pdev->dev.platform_data; | 35 | struct platform_nand_data *pdata = pdev->dev.platform_data; |
36 | struct plat_nand_data *data; | 36 | struct plat_nand_data *data; |
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index cc55cbc2b308..30a8ce6d3e69 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c | |||
@@ -22,7 +22,6 @@ | |||
22 | #include <linux/irq.h> | 22 | #include <linux/irq.h> |
23 | 23 | ||
24 | #include <mach/dma.h> | 24 | #include <mach/dma.h> |
25 | #include <mach/pxa-regs.h> | ||
26 | #include <mach/pxa3xx_nand.h> | 25 | #include <mach/pxa3xx_nand.h> |
27 | 26 | ||
28 | #define CHIP_DELAY_TIMEOUT (2 * HZ/10) | 27 | #define CHIP_DELAY_TIMEOUT (2 * HZ/10) |
@@ -171,7 +170,13 @@ static int use_dma = 1; | |||
171 | module_param(use_dma, bool, 0444); | 170 | module_param(use_dma, bool, 0444); |
172 | MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW"); | 171 | MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW"); |
173 | 172 | ||
174 | #ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN | 173 | /* |
174 | * Default NAND flash controller configuration setup by the | ||
175 | * bootloader. This configuration is used only when pdata->keep_config is set | ||
176 | */ | ||
177 | static struct pxa3xx_nand_timing default_timing; | ||
178 | static struct pxa3xx_nand_flash default_flash; | ||
179 | |||
175 | static struct pxa3xx_nand_cmdset smallpage_cmdset = { | 180 | static struct pxa3xx_nand_cmdset smallpage_cmdset = { |
176 | .read1 = 0x0000, | 181 | .read1 = 0x0000, |
177 | .read2 = 0x0050, | 182 | .read2 = 0x0050, |
@@ -198,6 +203,7 @@ static struct pxa3xx_nand_cmdset largepage_cmdset = { | |||
198 | .lock_status = 0x007A, | 203 | .lock_status = 0x007A, |
199 | }; | 204 | }; |
200 | 205 | ||
206 | #ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN | ||
201 | static struct pxa3xx_nand_timing samsung512MbX16_timing = { | 207 | static struct pxa3xx_nand_timing samsung512MbX16_timing = { |
202 | .tCH = 10, | 208 | .tCH = 10, |
203 | .tCS = 0, | 209 | .tCS = 0, |
@@ -297,9 +303,23 @@ static struct pxa3xx_nand_flash *builtin_flash_types[] = { | |||
297 | #define NDTR1_tWHR(c) (min((c), 15) << 4) | 303 | #define NDTR1_tWHR(c) (min((c), 15) << 4) |
298 | #define NDTR1_tAR(c) (min((c), 15) << 0) | 304 | #define NDTR1_tAR(c) (min((c), 15) << 0) |
299 | 305 | ||
306 | #define tCH_NDTR0(r) (((r) >> 19) & 0x7) | ||
307 | #define tCS_NDTR0(r) (((r) >> 16) & 0x7) | ||
308 | #define tWH_NDTR0(r) (((r) >> 11) & 0x7) | ||
309 | #define tWP_NDTR0(r) (((r) >> 8) & 0x7) | ||
310 | #define tRH_NDTR0(r) (((r) >> 3) & 0x7) | ||
311 | #define tRP_NDTR0(r) (((r) >> 0) & 0x7) | ||
312 | |||
313 | #define tR_NDTR1(r) (((r) >> 16) & 0xffff) | ||
314 | #define tWHR_NDTR1(r) (((r) >> 4) & 0xf) | ||
315 | #define tAR_NDTR1(r) (((r) >> 0) & 0xf) | ||
316 | |||
300 | /* convert nano-seconds to nand flash controller clock cycles */ | 317 | /* convert nano-seconds to nand flash controller clock cycles */ |
301 | #define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) - 1) | 318 | #define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) - 1) |
302 | 319 | ||
320 | /* convert nand flash controller clock cycles to nano-seconds */ | ||
321 | #define cycle2ns(c, clk) ((((c) + 1) * 1000000 + clk / 500) / (clk / 1000)) | ||
322 | |||
303 | static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, | 323 | static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, |
304 | const struct pxa3xx_nand_timing *t) | 324 | const struct pxa3xx_nand_timing *t) |
305 | { | 325 | { |
@@ -921,6 +941,82 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, | |||
921 | return 0; | 941 | return 0; |
922 | } | 942 | } |
923 | 943 | ||
944 | static void pxa3xx_nand_detect_timing(struct pxa3xx_nand_info *info, | ||
945 | struct pxa3xx_nand_timing *t) | ||
946 | { | ||
947 | unsigned long nand_clk = clk_get_rate(info->clk); | ||
948 | uint32_t ndtr0 = nand_readl(info, NDTR0CS0); | ||
949 | uint32_t ndtr1 = nand_readl(info, NDTR1CS0); | ||
950 | |||
951 | t->tCH = cycle2ns(tCH_NDTR0(ndtr0), nand_clk); | ||
952 | t->tCS = cycle2ns(tCS_NDTR0(ndtr0), nand_clk); | ||
953 | t->tWH = cycle2ns(tWH_NDTR0(ndtr0), nand_clk); | ||
954 | t->tWP = cycle2ns(tWP_NDTR0(ndtr0), nand_clk); | ||
955 | t->tRH = cycle2ns(tRH_NDTR0(ndtr0), nand_clk); | ||
956 | t->tRP = cycle2ns(tRP_NDTR0(ndtr0), nand_clk); | ||
957 | |||
958 | t->tR = cycle2ns(tR_NDTR1(ndtr1), nand_clk); | ||
959 | t->tWHR = cycle2ns(tWHR_NDTR1(ndtr1), nand_clk); | ||
960 | t->tAR = cycle2ns(tAR_NDTR1(ndtr1), nand_clk); | ||
961 | } | ||
962 | |||
963 | static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) | ||
964 | { | ||
965 | uint32_t ndcr = nand_readl(info, NDCR); | ||
966 | struct nand_flash_dev *type = NULL; | ||
967 | uint32_t id = -1; | ||
968 | int i; | ||
969 | |||
970 | default_flash.page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32; | ||
971 | default_flash.page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; | ||
972 | default_flash.flash_width = ndcr & NDCR_DWIDTH_M ? 16 : 8; | ||
973 | default_flash.dfc_width = ndcr & NDCR_DWIDTH_C ? 16 : 8; | ||
974 | |||
975 | if (default_flash.page_size == 2048) | ||
976 | default_flash.cmdset = &largepage_cmdset; | ||
977 | else | ||
978 | default_flash.cmdset = &smallpage_cmdset; | ||
979 | |||
980 | /* set info fields needed to __readid */ | ||
981 | info->flash_info = &default_flash; | ||
982 | info->read_id_bytes = (default_flash.page_size == 2048) ? 4 : 2; | ||
983 | info->reg_ndcr = ndcr; | ||
984 | |||
985 | if (__readid(info, &id)) | ||
986 | return -ENODEV; | ||
987 | |||
988 | /* Lookup the flash id */ | ||
989 | id = (id >> 8) & 0xff; /* device id is byte 2 */ | ||
990 | for (i = 0; nand_flash_ids[i].name != NULL; i++) { | ||
991 | if (id == nand_flash_ids[i].id) { | ||
992 | type = &nand_flash_ids[i]; | ||
993 | break; | ||
994 | } | ||
995 | } | ||
996 | |||
997 | if (!type) | ||
998 | return -ENODEV; | ||
999 | |||
1000 | /* fill the missing flash information */ | ||
1001 | i = __ffs(default_flash.page_per_block * default_flash.page_size); | ||
1002 | default_flash.num_blocks = type->chipsize << (20 - i); | ||
1003 | |||
1004 | info->oob_size = (default_flash.page_size == 2048) ? 64 : 16; | ||
1005 | |||
1006 | /* calculate addressing information */ | ||
1007 | info->col_addr_cycles = (default_flash.page_size == 2048) ? 2 : 1; | ||
1008 | |||
1009 | if (default_flash.num_blocks * default_flash.page_per_block > 65536) | ||
1010 | info->row_addr_cycles = 3; | ||
1011 | else | ||
1012 | info->row_addr_cycles = 2; | ||
1013 | |||
1014 | pxa3xx_nand_detect_timing(info, &default_timing); | ||
1015 | default_flash.timing = &default_timing; | ||
1016 | |||
1017 | return 0; | ||
1018 | } | ||
1019 | |||
924 | static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info, | 1020 | static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info, |
925 | const struct pxa3xx_nand_platform_data *pdata) | 1021 | const struct pxa3xx_nand_platform_data *pdata) |
926 | { | 1022 | { |
@@ -928,6 +1024,10 @@ static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info, | |||
928 | uint32_t id = -1; | 1024 | uint32_t id = -1; |
929 | int i; | 1025 | int i; |
930 | 1026 | ||
1027 | if (pdata->keep_config) | ||
1028 | if (pxa3xx_nand_detect_config(info) == 0) | ||
1029 | return 0; | ||
1030 | |||
931 | for (i = 0; i<pdata->num_flash; ++i) { | 1031 | for (i = 0; i<pdata->num_flash; ++i) { |
932 | f = pdata->flash + i; | 1032 | f = pdata->flash + i; |
933 | 1033 | ||
@@ -1079,6 +1179,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) | |||
1079 | 1179 | ||
1080 | this = &info->nand_chip; | 1180 | this = &info->nand_chip; |
1081 | mtd->priv = info; | 1181 | mtd->priv = info; |
1182 | mtd->owner = THIS_MODULE; | ||
1082 | 1183 | ||
1083 | info->clk = clk_get(&pdev->dev, NULL); | 1184 | info->clk = clk_get(&pdev->dev, NULL); |
1084 | if (IS_ERR(info->clk)) { | 1185 | if (IS_ERR(info->clk)) { |
@@ -1118,14 +1219,14 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) | |||
1118 | goto fail_put_clk; | 1219 | goto fail_put_clk; |
1119 | } | 1220 | } |
1120 | 1221 | ||
1121 | r = request_mem_region(r->start, r->end - r->start + 1, pdev->name); | 1222 | r = request_mem_region(r->start, resource_size(r), pdev->name); |
1122 | if (r == NULL) { | 1223 | if (r == NULL) { |
1123 | dev_err(&pdev->dev, "failed to request memory resource\n"); | 1224 | dev_err(&pdev->dev, "failed to request memory resource\n"); |
1124 | ret = -EBUSY; | 1225 | ret = -EBUSY; |
1125 | goto fail_put_clk; | 1226 | goto fail_put_clk; |
1126 | } | 1227 | } |
1127 | 1228 | ||
1128 | info->mmio_base = ioremap(r->start, r->end - r->start + 1); | 1229 | info->mmio_base = ioremap(r->start, resource_size(r)); |
1129 | if (info->mmio_base == NULL) { | 1230 | if (info->mmio_base == NULL) { |
1130 | dev_err(&pdev->dev, "ioremap() failed\n"); | 1231 | dev_err(&pdev->dev, "ioremap() failed\n"); |
1131 | ret = -ENODEV; | 1232 | ret = -ENODEV; |
@@ -1174,7 +1275,7 @@ fail_free_buf: | |||
1174 | fail_free_io: | 1275 | fail_free_io: |
1175 | iounmap(info->mmio_base); | 1276 | iounmap(info->mmio_base); |
1176 | fail_free_res: | 1277 | fail_free_res: |
1177 | release_mem_region(r->start, r->end - r->start + 1); | 1278 | release_mem_region(r->start, resource_size(r)); |
1178 | fail_put_clk: | 1279 | fail_put_clk: |
1179 | clk_disable(info->clk); | 1280 | clk_disable(info->clk); |
1180 | clk_put(info->clk); | 1281 | clk_put(info->clk); |
@@ -1187,6 +1288,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) | |||
1187 | { | 1288 | { |
1188 | struct mtd_info *mtd = platform_get_drvdata(pdev); | 1289 | struct mtd_info *mtd = platform_get_drvdata(pdev); |
1189 | struct pxa3xx_nand_info *info = mtd->priv; | 1290 | struct pxa3xx_nand_info *info = mtd->priv; |
1291 | struct resource *r; | ||
1190 | 1292 | ||
1191 | platform_set_drvdata(pdev, NULL); | 1293 | platform_set_drvdata(pdev, NULL); |
1192 | 1294 | ||
@@ -1199,6 +1301,14 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) | |||
1199 | info->data_buff, info->data_buff_phys); | 1301 | info->data_buff, info->data_buff_phys); |
1200 | } else | 1302 | } else |
1201 | kfree(info->data_buff); | 1303 | kfree(info->data_buff); |
1304 | |||
1305 | iounmap(info->mmio_base); | ||
1306 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1307 | release_mem_region(r->start, resource_size(r)); | ||
1308 | |||
1309 | clk_disable(info->clk); | ||
1310 | clk_put(info->clk); | ||
1311 | |||
1202 | kfree(mtd); | 1312 | kfree(mtd); |
1203 | return 0; | 1313 | return 0; |
1204 | } | 1314 | } |
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 821acb08ff1c..2bc896623e2d 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c | |||
@@ -58,7 +58,7 @@ static struct nand_bbt_descr flctl_4secc_smallpage = { | |||
58 | }; | 58 | }; |
59 | 59 | ||
60 | static struct nand_bbt_descr flctl_4secc_largepage = { | 60 | static struct nand_bbt_descr flctl_4secc_largepage = { |
61 | .options = 0, | 61 | .options = NAND_BBT_SCAN2NDPAGE, |
62 | .offs = 58, | 62 | .offs = 58, |
63 | .len = 2, | 63 | .len = 2, |
64 | .pattern = scan_ff_pattern, | 64 | .pattern = scan_ff_pattern, |
@@ -149,7 +149,7 @@ static void wait_wfifo_ready(struct sh_flctl *flctl) | |||
149 | printk(KERN_ERR "wait_wfifo_ready(): Timeout occured \n"); | 149 | printk(KERN_ERR "wait_wfifo_ready(): Timeout occured \n"); |
150 | } | 150 | } |
151 | 151 | ||
152 | static int wait_recfifo_ready(struct sh_flctl *flctl) | 152 | static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number) |
153 | { | 153 | { |
154 | uint32_t timeout = LOOP_TIMEOUT_MAX; | 154 | uint32_t timeout = LOOP_TIMEOUT_MAX; |
155 | int checked[4]; | 155 | int checked[4]; |
@@ -183,7 +183,12 @@ static int wait_recfifo_ready(struct sh_flctl *flctl) | |||
183 | uint8_t org; | 183 | uint8_t org; |
184 | int index; | 184 | int index; |
185 | 185 | ||
186 | index = data >> 16; | 186 | if (flctl->page_size) |
187 | index = (512 * sector_number) + | ||
188 | (data >> 16); | ||
189 | else | ||
190 | index = data >> 16; | ||
191 | |||
187 | org = flctl->done_buff[index]; | 192 | org = flctl->done_buff[index]; |
188 | flctl->done_buff[index] = org ^ (data & 0xFF); | 193 | flctl->done_buff[index] = org ^ (data & 0xFF); |
189 | checked[i] = 1; | 194 | checked[i] = 1; |
@@ -238,14 +243,14 @@ static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset) | |||
238 | } | 243 | } |
239 | } | 244 | } |
240 | 245 | ||
241 | static int read_ecfiforeg(struct sh_flctl *flctl, uint8_t *buff) | 246 | static int read_ecfiforeg(struct sh_flctl *flctl, uint8_t *buff, int sector) |
242 | { | 247 | { |
243 | int i; | 248 | int i; |
244 | unsigned long *ecc_buf = (unsigned long *)buff; | 249 | unsigned long *ecc_buf = (unsigned long *)buff; |
245 | void *fifo_addr = (void *)FLECFIFO(flctl); | 250 | void *fifo_addr = (void *)FLECFIFO(flctl); |
246 | 251 | ||
247 | for (i = 0; i < 4; i++) { | 252 | for (i = 0; i < 4; i++) { |
248 | if (wait_recfifo_ready(flctl)) | 253 | if (wait_recfifo_ready(flctl , sector)) |
249 | return 1; | 254 | return 1; |
250 | ecc_buf[i] = readl(fifo_addr); | 255 | ecc_buf[i] = readl(fifo_addr); |
251 | ecc_buf[i] = be32_to_cpu(ecc_buf[i]); | 256 | ecc_buf[i] = be32_to_cpu(ecc_buf[i]); |
@@ -384,7 +389,8 @@ static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr) | |||
384 | read_fiforeg(flctl, 512, 512 * sector); | 389 | read_fiforeg(flctl, 512, 512 * sector); |
385 | 390 | ||
386 | ret = read_ecfiforeg(flctl, | 391 | ret = read_ecfiforeg(flctl, |
387 | &flctl->done_buff[mtd->writesize + 16 * sector]); | 392 | &flctl->done_buff[mtd->writesize + 16 * sector], |
393 | sector); | ||
388 | 394 | ||
389 | if (ret) | 395 | if (ret) |
390 | flctl->hwecc_cant_correct[sector] = 1; | 396 | flctl->hwecc_cant_correct[sector] = 1; |
diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c new file mode 100644 index 000000000000..a4519a7bd683 --- /dev/null +++ b/drivers/mtd/nand/socrates_nand.c | |||
@@ -0,0 +1,325 @@ | |||
1 | /* | ||
2 | * drivers/mtd/nand/socrates_nand.c | ||
3 | * | ||
4 | * Copyright © 2008 Ilya Yanok, Emcraft Systems | ||
5 | * | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/slab.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/mtd/mtd.h> | ||
16 | #include <linux/mtd/nand.h> | ||
17 | #include <linux/mtd/partitions.h> | ||
18 | #include <linux/of_platform.h> | ||
19 | #include <linux/io.h> | ||
20 | |||
21 | #define FPGA_NAND_CMD_MASK (0x7 << 28) | ||
22 | #define FPGA_NAND_CMD_COMMAND (0x0 << 28) | ||
23 | #define FPGA_NAND_CMD_ADDR (0x1 << 28) | ||
24 | #define FPGA_NAND_CMD_READ (0x2 << 28) | ||
25 | #define FPGA_NAND_CMD_WRITE (0x3 << 28) | ||
26 | #define FPGA_NAND_BUSY (0x1 << 15) | ||
27 | #define FPGA_NAND_ENABLE (0x1 << 31) | ||
28 | #define FPGA_NAND_DATA_SHIFT 16 | ||
29 | |||
30 | struct socrates_nand_host { | ||
31 | struct nand_chip nand_chip; | ||
32 | struct mtd_info mtd; | ||
33 | void __iomem *io_base; | ||
34 | struct device *dev; | ||
35 | }; | ||
36 | |||
37 | /** | ||
38 | * socrates_nand_write_buf - write buffer to chip | ||
39 | * @mtd: MTD device structure | ||
40 | * @buf: data buffer | ||
41 | * @len: number of bytes to write | ||
42 | */ | ||
43 | static void socrates_nand_write_buf(struct mtd_info *mtd, | ||
44 | const uint8_t *buf, int len) | ||
45 | { | ||
46 | int i; | ||
47 | struct nand_chip *this = mtd->priv; | ||
48 | struct socrates_nand_host *host = this->priv; | ||
49 | |||
50 | for (i = 0; i < len; i++) { | ||
51 | out_be32(host->io_base, FPGA_NAND_ENABLE | | ||
52 | FPGA_NAND_CMD_WRITE | | ||
53 | (buf[i] << FPGA_NAND_DATA_SHIFT)); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | /** | ||
58 | * socrates_nand_read_buf - read chip data into buffer | ||
59 | * @mtd: MTD device structure | ||
60 | * @buf: buffer to store date | ||
61 | * @len: number of bytes to read | ||
62 | */ | ||
63 | static void socrates_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) | ||
64 | { | ||
65 | int i; | ||
66 | struct nand_chip *this = mtd->priv; | ||
67 | struct socrates_nand_host *host = this->priv; | ||
68 | uint32_t val; | ||
69 | |||
70 | val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ; | ||
71 | |||
72 | out_be32(host->io_base, val); | ||
73 | for (i = 0; i < len; i++) { | ||
74 | buf[i] = (in_be32(host->io_base) >> | ||
75 | FPGA_NAND_DATA_SHIFT) & 0xff; | ||
76 | } | ||
77 | } | ||
78 | |||
79 | /** | ||
80 | * socrates_nand_read_byte - read one byte from the chip | ||
81 | * @mtd: MTD device structure | ||
82 | */ | ||
83 | static uint8_t socrates_nand_read_byte(struct mtd_info *mtd) | ||
84 | { | ||
85 | uint8_t byte; | ||
86 | socrates_nand_read_buf(mtd, &byte, sizeof(byte)); | ||
87 | return byte; | ||
88 | } | ||
89 | |||
90 | /** | ||
91 | * socrates_nand_read_word - read one word from the chip | ||
92 | * @mtd: MTD device structure | ||
93 | */ | ||
94 | static uint16_t socrates_nand_read_word(struct mtd_info *mtd) | ||
95 | { | ||
96 | uint16_t word; | ||
97 | socrates_nand_read_buf(mtd, (uint8_t *)&word, sizeof(word)); | ||
98 | return word; | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * socrates_nand_verify_buf - Verify chip data against buffer | ||
103 | * @mtd: MTD device structure | ||
104 | * @buf: buffer containing the data to compare | ||
105 | * @len: number of bytes to compare | ||
106 | */ | ||
107 | static int socrates_nand_verify_buf(struct mtd_info *mtd, const u8 *buf, | ||
108 | int len) | ||
109 | { | ||
110 | int i; | ||
111 | |||
112 | for (i = 0; i < len; i++) { | ||
113 | if (buf[i] != socrates_nand_read_byte(mtd)) | ||
114 | return -EFAULT; | ||
115 | } | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | /* | ||
120 | * Hardware specific access to control-lines | ||
121 | */ | ||
122 | static void socrates_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, | ||
123 | unsigned int ctrl) | ||
124 | { | ||
125 | struct nand_chip *nand_chip = mtd->priv; | ||
126 | struct socrates_nand_host *host = nand_chip->priv; | ||
127 | uint32_t val; | ||
128 | |||
129 | if (cmd == NAND_CMD_NONE) | ||
130 | return; | ||
131 | |||
132 | if (ctrl & NAND_CLE) | ||
133 | val = FPGA_NAND_CMD_COMMAND; | ||
134 | else | ||
135 | val = FPGA_NAND_CMD_ADDR; | ||
136 | |||
137 | if (ctrl & NAND_NCE) | ||
138 | val |= FPGA_NAND_ENABLE; | ||
139 | |||
140 | val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT; | ||
141 | |||
142 | out_be32(host->io_base, val); | ||
143 | } | ||
144 | |||
145 | /* | ||
146 | * Read the Device Ready pin. | ||
147 | */ | ||
148 | static int socrates_nand_device_ready(struct mtd_info *mtd) | ||
149 | { | ||
150 | struct nand_chip *nand_chip = mtd->priv; | ||
151 | struct socrates_nand_host *host = nand_chip->priv; | ||
152 | |||
153 | if (in_be32(host->io_base) & FPGA_NAND_BUSY) | ||
154 | return 0; /* busy */ | ||
155 | return 1; | ||
156 | } | ||
157 | |||
158 | #ifdef CONFIG_MTD_PARTITIONS | ||
159 | static const char *part_probes[] = { "cmdlinepart", NULL }; | ||
160 | #endif | ||
161 | |||
162 | /* | ||
163 | * Probe for the NAND device. | ||
164 | */ | ||
165 | static int __devinit socrates_nand_probe(struct of_device *ofdev, | ||
166 | const struct of_device_id *ofid) | ||
167 | { | ||
168 | struct socrates_nand_host *host; | ||
169 | struct mtd_info *mtd; | ||
170 | struct nand_chip *nand_chip; | ||
171 | int res; | ||
172 | |||
173 | #ifdef CONFIG_MTD_PARTITIONS | ||
174 | struct mtd_partition *partitions = NULL; | ||
175 | int num_partitions = 0; | ||
176 | #endif | ||
177 | |||
178 | /* Allocate memory for the device structure (and zero it) */ | ||
179 | host = kzalloc(sizeof(struct socrates_nand_host), GFP_KERNEL); | ||
180 | if (!host) { | ||
181 | printk(KERN_ERR | ||
182 | "socrates_nand: failed to allocate device structure.\n"); | ||
183 | return -ENOMEM; | ||
184 | } | ||
185 | |||
186 | host->io_base = of_iomap(ofdev->node, 0); | ||
187 | if (host->io_base == NULL) { | ||
188 | printk(KERN_ERR "socrates_nand: ioremap failed\n"); | ||
189 | kfree(host); | ||
190 | return -EIO; | ||
191 | } | ||
192 | |||
193 | mtd = &host->mtd; | ||
194 | nand_chip = &host->nand_chip; | ||
195 | host->dev = &ofdev->dev; | ||
196 | |||
197 | nand_chip->priv = host; /* link the private data structures */ | ||
198 | mtd->priv = nand_chip; | ||
199 | mtd->name = "socrates_nand"; | ||
200 | mtd->owner = THIS_MODULE; | ||
201 | mtd->dev.parent = &ofdev->dev; | ||
202 | |||
203 | /*should never be accessed directly */ | ||
204 | nand_chip->IO_ADDR_R = (void *)0xdeadbeef; | ||
205 | nand_chip->IO_ADDR_W = (void *)0xdeadbeef; | ||
206 | |||
207 | nand_chip->cmd_ctrl = socrates_nand_cmd_ctrl; | ||
208 | nand_chip->read_byte = socrates_nand_read_byte; | ||
209 | nand_chip->read_word = socrates_nand_read_word; | ||
210 | nand_chip->write_buf = socrates_nand_write_buf; | ||
211 | nand_chip->read_buf = socrates_nand_read_buf; | ||
212 | nand_chip->verify_buf = socrates_nand_verify_buf; | ||
213 | nand_chip->dev_ready = socrates_nand_device_ready; | ||
214 | |||
215 | nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ | ||
216 | |||
217 | /* TODO: I have no idea what real delay is. */ | ||
218 | nand_chip->chip_delay = 20; /* 20us command delay time */ | ||
219 | |||
220 | dev_set_drvdata(&ofdev->dev, host); | ||
221 | |||
222 | /* first scan to find the device and get the page size */ | ||
223 | if (nand_scan_ident(mtd, 1)) { | ||
224 | res = -ENXIO; | ||
225 | goto out; | ||
226 | } | ||
227 | |||
228 | /* second phase scan */ | ||
229 | if (nand_scan_tail(mtd)) { | ||
230 | res = -ENXIO; | ||
231 | goto out; | ||
232 | } | ||
233 | |||
234 | #ifdef CONFIG_MTD_PARTITIONS | ||
235 | #ifdef CONFIG_MTD_CMDLINE_PARTS | ||
236 | num_partitions = parse_mtd_partitions(mtd, part_probes, | ||
237 | &partitions, 0); | ||
238 | if (num_partitions < 0) { | ||
239 | res = num_partitions; | ||
240 | goto release; | ||
241 | } | ||
242 | #endif | ||
243 | |||
244 | #ifdef CONFIG_MTD_OF_PARTS | ||
245 | if (num_partitions == 0) { | ||
246 | num_partitions = of_mtd_parse_partitions(&ofdev->dev, | ||
247 | ofdev->node, | ||
248 | &partitions); | ||
249 | if (num_partitions < 0) { | ||
250 | res = num_partitions; | ||
251 | goto release; | ||
252 | } | ||
253 | } | ||
254 | #endif | ||
255 | if (partitions && (num_partitions > 0)) | ||
256 | res = add_mtd_partitions(mtd, partitions, num_partitions); | ||
257 | else | ||
258 | #endif | ||
259 | res = add_mtd_device(mtd); | ||
260 | |||
261 | if (!res) | ||
262 | return res; | ||
263 | |||
264 | #ifdef CONFIG_MTD_PARTITIONS | ||
265 | release: | ||
266 | #endif | ||
267 | nand_release(mtd); | ||
268 | |||
269 | out: | ||
270 | dev_set_drvdata(&ofdev->dev, NULL); | ||
271 | iounmap(host->io_base); | ||
272 | kfree(host); | ||
273 | return res; | ||
274 | } | ||
275 | |||
276 | /* | ||
277 | * Remove a NAND device. | ||
278 | */ | ||
279 | static int __devexit socrates_nand_remove(struct of_device *ofdev) | ||
280 | { | ||
281 | struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev); | ||
282 | struct mtd_info *mtd = &host->mtd; | ||
283 | |||
284 | nand_release(mtd); | ||
285 | |||
286 | dev_set_drvdata(&ofdev->dev, NULL); | ||
287 | iounmap(host->io_base); | ||
288 | kfree(host); | ||
289 | |||
290 | return 0; | ||
291 | } | ||
292 | |||
293 | static struct of_device_id socrates_nand_match[] = | ||
294 | { | ||
295 | { | ||
296 | .compatible = "abb,socrates-nand", | ||
297 | }, | ||
298 | {}, | ||
299 | }; | ||
300 | |||
301 | MODULE_DEVICE_TABLE(of, socrates_nand_match); | ||
302 | |||
303 | static struct of_platform_driver socrates_nand_driver = { | ||
304 | .name = "socrates_nand", | ||
305 | .match_table = socrates_nand_match, | ||
306 | .probe = socrates_nand_probe, | ||
307 | .remove = __devexit_p(socrates_nand_remove), | ||
308 | }; | ||
309 | |||
310 | static int __init socrates_nand_init(void) | ||
311 | { | ||
312 | return of_register_platform_driver(&socrates_nand_driver); | ||
313 | } | ||
314 | |||
315 | static void __exit socrates_nand_exit(void) | ||
316 | { | ||
317 | of_unregister_platform_driver(&socrates_nand_driver); | ||
318 | } | ||
319 | |||
320 | module_init(socrates_nand_init); | ||
321 | module_exit(socrates_nand_exit); | ||
322 | |||
323 | MODULE_LICENSE("GPL"); | ||
324 | MODULE_AUTHOR("Ilya Yanok"); | ||
325 | MODULE_DESCRIPTION("NAND driver for Socrates board"); | ||
diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c new file mode 100644 index 000000000000..812479264896 --- /dev/null +++ b/drivers/mtd/nand/txx9ndfmc.c | |||
@@ -0,0 +1,428 @@ | |||
1 | /* | ||
2 | * TXx9 NAND flash memory controller driver | ||
3 | * Based on RBTX49xx patch from CELF patch archive. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * (C) Copyright TOSHIBA CORPORATION 2004-2007 | ||
10 | * All Rights Reserved. | ||
11 | */ | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/mtd/mtd.h> | ||
18 | #include <linux/mtd/nand.h> | ||
19 | #include <linux/mtd/nand_ecc.h> | ||
20 | #include <linux/mtd/partitions.h> | ||
21 | #include <linux/io.h> | ||
22 | #include <asm/txx9/ndfmc.h> | ||
23 | |||
24 | /* TXX9 NDFMC Registers */ | ||
25 | #define TXX9_NDFDTR 0x00 | ||
26 | #define TXX9_NDFMCR 0x04 | ||
27 | #define TXX9_NDFSR 0x08 | ||
28 | #define TXX9_NDFISR 0x0c | ||
29 | #define TXX9_NDFIMR 0x10 | ||
30 | #define TXX9_NDFSPR 0x14 | ||
31 | #define TXX9_NDFRSTR 0x18 /* not TX4939 */ | ||
32 | |||
33 | /* NDFMCR : NDFMC Mode Control */ | ||
34 | #define TXX9_NDFMCR_WE 0x80 | ||
35 | #define TXX9_NDFMCR_ECC_ALL 0x60 | ||
36 | #define TXX9_NDFMCR_ECC_RESET 0x60 | ||
37 | #define TXX9_NDFMCR_ECC_READ 0x40 | ||
38 | #define TXX9_NDFMCR_ECC_ON 0x20 | ||
39 | #define TXX9_NDFMCR_ECC_OFF 0x00 | ||
40 | #define TXX9_NDFMCR_CE 0x10 | ||
41 | #define TXX9_NDFMCR_BSPRT 0x04 /* TX4925/TX4926 only */ | ||
42 | #define TXX9_NDFMCR_ALE 0x02 | ||
43 | #define TXX9_NDFMCR_CLE 0x01 | ||
44 | /* TX4939 only */ | ||
45 | #define TXX9_NDFMCR_X16 0x0400 | ||
46 | #define TXX9_NDFMCR_DMAREQ_MASK 0x0300 | ||
47 | #define TXX9_NDFMCR_DMAREQ_NODMA 0x0000 | ||
48 | #define TXX9_NDFMCR_DMAREQ_128 0x0100 | ||
49 | #define TXX9_NDFMCR_DMAREQ_256 0x0200 | ||
50 | #define TXX9_NDFMCR_DMAREQ_512 0x0300 | ||
51 | #define TXX9_NDFMCR_CS_MASK 0x0c | ||
52 | #define TXX9_NDFMCR_CS(ch) ((ch) << 2) | ||
53 | |||
54 | /* NDFMCR : NDFMC Status */ | ||
55 | #define TXX9_NDFSR_BUSY 0x80 | ||
56 | /* TX4939 only */ | ||
57 | #define TXX9_NDFSR_DMARUN 0x40 | ||
58 | |||
59 | /* NDFMCR : NDFMC Reset */ | ||
60 | #define TXX9_NDFRSTR_RST 0x01 | ||
61 | |||
62 | struct txx9ndfmc_priv { | ||
63 | struct platform_device *dev; | ||
64 | struct nand_chip chip; | ||
65 | struct mtd_info mtd; | ||
66 | int cs; | ||
67 | char mtdname[BUS_ID_SIZE + 2]; | ||
68 | }; | ||
69 | |||
70 | #define MAX_TXX9NDFMC_DEV 4 | ||
71 | struct txx9ndfmc_drvdata { | ||
72 | struct mtd_info *mtds[MAX_TXX9NDFMC_DEV]; | ||
73 | void __iomem *base; | ||
74 | unsigned char hold; /* in gbusclock */ | ||
75 | unsigned char spw; /* in gbusclock */ | ||
76 | struct nand_hw_control hw_control; | ||
77 | #ifdef CONFIG_MTD_PARTITIONS | ||
78 | struct mtd_partition *parts[MAX_TXX9NDFMC_DEV]; | ||
79 | #endif | ||
80 | }; | ||
81 | |||
82 | static struct platform_device *mtd_to_platdev(struct mtd_info *mtd) | ||
83 | { | ||
84 | struct nand_chip *chip = mtd->priv; | ||
85 | struct txx9ndfmc_priv *txx9_priv = chip->priv; | ||
86 | return txx9_priv->dev; | ||
87 | } | ||
88 | |||
89 | static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg) | ||
90 | { | ||
91 | struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); | ||
92 | struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; | ||
93 | |||
94 | return drvdata->base + (reg << plat->shift); | ||
95 | } | ||
96 | |||
97 | static u32 txx9ndfmc_read(struct platform_device *dev, unsigned int reg) | ||
98 | { | ||
99 | return __raw_readl(ndregaddr(dev, reg)); | ||
100 | } | ||
101 | |||
102 | static void txx9ndfmc_write(struct platform_device *dev, | ||
103 | u32 val, unsigned int reg) | ||
104 | { | ||
105 | __raw_writel(val, ndregaddr(dev, reg)); | ||
106 | } | ||
107 | |||
108 | static uint8_t txx9ndfmc_read_byte(struct mtd_info *mtd) | ||
109 | { | ||
110 | struct platform_device *dev = mtd_to_platdev(mtd); | ||
111 | |||
112 | return txx9ndfmc_read(dev, TXX9_NDFDTR); | ||
113 | } | ||
114 | |||
115 | static void txx9ndfmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, | ||
116 | int len) | ||
117 | { | ||
118 | struct platform_device *dev = mtd_to_platdev(mtd); | ||
119 | void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); | ||
120 | u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); | ||
121 | |||
122 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_WE, TXX9_NDFMCR); | ||
123 | while (len--) | ||
124 | __raw_writel(*buf++, ndfdtr); | ||
125 | txx9ndfmc_write(dev, mcr, TXX9_NDFMCR); | ||
126 | } | ||
127 | |||
128 | static void txx9ndfmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) | ||
129 | { | ||
130 | struct platform_device *dev = mtd_to_platdev(mtd); | ||
131 | void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); | ||
132 | |||
133 | while (len--) | ||
134 | *buf++ = __raw_readl(ndfdtr); | ||
135 | } | ||
136 | |||
137 | static int txx9ndfmc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, | ||
138 | int len) | ||
139 | { | ||
140 | struct platform_device *dev = mtd_to_platdev(mtd); | ||
141 | void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); | ||
142 | |||
143 | while (len--) | ||
144 | if (*buf++ != (uint8_t)__raw_readl(ndfdtr)) | ||
145 | return -EFAULT; | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd, | ||
150 | unsigned int ctrl) | ||
151 | { | ||
152 | struct nand_chip *chip = mtd->priv; | ||
153 | struct txx9ndfmc_priv *txx9_priv = chip->priv; | ||
154 | struct platform_device *dev = txx9_priv->dev; | ||
155 | struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; | ||
156 | |||
157 | if (ctrl & NAND_CTRL_CHANGE) { | ||
158 | u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); | ||
159 | |||
160 | mcr &= ~(TXX9_NDFMCR_CLE | TXX9_NDFMCR_ALE | TXX9_NDFMCR_CE); | ||
161 | mcr |= ctrl & NAND_CLE ? TXX9_NDFMCR_CLE : 0; | ||
162 | mcr |= ctrl & NAND_ALE ? TXX9_NDFMCR_ALE : 0; | ||
163 | /* TXX9_NDFMCR_CE bit is 0:high 1:low */ | ||
164 | mcr |= ctrl & NAND_NCE ? TXX9_NDFMCR_CE : 0; | ||
165 | if (txx9_priv->cs >= 0 && (ctrl & NAND_NCE)) { | ||
166 | mcr &= ~TXX9_NDFMCR_CS_MASK; | ||
167 | mcr |= TXX9_NDFMCR_CS(txx9_priv->cs); | ||
168 | } | ||
169 | txx9ndfmc_write(dev, mcr, TXX9_NDFMCR); | ||
170 | } | ||
171 | if (cmd != NAND_CMD_NONE) | ||
172 | txx9ndfmc_write(dev, cmd & 0xff, TXX9_NDFDTR); | ||
173 | if (plat->flags & NDFMC_PLAT_FLAG_DUMMYWRITE) { | ||
174 | /* dummy write to update external latch */ | ||
175 | if ((ctrl & NAND_CTRL_CHANGE) && cmd == NAND_CMD_NONE) | ||
176 | txx9ndfmc_write(dev, 0, TXX9_NDFDTR); | ||
177 | } | ||
178 | mmiowb(); | ||
179 | } | ||
180 | |||
181 | static int txx9ndfmc_dev_ready(struct mtd_info *mtd) | ||
182 | { | ||
183 | struct platform_device *dev = mtd_to_platdev(mtd); | ||
184 | |||
185 | return !(txx9ndfmc_read(dev, TXX9_NDFSR) & TXX9_NDFSR_BUSY); | ||
186 | } | ||
187 | |||
188 | static int txx9ndfmc_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, | ||
189 | uint8_t *ecc_code) | ||
190 | { | ||
191 | struct platform_device *dev = mtd_to_platdev(mtd); | ||
192 | u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); | ||
193 | |||
194 | mcr &= ~TXX9_NDFMCR_ECC_ALL; | ||
195 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); | ||
196 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR); | ||
197 | ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR); | ||
198 | ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR); | ||
199 | ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR); | ||
200 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) | ||
205 | { | ||
206 | struct platform_device *dev = mtd_to_platdev(mtd); | ||
207 | u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); | ||
208 | |||
209 | mcr &= ~TXX9_NDFMCR_ECC_ALL; | ||
210 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_RESET, TXX9_NDFMCR); | ||
211 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); | ||
212 | txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_ON, TXX9_NDFMCR); | ||
213 | } | ||
214 | |||
215 | static void txx9ndfmc_initialize(struct platform_device *dev) | ||
216 | { | ||
217 | struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; | ||
218 | struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); | ||
219 | int tmout = 100; | ||
220 | |||
221 | if (plat->flags & NDFMC_PLAT_FLAG_NO_RSTR) | ||
222 | ; /* no NDFRSTR. Write to NDFSPR resets the NDFMC. */ | ||
223 | else { | ||
224 | /* reset NDFMC */ | ||
225 | txx9ndfmc_write(dev, | ||
226 | txx9ndfmc_read(dev, TXX9_NDFRSTR) | | ||
227 | TXX9_NDFRSTR_RST, | ||
228 | TXX9_NDFRSTR); | ||
229 | while (txx9ndfmc_read(dev, TXX9_NDFRSTR) & TXX9_NDFRSTR_RST) { | ||
230 | if (--tmout == 0) { | ||
231 | dev_err(&dev->dev, "reset failed.\n"); | ||
232 | break; | ||
233 | } | ||
234 | udelay(1); | ||
235 | } | ||
236 | } | ||
237 | /* setup Hold Time, Strobe Pulse Width */ | ||
238 | txx9ndfmc_write(dev, (drvdata->hold << 4) | drvdata->spw, TXX9_NDFSPR); | ||
239 | txx9ndfmc_write(dev, | ||
240 | (plat->flags & NDFMC_PLAT_FLAG_USE_BSPRT) ? | ||
241 | TXX9_NDFMCR_BSPRT : 0, TXX9_NDFMCR); | ||
242 | } | ||
243 | |||
244 | #define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \ | ||
245 | DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000) | ||
246 | |||
247 | static int __init txx9ndfmc_probe(struct platform_device *dev) | ||
248 | { | ||
249 | struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; | ||
250 | #ifdef CONFIG_MTD_PARTITIONS | ||
251 | static const char *probes[] = { "cmdlinepart", NULL }; | ||
252 | #endif | ||
253 | int hold, spw; | ||
254 | int i; | ||
255 | struct txx9ndfmc_drvdata *drvdata; | ||
256 | unsigned long gbusclk = plat->gbus_clock; | ||
257 | struct resource *res; | ||
258 | |||
259 | res = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
260 | if (!res) | ||
261 | return -ENODEV; | ||
262 | drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL); | ||
263 | if (!drvdata) | ||
264 | return -ENOMEM; | ||
265 | if (!devm_request_mem_region(&dev->dev, res->start, | ||
266 | resource_size(res), dev_name(&dev->dev))) | ||
267 | return -EBUSY; | ||
268 | drvdata->base = devm_ioremap(&dev->dev, res->start, | ||
269 | resource_size(res)); | ||
270 | if (!drvdata->base) | ||
271 | return -EBUSY; | ||
272 | |||
273 | hold = plat->hold ?: 20; /* tDH */ | ||
274 | spw = plat->spw ?: 90; /* max(tREADID, tWP, tRP) */ | ||
275 | |||
276 | hold = TXX9NDFMC_NS_TO_CYC(gbusclk, hold); | ||
277 | spw = TXX9NDFMC_NS_TO_CYC(gbusclk, spw); | ||
278 | if (plat->flags & NDFMC_PLAT_FLAG_HOLDADD) | ||
279 | hold -= 2; /* actual hold time : (HOLD + 2) BUSCLK */ | ||
280 | spw -= 1; /* actual wait time : (SPW + 1) BUSCLK */ | ||
281 | hold = clamp(hold, 1, 15); | ||
282 | drvdata->hold = hold; | ||
283 | spw = clamp(spw, 1, 15); | ||
284 | drvdata->spw = spw; | ||
285 | dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n", | ||
286 | (gbusclk + 500000) / 1000000, hold, spw); | ||
287 | |||
288 | spin_lock_init(&drvdata->hw_control.lock); | ||
289 | init_waitqueue_head(&drvdata->hw_control.wq); | ||
290 | |||
291 | platform_set_drvdata(dev, drvdata); | ||
292 | txx9ndfmc_initialize(dev); | ||
293 | |||
294 | for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { | ||
295 | struct txx9ndfmc_priv *txx9_priv; | ||
296 | struct nand_chip *chip; | ||
297 | struct mtd_info *mtd; | ||
298 | #ifdef CONFIG_MTD_PARTITIONS | ||
299 | int nr_parts; | ||
300 | #endif | ||
301 | |||
302 | if (!(plat->ch_mask & (1 << i))) | ||
303 | continue; | ||
304 | txx9_priv = kzalloc(sizeof(struct txx9ndfmc_priv), | ||
305 | GFP_KERNEL); | ||
306 | if (!txx9_priv) { | ||
307 | dev_err(&dev->dev, "Unable to allocate " | ||
308 | "TXx9 NDFMC MTD device structure.\n"); | ||
309 | continue; | ||
310 | } | ||
311 | chip = &txx9_priv->chip; | ||
312 | mtd = &txx9_priv->mtd; | ||
313 | mtd->owner = THIS_MODULE; | ||
314 | |||
315 | mtd->priv = chip; | ||
316 | |||
317 | chip->read_byte = txx9ndfmc_read_byte; | ||
318 | chip->read_buf = txx9ndfmc_read_buf; | ||
319 | chip->write_buf = txx9ndfmc_write_buf; | ||
320 | chip->verify_buf = txx9ndfmc_verify_buf; | ||
321 | chip->cmd_ctrl = txx9ndfmc_cmd_ctrl; | ||
322 | chip->dev_ready = txx9ndfmc_dev_ready; | ||
323 | chip->ecc.calculate = txx9ndfmc_calculate_ecc; | ||
324 | chip->ecc.correct = nand_correct_data; | ||
325 | chip->ecc.hwctl = txx9ndfmc_enable_hwecc; | ||
326 | chip->ecc.mode = NAND_ECC_HW; | ||
327 | chip->ecc.size = 256; | ||
328 | chip->ecc.bytes = 3; | ||
329 | chip->chip_delay = 100; | ||
330 | chip->controller = &drvdata->hw_control; | ||
331 | |||
332 | chip->priv = txx9_priv; | ||
333 | txx9_priv->dev = dev; | ||
334 | |||
335 | if (plat->ch_mask != 1) { | ||
336 | txx9_priv->cs = i; | ||
337 | sprintf(txx9_priv->mtdname, "%s.%u", | ||
338 | dev_name(&dev->dev), i); | ||
339 | } else { | ||
340 | txx9_priv->cs = -1; | ||
341 | strcpy(txx9_priv->mtdname, dev_name(&dev->dev)); | ||
342 | } | ||
343 | if (plat->wide_mask & (1 << i)) | ||
344 | chip->options |= NAND_BUSWIDTH_16; | ||
345 | |||
346 | if (nand_scan(mtd, 1)) { | ||
347 | kfree(txx9_priv); | ||
348 | continue; | ||
349 | } | ||
350 | mtd->name = txx9_priv->mtdname; | ||
351 | |||
352 | #ifdef CONFIG_MTD_PARTITIONS | ||
353 | nr_parts = parse_mtd_partitions(mtd, probes, | ||
354 | &drvdata->parts[i], 0); | ||
355 | if (nr_parts > 0) | ||
356 | add_mtd_partitions(mtd, drvdata->parts[i], nr_parts); | ||
357 | #endif | ||
358 | add_mtd_device(mtd); | ||
359 | drvdata->mtds[i] = mtd; | ||
360 | } | ||
361 | |||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | static int __exit txx9ndfmc_remove(struct platform_device *dev) | ||
366 | { | ||
367 | struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); | ||
368 | int i; | ||
369 | |||
370 | platform_set_drvdata(dev, NULL); | ||
371 | if (!drvdata) | ||
372 | return 0; | ||
373 | for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { | ||
374 | struct mtd_info *mtd = drvdata->mtds[i]; | ||
375 | struct nand_chip *chip; | ||
376 | struct txx9ndfmc_priv *txx9_priv; | ||
377 | |||
378 | if (!mtd) | ||
379 | continue; | ||
380 | chip = mtd->priv; | ||
381 | txx9_priv = chip->priv; | ||
382 | |||
383 | #ifdef CONFIG_MTD_PARTITIONS | ||
384 | del_mtd_partitions(mtd); | ||
385 | kfree(drvdata->parts[i]); | ||
386 | #endif | ||
387 | del_mtd_device(mtd); | ||
388 | kfree(txx9_priv); | ||
389 | } | ||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | #ifdef CONFIG_PM | ||
394 | static int txx9ndfmc_resume(struct platform_device *dev) | ||
395 | { | ||
396 | if (platform_get_drvdata(dev)) | ||
397 | txx9ndfmc_initialize(dev); | ||
398 | return 0; | ||
399 | } | ||
400 | #else | ||
401 | #define txx9ndfmc_resume NULL | ||
402 | #endif | ||
403 | |||
404 | static struct platform_driver txx9ndfmc_driver = { | ||
405 | .remove = __exit_p(txx9ndfmc_remove), | ||
406 | .resume = txx9ndfmc_resume, | ||
407 | .driver = { | ||
408 | .name = "txx9ndfmc", | ||
409 | .owner = THIS_MODULE, | ||
410 | }, | ||
411 | }; | ||
412 | |||
413 | static int __init txx9ndfmc_init(void) | ||
414 | { | ||
415 | return platform_driver_probe(&txx9ndfmc_driver, txx9ndfmc_probe); | ||
416 | } | ||
417 | |||
418 | static void __exit txx9ndfmc_exit(void) | ||
419 | { | ||
420 | platform_driver_unregister(&txx9ndfmc_driver); | ||
421 | } | ||
422 | |||
423 | module_init(txx9ndfmc_init); | ||
424 | module_exit(txx9ndfmc_exit); | ||
425 | |||
426 | MODULE_LICENSE("GPL"); | ||
427 | MODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver"); | ||
428 | MODULE_ALIAS("platform:txx9ndfmc"); | ||
diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index d1c4546513f7..e3f8495a94c2 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c | |||
@@ -15,11 +15,11 @@ | |||
15 | #include <asm/errno.h> | 15 | #include <asm/errno.h> |
16 | #include <asm/io.h> | 16 | #include <asm/io.h> |
17 | #include <asm/uaccess.h> | 17 | #include <asm/uaccess.h> |
18 | #include <linux/miscdevice.h> | ||
19 | #include <linux/delay.h> | 18 | #include <linux/delay.h> |
20 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
21 | #include <linux/init.h> | 20 | #include <linux/init.h> |
22 | #include <linux/hdreg.h> | 21 | #include <linux/hdreg.h> |
22 | #include <linux/blkdev.h> | ||
23 | 23 | ||
24 | #include <linux/kmod.h> | 24 | #include <linux/kmod.h> |
25 | #include <linux/mtd/mtd.h> | 25 | #include <linux/mtd/mtd.h> |
@@ -818,3 +818,4 @@ module_exit(cleanup_nftl); | |||
818 | MODULE_LICENSE("GPL"); | 818 | MODULE_LICENSE("GPL"); |
819 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al."); | 819 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al."); |
820 | MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium"); | 820 | MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium"); |
821 | MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR); | ||
diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index 9e45b3f39c0e..3e164f0c9295 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c | |||
@@ -46,6 +46,13 @@ int __devinit of_mtd_parse_partitions(struct device *dev, | |||
46 | const u32 *reg; | 46 | const u32 *reg; |
47 | int len; | 47 | int len; |
48 | 48 | ||
49 | /* check if this is a partition node */ | ||
50 | partname = of_get_property(pp, "name", &len); | ||
51 | if (strcmp(partname, "partition") != 0) { | ||
52 | nr_parts--; | ||
53 | continue; | ||
54 | } | ||
55 | |||
49 | reg = of_get_property(pp, "reg", &len); | 56 | reg = of_get_property(pp, "reg", &len); |
50 | if (!reg || (len != 2 * sizeof(u32))) { | 57 | if (!reg || (len != 2 * sizeof(u32))) { |
51 | of_node_put(pp); | 58 | of_node_put(pp); |
diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index 5b69e7773c6c..3a496c33fb52 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c | |||
@@ -36,10 +36,9 @@ struct onenand_info { | |||
36 | struct onenand_chip onenand; | 36 | struct onenand_chip onenand; |
37 | }; | 37 | }; |
38 | 38 | ||
39 | static int __devinit generic_onenand_probe(struct device *dev) | 39 | static int __devinit generic_onenand_probe(struct platform_device *pdev) |
40 | { | 40 | { |
41 | struct onenand_info *info; | 41 | struct onenand_info *info; |
42 | struct platform_device *pdev = to_platform_device(dev); | ||
43 | struct flash_platform_data *pdata = pdev->dev.platform_data; | 42 | struct flash_platform_data *pdata = pdev->dev.platform_data; |
44 | struct resource *res = pdev->resource; | 43 | struct resource *res = pdev->resource; |
45 | unsigned long size = res->end - res->start + 1; | 44 | unsigned long size = res->end - res->start + 1; |
@@ -49,7 +48,7 @@ static int __devinit generic_onenand_probe(struct device *dev) | |||
49 | if (!info) | 48 | if (!info) |
50 | return -ENOMEM; | 49 | return -ENOMEM; |
51 | 50 | ||
52 | if (!request_mem_region(res->start, size, dev->driver->name)) { | 51 | if (!request_mem_region(res->start, size, pdev->dev.driver->name)) { |
53 | err = -EBUSY; | 52 | err = -EBUSY; |
54 | goto out_free_info; | 53 | goto out_free_info; |
55 | } | 54 | } |
@@ -82,7 +81,7 @@ static int __devinit generic_onenand_probe(struct device *dev) | |||
82 | #endif | 81 | #endif |
83 | err = add_mtd_device(&info->mtd); | 82 | err = add_mtd_device(&info->mtd); |
84 | 83 | ||
85 | dev_set_drvdata(&pdev->dev, info); | 84 | platform_set_drvdata(pdev, info); |
86 | 85 | ||
87 | return 0; | 86 | return 0; |
88 | 87 | ||
@@ -96,14 +95,13 @@ out_free_info: | |||
96 | return err; | 95 | return err; |
97 | } | 96 | } |
98 | 97 | ||
99 | static int __devexit generic_onenand_remove(struct device *dev) | 98 | static int __devexit generic_onenand_remove(struct platform_device *pdev) |
100 | { | 99 | { |
101 | struct platform_device *pdev = to_platform_device(dev); | 100 | struct onenand_info *info = platform_get_drvdata(pdev); |
102 | struct onenand_info *info = dev_get_drvdata(&pdev->dev); | ||
103 | struct resource *res = pdev->resource; | 101 | struct resource *res = pdev->resource; |
104 | unsigned long size = res->end - res->start + 1; | 102 | unsigned long size = res->end - res->start + 1; |
105 | 103 | ||
106 | dev_set_drvdata(&pdev->dev, NULL); | 104 | platform_set_drvdata(pdev, NULL); |
107 | 105 | ||
108 | if (info) { | 106 | if (info) { |
109 | if (info->parts) | 107 | if (info->parts) |
@@ -120,9 +118,11 @@ static int __devexit generic_onenand_remove(struct device *dev) | |||
120 | return 0; | 118 | return 0; |
121 | } | 119 | } |
122 | 120 | ||
123 | static struct device_driver generic_onenand_driver = { | 121 | static struct platform_driver generic_onenand_driver = { |
124 | .name = DRIVER_NAME, | 122 | .driver = { |
125 | .bus = &platform_bus_type, | 123 | .name = DRIVER_NAME, |
124 | .owner = THIS_MODULE, | ||
125 | }, | ||
126 | .probe = generic_onenand_probe, | 126 | .probe = generic_onenand_probe, |
127 | .remove = __devexit_p(generic_onenand_remove), | 127 | .remove = __devexit_p(generic_onenand_remove), |
128 | }; | 128 | }; |
@@ -131,12 +131,12 @@ MODULE_ALIAS(DRIVER_NAME); | |||
131 | 131 | ||
132 | static int __init generic_onenand_init(void) | 132 | static int __init generic_onenand_init(void) |
133 | { | 133 | { |
134 | return driver_register(&generic_onenand_driver); | 134 | return platform_driver_register(&generic_onenand_driver); |
135 | } | 135 | } |
136 | 136 | ||
137 | static void __exit generic_onenand_exit(void) | 137 | static void __exit generic_onenand_exit(void) |
138 | { | 138 | { |
139 | driver_unregister(&generic_onenand_driver); | 139 | platform_driver_unregister(&generic_onenand_driver); |
140 | } | 140 | } |
141 | 141 | ||
142 | module_init(generic_onenand_init); | 142 | module_init(generic_onenand_init); |
diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 77a4f1446156..f2e9de1414df 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c | |||
@@ -294,6 +294,10 @@ static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, | |||
294 | if (bram_offset & 3 || (size_t)buf & 3 || count < 384) | 294 | if (bram_offset & 3 || (size_t)buf & 3 || count < 384) |
295 | goto out_copy; | 295 | goto out_copy; |
296 | 296 | ||
297 | /* panic_write() may be in an interrupt context */ | ||
298 | if (in_interrupt()) | ||
299 | goto out_copy; | ||
300 | |||
297 | if (buf >= high_memory) { | 301 | if (buf >= high_memory) { |
298 | struct page *p1; | 302 | struct page *p1; |
299 | 303 | ||
@@ -672,6 +676,8 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev) | |||
672 | c->mtd.priv = &c->onenand; | 676 | c->mtd.priv = &c->onenand; |
673 | c->mtd.owner = THIS_MODULE; | 677 | c->mtd.owner = THIS_MODULE; |
674 | 678 | ||
679 | c->mtd.dev.parent = &pdev->dev; | ||
680 | |||
675 | if (c->dma_channel >= 0) { | 681 | if (c->dma_channel >= 0) { |
676 | struct onenand_chip *this = &c->onenand; | 682 | struct onenand_chip *this = &c->onenand; |
677 | 683 | ||
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 529af271db17..30d6999e5f9f 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
@@ -1455,7 +1455,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, | |||
1455 | struct mtd_oob_ops *ops) | 1455 | struct mtd_oob_ops *ops) |
1456 | { | 1456 | { |
1457 | struct onenand_chip *this = mtd->priv; | 1457 | struct onenand_chip *this = mtd->priv; |
1458 | int written = 0, column, thislen, subpage; | 1458 | int written = 0, column, thislen = 0, subpage = 0; |
1459 | int prev = 0, prevlen = 0, prev_subpage = 0, first = 1; | ||
1459 | int oobwritten = 0, oobcolumn, thisooblen, oobsize; | 1460 | int oobwritten = 0, oobcolumn, thisooblen, oobsize; |
1460 | size_t len = ops->len; | 1461 | size_t len = ops->len; |
1461 | size_t ooblen = ops->ooblen; | 1462 | size_t ooblen = ops->ooblen; |
@@ -1482,6 +1483,10 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, | |||
1482 | return -EINVAL; | 1483 | return -EINVAL; |
1483 | } | 1484 | } |
1484 | 1485 | ||
1486 | /* Check zero length */ | ||
1487 | if (!len) | ||
1488 | return 0; | ||
1489 | |||
1485 | if (ops->mode == MTD_OOB_AUTO) | 1490 | if (ops->mode == MTD_OOB_AUTO) |
1486 | oobsize = this->ecclayout->oobavail; | 1491 | oobsize = this->ecclayout->oobavail; |
1487 | else | 1492 | else |
@@ -1492,79 +1497,121 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, | |||
1492 | column = to & (mtd->writesize - 1); | 1497 | column = to & (mtd->writesize - 1); |
1493 | 1498 | ||
1494 | /* Loop until all data write */ | 1499 | /* Loop until all data write */ |
1495 | while (written < len) { | 1500 | while (1) { |
1496 | u_char *wbuf = (u_char *) buf; | 1501 | if (written < len) { |
1502 | u_char *wbuf = (u_char *) buf; | ||
1497 | 1503 | ||
1498 | thislen = min_t(int, mtd->writesize - column, len - written); | 1504 | thislen = min_t(int, mtd->writesize - column, len - written); |
1499 | thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten); | 1505 | thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten); |
1500 | 1506 | ||
1501 | cond_resched(); | 1507 | cond_resched(); |
1502 | 1508 | ||
1503 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); | 1509 | this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); |
1504 | 1510 | ||
1505 | /* Partial page write */ | 1511 | /* Partial page write */ |
1506 | subpage = thislen < mtd->writesize; | 1512 | subpage = thislen < mtd->writesize; |
1507 | if (subpage) { | 1513 | if (subpage) { |
1508 | memset(this->page_buf, 0xff, mtd->writesize); | 1514 | memset(this->page_buf, 0xff, mtd->writesize); |
1509 | memcpy(this->page_buf + column, buf, thislen); | 1515 | memcpy(this->page_buf + column, buf, thislen); |
1510 | wbuf = this->page_buf; | 1516 | wbuf = this->page_buf; |
1511 | } | 1517 | } |
1512 | 1518 | ||
1513 | this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); | 1519 | this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); |
1514 | 1520 | ||
1515 | if (oob) { | 1521 | if (oob) { |
1516 | oobbuf = this->oob_buf; | 1522 | oobbuf = this->oob_buf; |
1517 | 1523 | ||
1518 | /* We send data to spare ram with oobsize | 1524 | /* We send data to spare ram with oobsize |
1519 | * to prevent byte access */ | 1525 | * to prevent byte access */ |
1520 | memset(oobbuf, 0xff, mtd->oobsize); | 1526 | memset(oobbuf, 0xff, mtd->oobsize); |
1521 | if (ops->mode == MTD_OOB_AUTO) | 1527 | if (ops->mode == MTD_OOB_AUTO) |
1522 | onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen); | 1528 | onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen); |
1523 | else | 1529 | else |
1524 | memcpy(oobbuf + oobcolumn, oob, thisooblen); | 1530 | memcpy(oobbuf + oobcolumn, oob, thisooblen); |
1525 | 1531 | ||
1526 | oobwritten += thisooblen; | 1532 | oobwritten += thisooblen; |
1527 | oob += thisooblen; | 1533 | oob += thisooblen; |
1528 | oobcolumn = 0; | 1534 | oobcolumn = 0; |
1535 | } else | ||
1536 | oobbuf = (u_char *) ffchars; | ||
1537 | |||
1538 | this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); | ||
1529 | } else | 1539 | } else |
1530 | oobbuf = (u_char *) ffchars; | 1540 | ONENAND_SET_NEXT_BUFFERRAM(this); |
1531 | 1541 | ||
1532 | this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); | 1542 | /* |
1543 | * 2 PLANE, MLC, and Flex-OneNAND doesn't support | ||
1544 | * write-while-programe feature. | ||
1545 | */ | ||
1546 | if (!ONENAND_IS_2PLANE(this) && !first) { | ||
1547 | ONENAND_SET_PREV_BUFFERRAM(this); | ||
1533 | 1548 | ||
1534 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); | 1549 | ret = this->wait(mtd, FL_WRITING); |
1535 | 1550 | ||
1536 | ret = this->wait(mtd, FL_WRITING); | 1551 | /* In partial page write we don't update bufferram */ |
1552 | onenand_update_bufferram(mtd, prev, !ret && !prev_subpage); | ||
1553 | if (ret) { | ||
1554 | written -= prevlen; | ||
1555 | printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); | ||
1556 | break; | ||
1557 | } | ||
1537 | 1558 | ||
1538 | /* In partial page write we don't update bufferram */ | 1559 | if (written == len) { |
1539 | onenand_update_bufferram(mtd, to, !ret && !subpage); | 1560 | /* Only check verify write turn on */ |
1540 | if (ONENAND_IS_2PLANE(this)) { | 1561 | ret = onenand_verify(mtd, buf - len, to - len, len); |
1541 | ONENAND_SET_BUFFERRAM1(this); | 1562 | if (ret) |
1542 | onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage); | 1563 | printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); |
1543 | } | 1564 | break; |
1565 | } | ||
1544 | 1566 | ||
1545 | if (ret) { | 1567 | ONENAND_SET_NEXT_BUFFERRAM(this); |
1546 | printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); | ||
1547 | break; | ||
1548 | } | 1568 | } |
1549 | 1569 | ||
1550 | /* Only check verify write turn on */ | 1570 | this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); |
1551 | ret = onenand_verify(mtd, buf, to, thislen); | ||
1552 | if (ret) { | ||
1553 | printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); | ||
1554 | break; | ||
1555 | } | ||
1556 | 1571 | ||
1557 | written += thislen; | 1572 | /* |
1573 | * 2 PLANE, MLC, and Flex-OneNAND wait here | ||
1574 | */ | ||
1575 | if (ONENAND_IS_2PLANE(this)) { | ||
1576 | ret = this->wait(mtd, FL_WRITING); | ||
1558 | 1577 | ||
1559 | if (written == len) | 1578 | /* In partial page write we don't update bufferram */ |
1560 | break; | 1579 | onenand_update_bufferram(mtd, to, !ret && !subpage); |
1580 | if (ret) { | ||
1581 | printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret); | ||
1582 | break; | ||
1583 | } | ||
1584 | |||
1585 | /* Only check verify write turn on */ | ||
1586 | ret = onenand_verify(mtd, buf, to, thislen); | ||
1587 | if (ret) { | ||
1588 | printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret); | ||
1589 | break; | ||
1590 | } | ||
1591 | |||
1592 | written += thislen; | ||
1593 | |||
1594 | if (written == len) | ||
1595 | break; | ||
1596 | |||
1597 | } else | ||
1598 | written += thislen; | ||
1561 | 1599 | ||
1562 | column = 0; | 1600 | column = 0; |
1601 | prev_subpage = subpage; | ||
1602 | prev = to; | ||
1603 | prevlen = thislen; | ||
1563 | to += thislen; | 1604 | to += thislen; |
1564 | buf += thislen; | 1605 | buf += thislen; |
1606 | first = 0; | ||
1565 | } | 1607 | } |
1566 | 1608 | ||
1609 | /* In error case, clear all bufferrams */ | ||
1610 | if (written != len) | ||
1611 | onenand_invalidate_bufferram(mtd, 0, -1); | ||
1612 | |||
1567 | ops->retlen = written; | 1613 | ops->retlen = written; |
1614 | ops->oobretlen = oobwritten; | ||
1568 | 1615 | ||
1569 | return ret; | 1616 | return ret; |
1570 | } | 1617 | } |
diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/mtd_oobtest.c index afbc3f8126db..a18e8d2f2557 100644 --- a/drivers/mtd/tests/mtd_oobtest.c +++ b/drivers/mtd/tests/mtd_oobtest.c | |||
@@ -136,7 +136,7 @@ static int write_eraseblock(int ebnum) | |||
136 | ops.ooblen = use_len; | 136 | ops.ooblen = use_len; |
137 | ops.oobretlen = 0; | 137 | ops.oobretlen = 0; |
138 | ops.ooboffs = use_offset; | 138 | ops.ooboffs = use_offset; |
139 | ops.datbuf = 0; | 139 | ops.datbuf = NULL; |
140 | ops.oobbuf = writebuf; | 140 | ops.oobbuf = writebuf; |
141 | err = mtd->write_oob(mtd, addr, &ops); | 141 | err = mtd->write_oob(mtd, addr, &ops); |
142 | if (err || ops.oobretlen != use_len) { | 142 | if (err || ops.oobretlen != use_len) { |
@@ -189,7 +189,7 @@ static int verify_eraseblock(int ebnum) | |||
189 | ops.ooblen = use_len; | 189 | ops.ooblen = use_len; |
190 | ops.oobretlen = 0; | 190 | ops.oobretlen = 0; |
191 | ops.ooboffs = use_offset; | 191 | ops.ooboffs = use_offset; |
192 | ops.datbuf = 0; | 192 | ops.datbuf = NULL; |
193 | ops.oobbuf = readbuf; | 193 | ops.oobbuf = readbuf; |
194 | err = mtd->read_oob(mtd, addr, &ops); | 194 | err = mtd->read_oob(mtd, addr, &ops); |
195 | if (err || ops.oobretlen != use_len) { | 195 | if (err || ops.oobretlen != use_len) { |
@@ -216,7 +216,7 @@ static int verify_eraseblock(int ebnum) | |||
216 | ops.ooblen = mtd->ecclayout->oobavail; | 216 | ops.ooblen = mtd->ecclayout->oobavail; |
217 | ops.oobretlen = 0; | 217 | ops.oobretlen = 0; |
218 | ops.ooboffs = 0; | 218 | ops.ooboffs = 0; |
219 | ops.datbuf = 0; | 219 | ops.datbuf = NULL; |
220 | ops.oobbuf = readbuf; | 220 | ops.oobbuf = readbuf; |
221 | err = mtd->read_oob(mtd, addr, &ops); | 221 | err = mtd->read_oob(mtd, addr, &ops); |
222 | if (err || ops.oobretlen != mtd->ecclayout->oobavail) { | 222 | if (err || ops.oobretlen != mtd->ecclayout->oobavail) { |
@@ -281,7 +281,7 @@ static int verify_eraseblock_in_one_go(int ebnum) | |||
281 | ops.ooblen = len; | 281 | ops.ooblen = len; |
282 | ops.oobretlen = 0; | 282 | ops.oobretlen = 0; |
283 | ops.ooboffs = 0; | 283 | ops.ooboffs = 0; |
284 | ops.datbuf = 0; | 284 | ops.datbuf = NULL; |
285 | ops.oobbuf = readbuf; | 285 | ops.oobbuf = readbuf; |
286 | err = mtd->read_oob(mtd, addr, &ops); | 286 | err = mtd->read_oob(mtd, addr, &ops); |
287 | if (err || ops.oobretlen != len) { | 287 | if (err || ops.oobretlen != len) { |
@@ -522,7 +522,7 @@ static int __init mtd_oobtest_init(void) | |||
522 | ops.ooblen = 1; | 522 | ops.ooblen = 1; |
523 | ops.oobretlen = 0; | 523 | ops.oobretlen = 0; |
524 | ops.ooboffs = mtd->ecclayout->oobavail; | 524 | ops.ooboffs = mtd->ecclayout->oobavail; |
525 | ops.datbuf = 0; | 525 | ops.datbuf = NULL; |
526 | ops.oobbuf = writebuf; | 526 | ops.oobbuf = writebuf; |
527 | printk(PRINT_PREF "attempting to start write past end of OOB\n"); | 527 | printk(PRINT_PREF "attempting to start write past end of OOB\n"); |
528 | printk(PRINT_PREF "an error is expected...\n"); | 528 | printk(PRINT_PREF "an error is expected...\n"); |
@@ -542,7 +542,7 @@ static int __init mtd_oobtest_init(void) | |||
542 | ops.ooblen = 1; | 542 | ops.ooblen = 1; |
543 | ops.oobretlen = 0; | 543 | ops.oobretlen = 0; |
544 | ops.ooboffs = mtd->ecclayout->oobavail; | 544 | ops.ooboffs = mtd->ecclayout->oobavail; |
545 | ops.datbuf = 0; | 545 | ops.datbuf = NULL; |
546 | ops.oobbuf = readbuf; | 546 | ops.oobbuf = readbuf; |
547 | printk(PRINT_PREF "attempting to start read past end of OOB\n"); | 547 | printk(PRINT_PREF "attempting to start read past end of OOB\n"); |
548 | printk(PRINT_PREF "an error is expected...\n"); | 548 | printk(PRINT_PREF "an error is expected...\n"); |
@@ -566,7 +566,7 @@ static int __init mtd_oobtest_init(void) | |||
566 | ops.ooblen = mtd->ecclayout->oobavail + 1; | 566 | ops.ooblen = mtd->ecclayout->oobavail + 1; |
567 | ops.oobretlen = 0; | 567 | ops.oobretlen = 0; |
568 | ops.ooboffs = 0; | 568 | ops.ooboffs = 0; |
569 | ops.datbuf = 0; | 569 | ops.datbuf = NULL; |
570 | ops.oobbuf = writebuf; | 570 | ops.oobbuf = writebuf; |
571 | printk(PRINT_PREF "attempting to write past end of device\n"); | 571 | printk(PRINT_PREF "attempting to write past end of device\n"); |
572 | printk(PRINT_PREF "an error is expected...\n"); | 572 | printk(PRINT_PREF "an error is expected...\n"); |
@@ -586,7 +586,7 @@ static int __init mtd_oobtest_init(void) | |||
586 | ops.ooblen = mtd->ecclayout->oobavail + 1; | 586 | ops.ooblen = mtd->ecclayout->oobavail + 1; |
587 | ops.oobretlen = 0; | 587 | ops.oobretlen = 0; |
588 | ops.ooboffs = 0; | 588 | ops.ooboffs = 0; |
589 | ops.datbuf = 0; | 589 | ops.datbuf = NULL; |
590 | ops.oobbuf = readbuf; | 590 | ops.oobbuf = readbuf; |
591 | printk(PRINT_PREF "attempting to read past end of device\n"); | 591 | printk(PRINT_PREF "attempting to read past end of device\n"); |
592 | printk(PRINT_PREF "an error is expected...\n"); | 592 | printk(PRINT_PREF "an error is expected...\n"); |
@@ -610,7 +610,7 @@ static int __init mtd_oobtest_init(void) | |||
610 | ops.ooblen = mtd->ecclayout->oobavail; | 610 | ops.ooblen = mtd->ecclayout->oobavail; |
611 | ops.oobretlen = 0; | 611 | ops.oobretlen = 0; |
612 | ops.ooboffs = 1; | 612 | ops.ooboffs = 1; |
613 | ops.datbuf = 0; | 613 | ops.datbuf = NULL; |
614 | ops.oobbuf = writebuf; | 614 | ops.oobbuf = writebuf; |
615 | printk(PRINT_PREF "attempting to write past end of device\n"); | 615 | printk(PRINT_PREF "attempting to write past end of device\n"); |
616 | printk(PRINT_PREF "an error is expected...\n"); | 616 | printk(PRINT_PREF "an error is expected...\n"); |
@@ -630,7 +630,7 @@ static int __init mtd_oobtest_init(void) | |||
630 | ops.ooblen = mtd->ecclayout->oobavail; | 630 | ops.ooblen = mtd->ecclayout->oobavail; |
631 | ops.oobretlen = 0; | 631 | ops.oobretlen = 0; |
632 | ops.ooboffs = 1; | 632 | ops.ooboffs = 1; |
633 | ops.datbuf = 0; | 633 | ops.datbuf = NULL; |
634 | ops.oobbuf = readbuf; | 634 | ops.oobbuf = readbuf; |
635 | printk(PRINT_PREF "attempting to read past end of device\n"); | 635 | printk(PRINT_PREF "attempting to read past end of device\n"); |
636 | printk(PRINT_PREF "an error is expected...\n"); | 636 | printk(PRINT_PREF "an error is expected...\n"); |
@@ -670,7 +670,7 @@ static int __init mtd_oobtest_init(void) | |||
670 | ops.ooblen = sz; | 670 | ops.ooblen = sz; |
671 | ops.oobretlen = 0; | 671 | ops.oobretlen = 0; |
672 | ops.ooboffs = 0; | 672 | ops.ooboffs = 0; |
673 | ops.datbuf = 0; | 673 | ops.datbuf = NULL; |
674 | ops.oobbuf = writebuf; | 674 | ops.oobbuf = writebuf; |
675 | err = mtd->write_oob(mtd, addr, &ops); | 675 | err = mtd->write_oob(mtd, addr, &ops); |
676 | if (err) | 676 | if (err) |
@@ -698,7 +698,7 @@ static int __init mtd_oobtest_init(void) | |||
698 | ops.ooblen = mtd->ecclayout->oobavail * 2; | 698 | ops.ooblen = mtd->ecclayout->oobavail * 2; |
699 | ops.oobretlen = 0; | 699 | ops.oobretlen = 0; |
700 | ops.ooboffs = 0; | 700 | ops.ooboffs = 0; |
701 | ops.datbuf = 0; | 701 | ops.datbuf = NULL; |
702 | ops.oobbuf = readbuf; | 702 | ops.oobbuf = readbuf; |
703 | err = mtd->read_oob(mtd, addr, &ops); | 703 | err = mtd->read_oob(mtd, addr, &ops); |
704 | if (err) | 704 | if (err) |
diff --git a/drivers/mtd/tests/mtd_readtest.c b/drivers/mtd/tests/mtd_readtest.c index 645e77fdc63d..79fc4530987b 100644 --- a/drivers/mtd/tests/mtd_readtest.c +++ b/drivers/mtd/tests/mtd_readtest.c | |||
@@ -71,7 +71,7 @@ static int read_eraseblock_by_page(int ebnum) | |||
71 | ops.ooblen = mtd->oobsize; | 71 | ops.ooblen = mtd->oobsize; |
72 | ops.oobretlen = 0; | 72 | ops.oobretlen = 0; |
73 | ops.ooboffs = 0; | 73 | ops.ooboffs = 0; |
74 | ops.datbuf = 0; | 74 | ops.datbuf = NULL; |
75 | ops.oobbuf = oobbuf; | 75 | ops.oobbuf = oobbuf; |
76 | ret = mtd->read_oob(mtd, addr, &ops); | 76 | ret = mtd->read_oob(mtd, addr, &ops); |
77 | if (ret || ops.oobretlen != mtd->oobsize) { | 77 | if (ret || ops.oobretlen != mtd->oobsize) { |