11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Routines common to all CFI-type probes. 31da177e4SLinus Torvalds * (C) 2001-2003 Red Hat, Inc. 41da177e4SLinus Torvalds * GPL'd 51f948b43SThomas Gleixner * $Id: gen_probe.c,v 1.24 2005/11/07 11:14:23 gleixner Exp $ 61da177e4SLinus Torvalds */ 71da177e4SLinus Torvalds 81da177e4SLinus Torvalds #include <linux/kernel.h> 91da177e4SLinus Torvalds #include <linux/slab.h> 101da177e4SLinus Torvalds #include <linux/module.h> 111da177e4SLinus Torvalds #include <linux/mtd/mtd.h> 121da177e4SLinus Torvalds #include <linux/mtd/map.h> 131da177e4SLinus Torvalds #include <linux/mtd/cfi.h> 141da177e4SLinus Torvalds #include <linux/mtd/gen_probe.h> 151da177e4SLinus Torvalds 161da177e4SLinus Torvalds static struct mtd_info *check_cmd_set(struct map_info *, int); 171da177e4SLinus Torvalds static struct cfi_private *genprobe_ident_chips(struct map_info *map, 181da177e4SLinus Torvalds struct chip_probe *cp); 191da177e4SLinus Torvalds static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp, 201da177e4SLinus Torvalds struct cfi_private *cfi); 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp) 231da177e4SLinus Torvalds { 241da177e4SLinus Torvalds struct mtd_info *mtd = NULL; 251da177e4SLinus Torvalds struct cfi_private *cfi; 261da177e4SLinus Torvalds 271da177e4SLinus Torvalds /* First probe the map to see if we have CFI stuff there. */ 281da177e4SLinus Torvalds cfi = genprobe_ident_chips(map, cp); 291da177e4SLinus Torvalds 301da177e4SLinus Torvalds if (!cfi) 311da177e4SLinus Torvalds return NULL; 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds map->fldrv_priv = cfi; 341da177e4SLinus Torvalds /* OK we liked it. Now find a driver for the command set it talks */ 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds mtd = check_cmd_set(map, 1); /* First the primary cmdset */ 371da177e4SLinus Torvalds if (!mtd) 381da177e4SLinus Torvalds mtd = check_cmd_set(map, 0); /* Then the secondary */ 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds if (mtd) 411da177e4SLinus Torvalds return mtd; 421da177e4SLinus Torvalds 431da177e4SLinus Torvalds printk(KERN_WARNING"gen_probe: No supported Vendor Command Set found\n"); 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds kfree(cfi->cfiq); 461da177e4SLinus Torvalds kfree(cfi); 471da177e4SLinus Torvalds map->fldrv_priv = NULL; 481da177e4SLinus Torvalds return NULL; 491da177e4SLinus Torvalds } 501da177e4SLinus Torvalds EXPORT_SYMBOL(mtd_do_chip_probe); 511da177e4SLinus Torvalds 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp) 541da177e4SLinus Torvalds { 551da177e4SLinus Torvalds struct cfi_private cfi; 561da177e4SLinus Torvalds struct cfi_private *retcfi; 571da177e4SLinus Torvalds unsigned long *chip_map; 581da177e4SLinus Torvalds int i, j, mapsize; 591da177e4SLinus Torvalds int max_chips; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds memset(&cfi, 0, sizeof(cfi)); 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds /* Call the probetype-specific code with all permutations of 641da177e4SLinus Torvalds interleave and device type, etc. */ 651da177e4SLinus Torvalds if (!genprobe_new_chip(map, cp, &cfi)) { 661da177e4SLinus Torvalds /* The probe didn't like it */ 671da177e4SLinus Torvalds printk(KERN_DEBUG "%s: Found no %s device at location zero\n", 681da177e4SLinus Torvalds cp->name, map->name); 691da177e4SLinus Torvalds return NULL; 701da177e4SLinus Torvalds } 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds #if 0 /* Let the CFI probe routine do this sanity check. The Intel and AMD 731da177e4SLinus Torvalds probe routines won't ever return a broken CFI structure anyway, 741da177e4SLinus Torvalds because they make them up themselves. 751da177e4SLinus Torvalds */ 761da177e4SLinus Torvalds if (cfi.cfiq->NumEraseRegions == 0) { 771da177e4SLinus Torvalds printk(KERN_WARNING "Number of erase regions is zero\n"); 781da177e4SLinus Torvalds kfree(cfi.cfiq); 791da177e4SLinus Torvalds return NULL; 801da177e4SLinus Torvalds } 811da177e4SLinus Torvalds #endif 821da177e4SLinus Torvalds cfi.chipshift = cfi.cfiq->DevSize; 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds if (cfi_interleave_is_1(&cfi)) { 851da177e4SLinus Torvalds ; 861da177e4SLinus Torvalds } else if (cfi_interleave_is_2(&cfi)) { 871da177e4SLinus Torvalds cfi.chipshift++; 881da177e4SLinus Torvalds } else if (cfi_interleave_is_4((&cfi))) { 891da177e4SLinus Torvalds cfi.chipshift += 2; 901da177e4SLinus Torvalds } else if (cfi_interleave_is_8(&cfi)) { 911da177e4SLinus Torvalds cfi.chipshift += 3; 921da177e4SLinus Torvalds } else { 931da177e4SLinus Torvalds BUG(); 941da177e4SLinus Torvalds } 951da177e4SLinus Torvalds 961da177e4SLinus Torvalds cfi.numchips = 1; 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds /* 991da177e4SLinus Torvalds * Allocate memory for bitmap of valid chips. 1001da177e4SLinus Torvalds * Align bitmap storage size to full byte. 1011da177e4SLinus Torvalds */ 1021da177e4SLinus Torvalds max_chips = map->size >> cfi.chipshift; 1031da177e4SLinus Torvalds mapsize = (max_chips / 8) + ((max_chips % 8) ? 1 : 0); 1041da177e4SLinus Torvalds chip_map = kmalloc(mapsize, GFP_KERNEL); 1051da177e4SLinus Torvalds if (!chip_map) { 1061da177e4SLinus Torvalds printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name); 1071da177e4SLinus Torvalds kfree(cfi.cfiq); 1081da177e4SLinus Torvalds return NULL; 1091da177e4SLinus Torvalds } 1101da177e4SLinus Torvalds memset (chip_map, 0, mapsize); 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds set_bit(0, chip_map); /* Mark first chip valid */ 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds /* 1151da177e4SLinus Torvalds * Now probe for other chips, checking sensibly for aliases while 1161da177e4SLinus Torvalds * we're at it. The new_chip probe above should have let the first 1171da177e4SLinus Torvalds * chip in read mode. 1181da177e4SLinus Torvalds */ 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds for (i = 1; i < max_chips; i++) { 1211da177e4SLinus Torvalds cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi); 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds /* 1251da177e4SLinus Torvalds * Now allocate the space for the structures we need to return to 1261da177e4SLinus Torvalds * our caller, and copy the appropriate data into them. 1271da177e4SLinus Torvalds */ 1281da177e4SLinus Torvalds 1291da177e4SLinus Torvalds retcfi = kmalloc(sizeof(struct cfi_private) + cfi.numchips * sizeof(struct flchip), GFP_KERNEL); 1301da177e4SLinus Torvalds 1311da177e4SLinus Torvalds if (!retcfi) { 1321da177e4SLinus Torvalds printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name); 1331da177e4SLinus Torvalds kfree(cfi.cfiq); 1341da177e4SLinus Torvalds kfree(chip_map); 1351da177e4SLinus Torvalds return NULL; 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds memcpy(retcfi, &cfi, sizeof(cfi)); 1391da177e4SLinus Torvalds memset(&retcfi->chips[0], 0, sizeof(struct flchip) * cfi.numchips); 1401da177e4SLinus Torvalds 1411da177e4SLinus Torvalds for (i = 0, j = 0; (j < cfi.numchips) && (i < max_chips); i++) { 1421da177e4SLinus Torvalds if(test_bit(i, chip_map)) { 1431da177e4SLinus Torvalds struct flchip *pchip = &retcfi->chips[j++]; 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds pchip->start = (i << cfi.chipshift); 1461da177e4SLinus Torvalds pchip->state = FL_READY; 1471da177e4SLinus Torvalds init_waitqueue_head(&pchip->wq); 1481da177e4SLinus Torvalds spin_lock_init(&pchip->_spinlock); 1491da177e4SLinus Torvalds pchip->mutex = &pchip->_spinlock; 1501da177e4SLinus Torvalds } 1511da177e4SLinus Torvalds } 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds kfree(chip_map); 1541da177e4SLinus Torvalds return retcfi; 1551da177e4SLinus Torvalds } 1561da177e4SLinus Torvalds 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp, 1591da177e4SLinus Torvalds struct cfi_private *cfi) 1601da177e4SLinus Torvalds { 1611da177e4SLinus Torvalds int min_chips = (map_bankwidth(map)/4?:1); /* At most 4-bytes wide. */ 1621da177e4SLinus Torvalds int max_chips = map_bankwidth(map); /* And minimum 1 */ 1631da177e4SLinus Torvalds int nr_chips, type; 1641da177e4SLinus Torvalds 1656170b434SRussell King for (nr_chips = max_chips; nr_chips >= min_chips; nr_chips >>= 1) { 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds if (!cfi_interleave_supported(nr_chips)) 1681da177e4SLinus Torvalds continue; 1691da177e4SLinus Torvalds 1701da177e4SLinus Torvalds cfi->interleave = nr_chips; 1711da177e4SLinus Torvalds 1721da177e4SLinus Torvalds /* Minimum device size. Don't look for one 8-bit device 1731da177e4SLinus Torvalds in a 16-bit bus, etc. */ 1741da177e4SLinus Torvalds type = map_bankwidth(map) / nr_chips; 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds for (; type <= CFI_DEVICETYPE_X32; type<<=1) { 1771da177e4SLinus Torvalds cfi->device_type = type; 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds if (cp->probe_chip(map, 0, NULL, cfi)) 1801da177e4SLinus Torvalds return 1; 1811da177e4SLinus Torvalds } 1821da177e4SLinus Torvalds } 1831da177e4SLinus Torvalds return 0; 1841da177e4SLinus Torvalds } 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds typedef struct mtd_info *cfi_cmdset_fn_t(struct map_info *, int); 1871da177e4SLinus Torvalds 1881da177e4SLinus Torvalds extern cfi_cmdset_fn_t cfi_cmdset_0001; 1891da177e4SLinus Torvalds extern cfi_cmdset_fn_t cfi_cmdset_0002; 1901da177e4SLinus Torvalds extern cfi_cmdset_fn_t cfi_cmdset_0020; 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map, 1931da177e4SLinus Torvalds int primary) 1941da177e4SLinus Torvalds { 1951da177e4SLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 1961da177e4SLinus Torvalds __u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID; 1971da177e4SLinus Torvalds #if defined(CONFIG_MODULES) && defined(HAVE_INTER_MODULE) 1981da177e4SLinus Torvalds char probename[32]; 1991da177e4SLinus Torvalds cfi_cmdset_fn_t *probe_function; 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds sprintf(probename, "cfi_cmdset_%4.4X", type); 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds probe_function = inter_module_get_request(probename, probename); 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds if (probe_function) { 2061da177e4SLinus Torvalds struct mtd_info *mtd; 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds mtd = (*probe_function)(map, primary); 2091da177e4SLinus Torvalds /* If it was happy, it'll have increased its own use count */ 2101da177e4SLinus Torvalds inter_module_put(probename); 2111da177e4SLinus Torvalds return mtd; 2121da177e4SLinus Torvalds } 2131da177e4SLinus Torvalds #endif 2141da177e4SLinus Torvalds printk(KERN_NOTICE "Support for command set %04X not present\n", 2151da177e4SLinus Torvalds type); 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds return NULL; 2181da177e4SLinus Torvalds } 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds static struct mtd_info *check_cmd_set(struct map_info *map, int primary) 2211da177e4SLinus Torvalds { 2221da177e4SLinus Torvalds struct cfi_private *cfi = map->fldrv_priv; 2231da177e4SLinus Torvalds __u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID; 2241da177e4SLinus Torvalds 2251da177e4SLinus Torvalds if (type == P_ID_NONE || type == P_ID_RESERVED) 2261da177e4SLinus Torvalds return NULL; 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds switch(type){ 2291da177e4SLinus Torvalds /* Urgh. Ifdefs. The version with weak symbols was 2301da177e4SLinus Torvalds * _much_ nicer. Shame it didn't seem to work on 2311da177e4SLinus Torvalds * anything but x86, really. 2321da177e4SLinus Torvalds * But we can't rely in inter_module_get() because 2331da177e4SLinus Torvalds * that'd mean we depend on link order. 2341da177e4SLinus Torvalds */ 2351da177e4SLinus Torvalds #ifdef CONFIG_MTD_CFI_INTELEXT 2361da177e4SLinus Torvalds case 0x0001: 2371da177e4SLinus Torvalds case 0x0003: 238638d9838SNicolas Pitre case 0x0200: 2391da177e4SLinus Torvalds return cfi_cmdset_0001(map, primary); 2401da177e4SLinus Torvalds #endif 2411da177e4SLinus Torvalds #ifdef CONFIG_MTD_CFI_AMDSTD 2421da177e4SLinus Torvalds case 0x0002: 2431da177e4SLinus Torvalds return cfi_cmdset_0002(map, primary); 2441da177e4SLinus Torvalds #endif 2451da177e4SLinus Torvalds #ifdef CONFIG_MTD_CFI_STAA 2461da177e4SLinus Torvalds case 0x0020: 2471da177e4SLinus Torvalds return cfi_cmdset_0020(map, primary); 2481da177e4SLinus Torvalds #endif 2491da177e4SLinus Torvalds } 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds return cfi_cmdset_unknown(map, primary); 2521da177e4SLinus Torvalds } 2531da177e4SLinus Torvalds 2541da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 2551da177e4SLinus Torvalds MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); 2561da177e4SLinus Torvalds MODULE_DESCRIPTION("Helper routines for flash chip probe code"); 257