📄 cfi_cmdset_0002.c
字号:
if (!(mode == FL_READY || mode == FL_POINT || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)) || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)))) goto sleep; /* We could check to see if we're trying to access the sector * that is currently being erased. However, no user will try * anything like that so we just wait for the timeout. */ /* Erase suspend */ /* It's harmless to issue the Erase-Suspend and Erase-Resume * commands when the erase algorithm isn't in progress. */ cfi_write(map, CMD(0xB0), chip->in_progress_block_addr); chip->oldstate = FL_ERASING; chip->state = FL_ERASE_SUSPENDING; chip->erase_suspended = 1; for (;;) { if (chip_ready(map, adr)) break; if (time_after(jiffies, timeo)) { /* Should have suspended the erase by now. * Send an Erase-Resume command as either * there was an error (so leave the erase * routine to recover from it) or we trying to * use the erase-in-progress sector. */ cfi_write(map, CMD(0x30), chip->in_progress_block_addr); chip->state = FL_ERASING; chip->oldstate = FL_READY; printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__); return -EIO; } cfi_spin_unlock(chip->mutex); cfi_udelay(1); cfi_spin_lock(chip->mutex); /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. So we can just loop here. */ } chip->state = FL_READY; return 0; case FL_POINT: /* Only if there's no operation suspended... */ if (mode == FL_READY && chip->oldstate == FL_READY) return 0; default: 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); goto resettime; }}static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr){ struct cfi_private *cfi = map->fldrv_priv; switch(chip->oldstate) { case FL_ERASING: chip->state = chip->oldstate; cfi_write(map, CMD(0x30), chip->in_progress_block_addr); chip->oldstate = FL_READY; chip->state = FL_ERASING; break; case FL_READY: case FL_STATUS: /* We should really make set_vpp() count, rather than doing this */ DISABLE_VPP(map); break; default: printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate); } wake_up(&chip->wq);}static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf){ unsigned long cmd_addr; struct cfi_private *cfi = map->fldrv_priv; int ret; adr += chip->start; /* Ensure cmd read/writes are aligned. */ cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); cfi_spin_lock(chip->mutex); ret = get_chip(map, chip, cmd_addr, FL_READY); if (ret) { cfi_spin_unlock(chip->mutex); return ret; } if (chip->state != FL_POINT && chip->state != FL_READY) { cfi_write(map, CMD(0xf0), cmd_addr); chip->state = FL_READY; } map_copy_from(map, buf, adr, len); put_chip(map, chip, cmd_addr); cfi_spin_unlock(chip->mutex); return 0;}static int cfi_amdstd_read (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; unsigned long ofs; int chipnum; int ret = 0; /* ofs: offset within the first chip that the first read should start */ chipnum = (from >> cfi->chipshift); ofs = from - (chipnum << cfi->chipshift); *retlen = 0; while (len) { unsigned long thislen; if (chipnum >= cfi->numchips) break; if ((len + ofs -1) >> cfi->chipshift) thislen = (1<<cfi->chipshift) - ofs; else thislen = len; ret = do_read_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); if (ret) break; *retlen += thislen; len -= thislen; buf += thislen; ofs = 0; chipnum++; } return ret;}static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf){ DECLARE_WAITQUEUE(wait, current); unsigned long timeo = jiffies + HZ; struct cfi_private *cfi = map->fldrv_priv; retry: cfi_spin_lock(chip->mutex); if (chip->state != FL_READY){#if 0 printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state);#endif 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; } adr += chip->start; chip->state = FL_READY; /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); map_copy_from(map, buf, adr, len); /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); wake_up(&chip->wq); cfi_spin_unlock(chip->mutex); return 0;}static int cfi_amdstd_secsi_read (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; unsigned long ofs; int chipnum; int ret = 0; /* ofs: offset within the first chip that the first read should start */ /* 8 secsi bytes per chip */ chipnum=from>>3; ofs=from & 7; *retlen = 0; while (len) { unsigned long thislen; if (chipnum >= cfi->numchips) break; if ((len + ofs -1) >> 3) thislen = (1<<3) - ofs; else thislen = len; ret = do_read_secsi_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); if (ret) break; *retlen += thislen; len -= thislen; buf += thislen; ofs = 0; chipnum++; } return ret;}static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum){ struct cfi_private *cfi = map->fldrv_priv; unsigned long timeo = jiffies + HZ; /* * We use a 1ms + 1 jiffies generic timeout for writes (most devices * have a max write time of a few hundreds usec). However, we should * use the maximum timeout value given by the chip at probe time * instead. Unfortunately, struct flchip does have a field for * maximum timeout, only for typical which can be far too short * depending of the conditions. The ' + 1' is to avoid having a * timeout of 0 jiffies if HZ is smaller than 1000. */ unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; int ret = 0; adr += chip->start; 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(): WRITE 0x%.8lx(0x%.8x)\n", __func__, adr, datum ); /* * Check for a NOP for the case when the datum to write is already * present - it saves time and works around buggy chips that corrupt * data at other locations when 0xff is written to a location that * already contains 0xff. */ if (cfi_read(map, adr) == datum) { DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): NOP\n", __func__); goto op_done; } ENABLE_VPP(map); /* * The CFI_DEVICETYPE_X8 argument is needed even when * cfi->device_type != CFI_DEVICETYPE_X8. The addresses for * command sequences don't scale even when the device is * wider. This is the case for many of the cfi_send_gen_cmd() * below. I'm not sure, however, why some use * cfi->device_type. */ 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); chip->state = FL_WRITING; cfi_spin_unlock(chip->mutex); cfi_udelay(chip->word_write_time); cfi_spin_lock(chip->mutex); /* See comment above for timeout value. */ 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__ ); /* 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_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; DECLARE_WAITQUEUE(wait, current); *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]; cfi_word datum; retry: 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 retry; } map_copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); cfi_spin_unlock(cfi->chips[chipnum].mutex); while (len && i < CFIDEV_BUSWIDTH) { tmp_buf[i++] = buf[n++]; len--; } /* already know that buswidth > 1 */ 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], bus_ofs, datum); if (ret) return ret; ofs += n; buf += n; (*retlen) += n; if (ofs >> cfi->chipshift) { chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; } } /* We are now aligned, write as much as possible */ while(len >= CFIDEV_BUSWIDTH) { cfi_word 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;#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; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -