📄 cfi_cmdset_0002_v4.c
字号:
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. */ dq6 = CMD(1<<6); dq5 = CMD(1<<5); timeo = jiffies + HZ; /* setting timeout to 1s for safety */ oldstatus = cfi_read(map, adr); status = cfi_read(map, adr); /* * some toshiba flash acts weird with the toggle bits, so * we check the datum as well */ while( (status != datum || ((status & dq6) != (oldstatus & dq6) && (status & dq5) != dq5)) && !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 ); } if( (status & dq6) != (oldstatus & dq6) ) { /* The erasing didn't stop?? */ if( (status & dq5) == dq5 ) { /* When DQ5 raises, we must check once again if DQ6 is toggling. If not, the erase has been completed OK. If not, reset chip. */ oldstatus = cfi_read(map, adr); status = cfi_read(map, adr); if ( (oldstatus & 0x00FF) == (status & 0x00FF) ) { printk(KERN_WARNING "Warning: DQ5 raised while program operation was in progress, however operation completed OK\n" ); } else { /* DQ5 is active so we can do a reset and stop the erase */ cfi_write(map, CMD(0xF0), chip->start); printk(KERN_WARNING "Internal flash device timeout occurred or write operation was performed while flash was programming.\n" ); } } else { printk(KERN_WARNING "Waiting for write to complete timed out in do_write_oneword."); chip->state = FL_READY; wake_up(&chip->wq); cfi_spin_unlock(chip->mutex); DISABLE_VPP(map); ret = -EIO; } } DISABLE_VPP(map); chip->state = FL_READY; wake_up(&chip->wq); cfi_spin_unlock(chip->mutex); return ret;}static inline int do_write_buffer(struct map_info *map, struct flchip *chip, unsigned long adr, const u_char *buf, int len){ unsigned long timeo = jiffies + HZ; unsigned int status; unsigned int dq7, dq5, dq1; struct cfi_private *cfi = map->fldrv_priv; DECLARE_WAITQUEUE(wait, current); int ret = 0; int z; __u32 datum = 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); timeo = jiffies + HZ; goto retry; } adr += chip->start; ENABLE_VPP(map); /* write buffers algorithm taken from Am29LV641MH/L manual */ cfi_send_gen_cmd(0xAA, 0xaaa, chip->start, map, cfi, 1, NULL); cfi_send_gen_cmd(0x55, 0x555, chip->start, map, cfi, 1, NULL); cfi_write(map, CMD(0x25), adr); chip->state = FL_WRITING_TO_BUFFER; cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), adr); /* word count */ /* Write data */ for (z = 0; z < len; z += CFIDEV_BUSWIDTH) { /* if (cfi_buswidth_is_2()) map->write16 (map, datum = *((__u16*)buf)++, adr+z); else if (cfi_buswidth_is_4()) map->write32 (map, datum = *((__u32*)buf)++, adr+z); */ cfi_write(map,datum=*((unsigned char*)buf)++, adr+z); } /* start program */ cfi_write(map, CMD(0x29), adr); chip->state = FL_WRITING; cfi_spin_unlock(chip->mutex); cfi_udelay(chip->buffer_write_time); cfi_spin_lock(chip->mutex); /* use data polling algorithm */ dq1 = CMD(1<<1); dq5 = CMD(1<<5); dq7 = CMD(1<<7); timeo = jiffies + HZ; /* setting timeout to 1s for safety */ z -= CFIDEV_BUSWIDTH; /* go to last written address */ do { status = cfi_read(map, adr+z); if( (dq7 & status) == (dq7 & datum) ) break;/* if( ((dq5 & status) == dq5) || ((dq1 & status) == dq1) ) { status = cfi_read( map, adr+z ); if( (dq7 & status) != (dq7 & datum) ) { ret = -EIO; break; } else break; }*/ if ((dq5 & status) == dq5) { status = cfi_read( map, adr+z ); if( (dq7 & status) != (dq7 & datum) ) { ret = -EIO; break; } else break; }else if((dq1 & status) == dq1){ status = cfi_read( map, adr+z ); if( (dq7 & status) != (dq7 & datum) ) { ret = -EIO; break; } else break; } if (need_resched()) { cfi_spin_unlock(chip->mutex); yield(); cfi_spin_lock(chip->mutex); } else udelay(1); } while( !time_after(jiffies, timeo) ); if( !ret && time_after( jiffies, timeo ) ) { printk(KERN_WARNING "Waiting for write to complete timed out in do_write_buffer."); ret = -EIO; } if( ret == -EIO ) { if( (dq1 & status) == dq1 ) { printk( "Flash write to Buffer aborted @ 0x%lx = 0x%x\n", adr, status ); cfi_send_gen_cmd(0xAA, 0xaaa, chip->start, map, cfi, 1, NULL); cfi_send_gen_cmd(0x55, 0x555, chip->start, map, cfi, 1, NULL); cfi_send_gen_cmd(0xF0, 0xaaa, chip->start, map, cfi, 1, NULL); } else { printk( "Flash write to buffer faileed @ 0x%lx = 0x%x\n", adr, status ); cfi_write(map, CMD(0xF0), chip->start); } } DISABLE_VPP(map); chip->state = FL_READY; wake_up(&chip->wq); cfi_spin_unlock(chip->mutex); return ret;}static int cfi_amdstd_write_words (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[4]; __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 = get_unaligned((__u16*)buf); } else if (cfi_buswidth_is_4()) { datum = get_unaligned((__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[4]; __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 int cfi_amdstd_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; struct cfi_private *cfi = map->fldrv_priv; int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; int ret = 0; int chipnum; unsigned long ofs; int count=0; int retlen2=0; /* code derived from cfi_cmdset_0001.c:cfi_intelext_write_words */ *retlen = 0; if (!len) return 0; chipnum = to >> cfi->chipshift; ofs = to - (chipnum << cfi->chipshift); /* If it's not bus-aligned, do the first word write */ if (ofs & (CFIDEV_BUSWIDTH-1)) { size_t local_len = (-ofs)&(CFIDEV_BUSWIDTH-1); if (local_len > len) local_len = len; ret = cfi_amdstd_write_words(mtd, to, local_len, retlen, buf); if (ret) return ret; ofs += local_len; buf += local_len; len -= local_len; if (ofs >> cfi->chipshift) { chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; } } /* Write buffer is worth it only if more than one word to write... */ while(len > CFIDEV_BUSWIDTH) { /* We must not cross write block boundaries */ int size = wbufsize - (ofs & (wbufsize-1)); if (size > len) size = len & ~(CFIDEV_BUSWIDTH-1); ret = do_write_buffer(map, &cfi->chips[chipnum], ofs, buf, size); if(ret) return ret;/* while(ret&&(count<1000)) { ret = do_write_buffer(map, &cfi->chips[chipnum], ofs, buf, size); printk("count=%d\n",count); count++; }*//* if(ret){ ret = cfi_amdstd_write_words(mtd, ofs, size, &retlen2, buf); if(ret) printk("error in write words\n"); ofs += size; buf += size; (*retlen) += retlen2; len -= size; }else{*/ ofs += size; buf += size; (*retlen) += size; len -= size;// } if (ofs >> cfi->chipshift) { chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; } } /* ... and write the remaining bytes */ if (len > 0) { size_t local_retlen; ret = cfi_amdstd_write_words(mtd, ofs + (chipnum << cfi->chipshift), len, &local_retlen, buf); if (ret) return ret; (*retlen) += local_retlen; } return 0;}static inline int do_erase_chip(struct map_info *map, struct flchip *chip){ unsigned int oldstatus, status; unsigned int dq6, dq5; unsigned long timeo = jiffies + HZ; unsigned int adr; struct cfi_private *cfi = map->fldrv_priv; DECLARE_WAITQUEUE(wait, current); 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; /* 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;#ifdef CONFIG_ARCH_EM86XX /* We need extra delay here since reading bit5/bit6 operation may froze the chip for some times */ cfi_spin_unlock(chip->mutex); schedule_timeout(((1<<cfi->cfiq->ChipEraseTimeoutTyp) * HZ) / 1000); cfi_spin_lock(chip->mutex);#endif /* Wait for the end of programing/erasure by using the toggle method. * As long as there is a programming procedure going on, bit 6 of the last * written byte is toggling it's state with each consectuve 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. */ dq6 = CMD(1<<6); dq5 = CMD(1<<5); oldstatus = cfi_read(map, adr); status = cfi_read(map, adr); while( ((status & dq6) != (oldstatus & dq6)) && ((status & dq5) != dq5) && !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();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -