📄 cfi_cmdset_0001.c
字号:
* another operation suspended (imagine what happens * when one chip was already done with the current * operation while another chip suspended it, then * we resume the whole thing at once). Yes, it * can happen! */ usec -= done; map_write(map, CMD(0xb0), adr); map_write(map, CMD(0x70), adr); suspended = xip_currtime(); do { if (xip_elapsed_since(suspended) > 100000) { /* * The chip doesn't want to suspend * after waiting for 100 msecs. * This is a critical error but there * is not much we can do here. */ return -EIO; } status = map_read(map, adr); } while (!map_word_andequal(map, status, OK, OK)); /* Suspend succeeded */ oldstate = chip->state; if (oldstate == FL_ERASING) { if (!map_word_bitsset(map, status, CMD(0x40))) break; newstate = FL_XIP_WHILE_ERASING; chip->erase_suspended = 1; } else { if (!map_word_bitsset(map, status, CMD(0x04))) break; newstate = FL_XIP_WHILE_WRITING; chip->write_suspended = 1; } chip->state = newstate; map_write(map, CMD(0xff), adr); (void) map_read(map, adr); asm volatile (".rep 8; nop; .endr"); local_irq_enable(); spin_unlock(chip->mutex); asm volatile (".rep 8; nop; .endr"); cond_resched(); /* * We're back. However someone else might have * decided to go write to the chip if we are in * a suspended erase state. If so let's wait * until it's done. */ spin_lock(chip->mutex); while (chip->state != newstate) { DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); spin_lock(chip->mutex); } /* Disallow XIP again */ local_irq_disable(); /* Resume the write or erase operation */ map_write(map, CMD(0xd0), adr); map_write(map, CMD(0x70), adr); chip->state = oldstate; start = xip_currtime(); } else if (usec >= 1000000/HZ) { /* * Try to save on CPU power when waiting delay * is at least a system timer tick period. * No need to be extremely accurate here. */ xip_cpu_idle(); } status = map_read(map, adr); done = xip_elapsed_since(start); } while (!map_word_andequal(map, status, OK, OK) && done < usec); return (done >= usec) ? -ETIME : 0;}/* * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while * the flash is actively programming or erasing since we have to poll for * the operation to complete anyway. We can't do that in a generic way with * a XIP setup so do it before the actual flash operation in this case * and stub it out from INVAL_CACHE_AND_WAIT. */#define XIP_INVAL_CACHED_RANGE(map, from, size) \ INVALIDATE_CACHED_RANGE(map, from, size)#define INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, inval_adr, inval_len, usec) \ xip_wait_for_operation(map, chip, cmd_adr, usec)#else#define xip_disable(map, chip, adr)#define xip_enable(map, chip, adr)#define XIP_INVAL_CACHED_RANGE(x...)#define INVAL_CACHE_AND_WAIT inval_cache_and_wait_for_operationstatic int inval_cache_and_wait_for_operation( struct map_info *map, struct flchip *chip, unsigned long cmd_adr, unsigned long inval_adr, int inval_len, unsigned int chip_op_time){ struct cfi_private *cfi = map->fldrv_priv; map_word status, status_OK = CMD(0x80); int chip_state = chip->state; unsigned int timeo, sleep_time; spin_unlock(chip->mutex); if (inval_len) INVALIDATE_CACHED_RANGE(map, inval_adr, inval_len); spin_lock(chip->mutex); /* set our timeout to 8 times the expected delay */ timeo = chip_op_time * 8; if (!timeo) timeo = 500000; sleep_time = chip_op_time / 2; for (;;) { status = map_read(map, cmd_adr); if (map_word_andequal(map, status, status_OK, status_OK)) break; if (!timeo) { map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; return -ETIME; } /* OK Still waiting. Drop the lock, wait a while and retry. */ spin_unlock(chip->mutex); if (sleep_time >= 1000000/HZ) { /* * Half of the normal delay still remaining * can be performed with a sleeping delay instead * of busy waiting. */ msleep(sleep_time/1000); timeo -= sleep_time; sleep_time = 1000000/HZ; } else { udelay(1); cond_resched(); timeo--; } spin_lock(chip->mutex); while (chip->state != chip_state) { /* Someone's suspended the operation: sleep */ DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); spin_lock(chip->mutex); } } /* Done and happy. */ chip->state = FL_STATUS; return 0;}#endif#define WAIT_TIMEOUT(map, chip, adr, udelay) \ INVAL_CACHE_AND_WAIT(map, chip, adr, 0, 0, udelay);static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len){ unsigned long cmd_addr; struct cfi_private *cfi = map->fldrv_priv; int ret = 0; adr += chip->start; /* Ensure cmd read/writes are aligned. */ cmd_addr = adr & ~(map_bankwidth(map)-1); spin_lock(chip->mutex); ret = get_chip(map, chip, cmd_addr, FL_POINT); if (!ret) { if (chip->state != FL_POINT && chip->state != FL_READY) map_write(map, CMD(0xff), cmd_addr); chip->state = FL_POINT; chip->ref_point_counter++; } spin_unlock(chip->mutex); return ret;}static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; unsigned long ofs; int chipnum; int ret = 0; if (!map->virt || (from + len > mtd->size)) return -EINVAL; *mtdbuf = (void *)map->virt + from; *retlen = 0; /* Now lock the chip(s) to POINT state */ /* ofs: offset within the first chip that the first read should start */ chipnum = (from >> cfi->chipshift); ofs = from - (chipnum << cfi->chipshift); 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_point_onechip(map, &cfi->chips[chipnum], ofs, thislen); if (ret) break; *retlen += thislen; len -= thislen; ofs = 0; chipnum++; } return 0;}static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; unsigned long ofs; int chipnum; /* Now unlock the chip(s) POINT state */ /* ofs: offset within the first chip that the first read should start */ chipnum = (from >> cfi->chipshift); ofs = from - (chipnum << cfi->chipshift); while (len) { unsigned long thislen; struct flchip *chip; chip = &cfi->chips[chipnum]; if (chipnum >= cfi->numchips) break; if ((len + ofs -1) >> cfi->chipshift) thislen = (1<<cfi->chipshift) - ofs; else thislen = len; spin_lock(chip->mutex); if (chip->state == FL_POINT) { chip->ref_point_counter--; if(chip->ref_point_counter == 0) chip->state = FL_READY; } else printk(KERN_ERR "%s: Warning: unpoint called on non pointed region\n", map->name); /* Should this give an error? */ put_chip(map, chip, chip->start); spin_unlock(chip->mutex); len -= thislen; ofs = 0; chipnum++; }}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 & ~(map_bankwidth(map)-1); spin_lock(chip->mutex); ret = get_chip(map, chip, cmd_addr, FL_READY); if (ret) { spin_unlock(chip->mutex); return ret; } if (chip->state != FL_POINT && chip->state != FL_READY) { map_write(map, CMD(0xff), cmd_addr); chip->state = FL_READY; } map_copy_from(map, buf, adr, len); put_chip(map, chip, cmd_addr); spin_unlock(chip->mutex); return 0;}static int cfi_intelext_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 int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum, int mode){ struct cfi_private *cfi = map->fldrv_priv; map_word status, write_cmd; int ret=0; adr += chip->start; switch (mode) { case FL_WRITING: write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0x40) : CMD(0x41); break; case FL_OTP_WRITE: write_cmd = CMD(0xc0); break; default: return -EINVAL; } spin_lock(chip->mutex); ret = get_chip(map, chip, adr, mode); if (ret) { spin_unlock(chip->mutex); return ret; } XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map)); ENABLE_VPP(map); xip_disable(map, chip, adr); map_write(map, write_cmd, adr); map_write(map, datum, adr); chip->state = mode; ret = INVAL_CACHE_AND_WAIT(map, chip, adr, adr, map_bankwidth(map), chip->word_write_time); if (ret) { xip_enable(map, chip, adr); printk(KERN_ERR "%s: word write error (status timeout)\n", map->name); goto out; } /* check for errors */ status = map_read(map, adr); if (map_word_bitsset(map, status, CMD(0x1a))) { unsigned long chipstatus = MERGESTATUS(status); /* reset status */ map_write(map, CMD(0x50), adr); map_write(map, CMD(0x70), adr); xip_enable(map, chip, adr); if (chipstatus & 0x02) { ret = -EROFS; } else if (chipstatus & 0x08) { printk(KERN_ERR "%s: word write error (bad VPP)\n", map->name); ret = -EIO; } else { printk(KERN_ERR "%s: word write error (status 0x%lx)\n", map->name, chipstatus); ret = -EINVAL; } goto out; } xip_enable(map, chip, adr); out: put_chip(map, chip, adr); spin_unlock(chip->mutex); return ret;}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 & (map_bankwidth(map)-1)) { unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1); int gap = ofs - bus_ofs; int n; map_word datum; n = min_t(int, len, map_bankwidth(map)-gap); datum = map_word_ff(map); datum = map_word_load_partial(map, datum, buf, gap, n); ret = do_write_oneword(map, &cfi->chips[chipnum], bus_ofs, datum, FL_WRITING); if (ret) return ret;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -