📄 cfi_cmdset_0002.c
字号:
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]; 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, 0); 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; cfi_word oldstatus, status, prev_oldstatus, prev_status; cfi_word dq6 = CMD(1<<6); /* see comments in do_write_oneword() regarding uWriteTimeo. */ static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; int ret = -EIO; int ta = 0; unsigned long cmd_adr; int z, bytes, words; cfi_word datum; DECLARE_RETRY_CMD_CNT(); 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; } RETRY_CMD_LABEL; 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; } 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 ); /* See comments in do_write_oneword() about checking status */ if ( (((status ^ oldstatus) & dq6) == 0) || ( ta = 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); } /* See comments in do_write_oneword() about "two more checks" */ 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 op_done; } if ( ta ) { /* Only check dq5 on the chips that are still toggling. */ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1; if ( status & dq5mask ) { /* dq5 asserted - decode interleave chips */ printk( KERN_WARNING "MTD %s(): FLASH internal timeout: 0x%.8x 0x%.8x 0x%8x\n", __func__, status & dq5mask, status, datum ); } else { printk( KERN_WARNING "MTD %s(): Software timed out during write.\n", __func__ ); } goto op_failed; } HANDLE_WACKY_STATE(); op_failed: /* reset on all failures. */ cfi_write( map, CMD(0xF0), chip->start ); /* FIXME - should have reset delay before continuing */ CHECK_RETRIES(); 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; cfi_word oldstatus, status, prev_oldstatus, prev_status; cfi_word dq6 = CMD(1<<6); unsigned long timeo = jiffies + HZ; unsigned long int adr; DECLARE_WAITQUEUE(wait, current); int ret = 0; int ta = 0; cfi_word datum = 0; DECLARE_RETRY_CMD_CNT(); 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; } RETRY_CMD_LABEL; 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; 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; } 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 ( (((status ^ oldstatus) & dq6) == 0) || ( ta = 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); } 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 ( cfi_buswidth_is_1() ) { datum = (__u8)~0; } else if ( cfi_buswidth_is_2() ) { datum = (__u16)~0; } else if ( cfi_buswidth_is_4() ) { datum = (__u32)~0;#ifdef CFI_WORD_64 } else if ( cfi_buswidth_is_8() ) { datum = (__u64)~0;#endif } else { printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", __func__, CFIDEV_BUSWIDTH); goto op_failed; } if ( oldstatus == datum && status == datum ) { /* success - do nothing */ goto op_done; } if ( ta ) { /* Only check dq5 on the chips that are still toggling. */ cfi_word 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 op_failed; } HANDLE_WACKY_STATE(); op_failed: /* reset on all failures. */ cfi_write( map, CMD(0xF0), chip->start ); /* FIXME - should have reset delay before continuing */ CHECK_RETRIES(); 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -