162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Common Flash Interface support: 462306a36Sopenharmony_ci * ST Advanced Architecture Command Set (ID 0x0020) 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * (C) 2000 Red Hat. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * 10/10/2000 Nicolas Pitre <nico@fluxnic.net> 962306a36Sopenharmony_ci * - completely revamped method functions so they are aware and 1062306a36Sopenharmony_ci * independent of the flash geometry (buswidth, interleave, etc.) 1162306a36Sopenharmony_ci * - scalability vs code size is completely set at compile-time 1262306a36Sopenharmony_ci * (see include/linux/mtd/cfi.h for selection) 1362306a36Sopenharmony_ci * - optimized write buffer method 1462306a36Sopenharmony_ci * 06/21/2002 Joern Engel <joern@wh.fh-wedel.de> and others 1562306a36Sopenharmony_ci * - modified Intel Command Set 0x0001 to support ST Advanced Architecture 1662306a36Sopenharmony_ci * (command set 0x0020) 1762306a36Sopenharmony_ci * - added a writev function 1862306a36Sopenharmony_ci * 07/13/2005 Joern Engel <joern@wh.fh-wedel.de> 1962306a36Sopenharmony_ci * - Plugged memory leak in cfi_staa_writev(). 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/types.h> 2462306a36Sopenharmony_ci#include <linux/kernel.h> 2562306a36Sopenharmony_ci#include <linux/sched.h> 2662306a36Sopenharmony_ci#include <asm/io.h> 2762306a36Sopenharmony_ci#include <asm/byteorder.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <linux/errno.h> 3062306a36Sopenharmony_ci#include <linux/slab.h> 3162306a36Sopenharmony_ci#include <linux/delay.h> 3262306a36Sopenharmony_ci#include <linux/interrupt.h> 3362306a36Sopenharmony_ci#include <linux/mtd/map.h> 3462306a36Sopenharmony_ci#include <linux/mtd/cfi.h> 3562306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); 3962306a36Sopenharmony_cistatic int cfi_staa_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); 4062306a36Sopenharmony_cistatic int cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs, 4162306a36Sopenharmony_ci unsigned long count, loff_t to, size_t *retlen); 4262306a36Sopenharmony_cistatic int cfi_staa_erase_varsize(struct mtd_info *, struct erase_info *); 4362306a36Sopenharmony_cistatic void cfi_staa_sync (struct mtd_info *); 4462306a36Sopenharmony_cistatic int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 4562306a36Sopenharmony_cistatic int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); 4662306a36Sopenharmony_cistatic int cfi_staa_suspend (struct mtd_info *); 4762306a36Sopenharmony_cistatic void cfi_staa_resume (struct mtd_info *); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void cfi_staa_destroy(struct mtd_info *); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistruct mtd_info *cfi_cmdset_0020(struct map_info *, int); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic struct mtd_info *cfi_staa_setup (struct map_info *); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic struct mtd_chip_driver cfi_staa_chipdrv = { 5662306a36Sopenharmony_ci .probe = NULL, /* Not usable directly */ 5762306a36Sopenharmony_ci .destroy = cfi_staa_destroy, 5862306a36Sopenharmony_ci .name = "cfi_cmdset_0020", 5962306a36Sopenharmony_ci .module = THIS_MODULE 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* #define DEBUG_LOCK_BITS */ 6362306a36Sopenharmony_ci//#define DEBUG_CFI_FEATURES 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 6662306a36Sopenharmony_cistatic void cfi_tell_features(struct cfi_pri_intelext *extp) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci int i; 6962306a36Sopenharmony_ci printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport); 7062306a36Sopenharmony_ci printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported"); 7162306a36Sopenharmony_ci printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported"); 7262306a36Sopenharmony_ci printk(" - Suspend Program: %s\n", extp->FeatureSupport&4?"supported":"unsupported"); 7362306a36Sopenharmony_ci printk(" - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported"); 7462306a36Sopenharmony_ci printk(" - Queued Erase: %s\n", extp->FeatureSupport&16?"supported":"unsupported"); 7562306a36Sopenharmony_ci printk(" - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported"); 7662306a36Sopenharmony_ci printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported"); 7762306a36Sopenharmony_ci printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported"); 7862306a36Sopenharmony_ci printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported"); 7962306a36Sopenharmony_ci for (i=9; i<32; i++) { 8062306a36Sopenharmony_ci if (extp->FeatureSupport & (1<<i)) 8162306a36Sopenharmony_ci printk(" - Unknown Bit %X: supported\n", i); 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci printk(" Supported functions after Suspend: %2.2X\n", extp->SuspendCmdSupport); 8562306a36Sopenharmony_ci printk(" - Program after Erase Suspend: %s\n", extp->SuspendCmdSupport&1?"supported":"unsupported"); 8662306a36Sopenharmony_ci for (i=1; i<8; i++) { 8762306a36Sopenharmony_ci if (extp->SuspendCmdSupport & (1<<i)) 8862306a36Sopenharmony_ci printk(" - Unknown Bit %X: supported\n", i); 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci printk(" Block Status Register Mask: %4.4X\n", extp->BlkStatusRegMask); 9262306a36Sopenharmony_ci printk(" - Lock Bit Active: %s\n", extp->BlkStatusRegMask&1?"yes":"no"); 9362306a36Sopenharmony_ci printk(" - Valid Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no"); 9462306a36Sopenharmony_ci for (i=2; i<16; i++) { 9562306a36Sopenharmony_ci if (extp->BlkStatusRegMask & (1<<i)) 9662306a36Sopenharmony_ci printk(" - Unknown Bit %X Active: yes\n",i); 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n", 10062306a36Sopenharmony_ci extp->VccOptimal >> 8, extp->VccOptimal & 0xf); 10162306a36Sopenharmony_ci if (extp->VppOptimal) 10262306a36Sopenharmony_ci printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", 10362306a36Sopenharmony_ci extp->VppOptimal >> 8, extp->VppOptimal & 0xf); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci#endif 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* This routine is made available to other mtd code via 10862306a36Sopenharmony_ci * inter_module_register. It must only be accessed through 10962306a36Sopenharmony_ci * inter_module_get which will bump the use count of this module. The 11062306a36Sopenharmony_ci * addresses passed back in cfi are valid as long as the use count of 11162306a36Sopenharmony_ci * this module is non-zero, i.e. between inter_module_get and 11262306a36Sopenharmony_ci * inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_cistruct mtd_info *cfi_cmdset_0020(struct map_info *map, int primary) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 11762306a36Sopenharmony_ci int i; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if (cfi->cfi_mode) { 12062306a36Sopenharmony_ci /* 12162306a36Sopenharmony_ci * It's a real CFI chip, not one for which the probe 12262306a36Sopenharmony_ci * routine faked a CFI structure. So we read the feature 12362306a36Sopenharmony_ci * table from it. 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_ci __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; 12662306a36Sopenharmony_ci struct cfi_pri_intelext *extp; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "ST Microelectronics"); 12962306a36Sopenharmony_ci if (!extp) 13062306a36Sopenharmony_ci return NULL; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (extp->MajorVersion != '1' || 13362306a36Sopenharmony_ci (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { 13462306a36Sopenharmony_ci printk(KERN_ERR " Unknown ST Microelectronics" 13562306a36Sopenharmony_ci " Extended Query version %c.%c.\n", 13662306a36Sopenharmony_ci extp->MajorVersion, extp->MinorVersion); 13762306a36Sopenharmony_ci kfree(extp); 13862306a36Sopenharmony_ci return NULL; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* Do some byteswapping if necessary */ 14262306a36Sopenharmony_ci extp->FeatureSupport = cfi32_to_cpu(map, extp->FeatureSupport); 14362306a36Sopenharmony_ci extp->BlkStatusRegMask = cfi32_to_cpu(map, 14462306a36Sopenharmony_ci extp->BlkStatusRegMask); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 14762306a36Sopenharmony_ci /* Tell the user about it in lots of lovely detail */ 14862306a36Sopenharmony_ci cfi_tell_features(extp); 14962306a36Sopenharmony_ci#endif 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Install our own private info structure */ 15262306a36Sopenharmony_ci cfi->cmdset_priv = extp; 15362306a36Sopenharmony_ci } 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci for (i=0; i< cfi->numchips; i++) { 15662306a36Sopenharmony_ci cfi->chips[i].word_write_time = 128; 15762306a36Sopenharmony_ci cfi->chips[i].buffer_write_time = 128; 15862306a36Sopenharmony_ci cfi->chips[i].erase_time = 1024; 15962306a36Sopenharmony_ci cfi->chips[i].ref_point_counter = 0; 16062306a36Sopenharmony_ci init_waitqueue_head(&(cfi->chips[i].wq)); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return cfi_staa_setup(map); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(cfi_cmdset_0020); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic struct mtd_info *cfi_staa_setup(struct map_info *map) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 17062306a36Sopenharmony_ci struct mtd_info *mtd; 17162306a36Sopenharmony_ci unsigned long offset = 0; 17262306a36Sopenharmony_ci int i,j; 17362306a36Sopenharmony_ci unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); 17662306a36Sopenharmony_ci //printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (!mtd) { 17962306a36Sopenharmony_ci kfree(cfi->cmdset_priv); 18062306a36Sopenharmony_ci return NULL; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci mtd->priv = map; 18462306a36Sopenharmony_ci mtd->type = MTD_NORFLASH; 18562306a36Sopenharmony_ci mtd->size = devsize * cfi->numchips; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; 18862306a36Sopenharmony_ci mtd->eraseregions = kmalloc_array(mtd->numeraseregions, 18962306a36Sopenharmony_ci sizeof(struct mtd_erase_region_info), 19062306a36Sopenharmony_ci GFP_KERNEL); 19162306a36Sopenharmony_ci if (!mtd->eraseregions) { 19262306a36Sopenharmony_ci kfree(cfi->cmdset_priv); 19362306a36Sopenharmony_ci kfree(mtd); 19462306a36Sopenharmony_ci return NULL; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci for (i=0; i<cfi->cfiq->NumEraseRegions; i++) { 19862306a36Sopenharmony_ci unsigned long ernum, ersize; 19962306a36Sopenharmony_ci ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave; 20062306a36Sopenharmony_ci ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (mtd->erasesize < ersize) { 20362306a36Sopenharmony_ci mtd->erasesize = ersize; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci for (j=0; j<cfi->numchips; j++) { 20662306a36Sopenharmony_ci mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset; 20762306a36Sopenharmony_ci mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize; 20862306a36Sopenharmony_ci mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci offset += (ersize * ernum); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (offset != devsize) { 21462306a36Sopenharmony_ci /* Argh */ 21562306a36Sopenharmony_ci printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); 21662306a36Sopenharmony_ci kfree(mtd->eraseregions); 21762306a36Sopenharmony_ci kfree(cfi->cmdset_priv); 21862306a36Sopenharmony_ci kfree(mtd); 21962306a36Sopenharmony_ci return NULL; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci for (i=0; i<mtd->numeraseregions;i++){ 22362306a36Sopenharmony_ci printk(KERN_DEBUG "%d: offset=0x%llx,size=0x%x,blocks=%d\n", 22462306a36Sopenharmony_ci i, (unsigned long long)mtd->eraseregions[i].offset, 22562306a36Sopenharmony_ci mtd->eraseregions[i].erasesize, 22662306a36Sopenharmony_ci mtd->eraseregions[i].numblocks); 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Also select the correct geometry setup too */ 23062306a36Sopenharmony_ci mtd->_erase = cfi_staa_erase_varsize; 23162306a36Sopenharmony_ci mtd->_read = cfi_staa_read; 23262306a36Sopenharmony_ci mtd->_write = cfi_staa_write_buffers; 23362306a36Sopenharmony_ci mtd->_writev = cfi_staa_writev; 23462306a36Sopenharmony_ci mtd->_sync = cfi_staa_sync; 23562306a36Sopenharmony_ci mtd->_lock = cfi_staa_lock; 23662306a36Sopenharmony_ci mtd->_unlock = cfi_staa_unlock; 23762306a36Sopenharmony_ci mtd->_suspend = cfi_staa_suspend; 23862306a36Sopenharmony_ci mtd->_resume = cfi_staa_resume; 23962306a36Sopenharmony_ci mtd->flags = MTD_CAP_NORFLASH & ~MTD_BIT_WRITEABLE; 24062306a36Sopenharmony_ci mtd->writesize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */ 24162306a36Sopenharmony_ci mtd->writebufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; 24262306a36Sopenharmony_ci map->fldrv = &cfi_staa_chipdrv; 24362306a36Sopenharmony_ci __module_get(THIS_MODULE); 24462306a36Sopenharmony_ci mtd->name = map->name; 24562306a36Sopenharmony_ci return mtd; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci map_word status, status_OK; 25262306a36Sopenharmony_ci unsigned long timeo; 25362306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 25462306a36Sopenharmony_ci int suspended = 0; 25562306a36Sopenharmony_ci unsigned long cmd_addr; 25662306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci adr += chip->start; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* Ensure cmd read/writes are aligned. */ 26162306a36Sopenharmony_ci cmd_addr = adr & ~(map_bankwidth(map)-1); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* Let's determine this according to the interleave only once */ 26462306a36Sopenharmony_ci status_OK = CMD(0x80); 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci timeo = jiffies + HZ; 26762306a36Sopenharmony_ci retry: 26862306a36Sopenharmony_ci mutex_lock(&chip->mutex); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* Check that the chip's ready to talk to us. 27162306a36Sopenharmony_ci * If it's in FL_ERASING state, suspend it and make it talk now. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci switch (chip->state) { 27462306a36Sopenharmony_ci case FL_ERASING: 27562306a36Sopenharmony_ci if (!(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)) 27662306a36Sopenharmony_ci goto sleep; /* We don't support erase suspend */ 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci map_write (map, CMD(0xb0), cmd_addr); 27962306a36Sopenharmony_ci /* If the flash has finished erasing, then 'erase suspend' 28062306a36Sopenharmony_ci * appears to make some (28F320) flash devices switch to 28162306a36Sopenharmony_ci * 'read' mode. Make sure that we switch to 'read status' 28262306a36Sopenharmony_ci * mode so we get the right data. --rmk 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci map_write(map, CMD(0x70), cmd_addr); 28562306a36Sopenharmony_ci chip->oldstate = FL_ERASING; 28662306a36Sopenharmony_ci chip->state = FL_ERASE_SUSPENDING; 28762306a36Sopenharmony_ci // printk("Erase suspending at 0x%lx\n", cmd_addr); 28862306a36Sopenharmony_ci for (;;) { 28962306a36Sopenharmony_ci status = map_read(map, cmd_addr); 29062306a36Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (time_after(jiffies, timeo)) { 29462306a36Sopenharmony_ci /* Urgh */ 29562306a36Sopenharmony_ci map_write(map, CMD(0xd0), cmd_addr); 29662306a36Sopenharmony_ci /* make sure we're in 'read status' mode */ 29762306a36Sopenharmony_ci map_write(map, CMD(0x70), cmd_addr); 29862306a36Sopenharmony_ci chip->state = FL_ERASING; 29962306a36Sopenharmony_ci wake_up(&chip->wq); 30062306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 30162306a36Sopenharmony_ci printk(KERN_ERR "Chip not ready after erase " 30262306a36Sopenharmony_ci "suspended: status = 0x%lx\n", status.x[0]); 30362306a36Sopenharmony_ci return -EIO; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 30762306a36Sopenharmony_ci cfi_udelay(1); 30862306a36Sopenharmony_ci mutex_lock(&chip->mutex); 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci suspended = 1; 31262306a36Sopenharmony_ci map_write(map, CMD(0xff), cmd_addr); 31362306a36Sopenharmony_ci chip->state = FL_READY; 31462306a36Sopenharmony_ci break; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci#if 0 31762306a36Sopenharmony_ci case FL_WRITING: 31862306a36Sopenharmony_ci /* Not quite yet */ 31962306a36Sopenharmony_ci#endif 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci case FL_READY: 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci case FL_CFI_QUERY: 32562306a36Sopenharmony_ci case FL_JEDEC_QUERY: 32662306a36Sopenharmony_ci map_write(map, CMD(0x70), cmd_addr); 32762306a36Sopenharmony_ci chip->state = FL_STATUS; 32862306a36Sopenharmony_ci fallthrough; 32962306a36Sopenharmony_ci case FL_STATUS: 33062306a36Sopenharmony_ci status = map_read(map, cmd_addr); 33162306a36Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) { 33262306a36Sopenharmony_ci map_write(map, CMD(0xff), cmd_addr); 33362306a36Sopenharmony_ci chip->state = FL_READY; 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Urgh. Chip not yet ready to talk to us. */ 33862306a36Sopenharmony_ci if (time_after(jiffies, timeo)) { 33962306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 34062306a36Sopenharmony_ci printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %lx\n", status.x[0]); 34162306a36Sopenharmony_ci return -EIO; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 34562306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 34662306a36Sopenharmony_ci cfi_udelay(1); 34762306a36Sopenharmony_ci goto retry; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci default: 35062306a36Sopenharmony_ci sleep: 35162306a36Sopenharmony_ci /* Stick ourselves on a wait queue to be woken when 35262306a36Sopenharmony_ci someone changes the status */ 35362306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 35462306a36Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 35562306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 35662306a36Sopenharmony_ci schedule(); 35762306a36Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 35862306a36Sopenharmony_ci timeo = jiffies + HZ; 35962306a36Sopenharmony_ci goto retry; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci map_copy_from(map, buf, adr, len); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (suspended) { 36562306a36Sopenharmony_ci chip->state = chip->oldstate; 36662306a36Sopenharmony_ci /* What if one interleaved chip has finished and the 36762306a36Sopenharmony_ci other hasn't? The old code would leave the finished 36862306a36Sopenharmony_ci one in READY mode. That's bad, and caused -EROFS 36962306a36Sopenharmony_ci errors to be returned from do_erase_oneblock because 37062306a36Sopenharmony_ci that's the only bit it checked for at the time. 37162306a36Sopenharmony_ci As the state machine appears to explicitly allow 37262306a36Sopenharmony_ci sending the 0x70 (Read Status) command to an erasing 37362306a36Sopenharmony_ci chip and expecting it to be ignored, that's what we 37462306a36Sopenharmony_ci do. */ 37562306a36Sopenharmony_ci map_write(map, CMD(0xd0), cmd_addr); 37662306a36Sopenharmony_ci map_write(map, CMD(0x70), cmd_addr); 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci wake_up(&chip->wq); 38062306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int cfi_staa_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct map_info *map = mtd->priv; 38762306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 38862306a36Sopenharmony_ci unsigned long ofs; 38962306a36Sopenharmony_ci int chipnum; 39062306a36Sopenharmony_ci int ret = 0; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* ofs: offset within the first chip that the first read should start */ 39362306a36Sopenharmony_ci chipnum = (from >> cfi->chipshift); 39462306a36Sopenharmony_ci ofs = from - (chipnum << cfi->chipshift); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci while (len) { 39762306a36Sopenharmony_ci unsigned long thislen; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (chipnum >= cfi->numchips) 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if ((len + ofs -1) >> cfi->chipshift) 40362306a36Sopenharmony_ci thislen = (1<<cfi->chipshift) - ofs; 40462306a36Sopenharmony_ci else 40562306a36Sopenharmony_ci thislen = len; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci ret = do_read_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); 40862306a36Sopenharmony_ci if (ret) 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci *retlen += thislen; 41262306a36Sopenharmony_ci len -= thislen; 41362306a36Sopenharmony_ci buf += thislen; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci ofs = 0; 41662306a36Sopenharmony_ci chipnum++; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci return ret; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic int do_write_buffer(struct map_info *map, struct flchip *chip, 42262306a36Sopenharmony_ci unsigned long adr, const u_char *buf, int len) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 42562306a36Sopenharmony_ci map_word status, status_OK; 42662306a36Sopenharmony_ci unsigned long cmd_adr, timeo; 42762306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 42862306a36Sopenharmony_ci int wbufsize, z; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* M58LW064A requires bus alignment for buffer wriets -- saw */ 43162306a36Sopenharmony_ci if (adr & (map_bankwidth(map)-1)) 43262306a36Sopenharmony_ci return -EINVAL; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; 43562306a36Sopenharmony_ci adr += chip->start; 43662306a36Sopenharmony_ci cmd_adr = adr & ~(wbufsize-1); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* Let's determine this according to the interleave only once */ 43962306a36Sopenharmony_ci status_OK = CMD(0x80); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci timeo = jiffies + HZ; 44262306a36Sopenharmony_ci retry: 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 44562306a36Sopenharmony_ci printk("%s: chip->state[%d]\n", __func__, chip->state); 44662306a36Sopenharmony_ci#endif 44762306a36Sopenharmony_ci mutex_lock(&chip->mutex); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* Check that the chip's ready to talk to us. 45062306a36Sopenharmony_ci * Later, we can actually think about interrupting it 45162306a36Sopenharmony_ci * if it's in FL_ERASING state. 45262306a36Sopenharmony_ci * Not just yet, though. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ci switch (chip->state) { 45562306a36Sopenharmony_ci case FL_READY: 45662306a36Sopenharmony_ci break; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci case FL_CFI_QUERY: 45962306a36Sopenharmony_ci case FL_JEDEC_QUERY: 46062306a36Sopenharmony_ci map_write(map, CMD(0x70), cmd_adr); 46162306a36Sopenharmony_ci chip->state = FL_STATUS; 46262306a36Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 46362306a36Sopenharmony_ci printk("%s: 1 status[%x]\n", __func__, map_read(map, cmd_adr)); 46462306a36Sopenharmony_ci#endif 46562306a36Sopenharmony_ci fallthrough; 46662306a36Sopenharmony_ci case FL_STATUS: 46762306a36Sopenharmony_ci status = map_read(map, cmd_adr); 46862306a36Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 46962306a36Sopenharmony_ci break; 47062306a36Sopenharmony_ci /* Urgh. Chip not yet ready to talk to us. */ 47162306a36Sopenharmony_ci if (time_after(jiffies, timeo)) { 47262306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 47362306a36Sopenharmony_ci printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %lx, status = %lx\n", 47462306a36Sopenharmony_ci status.x[0], map_read(map, cmd_adr).x[0]); 47562306a36Sopenharmony_ci return -EIO; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 47962306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 48062306a36Sopenharmony_ci cfi_udelay(1); 48162306a36Sopenharmony_ci goto retry; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci default: 48462306a36Sopenharmony_ci /* Stick ourselves on a wait queue to be woken when 48562306a36Sopenharmony_ci someone changes the status */ 48662306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 48762306a36Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 48862306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 48962306a36Sopenharmony_ci schedule(); 49062306a36Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 49162306a36Sopenharmony_ci timeo = jiffies + HZ; 49262306a36Sopenharmony_ci goto retry; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci ENABLE_VPP(map); 49662306a36Sopenharmony_ci map_write(map, CMD(0xe8), cmd_adr); 49762306a36Sopenharmony_ci chip->state = FL_WRITING_TO_BUFFER; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci z = 0; 50062306a36Sopenharmony_ci for (;;) { 50162306a36Sopenharmony_ci status = map_read(map, cmd_adr); 50262306a36Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 50362306a36Sopenharmony_ci break; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 50662306a36Sopenharmony_ci cfi_udelay(1); 50762306a36Sopenharmony_ci mutex_lock(&chip->mutex); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (++z > 100) { 51062306a36Sopenharmony_ci /* Argh. Not ready for write to buffer */ 51162306a36Sopenharmony_ci DISABLE_VPP(map); 51262306a36Sopenharmony_ci map_write(map, CMD(0x70), cmd_adr); 51362306a36Sopenharmony_ci chip->state = FL_STATUS; 51462306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 51562306a36Sopenharmony_ci printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx\n", status.x[0]); 51662306a36Sopenharmony_ci return -EIO; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* Write length of data to come */ 52162306a36Sopenharmony_ci map_write(map, CMD(len/map_bankwidth(map)-1), cmd_adr ); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Write data */ 52462306a36Sopenharmony_ci for (z = 0; z < len; 52562306a36Sopenharmony_ci z += map_bankwidth(map), buf += map_bankwidth(map)) { 52662306a36Sopenharmony_ci map_word d; 52762306a36Sopenharmony_ci d = map_word_load(map, buf); 52862306a36Sopenharmony_ci map_write(map, d, adr+z); 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci /* GO GO GO */ 53162306a36Sopenharmony_ci map_write(map, CMD(0xd0), cmd_adr); 53262306a36Sopenharmony_ci chip->state = FL_WRITING; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 53562306a36Sopenharmony_ci cfi_udelay(chip->buffer_write_time); 53662306a36Sopenharmony_ci mutex_lock(&chip->mutex); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci timeo = jiffies + (HZ/2); 53962306a36Sopenharmony_ci z = 0; 54062306a36Sopenharmony_ci for (;;) { 54162306a36Sopenharmony_ci if (chip->state != FL_WRITING) { 54262306a36Sopenharmony_ci /* Someone's suspended the write. Sleep */ 54362306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 54462306a36Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 54562306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 54662306a36Sopenharmony_ci schedule(); 54762306a36Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 54862306a36Sopenharmony_ci timeo = jiffies + (HZ / 2); /* FIXME */ 54962306a36Sopenharmony_ci mutex_lock(&chip->mutex); 55062306a36Sopenharmony_ci continue; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci status = map_read(map, cmd_adr); 55462306a36Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 55562306a36Sopenharmony_ci break; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* OK Still waiting */ 55862306a36Sopenharmony_ci if (time_after(jiffies, timeo)) { 55962306a36Sopenharmony_ci /* clear status */ 56062306a36Sopenharmony_ci map_write(map, CMD(0x50), cmd_adr); 56162306a36Sopenharmony_ci /* put back into read status register mode */ 56262306a36Sopenharmony_ci map_write(map, CMD(0x70), adr); 56362306a36Sopenharmony_ci chip->state = FL_STATUS; 56462306a36Sopenharmony_ci DISABLE_VPP(map); 56562306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 56662306a36Sopenharmony_ci printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); 56762306a36Sopenharmony_ci return -EIO; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 57162306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 57262306a36Sopenharmony_ci cfi_udelay(1); 57362306a36Sopenharmony_ci z++; 57462306a36Sopenharmony_ci mutex_lock(&chip->mutex); 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci if (!z) { 57762306a36Sopenharmony_ci chip->buffer_write_time--; 57862306a36Sopenharmony_ci if (!chip->buffer_write_time) 57962306a36Sopenharmony_ci chip->buffer_write_time++; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci if (z > 1) 58262306a36Sopenharmony_ci chip->buffer_write_time++; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* Done and happy. */ 58562306a36Sopenharmony_ci DISABLE_VPP(map); 58662306a36Sopenharmony_ci chip->state = FL_STATUS; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* check for errors: 'lock bit', 'VPP', 'dead cell'/'unerased cell' or 'incorrect cmd' -- saw */ 58962306a36Sopenharmony_ci if (map_word_bitsset(map, status, CMD(0x3a))) { 59062306a36Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 59162306a36Sopenharmony_ci printk("%s: 2 status[%lx]\n", __func__, status.x[0]); 59262306a36Sopenharmony_ci#endif 59362306a36Sopenharmony_ci /* clear status */ 59462306a36Sopenharmony_ci map_write(map, CMD(0x50), cmd_adr); 59562306a36Sopenharmony_ci /* put back into read status register mode */ 59662306a36Sopenharmony_ci map_write(map, CMD(0x70), adr); 59762306a36Sopenharmony_ci wake_up(&chip->wq); 59862306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 59962306a36Sopenharmony_ci return map_word_bitsset(map, status, CMD(0x02)) ? -EROFS : -EIO; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci wake_up(&chip->wq); 60262306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return 0; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to, 60862306a36Sopenharmony_ci size_t len, size_t *retlen, const u_char *buf) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct map_info *map = mtd->priv; 61162306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 61262306a36Sopenharmony_ci int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize; 61362306a36Sopenharmony_ci int ret; 61462306a36Sopenharmony_ci int chipnum; 61562306a36Sopenharmony_ci unsigned long ofs; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci chipnum = to >> cfi->chipshift; 61862306a36Sopenharmony_ci ofs = to - (chipnum << cfi->chipshift); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci#ifdef DEBUG_CFI_FEATURES 62162306a36Sopenharmony_ci printk("%s: map_bankwidth(map)[%x]\n", __func__, map_bankwidth(map)); 62262306a36Sopenharmony_ci printk("%s: chipnum[%x] wbufsize[%x]\n", __func__, chipnum, wbufsize); 62362306a36Sopenharmony_ci printk("%s: ofs[%x] len[%x]\n", __func__, ofs, len); 62462306a36Sopenharmony_ci#endif 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci /* Write buffer is worth it only if more than one word to write... */ 62762306a36Sopenharmony_ci while (len > 0) { 62862306a36Sopenharmony_ci /* We must not cross write block boundaries */ 62962306a36Sopenharmony_ci int size = wbufsize - (ofs & (wbufsize-1)); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if (size > len) 63262306a36Sopenharmony_ci size = len; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci ret = do_write_buffer(map, &cfi->chips[chipnum], 63562306a36Sopenharmony_ci ofs, buf, size); 63662306a36Sopenharmony_ci if (ret) 63762306a36Sopenharmony_ci return ret; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci ofs += size; 64062306a36Sopenharmony_ci buf += size; 64162306a36Sopenharmony_ci (*retlen) += size; 64262306a36Sopenharmony_ci len -= size; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (ofs >> cfi->chipshift) { 64562306a36Sopenharmony_ci chipnum ++; 64662306a36Sopenharmony_ci ofs = 0; 64762306a36Sopenharmony_ci if (chipnum == cfi->numchips) 64862306a36Sopenharmony_ci return 0; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci return 0; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci/* 65662306a36Sopenharmony_ci * Writev for ECC-Flashes is a little more complicated. We need to maintain 65762306a36Sopenharmony_ci * a small buffer for this. 65862306a36Sopenharmony_ci * XXX: If the buffer size is not a multiple of 2, this will break 65962306a36Sopenharmony_ci */ 66062306a36Sopenharmony_ci#define ECCBUF_SIZE (mtd->writesize) 66162306a36Sopenharmony_ci#define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1)) 66262306a36Sopenharmony_ci#define ECCBUF_MOD(x) ((x) & (ECCBUF_SIZE - 1)) 66362306a36Sopenharmony_cistatic int 66462306a36Sopenharmony_cicfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs, 66562306a36Sopenharmony_ci unsigned long count, loff_t to, size_t *retlen) 66662306a36Sopenharmony_ci{ 66762306a36Sopenharmony_ci unsigned long i; 66862306a36Sopenharmony_ci size_t totlen = 0, thislen; 66962306a36Sopenharmony_ci int ret = 0; 67062306a36Sopenharmony_ci size_t buflen = 0; 67162306a36Sopenharmony_ci char *buffer; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (!ECCBUF_SIZE) { 67462306a36Sopenharmony_ci /* We should fall back to a general writev implementation. 67562306a36Sopenharmony_ci * Until that is written, just break. 67662306a36Sopenharmony_ci */ 67762306a36Sopenharmony_ci return -EIO; 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci buffer = kmalloc(ECCBUF_SIZE, GFP_KERNEL); 68062306a36Sopenharmony_ci if (!buffer) 68162306a36Sopenharmony_ci return -ENOMEM; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci for (i=0; i<count; i++) { 68462306a36Sopenharmony_ci size_t elem_len = vecs[i].iov_len; 68562306a36Sopenharmony_ci void *elem_base = vecs[i].iov_base; 68662306a36Sopenharmony_ci if (!elem_len) /* FIXME: Might be unnecessary. Check that */ 68762306a36Sopenharmony_ci continue; 68862306a36Sopenharmony_ci if (buflen) { /* cut off head */ 68962306a36Sopenharmony_ci if (buflen + elem_len < ECCBUF_SIZE) { /* just accumulate */ 69062306a36Sopenharmony_ci memcpy(buffer+buflen, elem_base, elem_len); 69162306a36Sopenharmony_ci buflen += elem_len; 69262306a36Sopenharmony_ci continue; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci memcpy(buffer+buflen, elem_base, ECCBUF_SIZE-buflen); 69562306a36Sopenharmony_ci ret = mtd_write(mtd, to, ECCBUF_SIZE, &thislen, 69662306a36Sopenharmony_ci buffer); 69762306a36Sopenharmony_ci totlen += thislen; 69862306a36Sopenharmony_ci if (ret || thislen != ECCBUF_SIZE) 69962306a36Sopenharmony_ci goto write_error; 70062306a36Sopenharmony_ci elem_len -= thislen-buflen; 70162306a36Sopenharmony_ci elem_base += thislen-buflen; 70262306a36Sopenharmony_ci to += ECCBUF_SIZE; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci if (ECCBUF_DIV(elem_len)) { /* write clean aligned data */ 70562306a36Sopenharmony_ci ret = mtd_write(mtd, to, ECCBUF_DIV(elem_len), 70662306a36Sopenharmony_ci &thislen, elem_base); 70762306a36Sopenharmony_ci totlen += thislen; 70862306a36Sopenharmony_ci if (ret || thislen != ECCBUF_DIV(elem_len)) 70962306a36Sopenharmony_ci goto write_error; 71062306a36Sopenharmony_ci to += thislen; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci buflen = ECCBUF_MOD(elem_len); /* cut off tail */ 71362306a36Sopenharmony_ci if (buflen) { 71462306a36Sopenharmony_ci memset(buffer, 0xff, ECCBUF_SIZE); 71562306a36Sopenharmony_ci memcpy(buffer, elem_base + thislen, buflen); 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci if (buflen) { /* flush last page, even if not full */ 71962306a36Sopenharmony_ci /* This is sometimes intended behaviour, really */ 72062306a36Sopenharmony_ci ret = mtd_write(mtd, to, buflen, &thislen, buffer); 72162306a36Sopenharmony_ci totlen += thislen; 72262306a36Sopenharmony_ci if (ret || thislen != ECCBUF_SIZE) 72362306a36Sopenharmony_ci goto write_error; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ciwrite_error: 72662306a36Sopenharmony_ci if (retlen) 72762306a36Sopenharmony_ci *retlen = totlen; 72862306a36Sopenharmony_ci kfree(buffer); 72962306a36Sopenharmony_ci return ret; 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 73662306a36Sopenharmony_ci map_word status, status_OK; 73762306a36Sopenharmony_ci unsigned long timeo; 73862306a36Sopenharmony_ci int retries = 3; 73962306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 74062306a36Sopenharmony_ci int ret = 0; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci adr += chip->start; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* Let's determine this according to the interleave only once */ 74562306a36Sopenharmony_ci status_OK = CMD(0x80); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci timeo = jiffies + HZ; 74862306a36Sopenharmony_ciretry: 74962306a36Sopenharmony_ci mutex_lock(&chip->mutex); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* Check that the chip's ready to talk to us. */ 75262306a36Sopenharmony_ci switch (chip->state) { 75362306a36Sopenharmony_ci case FL_CFI_QUERY: 75462306a36Sopenharmony_ci case FL_JEDEC_QUERY: 75562306a36Sopenharmony_ci case FL_READY: 75662306a36Sopenharmony_ci map_write(map, CMD(0x70), adr); 75762306a36Sopenharmony_ci chip->state = FL_STATUS; 75862306a36Sopenharmony_ci fallthrough; 75962306a36Sopenharmony_ci case FL_STATUS: 76062306a36Sopenharmony_ci status = map_read(map, adr); 76162306a36Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 76262306a36Sopenharmony_ci break; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* Urgh. Chip not yet ready to talk to us. */ 76562306a36Sopenharmony_ci if (time_after(jiffies, timeo)) { 76662306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 76762306a36Sopenharmony_ci printk(KERN_ERR "waiting for chip to be ready timed out in erase\n"); 76862306a36Sopenharmony_ci return -EIO; 76962306a36Sopenharmony_ci } 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 77262306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 77362306a36Sopenharmony_ci cfi_udelay(1); 77462306a36Sopenharmony_ci goto retry; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci default: 77762306a36Sopenharmony_ci /* Stick ourselves on a wait queue to be woken when 77862306a36Sopenharmony_ci someone changes the status */ 77962306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 78062306a36Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 78162306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 78262306a36Sopenharmony_ci schedule(); 78362306a36Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 78462306a36Sopenharmony_ci timeo = jiffies + HZ; 78562306a36Sopenharmony_ci goto retry; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci ENABLE_VPP(map); 78962306a36Sopenharmony_ci /* Clear the status register first */ 79062306a36Sopenharmony_ci map_write(map, CMD(0x50), adr); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci /* Now erase */ 79362306a36Sopenharmony_ci map_write(map, CMD(0x20), adr); 79462306a36Sopenharmony_ci map_write(map, CMD(0xD0), adr); 79562306a36Sopenharmony_ci chip->state = FL_ERASING; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 79862306a36Sopenharmony_ci msleep(1000); 79962306a36Sopenharmony_ci mutex_lock(&chip->mutex); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci /* FIXME. Use a timer to check this, and return immediately. */ 80262306a36Sopenharmony_ci /* Once the state machine's known to be working I'll do that */ 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci timeo = jiffies + (HZ*20); 80562306a36Sopenharmony_ci for (;;) { 80662306a36Sopenharmony_ci if (chip->state != FL_ERASING) { 80762306a36Sopenharmony_ci /* Someone's suspended the erase. Sleep */ 80862306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 80962306a36Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 81062306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 81162306a36Sopenharmony_ci schedule(); 81262306a36Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 81362306a36Sopenharmony_ci timeo = jiffies + (HZ*20); /* FIXME */ 81462306a36Sopenharmony_ci mutex_lock(&chip->mutex); 81562306a36Sopenharmony_ci continue; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci status = map_read(map, adr); 81962306a36Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 82062306a36Sopenharmony_ci break; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci /* OK Still waiting */ 82362306a36Sopenharmony_ci if (time_after(jiffies, timeo)) { 82462306a36Sopenharmony_ci map_write(map, CMD(0x70), adr); 82562306a36Sopenharmony_ci chip->state = FL_STATUS; 82662306a36Sopenharmony_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]); 82762306a36Sopenharmony_ci DISABLE_VPP(map); 82862306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 82962306a36Sopenharmony_ci return -EIO; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 83362306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 83462306a36Sopenharmony_ci cfi_udelay(1); 83562306a36Sopenharmony_ci mutex_lock(&chip->mutex); 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci DISABLE_VPP(map); 83962306a36Sopenharmony_ci ret = 0; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci /* We've broken this before. It doesn't hurt to be safe */ 84262306a36Sopenharmony_ci map_write(map, CMD(0x70), adr); 84362306a36Sopenharmony_ci chip->state = FL_STATUS; 84462306a36Sopenharmony_ci status = map_read(map, adr); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci /* check for lock bit */ 84762306a36Sopenharmony_ci if (map_word_bitsset(map, status, CMD(0x3a))) { 84862306a36Sopenharmony_ci unsigned char chipstatus = status.x[0]; 84962306a36Sopenharmony_ci if (!map_word_equal(map, status, CMD(chipstatus))) { 85062306a36Sopenharmony_ci int i, w; 85162306a36Sopenharmony_ci for (w=0; w<map_words(map); w++) { 85262306a36Sopenharmony_ci for (i = 0; i<cfi_interleave(cfi); i++) { 85362306a36Sopenharmony_ci chipstatus |= status.x[w] >> (cfi->device_type * 8); 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n", 85762306a36Sopenharmony_ci status.x[0], chipstatus); 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci /* Reset the error bits */ 86062306a36Sopenharmony_ci map_write(map, CMD(0x50), adr); 86162306a36Sopenharmony_ci map_write(map, CMD(0x70), adr); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci if ((chipstatus & 0x30) == 0x30) { 86462306a36Sopenharmony_ci printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus); 86562306a36Sopenharmony_ci ret = -EIO; 86662306a36Sopenharmony_ci } else if (chipstatus & 0x02) { 86762306a36Sopenharmony_ci /* Protection bit set */ 86862306a36Sopenharmony_ci ret = -EROFS; 86962306a36Sopenharmony_ci } else if (chipstatus & 0x8) { 87062306a36Sopenharmony_ci /* Voltage */ 87162306a36Sopenharmony_ci printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus); 87262306a36Sopenharmony_ci ret = -EIO; 87362306a36Sopenharmony_ci } else if (chipstatus & 0x20) { 87462306a36Sopenharmony_ci if (retries--) { 87562306a36Sopenharmony_ci printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus); 87662306a36Sopenharmony_ci timeo = jiffies + HZ; 87762306a36Sopenharmony_ci chip->state = FL_STATUS; 87862306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 87962306a36Sopenharmony_ci goto retry; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus); 88262306a36Sopenharmony_ci ret = -EIO; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci wake_up(&chip->wq); 88762306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 88862306a36Sopenharmony_ci return ret; 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic int cfi_staa_erase_varsize(struct mtd_info *mtd, 89262306a36Sopenharmony_ci struct erase_info *instr) 89362306a36Sopenharmony_ci{ struct map_info *map = mtd->priv; 89462306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 89562306a36Sopenharmony_ci unsigned long adr, len; 89662306a36Sopenharmony_ci int chipnum, ret; 89762306a36Sopenharmony_ci int i, first; 89862306a36Sopenharmony_ci struct mtd_erase_region_info *regions = mtd->eraseregions; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci /* Check that both start and end of the requested erase are 90162306a36Sopenharmony_ci * aligned with the erasesize at the appropriate addresses. 90262306a36Sopenharmony_ci */ 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci i = 0; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* Skip all erase regions which are ended before the start of 90762306a36Sopenharmony_ci the requested erase. Actually, to save on the calculations, 90862306a36Sopenharmony_ci we skip to the first erase region which starts after the 90962306a36Sopenharmony_ci start of the requested erase, and then go back one. 91062306a36Sopenharmony_ci */ 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci while (i < mtd->numeraseregions && instr->addr >= regions[i].offset) 91362306a36Sopenharmony_ci i++; 91462306a36Sopenharmony_ci i--; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* OK, now i is pointing at the erase region in which this 91762306a36Sopenharmony_ci erase request starts. Check the start of the requested 91862306a36Sopenharmony_ci erase range is aligned with the erase size which is in 91962306a36Sopenharmony_ci effect here. 92062306a36Sopenharmony_ci */ 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci if (instr->addr & (regions[i].erasesize-1)) 92362306a36Sopenharmony_ci return -EINVAL; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci /* Remember the erase region we start on */ 92662306a36Sopenharmony_ci first = i; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci /* Next, check that the end of the requested erase is aligned 92962306a36Sopenharmony_ci * with the erase region at that address. 93062306a36Sopenharmony_ci */ 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci while (i<mtd->numeraseregions && (instr->addr + instr->len) >= regions[i].offset) 93362306a36Sopenharmony_ci i++; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* As before, drop back one to point at the region in which 93662306a36Sopenharmony_ci the address actually falls 93762306a36Sopenharmony_ci */ 93862306a36Sopenharmony_ci i--; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if ((instr->addr + instr->len) & (regions[i].erasesize-1)) 94162306a36Sopenharmony_ci return -EINVAL; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci chipnum = instr->addr >> cfi->chipshift; 94462306a36Sopenharmony_ci adr = instr->addr - (chipnum << cfi->chipshift); 94562306a36Sopenharmony_ci len = instr->len; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci i=first; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci while(len) { 95062306a36Sopenharmony_ci ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (ret) 95362306a36Sopenharmony_ci return ret; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci adr += regions[i].erasesize; 95662306a36Sopenharmony_ci len -= regions[i].erasesize; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (adr % (1<< cfi->chipshift) == (((unsigned long)regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) 95962306a36Sopenharmony_ci i++; 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci if (adr >> cfi->chipshift) { 96262306a36Sopenharmony_ci adr = 0; 96362306a36Sopenharmony_ci chipnum++; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (chipnum >= cfi->numchips) 96662306a36Sopenharmony_ci break; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci return 0; 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_cistatic void cfi_staa_sync (struct mtd_info *mtd) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci struct map_info *map = mtd->priv; 97662306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 97762306a36Sopenharmony_ci int i; 97862306a36Sopenharmony_ci struct flchip *chip; 97962306a36Sopenharmony_ci int ret = 0; 98062306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci for (i=0; !ret && i<cfi->numchips; i++) { 98362306a36Sopenharmony_ci chip = &cfi->chips[i]; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci retry: 98662306a36Sopenharmony_ci mutex_lock(&chip->mutex); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci switch(chip->state) { 98962306a36Sopenharmony_ci case FL_READY: 99062306a36Sopenharmony_ci case FL_STATUS: 99162306a36Sopenharmony_ci case FL_CFI_QUERY: 99262306a36Sopenharmony_ci case FL_JEDEC_QUERY: 99362306a36Sopenharmony_ci chip->oldstate = chip->state; 99462306a36Sopenharmony_ci chip->state = FL_SYNCING; 99562306a36Sopenharmony_ci /* No need to wake_up() on this state change - 99662306a36Sopenharmony_ci * as the whole point is that nobody can do anything 99762306a36Sopenharmony_ci * with the chip now anyway. 99862306a36Sopenharmony_ci */ 99962306a36Sopenharmony_ci fallthrough; 100062306a36Sopenharmony_ci case FL_SYNCING: 100162306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 100262306a36Sopenharmony_ci break; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci default: 100562306a36Sopenharmony_ci /* Not an idle state */ 100662306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 100762306a36Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 101062306a36Sopenharmony_ci schedule(); 101162306a36Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci goto retry; 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci /* Unlock the chips again */ 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci for (i--; i >=0; i--) { 102062306a36Sopenharmony_ci chip = &cfi->chips[i]; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci mutex_lock(&chip->mutex); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci if (chip->state == FL_SYNCING) { 102562306a36Sopenharmony_ci chip->state = chip->oldstate; 102662306a36Sopenharmony_ci wake_up(&chip->wq); 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 102962306a36Sopenharmony_ci } 103062306a36Sopenharmony_ci} 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_cistatic inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 103562306a36Sopenharmony_ci map_word status, status_OK; 103662306a36Sopenharmony_ci unsigned long timeo = jiffies + HZ; 103762306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci adr += chip->start; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci /* Let's determine this according to the interleave only once */ 104262306a36Sopenharmony_ci status_OK = CMD(0x80); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci timeo = jiffies + HZ; 104562306a36Sopenharmony_ciretry: 104662306a36Sopenharmony_ci mutex_lock(&chip->mutex); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci /* Check that the chip's ready to talk to us. */ 104962306a36Sopenharmony_ci switch (chip->state) { 105062306a36Sopenharmony_ci case FL_CFI_QUERY: 105162306a36Sopenharmony_ci case FL_JEDEC_QUERY: 105262306a36Sopenharmony_ci case FL_READY: 105362306a36Sopenharmony_ci map_write(map, CMD(0x70), adr); 105462306a36Sopenharmony_ci chip->state = FL_STATUS; 105562306a36Sopenharmony_ci fallthrough; 105662306a36Sopenharmony_ci case FL_STATUS: 105762306a36Sopenharmony_ci status = map_read(map, adr); 105862306a36Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 105962306a36Sopenharmony_ci break; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci /* Urgh. Chip not yet ready to talk to us. */ 106262306a36Sopenharmony_ci if (time_after(jiffies, timeo)) { 106362306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 106462306a36Sopenharmony_ci printk(KERN_ERR "waiting for chip to be ready timed out in lock\n"); 106562306a36Sopenharmony_ci return -EIO; 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 106962306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 107062306a36Sopenharmony_ci cfi_udelay(1); 107162306a36Sopenharmony_ci goto retry; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci default: 107462306a36Sopenharmony_ci /* Stick ourselves on a wait queue to be woken when 107562306a36Sopenharmony_ci someone changes the status */ 107662306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 107762306a36Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 107862306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 107962306a36Sopenharmony_ci schedule(); 108062306a36Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 108162306a36Sopenharmony_ci timeo = jiffies + HZ; 108262306a36Sopenharmony_ci goto retry; 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci ENABLE_VPP(map); 108662306a36Sopenharmony_ci map_write(map, CMD(0x60), adr); 108762306a36Sopenharmony_ci map_write(map, CMD(0x01), adr); 108862306a36Sopenharmony_ci chip->state = FL_LOCKING; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 109162306a36Sopenharmony_ci msleep(1000); 109262306a36Sopenharmony_ci mutex_lock(&chip->mutex); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci /* FIXME. Use a timer to check this, and return immediately. */ 109562306a36Sopenharmony_ci /* Once the state machine's known to be working I'll do that */ 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci timeo = jiffies + (HZ*2); 109862306a36Sopenharmony_ci for (;;) { 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci status = map_read(map, adr); 110162306a36Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 110262306a36Sopenharmony_ci break; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci /* OK Still waiting */ 110562306a36Sopenharmony_ci if (time_after(jiffies, timeo)) { 110662306a36Sopenharmony_ci map_write(map, CMD(0x70), adr); 110762306a36Sopenharmony_ci chip->state = FL_STATUS; 110862306a36Sopenharmony_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]); 110962306a36Sopenharmony_ci DISABLE_VPP(map); 111062306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 111162306a36Sopenharmony_ci return -EIO; 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 111562306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 111662306a36Sopenharmony_ci cfi_udelay(1); 111762306a36Sopenharmony_ci mutex_lock(&chip->mutex); 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci /* Done and happy. */ 112162306a36Sopenharmony_ci chip->state = FL_STATUS; 112262306a36Sopenharmony_ci DISABLE_VPP(map); 112362306a36Sopenharmony_ci wake_up(&chip->wq); 112462306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 112562306a36Sopenharmony_ci return 0; 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_cistatic int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 112862306a36Sopenharmony_ci{ 112962306a36Sopenharmony_ci struct map_info *map = mtd->priv; 113062306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 113162306a36Sopenharmony_ci unsigned long adr; 113262306a36Sopenharmony_ci int chipnum, ret; 113362306a36Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 113462306a36Sopenharmony_ci int ofs_factor = cfi->interleave * cfi->device_type; 113562306a36Sopenharmony_ci#endif 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (ofs & (mtd->erasesize - 1)) 113862306a36Sopenharmony_ci return -EINVAL; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci if (len & (mtd->erasesize -1)) 114162306a36Sopenharmony_ci return -EINVAL; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci chipnum = ofs >> cfi->chipshift; 114462306a36Sopenharmony_ci adr = ofs - (chipnum << cfi->chipshift); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci while(len) { 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 114962306a36Sopenharmony_ci cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); 115062306a36Sopenharmony_ci printk("before lock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); 115162306a36Sopenharmony_ci cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); 115262306a36Sopenharmony_ci#endif 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci ret = do_lock_oneblock(map, &cfi->chips[chipnum], adr); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 115762306a36Sopenharmony_ci cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); 115862306a36Sopenharmony_ci printk("after lock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); 115962306a36Sopenharmony_ci cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); 116062306a36Sopenharmony_ci#endif 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci if (ret) 116362306a36Sopenharmony_ci return ret; 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci adr += mtd->erasesize; 116662306a36Sopenharmony_ci len -= mtd->erasesize; 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci if (adr >> cfi->chipshift) { 116962306a36Sopenharmony_ci adr = 0; 117062306a36Sopenharmony_ci chipnum++; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (chipnum >= cfi->numchips) 117362306a36Sopenharmony_ci break; 117462306a36Sopenharmony_ci } 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci return 0; 117762306a36Sopenharmony_ci} 117862306a36Sopenharmony_cistatic inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) 117962306a36Sopenharmony_ci{ 118062306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 118162306a36Sopenharmony_ci map_word status, status_OK; 118262306a36Sopenharmony_ci unsigned long timeo = jiffies + HZ; 118362306a36Sopenharmony_ci DECLARE_WAITQUEUE(wait, current); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci adr += chip->start; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci /* Let's determine this according to the interleave only once */ 118862306a36Sopenharmony_ci status_OK = CMD(0x80); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci timeo = jiffies + HZ; 119162306a36Sopenharmony_ciretry: 119262306a36Sopenharmony_ci mutex_lock(&chip->mutex); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci /* Check that the chip's ready to talk to us. */ 119562306a36Sopenharmony_ci switch (chip->state) { 119662306a36Sopenharmony_ci case FL_CFI_QUERY: 119762306a36Sopenharmony_ci case FL_JEDEC_QUERY: 119862306a36Sopenharmony_ci case FL_READY: 119962306a36Sopenharmony_ci map_write(map, CMD(0x70), adr); 120062306a36Sopenharmony_ci chip->state = FL_STATUS; 120162306a36Sopenharmony_ci fallthrough; 120262306a36Sopenharmony_ci case FL_STATUS: 120362306a36Sopenharmony_ci status = map_read(map, adr); 120462306a36Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 120562306a36Sopenharmony_ci break; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci /* Urgh. Chip not yet ready to talk to us. */ 120862306a36Sopenharmony_ci if (time_after(jiffies, timeo)) { 120962306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 121062306a36Sopenharmony_ci printk(KERN_ERR "waiting for chip to be ready timed out in unlock\n"); 121162306a36Sopenharmony_ci return -EIO; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci /* Latency issues. Drop the lock, wait a while and retry */ 121562306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 121662306a36Sopenharmony_ci cfi_udelay(1); 121762306a36Sopenharmony_ci goto retry; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci default: 122062306a36Sopenharmony_ci /* Stick ourselves on a wait queue to be woken when 122162306a36Sopenharmony_ci someone changes the status */ 122262306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 122362306a36Sopenharmony_ci add_wait_queue(&chip->wq, &wait); 122462306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 122562306a36Sopenharmony_ci schedule(); 122662306a36Sopenharmony_ci remove_wait_queue(&chip->wq, &wait); 122762306a36Sopenharmony_ci timeo = jiffies + HZ; 122862306a36Sopenharmony_ci goto retry; 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci ENABLE_VPP(map); 123262306a36Sopenharmony_ci map_write(map, CMD(0x60), adr); 123362306a36Sopenharmony_ci map_write(map, CMD(0xD0), adr); 123462306a36Sopenharmony_ci chip->state = FL_UNLOCKING; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 123762306a36Sopenharmony_ci msleep(1000); 123862306a36Sopenharmony_ci mutex_lock(&chip->mutex); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci /* FIXME. Use a timer to check this, and return immediately. */ 124162306a36Sopenharmony_ci /* Once the state machine's known to be working I'll do that */ 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci timeo = jiffies + (HZ*2); 124462306a36Sopenharmony_ci for (;;) { 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci status = map_read(map, adr); 124762306a36Sopenharmony_ci if (map_word_andequal(map, status, status_OK, status_OK)) 124862306a36Sopenharmony_ci break; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci /* OK Still waiting */ 125162306a36Sopenharmony_ci if (time_after(jiffies, timeo)) { 125262306a36Sopenharmony_ci map_write(map, CMD(0x70), adr); 125362306a36Sopenharmony_ci chip->state = FL_STATUS; 125462306a36Sopenharmony_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]); 125562306a36Sopenharmony_ci DISABLE_VPP(map); 125662306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 125762306a36Sopenharmony_ci return -EIO; 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci /* Latency issues. Drop the unlock, wait a while and retry */ 126162306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 126262306a36Sopenharmony_ci cfi_udelay(1); 126362306a36Sopenharmony_ci mutex_lock(&chip->mutex); 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci /* Done and happy. */ 126762306a36Sopenharmony_ci chip->state = FL_STATUS; 126862306a36Sopenharmony_ci DISABLE_VPP(map); 126962306a36Sopenharmony_ci wake_up(&chip->wq); 127062306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 127162306a36Sopenharmony_ci return 0; 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_cistatic int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 127462306a36Sopenharmony_ci{ 127562306a36Sopenharmony_ci struct map_info *map = mtd->priv; 127662306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 127762306a36Sopenharmony_ci unsigned long adr; 127862306a36Sopenharmony_ci int chipnum, ret; 127962306a36Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 128062306a36Sopenharmony_ci int ofs_factor = cfi->interleave * cfi->device_type; 128162306a36Sopenharmony_ci#endif 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci chipnum = ofs >> cfi->chipshift; 128462306a36Sopenharmony_ci adr = ofs - (chipnum << cfi->chipshift); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 128762306a36Sopenharmony_ci { 128862306a36Sopenharmony_ci unsigned long temp_adr = adr; 128962306a36Sopenharmony_ci unsigned long temp_len = len; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); 129262306a36Sopenharmony_ci while (temp_len) { 129362306a36Sopenharmony_ci printk("before unlock %x: block status register is %x\n",temp_adr,cfi_read_query(map, temp_adr+(2*ofs_factor))); 129462306a36Sopenharmony_ci temp_adr += mtd->erasesize; 129562306a36Sopenharmony_ci temp_len -= mtd->erasesize; 129662306a36Sopenharmony_ci } 129762306a36Sopenharmony_ci cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci#endif 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci ret = do_unlock_oneblock(map, &cfi->chips[chipnum], adr); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci#ifdef DEBUG_LOCK_BITS 130462306a36Sopenharmony_ci cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); 130562306a36Sopenharmony_ci printk("after unlock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); 130662306a36Sopenharmony_ci cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); 130762306a36Sopenharmony_ci#endif 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci return ret; 131062306a36Sopenharmony_ci} 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_cistatic int cfi_staa_suspend(struct mtd_info *mtd) 131362306a36Sopenharmony_ci{ 131462306a36Sopenharmony_ci struct map_info *map = mtd->priv; 131562306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 131662306a36Sopenharmony_ci int i; 131762306a36Sopenharmony_ci struct flchip *chip; 131862306a36Sopenharmony_ci int ret = 0; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci for (i=0; !ret && i<cfi->numchips; i++) { 132162306a36Sopenharmony_ci chip = &cfi->chips[i]; 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci mutex_lock(&chip->mutex); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci switch(chip->state) { 132662306a36Sopenharmony_ci case FL_READY: 132762306a36Sopenharmony_ci case FL_STATUS: 132862306a36Sopenharmony_ci case FL_CFI_QUERY: 132962306a36Sopenharmony_ci case FL_JEDEC_QUERY: 133062306a36Sopenharmony_ci chip->oldstate = chip->state; 133162306a36Sopenharmony_ci chip->state = FL_PM_SUSPENDED; 133262306a36Sopenharmony_ci /* No need to wake_up() on this state change - 133362306a36Sopenharmony_ci * as the whole point is that nobody can do anything 133462306a36Sopenharmony_ci * with the chip now anyway. 133562306a36Sopenharmony_ci */ 133662306a36Sopenharmony_ci break; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci case FL_PM_SUSPENDED: 133962306a36Sopenharmony_ci break; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci default: 134262306a36Sopenharmony_ci ret = -EAGAIN; 134362306a36Sopenharmony_ci break; 134462306a36Sopenharmony_ci } 134562306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci /* Unlock the chips again */ 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci if (ret) { 135162306a36Sopenharmony_ci for (i--; i >=0; i--) { 135262306a36Sopenharmony_ci chip = &cfi->chips[i]; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci mutex_lock(&chip->mutex); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci if (chip->state == FL_PM_SUSPENDED) { 135762306a36Sopenharmony_ci /* No need to force it into a known state here, 135862306a36Sopenharmony_ci because we're returning failure, and it didn't 135962306a36Sopenharmony_ci get power cycled */ 136062306a36Sopenharmony_ci chip->state = chip->oldstate; 136162306a36Sopenharmony_ci wake_up(&chip->wq); 136262306a36Sopenharmony_ci } 136362306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 136462306a36Sopenharmony_ci } 136562306a36Sopenharmony_ci } 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci return ret; 136862306a36Sopenharmony_ci} 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_cistatic void cfi_staa_resume(struct mtd_info *mtd) 137162306a36Sopenharmony_ci{ 137262306a36Sopenharmony_ci struct map_info *map = mtd->priv; 137362306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 137462306a36Sopenharmony_ci int i; 137562306a36Sopenharmony_ci struct flchip *chip; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci for (i=0; i<cfi->numchips; i++) { 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci chip = &cfi->chips[i]; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci mutex_lock(&chip->mutex); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci /* Go to known state. Chip may have been power cycled */ 138462306a36Sopenharmony_ci if (chip->state == FL_PM_SUSPENDED) { 138562306a36Sopenharmony_ci map_write(map, CMD(0xFF), 0); 138662306a36Sopenharmony_ci chip->state = FL_READY; 138762306a36Sopenharmony_ci wake_up(&chip->wq); 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci mutex_unlock(&chip->mutex); 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci} 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_cistatic void cfi_staa_destroy(struct mtd_info *mtd) 139562306a36Sopenharmony_ci{ 139662306a36Sopenharmony_ci struct map_info *map = mtd->priv; 139762306a36Sopenharmony_ci struct cfi_private *cfi = map->fldrv_priv; 139862306a36Sopenharmony_ci kfree(cfi->cmdset_priv); 139962306a36Sopenharmony_ci kfree(cfi); 140062306a36Sopenharmony_ci} 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1403