📄 cfi_cmdset_0001.c
字号:
} if (z > 1) chip->word_write_time++; /* Done and happy. */ DISABLE_VPP(map); chip->state = FL_STATUS; /* check for lock bit */ if (status & CMD(0x02)) { /* clear status */ cfi_write(map, CMD(0x50), adr); /* put back into read status register mode */ cfi_write(map, CMD(0x70), adr); wake_up(&chip->wq); spin_unlock_bh(chip->mutex); return -EROFS; } wake_up(&chip->wq); spin_unlock_bh(chip->mutex); return 0;}static int cfi_intelext_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; *retlen = 0; if (!len) return 0; chipnum = to >> cfi->chipshift; ofs = to - (chipnum << cfi->chipshift); /* 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 gap = ofs - bus_ofs; int i = 0, n = 0; u_char tmp_buf[4]; __u32 datum; while (gap--) tmp_buf[i++] = 0xff; while (len && i < CFIDEV_BUSWIDTH) tmp_buf[i++] = buf[n++], len--; while (i < CFIDEV_BUSWIDTH) tmp_buf[i++] = 0xff; 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); if (ret) return ret; ofs += n; buf += n; (*retlen) += n; if (ofs >> cfi->chipshift) { chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; } } 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); 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; } } if (len & (CFIDEV_BUSWIDTH-1)) { int i = 0, n = 0; u_char tmp_buf[4]; __u32 datum; while (len--) tmp_buf[i++] = buf[n++]; while (i < CFIDEV_BUSWIDTH) tmp_buf[i++] = 0xff; 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); if (ret) return ret; (*retlen) += n; } return 0;}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; __u32 status, status_OK; unsigned long cmd_adr, timeo; DECLARE_WAITQUEUE(wait, current); int wbufsize, z; wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; adr += chip->start; cmd_adr = adr & ~(wbufsize-1); /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); timeo = jiffies + HZ; retry: spin_lock_bh(chip->mutex); /* Check that the chip's ready to talk to us. * Later, we can actually think about interrupting it * if it's in FL_ERASING state. * Not just yet, though. */ switch (chip->state) { case FL_READY: break; case FL_CFI_QUERY: case FL_JEDEC_QUERY: cfi_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; case FL_STATUS: status = cfi_read(map, cmd_adr); if ((status & status_OK) == status_OK) break; /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { spin_unlock_bh(chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in buffer write\n"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock_bh(chip->mutex); cfi_udelay(1); goto retry; default: /* Stick ourselves on a wait queue to be woken when someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); spin_unlock_bh(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; goto retry; } ENABLE_VPP(map); cfi_write(map, CMD(0xe8), cmd_adr); chip->state = FL_WRITING_TO_BUFFER; z = 0; for (;;) { status = cfi_read(map, cmd_adr); if ((status & status_OK) == status_OK) break; spin_unlock_bh(chip->mutex); cfi_udelay(1); spin_lock_bh(chip->mutex); if (++z > 20) { /* Argh. Not ready for write to buffer */ cfi_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; DISABLE_VPP(map); spin_unlock_bh(chip->mutex); printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %x, status = %x\n", status, cfi_read(map, cmd_adr)); return -EIO; } } /* Write length of data to come */ cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr ); /* Write data */ for (z = 0; z < len; z += CFIDEV_BUSWIDTH) { if (cfi_buswidth_is_1()) { map->write8 (map, *((__u8*)buf)++, adr+z); } else if (cfi_buswidth_is_2()) { map->write16 (map, *((__u16*)buf)++, adr+z); } else if (cfi_buswidth_is_4()) { map->write32 (map, *((__u32*)buf)++, adr+z); } else { DISABLE_VPP(map); return -EINVAL; } } /* GO GO GO */ cfi_write(map, CMD(0xd0), cmd_adr); chip->state = FL_WRITING; spin_unlock_bh(chip->mutex); cfi_udelay(chip->buffer_write_time); spin_lock_bh(chip->mutex); timeo = jiffies + (HZ/2); z = 0; for (;;) { if (chip->state != FL_WRITING) { /* Someone's suspended the write. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); spin_unlock_bh(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ / 2); /* FIXME */ spin_lock_bh(chip->mutex); continue; } status = cfi_read(map, cmd_adr); if ((status & status_OK) == status_OK) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { chip->state = FL_STATUS; DISABLE_VPP(map); spin_unlock_bh(chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock_bh(chip->mutex); cfi_udelay(1); z++; spin_lock_bh(chip->mutex); } if (!z) { chip->buffer_write_time--; if (!chip->buffer_write_time) chip->buffer_write_time++; } if (z > 1) chip->buffer_write_time++; /* Done and happy. */ DISABLE_VPP(map); chip->state = FL_STATUS; /* check for lock bit */ if (status & CMD(0x02)) { /* clear status */ cfi_write(map, CMD(0x50), cmd_adr); /* put back into read status register mode */ cfi_write(map, CMD(0x70), adr); wake_up(&chip->wq); spin_unlock_bh(chip->mutex); return -EROFS; } wake_up(&chip->wq); spin_unlock_bh(chip->mutex); return 0;}static int cfi_intelext_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_intelext_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; 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_intelext_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_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr){ struct cfi_private *cfi = map->fldrv_priv; __u32 status, status_OK; unsigned long timeo; int retries = 3; DECLARE_WAITQUEUE(wait, current); int ret = 0; adr += chip->start; /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); timeo = jiffies + HZ;retry: spin_lock_bh(chip->mutex); /* Check that the chip's ready to talk to us. */ switch (chip->state) { case FL_CFI_QUERY: case FL_JEDEC_QUERY: case FL_READY: cfi_write(map, CMD(0x70), adr); chip->state = FL_STATUS; case FL_STATUS: status = cfi_read(map, adr); if ((status & status_OK) == status_OK) break; /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { spin_unlock_bh(chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in erase\n"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock_bh(chip->mutex); cfi_udelay(1); goto retry; default: /* Stick ourselves on a wait queue to be woken when someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); spin_unlock_bh(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; goto retry; } ENABLE_VPP(map); /* Clear the status register first */ cfi_write(map, CMD(0x50), adr); /* Now erase */ cfi_write(map, CMD(0x20), adr); cfi_write(map, CMD(0xD0), adr); chip->state = FL_ERASING; spin_unlock_bh(chip->mutex); schedule_timeout(HZ); spin_lock_bh(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ 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); spin_unlock_bh(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ*20); /* FIXME */ spin_lock_bh(chip->mutex); continue; } status = cfi_read(map, adr); if ((status & status_OK) == status_OK) break; /* OK Still waiting */ if (time_after(jiffies, timeo)) { cfi_write(map, CMD(0x70), adr); chip->state = FL_STATUS; printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); DISABLE_VPP(map); spin_unlock_bh(chip->mutex); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock_bh(chip->mutex); cfi_udelay(1); spin_lock_bh(chip->mutex); } DISABLE_VPP(map); ret = 0; /* We've broken this before. It doesn't hurt to be safe */ cfi_write(map, CMD(0x70), adr); chip->state = FL_STATUS; status = cfi_read(map, adr); /* check for lock bit */ if (status & CMD(0x3a)) { unsigned char chipstatus = status; if (status != CMD(status & 0xff)) { int i; for (i = 1; i<CFIDEV_INTERLEAVE; i++) { chipstatus |= status >> (cfi->device_type * 8); } printk(KERN_WARNING "Status is not identical for all chips: 0x%x. Merging to give 0x%02x\n", status, chipstatus); } /* Reset the error bits */ cfi_write(map, CMD(0x50), adr); cfi_write(map, CMD(0x70), adr); if ((chipstatus & 0x30) == 0x30) { printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", status); ret = -EIO; } else if (chipstatus & 0x02) { /* Protection bit set */ ret = -EROFS; } else if (chipstatus & 0x8) { /* Voltage */ printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", status); ret = -EIO; } else if (chipstatus & 0x20) { if (retries--) { printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, status); timeo = jiffies + HZ; chip->state = FL_STATUS; spin_unlock_bh(chip->mutex); goto retry; } printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, status); ret = -EIO; } } wake_up(&chip->wq); spin_unlock_bh(chip->mutex); return ret;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -