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 accomodated. 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 remaing portion of first flash, and all of second flash */ 62#define ROOT_PARTITION_SIZE_KiB (3*128) + (8*128) 63 64static 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 87static 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 101struct 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 113static struct clps_info info[NR_SUBMTD]; 114 115static 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#ifdef CONFIG_MTD_CONCAT 198 *rmtd = mtd_concat_create(subdev, found, 199 "clps flash"); 200 if (*rmtd == NULL) 201 ret = -ENXIO; 202#else 203 printk(KERN_ERR "clps flash: multiple devices " 204 "found but MTD concat support disabled.\n"); 205 ret = -ENXIO; 206#endif 207 } 208 } 209 210 /* 211 * If we failed, clean up. 212 */ 213 if (ret) { 214 do { 215 if (clps[i].mtd) 216 map_destroy(clps[i].mtd); 217 if (clps[i].vbase) 218 iounmap(clps[i].vbase); 219 if (clps[i].res) 220 release_resource(clps[i].res); 221 } while (i--); 222 223 kfree(maps); 224 } 225 226 return ret; 227} 228 229static void __exit clps_destroy_mtd(struct clps_info *clps, struct mtd_info *mtd) 230{ 231 int i; 232 233 del_mtd_partitions(mtd); 234 235 if (mtd != clps[0].mtd) 236 mtd_concat_destroy(mtd); 237 238 for (i = NR_SUBMTD; i >= 0; i--) { 239 if (clps[i].mtd) 240 map_destroy(clps[i].mtd); 241 if (clps[i].vbase) 242 iounmap(clps[i].vbase); 243 if (clps[i].res) 244 release_resource(clps[i].res); 245 } 246 kfree(clps[0].map); 247} 248 249/* 250 * We define the memory space, size, and width for the flash memory 251 * space here. 252 */ 253 254static int __init clps_setup_flash(void) 255{ 256 int nr = 0; 257 258#ifdef CONFIG_ARCH_CEIVA 259 if (machine_is_ceiva()) { 260 info[0].base = CS0_PHYS_BASE; 261 info[0].size = SZ_32M; 262 info[0].width = CEIVA_FLASH_WIDTH; 263 info[1].base = CS1_PHYS_BASE; 264 info[1].size = SZ_32M; 265 info[1].width = CEIVA_FLASH_WIDTH; 266 nr = 2; 267 } 268#endif 269 return nr; 270} 271 272static struct mtd_partition *parsed_parts; 273static const char *probes[] = { "cmdlinepart", "RedBoot", NULL }; 274 275static void __init clps_locate_partitions(struct mtd_info *mtd) 276{ 277 const char *part_type = NULL; 278 int nr_parts = 0; 279 do { 280 /* 281 * Partition selection stuff. 282 */ 283 nr_parts = parse_mtd_partitions(mtd, probes, &parsed_parts, 0); 284 if (nr_parts > 0) { 285 part_type = "command line"; 286 break; 287 } 288#ifdef CONFIG_MTD_CEIVA_STATICMAP 289 nr_parts = clps_static_partitions(&parsed_parts); 290 if (nr_parts > 0) { 291 part_type = "static"; 292 break; 293 } 294 printk("found: %d partitions\n", nr_parts); 295#endif 296 } while (0); 297 298 if (nr_parts == 0) { 299 printk(KERN_NOTICE "clps flash: no partition info " 300 "available, registering whole flash\n"); 301 add_mtd_device(mtd); 302 } else { 303 printk(KERN_NOTICE "clps flash: using %s partition " 304 "definition\n", part_type); 305 add_mtd_partitions(mtd, parsed_parts, nr_parts); 306 } 307 308 /* Always succeeds. */ 309} 310 311static void __exit clps_destroy_partitions(void) 312{ 313 kfree(parsed_parts); 314} 315 316static struct mtd_info *mymtd; 317 318static int __init clps_mtd_init(void) 319{ 320 int ret; 321 int nr; 322 323 nr = clps_setup_flash(); 324 if (nr < 0) 325 return nr; 326 327 ret = clps_setup_mtd(info, nr, &mymtd); 328 if (ret) 329 return ret; 330 331 clps_locate_partitions(mymtd); 332 333 return 0; 334} 335 336static void __exit clps_mtd_cleanup(void) 337{ 338 clps_destroy_mtd(info, mymtd); 339 clps_destroy_partitions(); 340} 341 342module_init(clps_mtd_init); 343module_exit(clps_mtd_cleanup); 344 345MODULE_AUTHOR("Rob Scott"); 346MODULE_DESCRIPTION("Cirrus Logic JEDEC map driver"); 347MODULE_LICENSE("GPL"); 348