📄 cfi_cmdset_0002.c
字号:
ret = do_write_oneword(map, &cfi->chips[chipnum], ofs, datum); if (ret) return ret; ofs += CFIDEV_BUSWIDTH; buf += CFIDEV_BUSWIDTH; (*retlen) += CFIDEV_BUSWIDTH; len -= CFIDEV_BUSWIDTH; if (ofs >> cfi->chipshift) { chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; chipstart = cfi->chips[chipnum].start; } } /* Write the trailing bytes if any */ if (len & (CFIDEV_BUSWIDTH-1)) { int i = 0, n = 0; u_char tmp_buf[8]; cfi_word datum; retry1: cfi_spin_lock(cfi->chips[chipnum].mutex); if (cfi->chips[chipnum].state != FL_READY) {#if 0 printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);#endif set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&cfi->chips[chipnum].wq, &wait); cfi_spin_unlock(cfi->chips[chipnum].mutex); schedule(); remove_wait_queue(&cfi->chips[chipnum].wq, &wait);#if 0 if(signal_pending(current)) return -EINTR;#endif goto retry1; } map_copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); cfi_spin_unlock(cfi->chips[chipnum].mutex); 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;#ifdef CFI_WORD_64 } else if (cfi_buswidth_is_8()) { datum = *(__u64*)tmp_buf;#endif } else { printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", __func__, CFIDEV_BUSWIDTH); return -EINVAL; } ret = do_write_oneword(map, &cfi->chips[chipnum], ofs, datum); if (ret) return ret; (*retlen) += n; } return 0;}/* * FIXME: interleaved mode not tested, and probably not supported! */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; unsigned long timeo = jiffies + HZ; /* see comments in do_write_oneword() regarding uWriteTimeo. */ static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; int ret = -EIO; unsigned long cmd_adr; int z, bytes, words; cfi_word datum; adr += chip->start; cmd_adr = adr; cfi_spin_lock(chip->mutex); ret = get_chip(map, chip, adr, FL_WRITING); if (ret) { cfi_spin_unlock(chip->mutex); return ret; } 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;#ifdef CFI_WORD_64 } else if (cfi_buswidth_is_8()) { datum = *(__u64*)buf;#endif } else { printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", __func__, CFIDEV_BUSWIDTH); return -EINVAL; } DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n", __func__, adr, datum ); 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(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); /* Write Buffer Load */ cfi_write(map, CMD(0x25), cmd_adr); chip->state = FL_WRITING_TO_BUFFER; /* Write length of data to come */ bytes = len & (CFIDEV_BUSWIDTH-1); words = len / CFIDEV_BUSWIDTH; cfi_write(map, CMD(words - !bytes), cmd_adr ); /* Write data */ z = 0; while(z < words * CFIDEV_BUSWIDTH) { if (cfi_buswidth_is_1()) { datum = *((__u8*)buf); map_write8 (map, *((__u8*)buf)++, adr+z); } else if (cfi_buswidth_is_2()) { datum = *((__u16*)buf); map_write16 (map, *((__u16*)buf)++, adr+z); } else if (cfi_buswidth_is_4()) { datum = *((__u32*)buf); map_write32 (map, *((__u32*)buf)++, adr+z);#ifdef CFI_WORD_64 } else if (cfi_buswidth_is_8()) { datum = *((__u64*)buf); map_write64 (map, *((__u64*)buf)++, adr+z);#endif } else { printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", __func__, CFIDEV_BUSWIDTH); ret = -EINVAL; goto op_failed; } z += CFIDEV_BUSWIDTH; } if (bytes) { int i = 0, n = 0; u_char tmp_buf[8], *tmp_p = tmp_buf; while (bytes--) tmp_buf[i++] = buf[n++]; while (i < CFIDEV_BUSWIDTH) tmp_buf[i++] = 0xff; if (cfi_buswidth_is_2()) { datum = *((__u16*)tmp_p); map_write16 (map, *((__u16*)tmp_p)++, adr+z); } else if (cfi_buswidth_is_4()) { datum = *((__u32*)tmp_p); map_write32 (map, *((__u32*)tmp_p)++, adr+z);#ifdef CFI_WORD_64 } else if (cfi_buswidth_is_8()) { datum = *((__u64*)tmp_p); map_write64 (map, *((__u64*)tmp_p)++, adr+z);#endif } else { printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", __func__, CFIDEV_BUSWIDTH); ret = -EINVAL; goto op_failed; } } else if (words > 0) { z -= CFIDEV_BUSWIDTH; } adr += z; /* Write Buffer Program Confirm: GO GO GO */ cfi_write(map, CMD(0x29), cmd_adr); chip->state = FL_WRITING; cfi_spin_unlock(chip->mutex); cfi_udelay(chip->buffer_write_time); cfi_spin_lock(chip->mutex); timeo = jiffies + uWriteTimeout; for (;;) { if (chip->state != FL_WRITING) { /* Someone's suspended the write. Sleep */ DECLARE_WAITQUEUE(wait, current); 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 / 2); /* FIXME */ cfi_spin_lock(chip->mutex); continue; } if (chip_ready(map, adr)) goto op_done; if( time_after(jiffies, timeo)) break; /* Latency issues. Drop the lock, wait a while and retry */ cfi_spin_unlock(chip->mutex); cfi_udelay(1); cfi_spin_lock(chip->mutex); } printk(KERN_WARNING "MTD %s(): software timeout\n", __func__ ); op_failed: /* reset on all failures. */ cfi_write( map, CMD(0xF0), chip->start ); /* FIXME - should have reset delay before continuing */ ret = -EIO; op_done: chip->state = FL_READY; put_chip(map, chip, adr); cfi_spin_unlock(chip->mutex); return ret;}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; *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) { /* We must not cross write block boundaries */ int size = wbufsize - (ofs & (wbufsize-1)); if (size > len) size = len; ret = do_write_buffer(map, &cfi->chips[chipnum], ofs, buf, size); if (ret) return ret; ofs += size; buf += size; (*retlen) += size; len -= size; if (ofs >> cfi->chipshift) { chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; } } return 0;}/* * Handle devices with one erase region, that only implement * the chip erase command. */static inline int do_erase_chip(struct map_info *map, struct flchip *chip){ struct cfi_private *cfi = map->fldrv_priv; unsigned long timeo = jiffies + HZ; unsigned long int adr; DECLARE_WAITQUEUE(wait, current); int ret = 0; adr = cfi->addr_unlock1; cfi_spin_lock(chip->mutex); ret = get_chip(map, chip, adr, FL_WRITING); if (ret) { cfi_spin_unlock(chip->mutex); return ret; } DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", __func__, chip->start ); 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); chip->state = FL_ERASING; chip->erase_suspended = 0; chip->in_progress_block_addr = adr; cfi_spin_unlock(chip->mutex); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout((chip->erase_time*HZ)/(2*1000)); cfi_spin_lock(chip->mutex); timeo = jiffies + (HZ*20); for (;;) { 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); schedule(); remove_wait_queue(&chip->wq, &wait); cfi_spin_lock(chip->mutex); continue; } if (chip->erase_suspended) { /* This erase was suspended and resumed. Adjust the timeout */ timeo = jiffies + (HZ*20); /* FIXME */ chip->erase_suspended = 0; } if (chip_ready_erase(map, adr)) goto op_done; if (time_after(jiffies, timeo)) break; /* Latency issues. Drop the lock, wait a while and retry */ cfi_spin_unlock(chip->mutex); set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(1); cfi_spin_lock(chip->mutex); } printk(KERN_WARNING "MTD %s(): software timeout\n", __func__ ); /* reset on all failures. */ cfi_write( map, CMD(0xF0), chip->start ); /* FIXME - should have reset delay before continuing */ ret = -EIO; op_done: chip->state = FL_READY; put_chip(map, chip, adr); cfi_spin_unlock(chip->mutex); return ret;}typedef int (*frob_t)(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk);static int cfi_amdstd_varsize_frob(struct mtd_info *mtd, frob_t frob, loff_t ofs, size_t len, void *thunk){ 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; 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 (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset) i++; /* As before, drop back one to point at the region in which the address actually falls */ i--;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -