ia64/linux-2.6.18-xen.hg

annotate drivers/mtd/chips/cfi_probe.c @ 897:329ea0ccb344

balloon: try harder to balloon up under memory pressure.

Currently if the balloon driver is unable to increase the guest's
reservation it assumes the failure was due to reaching its full
allocation, gives up on the ballooning operation and records the limit
it reached as the "hard limit". The driver will not try again until
the target is set again (even to the same value).

However it is possible that ballooning has in fact failed due to
memory pressure in the host and therefore it is desirable to keep
attempting to reach the target in case memory becomes available. The
most likely scenario is that some guests are ballooning down while
others are ballooning up and therefore there is temporary memory
pressure while things stabilise. You would not expect a well behaved
toolstack to ask a domain to balloon to more than its allocation nor
would you expect it to deliberately over-commit memory by setting
balloon targets which exceed the total host memory.

This patch drops the concept of a hard limit and causes the balloon
driver to retry increasing the reservation on a timer in the same
manner as when decreasing the reservation.

Also if we partially succeed in increasing the reservation
(i.e. receive less pages than we asked for) then we may as well keep
those pages rather than returning them to Xen.

Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jun 05 14:01:20 2009 +0100 (2009-06-05)
parents 831230e53067
children
rev   line source
ian@0 1 /*
ian@0 2 Common Flash Interface probe code.
ian@0 3 (C) 2000 Red Hat. GPL'd.
ian@0 4 $Id: cfi_probe.c,v 1.86 2005/11/29 14:48:31 gleixner Exp $
ian@0 5 */
ian@0 6
ian@0 7 #include <linux/module.h>
ian@0 8 #include <linux/types.h>
ian@0 9 #include <linux/kernel.h>
ian@0 10 #include <linux/init.h>
ian@0 11 #include <asm/io.h>
ian@0 12 #include <asm/byteorder.h>
ian@0 13 #include <linux/errno.h>
ian@0 14 #include <linux/slab.h>
ian@0 15 #include <linux/interrupt.h>
ian@0 16
ian@0 17 #include <linux/mtd/xip.h>
ian@0 18 #include <linux/mtd/map.h>
ian@0 19 #include <linux/mtd/cfi.h>
ian@0 20 #include <linux/mtd/gen_probe.h>
ian@0 21
ian@0 22 //#define DEBUG_CFI
ian@0 23
ian@0 24 #ifdef DEBUG_CFI
ian@0 25 static void print_cfi_ident(struct cfi_ident *);
ian@0 26 #endif
ian@0 27
ian@0 28 static int cfi_probe_chip(struct map_info *map, __u32 base,
ian@0 29 unsigned long *chip_map, struct cfi_private *cfi);
ian@0 30 static int cfi_chip_setup(struct map_info *map, struct cfi_private *cfi);
ian@0 31
ian@0 32 struct mtd_info *cfi_probe(struct map_info *map);
ian@0 33
ian@0 34 #ifdef CONFIG_MTD_XIP
ian@0 35
ian@0 36 /* only needed for short periods, so this is rather simple */
ian@0 37 #define xip_disable() local_irq_disable()
ian@0 38
ian@0 39 #define xip_allowed(base, map) \
ian@0 40 do { \
ian@0 41 (void) map_read(map, base); \
ian@0 42 asm volatile (".rep 8; nop; .endr"); \
ian@0 43 local_irq_enable(); \
ian@0 44 } while (0)
ian@0 45
ian@0 46 #define xip_enable(base, map, cfi) \
ian@0 47 do { \
ian@0 48 cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \
ian@0 49 cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \
ian@0 50 xip_allowed(base, map); \
ian@0 51 } while (0)
ian@0 52
ian@0 53 #define xip_disable_qry(base, map, cfi) \
ian@0 54 do { \
ian@0 55 xip_disable(); \
ian@0 56 cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \
ian@0 57 cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \
ian@0 58 cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); \
ian@0 59 } while (0)
ian@0 60
ian@0 61 #else
ian@0 62
ian@0 63 #define xip_disable() do { } while (0)
ian@0 64 #define xip_allowed(base, map) do { } while (0)
ian@0 65 #define xip_enable(base, map, cfi) do { } while (0)
ian@0 66 #define xip_disable_qry(base, map, cfi) do { } while (0)
ian@0 67
ian@0 68 #endif
ian@0 69
ian@0 70 /* check for QRY.
ian@0 71 in: interleave,type,mode
ian@0 72 ret: table index, <0 for error
ian@0 73 */
ian@0 74 static int __xipram qry_present(struct map_info *map, __u32 base,
ian@0 75 struct cfi_private *cfi)
ian@0 76 {
ian@0 77 int osf = cfi->interleave * cfi->device_type; // scale factor
ian@0 78 map_word val[3];
ian@0 79 map_word qry[3];
ian@0 80
ian@0 81 qry[0] = cfi_build_cmd('Q', map, cfi);
ian@0 82 qry[1] = cfi_build_cmd('R', map, cfi);
ian@0 83 qry[2] = cfi_build_cmd('Y', map, cfi);
ian@0 84
ian@0 85 val[0] = map_read(map, base + osf*0x10);
ian@0 86 val[1] = map_read(map, base + osf*0x11);
ian@0 87 val[2] = map_read(map, base + osf*0x12);
ian@0 88
ian@0 89 if (!map_word_equal(map, qry[0], val[0]))
ian@0 90 return 0;
ian@0 91
ian@0 92 if (!map_word_equal(map, qry[1], val[1]))
ian@0 93 return 0;
ian@0 94
ian@0 95 if (!map_word_equal(map, qry[2], val[2]))
ian@0 96 return 0;
ian@0 97
ian@0 98 return 1; // "QRY" found
ian@0 99 }
ian@0 100
ian@0 101 static int __xipram cfi_probe_chip(struct map_info *map, __u32 base,
ian@0 102 unsigned long *chip_map, struct cfi_private *cfi)
ian@0 103 {
ian@0 104 int i;
ian@0 105
ian@0 106 if ((base + 0) >= map->size) {
ian@0 107 printk(KERN_NOTICE
ian@0 108 "Probe at base[0x00](0x%08lx) past the end of the map(0x%08lx)\n",
ian@0 109 (unsigned long)base, map->size -1);
ian@0 110 return 0;
ian@0 111 }
ian@0 112 if ((base + 0xff) >= map->size) {
ian@0 113 printk(KERN_NOTICE
ian@0 114 "Probe at base[0x55](0x%08lx) past the end of the map(0x%08lx)\n",
ian@0 115 (unsigned long)base + 0x55, map->size -1);
ian@0 116 return 0;
ian@0 117 }
ian@0 118
ian@0 119 xip_disable();
ian@0 120 cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
ian@0 121 cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
ian@0 122 cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
ian@0 123
ian@0 124 if (!qry_present(map,base,cfi)) {
ian@0 125 xip_enable(base, map, cfi);
ian@0 126 return 0;
ian@0 127 }
ian@0 128
ian@0 129 if (!cfi->numchips) {
ian@0 130 /* This is the first time we're called. Set up the CFI
ian@0 131 stuff accordingly and return */
ian@0 132 return cfi_chip_setup(map, cfi);
ian@0 133 }
ian@0 134
ian@0 135 /* Check each previous chip to see if it's an alias */
ian@0 136 for (i=0; i < (base >> cfi->chipshift); i++) {
ian@0 137 unsigned long start;
ian@0 138 if(!test_bit(i, chip_map)) {
ian@0 139 /* Skip location; no valid chip at this address */
ian@0 140 continue;
ian@0 141 }
ian@0 142 start = i << cfi->chipshift;
ian@0 143 /* This chip should be in read mode if it's one
ian@0 144 we've already touched. */
ian@0 145 if (qry_present(map, start, cfi)) {
ian@0 146 /* Eep. This chip also had the QRY marker.
ian@0 147 * Is it an alias for the new one? */
ian@0 148 cfi_send_gen_cmd(0xF0, 0, start, map, cfi, cfi->device_type, NULL);
ian@0 149 cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL);
ian@0 150
ian@0 151 /* If the QRY marker goes away, it's an alias */
ian@0 152 if (!qry_present(map, start, cfi)) {
ian@0 153 xip_allowed(base, map);
ian@0 154 printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
ian@0 155 map->name, base, start);
ian@0 156 return 0;
ian@0 157 }
ian@0 158 /* Yes, it's actually got QRY for data. Most
ian@0 159 * unfortunate. Stick the new chip in read mode
ian@0 160 * too and if it's the same, assume it's an alias. */
ian@0 161 /* FIXME: Use other modes to do a proper check */
ian@0 162 cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
ian@0 163 cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL);
ian@0 164
ian@0 165 if (qry_present(map, base, cfi)) {
ian@0 166 xip_allowed(base, map);
ian@0 167 printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
ian@0 168 map->name, base, start);
ian@0 169 return 0;
ian@0 170 }
ian@0 171 }
ian@0 172 }
ian@0 173
ian@0 174 /* OK, if we got to here, then none of the previous chips appear to
ian@0 175 be aliases for the current one. */
ian@0 176 set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */
ian@0 177 cfi->numchips++;
ian@0 178
ian@0 179 /* Put it back into Read Mode */
ian@0 180 cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
ian@0 181 cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
ian@0 182 xip_allowed(base, map);
ian@0 183
ian@0 184 printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n",
ian@0 185 map->name, cfi->interleave, cfi->device_type*8, base,
ian@0 186 map->bankwidth*8);
ian@0 187
ian@0 188 return 1;
ian@0 189 }
ian@0 190
ian@0 191 static int __xipram cfi_chip_setup(struct map_info *map,
ian@0 192 struct cfi_private *cfi)
ian@0 193 {
ian@0 194 int ofs_factor = cfi->interleave*cfi->device_type;
ian@0 195 __u32 base = 0;
ian@0 196 int num_erase_regions = cfi_read_query(map, base + (0x10 + 28)*ofs_factor);
ian@0 197 int i;
ian@0 198
ian@0 199 xip_enable(base, map, cfi);
ian@0 200 #ifdef DEBUG_CFI
ian@0 201 printk("Number of erase regions: %d\n", num_erase_regions);
ian@0 202 #endif
ian@0 203 if (!num_erase_regions)
ian@0 204 return 0;
ian@0 205
ian@0 206 cfi->cfiq = kmalloc(sizeof(struct cfi_ident) + num_erase_regions * 4, GFP_KERNEL);
ian@0 207 if (!cfi->cfiq) {
ian@0 208 printk(KERN_WARNING "%s: kmalloc failed for CFI ident structure\n", map->name);
ian@0 209 return 0;
ian@0 210 }
ian@0 211
ian@0 212 memset(cfi->cfiq,0,sizeof(struct cfi_ident));
ian@0 213
ian@0 214 cfi->cfi_mode = CFI_MODE_CFI;
ian@0 215
ian@0 216 /* Read the CFI info structure */
ian@0 217 xip_disable_qry(base, map, cfi);
ian@0 218 for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++)
ian@0 219 ((unsigned char *)cfi->cfiq)[i] = cfi_read_query(map,base + (0x10 + i)*ofs_factor);
ian@0 220
ian@0 221 /* Note we put the device back into Read Mode BEFORE going into Auto
ian@0 222 * Select Mode, as some devices support nesting of modes, others
ian@0 223 * don't. This way should always work.
ian@0 224 * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and
ian@0 225 * so should be treated as nops or illegal (and so put the device
ian@0 226 * back into Read Mode, which is a nop in this case).
ian@0 227 */
ian@0 228 cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL);
ian@0 229 cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL);
ian@0 230 cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL);
ian@0 231 cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL);
ian@0 232 cfi->mfr = cfi_read_query16(map, base);
ian@0 233 cfi->id = cfi_read_query16(map, base + ofs_factor);
ian@0 234
ian@0 235 /* Put it back into Read Mode */
ian@0 236 cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
ian@0 237 /* ... even if it's an Intel chip */
ian@0 238 cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
ian@0 239 xip_allowed(base, map);
ian@0 240
ian@0 241 /* Do any necessary byteswapping */
ian@0 242 cfi->cfiq->P_ID = le16_to_cpu(cfi->cfiq->P_ID);
ian@0 243
ian@0 244 cfi->cfiq->P_ADR = le16_to_cpu(cfi->cfiq->P_ADR);
ian@0 245 cfi->cfiq->A_ID = le16_to_cpu(cfi->cfiq->A_ID);
ian@0 246 cfi->cfiq->A_ADR = le16_to_cpu(cfi->cfiq->A_ADR);
ian@0 247 cfi->cfiq->InterfaceDesc = le16_to_cpu(cfi->cfiq->InterfaceDesc);
ian@0 248 cfi->cfiq->MaxBufWriteSize = le16_to_cpu(cfi->cfiq->MaxBufWriteSize);
ian@0 249
ian@0 250 #ifdef DEBUG_CFI
ian@0 251 /* Dump the information therein */
ian@0 252 print_cfi_ident(cfi->cfiq);
ian@0 253 #endif
ian@0 254
ian@0 255 for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
ian@0 256 cfi->cfiq->EraseRegionInfo[i] = le32_to_cpu(cfi->cfiq->EraseRegionInfo[i]);
ian@0 257
ian@0 258 #ifdef DEBUG_CFI
ian@0 259 printk(" Erase Region #%d: BlockSize 0x%4.4X bytes, %d blocks\n",
ian@0 260 i, (cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff,
ian@0 261 (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1);
ian@0 262 #endif
ian@0 263 }
ian@0 264
ian@0 265 printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n",
ian@0 266 map->name, cfi->interleave, cfi->device_type*8, base,
ian@0 267 map->bankwidth*8);
ian@0 268
ian@0 269 return 1;
ian@0 270 }
ian@0 271
ian@0 272 #ifdef DEBUG_CFI
ian@0 273 static char *vendorname(__u16 vendor)
ian@0 274 {
ian@0 275 switch (vendor) {
ian@0 276 case P_ID_NONE:
ian@0 277 return "None";
ian@0 278
ian@0 279 case P_ID_INTEL_EXT:
ian@0 280 return "Intel/Sharp Extended";
ian@0 281
ian@0 282 case P_ID_AMD_STD:
ian@0 283 return "AMD/Fujitsu Standard";
ian@0 284
ian@0 285 case P_ID_INTEL_STD:
ian@0 286 return "Intel/Sharp Standard";
ian@0 287
ian@0 288 case P_ID_AMD_EXT:
ian@0 289 return "AMD/Fujitsu Extended";
ian@0 290
ian@0 291 case P_ID_WINBOND:
ian@0 292 return "Winbond Standard";
ian@0 293
ian@0 294 case P_ID_ST_ADV:
ian@0 295 return "ST Advanced";
ian@0 296
ian@0 297 case P_ID_MITSUBISHI_STD:
ian@0 298 return "Mitsubishi Standard";
ian@0 299
ian@0 300 case P_ID_MITSUBISHI_EXT:
ian@0 301 return "Mitsubishi Extended";
ian@0 302
ian@0 303 case P_ID_SST_PAGE:
ian@0 304 return "SST Page Write";
ian@0 305
ian@0 306 case P_ID_INTEL_PERFORMANCE:
ian@0 307 return "Intel Performance Code";
ian@0 308
ian@0 309 case P_ID_INTEL_DATA:
ian@0 310 return "Intel Data";
ian@0 311
ian@0 312 case P_ID_RESERVED:
ian@0 313 return "Not Allowed / Reserved for Future Use";
ian@0 314
ian@0 315 default:
ian@0 316 return "Unknown";
ian@0 317 }
ian@0 318 }
ian@0 319
ian@0 320
ian@0 321 static void print_cfi_ident(struct cfi_ident *cfip)
ian@0 322 {
ian@0 323 #if 0
ian@0 324 if (cfip->qry[0] != 'Q' || cfip->qry[1] != 'R' || cfip->qry[2] != 'Y') {
ian@0 325 printk("Invalid CFI ident structure.\n");
ian@0 326 return;
ian@0 327 }
ian@0 328 #endif
ian@0 329 printk("Primary Vendor Command Set: %4.4X (%s)\n", cfip->P_ID, vendorname(cfip->P_ID));
ian@0 330 if (cfip->P_ADR)
ian@0 331 printk("Primary Algorithm Table at %4.4X\n", cfip->P_ADR);
ian@0 332 else
ian@0 333 printk("No Primary Algorithm Table\n");
ian@0 334
ian@0 335 printk("Alternative Vendor Command Set: %4.4X (%s)\n", cfip->A_ID, vendorname(cfip->A_ID));
ian@0 336 if (cfip->A_ADR)
ian@0 337 printk("Alternate Algorithm Table at %4.4X\n", cfip->A_ADR);
ian@0 338 else
ian@0 339 printk("No Alternate Algorithm Table\n");
ian@0 340
ian@0 341
ian@0 342 printk("Vcc Minimum: %2d.%d V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf);
ian@0 343 printk("Vcc Maximum: %2d.%d V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf);
ian@0 344 if (cfip->VppMin) {
ian@0 345 printk("Vpp Minimum: %2d.%d V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf);
ian@0 346 printk("Vpp Maximum: %2d.%d V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf);
ian@0 347 }
ian@0 348 else
ian@0 349 printk("No Vpp line\n");
ian@0 350
ian@0 351 printk("Typical byte/word write timeout: %d µs\n", 1<<cfip->WordWriteTimeoutTyp);
ian@0 352 printk("Maximum byte/word write timeout: %d µs\n", (1<<cfip->WordWriteTimeoutMax) * (1<<cfip->WordWriteTimeoutTyp));
ian@0 353
ian@0 354 if (cfip->BufWriteTimeoutTyp || cfip->BufWriteTimeoutMax) {
ian@0 355 printk("Typical full buffer write timeout: %d µs\n", 1<<cfip->BufWriteTimeoutTyp);
ian@0 356 printk("Maximum full buffer write timeout: %d µs\n", (1<<cfip->BufWriteTimeoutMax) * (1<<cfip->BufWriteTimeoutTyp));
ian@0 357 }
ian@0 358 else
ian@0 359 printk("Full buffer write not supported\n");
ian@0 360
ian@0 361 printk("Typical block erase timeout: %d ms\n", 1<<cfip->BlockEraseTimeoutTyp);
ian@0 362 printk("Maximum block erase timeout: %d ms\n", (1<<cfip->BlockEraseTimeoutMax) * (1<<cfip->BlockEraseTimeoutTyp));
ian@0 363 if (cfip->ChipEraseTimeoutTyp || cfip->ChipEraseTimeoutMax) {
ian@0 364 printk("Typical chip erase timeout: %d ms\n", 1<<cfip->ChipEraseTimeoutTyp);
ian@0 365 printk("Maximum chip erase timeout: %d ms\n", (1<<cfip->ChipEraseTimeoutMax) * (1<<cfip->ChipEraseTimeoutTyp));
ian@0 366 }
ian@0 367 else
ian@0 368 printk("Chip erase not supported\n");
ian@0 369
ian@0 370 printk("Device size: 0x%X bytes (%d MiB)\n", 1 << cfip->DevSize, 1<< (cfip->DevSize - 20));
ian@0 371 printk("Flash Device Interface description: 0x%4.4X\n", cfip->InterfaceDesc);
ian@0 372 switch(cfip->InterfaceDesc) {
ian@0 373 case 0:
ian@0 374 printk(" - x8-only asynchronous interface\n");
ian@0 375 break;
ian@0 376
ian@0 377 case 1:
ian@0 378 printk(" - x16-only asynchronous interface\n");
ian@0 379 break;
ian@0 380
ian@0 381 case 2:
ian@0 382 printk(" - supports x8 and x16 via BYTE# with asynchronous interface\n");
ian@0 383 break;
ian@0 384
ian@0 385 case 3:
ian@0 386 printk(" - x32-only asynchronous interface\n");
ian@0 387 break;
ian@0 388
ian@0 389 case 4:
ian@0 390 printk(" - supports x16 and x32 via Word# with asynchronous interface\n");
ian@0 391 break;
ian@0 392
ian@0 393 case 65535:
ian@0 394 printk(" - Not Allowed / Reserved\n");
ian@0 395 break;
ian@0 396
ian@0 397 default:
ian@0 398 printk(" - Unknown\n");
ian@0 399 break;
ian@0 400 }
ian@0 401
ian@0 402 printk("Max. bytes in buffer write: 0x%x\n", 1<< cfip->MaxBufWriteSize);
ian@0 403 printk("Number of Erase Block Regions: %d\n", cfip->NumEraseRegions);
ian@0 404
ian@0 405 }
ian@0 406 #endif /* DEBUG_CFI */
ian@0 407
ian@0 408 static struct chip_probe cfi_chip_probe = {
ian@0 409 .name = "CFI",
ian@0 410 .probe_chip = cfi_probe_chip
ian@0 411 };
ian@0 412
ian@0 413 struct mtd_info *cfi_probe(struct map_info *map)
ian@0 414 {
ian@0 415 /*
ian@0 416 * Just use the generic probe stuff to call our CFI-specific
ian@0 417 * chip_probe routine in all the possible permutations, etc.
ian@0 418 */
ian@0 419 return mtd_do_chip_probe(map, &cfi_chip_probe);
ian@0 420 }
ian@0 421
ian@0 422 static struct mtd_chip_driver cfi_chipdrv = {
ian@0 423 .probe = cfi_probe,
ian@0 424 .name = "cfi_probe",
ian@0 425 .module = THIS_MODULE
ian@0 426 };
ian@0 427
ian@0 428 static int __init cfi_probe_init(void)
ian@0 429 {
ian@0 430 register_mtd_chip_driver(&cfi_chipdrv);
ian@0 431 return 0;
ian@0 432 }
ian@0 433
ian@0 434 static void __exit cfi_probe_exit(void)
ian@0 435 {
ian@0 436 unregister_mtd_chip_driver(&cfi_chipdrv);
ian@0 437 }
ian@0 438
ian@0 439 module_init(cfi_probe_init);
ian@0 440 module_exit(cfi_probe_exit);
ian@0 441
ian@0 442 MODULE_LICENSE("GPL");
ian@0 443 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al.");
ian@0 444 MODULE_DESCRIPTION("Probe code for CFI-compliant flash chips");