📄 cfi_cmdset_0002.c
字号:
printk(KERN_DEBUG "Wake up to write:\n"); if(signal_pending(current)) return -EINTR;#endif timeo = jiffies + HZ; goto retry; } chip->state = FL_WRITING; adr += chip->start; DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n", __func__, adr, datum ); ENABLE_VPP(map); if (fast) { /* Unlock bypass */ cfi_send_gen_cmd(0xA0, 0, chip->start, map, cfi, cfi->device_type, NULL); } else { cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); } cfi_write(map, datum, adr); cfi_spin_unlock(chip->mutex); cfi_udelay(chip->word_write_time); cfi_spin_lock(chip->mutex); /* * Polling toggle bits instead of reading back many times * This ensures that write operation is really completed, * or tells us why it failed. * * It appears tha the polling and decoding of error state might * be simplified. Don't do it unless you really know what you * are doing. You must remember that JESD21-C 3.5.3 states that * the status must be read back an _additional_ two times before * a failure is determined. This is because these devices have * internal state machines that are asynchronous to the external * data bus. During an erase or write the read-back status of the * polling bits might be transitioning internaly when the external * read-back occurs. This means that the bits aren't in the final * state and they might appear to report an error as they transition * and are in a weird state. This will produce infrequent errors * that will usually disappear the next time an erase or write * happens (Try tracking those errors down!). To ensure that * the bits are not in transition the location must be read-back * two more times and compared against what was written - BOTH reads * MUST match what was written - don't think this can be simplified * to only the last read matching. If the comparison fails, error * state can then be decoded. * * - Thayne Harbaugh */ dq6 = CMD(1<<6); /* See comment above for timeout value. */ timeo = jiffies + uWriteTimeout; oldstatus = cfi_read(map, adr); status = cfi_read(map, adr); DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", __func__, oldstatus, status ); /* * This only checks if dq6 is still toggling and that our * timer hasn't expired. We purposefully ignore the chips * internal timer that will assert dq5 and leave dq6 toggling. * This is done for a variety of reasons: * 1) Not all chips support dq5. * 2) Dealing with asynchronous status bit and data updates * and reading a device two more times creates _messy_ * logic when trying to deal with interleaved devices - * some may be changing while others are still busy. * 3) Checking dq5 only helps to optimize an error case that * should at worst be infrequent and at best non-existent. * * If our timeout occurs _then_ we will check dq5 to see * if the device also had an internal timeout. */ while( ( ( status ^ oldstatus ) & dq6 ) && ! ( ta = time_after(jiffies, timeo) ) ) { if (need_resched()) { cfi_spin_unlock(chip->mutex); yield(); cfi_spin_lock(chip->mutex); } else udelay(1); oldstatus = cfi_read( map, adr ); status = cfi_read( map, adr ); DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", __func__, oldstatus, status ); } /* * Something kicked us out of the read-back loop. We'll * check success befor checking failure. * Even though dq6 might be true data, it is unkown if * all of the other bits have changed to true data due to * the asynchronous nature of the internal state machine. * We will read two more times and use this to either * verify that the write completed successfully or * that something really went wrong. BOTH reads * must match what was written - this certifies that * bits aren't still changing and that the status * bits erroneously match the datum that was written. */ prev_oldstatus = oldstatus; prev_status = status; oldstatus = cfi_read(map, adr); status = cfi_read(map, adr); DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", __func__, oldstatus, status ); if ( oldstatus == datum && status == datum ) { /* success - do nothing */ goto write_done; } if ( ta ) { int dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1; if ( status & dq5mask ) { /* dq5 asserted - decode interleave chips */ printk( KERN_WARNING "MTD %s(): FLASH internal timeout: 0x%.8x\n", __func__, status & dq5mask ); } else { printk( KERN_WARNING "MTD %s(): Software timed out during write.\n", __func__ ); } goto write_failed; } /* * If we get to here then it means that something * is wrong and it's not a timeout. Something * is seriously wacky! Dump some debug info. */ printk(KERN_WARNING "MTD %s(): Wacky! Unable to decode failure status\n", __func__ ); printk(KERN_WARNING "MTD %s(): 0x%.8lx(0x%.8x): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", __func__, adr, datum, prev_oldstatus, prev_status, oldstatus, status); write_failed: ret = -EIO; /* reset on all failures. */ cfi_write( map, CMD(0xF0), chip->start ); write_done: DISABLE_VPP(map); chip->state = FL_READY; wake_up(&chip->wq); cfi_spin_unlock(chip->mutex); return ret;}static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; int ret = 0; int chipnum; unsigned long ofs, chipstart; *retlen = 0; if (!len) return 0; chipnum = to >> cfi->chipshift; ofs = to - (chipnum << cfi->chipshift); chipstart = cfi->chips[chipnum].start; /* If it's not bus-aligned, do the first byte write */ if (ofs & (CFIDEV_BUSWIDTH-1)) { unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1); int i = ofs - bus_ofs; int n = 0; u_char tmp_buf[8]; __u32 datum; map->copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); while (len && i < CFIDEV_BUSWIDTH) tmp_buf[i++] = buf[n++], len--; if (cfi_buswidth_is_2()) { datum = *(__u16*)tmp_buf; } else if (cfi_buswidth_is_4()) { datum = *(__u32*)tmp_buf; } else { return -EINVAL; /* should never happen, but be safe */ } ret = do_write_oneword(map, &cfi->chips[chipnum], bus_ofs, datum, 0); if (ret) return ret; ofs += n; buf += n; (*retlen) += n; if (ofs >> cfi->chipshift) { chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; } } if (cfi->fast_prog) { /* Go into unlock bypass mode */ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); } /* We are now aligned, write as much as possible */ while(len >= CFIDEV_BUSWIDTH) { __u32 datum; if (cfi_buswidth_is_1()) { datum = *(__u8*)buf; } else if (cfi_buswidth_is_2()) { datum = *(__u16*)buf; } else if (cfi_buswidth_is_4()) { datum = *(__u32*)buf; } else { return -EINVAL; } ret = do_write_oneword(map, &cfi->chips[chipnum], ofs, datum, cfi->fast_prog); if (ret) { if (cfi->fast_prog){ /* Get out of unlock bypass mode */ cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); } return ret; } ofs += CFIDEV_BUSWIDTH; buf += CFIDEV_BUSWIDTH; (*retlen) += CFIDEV_BUSWIDTH; len -= CFIDEV_BUSWIDTH; if (ofs >> cfi->chipshift) { if (cfi->fast_prog){ /* Get out of unlock bypass mode */ cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); } chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; chipstart = cfi->chips[chipnum].start; if (cfi->fast_prog){ /* Go into unlock bypass mode for next set of chips */ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); } } } if (cfi->fast_prog){ /* Get out of unlock bypass mode */ cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); } /* Write the trailing bytes if any */ if (len & (CFIDEV_BUSWIDTH-1)) { int i = 0, n = 0; u_char tmp_buf[8]; __u32 datum; map->copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); while (len--) tmp_buf[i++] = buf[n++]; if (cfi_buswidth_is_2()) { datum = *(__u16*)tmp_buf; } else if (cfi_buswidth_is_4()) { datum = *(__u32*)tmp_buf; } else { return -EINVAL; /* should never happen, but be safe */ } ret = do_write_oneword(map, &cfi->chips[chipnum], ofs, datum, 0); if (ret) return ret; (*retlen) += n; } return 0;}static inline int do_erase_chip(struct map_info *map, struct flchip *chip){ unsigned int oldstatus, status, prev_oldstatus, prev_status; unsigned int dq6; unsigned long timeo = jiffies + HZ; unsigned long int adr; struct cfi_private *cfi = map->fldrv_priv; DECLARE_WAITQUEUE(wait, current); int ret = 0; int ta = 0; __u32 ones = 0; retry: cfi_spin_lock(chip->mutex); if (chip->state != FL_READY){ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); cfi_spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait);#if 0 if(signal_pending(current)) return -EINTR;#endif timeo = jiffies + HZ; goto retry; } chip->state = FL_ERASING; DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", __func__, chip->start ); /* Handle devices with one erase region, that only implement * the chip erase command. */ ENABLE_VPP(map); cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); timeo = jiffies + (HZ*20); adr = cfi->addr_unlock1; /* Wait for the end of programing/erasure by using the toggle method. * As long as there is a programming procedure going on, bit 6 * is toggling it's state with each consecutive read. * The toggling stops as soon as the procedure is completed. * * If the process has gone on for too long on the chip bit 5 gets. * After bit5 is set you can kill the operation by sending a reset * command to the chip. */ /* see comments in do_write_oneword */ dq6 = CMD(1<<6); oldstatus = cfi_read(map, adr); status = cfi_read(map, adr); DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", __func__, oldstatus, status ); while( ( ( status ^ oldstatus ) & dq6 ) && ! ( ta = time_after(jiffies, timeo) ) ) { int wait_reps; /* an initial short sleep */ cfi_spin_unlock(chip->mutex); schedule_timeout(HZ/100); cfi_spin_lock(chip->mutex); if (chip->state != FL_ERASING) { /* Someone's suspended the erase. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); cfi_spin_unlock(chip->mutex); printk("erase suspended. Sleeping\n"); schedule(); remove_wait_queue(&chip->wq, &wait);#if 0 if (signal_pending(current)) return -EINTR;#endif timeo = jiffies + (HZ*2); /* FIXME */ cfi_spin_lock(chip->mutex); continue; } /* Busy wait for 1/10 of a milisecond */ for(wait_reps = 0; (wait_reps < 100) && ( ( status ^ oldstatus ) & dq6 ); wait_reps++) { /* Latency issues. Drop the lock, wait a while and retry */ cfi_spin_unlock(chip->mutex); cfi_udelay(1); cfi_spin_lock(chip->mutex); oldstatus = cfi_read(map, adr); status = cfi_read(map, adr); DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", __func__, oldstatus, status ); } oldstatus = cfi_read(map, adr); status = cfi_read(map, adr); DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", __func__, oldstatus, status ); } prev_oldstatus = oldstatus; prev_status = status; oldstatus = cfi_read(map, adr); status = cfi_read(map, adr); DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", __func__, oldstatus, status ); ones = CMD( (__u8)~0 ); if ( oldstatus == ones && status == ones ) { /* success - do nothing */ goto erase_done; } if ( ta ) { int dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1; if ( status & dq5mask ) { /* dq5 asserted - decode interleave chips */ printk( KERN_WARNING "MTD %s(): FLASH internal timeout: 0x%.8x\n", __func__, status & dq5mask ); } else { printk( KERN_WARNING "MTD %s(): Software timed out during write.\n", __func__ ); } goto erase_failed; } printk(KERN_WARNING "MTD %s(): Wacky! Unable to decode failure status\n", __func__ ); printk(KERN_WARNING "MTD %s(): 0x%.8lx(0x%.8x): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", __func__, adr, ones, prev_oldstatus, prev_status, oldstatus, status); erase_failed: ret = -EIO; /* reset on all failures. */ cfi_write( map, CMD(0xF0), chip->start ); erase_done: DISABLE_VPP(map); chip->state = FL_READY; wake_up(&chip->wq); cfi_spin_unlock(chip->mutex); return ret;}static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr){ unsigned int oldstatus, status, prev_oldstatus, prev_status; unsigned int dq6; unsigned long timeo = jiffies + HZ; struct cfi_private *cfi = map->fldrv_priv; DECLARE_WAITQUEUE(wait, current); int ret = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -