--- zzzz-none-000/linux-2.4.17/drivers/mtd/chips/cfi_cmdset_0001.c 2001-10-04 22:14:59.000000000 +0000 +++ sangam-fb-322/linux-2.4.17/drivers/mtd/chips/cfi_cmdset_0001.c 2004-11-24 13:22:49.000000000 +0000 @@ -4,9 +4,9 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0001.c,v 1.87 2001/10/02 15:05:11 dwmw2 Exp $ + * $Id: cfi_cmdset_0001.c,v 1.8 2002/10/10 18:06:03 jyothi Exp $ + * * - * * 10/10/2000 Nicolas Pitre * - completely revamped method functions so they are aware and * independent of the flash geometry (buswidth, interleave, etc.) @@ -30,7 +30,14 @@ #include #include +/* IMPORTANT: Define this flag to make write blocking, i.e the delay in + * do_write_onword will not relinquish time slice for the read (do_read_oneword) + * and erase (do_erase_oneblock) routines.*/ +#define BLOCKING_WRITE + static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); @@ -54,11 +61,12 @@ }; /* #define DEBUG_LOCK_BITS */ -/* #define DEBUG_CFI_FEATURES */ +/*#define DEBUG_CFI_FEATURES*/ #ifdef DEBUG_CFI_FEATURES static void cfi_tell_features(struct cfi_pri_intelext *extp) { + int i; printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport); printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported"); printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported"); @@ -70,17 +78,17 @@ printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported"); printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported"); for (i=9; i<32; i++) { - if (extp->FeatureSupport & (1<FeatureSupport & (1<SuspendCmdSupport); printk(" - Program after Erase Suspend: %s\n", extp->SuspendCmdSupport&1?"supported":"unsupported"); for (i=1; i<8; i++) { if (extp->SuspendCmdSupport & (1<BlkStatusRegMask); printk(" - Lock Bit Active: %s\n", extp->BlkStatusRegMask&1?"yes":"no"); printk(" - Valid Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no"); @@ -88,11 +96,11 @@ if (extp->BlkStatusRegMask & (1<VccOptimal >> 8, extp->VccOptimal & 0xf); if (extp->VppOptimal) - printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", + printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", extp->VppOptimal >> 8, extp->VppOptimal & 0xf); } #endif @@ -111,7 +119,7 @@ __u32 base = cfi->chips[0].start; if (cfi->cfi_mode) { - /* + /* * It's a real CFI chip, not one for which the probe * routine faked a CFI structure. So we read the feature * table from it. @@ -132,14 +140,14 @@ printk(KERN_ERR "Failed to allocate memory\n"); return NULL; } - + /* Read in the Extended Query Table */ for (i=0; iMajorVersion != '1' || + + if (extp->MajorVersion != '1' || (extp->MinorVersion < '0' || extp->MinorVersion > '2')) { printk(KERN_WARNING " Unknown IntelExt Extended Query " "version %c.%c.\n", extp->MajorVersion, @@ -147,29 +155,30 @@ kfree(extp); return NULL; } - + /* Do some byteswapping if necessary */ extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport); extp->BlkStatusRegMask = cfi32_to_cpu(extp->BlkStatusRegMask); - + extp->ProtRegAddr = cfi32_to_cpu(extp->ProtRegAddr); + #ifdef DEBUG_CFI_FEATURES /* Tell the user about it in lots of lovely detail */ cfi_tell_features(extp); -#endif +#endif /* Install our own private info structure */ cfi->cmdset_priv = extp; - } + } for (i=0; i< cfi->numchips; i++) { cfi->chips[i].word_write_time = 128; cfi->chips[i].buffer_write_time = 128; cfi->chips[i].erase_time = 1024; - } + } map->fldrv = &cfi_intelext_chipdrv; MOD_INC_USE_COUNT; - + /* Make sure it's in read mode */ cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL); return cfi_intelext_setup(map); @@ -198,14 +207,14 @@ mtd->size = devsize * cfi->numchips; mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; - mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) + mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL); - if (!mtd->eraseregions) { + if (!mtd->eraseregions) { printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n"); kfree(cfi->cmdset_priv); return NULL; } - + for (i=0; icfiq->NumEraseRegions; i++) { unsigned long ernum, ersize; ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave; @@ -237,7 +246,7 @@ mtd->eraseregions[i].numblocks); } - /* Also select the correct geometry setup too */ + /* Also select the correct geometry setup too */ mtd->erase = cfi_intelext_erase_varsize; mtd->read = cfi_intelext_read; if ( cfi->cfiq->BufWriteTimeoutTyp ) { @@ -247,6 +256,8 @@ //printk(KERN_INFO "Using word write method\n" ); mtd->write = cfi_intelext_write_words; } + mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; + mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; mtd->sync = cfi_intelext_sync; mtd->lock = cfi_intelext_lock; mtd->unlock = cfi_intelext_unlock; @@ -270,9 +281,8 @@ struct cfi_private *cfi = map->fldrv_priv; adr += chip->start; - - /* Ensure cmd read/writes are aligned. */ - cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); + /* Ensure cmd read/writes are aligned. */ + cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); @@ -288,7 +298,7 @@ case FL_ERASING: if (!((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2) goto sleep; /* We don't support erase suspend */ - + cfi_write (map, CMD(0xb0), cmd_addr); /* If the flash has finished erasing, then 'erase suspend' * appears to make some (28F320) flash devices switch to @@ -303,7 +313,7 @@ status = cfi_read(map, cmd_addr); if ((status & status_OK) == status_OK) break; - + if (time_after(jiffies, timeo)) { /* Urgh */ cfi_write(map, CMD(0xd0), cmd_addr); @@ -315,17 +325,17 @@ "suspended: status = 0x%x\n", status); return -EIO; } - + spin_unlock_bh(chip->mutex); cfi_udelay(1); spin_lock_bh(chip->mutex); } - + suspended = 1; cfi_write(map, CMD(0xff), cmd_addr); chip->state = FL_READY; break; - + #if 0 case FL_WRITING: /* Not quite yet */ @@ -340,13 +350,16 @@ chip->state = FL_STATUS; case FL_STATUS: +#if defined(CONFIG_MTD_CFI_INTELEXT) + cfi_write(map, CMD(0x70), adr); +#endif status = cfi_read(map, cmd_addr); if ((status & status_OK) == status_OK) { cfi_write(map, CMD(0xff), cmd_addr); chip->state = FL_READY; break; } - + /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { spin_unlock_bh(chip->mutex); @@ -376,17 +389,17 @@ if (suspended) { chip->state = chip->oldstate; - /* What if one interleaved chip has finished and the + /* What if one interleaved chip has finished and the other hasn't? The old code would leave the finished - one in READY mode. That's bad, and caused -EROFS + one in READY mode. That's bad, and caused -EROFS errors to be returned from do_erase_oneblock because that's the only bit it checked for at the time. - As the state machine appears to explicitly allow + As the state machine appears to explicitly allow sending the 0x70 (Read Status) command to an erasing - chip and expecting it to be ignored, that's what we + chip and expecting it to be ignored, that's what we do. */ cfi_write(map, CMD(0xd0), cmd_addr); - cfi_write(map, CMD(0x70), cmd_addr); + cfi_write(map, CMD(0x70), cmd_addr); } wake_up(&chip->wq); @@ -426,13 +439,122 @@ *retlen += thislen; len -= thislen; buf += thislen; - + ofs = 0; chipnum++; } return ret; } +static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, int base_offst, int reg_sz) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp=cfi->cmdset_priv; + int ofs_factor = cfi->interleave * cfi->device_type; + int count=len; + struct flchip *chip; + int chip_num,offst; + unsigned long timeo; + DECLARE_WAITQUEUE(wait, current); + + /* Calculate which chip & protection register offset we need */ + chip_num=((unsigned int)from/reg_sz); + offst=from-(reg_sz*chip_num)+base_offst; + + while(count){ + + if(chip_num>=cfi->numchips) + goto out; + + /* Make sure that the chip is in the right state */ + + timeo = jiffies + HZ; + chip=&cfi->chips[chip_num]; + retry: + spin_lock_bh(chip->mutex); + + switch (chip->state) { + case FL_READY: + case FL_STATUS: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + break; + + default: + /* Stick ourselves on a wait queue to be woken when + someone changes the status */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + HZ; + goto retry; + } + + /* Now read the data required from this flash */ + + cfi_send_gen_cmd(0x90, 0x55,chip->start, map, cfi, cfi->device_type, NULL); + while(count && ((offst-base_offst)read8(map,(chip->start+(extp->ProtRegAddr*ofs_factor)+offst)); + buf++; + offst++; + count--; + } + + chip->state=FL_CFI_QUERY; + spin_unlock_bh(chip->mutex); + /* Move on to the next chip */ + chip_num++; + offst=base_offst; + + } + + out: + wake_up(&chip->wq); + return len-count; +} + +static int cfi_intelext_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp=cfi->cmdset_priv; + int base_offst,reg_sz; + + /* Check that we actually have some protection registers */ + if(!(extp->FeatureSupport&64)){ + printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); + return 0; + } + + base_offst=(1<FactProtRegSize); + reg_sz=(1<UserProtRegSize); + + return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); +} + +static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp=cfi->cmdset_priv; + int base_offst,reg_sz; + + /* Check that we actually have some protection registers */ + if(!(extp->FeatureSupport&64)){ + printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); + return 0; + } + + base_offst=0; + reg_sz=(1<FactProtRegSize); + + return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); +} + + static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum) { struct cfi_private *cfi = map->fldrv_priv; @@ -458,17 +580,20 @@ switch (chip->state) { case FL_READY: break; - + case FL_CFI_QUERY: case FL_JEDEC_QUERY: cfi_write(map, CMD(0x70), adr); chip->state = FL_STATUS; case FL_STATUS: +#if defined(CONFIG_MTD_CFI_INTELEXT) + cfi_write(map, CMD(0x70), adr); +#endif status = cfi_read(map, adr); if ((status & status_OK) == status_OK) break; - + /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { spin_unlock_bh(chip->mutex); @@ -494,13 +619,23 @@ } ENABLE_VPP(map); + +#if defined(CONFIG_MTD_CFI_INTELEXT) + cfi_write(map, CMD(0x60), adr); + cfi_write(map, CMD(0xD0), adr); +#endif /* CONFIG_MTD_CFI_INTELEXT */ + cfi_write(map, CMD(0x40), adr); cfi_write(map, datum, adr); chip->state = FL_WRITING; +#ifdef BLOCKING_WRITE + udelay(chip->word_write_time); +#else spin_unlock_bh(chip->mutex); cfi_udelay(chip->word_write_time); spin_lock_bh(chip->mutex); +#endif /* BLOCKING_WRITE */ timeo = jiffies + (HZ/2); z = 0; @@ -520,7 +655,7 @@ status = cfi_read(map, adr); if ((status & status_OK) == status_OK) break; - + /* OK Still waiting */ if (time_after(jiffies, timeo)) { chip->state = FL_STATUS; @@ -541,22 +676,40 @@ if (!chip->word_write_time) chip->word_write_time++; } - if (z > 1) + if (z > 1) chip->word_write_time++; /* Done and happy. */ DISABLE_VPP(map); + +#if defined(CONFIG_MTD_CFI_INTELEXT) + cfi_write(map, CMD(0xFF), adr); + cfi_write(map, CMD(0x60), adr); + cfi_write(map, CMD(0x01), adr); + cfi_write(map, CMD(0x70), adr); +#endif /* CONFIG_MTD_CFI_INTELEXT */ + chip->state = FL_STATUS; + /* check for lock bit */ if (status & CMD(0x02)) { /* clear status */ cfi_write(map, CMD(0x50), adr); /* put back into read status register mode */ cfi_write(map, CMD(0x70), adr); + +#if defined(CONFIG_MTD_CFI_INTELEXT) + cfi_write(map, CMD(0xFF), adr); +#endif + wake_up(&chip->wq); spin_unlock_bh(chip->mutex); return -EROFS; } +#if defined(CONFIG_MTD_CFI_INTELEXT) + cfi_write(map, CMD(0xFF), adr); +#endif + wake_up(&chip->wq); spin_unlock_bh(chip->mutex); return 0; @@ -603,21 +756,21 @@ ret = do_write_oneword(map, &cfi->chips[chipnum], bus_ofs, datum); - if (ret) + if (ret) return ret; - + ofs += n; buf += n; (*retlen) += n; if (ofs >> cfi->chipshift) { - chipnum ++; + chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; } } - + while(len >= CFIDEV_BUSWIDTH) { __u32 datum; @@ -642,7 +795,7 @@ len -= CFIDEV_BUSWIDTH; if (ofs >> cfi->chipshift) { - chipnum ++; + chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; @@ -669,9 +822,9 @@ ret = do_write_oneword(map, &cfi->chips[chipnum], ofs, datum); - if (ret) + if (ret) return ret; - + (*retlen) += n; } @@ -679,7 +832,7 @@ } -static inline int do_write_buffer(struct map_info *map, struct flchip *chip, +static inline int do_write_buffer(struct map_info *map, struct flchip *chip, unsigned long adr, const u_char *buf, int len) { struct cfi_private *cfi = map->fldrv_priv; @@ -691,7 +844,7 @@ wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; adr += chip->start; cmd_adr = adr & ~(wbufsize-1); - + /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); @@ -707,13 +860,16 @@ switch (chip->state) { case FL_READY: break; - + case FL_CFI_QUERY: case FL_JEDEC_QUERY: cfi_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; case FL_STATUS: +#if defined(CONFIG_MTD_CFI_INTELEXT) + cfi_write(map, CMD(0x70), adr); +#endif status = cfi_read(map, cmd_adr); if ((status & status_OK) == status_OK) break; @@ -742,6 +898,7 @@ } ENABLE_VPP(map); + cfi_write(map, CMD(0x50), cmd_adr); cfi_write(map, CMD(0xe8), cmd_adr); chip->state = FL_WRITING_TO_BUFFER; @@ -817,7 +974,7 @@ printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); return -EIO; } - + /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock_bh(chip->mutex); cfi_udelay(1); @@ -829,7 +986,7 @@ if (!chip->buffer_write_time) chip->buffer_write_time++; } - if (z > 1) + if (z > 1) chip->buffer_write_time++; /* Done and happy. */ @@ -850,7 +1007,7 @@ return 0; } -static int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to, +static int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { struct map_info *map = mtd->priv; @@ -895,7 +1052,7 @@ if (size > len) size = len & ~(CFIDEV_BUSWIDTH-1); - ret = do_write_buffer(map, &cfi->chips[chipnum], + ret = do_write_buffer(map, &cfi->chips[chipnum], ofs, buf, size); if (ret) return ret; @@ -906,7 +1063,7 @@ len -= size; if (ofs >> cfi->chipshift) { - chipnum ++; + chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; @@ -926,8 +1083,95 @@ return 0; } +typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip, unsigned long adr); +static int cfi_intelext_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, loff_t ofs, size_t len) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long adr; + int chipnum, ret = 0; + int i, first; + struct mtd_erase_region_info *regions = mtd->eraseregions; + + if (ofs > mtd->size) + return -EINVAL; -static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) + if ((len + ofs) > mtd->size) + return -EINVAL; + + /* Check that both start and end of the requested erase are + * aligned with the erasesize at the appropriate addresses. + */ + + i = 0; + + /* Skip all erase regions which are ended before the start of + the requested erase. Actually, to save on the calculations, + we skip to the first erase region which starts after the + start of the requested erase, and then go back one. + */ + + while (i < mtd->numeraseregions && ofs >= regions[i].offset) + i++; + i--; + + /* OK, now i is pointing at the erase region in which this + erase request starts. Check the start of the requested + erase range is aligned with the erase size which is in + effect here. + */ + + if (ofs & (regions[i].erasesize-1)) + return -EINVAL; + + /* Remember the erase region we start on */ + first = i; + + /* Next, check that the end of the requested erase is aligned + * with the erase region at that address. + */ + + while (inumeraseregions && (ofs + len) >= regions[i].offset) + i++; + + /* As before, drop back one to point at the region in which + the address actually falls + */ + i--; + + if ((ofs + len) & (regions[i].erasesize-1)) + return -EINVAL; + + chipnum = ofs >> cfi->chipshift; + adr = ofs - (chipnum << cfi->chipshift); + + i=first; + + while(len) { + ret = (*frob)(map, &cfi->chips[chipnum], adr); + + if (ret) + return ret; + + adr += regions[i].erasesize; + len -= regions[i].erasesize; + + if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) + i++; + + if (adr >> cfi->chipshift) { + adr = 0; + chipnum++; + + if (chipnum >= cfi->numchips) + break; + } + } + + return 0; +} + +static int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) { struct cfi_private *cfi = map->fldrv_priv; __u32 status, status_OK; @@ -937,7 +1181,6 @@ int ret = 0; adr += chip->start; - /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); @@ -954,10 +1197,13 @@ chip->state = FL_STATUS; case FL_STATUS: +#if defined(CONFIG_MTD_CFI_INTELEXT) + cfi_write(map, CMD(0x70), adr); +#endif status = cfi_read(map, adr); if ((status & status_OK) == status_OK) break; - + /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { spin_unlock_bh(chip->mutex); @@ -983,14 +1229,21 @@ } ENABLE_VPP(map); + /* Clear the status register first */ cfi_write(map, CMD(0x50), adr); +#if defined(CONFIG_MTD_CFI_INTELEXT) + cfi_write(map, CMD(0x60), adr); + cfi_write(map, CMD(0xD0), adr); +#endif /* CONFIG_MTD_CFI_INTELEXT */ + /* Now erase */ cfi_write(map, CMD(0x20), adr); cfi_write(map, CMD(0xD0), adr); + chip->state = FL_ERASING; - + spin_unlock_bh(chip->mutex); schedule_timeout(HZ); spin_lock_bh(chip->mutex); @@ -1015,7 +1268,7 @@ status = cfi_read(map, adr); if ((status & status_OK) == status_OK) break; - + /* OK Still waiting */ if (time_after(jiffies, timeo)) { cfi_write(map, CMD(0x70), adr); @@ -1025,16 +1278,23 @@ spin_unlock_bh(chip->mutex); return -EIO; } - + /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock_bh(chip->mutex); cfi_udelay(1); spin_lock_bh(chip->mutex); } - + DISABLE_VPP(map); ret = 0; +#if defined(CONFIG_MTD_CFI_INTELEXT) + cfi_write(map, CMD(0xFF), adr); + cfi_write(map, CMD(0xD0), adr); + cfi_write(map, CMD(0x01), adr); + cfi_write(map, CMD(0x70), adr); +#endif /* CONFIG_MTD_CFI_INTELEXT */ + /* We've broken this before. It doesn't hurt to be safe */ cfi_write(map, CMD(0x70), adr); chip->state = FL_STATUS; @@ -1053,7 +1313,11 @@ /* Reset the error bits */ cfi_write(map, CMD(0x50), adr); cfi_write(map, CMD(0x70), adr); - + +#if defined(CONFIG_MTD_CFI_INTELEXT) + cfi_write(map, CMD(0xFF), adr); +#endif + if ((chipstatus & 0x30) == 0x30) { printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", status); ret = -EIO; @@ -1083,93 +1347,21 @@ } int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) -{ struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - unsigned long adr, len; - int chipnum, ret = 0; - int i, first; - struct mtd_erase_region_info *regions = mtd->eraseregions; - - if (instr->addr > mtd->size) - return -EINVAL; - - if ((instr->len + instr->addr) > mtd->size) - return -EINVAL; - - /* Check that both start and end of the requested erase are - * aligned with the erasesize at the appropriate addresses. - */ - - i = 0; - - /* Skip all erase regions which are ended before the start of - the requested erase. Actually, to save on the calculations, - we skip to the first erase region which starts after the - start of the requested erase, and then go back one. - */ - - while (i < mtd->numeraseregions && instr->addr >= regions[i].offset) - i++; - i--; - - /* OK, now i is pointing at the erase region in which this - erase request starts. Check the start of the requested - erase range is aligned with the erase size which is in - effect here. - */ - - if (instr->addr & (regions[i].erasesize-1)) - return -EINVAL; - - /* Remember the erase region we start on */ - first = i; - - /* Next, check that the end of the requested erase is aligned - * with the erase region at that address. - */ - - while (inumeraseregions && (instr->addr + instr->len) >= regions[i].offset) - i++; - - /* As before, drop back one to point at the region in which - the address actually falls - */ - i--; - - if ((instr->addr + instr->len) & (regions[i].erasesize-1)) - return -EINVAL; +{ + unsigned long ofs, len; + int ret; - chipnum = instr->addr >> cfi->chipshift; - adr = instr->addr - (chipnum << cfi->chipshift); + ofs = instr->addr; len = instr->len; - i=first; + ret = cfi_intelext_varsize_frob(mtd, do_erase_oneblock, ofs, len); + if (ret) + return ret; - while(len) { - ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); - - if (ret) - return ret; - - adr += regions[i].erasesize; - len -= regions[i].erasesize; - - if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) - i++; - - if (adr >> cfi->chipshift) { - adr = 0; - chipnum++; - - if (chipnum >= cfi->numchips) - break; - } - } - instr->state = MTD_ERASE_DONE; if (instr->callback) instr->callback(instr); - + return 0; } @@ -1195,7 +1387,7 @@ case FL_JEDEC_QUERY: chip->oldstate = chip->state; chip->state = FL_SYNCING; - /* No need to wake_up() on this state change - + /* No need to wake_up() on this state change - * as the whole point is that nobody can do anything * with the chip now anyway. */ @@ -1206,11 +1398,11 @@ default: /* Not an idle state */ add_wait_queue(&chip->wq, &wait); - + spin_unlock_bh(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - + goto retry; } } @@ -1221,7 +1413,7 @@ chip = &cfi->chips[i]; spin_lock_bh(chip->mutex); - + if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; wake_up(&chip->wq); @@ -1230,11 +1422,24 @@ } } -static inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +#ifdef DEBUG_LOCK_BITS +static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + int ofs_factor = cfi->interleave * cfi->device_type; + + cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); + printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", adr, cfi_read_query(map, adr+(2*ofs_factor))); + cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); + return 0; +} +#endif + +static int do_lock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) { struct cfi_private *cfi = map->fldrv_priv; __u32 status, status_OK; - unsigned long timeo = jiffies + HZ; + unsigned long timeo; DECLARE_WAITQUEUE(wait, current); adr += chip->start; @@ -1255,10 +1460,13 @@ chip->state = FL_STATUS; case FL_STATUS: +#if defined(CONFIG_MTD_CFI_INTELEXT) + cfi_write(map, CMD(0x70), adr); +#endif status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) + if ((status & status_OK) == status_OK) break; - + /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { spin_unlock_bh(chip->mutex); @@ -1287,7 +1495,7 @@ cfi_write(map, CMD(0x60), adr); cfi_write(map, CMD(0x01), adr); chip->state = FL_LOCKING; - + spin_unlock_bh(chip->mutex); schedule_timeout(HZ); spin_lock_bh(chip->mutex); @@ -1295,13 +1503,13 @@ /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ - timeo = jiffies + (HZ*2); + timeo = jiffies + (HZ*20); for (;;) { status = cfi_read(map, adr); if ((status & status_OK) == status_OK) break; - + /* OK Still waiting */ if (time_after(jiffies, timeo)) { cfi_write(map, CMD(0x70), adr); @@ -1311,13 +1519,13 @@ spin_unlock_bh(chip->mutex); return -EIO; } - + /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock_bh(chip->mutex); cfi_udelay(1); spin_lock_bh(chip->mutex); } - + /* Done and happy. */ chip->state = FL_STATUS; DISABLE_VPP(map); @@ -1325,65 +1533,29 @@ spin_unlock_bh(chip->mutex); return 0; } + static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) { - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - unsigned long adr; - int chipnum, ret = 0; + int ret; #ifdef DEBUG_LOCK_BITS - int ofs_factor = cfi->interleave * cfi->device_type; + printk(KERN_DEBUG __FUNCTION__ ": before locking ofs=0x%08x, len=0x%08lx\n", ofs, len); + cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len); #endif - if (ofs & (mtd->erasesize - 1)) - return -EINVAL; - - if (len & (mtd->erasesize -1)) - return -EINVAL; - - if ((len + ofs) > mtd->size) - return -EINVAL; - - chipnum = ofs >> cfi->chipshift; - adr = ofs - (chipnum << cfi->chipshift); - - while(len) { - + ret = cfi_intelext_varsize_frob(mtd, do_lock_oneblock, ofs, len); #ifdef DEBUG_LOCK_BITS - cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); - printk("before lock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); - cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); + printk(KERN_DEBUG __FUNCTION__ ": after locking, ret=%d\n", ret); + cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len); #endif - ret = do_lock_oneblock(map, &cfi->chips[chipnum], adr); - -#ifdef DEBUG_LOCK_BITS - cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); - printk("after lock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); - cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); -#endif - - if (ret) - return ret; - - adr += mtd->erasesize; - len -= mtd->erasesize; - - if (adr >> cfi->chipshift) { - adr = 0; - chipnum++; - - if (chipnum >= cfi->numchips) - break; - } - } - return 0; + return ret; } -static inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) + +static int do_unlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) { struct cfi_private *cfi = map->fldrv_priv; __u32 status, status_OK; - unsigned long timeo = jiffies + HZ; + unsigned long timeo; DECLARE_WAITQUEUE(wait, current); adr += chip->start; @@ -1404,10 +1576,13 @@ chip->state = FL_STATUS; case FL_STATUS: +#if defined(CONFIG_MTD_CFI_INTELEXT) + cfi_write(map, CMD(0x70), adr); +#endif status = cfi_read(map, adr); if ((status & status_OK) == status_OK) break; - + /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { spin_unlock_bh(chip->mutex); @@ -1436,7 +1611,7 @@ cfi_write(map, CMD(0x60), adr); cfi_write(map, CMD(0xD0), adr); chip->state = FL_UNLOCKING; - + spin_unlock_bh(chip->mutex); schedule_timeout(HZ); spin_lock_bh(chip->mutex); @@ -1444,13 +1619,13 @@ /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ - timeo = jiffies + (HZ*2); + timeo = jiffies + (HZ*20); for (;;) { status = cfi_read(map, adr); if ((status & status_OK) == status_OK) break; - + /* OK Still waiting */ if (time_after(jiffies, timeo)) { cfi_write(map, CMD(0x70), adr); @@ -1460,13 +1635,13 @@ spin_unlock_bh(chip->mutex); return -EIO; } - - /* Latency issues. Drop the unlock, wait a while and retry */ + + /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock_bh(chip->mutex); cfi_udelay(1); spin_lock_bh(chip->mutex); } - + /* Done and happy. */ chip->state = FL_STATUS; DISABLE_VPP(map); @@ -1474,42 +1649,24 @@ spin_unlock_bh(chip->mutex); return 0; } + static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) { - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - unsigned long adr; - int chipnum, ret = 0; -#ifdef DEBUG_LOCK_BITS - int ofs_factor = cfi->interleave * cfi->device_type; -#endif - - chipnum = ofs >> cfi->chipshift; - adr = ofs - (chipnum << cfi->chipshift); + int ret; #ifdef DEBUG_LOCK_BITS - { - unsigned long temp_adr = adr; - unsigned long temp_len = len; - - cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); - while (temp_len) { - printk("before unlock %x: block status register is %x\n",temp_adr,cfi_read_query(map, temp_adr+(2*ofs_factor))); - temp_adr += mtd->erasesize; - temp_len -= mtd->erasesize; - } - cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); - } + printk(KERN_DEBUG __FUNCTION__ ": before locking ofs=0x%08x, len=0x%08lx\n", ofs, len); + cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len); + #endif - ret = do_unlock_oneblock(map, &cfi->chips[chipnum], adr); + ret = cfi_intelext_varsize_frob(mtd, do_unlock_oneblock, ofs, len); #ifdef DEBUG_LOCK_BITS - cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); - printk("after unlock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); - cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); + printk(KERN_DEBUG __FUNCTION__ ": after locking, ret=%d\n", ret); + cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len); #endif - + return ret; } @@ -1533,7 +1690,7 @@ case FL_JEDEC_QUERY: chip->oldstate = chip->state; chip->state = FL_PM_SUSPENDED; - /* No need to wake_up() on this state change - + /* No need to wake_up() on this state change - * as the whole point is that nobody can do anything * with the chip now anyway. */ @@ -1552,9 +1709,9 @@ if (ret) { for (i--; i >=0; i--) { chip = &cfi->chips[i]; - + spin_lock_bh(chip->mutex); - + if (chip->state == FL_PM_SUSPENDED) { /* No need to force it into a known state here, because we're returning failure, and it didn't @@ -1564,8 +1721,8 @@ } spin_unlock_bh(chip->mutex); } - } - + } + return ret; } @@ -1577,11 +1734,11 @@ struct flchip *chip; for (i=0; inumchips; i++) { - + chip = &cfi->chips[i]; spin_lock_bh(chip->mutex); - + /* Go to known state. Chip may have been power cycled */ if (chip->state == FL_PM_SUSPENDED) { cfi_write(map, CMD(0xFF), 0); @@ -1623,3 +1780,4 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse et al."); MODULE_DESCRIPTION("MTD chip driver for Intel/Sharp flash chips"); +