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"); | ||
