18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Common Flash Interface support: 38c2ecf20Sopenharmony_ci * ST Advanced Architecture Command Set (ID 0x0020) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) 2000 Red Hat. GPL'd 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * 10/10/2000 Nicolas Pitre <nico@fluxnic.net> 88c2ecf20Sopenharmony_ci * - completely revamped method functions so they are aware and 98c2ecf20Sopenharmony_ci * independent of the flash geometry (buswidth, interleave, etc.) 108c2ecf20Sopenharmony_ci * - scalability vs code size is completely set at compile-time 118c2ecf20Sopenharmony_ci * (see include/linux/mtd/cfi.h for selection) 128c2ecf20Sopenharmony_ci * - optimized write buffer method 138c2ecf20Sopenharmony_ci * 06/21/2002 Joern Engel <joern@wh.fh-wedel.de> and others 148c2ecf20Sopenharmony_ci * - modified Intel Command Set 0x0001 to support ST Advanced Architecture 158c2ecf20Sopenharmony_ci * (command set 0x0020) 168c2ecf20Sopenharmony_ci * - added a writev function 178c2ecf20Sopenharmony_ci * 07/13/2005 Joern Engel <joern@wh.fh-wedel.de> 188c2ecf20Sopenharmony_ci * - Plugged memory leak in cfi_staa_writev(). 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/types.h> 238c2ecf20Sopenharmony_ci#include <linux/kernel.h> 248c2ecf20Sopenharmony_ci#include <linux/sched.h> 258c2ecf20Sopenharmony_ci#include <asm/io.h> 268c2ecf20Sopenharmony_ci#include <asm/byteorder.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <linux/errno.h> 298c2ecf20Sopenharmony_ci#include <linux/slab.h> 308c2ecf20Sopenharmony_ci#include <linux/delay.h> 318c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 328c2ecf20Sopenharmony_ci#include <linux/mtd/map.h> 338c2ecf20Sopenharmony_ci#include <linux/mtd/cfi.h> 348c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); 388c2ecf20Sopenharmony_cistatic int cfi_staa_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); 398c2ecf20Sopenharmony_cistatic int cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs, 408c2ecf20Sopenharmony_ci unsigned long count, loff_t to, size_t *retlen); 418c2ecf20Sopenharmony_cistatic int cfi_staa_erase_varsize(struct mtd_info *, struct erase_info *); 428c2ecf20Sopenharmony_cistatic void cfi_staa_sync (struct mtd_info *); 438c2ecf20Sopenharmony_cistatic int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 448c2ecf20Sopenharmony_cistatic int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 458c2ecf20Sopenharmony_cistatic int cfi_staa_suspend (struct mtd_info *); 468c2ecf20Sopenharmony_cistatic void cfi_staa_resume (struct mtd_info *); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void cfi_staa_destroy(struct mtd_info *); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct mtd_info *cfi_cmdset_0020(struct map_info *, int); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic struct mtd_info *cfi_staa_setup (struct map_info *); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic struct mtd_chip_driver cfi_staa_chipdrv = { 558c2ecf20Sopenharmony_ci .probe = NULL, /* Not usable directly */ 568c2ecf20Sopenharmony_ci .destroy = cfi_staa_destroy, 578c2ecf20Sopenharmony_ci .name = "cfi_cmdset_0020", 588c2ecf20Sopenharmony_ci .module = THIS_MODULE 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* #define DEBUG_LOCK_BITS */ 628c2ecf20Sopenharmony_ci//#define DEBUG_CFI_FEATURES 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 658c2ecf20Sopenharmony_cistatic void cfi_tell_features(struct cfi_pri_intelext *extp) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci int i; 688c2ecf20Sopenharmony_ci printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport); 698c2ecf20Sopenharmony_ci printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported"); 708c2ecf20Sopenharmony_ci printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported"); 718c2ecf20Sopenharmony_ci printk(" - Suspend Program: %s\n", extp->FeatureSupport&4?"supported":"unsupported"); 728c2ecf20Sopenharmony_ci printk(" - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported"); 738c2ecf20Sopenharmony_ci printk(" - Queued Erase: %s\n", extp->FeatureSupport&16?"supported":"unsupported"); 748c2ecf20Sopenharmony_ci printk(" - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported"); 758c2ecf20Sopenharmony_ci printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported"); 768c2ecf20Sopenharmony_ci printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported"); 778c2ecf20Sopenharmony_ci printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported"); 788c2ecf20Sopenharmony_ci for (i=9; i<32; i++) { 798c2ecf20Sopenharmony_ci if (extp->FeatureSupport & (1<<i)) 808c2ecf20Sopenharmony_ci printk(" - Unknown Bit %X: supported\n", i); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci printk(" Supported functions after Suspend: %2.2X\n", extp->SuspendCmdSupport); 848c2ecf20Sopenharmony_ci printk(" - Program after Erase Suspend: %s\n", extp->SuspendCmdSupport&1?"supported":"unsupported"); 858c2ecf20Sopenharmony_ci for (i=1; i<8; i++) { 868c2ecf20Sopenharmony_ci if (extp->SuspendCmdSupport & (1<<i)) 878c2ecf20Sopenharmony_ci printk(" - Unknown Bit %X: supported\n", i); 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci printk(" Block Status Register Mask: %4.4X\n", extp->BlkStatusRegMask); 918c2ecf20Sopenharmony_ci printk(" - Lock Bit Active: %s\n", extp->BlkStatusRegMask&1?"yes":"no"); 928c2ecf20Sopenharmony_ci printk(" - Valid Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no"); 938c2ecf20Sopenharmony_ci for (i=2; i<16; i++) { 948c2ecf20Sopenharmony_ci if (extp->BlkStatusRegMask & (1<<i)) 958c2ecf20Sopenharmony_ci printk(" - Unknown Bit %X Active: yes\n",i); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n", 998c2ecf20Sopenharmony_ci extp->VccOptimal >> 8, extp->VccOptimal & 0xf); 1008c2ecf20Sopenharmony_ci if (extp->VppOptimal) 1018c2ecf20Sopenharmony_ci printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", 1028c2ecf20Sopenharmony_ci extp->VppOptimal >> 8, extp->VppOptimal & 0xf); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci#endif 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* This routine is made available to other mtd code via 1078c2ecf20Sopenharmony_ci * inter_module_register. It must only be accessed through 1088c2ecf20Sopenharmony_ci * inter_module_get which will bump the use count of this module. The 1098c2ecf20Sopenharmony_ci * addresses passed back in cfi are valid as long as the use count of 1108c2ecf20Sopenharmony_ci * this module is non-zero, i.e. between inter_module_get and 1118c2ecf20Sopenharmony_ci * inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_cistruct mtd_info *cfi_cmdset_0020(struct map_info *map, int primary) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 1168c2ecf20Sopenharmony_ci int i; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (cfi->cfi_mode) { 1198c2ecf20Sopenharmony_ci /* 1208c2ecf20Sopenharmony_ci * It's a real CFI chip, not one for which the probe 1218c2ecf20Sopenharmony_ci * routine faked a CFI structure. So we read the feature 1228c2ecf20Sopenharmony_ci * table from it. 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; 1258c2ecf20Sopenharmony_ci struct cfi_pri_intelext *extp; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "ST Microelectronics"); 1288c2ecf20Sopenharmony_ci if (!extp) 1298c2ecf20Sopenharmony_ci return NULL; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (extp->MajorVersion != '1' || 1328c2ecf20Sopenharmony_ci (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { 1338c2ecf20Sopenharmony_ci printk(KERN_ERR " Unknown ST Microelectronics" 1348c2ecf20Sopenharmony_ci " Extended Query version %c.%c.\n", 1358c2ecf20Sopenharmony_ci extp->MajorVersion, extp->MinorVersion); 1368c2ecf20Sopenharmony_ci kfree(extp); 1378c2ecf20Sopenharmony_ci return NULL; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Do some byteswapping if necessary */ 1418c2ecf20Sopenharmony_ci extp->FeatureSupport = cfi32_to_cpu(map, extp->FeatureSupport); 1428c2ecf20Sopenharmony_ci extp->BlkStatusRegMask = cfi32_to_cpu(map, 1438c2ecf20Sopenharmony_ci extp->BlkStatusRegMask); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 1468c2ecf20Sopenharmony_ci /* Tell the user about it in lots of lovely detail */ 1478c2ecf20Sopenharmony_ci cfi_tell_features(extp); 1488c2ecf20Sopenharmony_ci#endif 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* Install our own private info structure */ 1518c2ecf20Sopenharmony_ci cfi->cmdset_priv = extp; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci for (i=0; i< cfi->numchips; i++) { 1558c2ecf20Sopenharmony_ci cfi->chips[i].word_write_time = 128; 1568c2ecf20Sopenharmony_ci cfi->chips[i].buffer_write_time = 128; 1578c2ecf20Sopenharmony_ci cfi->chips[i].erase_time = 1024; 1588c2ecf20Sopenharmony_ci cfi->chips[i].ref_point_counter = 0; 1598c2ecf20Sopenharmony_ci init_waitqueue_head(&(cfi->chips[i].wq)); 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return cfi_staa_setup(map); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0020); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic struct mtd_info *cfi_staa_setup(struct map_info *map) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 1698c2ecf20Sopenharmony_ci struct mtd_info *mtd; 1708c2ecf20Sopenharmony_ci unsigned long offset = 0; 1718c2ecf20Sopenharmony_ci int i,j; 1728c2ecf20Sopenharmony_ci unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); 1758c2ecf20Sopenharmony_ci //printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (!mtd) { 1788c2ecf20Sopenharmony_ci kfree(cfi->cmdset_priv); 1798c2ecf20Sopenharmony_ci return NULL; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci mtd->priv = map; 1838c2ecf20Sopenharmony_ci mtd->type = MTD_NORFLASH; 1848c2ecf20Sopenharmony_ci mtd->size = devsize * cfi->numchips; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; 1878c2ecf20Sopenharmony_ci mtd->eraseregions = kmalloc_array(mtd->numeraseregions, 1888c2ecf20Sopenharmony_ci sizeof(struct mtd_erase_region_info), 1898c2ecf20Sopenharmony_ci GFP_KERNEL); 1908c2ecf20Sopenharmony_ci if (!mtd->eraseregions) { 1918c2ecf20Sopenharmony_ci kfree(cfi->cmdset_priv); 1928c2ecf20Sopenharmony_ci kfree(mtd); 1938c2ecf20Sopenharmony_ci return NULL; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci for (i=0; i<cfi->cfiq->NumEraseRegions; i++) { 1978c2ecf20Sopenharmony_ci unsigned long ernum, ersize; 1988c2ecf20Sopenharmony_ci ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave; 1998c2ecf20Sopenharmony_ci ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (mtd->erasesize < ersize) { 2028c2ecf20Sopenharmony_ci mtd->erasesize = ersize; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci for (j=0; j<cfi->numchips; j++) { 2058c2ecf20Sopenharmony_ci mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset; 2068c2ecf20Sopenharmony_ci mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize; 2078c2ecf20Sopenharmony_ci mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci offset += (ersize * ernum); 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (offset != devsize) { 2138c2ecf20Sopenharmony_ci /* Argh */ 2148c2ecf20Sopenharmony_ci printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); 2158c2ecf20Sopenharmony_ci kfree(mtd->eraseregions); 2168c2ecf20Sopenharmony_ci kfree(cfi->cmdset_priv); 2178c2ecf20Sopenharmony_ci kfree(mtd); 2188c2ecf20Sopenharmony_ci return NULL; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci for (i=0; i<mtd->numeraseregions;i++){ 2228c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%d: offset=0x%llx,size=0x%x,blocks=%d\n", 2238c2ecf20Sopenharmony_ci i, (unsigned long long)mtd->eraseregions[i].offset, 2248c2ecf20Sopenharmony_ci mtd->eraseregions[i].erasesize, 2258c2ecf20Sopenharmony_ci mtd->eraseregions[i].numblocks); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* Also select the correct geometry setup too */ 2298c2ecf20Sopenharmony_ci mtd->_erase = cfi_staa_erase_varsize; 2308c2ecf20Sopenharmony_ci mtd->_read = cfi_staa_read; 2318c2ecf20Sopenharmony_ci mtd->_write = cfi_staa_write_buffers; 2328c2ecf20Sopenharmony_ci mtd->_writev = cfi_staa_writev; 2338c2ecf20Sopenharmony_ci mtd->_sync = cfi_staa_sync; 2348c2ecf20Sopenharmony_ci mtd->_lock = cfi_staa_lock; 2358c2ecf20Sopenharmony_ci mtd->_unlock = cfi_staa_unlock; 2368c2ecf20Sopenharmony_ci mtd->_suspend = cfi_staa_suspend; 2378c2ecf20Sopenharmony_ci mtd->_resume = cfi_staa_resume; 2388c2ecf20Sopenharmony_ci mtd->flags = MTD_CAP_NORFLASH & ~MTD_BIT_WRITEABLE; 2398c2ecf20Sopenharmony_ci mtd->writesize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */ 2408c2ecf20Sopenharmony_ci mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; 2418c2ecf20Sopenharmony_ci map->fldrv = &cfi_staa_chipdrv; 2428c2ecf20Sopenharmony_ci __module_get(THIS_MODULE); 2438c2ecf20Sopenharmony_ci mtd->name = map->name; 2448c2ecf20Sopenharmony_ci return mtd; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci map_word status, status_OK; 2518c2ecf20Sopenharmony_ci unsigned long timeo; 2528c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 2538c2ecf20Sopenharmony_ci int suspended = 0; 2548c2ecf20Sopenharmony_ci unsigned long cmd_addr; 2558c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci adr += chip->start; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* Ensure cmd read/writes are aligned. */ 2608c2ecf20Sopenharmony_ci cmd_addr = adr & ~(map_bankwidth(map)-1); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* Let's determine this according to the interleave only once */ 2638c2ecf20Sopenharmony_ci status_OK = CMD(0x80); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci timeo = jiffies + HZ; 2668c2ecf20Sopenharmony_ci retry: 2678c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* Check that the chip's ready to talk to us. 2708c2ecf20Sopenharmony_ci * If it's in FL_ERASING state, suspend it and make it talk now. 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_ci switch (chip->state) { 2738c2ecf20Sopenharmony_ci case FL_ERASING: 2748c2ecf20Sopenharmony_ci if (!(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)) 2758c2ecf20Sopenharmony_ci goto sleep; /* We don't support erase suspend */ 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci map_write (map, CMD(0xb0), cmd_addr); 2788c2ecf20Sopenharmony_ci /* If the flash has finished erasing, then 'erase suspend' 2798c2ecf20Sopenharmony_ci * appears to make some (28F320) flash devices switch to 2808c2ecf20Sopenharmony_ci * 'read' mode. Make sure that we switch to 'read status' 2818c2ecf20Sopenharmony_ci * mode so we get the right data. --rmk 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), cmd_addr); 2848c2ecf20Sopenharmony_ci chip->oldstate = FL_ERASING; 2858c2ecf20Sopenharmony_ci chip->state = FL_ERASE_SUSPENDING; 2868c2ecf20Sopenharmony_ci // printk("Erase suspending at 0x%lx\n", cmd_addr); 2878c2ecf20Sopenharmony_ci for (;;) { 2888c2ecf20Sopenharmony_ci status = map_read(map, cmd_addr); 2898c2ecf20Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 2908c2ecf20Sopenharmony_ci break; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 2938c2ecf20Sopenharmony_ci /* Urgh */ 2948c2ecf20Sopenharmony_ci map_write(map, CMD(0xd0), cmd_addr); 2958c2ecf20Sopenharmony_ci /* make sure we're in 'read status' mode */ 2968c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), cmd_addr); 2978c2ecf20Sopenharmony_ci chip->state = FL_ERASING; 2988c2ecf20Sopenharmony_ci wake_up(&chip->wq); 2998c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 3008c2ecf20Sopenharmony_ci printk(KERN_ERR "Chip not ready after erase " 3018c2ecf20Sopenharmony_ci "suspended: status = 0x%lx\n", status.x[0]); 3028c2ecf20Sopenharmony_ci return -EIO; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 3068c2ecf20Sopenharmony_ci cfi_udelay(1); 3078c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci suspended = 1; 3118c2ecf20Sopenharmony_ci map_write(map, CMD(0xff), cmd_addr); 3128c2ecf20Sopenharmony_ci chip->state = FL_READY; 3138c2ecf20Sopenharmony_ci break; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci#if 0 3168c2ecf20Sopenharmony_ci case FL_WRITING: 3178c2ecf20Sopenharmony_ci /* Not quite yet */ 3188c2ecf20Sopenharmony_ci#endif 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci case FL_READY: 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci case FL_CFI_QUERY: 3248c2ecf20Sopenharmony_ci case FL_JEDEC_QUERY: 3258c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), cmd_addr); 3268c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 3278c2ecf20Sopenharmony_ci fallthrough; 3288c2ecf20Sopenharmony_ci case FL_STATUS: 3298c2ecf20Sopenharmony_ci status = map_read(map, cmd_addr); 3308c2ecf20Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) { 3318c2ecf20Sopenharmony_ci map_write(map, CMD(0xff), cmd_addr); 3328c2ecf20Sopenharmony_ci chip->state = FL_READY; 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* Urgh. Chip not yet ready to talk to us. */ 3378c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 3388c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 3398c2ecf20Sopenharmony_ci printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %lx\n", status.x[0]); 3408c2ecf20Sopenharmony_ci return -EIO; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 3448c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 3458c2ecf20Sopenharmony_ci cfi_udelay(1); 3468c2ecf20Sopenharmony_ci goto retry; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci default: 3498c2ecf20Sopenharmony_ci sleep: 3508c2ecf20Sopenharmony_ci /* Stick ourselves on a wait queue to be woken when 3518c2ecf20Sopenharmony_ci someone changes the status */ 3528c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 3538c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 3548c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 3558c2ecf20Sopenharmony_ci schedule(); 3568c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 3578c2ecf20Sopenharmony_ci timeo = jiffies + HZ; 3588c2ecf20Sopenharmony_ci goto retry; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci map_copy_from(map, buf, adr, len); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (suspended) { 3648c2ecf20Sopenharmony_ci chip->state = chip->oldstate; 3658c2ecf20Sopenharmony_ci /* What if one interleaved chip has finished and the 3668c2ecf20Sopenharmony_ci other hasn't? The old code would leave the finished 3678c2ecf20Sopenharmony_ci one in READY mode. That's bad, and caused -EROFS 3688c2ecf20Sopenharmony_ci errors to be returned from do_erase_oneblock because 3698c2ecf20Sopenharmony_ci that's the only bit it checked for at the time. 3708c2ecf20Sopenharmony_ci As the state machine appears to explicitly allow 3718c2ecf20Sopenharmony_ci sending the 0x70 (Read Status) command to an erasing 3728c2ecf20Sopenharmony_ci chip and expecting it to be ignored, that's what we 3738c2ecf20Sopenharmony_ci do. */ 3748c2ecf20Sopenharmony_ci map_write(map, CMD(0xd0), cmd_addr); 3758c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), cmd_addr); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci wake_up(&chip->wq); 3798c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int cfi_staa_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 3868c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 3878c2ecf20Sopenharmony_ci unsigned long ofs; 3888c2ecf20Sopenharmony_ci int chipnum; 3898c2ecf20Sopenharmony_ci int ret = 0; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* ofs: offset within the first chip that the first read should start */ 3928c2ecf20Sopenharmony_ci chipnum = (from >> cfi->chipshift); 3938c2ecf20Sopenharmony_ci ofs = from - (chipnum << cfi->chipshift); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci while (len) { 3968c2ecf20Sopenharmony_ci unsigned long thislen; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (chipnum >= cfi->numchips) 3998c2ecf20Sopenharmony_ci break; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if ((len + ofs -1) >> cfi->chipshift) 4028c2ecf20Sopenharmony_ci thislen = (1<<cfi->chipshift) - ofs; 4038c2ecf20Sopenharmony_ci else 4048c2ecf20Sopenharmony_ci thislen = len; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci ret = do_read_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); 4078c2ecf20Sopenharmony_ci if (ret) 4088c2ecf20Sopenharmony_ci break; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci *retlen += thislen; 4118c2ecf20Sopenharmony_ci len -= thislen; 4128c2ecf20Sopenharmony_ci buf += thislen; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci ofs = 0; 4158c2ecf20Sopenharmony_ci chipnum++; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci return ret; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int do_write_buffer(struct map_info *map, struct flchip *chip, 4218c2ecf20Sopenharmony_ci unsigned long adr, const u_char *buf, int len) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 4248c2ecf20Sopenharmony_ci map_word status, status_OK; 4258c2ecf20Sopenharmony_ci unsigned long cmd_adr, timeo; 4268c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 4278c2ecf20Sopenharmony_ci int wbufsize, z; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* M58LW064A requires bus alignment for buffer wriets -- saw */ 4308c2ecf20Sopenharmony_ci if (adr & (map_bankwidth(map)-1)) 4318c2ecf20Sopenharmony_ci return -EINVAL; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; 4348c2ecf20Sopenharmony_ci adr += chip->start; 4358c2ecf20Sopenharmony_ci cmd_adr = adr & ~(wbufsize-1); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci /* Let's determine this according to the interleave only once */ 4388c2ecf20Sopenharmony_ci status_OK = CMD(0x80); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci timeo = jiffies + HZ; 4418c2ecf20Sopenharmony_ci retry: 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 4448c2ecf20Sopenharmony_ci printk("%s: chip->state[%d]\n", __func__, chip->state); 4458c2ecf20Sopenharmony_ci#endif 4468c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Check that the chip's ready to talk to us. 4498c2ecf20Sopenharmony_ci * Later, we can actually think about interrupting it 4508c2ecf20Sopenharmony_ci * if it's in FL_ERASING state. 4518c2ecf20Sopenharmony_ci * Not just yet, though. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ci switch (chip->state) { 4548c2ecf20Sopenharmony_ci case FL_READY: 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci case FL_CFI_QUERY: 4588c2ecf20Sopenharmony_ci case FL_JEDEC_QUERY: 4598c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), cmd_adr); 4608c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 4618c2ecf20Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 4628c2ecf20Sopenharmony_ci printk("%s: 1 status[%x]\n", __func__, map_read(map, cmd_adr)); 4638c2ecf20Sopenharmony_ci#endif 4648c2ecf20Sopenharmony_ci fallthrough; 4658c2ecf20Sopenharmony_ci case FL_STATUS: 4668c2ecf20Sopenharmony_ci status = map_read(map, cmd_adr); 4678c2ecf20Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 4688c2ecf20Sopenharmony_ci break; 4698c2ecf20Sopenharmony_ci /* Urgh. Chip not yet ready to talk to us. */ 4708c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 4718c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 4728c2ecf20Sopenharmony_ci printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %lx, status = %lx\n", 4738c2ecf20Sopenharmony_ci status.x[0], map_read(map, cmd_adr).x[0]); 4748c2ecf20Sopenharmony_ci return -EIO; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 4788c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 4798c2ecf20Sopenharmony_ci cfi_udelay(1); 4808c2ecf20Sopenharmony_ci goto retry; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci default: 4838c2ecf20Sopenharmony_ci /* Stick ourselves on a wait queue to be woken when 4848c2ecf20Sopenharmony_ci someone changes the status */ 4858c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 4868c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 4878c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 4888c2ecf20Sopenharmony_ci schedule(); 4898c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 4908c2ecf20Sopenharmony_ci timeo = jiffies + HZ; 4918c2ecf20Sopenharmony_ci goto retry; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci ENABLE_VPP(map); 4958c2ecf20Sopenharmony_ci map_write(map, CMD(0xe8), cmd_adr); 4968c2ecf20Sopenharmony_ci chip->state = FL_WRITING_TO_BUFFER; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci z = 0; 4998c2ecf20Sopenharmony_ci for (;;) { 5008c2ecf20Sopenharmony_ci status = map_read(map, cmd_adr); 5018c2ecf20Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 5028c2ecf20Sopenharmony_ci break; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 5058c2ecf20Sopenharmony_ci cfi_udelay(1); 5068c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (++z > 100) { 5098c2ecf20Sopenharmony_ci /* Argh. Not ready for write to buffer */ 5108c2ecf20Sopenharmony_ci DISABLE_VPP(map); 5118c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), cmd_adr); 5128c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 5138c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 5148c2ecf20Sopenharmony_ci printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx\n", status.x[0]); 5158c2ecf20Sopenharmony_ci return -EIO; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* Write length of data to come */ 5208c2ecf20Sopenharmony_ci map_write(map, CMD(len/map_bankwidth(map)-1), cmd_adr ); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* Write data */ 5238c2ecf20Sopenharmony_ci for (z = 0; z < len; 5248c2ecf20Sopenharmony_ci z += map_bankwidth(map), buf += map_bankwidth(map)) { 5258c2ecf20Sopenharmony_ci map_word d; 5268c2ecf20Sopenharmony_ci d = map_word_load(map, buf); 5278c2ecf20Sopenharmony_ci map_write(map, d, adr+z); 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci /* GO GO GO */ 5308c2ecf20Sopenharmony_ci map_write(map, CMD(0xd0), cmd_adr); 5318c2ecf20Sopenharmony_ci chip->state = FL_WRITING; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 5348c2ecf20Sopenharmony_ci cfi_udelay(chip->buffer_write_time); 5358c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci timeo = jiffies + (HZ/2); 5388c2ecf20Sopenharmony_ci z = 0; 5398c2ecf20Sopenharmony_ci for (;;) { 5408c2ecf20Sopenharmony_ci if (chip->state != FL_WRITING) { 5418c2ecf20Sopenharmony_ci /* Someone's suspended the write. Sleep */ 5428c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 5438c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 5448c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 5458c2ecf20Sopenharmony_ci schedule(); 5468c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 5478c2ecf20Sopenharmony_ci timeo = jiffies + (HZ / 2); /* FIXME */ 5488c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 5498c2ecf20Sopenharmony_ci continue; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci status = map_read(map, cmd_adr); 5538c2ecf20Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 5548c2ecf20Sopenharmony_ci break; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* OK Still waiting */ 5578c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 5588c2ecf20Sopenharmony_ci /* clear status */ 5598c2ecf20Sopenharmony_ci map_write(map, CMD(0x50), cmd_adr); 5608c2ecf20Sopenharmony_ci /* put back into read status register mode */ 5618c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 5628c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 5638c2ecf20Sopenharmony_ci DISABLE_VPP(map); 5648c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 5658c2ecf20Sopenharmony_ci printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); 5668c2ecf20Sopenharmony_ci return -EIO; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 5708c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 5718c2ecf20Sopenharmony_ci cfi_udelay(1); 5728c2ecf20Sopenharmony_ci z++; 5738c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci if (!z) { 5768c2ecf20Sopenharmony_ci chip->buffer_write_time--; 5778c2ecf20Sopenharmony_ci if (!chip->buffer_write_time) 5788c2ecf20Sopenharmony_ci chip->buffer_write_time++; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci if (z > 1) 5818c2ecf20Sopenharmony_ci chip->buffer_write_time++; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* Done and happy. */ 5848c2ecf20Sopenharmony_ci DISABLE_VPP(map); 5858c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* check for errors: 'lock bit', 'VPP', 'dead cell'/'unerased cell' or 'incorrect cmd' -- saw */ 5888c2ecf20Sopenharmony_ci if (map_word_bitsset(map, status, CMD(0x3a))) { 5898c2ecf20Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 5908c2ecf20Sopenharmony_ci printk("%s: 2 status[%lx]\n", __func__, status.x[0]); 5918c2ecf20Sopenharmony_ci#endif 5928c2ecf20Sopenharmony_ci /* clear status */ 5938c2ecf20Sopenharmony_ci map_write(map, CMD(0x50), cmd_adr); 5948c2ecf20Sopenharmony_ci /* put back into read status register mode */ 5958c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 5968c2ecf20Sopenharmony_ci wake_up(&chip->wq); 5978c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 5988c2ecf20Sopenharmony_ci return map_word_bitsset(map, status, CMD(0x02)) ? -EROFS : -EIO; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci wake_up(&chip->wq); 6018c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci return 0; 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to, 6078c2ecf20Sopenharmony_ci size_t len, size_t *retlen, const u_char *buf) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 6108c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 6118c2ecf20Sopenharmony_ci int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; 6128c2ecf20Sopenharmony_ci int ret; 6138c2ecf20Sopenharmony_ci int chipnum; 6148c2ecf20Sopenharmony_ci unsigned long ofs; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci chipnum = to >> cfi->chipshift; 6178c2ecf20Sopenharmony_ci ofs = to - (chipnum << cfi->chipshift); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 6208c2ecf20Sopenharmony_ci printk("%s: map_bankwidth(map)[%x]\n", __func__, map_bankwidth(map)); 6218c2ecf20Sopenharmony_ci printk("%s: chipnum[%x] wbufsize[%x]\n", __func__, chipnum, wbufsize); 6228c2ecf20Sopenharmony_ci printk("%s: ofs[%x] len[%x]\n", __func__, ofs, len); 6238c2ecf20Sopenharmony_ci#endif 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* Write buffer is worth it only if more than one word to write... */ 6268c2ecf20Sopenharmony_ci while (len > 0) { 6278c2ecf20Sopenharmony_ci /* We must not cross write block boundaries */ 6288c2ecf20Sopenharmony_ci int size = wbufsize - (ofs & (wbufsize-1)); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (size > len) 6318c2ecf20Sopenharmony_ci size = len; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci ret = do_write_buffer(map, &cfi->chips[chipnum], 6348c2ecf20Sopenharmony_ci ofs, buf, size); 6358c2ecf20Sopenharmony_ci if (ret) 6368c2ecf20Sopenharmony_ci return ret; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci ofs += size; 6398c2ecf20Sopenharmony_ci buf += size; 6408c2ecf20Sopenharmony_ci (*retlen) += size; 6418c2ecf20Sopenharmony_ci len -= size; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci if (ofs >> cfi->chipshift) { 6448c2ecf20Sopenharmony_ci chipnum ++; 6458c2ecf20Sopenharmony_ci ofs = 0; 6468c2ecf20Sopenharmony_ci if (chipnum == cfi->numchips) 6478c2ecf20Sopenharmony_ci return 0; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci return 0; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci/* 6558c2ecf20Sopenharmony_ci * Writev for ECC-Flashes is a little more complicated. We need to maintain 6568c2ecf20Sopenharmony_ci * a small buffer for this. 6578c2ecf20Sopenharmony_ci * XXX: If the buffer size is not a multiple of 2, this will break 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_ci#define ECCBUF_SIZE (mtd->writesize) 6608c2ecf20Sopenharmony_ci#define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1)) 6618c2ecf20Sopenharmony_ci#define ECCBUF_MOD(x) ((x) & (ECCBUF_SIZE - 1)) 6628c2ecf20Sopenharmony_cistatic int 6638c2ecf20Sopenharmony_cicfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs, 6648c2ecf20Sopenharmony_ci unsigned long count, loff_t to, size_t *retlen) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci unsigned long i; 6678c2ecf20Sopenharmony_ci size_t totlen = 0, thislen; 6688c2ecf20Sopenharmony_ci int ret = 0; 6698c2ecf20Sopenharmony_ci size_t buflen = 0; 6708c2ecf20Sopenharmony_ci char *buffer; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci if (!ECCBUF_SIZE) { 6738c2ecf20Sopenharmony_ci /* We should fall back to a general writev implementation. 6748c2ecf20Sopenharmony_ci * Until that is written, just break. 6758c2ecf20Sopenharmony_ci */ 6768c2ecf20Sopenharmony_ci return -EIO; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci buffer = kmalloc(ECCBUF_SIZE, GFP_KERNEL); 6798c2ecf20Sopenharmony_ci if (!buffer) 6808c2ecf20Sopenharmony_ci return -ENOMEM; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci for (i=0; i<count; i++) { 6838c2ecf20Sopenharmony_ci size_t elem_len = vecs[i].iov_len; 6848c2ecf20Sopenharmony_ci void *elem_base = vecs[i].iov_base; 6858c2ecf20Sopenharmony_ci if (!elem_len) /* FIXME: Might be unnecessary. Check that */ 6868c2ecf20Sopenharmony_ci continue; 6878c2ecf20Sopenharmony_ci if (buflen) { /* cut off head */ 6888c2ecf20Sopenharmony_ci if (buflen + elem_len < ECCBUF_SIZE) { /* just accumulate */ 6898c2ecf20Sopenharmony_ci memcpy(buffer+buflen, elem_base, elem_len); 6908c2ecf20Sopenharmony_ci buflen += elem_len; 6918c2ecf20Sopenharmony_ci continue; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci memcpy(buffer+buflen, elem_base, ECCBUF_SIZE-buflen); 6948c2ecf20Sopenharmony_ci ret = mtd_write(mtd, to, ECCBUF_SIZE, &thislen, 6958c2ecf20Sopenharmony_ci buffer); 6968c2ecf20Sopenharmony_ci totlen += thislen; 6978c2ecf20Sopenharmony_ci if (ret || thislen != ECCBUF_SIZE) 6988c2ecf20Sopenharmony_ci goto write_error; 6998c2ecf20Sopenharmony_ci elem_len -= thislen-buflen; 7008c2ecf20Sopenharmony_ci elem_base += thislen-buflen; 7018c2ecf20Sopenharmony_ci to += ECCBUF_SIZE; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci if (ECCBUF_DIV(elem_len)) { /* write clean aligned data */ 7048c2ecf20Sopenharmony_ci ret = mtd_write(mtd, to, ECCBUF_DIV(elem_len), 7058c2ecf20Sopenharmony_ci &thislen, elem_base); 7068c2ecf20Sopenharmony_ci totlen += thislen; 7078c2ecf20Sopenharmony_ci if (ret || thislen != ECCBUF_DIV(elem_len)) 7088c2ecf20Sopenharmony_ci goto write_error; 7098c2ecf20Sopenharmony_ci to += thislen; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci buflen = ECCBUF_MOD(elem_len); /* cut off tail */ 7128c2ecf20Sopenharmony_ci if (buflen) { 7138c2ecf20Sopenharmony_ci memset(buffer, 0xff, ECCBUF_SIZE); 7148c2ecf20Sopenharmony_ci memcpy(buffer, elem_base + thislen, buflen); 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci if (buflen) { /* flush last page, even if not full */ 7188c2ecf20Sopenharmony_ci /* This is sometimes intended behaviour, really */ 7198c2ecf20Sopenharmony_ci ret = mtd_write(mtd, to, buflen, &thislen, buffer); 7208c2ecf20Sopenharmony_ci totlen += thislen; 7218c2ecf20Sopenharmony_ci if (ret || thislen != ECCBUF_SIZE) 7228c2ecf20Sopenharmony_ci goto write_error; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ciwrite_error: 7258c2ecf20Sopenharmony_ci if (retlen) 7268c2ecf20Sopenharmony_ci *retlen = totlen; 7278c2ecf20Sopenharmony_ci kfree(buffer); 7288c2ecf20Sopenharmony_ci return ret; 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_cistatic inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 7358c2ecf20Sopenharmony_ci map_word status, status_OK; 7368c2ecf20Sopenharmony_ci unsigned long timeo; 7378c2ecf20Sopenharmony_ci int retries = 3; 7388c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 7398c2ecf20Sopenharmony_ci int ret = 0; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci adr += chip->start; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci /* Let's determine this according to the interleave only once */ 7448c2ecf20Sopenharmony_ci status_OK = CMD(0x80); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci timeo = jiffies + HZ; 7478c2ecf20Sopenharmony_ciretry: 7488c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci /* Check that the chip's ready to talk to us. */ 7518c2ecf20Sopenharmony_ci switch (chip->state) { 7528c2ecf20Sopenharmony_ci case FL_CFI_QUERY: 7538c2ecf20Sopenharmony_ci case FL_JEDEC_QUERY: 7548c2ecf20Sopenharmony_ci case FL_READY: 7558c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 7568c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 7578c2ecf20Sopenharmony_ci fallthrough; 7588c2ecf20Sopenharmony_ci case FL_STATUS: 7598c2ecf20Sopenharmony_ci status = map_read(map, adr); 7608c2ecf20Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 7618c2ecf20Sopenharmony_ci break; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci /* Urgh. Chip not yet ready to talk to us. */ 7648c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 7658c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 7668c2ecf20Sopenharmony_ci printk(KERN_ERR "waiting for chip to be ready timed out in erase\n"); 7678c2ecf20Sopenharmony_ci return -EIO; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 7718c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 7728c2ecf20Sopenharmony_ci cfi_udelay(1); 7738c2ecf20Sopenharmony_ci goto retry; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci default: 7768c2ecf20Sopenharmony_ci /* Stick ourselves on a wait queue to be woken when 7778c2ecf20Sopenharmony_ci someone changes the status */ 7788c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 7798c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 7808c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 7818c2ecf20Sopenharmony_ci schedule(); 7828c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 7838c2ecf20Sopenharmony_ci timeo = jiffies + HZ; 7848c2ecf20Sopenharmony_ci goto retry; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci ENABLE_VPP(map); 7888c2ecf20Sopenharmony_ci /* Clear the status register first */ 7898c2ecf20Sopenharmony_ci map_write(map, CMD(0x50), adr); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci /* Now erase */ 7928c2ecf20Sopenharmony_ci map_write(map, CMD(0x20), adr); 7938c2ecf20Sopenharmony_ci map_write(map, CMD(0xD0), adr); 7948c2ecf20Sopenharmony_ci chip->state = FL_ERASING; 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 7978c2ecf20Sopenharmony_ci msleep(1000); 7988c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* FIXME. Use a timer to check this, and return immediately. */ 8018c2ecf20Sopenharmony_ci /* Once the state machine's known to be working I'll do that */ 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci timeo = jiffies + (HZ*20); 8048c2ecf20Sopenharmony_ci for (;;) { 8058c2ecf20Sopenharmony_ci if (chip->state != FL_ERASING) { 8068c2ecf20Sopenharmony_ci /* Someone's suspended the erase. Sleep */ 8078c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 8088c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 8098c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 8108c2ecf20Sopenharmony_ci schedule(); 8118c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 8128c2ecf20Sopenharmony_ci timeo = jiffies + (HZ*20); /* FIXME */ 8138c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 8148c2ecf20Sopenharmony_ci continue; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci status = map_read(map, adr); 8188c2ecf20Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 8198c2ecf20Sopenharmony_ci break; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci /* OK Still waiting */ 8228c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 8238c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 8248c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 8258c2ecf20Sopenharmony_ci printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); 8268c2ecf20Sopenharmony_ci DISABLE_VPP(map); 8278c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 8288c2ecf20Sopenharmony_ci return -EIO; 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 8328c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 8338c2ecf20Sopenharmony_ci cfi_udelay(1); 8348c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci DISABLE_VPP(map); 8388c2ecf20Sopenharmony_ci ret = 0; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci /* We've broken this before. It doesn't hurt to be safe */ 8418c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 8428c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 8438c2ecf20Sopenharmony_ci status = map_read(map, adr); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci /* check for lock bit */ 8468c2ecf20Sopenharmony_ci if (map_word_bitsset(map, status, CMD(0x3a))) { 8478c2ecf20Sopenharmony_ci unsigned char chipstatus = status.x[0]; 8488c2ecf20Sopenharmony_ci if (!map_word_equal(map, status, CMD(chipstatus))) { 8498c2ecf20Sopenharmony_ci int i, w; 8508c2ecf20Sopenharmony_ci for (w=0; w<map_words(map); w++) { 8518c2ecf20Sopenharmony_ci for (i = 0; i<cfi_interleave(cfi); i++) { 8528c2ecf20Sopenharmony_ci chipstatus |= status.x[w] >> (cfi->device_type * 8); 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n", 8568c2ecf20Sopenharmony_ci status.x[0], chipstatus); 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci /* Reset the error bits */ 8598c2ecf20Sopenharmony_ci map_write(map, CMD(0x50), adr); 8608c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci if ((chipstatus & 0x30) == 0x30) { 8638c2ecf20Sopenharmony_ci printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus); 8648c2ecf20Sopenharmony_ci ret = -EIO; 8658c2ecf20Sopenharmony_ci } else if (chipstatus & 0x02) { 8668c2ecf20Sopenharmony_ci /* Protection bit set */ 8678c2ecf20Sopenharmony_ci ret = -EROFS; 8688c2ecf20Sopenharmony_ci } else if (chipstatus & 0x8) { 8698c2ecf20Sopenharmony_ci /* Voltage */ 8708c2ecf20Sopenharmony_ci printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus); 8718c2ecf20Sopenharmony_ci ret = -EIO; 8728c2ecf20Sopenharmony_ci } else if (chipstatus & 0x20) { 8738c2ecf20Sopenharmony_ci if (retries--) { 8748c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus); 8758c2ecf20Sopenharmony_ci timeo = jiffies + HZ; 8768c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 8778c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 8788c2ecf20Sopenharmony_ci goto retry; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus); 8818c2ecf20Sopenharmony_ci ret = -EIO; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci } 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci wake_up(&chip->wq); 8868c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 8878c2ecf20Sopenharmony_ci return ret; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic int cfi_staa_erase_varsize(struct mtd_info *mtd, 8918c2ecf20Sopenharmony_ci struct erase_info *instr) 8928c2ecf20Sopenharmony_ci{ struct map_info *map = mtd->priv; 8938c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 8948c2ecf20Sopenharmony_ci unsigned long adr, len; 8958c2ecf20Sopenharmony_ci int chipnum, ret; 8968c2ecf20Sopenharmony_ci int i, first; 8978c2ecf20Sopenharmony_ci struct mtd_erase_region_info *regions = mtd->eraseregions; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci /* Check that both start and end of the requested erase are 9008c2ecf20Sopenharmony_ci * aligned with the erasesize at the appropriate addresses. 9018c2ecf20Sopenharmony_ci */ 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci i = 0; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci /* Skip all erase regions which are ended before the start of 9068c2ecf20Sopenharmony_ci the requested erase. Actually, to save on the calculations, 9078c2ecf20Sopenharmony_ci we skip to the first erase region which starts after the 9088c2ecf20Sopenharmony_ci start of the requested erase, and then go back one. 9098c2ecf20Sopenharmony_ci */ 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci while (i < mtd->numeraseregions && instr->addr >= regions[i].offset) 9128c2ecf20Sopenharmony_ci i++; 9138c2ecf20Sopenharmony_ci i--; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci /* OK, now i is pointing at the erase region in which this 9168c2ecf20Sopenharmony_ci erase request starts. Check the start of the requested 9178c2ecf20Sopenharmony_ci erase range is aligned with the erase size which is in 9188c2ecf20Sopenharmony_ci effect here. 9198c2ecf20Sopenharmony_ci */ 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (instr->addr & (regions[i].erasesize-1)) 9228c2ecf20Sopenharmony_ci return -EINVAL; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci /* Remember the erase region we start on */ 9258c2ecf20Sopenharmony_ci first = i; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci /* Next, check that the end of the requested erase is aligned 9288c2ecf20Sopenharmony_ci * with the erase region at that address. 9298c2ecf20Sopenharmony_ci */ 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci while (i<mtd->numeraseregions && (instr->addr + instr->len) >= regions[i].offset) 9328c2ecf20Sopenharmony_ci i++; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci /* As before, drop back one to point at the region in which 9358c2ecf20Sopenharmony_ci the address actually falls 9368c2ecf20Sopenharmony_ci */ 9378c2ecf20Sopenharmony_ci i--; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if ((instr->addr + instr->len) & (regions[i].erasesize-1)) 9408c2ecf20Sopenharmony_ci return -EINVAL; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci chipnum = instr->addr >> cfi->chipshift; 9438c2ecf20Sopenharmony_ci adr = instr->addr - (chipnum << cfi->chipshift); 9448c2ecf20Sopenharmony_ci len = instr->len; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci i=first; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci while(len) { 9498c2ecf20Sopenharmony_ci ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci if (ret) 9528c2ecf20Sopenharmony_ci return ret; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci adr += regions[i].erasesize; 9558c2ecf20Sopenharmony_ci len -= regions[i].erasesize; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (adr % (1<< cfi->chipshift) == (((unsigned long)regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) 9588c2ecf20Sopenharmony_ci i++; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci if (adr >> cfi->chipshift) { 9618c2ecf20Sopenharmony_ci adr = 0; 9628c2ecf20Sopenharmony_ci chipnum++; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci if (chipnum >= cfi->numchips) 9658c2ecf20Sopenharmony_ci break; 9668c2ecf20Sopenharmony_ci } 9678c2ecf20Sopenharmony_ci } 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci return 0; 9708c2ecf20Sopenharmony_ci} 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_cistatic void cfi_staa_sync (struct mtd_info *mtd) 9738c2ecf20Sopenharmony_ci{ 9748c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 9758c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 9768c2ecf20Sopenharmony_ci int i; 9778c2ecf20Sopenharmony_ci struct flchip *chip; 9788c2ecf20Sopenharmony_ci int ret = 0; 9798c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci for (i=0; !ret && i<cfi->numchips; i++) { 9828c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci retry: 9858c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci switch(chip->state) { 9888c2ecf20Sopenharmony_ci case FL_READY: 9898c2ecf20Sopenharmony_ci case FL_STATUS: 9908c2ecf20Sopenharmony_ci case FL_CFI_QUERY: 9918c2ecf20Sopenharmony_ci case FL_JEDEC_QUERY: 9928c2ecf20Sopenharmony_ci chip->oldstate = chip->state; 9938c2ecf20Sopenharmony_ci chip->state = FL_SYNCING; 9948c2ecf20Sopenharmony_ci /* No need to wake_up() on this state change - 9958c2ecf20Sopenharmony_ci * as the whole point is that nobody can do anything 9968c2ecf20Sopenharmony_ci * with the chip now anyway. 9978c2ecf20Sopenharmony_ci */ 9988c2ecf20Sopenharmony_ci fallthrough; 9998c2ecf20Sopenharmony_ci case FL_SYNCING: 10008c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 10018c2ecf20Sopenharmony_ci break; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci default: 10048c2ecf20Sopenharmony_ci /* Not an idle state */ 10058c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 10068c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 10098c2ecf20Sopenharmony_ci schedule(); 10108c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci goto retry; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci /* Unlock the chips again */ 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci for (i--; i >=0; i--) { 10198c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci if (chip->state == FL_SYNCING) { 10248c2ecf20Sopenharmony_ci chip->state = chip->oldstate; 10258c2ecf20Sopenharmony_ci wake_up(&chip->wq); 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci} 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_cistatic inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) 10328c2ecf20Sopenharmony_ci{ 10338c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 10348c2ecf20Sopenharmony_ci map_word status, status_OK; 10358c2ecf20Sopenharmony_ci unsigned long timeo = jiffies + HZ; 10368c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci adr += chip->start; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci /* Let's determine this according to the interleave only once */ 10418c2ecf20Sopenharmony_ci status_OK = CMD(0x80); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci timeo = jiffies + HZ; 10448c2ecf20Sopenharmony_ciretry: 10458c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci /* Check that the chip's ready to talk to us. */ 10488c2ecf20Sopenharmony_ci switch (chip->state) { 10498c2ecf20Sopenharmony_ci case FL_CFI_QUERY: 10508c2ecf20Sopenharmony_ci case FL_JEDEC_QUERY: 10518c2ecf20Sopenharmony_ci case FL_READY: 10528c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 10538c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 10548c2ecf20Sopenharmony_ci fallthrough; 10558c2ecf20Sopenharmony_ci case FL_STATUS: 10568c2ecf20Sopenharmony_ci status = map_read(map, adr); 10578c2ecf20Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 10588c2ecf20Sopenharmony_ci break; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci /* Urgh. Chip not yet ready to talk to us. */ 10618c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 10628c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 10638c2ecf20Sopenharmony_ci printk(KERN_ERR "waiting for chip to be ready timed out in lock\n"); 10648c2ecf20Sopenharmony_ci return -EIO; 10658c2ecf20Sopenharmony_ci } 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 10688c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 10698c2ecf20Sopenharmony_ci cfi_udelay(1); 10708c2ecf20Sopenharmony_ci goto retry; 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci default: 10738c2ecf20Sopenharmony_ci /* Stick ourselves on a wait queue to be woken when 10748c2ecf20Sopenharmony_ci someone changes the status */ 10758c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 10768c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 10778c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 10788c2ecf20Sopenharmony_ci schedule(); 10798c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 10808c2ecf20Sopenharmony_ci timeo = jiffies + HZ; 10818c2ecf20Sopenharmony_ci goto retry; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci ENABLE_VPP(map); 10858c2ecf20Sopenharmony_ci map_write(map, CMD(0x60), adr); 10868c2ecf20Sopenharmony_ci map_write(map, CMD(0x01), adr); 10878c2ecf20Sopenharmony_ci chip->state = FL_LOCKING; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 10908c2ecf20Sopenharmony_ci msleep(1000); 10918c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci /* FIXME. Use a timer to check this, and return immediately. */ 10948c2ecf20Sopenharmony_ci /* Once the state machine's known to be working I'll do that */ 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci timeo = jiffies + (HZ*2); 10978c2ecf20Sopenharmony_ci for (;;) { 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci status = map_read(map, adr); 11008c2ecf20Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 11018c2ecf20Sopenharmony_ci break; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci /* OK Still waiting */ 11048c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 11058c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 11068c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 11078c2ecf20Sopenharmony_ci printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); 11088c2ecf20Sopenharmony_ci DISABLE_VPP(map); 11098c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 11108c2ecf20Sopenharmony_ci return -EIO; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 11148c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 11158c2ecf20Sopenharmony_ci cfi_udelay(1); 11168c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci /* Done and happy. */ 11208c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 11218c2ecf20Sopenharmony_ci DISABLE_VPP(map); 11228c2ecf20Sopenharmony_ci wake_up(&chip->wq); 11238c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 11248c2ecf20Sopenharmony_ci return 0; 11258c2ecf20Sopenharmony_ci} 11268c2ecf20Sopenharmony_cistatic int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 11278c2ecf20Sopenharmony_ci{ 11288c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 11298c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 11308c2ecf20Sopenharmony_ci unsigned long adr; 11318c2ecf20Sopenharmony_ci int chipnum, ret; 11328c2ecf20Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 11338c2ecf20Sopenharmony_ci int ofs_factor = cfi->interleave * cfi->device_type; 11348c2ecf20Sopenharmony_ci#endif 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci if (ofs & (mtd->erasesize - 1)) 11378c2ecf20Sopenharmony_ci return -EINVAL; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (len & (mtd->erasesize -1)) 11408c2ecf20Sopenharmony_ci return -EINVAL; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci chipnum = ofs >> cfi->chipshift; 11438c2ecf20Sopenharmony_ci adr = ofs - (chipnum << cfi->chipshift); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci while(len) { 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 11488c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); 11498c2ecf20Sopenharmony_ci printk("before lock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); 11508c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); 11518c2ecf20Sopenharmony_ci#endif 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci ret = do_lock_oneblock(map, &cfi->chips[chipnum], adr); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 11568c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); 11578c2ecf20Sopenharmony_ci printk("after lock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); 11588c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); 11598c2ecf20Sopenharmony_ci#endif 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci if (ret) 11628c2ecf20Sopenharmony_ci return ret; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci adr += mtd->erasesize; 11658c2ecf20Sopenharmony_ci len -= mtd->erasesize; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci if (adr >> cfi->chipshift) { 11688c2ecf20Sopenharmony_ci adr = 0; 11698c2ecf20Sopenharmony_ci chipnum++; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (chipnum >= cfi->numchips) 11728c2ecf20Sopenharmony_ci break; 11738c2ecf20Sopenharmony_ci } 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci return 0; 11768c2ecf20Sopenharmony_ci} 11778c2ecf20Sopenharmony_cistatic inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) 11788c2ecf20Sopenharmony_ci{ 11798c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 11808c2ecf20Sopenharmony_ci map_word status, status_OK; 11818c2ecf20Sopenharmony_ci unsigned long timeo = jiffies + HZ; 11828c2ecf20Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci adr += chip->start; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* Let's determine this according to the interleave only once */ 11878c2ecf20Sopenharmony_ci status_OK = CMD(0x80); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci timeo = jiffies + HZ; 11908c2ecf20Sopenharmony_ciretry: 11918c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* Check that the chip's ready to talk to us. */ 11948c2ecf20Sopenharmony_ci switch (chip->state) { 11958c2ecf20Sopenharmony_ci case FL_CFI_QUERY: 11968c2ecf20Sopenharmony_ci case FL_JEDEC_QUERY: 11978c2ecf20Sopenharmony_ci case FL_READY: 11988c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 11998c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 12008c2ecf20Sopenharmony_ci fallthrough; 12018c2ecf20Sopenharmony_ci case FL_STATUS: 12028c2ecf20Sopenharmony_ci status = map_read(map, adr); 12038c2ecf20Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 12048c2ecf20Sopenharmony_ci break; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci /* Urgh. Chip not yet ready to talk to us. */ 12078c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 12088c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 12098c2ecf20Sopenharmony_ci printk(KERN_ERR "waiting for chip to be ready timed out in unlock\n"); 12108c2ecf20Sopenharmony_ci return -EIO; 12118c2ecf20Sopenharmony_ci } 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 12148c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 12158c2ecf20Sopenharmony_ci cfi_udelay(1); 12168c2ecf20Sopenharmony_ci goto retry; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci default: 12198c2ecf20Sopenharmony_ci /* Stick ourselves on a wait queue to be woken when 12208c2ecf20Sopenharmony_ci someone changes the status */ 12218c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 12228c2ecf20Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 12238c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 12248c2ecf20Sopenharmony_ci schedule(); 12258c2ecf20Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 12268c2ecf20Sopenharmony_ci timeo = jiffies + HZ; 12278c2ecf20Sopenharmony_ci goto retry; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci ENABLE_VPP(map); 12318c2ecf20Sopenharmony_ci map_write(map, CMD(0x60), adr); 12328c2ecf20Sopenharmony_ci map_write(map, CMD(0xD0), adr); 12338c2ecf20Sopenharmony_ci chip->state = FL_UNLOCKING; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 12368c2ecf20Sopenharmony_ci msleep(1000); 12378c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci /* FIXME. Use a timer to check this, and return immediately. */ 12408c2ecf20Sopenharmony_ci /* Once the state machine's known to be working I'll do that */ 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci timeo = jiffies + (HZ*2); 12438c2ecf20Sopenharmony_ci for (;;) { 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci status = map_read(map, adr); 12468c2ecf20Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 12478c2ecf20Sopenharmony_ci break; 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci /* OK Still waiting */ 12508c2ecf20Sopenharmony_ci if (time_after(jiffies, timeo)) { 12518c2ecf20Sopenharmony_ci map_write(map, CMD(0x70), adr); 12528c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 12538c2ecf20Sopenharmony_ci printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); 12548c2ecf20Sopenharmony_ci DISABLE_VPP(map); 12558c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 12568c2ecf20Sopenharmony_ci return -EIO; 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci /* Latency issues. Drop the unlock, wait a while and retry */ 12608c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 12618c2ecf20Sopenharmony_ci cfi_udelay(1); 12628c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 12638c2ecf20Sopenharmony_ci } 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci /* Done and happy. */ 12668c2ecf20Sopenharmony_ci chip->state = FL_STATUS; 12678c2ecf20Sopenharmony_ci DISABLE_VPP(map); 12688c2ecf20Sopenharmony_ci wake_up(&chip->wq); 12698c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 12708c2ecf20Sopenharmony_ci return 0; 12718c2ecf20Sopenharmony_ci} 12728c2ecf20Sopenharmony_cistatic int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 12738c2ecf20Sopenharmony_ci{ 12748c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 12758c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 12768c2ecf20Sopenharmony_ci unsigned long adr; 12778c2ecf20Sopenharmony_ci int chipnum, ret; 12788c2ecf20Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 12798c2ecf20Sopenharmony_ci int ofs_factor = cfi->interleave * cfi->device_type; 12808c2ecf20Sopenharmony_ci#endif 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci chipnum = ofs >> cfi->chipshift; 12838c2ecf20Sopenharmony_ci adr = ofs - (chipnum << cfi->chipshift); 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 12868c2ecf20Sopenharmony_ci { 12878c2ecf20Sopenharmony_ci unsigned long temp_adr = adr; 12888c2ecf20Sopenharmony_ci unsigned long temp_len = len; 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); 12918c2ecf20Sopenharmony_ci while (temp_len) { 12928c2ecf20Sopenharmony_ci printk("before unlock %x: block status register is %x\n",temp_adr,cfi_read_query(map, temp_adr+(2*ofs_factor))); 12938c2ecf20Sopenharmony_ci temp_adr += mtd->erasesize; 12948c2ecf20Sopenharmony_ci temp_len -= mtd->erasesize; 12958c2ecf20Sopenharmony_ci } 12968c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci#endif 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci ret = do_unlock_oneblock(map, &cfi->chips[chipnum], adr); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 13038c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); 13048c2ecf20Sopenharmony_ci printk("after unlock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); 13058c2ecf20Sopenharmony_ci cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); 13068c2ecf20Sopenharmony_ci#endif 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci return ret; 13098c2ecf20Sopenharmony_ci} 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_cistatic int cfi_staa_suspend(struct mtd_info *mtd) 13128c2ecf20Sopenharmony_ci{ 13138c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 13148c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 13158c2ecf20Sopenharmony_ci int i; 13168c2ecf20Sopenharmony_ci struct flchip *chip; 13178c2ecf20Sopenharmony_ci int ret = 0; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci for (i=0; !ret && i<cfi->numchips; i++) { 13208c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci switch(chip->state) { 13258c2ecf20Sopenharmony_ci case FL_READY: 13268c2ecf20Sopenharmony_ci case FL_STATUS: 13278c2ecf20Sopenharmony_ci case FL_CFI_QUERY: 13288c2ecf20Sopenharmony_ci case FL_JEDEC_QUERY: 13298c2ecf20Sopenharmony_ci chip->oldstate = chip->state; 13308c2ecf20Sopenharmony_ci chip->state = FL_PM_SUSPENDED; 13318c2ecf20Sopenharmony_ci /* No need to wake_up() on this state change - 13328c2ecf20Sopenharmony_ci * as the whole point is that nobody can do anything 13338c2ecf20Sopenharmony_ci * with the chip now anyway. 13348c2ecf20Sopenharmony_ci */ 13358c2ecf20Sopenharmony_ci case FL_PM_SUSPENDED: 13368c2ecf20Sopenharmony_ci break; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci default: 13398c2ecf20Sopenharmony_ci ret = -EAGAIN; 13408c2ecf20Sopenharmony_ci break; 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci /* Unlock the chips again */ 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci if (ret) { 13488c2ecf20Sopenharmony_ci for (i--; i >=0; i--) { 13498c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci if (chip->state == FL_PM_SUSPENDED) { 13548c2ecf20Sopenharmony_ci /* No need to force it into a known state here, 13558c2ecf20Sopenharmony_ci because we're returning failure, and it didn't 13568c2ecf20Sopenharmony_ci get power cycled */ 13578c2ecf20Sopenharmony_ci chip->state = chip->oldstate; 13588c2ecf20Sopenharmony_ci wake_up(&chip->wq); 13598c2ecf20Sopenharmony_ci } 13608c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci return ret; 13658c2ecf20Sopenharmony_ci} 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_cistatic void cfi_staa_resume(struct mtd_info *mtd) 13688c2ecf20Sopenharmony_ci{ 13698c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 13708c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 13718c2ecf20Sopenharmony_ci int i; 13728c2ecf20Sopenharmony_ci struct flchip *chip; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci for (i=0; i<cfi->numchips; i++) { 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci chip = &cfi->chips[i]; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci /* Go to known state. Chip may have been power cycled */ 13818c2ecf20Sopenharmony_ci if (chip->state == FL_PM_SUSPENDED) { 13828c2ecf20Sopenharmony_ci map_write(map, CMD(0xFF), 0); 13838c2ecf20Sopenharmony_ci chip->state = FL_READY; 13848c2ecf20Sopenharmony_ci wake_up(&chip->wq); 13858c2ecf20Sopenharmony_ci } 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci} 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_cistatic void cfi_staa_destroy(struct mtd_info *mtd) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci struct map_info *map = mtd->priv; 13948c2ecf20Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 13958c2ecf20Sopenharmony_ci kfree(cfi->cmdset_priv); 13968c2ecf20Sopenharmony_ci kfree(cfi); 13978c2ecf20Sopenharmony_ci} 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1400