diff options
Diffstat (limited to 'drivers/mtd/maps')
-rw-r--r-- | drivers/mtd/maps/bcm963xx-flash.c | 276 | ||||
-rw-r--r-- | drivers/mtd/maps/cdb89712.c | 278 | ||||
-rw-r--r-- | drivers/mtd/maps/ceiva.c | 341 | ||||
-rw-r--r-- | drivers/mtd/maps/edb7312.c | 134 | ||||
-rw-r--r-- | drivers/mtd/maps/fortunet.c | 277 | ||||
-rw-r--r-- | drivers/mtd/maps/tegra_nor.c | 483 | ||||
-rw-r--r-- | drivers/mtd/maps/wr_sbc82xx_flash.c | 181 |
7 files changed, 1970 insertions, 0 deletions
diff --git a/drivers/mtd/maps/bcm963xx-flash.c b/drivers/mtd/maps/bcm963xx-flash.c new file mode 100644 index 00000000000..608967fe74c --- /dev/null +++ b/drivers/mtd/maps/bcm963xx-flash.c | |||
@@ -0,0 +1,276 @@ | |||
1 | /* | ||
2 | * Copyright © 2006-2008 Florian Fainelli <florian@openwrt.org> | ||
3 | * Mike Albon <malbon@openwrt.org> | ||
4 | * Copyright © 2009-2010 Daniel Dickinson <openwrt@cshore.neomailbox.net> | ||
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 as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
19 | */ | ||
20 | |||
21 | #include <linux/init.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/mtd/map.h> | ||
25 | #include <linux/mtd/mtd.h> | ||
26 | #include <linux/mtd/partitions.h> | ||
27 | #include <linux/vmalloc.h> | ||
28 | #include <linux/platform_device.h> | ||
29 | #include <linux/io.h> | ||
30 | |||
31 | #include <asm/mach-bcm63xx/bcm963xx_tag.h> | ||
32 | |||
33 | #define BCM63XX_BUSWIDTH 2 /* Buswidth */ | ||
34 | #define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */ | ||
35 | |||
36 | #define PFX KBUILD_MODNAME ": " | ||
37 | |||
38 | static struct mtd_partition *parsed_parts; | ||
39 | |||
40 | static struct mtd_info *bcm963xx_mtd_info; | ||
41 | |||
42 | static struct map_info bcm963xx_map = { | ||
43 | .name = "bcm963xx", | ||
44 | .bankwidth = BCM63XX_BUSWIDTH, | ||
45 | }; | ||
46 | |||
47 | static int parse_cfe_partitions(struct mtd_info *master, | ||
48 | struct mtd_partition **pparts) | ||
49 | { | ||
50 | /* CFE, NVRAM and global Linux are always present */ | ||
51 | int nrparts = 3, curpart = 0; | ||
52 | struct bcm_tag *buf; | ||
53 | struct mtd_partition *parts; | ||
54 | int ret; | ||
55 | size_t retlen; | ||
56 | unsigned int rootfsaddr, kerneladdr, spareaddr; | ||
57 | unsigned int rootfslen, kernellen, sparelen, totallen; | ||
58 | int namelen = 0; | ||
59 | int i; | ||
60 | char *boardid; | ||
61 | char *tagversion; | ||
62 | |||
63 | /* Allocate memory for buffer */ | ||
64 | buf = vmalloc(sizeof(struct bcm_tag)); | ||
65 | if (!buf) | ||
66 | return -ENOMEM; | ||
67 | |||
68 | /* Get the tag */ | ||
69 | ret = master->read(master, master->erasesize, sizeof(struct bcm_tag), | ||
70 | &retlen, (void *)buf); | ||
71 | if (retlen != sizeof(struct bcm_tag)) { | ||
72 | vfree(buf); | ||
73 | return -EIO; | ||
74 | } | ||
75 | |||
76 | sscanf(buf->kernel_address, "%u", &kerneladdr); | ||
77 | sscanf(buf->kernel_length, "%u", &kernellen); | ||
78 | sscanf(buf->total_length, "%u", &totallen); | ||
79 | tagversion = &(buf->tag_version[0]); | ||
80 | boardid = &(buf->board_id[0]); | ||
81 | |||
82 | printk(KERN_INFO PFX "CFE boot tag found with version %s " | ||
83 | "and board type %s\n", tagversion, boardid); | ||
84 | |||
85 | kerneladdr = kerneladdr - BCM63XX_EXTENDED_SIZE; | ||
86 | rootfsaddr = kerneladdr + kernellen; | ||
87 | spareaddr = roundup(totallen, master->erasesize) + master->erasesize; | ||
88 | sparelen = master->size - spareaddr - master->erasesize; | ||
89 | rootfslen = spareaddr - rootfsaddr; | ||
90 | |||
91 | /* Determine number of partitions */ | ||
92 | namelen = 8; | ||
93 | if (rootfslen > 0) { | ||
94 | nrparts++; | ||
95 | namelen += 6; | ||
96 | }; | ||
97 | if (kernellen > 0) { | ||
98 | nrparts++; | ||
99 | namelen += 6; | ||
100 | }; | ||
101 | |||
102 | /* Ask kernel for more memory */ | ||
103 | parts = kzalloc(sizeof(*parts) * nrparts + 10 * nrparts, GFP_KERNEL); | ||
104 | if (!parts) { | ||
105 | vfree(buf); | ||
106 | return -ENOMEM; | ||
107 | }; | ||
108 | |||
109 | /* Start building partition list */ | ||
110 | parts[curpart].name = "CFE"; | ||
111 | parts[curpart].offset = 0; | ||
112 | parts[curpart].size = master->erasesize; | ||
113 | curpart++; | ||
114 | |||
115 | if (kernellen > 0) { | ||
116 | parts[curpart].name = "kernel"; | ||
117 | parts[curpart].offset = kerneladdr; | ||
118 | parts[curpart].size = kernellen; | ||
119 | curpart++; | ||
120 | }; | ||
121 | |||
122 | if (rootfslen > 0) { | ||
123 | parts[curpart].name = "rootfs"; | ||
124 | parts[curpart].offset = rootfsaddr; | ||
125 | parts[curpart].size = rootfslen; | ||
126 | if (sparelen > 0) | ||
127 | parts[curpart].size += sparelen; | ||
128 | curpart++; | ||
129 | }; | ||
130 | |||
131 | parts[curpart].name = "nvram"; | ||
132 | parts[curpart].offset = master->size - master->erasesize; | ||
133 | parts[curpart].size = master->erasesize; | ||
134 | |||
135 | /* Global partition "linux" to make easy firmware upgrade */ | ||
136 | curpart++; | ||
137 | parts[curpart].name = "linux"; | ||
138 | parts[curpart].offset = parts[0].size; | ||
139 | parts[curpart].size = master->size - parts[0].size - parts[3].size; | ||
140 | |||
141 | for (i = 0; i < nrparts; i++) | ||
142 | printk(KERN_INFO PFX "Partition %d is %s offset %lx and " | ||
143 | "length %lx\n", i, parts[i].name, | ||
144 | (long unsigned int)(parts[i].offset), | ||
145 | (long unsigned int)(parts[i].size)); | ||
146 | |||
147 | printk(KERN_INFO PFX "Spare partition is %x offset and length %x\n", | ||
148 | spareaddr, sparelen); | ||
149 | *pparts = parts; | ||
150 | vfree(buf); | ||
151 | |||
152 | return nrparts; | ||
153 | }; | ||
154 | |||
155 | static int bcm963xx_detect_cfe(struct mtd_info *master) | ||
156 | { | ||
157 | int idoffset = 0x4e0; | ||
158 | static char idstring[8] = "CFE1CFE1"; | ||
159 | char buf[9]; | ||
160 | int ret; | ||
161 | size_t retlen; | ||
162 | |||
163 | ret = master->read(master, idoffset, 8, &retlen, (void *)buf); | ||
164 | buf[retlen] = 0; | ||
165 | printk(KERN_INFO PFX "Read Signature value of %s\n", buf); | ||
166 | |||
167 | return strncmp(idstring, buf, 8); | ||
168 | } | ||
169 | |||
170 | static int bcm963xx_probe(struct platform_device *pdev) | ||
171 | { | ||
172 | int err = 0; | ||
173 | int parsed_nr_parts = 0; | ||
174 | char *part_type; | ||
175 | struct resource *r; | ||
176 | |||
177 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
178 | if (!r) { | ||
179 | dev_err(&pdev->dev, "no resource supplied\n"); | ||
180 | return -ENODEV; | ||
181 | } | ||
182 | |||
183 | bcm963xx_map.phys = r->start; | ||
184 | bcm963xx_map.size = resource_size(r); | ||
185 | bcm963xx_map.virt = ioremap(r->start, resource_size(r)); | ||
186 | if (!bcm963xx_map.virt) { | ||
187 | dev_err(&pdev->dev, "failed to ioremap\n"); | ||
188 | return -EIO; | ||
189 | } | ||
190 | |||
191 | dev_info(&pdev->dev, "0x%08lx at 0x%08x\n", | ||
192 | bcm963xx_map.size, bcm963xx_map.phys); | ||
193 | |||
194 | simple_map_init(&bcm963xx_map); | ||
195 | |||
196 | bcm963xx_mtd_info = do_map_probe("cfi_probe", &bcm963xx_map); | ||
197 | if (!bcm963xx_mtd_info) { | ||
198 | dev_err(&pdev->dev, "failed to probe using CFI\n"); | ||
199 | bcm963xx_mtd_info = do_map_probe("jedec_probe", &bcm963xx_map); | ||
200 | if (bcm963xx_mtd_info) | ||
201 | goto probe_ok; | ||
202 | dev_err(&pdev->dev, "failed to probe using JEDEC\n"); | ||
203 | err = -EIO; | ||
204 | goto err_probe; | ||
205 | } | ||
206 | |||
207 | probe_ok: | ||
208 | bcm963xx_mtd_info->owner = THIS_MODULE; | ||
209 | |||
210 | /* This is mutually exclusive */ | ||
211 | if (bcm963xx_detect_cfe(bcm963xx_mtd_info) == 0) { | ||
212 | dev_info(&pdev->dev, "CFE bootloader detected\n"); | ||
213 | if (parsed_nr_parts == 0) { | ||
214 | int ret = parse_cfe_partitions(bcm963xx_mtd_info, | ||
215 | &parsed_parts); | ||
216 | if (ret > 0) { | ||
217 | part_type = "CFE"; | ||
218 | parsed_nr_parts = ret; | ||
219 | } | ||
220 | } | ||
221 | } else { | ||
222 | dev_info(&pdev->dev, "unsupported bootloader\n"); | ||
223 | err = -ENODEV; | ||
224 | goto err_probe; | ||
225 | } | ||
226 | |||
227 | return mtd_device_register(bcm963xx_mtd_info, parsed_parts, | ||
228 | parsed_nr_parts); | ||
229 | |||
230 | err_probe: | ||
231 | iounmap(bcm963xx_map.virt); | ||
232 | return err; | ||
233 | } | ||
234 | |||
235 | static int bcm963xx_remove(struct platform_device *pdev) | ||
236 | { | ||
237 | if (bcm963xx_mtd_info) { | ||
238 | mtd_device_unregister(bcm963xx_mtd_info); | ||
239 | map_destroy(bcm963xx_mtd_info); | ||
240 | } | ||
241 | |||
242 | if (bcm963xx_map.virt) { | ||
243 | iounmap(bcm963xx_map.virt); | ||
244 | bcm963xx_map.virt = 0; | ||
245 | } | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | static struct platform_driver bcm63xx_mtd_dev = { | ||
251 | .probe = bcm963xx_probe, | ||
252 | .remove = bcm963xx_remove, | ||
253 | .driver = { | ||
254 | .name = "bcm963xx-flash", | ||
255 | .owner = THIS_MODULE, | ||
256 | }, | ||
257 | }; | ||
258 | |||
259 | static int __init bcm963xx_mtd_init(void) | ||
260 | { | ||
261 | return platform_driver_register(&bcm63xx_mtd_dev); | ||
262 | } | ||
263 | |||
264 | static void __exit bcm963xx_mtd_exit(void) | ||
265 | { | ||
266 | platform_driver_unregister(&bcm63xx_mtd_dev); | ||
267 | } | ||
268 | |||
269 | module_init(bcm963xx_mtd_init); | ||
270 | module_exit(bcm963xx_mtd_exit); | ||
271 | |||
272 | MODULE_LICENSE("GPL"); | ||
273 | MODULE_DESCRIPTION("Broadcom BCM63xx MTD driver for CFE and RedBoot"); | ||
274 | MODULE_AUTHOR("Daniel Dickinson <openwrt@cshore.neomailbox.net>"); | ||
275 | MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); | ||
276 | MODULE_AUTHOR("Mike Albon <malbon@openwrt.org>"); | ||
diff --git a/drivers/mtd/maps/cdb89712.c b/drivers/mtd/maps/cdb89712.c new file mode 100644 index 00000000000..c29cbf87ea0 --- /dev/null +++ b/drivers/mtd/maps/cdb89712.c | |||
@@ -0,0 +1,278 @@ | |||
1 | /* | ||
2 | * Flash on Cirrus CDB89712 | ||
3 | * | ||
4 | */ | ||
5 | |||
6 | #include <linux/module.h> | ||
7 | #include <linux/types.h> | ||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/ioport.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <asm/io.h> | ||
12 | #include <mach/hardware.h> | ||
13 | #include <linux/mtd/mtd.h> | ||
14 | #include <linux/mtd/map.h> | ||
15 | #include <linux/mtd/partitions.h> | ||
16 | |||
17 | /* dynamic ioremap() areas */ | ||
18 | #define FLASH_START 0x00000000 | ||
19 | #define FLASH_SIZE 0x800000 | ||
20 | #define FLASH_WIDTH 4 | ||
21 | |||
22 | #define SRAM_START 0x60000000 | ||
23 | #define SRAM_SIZE 0xc000 | ||
24 | #define SRAM_WIDTH 4 | ||
25 | |||
26 | #define BOOTROM_START 0x70000000 | ||
27 | #define BOOTROM_SIZE 0x80 | ||
28 | #define BOOTROM_WIDTH 4 | ||
29 | |||
30 | |||
31 | static struct mtd_info *flash_mtd; | ||
32 | |||
33 | struct map_info cdb89712_flash_map = { | ||
34 | .name = "flash", | ||
35 | .size = FLASH_SIZE, | ||
36 | .bankwidth = FLASH_WIDTH, | ||
37 | .phys = FLASH_START, | ||
38 | }; | ||
39 | |||
40 | struct resource cdb89712_flash_resource = { | ||
41 | .name = "Flash", | ||
42 | .start = FLASH_START, | ||
43 | .end = FLASH_START + FLASH_SIZE - 1, | ||
44 | .flags = IORESOURCE_IO | IORESOURCE_BUSY, | ||
45 | }; | ||
46 | |||
47 | static int __init init_cdb89712_flash (void) | ||
48 | { | ||
49 | int err; | ||
50 | |||
51 | if (request_resource (&ioport_resource, &cdb89712_flash_resource)) { | ||
52 | printk(KERN_NOTICE "Failed to reserve Cdb89712 FLASH space\n"); | ||
53 | err = -EBUSY; | ||
54 | goto out; | ||
55 | } | ||
56 | |||
57 | cdb89712_flash_map.virt = ioremap(FLASH_START, FLASH_SIZE); | ||
58 | if (!cdb89712_flash_map.virt) { | ||
59 | printk(KERN_NOTICE "Failed to ioremap Cdb89712 FLASH space\n"); | ||
60 | err = -EIO; | ||
61 | goto out_resource; | ||
62 | } | ||
63 | simple_map_init(&cdb89712_flash_map); | ||
64 | flash_mtd = do_map_probe("cfi_probe", &cdb89712_flash_map); | ||
65 | if (!flash_mtd) { | ||
66 | flash_mtd = do_map_probe("map_rom", &cdb89712_flash_map); | ||
67 | if (flash_mtd) | ||
68 | flash_mtd->erasesize = 0x10000; | ||
69 | } | ||
70 | if (!flash_mtd) { | ||
71 | printk("FLASH probe failed\n"); | ||
72 | err = -ENXIO; | ||
73 | goto out_ioremap; | ||
74 | } | ||
75 | |||
76 | flash_mtd->owner = THIS_MODULE; | ||
77 | |||
78 | if (mtd_device_register(flash_mtd, NULL, 0)) { | ||
79 | printk("FLASH device addition failed\n"); | ||
80 | err = -ENOMEM; | ||
81 | goto out_probe; | ||
82 | } | ||
83 | |||
84 | return 0; | ||
85 | |||
86 | out_probe: | ||
87 | map_destroy(flash_mtd); | ||
88 | flash_mtd = 0; | ||
89 | out_ioremap: | ||
90 | iounmap((void *)cdb89712_flash_map.virt); | ||
91 | out_resource: | ||
92 | release_resource (&cdb89712_flash_resource); | ||
93 | out: | ||
94 | return err; | ||
95 | } | ||
96 | |||
97 | |||
98 | |||
99 | |||
100 | |||
101 | static struct mtd_info *sram_mtd; | ||
102 | |||
103 | struct map_info cdb89712_sram_map = { | ||
104 | .name = "SRAM", | ||
105 | .size = SRAM_SIZE, | ||
106 | .bankwidth = SRAM_WIDTH, | ||
107 | .phys = SRAM_START, | ||
108 | }; | ||
109 | |||
110 | struct resource cdb89712_sram_resource = { | ||
111 | .name = "SRAM", | ||
112 | .start = SRAM_START, | ||
113 | .end = SRAM_START + SRAM_SIZE - 1, | ||
114 | .flags = IORESOURCE_IO | IORESOURCE_BUSY, | ||
115 | }; | ||
116 | |||
117 | static int __init init_cdb89712_sram (void) | ||
118 | { | ||
119 | int err; | ||
120 | |||
121 | if (request_resource (&ioport_resource, &cdb89712_sram_resource)) { | ||
122 | printk(KERN_NOTICE "Failed to reserve Cdb89712 SRAM space\n"); | ||
123 | err = -EBUSY; | ||
124 | goto out; | ||
125 | } | ||
126 | |||
127 | cdb89712_sram_map.virt = ioremap(SRAM_START, SRAM_SIZE); | ||
128 | if (!cdb89712_sram_map.virt) { | ||
129 | printk(KERN_NOTICE "Failed to ioremap Cdb89712 SRAM space\n"); | ||
130 | err = -EIO; | ||
131 | goto out_resource; | ||
132 | } | ||
133 | simple_map_init(&cdb89712_sram_map); | ||
134 | sram_mtd = do_map_probe("map_ram", &cdb89712_sram_map); | ||
135 | if (!sram_mtd) { | ||
136 | printk("SRAM probe failed\n"); | ||
137 | err = -ENXIO; | ||
138 | goto out_ioremap; | ||
139 | } | ||
140 | |||
141 | sram_mtd->owner = THIS_MODULE; | ||
142 | sram_mtd->erasesize = 16; | ||
143 | |||
144 | if (mtd_device_register(sram_mtd, NULL, 0)) { | ||
145 | printk("SRAM device addition failed\n"); | ||
146 | err = -ENOMEM; | ||
147 | goto out_probe; | ||
148 | } | ||
149 | |||
150 | return 0; | ||
151 | |||
152 | out_probe: | ||
153 | map_destroy(sram_mtd); | ||
154 | sram_mtd = 0; | ||
155 | out_ioremap: | ||
156 | iounmap((void *)cdb89712_sram_map.virt); | ||
157 | out_resource: | ||
158 | release_resource (&cdb89712_sram_resource); | ||
159 | out: | ||
160 | return err; | ||
161 | } | ||
162 | |||
163 | |||
164 | |||
165 | |||
166 | |||
167 | |||
168 | |||
169 | static struct mtd_info *bootrom_mtd; | ||
170 | |||
171 | struct map_info cdb89712_bootrom_map = { | ||
172 | .name = "BootROM", | ||
173 | .size = BOOTROM_SIZE, | ||
174 | .bankwidth = BOOTROM_WIDTH, | ||
175 | .phys = BOOTROM_START, | ||
176 | }; | ||
177 | |||
178 | struct resource cdb89712_bootrom_resource = { | ||
179 | .name = "BootROM", | ||
180 | .start = BOOTROM_START, | ||
181 | .end = BOOTROM_START + BOOTROM_SIZE - 1, | ||
182 | .flags = IORESOURCE_IO | IORESOURCE_BUSY, | ||
183 | }; | ||
184 | |||
185 | static int __init init_cdb89712_bootrom (void) | ||
186 | { | ||
187 | int err; | ||
188 | |||
189 | if (request_resource (&ioport_resource, &cdb89712_bootrom_resource)) { | ||
190 | printk(KERN_NOTICE "Failed to reserve Cdb89712 BOOTROM space\n"); | ||
191 | err = -EBUSY; | ||
192 | goto out; | ||
193 | } | ||
194 | |||
195 | cdb89712_bootrom_map.virt = ioremap(BOOTROM_START, BOOTROM_SIZE); | ||
196 | if (!cdb89712_bootrom_map.virt) { | ||
197 | printk(KERN_NOTICE "Failed to ioremap Cdb89712 BootROM space\n"); | ||
198 | err = -EIO; | ||
199 | goto out_resource; | ||
200 | } | ||
201 | simple_map_init(&cdb89712_bootrom_map); | ||
202 | bootrom_mtd = do_map_probe("map_rom", &cdb89712_bootrom_map); | ||
203 | if (!bootrom_mtd) { | ||
204 | printk("BootROM probe failed\n"); | ||
205 | err = -ENXIO; | ||
206 | goto out_ioremap; | ||
207 | } | ||
208 | |||
209 | bootrom_mtd->owner = THIS_MODULE; | ||
210 | bootrom_mtd->erasesize = 0x10000; | ||
211 | |||
212 | if (mtd_device_register(bootrom_mtd, NULL, 0)) { | ||
213 | printk("BootROM device addition failed\n"); | ||
214 | err = -ENOMEM; | ||
215 | goto out_probe; | ||
216 | } | ||
217 | |||
218 | return 0; | ||
219 | |||
220 | out_probe: | ||
221 | map_destroy(bootrom_mtd); | ||
222 | bootrom_mtd = 0; | ||
223 | out_ioremap: | ||
224 | iounmap((void *)cdb89712_bootrom_map.virt); | ||
225 | out_resource: | ||
226 | release_resource (&cdb89712_bootrom_resource); | ||
227 | out: | ||
228 | return err; | ||
229 | } | ||
230 | |||
231 | |||
232 | |||
233 | |||
234 | |||
235 | static int __init init_cdb89712_maps(void) | ||
236 | { | ||
237 | |||
238 | printk(KERN_INFO "Cirrus CDB89712 MTD mappings:\n Flash 0x%x at 0x%x\n SRAM 0x%x at 0x%x\n BootROM 0x%x at 0x%x\n", | ||
239 | FLASH_SIZE, FLASH_START, SRAM_SIZE, SRAM_START, BOOTROM_SIZE, BOOTROM_START); | ||
240 | |||
241 | init_cdb89712_flash(); | ||
242 | init_cdb89712_sram(); | ||
243 | init_cdb89712_bootrom(); | ||
244 | |||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | |||
249 | static void __exit cleanup_cdb89712_maps(void) | ||
250 | { | ||
251 | if (sram_mtd) { | ||
252 | mtd_device_unregister(sram_mtd); | ||
253 | map_destroy(sram_mtd); | ||
254 | iounmap((void *)cdb89712_sram_map.virt); | ||
255 | release_resource (&cdb89712_sram_resource); | ||
256 | } | ||
257 | |||
258 | if (flash_mtd) { | ||
259 | mtd_device_unregister(flash_mtd); | ||
260 | map_destroy(flash_mtd); | ||
261 | iounmap((void *)cdb89712_flash_map.virt); | ||
262 | release_resource (&cdb89712_flash_resource); | ||
263 | } | ||
264 | |||
265 | if (bootrom_mtd) { | ||
266 | mtd_device_unregister(bootrom_mtd); | ||
267 | map_destroy(bootrom_mtd); | ||
268 | iounmap((void *)cdb89712_bootrom_map.virt); | ||
269 | release_resource (&cdb89712_bootrom_resource); | ||
270 | } | ||
271 | } | ||
272 | |||
273 | module_init(init_cdb89712_maps); | ||
274 | module_exit(cleanup_cdb89712_maps); | ||
275 | |||
276 | MODULE_AUTHOR("Ray L"); | ||
277 | MODULE_DESCRIPTION("ARM CDB89712 map driver"); | ||
278 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mtd/maps/ceiva.c b/drivers/mtd/maps/ceiva.c new file mode 100644 index 00000000000..06f9c981572 --- /dev/null +++ b/drivers/mtd/maps/ceiva.c | |||
@@ -0,0 +1,341 @@ | |||
1 | /* | ||
2 | * Ceiva flash memory driver. | ||
3 | * Copyright (C) 2002 Rob Scott <rscott@mtrob.fdns.net> | ||
4 | * | ||
5 | * Note: this driver supports jedec compatible devices. Modification | ||
6 | * for CFI compatible devices should be straight forward: change | ||
7 | * jedec_probe to cfi_probe. | ||
8 | * | ||
9 | * Based on: sa1100-flash.c, which has the following copyright: | ||
10 | * Flash memory access on SA11x0 based devices | ||
11 | * | ||
12 | * (C) 2000 Nicolas Pitre <nico@fluxnic.net> | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/ioport.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/slab.h> | ||
22 | |||
23 | #include <linux/mtd/mtd.h> | ||
24 | #include <linux/mtd/map.h> | ||
25 | #include <linux/mtd/partitions.h> | ||
26 | #include <linux/mtd/concat.h> | ||
27 | |||
28 | #include <mach/hardware.h> | ||
29 | #include <asm/mach-types.h> | ||
30 | #include <asm/io.h> | ||
31 | #include <asm/sizes.h> | ||
32 | |||
33 | /* | ||
34 | * This isn't complete yet, so... | ||
35 | */ | ||
36 | #define CONFIG_MTD_CEIVA_STATICMAP | ||
37 | |||
38 | #ifdef CONFIG_MTD_CEIVA_STATICMAP | ||
39 | /* | ||
40 | * See include/linux/mtd/partitions.h for definition of the mtd_partition | ||
41 | * structure. | ||
42 | * | ||
43 | * Please note: | ||
44 | * 1. The flash size given should be the largest flash size that can | ||
45 | * be accommodated. | ||
46 | * | ||
47 | * 2. The bus width must defined in clps_setup_flash. | ||
48 | * | ||
49 | * The MTD layer will detect flash chip aliasing and reduce the size of | ||
50 | * the map accordingly. | ||
51 | * | ||
52 | */ | ||
53 | |||
54 | #ifdef CONFIG_ARCH_CEIVA | ||
55 | /* Flash / Partition sizing */ | ||
56 | /* For the 28F8003, we use the block mapping to calcuate the sizes */ | ||
57 | #define MAX_SIZE_KiB (16 + 8 + 8 + 96 + (7*128)) | ||
58 | #define BOOT_PARTITION_SIZE_KiB (16) | ||
59 | #define PARAMS_PARTITION_SIZE_KiB (8) | ||
60 | #define KERNEL_PARTITION_SIZE_KiB (4*128) | ||
61 | /* Use both remaining portion of first flash, and all of second flash */ | ||
62 | #define ROOT_PARTITION_SIZE_KiB (3*128) + (8*128) | ||
63 | |||
64 | static struct mtd_partition ceiva_partitions[] = { | ||
65 | { | ||
66 | .name = "Ceiva BOOT partition", | ||
67 | .size = BOOT_PARTITION_SIZE_KiB*1024, | ||
68 | .offset = 0, | ||
69 | |||
70 | },{ | ||
71 | .name = "Ceiva parameters partition", | ||
72 | .size = PARAMS_PARTITION_SIZE_KiB*1024, | ||
73 | .offset = (16 + 8) * 1024, | ||
74 | },{ | ||
75 | .name = "Ceiva kernel partition", | ||
76 | .size = (KERNEL_PARTITION_SIZE_KiB)*1024, | ||
77 | .offset = 0x20000, | ||
78 | |||
79 | },{ | ||
80 | .name = "Ceiva root filesystem partition", | ||
81 | .offset = MTDPART_OFS_APPEND, | ||
82 | .size = (ROOT_PARTITION_SIZE_KiB)*1024, | ||
83 | } | ||
84 | }; | ||
85 | #endif | ||
86 | |||
87 | static int __init clps_static_partitions(struct mtd_partition **parts) | ||
88 | { | ||
89 | int nb_parts = 0; | ||
90 | |||
91 | #ifdef CONFIG_ARCH_CEIVA | ||
92 | if (machine_is_ceiva()) { | ||
93 | *parts = ceiva_partitions; | ||
94 | nb_parts = ARRAY_SIZE(ceiva_partitions); | ||
95 | } | ||
96 | #endif | ||
97 | return nb_parts; | ||
98 | } | ||
99 | #endif | ||
100 | |||
101 | struct clps_info { | ||
102 | unsigned long base; | ||
103 | unsigned long size; | ||
104 | int width; | ||
105 | void *vbase; | ||
106 | struct map_info *map; | ||
107 | struct mtd_info *mtd; | ||
108 | struct resource *res; | ||
109 | }; | ||
110 | |||
111 | #define NR_SUBMTD 4 | ||
112 | |||
113 | static struct clps_info info[NR_SUBMTD]; | ||
114 | |||
115 | static int __init clps_setup_mtd(struct clps_info *clps, int nr, struct mtd_info **rmtd) | ||
116 | { | ||
117 | struct mtd_info *subdev[nr]; | ||
118 | struct map_info *maps; | ||
119 | int i, found = 0, ret = 0; | ||
120 | |||
121 | /* | ||
122 | * Allocate the map_info structs in one go. | ||
123 | */ | ||
124 | maps = kzalloc(sizeof(struct map_info) * nr, GFP_KERNEL); | ||
125 | if (!maps) | ||
126 | return -ENOMEM; | ||
127 | /* | ||
128 | * Claim and then map the memory regions. | ||
129 | */ | ||
130 | for (i = 0; i < nr; i++) { | ||
131 | if (clps[i].base == (unsigned long)-1) | ||
132 | break; | ||
133 | |||
134 | clps[i].res = request_mem_region(clps[i].base, clps[i].size, "clps flash"); | ||
135 | if (!clps[i].res) { | ||
136 | ret = -EBUSY; | ||
137 | break; | ||
138 | } | ||
139 | |||
140 | clps[i].map = maps + i; | ||
141 | |||
142 | clps[i].map->name = "clps flash"; | ||
143 | clps[i].map->phys = clps[i].base; | ||
144 | |||
145 | clps[i].vbase = ioremap(clps[i].base, clps[i].size); | ||
146 | if (!clps[i].vbase) { | ||
147 | ret = -ENOMEM; | ||
148 | break; | ||
149 | } | ||
150 | |||
151 | clps[i].map->virt = (void __iomem *)clps[i].vbase; | ||
152 | clps[i].map->bankwidth = clps[i].width; | ||
153 | clps[i].map->size = clps[i].size; | ||
154 | |||
155 | simple_map_init(&clps[i].map); | ||
156 | |||
157 | clps[i].mtd = do_map_probe("jedec_probe", clps[i].map); | ||
158 | if (clps[i].mtd == NULL) { | ||
159 | ret = -ENXIO; | ||
160 | break; | ||
161 | } | ||
162 | clps[i].mtd->owner = THIS_MODULE; | ||
163 | subdev[i] = clps[i].mtd; | ||
164 | |||
165 | printk(KERN_INFO "clps flash: JEDEC device at 0x%08lx, %dMiB, " | ||
166 | "%d-bit\n", clps[i].base, clps[i].mtd->size >> 20, | ||
167 | clps[i].width * 8); | ||
168 | found += 1; | ||
169 | } | ||
170 | |||
171 | /* | ||
172 | * ENXIO is special. It means we didn't find a chip when | ||
173 | * we probed. We need to tear down the mapping, free the | ||
174 | * resource and mark it as such. | ||
175 | */ | ||
176 | if (ret == -ENXIO) { | ||
177 | iounmap(clps[i].vbase); | ||
178 | clps[i].vbase = NULL; | ||
179 | release_resource(clps[i].res); | ||
180 | clps[i].res = NULL; | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * If we found one device, don't bother with concat support. | ||
185 | * If we found multiple devices, use concat if we have it | ||
186 | * available, otherwise fail. | ||
187 | */ | ||
188 | if (ret == 0 || ret == -ENXIO) { | ||
189 | if (found == 1) { | ||
190 | *rmtd = subdev[0]; | ||
191 | ret = 0; | ||
192 | } else if (found > 1) { | ||
193 | /* | ||
194 | * We detected multiple devices. Concatenate | ||
195 | * them together. | ||
196 | */ | ||
197 | *rmtd = mtd_concat_create(subdev, found, | ||
198 | "clps flash"); | ||
199 | if (*rmtd == NULL) | ||
200 | ret = -ENXIO; | ||
201 | } | ||
202 | } | ||
203 | |||
204 | /* | ||
205 | * If we failed, clean up. | ||
206 | */ | ||
207 | if (ret) { | ||
208 | do { | ||
209 | if (clps[i].mtd) | ||
210 | map_destroy(clps[i].mtd); | ||
211 | if (clps[i].vbase) | ||
212 | iounmap(clps[i].vbase); | ||
213 | if (clps[i].res) | ||
214 | release_resource(clps[i].res); | ||
215 | } while (i--); | ||
216 | |||
217 | kfree(maps); | ||
218 | } | ||
219 | |||
220 | return ret; | ||
221 | } | ||
222 | |||
223 | static void __exit clps_destroy_mtd(struct clps_info *clps, struct mtd_info *mtd) | ||
224 | { | ||
225 | int i; | ||
226 | |||
227 | mtd_device_unregister(mtd); | ||
228 | |||
229 | if (mtd != clps[0].mtd) | ||
230 | mtd_concat_destroy(mtd); | ||
231 | |||
232 | for (i = NR_SUBMTD; i >= 0; i--) { | ||
233 | if (clps[i].mtd) | ||
234 | map_destroy(clps[i].mtd); | ||
235 | if (clps[i].vbase) | ||
236 | iounmap(clps[i].vbase); | ||
237 | if (clps[i].res) | ||
238 | release_resource(clps[i].res); | ||
239 | } | ||
240 | kfree(clps[0].map); | ||
241 | } | ||
242 | |||
243 | /* | ||
244 | * We define the memory space, size, and width for the flash memory | ||
245 | * space here. | ||
246 | */ | ||
247 | |||
248 | static int __init clps_setup_flash(void) | ||
249 | { | ||
250 | int nr = 0; | ||
251 | |||
252 | #ifdef CONFIG_ARCH_CEIVA | ||
253 | if (machine_is_ceiva()) { | ||
254 | info[0].base = CS0_PHYS_BASE; | ||
255 | info[0].size = SZ_32M; | ||
256 | info[0].width = CEIVA_FLASH_WIDTH; | ||
257 | info[1].base = CS1_PHYS_BASE; | ||
258 | info[1].size = SZ_32M; | ||
259 | info[1].width = CEIVA_FLASH_WIDTH; | ||
260 | nr = 2; | ||
261 | } | ||
262 | #endif | ||
263 | return nr; | ||
264 | } | ||
265 | |||
266 | static struct mtd_partition *parsed_parts; | ||
267 | static const char *probes[] = { "cmdlinepart", "RedBoot", NULL }; | ||
268 | |||
269 | static void __init clps_locate_partitions(struct mtd_info *mtd) | ||
270 | { | ||
271 | const char *part_type = NULL; | ||
272 | int nr_parts = 0; | ||
273 | do { | ||
274 | /* | ||
275 | * Partition selection stuff. | ||
276 | */ | ||
277 | nr_parts = parse_mtd_partitions(mtd, probes, &parsed_parts, 0); | ||
278 | if (nr_parts > 0) { | ||
279 | part_type = "command line"; | ||
280 | break; | ||
281 | } | ||
282 | #ifdef CONFIG_MTD_CEIVA_STATICMAP | ||
283 | nr_parts = clps_static_partitions(&parsed_parts); | ||
284 | if (nr_parts > 0) { | ||
285 | part_type = "static"; | ||
286 | break; | ||
287 | } | ||
288 | printk("found: %d partitions\n", nr_parts); | ||
289 | #endif | ||
290 | } while (0); | ||
291 | |||
292 | if (nr_parts == 0) { | ||
293 | printk(KERN_NOTICE "clps flash: no partition info " | ||
294 | "available, registering whole flash\n"); | ||
295 | mtd_device_register(mtd, NULL, 0); | ||
296 | } else { | ||
297 | printk(KERN_NOTICE "clps flash: using %s partition " | ||
298 | "definition\n", part_type); | ||
299 | mtd_device_register(mtd, parsed_parts, nr_parts); | ||
300 | } | ||
301 | |||
302 | /* Always succeeds. */ | ||
303 | } | ||
304 | |||
305 | static void __exit clps_destroy_partitions(void) | ||
306 | { | ||
307 | kfree(parsed_parts); | ||
308 | } | ||
309 | |||
310 | static struct mtd_info *mymtd; | ||
311 | |||
312 | static int __init clps_mtd_init(void) | ||
313 | { | ||
314 | int ret; | ||
315 | int nr; | ||
316 | |||
317 | nr = clps_setup_flash(); | ||
318 | if (nr < 0) | ||
319 | return nr; | ||
320 | |||
321 | ret = clps_setup_mtd(info, nr, &mymtd); | ||
322 | if (ret) | ||
323 | return ret; | ||
324 | |||
325 | clps_locate_partitions(mymtd); | ||
326 | |||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | static void __exit clps_mtd_cleanup(void) | ||
331 | { | ||
332 | clps_destroy_mtd(info, mymtd); | ||
333 | clps_destroy_partitions(); | ||
334 | } | ||
335 | |||
336 | module_init(clps_mtd_init); | ||
337 | module_exit(clps_mtd_cleanup); | ||
338 | |||
339 | MODULE_AUTHOR("Rob Scott"); | ||
340 | MODULE_DESCRIPTION("Cirrus Logic JEDEC map driver"); | ||
341 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mtd/maps/edb7312.c b/drivers/mtd/maps/edb7312.c new file mode 100644 index 00000000000..fe42a212bb3 --- /dev/null +++ b/drivers/mtd/maps/edb7312.c | |||
@@ -0,0 +1,134 @@ | |||
1 | /* | ||
2 | * Handle mapping of the NOR flash on Cogent EDB7312 boards | ||
3 | * | ||
4 | * Copyright 2002 SYSGO Real-Time Solutions GmbH | ||
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 | |||
11 | #include <linux/module.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <asm/io.h> | ||
16 | #include <linux/mtd/mtd.h> | ||
17 | #include <linux/mtd/map.h> | ||
18 | #include <linux/mtd/partitions.h> | ||
19 | |||
20 | #define WINDOW_ADDR 0x00000000 /* physical properties of flash */ | ||
21 | #define WINDOW_SIZE 0x01000000 | ||
22 | #define BUSWIDTH 2 | ||
23 | #define FLASH_BLOCKSIZE_MAIN 0x20000 | ||
24 | #define FLASH_NUMBLOCKS_MAIN 128 | ||
25 | /* can be "cfi_probe", "jedec_probe", "map_rom", NULL }; */ | ||
26 | #define PROBETYPES { "cfi_probe", NULL } | ||
27 | |||
28 | #define MSG_PREFIX "EDB7312-NOR:" /* prefix for our printk()'s */ | ||
29 | #define MTDID "edb7312-nor" /* for mtdparts= partitioning */ | ||
30 | |||
31 | static struct mtd_info *mymtd; | ||
32 | |||
33 | struct map_info edb7312nor_map = { | ||
34 | .name = "NOR flash on EDB7312", | ||
35 | .size = WINDOW_SIZE, | ||
36 | .bankwidth = BUSWIDTH, | ||
37 | .phys = WINDOW_ADDR, | ||
38 | }; | ||
39 | |||
40 | /* | ||
41 | * MTD partitioning stuff | ||
42 | */ | ||
43 | static struct mtd_partition static_partitions[3] = | ||
44 | { | ||
45 | { | ||
46 | .name = "ARMboot", | ||
47 | .size = 0x40000, | ||
48 | .offset = 0 | ||
49 | }, | ||
50 | { | ||
51 | .name = "Kernel", | ||
52 | .size = 0x200000, | ||
53 | .offset = 0x40000 | ||
54 | }, | ||
55 | { | ||
56 | .name = "RootFS", | ||
57 | .size = 0xDC0000, | ||
58 | .offset = 0x240000 | ||
59 | }, | ||
60 | }; | ||
61 | |||
62 | static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; | ||
63 | |||
64 | static int mtd_parts_nb = 0; | ||
65 | static struct mtd_partition *mtd_parts = 0; | ||
66 | |||
67 | static int __init init_edb7312nor(void) | ||
68 | { | ||
69 | static const char *rom_probe_types[] = PROBETYPES; | ||
70 | const char **type; | ||
71 | const char *part_type = 0; | ||
72 | |||
73 | printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n", | ||
74 | WINDOW_SIZE, WINDOW_ADDR); | ||
75 | edb7312nor_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); | ||
76 | |||
77 | if (!edb7312nor_map.virt) { | ||
78 | printk(MSG_PREFIX "failed to ioremap\n"); | ||
79 | return -EIO; | ||
80 | } | ||
81 | |||
82 | simple_map_init(&edb7312nor_map); | ||
83 | |||
84 | mymtd = 0; | ||
85 | type = rom_probe_types; | ||
86 | for(; !mymtd && *type; type++) { | ||
87 | mymtd = do_map_probe(*type, &edb7312nor_map); | ||
88 | } | ||
89 | if (mymtd) { | ||
90 | mymtd->owner = THIS_MODULE; | ||
91 | |||
92 | mtd_parts_nb = parse_mtd_partitions(mymtd, probes, &mtd_parts, MTDID); | ||
93 | if (mtd_parts_nb > 0) | ||
94 | part_type = "detected"; | ||
95 | |||
96 | if (mtd_parts_nb == 0) { | ||
97 | mtd_parts = static_partitions; | ||
98 | mtd_parts_nb = ARRAY_SIZE(static_partitions); | ||
99 | part_type = "static"; | ||
100 | } | ||
101 | |||
102 | if (mtd_parts_nb == 0) | ||
103 | printk(KERN_NOTICE MSG_PREFIX "no partition info available\n"); | ||
104 | else | ||
105 | printk(KERN_NOTICE MSG_PREFIX | ||
106 | "using %s partition definition\n", part_type); | ||
107 | /* Register the whole device first. */ | ||
108 | mtd_device_register(mymtd, NULL, 0); | ||
109 | mtd_device_register(mymtd, mtd_parts, mtd_parts_nb); | ||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | iounmap((void *)edb7312nor_map.virt); | ||
114 | return -ENXIO; | ||
115 | } | ||
116 | |||
117 | static void __exit cleanup_edb7312nor(void) | ||
118 | { | ||
119 | if (mymtd) { | ||
120 | mtd_device_unregister(mymtd); | ||
121 | map_destroy(mymtd); | ||
122 | } | ||
123 | if (edb7312nor_map.virt) { | ||
124 | iounmap((void *)edb7312nor_map.virt); | ||
125 | edb7312nor_map.virt = 0; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | module_init(init_edb7312nor); | ||
130 | module_exit(cleanup_edb7312nor); | ||
131 | |||
132 | MODULE_LICENSE("GPL"); | ||
133 | MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>"); | ||
134 | MODULE_DESCRIPTION("Generic configurable MTD map driver"); | ||
diff --git a/drivers/mtd/maps/fortunet.c b/drivers/mtd/maps/fortunet.c new file mode 100644 index 00000000000..956e2e4f30e --- /dev/null +++ b/drivers/mtd/maps/fortunet.c | |||
@@ -0,0 +1,277 @@ | |||
1 | /* fortunet.c memory map | ||
2 | * | ||
3 | */ | ||
4 | |||
5 | #include <linux/module.h> | ||
6 | #include <linux/types.h> | ||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/init.h> | ||
9 | #include <linux/string.h> | ||
10 | |||
11 | #include <linux/mtd/mtd.h> | ||
12 | #include <linux/mtd/map.h> | ||
13 | #include <linux/mtd/partitions.h> | ||
14 | |||
15 | #include <asm/io.h> | ||
16 | |||
17 | #define MAX_NUM_REGIONS 4 | ||
18 | #define MAX_NUM_PARTITIONS 8 | ||
19 | |||
20 | #define DEF_WINDOW_ADDR_PHY 0x00000000 | ||
21 | #define DEF_WINDOW_SIZE 0x00800000 // 8 Mega Bytes | ||
22 | |||
23 | #define MTD_FORTUNET_PK "MTD FortuNet: " | ||
24 | |||
25 | #define MAX_NAME_SIZE 128 | ||
26 | |||
27 | struct map_region | ||
28 | { | ||
29 | int window_addr_physical; | ||
30 | int altbankwidth; | ||
31 | struct map_info map_info; | ||
32 | struct mtd_info *mymtd; | ||
33 | struct mtd_partition parts[MAX_NUM_PARTITIONS]; | ||
34 | char map_name[MAX_NAME_SIZE]; | ||
35 | char parts_name[MAX_NUM_PARTITIONS][MAX_NAME_SIZE]; | ||
36 | }; | ||
37 | |||
38 | static struct map_region map_regions[MAX_NUM_REGIONS]; | ||
39 | static int map_regions_set[MAX_NUM_REGIONS] = {0,0,0,0}; | ||
40 | static int map_regions_parts[MAX_NUM_REGIONS] = {0,0,0,0}; | ||
41 | |||
42 | |||
43 | |||
44 | struct map_info default_map = { | ||
45 | .size = DEF_WINDOW_SIZE, | ||
46 | .bankwidth = 4, | ||
47 | }; | ||
48 | |||
49 | static char * __init get_string_option(char *dest,int dest_size,char *sor) | ||
50 | { | ||
51 | if(!dest_size) | ||
52 | return sor; | ||
53 | dest_size--; | ||
54 | while(*sor) | ||
55 | { | ||
56 | if(*sor==',') | ||
57 | { | ||
58 | sor++; | ||
59 | break; | ||
60 | } | ||
61 | else if(*sor=='\"') | ||
62 | { | ||
63 | sor++; | ||
64 | while(*sor) | ||
65 | { | ||
66 | if(*sor=='\"') | ||
67 | { | ||
68 | sor++; | ||
69 | break; | ||
70 | } | ||
71 | *dest = *sor; | ||
72 | dest++; | ||
73 | sor++; | ||
74 | dest_size--; | ||
75 | if(!dest_size) | ||
76 | { | ||
77 | *dest = 0; | ||
78 | return sor; | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | else | ||
83 | { | ||
84 | *dest = *sor; | ||
85 | dest++; | ||
86 | sor++; | ||
87 | dest_size--; | ||
88 | if(!dest_size) | ||
89 | { | ||
90 | *dest = 0; | ||
91 | return sor; | ||
92 | } | ||
93 | } | ||
94 | } | ||
95 | *dest = 0; | ||
96 | return sor; | ||
97 | } | ||
98 | |||
99 | static int __init MTD_New_Region(char *line) | ||
100 | { | ||
101 | char string[MAX_NAME_SIZE]; | ||
102 | int params[6]; | ||
103 | get_options (get_string_option(string,sizeof(string),line),6,params); | ||
104 | if(params[0]<1) | ||
105 | { | ||
106 | printk(MTD_FORTUNET_PK "Bad parameters for MTD Region " | ||
107 | " name,region-number[,base,size,bankwidth,altbankwidth]\n"); | ||
108 | return 1; | ||
109 | } | ||
110 | if((params[1]<0)||(params[1]>=MAX_NUM_REGIONS)) | ||
111 | { | ||
112 | printk(MTD_FORTUNET_PK "Bad region index of %d only have 0..%u regions\n", | ||
113 | params[1],MAX_NUM_REGIONS-1); | ||
114 | return 1; | ||
115 | } | ||
116 | memset(&map_regions[params[1]],0,sizeof(map_regions[params[1]])); | ||
117 | memcpy(&map_regions[params[1]].map_info, | ||
118 | &default_map,sizeof(map_regions[params[1]].map_info)); | ||
119 | map_regions_set[params[1]] = 1; | ||
120 | map_regions[params[1]].window_addr_physical = DEF_WINDOW_ADDR_PHY; | ||
121 | map_regions[params[1]].altbankwidth = 2; | ||
122 | map_regions[params[1]].mymtd = NULL; | ||
123 | map_regions[params[1]].map_info.name = map_regions[params[1]].map_name; | ||
124 | strcpy(map_regions[params[1]].map_info.name,string); | ||
125 | if(params[0]>1) | ||
126 | { | ||
127 | map_regions[params[1]].window_addr_physical = params[2]; | ||
128 | } | ||
129 | if(params[0]>2) | ||
130 | { | ||
131 | map_regions[params[1]].map_info.size = params[3]; | ||
132 | } | ||
133 | if(params[0]>3) | ||
134 | { | ||
135 | map_regions[params[1]].map_info.bankwidth = params[4]; | ||
136 | } | ||
137 | if(params[0]>4) | ||
138 | { | ||
139 | map_regions[params[1]].altbankwidth = params[5]; | ||
140 | } | ||
141 | return 1; | ||
142 | } | ||
143 | |||
144 | static int __init MTD_New_Partition(char *line) | ||
145 | { | ||
146 | char string[MAX_NAME_SIZE]; | ||
147 | int params[4]; | ||
148 | get_options (get_string_option(string,sizeof(string),line),4,params); | ||
149 | if(params[0]<3) | ||
150 | { | ||
151 | printk(MTD_FORTUNET_PK "Bad parameters for MTD Partition " | ||
152 | " name,region-number,size,offset\n"); | ||
153 | return 1; | ||
154 | } | ||
155 | if((params[1]<0)||(params[1]>=MAX_NUM_REGIONS)) | ||
156 | { | ||
157 | printk(MTD_FORTUNET_PK "Bad region index of %d only have 0..%u regions\n", | ||
158 | params[1],MAX_NUM_REGIONS-1); | ||
159 | return 1; | ||
160 | } | ||
161 | if(map_regions_parts[params[1]]>=MAX_NUM_PARTITIONS) | ||
162 | { | ||
163 | printk(MTD_FORTUNET_PK "Out of space for partition in this region\n"); | ||
164 | return 1; | ||
165 | } | ||
166 | map_regions[params[1]].parts[map_regions_parts[params[1]]].name = | ||
167 | map_regions[params[1]]. parts_name[map_regions_parts[params[1]]]; | ||
168 | strcpy(map_regions[params[1]].parts[map_regions_parts[params[1]]].name,string); | ||
169 | map_regions[params[1]].parts[map_regions_parts[params[1]]].size = | ||
170 | params[2]; | ||
171 | map_regions[params[1]].parts[map_regions_parts[params[1]]].offset = | ||
172 | params[3]; | ||
173 | map_regions[params[1]].parts[map_regions_parts[params[1]]].mask_flags = 0; | ||
174 | map_regions_parts[params[1]]++; | ||
175 | return 1; | ||
176 | } | ||
177 | |||
178 | __setup("MTD_Region=", MTD_New_Region); | ||
179 | __setup("MTD_Partition=", MTD_New_Partition); | ||
180 | |||
181 | /* Backwards-spelling-compatibility */ | ||
182 | __setup("MTD_Partion=", MTD_New_Partition); | ||
183 | |||
184 | static int __init init_fortunet(void) | ||
185 | { | ||
186 | int ix,iy; | ||
187 | for(iy=ix=0;ix<MAX_NUM_REGIONS;ix++) | ||
188 | { | ||
189 | if(map_regions_parts[ix]&&(!map_regions_set[ix])) | ||
190 | { | ||
191 | printk(MTD_FORTUNET_PK "Region %d is not setup (Setting to default)\n", | ||
192 | ix); | ||
193 | memset(&map_regions[ix],0,sizeof(map_regions[ix])); | ||
194 | memcpy(&map_regions[ix].map_info,&default_map, | ||
195 | sizeof(map_regions[ix].map_info)); | ||
196 | map_regions_set[ix] = 1; | ||
197 | map_regions[ix].window_addr_physical = DEF_WINDOW_ADDR_PHY; | ||
198 | map_regions[ix].altbankwidth = 2; | ||
199 | map_regions[ix].mymtd = NULL; | ||
200 | map_regions[ix].map_info.name = map_regions[ix].map_name; | ||
201 | strcpy(map_regions[ix].map_info.name,"FORTUNET"); | ||
202 | } | ||
203 | if(map_regions_set[ix]) | ||
204 | { | ||
205 | iy++; | ||
206 | printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at physically " | ||
207 | " address %x size %x\n", | ||
208 | map_regions[ix].map_info.name, | ||
209 | map_regions[ix].window_addr_physical, | ||
210 | map_regions[ix].map_info.size); | ||
211 | |||
212 | map_regions[ix].map_info.phys = map_regions[ix].window_addr_physical, | ||
213 | |||
214 | map_regions[ix].map_info.virt = | ||
215 | ioremap_nocache( | ||
216 | map_regions[ix].window_addr_physical, | ||
217 | map_regions[ix].map_info.size); | ||
218 | if(!map_regions[ix].map_info.virt) | ||
219 | { | ||
220 | int j = 0; | ||
221 | printk(MTD_FORTUNET_PK "%s flash failed to ioremap!\n", | ||
222 | map_regions[ix].map_info.name); | ||
223 | for (j = 0 ; j < ix; j++) | ||
224 | iounmap(map_regions[j].map_info.virt); | ||
225 | return -ENXIO; | ||
226 | } | ||
227 | simple_map_init(&map_regions[ix].map_info); | ||
228 | |||
229 | printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is virtually at: %x\n", | ||
230 | map_regions[ix].map_info.name, | ||
231 | map_regions[ix].map_info.virt); | ||
232 | map_regions[ix].mymtd = do_map_probe("cfi_probe", | ||
233 | &map_regions[ix].map_info); | ||
234 | if((!map_regions[ix].mymtd)&&( | ||
235 | map_regions[ix].altbankwidth!=map_regions[ix].map_info.bankwidth)) | ||
236 | { | ||
237 | printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternate bankwidth " | ||
238 | "for %s flash.\n", | ||
239 | map_regions[ix].map_info.name); | ||
240 | map_regions[ix].map_info.bankwidth = | ||
241 | map_regions[ix].altbankwidth; | ||
242 | map_regions[ix].mymtd = do_map_probe("cfi_probe", | ||
243 | &map_regions[ix].map_info); | ||
244 | } | ||
245 | map_regions[ix].mymtd->owner = THIS_MODULE; | ||
246 | mtd_device_register(map_regions[ix].mymtd, | ||
247 | map_regions[ix].parts, | ||
248 | map_regions_parts[ix]); | ||
249 | } | ||
250 | } | ||
251 | if(iy) | ||
252 | return 0; | ||
253 | return -ENXIO; | ||
254 | } | ||
255 | |||
256 | static void __exit cleanup_fortunet(void) | ||
257 | { | ||
258 | int ix; | ||
259 | for(ix=0;ix<MAX_NUM_REGIONS;ix++) | ||
260 | { | ||
261 | if(map_regions_set[ix]) | ||
262 | { | ||
263 | if( map_regions[ix].mymtd ) | ||
264 | { | ||
265 | mtd_device_unregister(map_regions[ix].mymtd); | ||
266 | map_destroy( map_regions[ix].mymtd ); | ||
267 | } | ||
268 | iounmap((void *)map_regions[ix].map_info.virt); | ||
269 | } | ||
270 | } | ||
271 | } | ||
272 | |||
273 | module_init(init_fortunet); | ||
274 | module_exit(cleanup_fortunet); | ||
275 | |||
276 | MODULE_AUTHOR("FortuNet, Inc."); | ||
277 | MODULE_DESCRIPTION("MTD map driver for FortuNet boards"); | ||
diff --git a/drivers/mtd/maps/tegra_nor.c b/drivers/mtd/maps/tegra_nor.c new file mode 100644 index 00000000000..b455fd5e1c0 --- /dev/null +++ b/drivers/mtd/maps/tegra_nor.c | |||
@@ -0,0 +1,483 @@ | |||
1 | /* | ||
2 | * drivers/mtd/maps/tegra_nor.c | ||
3 | * | ||
4 | * MTD mapping driver for the internal SNOR controller in Tegra SoCs | ||
5 | * | ||
6 | * Copyright (C) 2009 - 2012 NVIDIA Corporation | ||
7 | * | ||
8 | * Author: | ||
9 | * Raghavendra VK <rvk@nvidia.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, but WITHOUT | ||
17 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
18 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
19 | * more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License along | ||
22 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
23 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
24 | */ | ||
25 | |||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/types.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/ioport.h> | ||
32 | #include <linux/slab.h> | ||
33 | #include <linux/interrupt.h> | ||
34 | #include <linux/irq.h> | ||
35 | #include <linux/mutex.h> | ||
36 | #include <linux/mtd/mtd.h> | ||
37 | #include <linux/mtd/map.h> | ||
38 | #include <linux/mtd/partitions.h> | ||
39 | #include <linux/dma-mapping.h> | ||
40 | #include <linux/proc_fs.h> | ||
41 | #include <linux/io.h> | ||
42 | #include <linux/uaccess.h> | ||
43 | #include <linux/clk.h> | ||
44 | #include <linux/platform_data/tegra_nor.h> | ||
45 | #include <asm/cacheflush.h> | ||
46 | |||
47 | #define __BITMASK0(len) (BIT(len) - 1) | ||
48 | #define REG_FIELD(val, start, len) (((val) & __BITMASK0(len)) << (start)) | ||
49 | #define REG_GET_FIELD(val, start, len) (((val) >> (start)) & __BITMASK0(len)) | ||
50 | |||
51 | /* tegra gmi registers... */ | ||
52 | #define TEGRA_SNOR_CONFIG_REG 0x00 | ||
53 | #define TEGRA_SNOR_NOR_ADDR_PTR_REG 0x08 | ||
54 | #define TEGRA_SNOR_AHB_ADDR_PTR_REG 0x0C | ||
55 | #define TEGRA_SNOR_TIMING0_REG 0x10 | ||
56 | #define TEGRA_SNOR_TIMING1_REG 0x14 | ||
57 | #define TEGRA_SNOR_DMA_CFG_REG 0x20 | ||
58 | |||
59 | /* config register */ | ||
60 | #define TEGRA_SNOR_CONFIG_GO BIT(31) | ||
61 | #define TEGRA_SNOR_CONFIG_WORDWIDE BIT(30) | ||
62 | #define TEGRA_SNOR_CONFIG_DEVICE_TYPE BIT(29) | ||
63 | #define TEGRA_SNOR_CONFIG_MUX_MODE BIT(28) | ||
64 | #define TEGRA_SNOR_CONFIG_BURST_LEN(val) REG_FIELD((val), 26, 2) | ||
65 | #define TEGRA_SNOR_CONFIG_RDY_ACTIVE BIT(24) | ||
66 | #define TEGRA_SNOR_CONFIG_RDY_POLARITY BIT(23) | ||
67 | #define TEGRA_SNOR_CONFIG_ADV_POLARITY BIT(22) | ||
68 | #define TEGRA_SNOR_CONFIG_OE_WE_POLARITY BIT(21) | ||
69 | #define TEGRA_SNOR_CONFIG_CS_POLARITY BIT(20) | ||
70 | #define TEGRA_SNOR_CONFIG_NOR_DPD BIT(19) | ||
71 | #define TEGRA_SNOR_CONFIG_WP BIT(15) | ||
72 | #define TEGRA_SNOR_CONFIG_PAGE_SZ(val) REG_FIELD((val), 8, 2) | ||
73 | #define TEGRA_SNOR_CONFIG_MST_ENB BIT(7) | ||
74 | #define TEGRA_SNOR_CONFIG_SNOR_CS(val) REG_FIELD((val), 4, 2) | ||
75 | #define TEGRA_SNOR_CONFIG_CE_LAST REG_FIELD(3) | ||
76 | #define TEGRA_SNOR_CONFIG_CE_FIRST REG_FIELD(2) | ||
77 | #define TEGRA_SNOR_CONFIG_DEVICE_MODE(val) REG_FIELD((val), 0, 2) | ||
78 | |||
79 | /* dma config register */ | ||
80 | #define TEGRA_SNOR_DMA_CFG_GO BIT(31) | ||
81 | #define TEGRA_SNOR_DMA_CFG_BSY BIT(30) | ||
82 | #define TEGRA_SNOR_DMA_CFG_DIR BIT(29) | ||
83 | #define TEGRA_SNOR_DMA_CFG_INT_ENB BIT(28) | ||
84 | #define TEGRA_SNOR_DMA_CFG_INT_STA BIT(27) | ||
85 | #define TEGRA_SNOR_DMA_CFG_BRST_SZ(val) REG_FIELD((val), 24, 3) | ||
86 | #define TEGRA_SNOR_DMA_CFG_WRD_CNT(val) REG_FIELD((val), 2, 14) | ||
87 | |||
88 | /* timing 0 register */ | ||
89 | #define TEGRA_SNOR_TIMING0_PG_RDY(val) REG_FIELD((val), 28, 4) | ||
90 | #define TEGRA_SNOR_TIMING0_PG_SEQ(val) REG_FIELD((val), 20, 4) | ||
91 | #define TEGRA_SNOR_TIMING0_MUX(val) REG_FIELD((val), 12, 4) | ||
92 | #define TEGRA_SNOR_TIMING0_HOLD(val) REG_FIELD((val), 8, 4) | ||
93 | #define TEGRA_SNOR_TIMING0_ADV(val) REG_FIELD((val), 4, 4) | ||
94 | #define TEGRA_SNOR_TIMING0_CE(val) REG_FIELD((val), 0, 4) | ||
95 | |||
96 | /* timing 1 register */ | ||
97 | #define TEGRA_SNOR_TIMING1_WE(val) REG_FIELD((val), 16, 8) | ||
98 | #define TEGRA_SNOR_TIMING1_OE(val) REG_FIELD((val), 8, 8) | ||
99 | #define TEGRA_SNOR_TIMING1_WAIT(val) REG_FIELD((val), 0, 8) | ||
100 | |||
101 | /* SNOR DMA supports 2^14 AHB (32-bit words) | ||
102 | * Maximum data in one transfer = 2^16 bytes | ||
103 | */ | ||
104 | #define TEGRA_SNOR_DMA_LIMIT 0x10000 | ||
105 | #define TEGRA_SNOR_DMA_LIMIT_WORDS (TEGRA_SNOR_DMA_LIMIT >> 2) | ||
106 | |||
107 | /* Even if BW is 1 MB/s, maximum time to | ||
108 | * transfer SNOR_DMA_LIMIT bytes is 66 ms | ||
109 | */ | ||
110 | #define TEGRA_SNOR_DMA_TIMEOUT_MS 67 | ||
111 | |||
112 | struct tegra_nor_info { | ||
113 | struct tegra_nor_platform_data *plat; | ||
114 | struct device *dev; | ||
115 | struct clk *clk; | ||
116 | struct mtd_partition *parts; | ||
117 | struct mtd_info *mtd; | ||
118 | struct map_info map; | ||
119 | struct completion dma_complete; | ||
120 | void __iomem *base; | ||
121 | void *dma_virt_buffer; | ||
122 | dma_addr_t dma_phys_buffer; | ||
123 | u32 init_config; | ||
124 | u32 timing0_default, timing1_default; | ||
125 | u32 timing0_read, timing1_read; | ||
126 | }; | ||
127 | |||
128 | static inline unsigned long snor_tegra_readl(struct tegra_nor_info *tnor, | ||
129 | unsigned long reg) | ||
130 | { | ||
131 | return readl(tnor->base + reg); | ||
132 | } | ||
133 | |||
134 | static inline void snor_tegra_writel(struct tegra_nor_info *tnor, | ||
135 | unsigned long val, unsigned long reg) | ||
136 | { | ||
137 | writel(val, tnor->base + reg); | ||
138 | } | ||
139 | |||
140 | #define DRV_NAME "tegra-nor" | ||
141 | |||
142 | static const char * const part_probes[] = { "cmdlinepart", NULL }; | ||
143 | |||
144 | static int wait_for_dma_completion(struct tegra_nor_info *info) | ||
145 | { | ||
146 | unsigned long dma_timeout; | ||
147 | int ret; | ||
148 | |||
149 | dma_timeout = msecs_to_jiffies(TEGRA_SNOR_DMA_TIMEOUT_MS); | ||
150 | ret = wait_for_completion_timeout(&info->dma_complete, dma_timeout); | ||
151 | return ret ? 0 : -ETIMEDOUT; | ||
152 | } | ||
153 | |||
154 | static void tegra_flash_dma(struct map_info *map, | ||
155 | void *to, unsigned long from, ssize_t len) | ||
156 | { | ||
157 | u32 snor_config, dma_config = 0; | ||
158 | int dma_transfer_count = 0, word32_count = 0; | ||
159 | u32 nor_address, current_transfer = 0; | ||
160 | u32 copy_to = (u32)to; | ||
161 | struct tegra_nor_info *c = | ||
162 | container_of(map, struct tegra_nor_info, map); | ||
163 | unsigned int bytes_remaining = len; | ||
164 | |||
165 | snor_config = c->init_config; | ||
166 | snor_tegra_writel(c, c->timing0_read, TEGRA_SNOR_TIMING0_REG); | ||
167 | snor_tegra_writel(c, c->timing1_read, TEGRA_SNOR_TIMING1_REG); | ||
168 | |||
169 | if (len > 32) { | ||
170 | word32_count = len >> 2; | ||
171 | bytes_remaining = len & 0x00000003; | ||
172 | /* | ||
173 | * The parameters can be setup in any order since we write to | ||
174 | * controller register only after all parameters are set. | ||
175 | */ | ||
176 | /* SNOR CONFIGURATION SETUP */ | ||
177 | snor_config |= TEGRA_SNOR_CONFIG_DEVICE_MODE(1); | ||
178 | /* 8 word page */ | ||
179 | snor_config |= TEGRA_SNOR_CONFIG_PAGE_SZ(2); | ||
180 | snor_config |= TEGRA_SNOR_CONFIG_MST_ENB; | ||
181 | /* SNOR DMA CONFIGURATION SETUP */ | ||
182 | /* NOR -> AHB */ | ||
183 | dma_config &= ~TEGRA_SNOR_DMA_CFG_DIR; | ||
184 | /* One word burst */ | ||
185 | dma_config |= TEGRA_SNOR_DMA_CFG_BRST_SZ(4); | ||
186 | |||
187 | for (nor_address = (unsigned int)(map->phys + from); | ||
188 | word32_count > 0; | ||
189 | word32_count -= current_transfer, | ||
190 | dma_transfer_count += current_transfer, | ||
191 | nor_address += (current_transfer * 4), | ||
192 | copy_to += (current_transfer * 4)) { | ||
193 | |||
194 | current_transfer = | ||
195 | (word32_count > TEGRA_SNOR_DMA_LIMIT_WORDS) | ||
196 | ? (TEGRA_SNOR_DMA_LIMIT_WORDS) : word32_count; | ||
197 | /* Start NOR operation */ | ||
198 | snor_config |= TEGRA_SNOR_CONFIG_GO; | ||
199 | dma_config |= TEGRA_SNOR_DMA_CFG_GO; | ||
200 | /* Enable interrupt before every transaction since the | ||
201 | * interrupt handler disables it */ | ||
202 | dma_config |= TEGRA_SNOR_DMA_CFG_INT_ENB; | ||
203 | /* Num of AHB (32-bit) words to transferred minus 1 */ | ||
204 | dma_config |= | ||
205 | TEGRA_SNOR_DMA_CFG_WRD_CNT(current_transfer - 1); | ||
206 | snor_tegra_writel(c, c->dma_phys_buffer, | ||
207 | TEGRA_SNOR_AHB_ADDR_PTR_REG); | ||
208 | snor_tegra_writel(c, nor_address, | ||
209 | TEGRA_SNOR_NOR_ADDR_PTR_REG); | ||
210 | snor_tegra_writel(c, snor_config, | ||
211 | TEGRA_SNOR_CONFIG_REG); | ||
212 | snor_tegra_writel(c, dma_config, | ||
213 | TEGRA_SNOR_DMA_CFG_REG); | ||
214 | if (wait_for_dma_completion(c)) { | ||
215 | dev_err(c->dev, "timout waiting for DMA\n"); | ||
216 | /* Transfer the remaining words by memcpy */ | ||
217 | bytes_remaining += (word32_count << 2); | ||
218 | break; | ||
219 | } | ||
220 | memcpy((char *)(copy_to), (char *)(c->dma_virt_buffer), | ||
221 | (current_transfer << 2)); | ||
222 | |||
223 | } | ||
224 | } | ||
225 | /* Put the controller back into slave mode. */ | ||
226 | snor_config = snor_tegra_readl(c, TEGRA_SNOR_CONFIG_REG); | ||
227 | snor_config &= ~TEGRA_SNOR_CONFIG_MST_ENB; | ||
228 | snor_config |= TEGRA_SNOR_CONFIG_DEVICE_MODE(0); | ||
229 | snor_tegra_writel(c, snor_config, TEGRA_SNOR_CONFIG_REG); | ||
230 | |||
231 | memcpy_fromio(((char *)to + (dma_transfer_count << 2)), | ||
232 | ((char *)(map->virt + from) + (dma_transfer_count << 2)), | ||
233 | bytes_remaining); | ||
234 | |||
235 | snor_tegra_writel(c, c->timing0_default, TEGRA_SNOR_TIMING0_REG); | ||
236 | snor_tegra_writel(c, c->timing1_default, TEGRA_SNOR_TIMING1_REG); | ||
237 | } | ||
238 | |||
239 | static irqreturn_t tegra_nor_isr(int flag, void *dev_id) | ||
240 | { | ||
241 | struct tegra_nor_info *info = (struct tegra_nor_info *)dev_id; | ||
242 | u32 dma_config = snor_tegra_readl(info, TEGRA_SNOR_DMA_CFG_REG); | ||
243 | if (dma_config & TEGRA_SNOR_DMA_CFG_INT_STA) { | ||
244 | /* Disable interrupts. WAR for BUG:821560 */ | ||
245 | dma_config &= ~TEGRA_SNOR_DMA_CFG_INT_ENB; | ||
246 | snor_tegra_writel(info, dma_config, TEGRA_SNOR_DMA_CFG_REG); | ||
247 | complete(&info->dma_complete); | ||
248 | } else { | ||
249 | pr_err("%s: Spurious interrupt\n", __func__); | ||
250 | } | ||
251 | return IRQ_HANDLED; | ||
252 | } | ||
253 | |||
254 | static int tegra_snor_controller_init(struct tegra_nor_info *info) | ||
255 | { | ||
256 | struct tegra_nor_chip_parms *chip_parm = &info->plat->chip_parms; | ||
257 | u32 width = info->plat->flash.width; | ||
258 | u32 config = 0; | ||
259 | |||
260 | config |= TEGRA_SNOR_CONFIG_DEVICE_MODE(0); | ||
261 | config |= TEGRA_SNOR_CONFIG_SNOR_CS(0); | ||
262 | config &= ~TEGRA_SNOR_CONFIG_DEVICE_TYPE; /* Select NOR */ | ||
263 | config |= TEGRA_SNOR_CONFIG_WP; /* Enable writes */ | ||
264 | switch (width) { | ||
265 | case 2: | ||
266 | config &= ~TEGRA_SNOR_CONFIG_WORDWIDE; /* 16 bit */ | ||
267 | break; | ||
268 | case 4: | ||
269 | config |= TEGRA_SNOR_CONFIG_WORDWIDE; /* 32 bit */ | ||
270 | break; | ||
271 | default: | ||
272 | return -EINVAL; | ||
273 | } | ||
274 | config |= TEGRA_SNOR_CONFIG_BURST_LEN(0); | ||
275 | config &= ~TEGRA_SNOR_CONFIG_MUX_MODE; | ||
276 | snor_tegra_writel(info, config, TEGRA_SNOR_CONFIG_REG); | ||
277 | info->init_config = config; | ||
278 | |||
279 | info->timing0_default = chip_parm->timing_default.timing0; | ||
280 | info->timing0_read = chip_parm->timing_read.timing0; | ||
281 | info->timing1_default = chip_parm->timing_default.timing1; | ||
282 | info->timing1_read = chip_parm->timing_read.timing0; | ||
283 | |||
284 | snor_tegra_writel(info, info->timing1_default, TEGRA_SNOR_TIMING1_REG); | ||
285 | snor_tegra_writel(info, info->timing0_default, TEGRA_SNOR_TIMING0_REG); | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | static int tegra_nor_probe(struct platform_device *pdev) | ||
290 | { | ||
291 | int err = 0; | ||
292 | struct tegra_nor_platform_data *plat = pdev->dev.platform_data; | ||
293 | struct tegra_nor_info *info = NULL; | ||
294 | struct device *dev = &pdev->dev; | ||
295 | struct resource *res; | ||
296 | int irq; | ||
297 | |||
298 | if (!plat) { | ||
299 | pr_err("%s: no platform device info\n", __func__); | ||
300 | err = -EINVAL; | ||
301 | goto fail; | ||
302 | } | ||
303 | |||
304 | info = devm_kzalloc(dev, sizeof(struct tegra_nor_info), | ||
305 | GFP_KERNEL); | ||
306 | if (!info) { | ||
307 | err = -ENOMEM; | ||
308 | goto fail; | ||
309 | } | ||
310 | |||
311 | /* Get NOR controller & map the same */ | ||
312 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
313 | if (!res) { | ||
314 | dev_err(dev, "no mem resource?\n"); | ||
315 | err = -ENODEV; | ||
316 | goto fail; | ||
317 | } | ||
318 | |||
319 | if (!devm_request_mem_region(dev, res->start, resource_size(res), | ||
320 | dev_name(&pdev->dev))) { | ||
321 | dev_err(dev, "NOR region already claimed\n"); | ||
322 | err = -EBUSY; | ||
323 | goto fail; | ||
324 | } | ||
325 | |||
326 | info->base = devm_ioremap(dev, res->start, resource_size(res)); | ||
327 | if (!info->base) { | ||
328 | dev_err(dev, "Can't ioremap NOR region\n"); | ||
329 | err = -ENOMEM; | ||
330 | goto fail; | ||
331 | } | ||
332 | |||
333 | /* Get NOR flash aperture & map the same */ | ||
334 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
335 | if (!res) { | ||
336 | dev_err(dev, "no mem resource?\n"); | ||
337 | err = -ENODEV; | ||
338 | goto fail; | ||
339 | } | ||
340 | |||
341 | if (!devm_request_mem_region(dev, res->start, resource_size(res), | ||
342 | dev_name(dev))) { | ||
343 | dev_err(dev, "NOR region already claimed\n"); | ||
344 | err = -EBUSY; | ||
345 | goto fail; | ||
346 | } | ||
347 | |||
348 | info->map.virt = devm_ioremap(dev, res->start, | ||
349 | resource_size(res)); | ||
350 | if (!info->map.virt) { | ||
351 | dev_err(dev, "Can't ioremap NOR region\n"); | ||
352 | err = -ENOMEM; | ||
353 | goto fail; | ||
354 | } | ||
355 | |||
356 | info->plat = plat; | ||
357 | info->dev = dev; | ||
358 | info->map.bankwidth = plat->flash.width; | ||
359 | info->map.name = dev_name(dev); | ||
360 | info->map.phys = res->start; | ||
361 | info->map.size = resource_size(res); | ||
362 | |||
363 | info->clk = clk_get(dev, NULL); | ||
364 | if (IS_ERR(info->clk)) { | ||
365 | err = PTR_ERR(info->clk); | ||
366 | goto fail; | ||
367 | } | ||
368 | |||
369 | err = clk_enable(info->clk); | ||
370 | if (err != 0) | ||
371 | goto out_clk_put; | ||
372 | |||
373 | simple_map_init(&info->map); | ||
374 | info->map.copy_from = tegra_flash_dma; | ||
375 | |||
376 | /* Intialise the SNOR controller before probe */ | ||
377 | err = tegra_snor_controller_init(info); | ||
378 | if (err) { | ||
379 | dev_err(dev, "Error initializing controller\n"); | ||
380 | goto out_clk_disable; | ||
381 | } | ||
382 | |||
383 | init_completion(&info->dma_complete); | ||
384 | |||
385 | irq = platform_get_irq(pdev, 0); | ||
386 | if (!irq) { | ||
387 | dev_err(dev, "no irq resource?\n"); | ||
388 | err = -ENODEV; | ||
389 | goto out_clk_disable; | ||
390 | } | ||
391 | |||
392 | /* Register SNOR DMA completion interrupt */ | ||
393 | err = devm_request_irq(dev, irq, tegra_nor_isr, IRQF_DISABLED, | ||
394 | dev_name(dev), info); | ||
395 | if (err) { | ||
396 | dev_err(dev, "Failed to request irq %i\n", irq); | ||
397 | goto out_clk_disable; | ||
398 | } | ||
399 | info->dma_virt_buffer = dma_alloc_coherent(dev, | ||
400 | TEGRA_SNOR_DMA_LIMIT, | ||
401 | &info->dma_phys_buffer, | ||
402 | GFP_KERNEL); | ||
403 | if (info->dma_virt_buffer == NULL) { | ||
404 | dev_err(&pdev->dev, "Could not allocate buffer for DMA"); | ||
405 | err = -ENOMEM; | ||
406 | goto out_clk_disable; | ||
407 | } | ||
408 | |||
409 | info->mtd = do_map_probe(plat->flash.map_name, &info->map); | ||
410 | if (!info->mtd) { | ||
411 | err = -EIO; | ||
412 | goto out_dma_free_coherent; | ||
413 | } | ||
414 | info->mtd->owner = THIS_MODULE; | ||
415 | info->parts = NULL; | ||
416 | |||
417 | platform_set_drvdata(pdev, info); | ||
418 | err = parse_mtd_partitions(info->mtd, part_probes, &info->parts, 0); | ||
419 | if (err > 0) | ||
420 | err = mtd_device_register(info->mtd, info->parts, err); | ||
421 | else if (err <= 0 && plat->flash.parts) | ||
422 | err = | ||
423 | mtd_device_register(info->mtd, plat->flash.parts, | ||
424 | plat->flash.nr_parts); | ||
425 | else | ||
426 | mtd_device_register(info->mtd, NULL, 0); | ||
427 | |||
428 | return 0; | ||
429 | |||
430 | out_dma_free_coherent: | ||
431 | dma_free_coherent(dev, TEGRA_SNOR_DMA_LIMIT, | ||
432 | info->dma_virt_buffer, info->dma_phys_buffer); | ||
433 | out_clk_disable: | ||
434 | clk_disable(info->clk); | ||
435 | out_clk_put: | ||
436 | clk_put(info->clk); | ||
437 | fail: | ||
438 | pr_err("Tegra NOR probe failed\n"); | ||
439 | return err; | ||
440 | } | ||
441 | |||
442 | static int tegra_nor_remove(struct platform_device *pdev) | ||
443 | { | ||
444 | struct tegra_nor_info *info = platform_get_drvdata(pdev); | ||
445 | |||
446 | mtd_device_unregister(info->mtd); | ||
447 | if (info->parts) | ||
448 | kfree(info->parts); | ||
449 | dma_free_coherent(&pdev->dev, TEGRA_SNOR_DMA_LIMIT, | ||
450 | info->dma_virt_buffer, info->dma_phys_buffer); | ||
451 | map_destroy(info->mtd); | ||
452 | clk_disable(info->clk); | ||
453 | clk_put(info->clk); | ||
454 | |||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | static struct platform_driver __refdata tegra_nor_driver = { | ||
459 | .probe = tegra_nor_probe, | ||
460 | .remove = __devexit_p(tegra_nor_remove), | ||
461 | .driver = { | ||
462 | .name = DRV_NAME, | ||
463 | .owner = THIS_MODULE, | ||
464 | }, | ||
465 | }; | ||
466 | |||
467 | static int __init tegra_nor_init(void) | ||
468 | { | ||
469 | return platform_driver_register(&tegra_nor_driver); | ||
470 | } | ||
471 | |||
472 | static void __exit tegra_nor_exit(void) | ||
473 | { | ||
474 | platform_driver_unregister(&tegra_nor_driver); | ||
475 | } | ||
476 | |||
477 | module_init(tegra_nor_init); | ||
478 | module_exit(tegra_nor_exit); | ||
479 | |||
480 | MODULE_AUTHOR("Raghavendra VK <rvk@nvidia.com>"); | ||
481 | MODULE_DESCRIPTION("NOR Flash mapping driver for NVIDIA Tegra based boards"); | ||
482 | MODULE_LICENSE("GPL"); | ||
483 | MODULE_ALIAS("platform:" DRV_NAME); | ||
diff --git a/drivers/mtd/maps/wr_sbc82xx_flash.c b/drivers/mtd/maps/wr_sbc82xx_flash.c new file mode 100644 index 00000000000..901ce968efa --- /dev/null +++ b/drivers/mtd/maps/wr_sbc82xx_flash.c | |||
@@ -0,0 +1,181 @@ | |||
1 | /* | ||
2 | * Map for flash chips on Wind River PowerQUICC II SBC82xx board. | ||
3 | * | ||
4 | * Copyright (C) 2004 Red Hat, Inc. | ||
5 | * | ||
6 | * Author: David Woodhouse <dwmw2@infradead.org> | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #include <linux/module.h> | ||
11 | #include <linux/types.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <asm/io.h> | ||
16 | #include <linux/mtd/mtd.h> | ||
17 | #include <linux/mtd/map.h> | ||
18 | #include <linux/mtd/partitions.h> | ||
19 | |||
20 | #include <asm/immap_cpm2.h> | ||
21 | |||
22 | static struct mtd_info *sbcmtd[3]; | ||
23 | static struct mtd_partition *sbcmtd_parts[3]; | ||
24 | |||
25 | struct map_info sbc82xx_flash_map[3] = { | ||
26 | {.name = "Boot flash"}, | ||
27 | {.name = "Alternate boot flash"}, | ||
28 | {.name = "User flash"} | ||
29 | }; | ||
30 | |||
31 | static struct mtd_partition smallflash_parts[] = { | ||
32 | { | ||
33 | .name = "space", | ||
34 | .size = 0x100000, | ||
35 | .offset = 0, | ||
36 | }, { | ||
37 | .name = "bootloader", | ||
38 | .size = MTDPART_SIZ_FULL, | ||
39 | .offset = MTDPART_OFS_APPEND, | ||
40 | } | ||
41 | }; | ||
42 | |||
43 | static struct mtd_partition bigflash_parts[] = { | ||
44 | { | ||
45 | .name = "bootloader", | ||
46 | .size = 0x00100000, | ||
47 | .offset = 0, | ||
48 | }, { | ||
49 | .name = "file system", | ||
50 | .size = 0x01f00000, | ||
51 | .offset = MTDPART_OFS_APPEND, | ||
52 | }, { | ||
53 | .name = "boot config", | ||
54 | .size = 0x00100000, | ||
55 | .offset = MTDPART_OFS_APPEND, | ||
56 | }, { | ||
57 | .name = "space", | ||
58 | .size = 0x01f00000, | ||
59 | .offset = MTDPART_OFS_APPEND, | ||
60 | } | ||
61 | }; | ||
62 | |||
63 | static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL}; | ||
64 | |||
65 | #define init_sbc82xx_one_flash(map, br, or) \ | ||
66 | do { \ | ||
67 | (map).phys = (br & 1) ? (br & 0xffff8000) : 0; \ | ||
68 | (map).size = (br & 1) ? (~(or & 0xffff8000) + 1) : 0; \ | ||
69 | switch (br & 0x00001800) { \ | ||
70 | case 0x00000000: \ | ||
71 | case 0x00000800: (map).bankwidth = 1; break; \ | ||
72 | case 0x00001000: (map).bankwidth = 2; break; \ | ||
73 | case 0x00001800: (map).bankwidth = 4; break; \ | ||
74 | } \ | ||
75 | } while (0); | ||
76 | |||
77 | static int __init init_sbc82xx_flash(void) | ||
78 | { | ||
79 | volatile memctl_cpm2_t *mc = &cpm2_immr->im_memctl; | ||
80 | int bigflash; | ||
81 | int i; | ||
82 | |||
83 | #ifdef CONFIG_SBC8560 | ||
84 | mc = ioremap(0xff700000 + 0x5000, sizeof(memctl_cpm2_t)); | ||
85 | #else | ||
86 | mc = &cpm2_immr->im_memctl; | ||
87 | #endif | ||
88 | |||
89 | bigflash = 1; | ||
90 | if ((mc->memc_br0 & 0x00001800) == 0x00001800) | ||
91 | bigflash = 0; | ||
92 | |||
93 | init_sbc82xx_one_flash(sbc82xx_flash_map[0], mc->memc_br0, mc->memc_or0); | ||
94 | init_sbc82xx_one_flash(sbc82xx_flash_map[1], mc->memc_br6, mc->memc_or6); | ||
95 | init_sbc82xx_one_flash(sbc82xx_flash_map[2], mc->memc_br1, mc->memc_or1); | ||
96 | |||
97 | #ifdef CONFIG_SBC8560 | ||
98 | iounmap((void *) mc); | ||
99 | #endif | ||
100 | |||
101 | for (i=0; i<3; i++) { | ||
102 | int8_t flashcs[3] = { 0, 6, 1 }; | ||
103 | int nr_parts; | ||
104 | |||
105 | printk(KERN_NOTICE "PowerQUICC II %s (%ld MiB on CS%d", | ||
106 | sbc82xx_flash_map[i].name, | ||
107 | (sbc82xx_flash_map[i].size >> 20), | ||
108 | flashcs[i]); | ||
109 | if (!sbc82xx_flash_map[i].phys) { | ||
110 | /* We know it can't be at zero. */ | ||
111 | printk("): disabled by bootloader.\n"); | ||
112 | continue; | ||
113 | } | ||
114 | printk(" at %08lx)\n", sbc82xx_flash_map[i].phys); | ||
115 | |||
116 | sbc82xx_flash_map[i].virt = ioremap(sbc82xx_flash_map[i].phys, sbc82xx_flash_map[i].size); | ||
117 | |||
118 | if (!sbc82xx_flash_map[i].virt) { | ||
119 | printk("Failed to ioremap\n"); | ||
120 | continue; | ||
121 | } | ||
122 | |||
123 | simple_map_init(&sbc82xx_flash_map[i]); | ||
124 | |||
125 | sbcmtd[i] = do_map_probe("cfi_probe", &sbc82xx_flash_map[i]); | ||
126 | |||
127 | if (!sbcmtd[i]) | ||
128 | continue; | ||
129 | |||
130 | sbcmtd[i]->owner = THIS_MODULE; | ||
131 | |||
132 | nr_parts = parse_mtd_partitions(sbcmtd[i], part_probes, | ||
133 | &sbcmtd_parts[i], 0); | ||
134 | if (nr_parts > 0) { | ||
135 | mtd_device_register(sbcmtd[i], sbcmtd_parts[i], | ||
136 | nr_parts); | ||
137 | continue; | ||
138 | } | ||
139 | |||
140 | /* No partitioning detected. Use default */ | ||
141 | if (i == 2) { | ||
142 | mtd_device_register(sbcmtd[i], NULL, 0); | ||
143 | } else if (i == bigflash) { | ||
144 | mtd_device_register(sbcmtd[i], bigflash_parts, | ||
145 | ARRAY_SIZE(bigflash_parts)); | ||
146 | } else { | ||
147 | mtd_device_register(sbcmtd[i], smallflash_parts, | ||
148 | ARRAY_SIZE(smallflash_parts)); | ||
149 | } | ||
150 | } | ||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static void __exit cleanup_sbc82xx_flash(void) | ||
155 | { | ||
156 | int i; | ||
157 | |||
158 | for (i=0; i<3; i++) { | ||
159 | if (!sbcmtd[i]) | ||
160 | continue; | ||
161 | |||
162 | if (i<2 || sbcmtd_parts[i]) | ||
163 | mtd_device_unregister(sbcmtd[i]); | ||
164 | else | ||
165 | mtd_device_unregister(sbcmtd[i]); | ||
166 | |||
167 | kfree(sbcmtd_parts[i]); | ||
168 | map_destroy(sbcmtd[i]); | ||
169 | |||
170 | iounmap((void *)sbc82xx_flash_map[i].virt); | ||
171 | sbc82xx_flash_map[i].virt = 0; | ||
172 | } | ||
173 | } | ||
174 | |||
175 | module_init(init_sbc82xx_flash); | ||
176 | module_exit(cleanup_sbc82xx_flash); | ||
177 | |||
178 | |||
179 | MODULE_LICENSE("GPL"); | ||
180 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | ||
181 | MODULE_DESCRIPTION("Flash map driver for WindRiver PowerQUICC II"); | ||