📄 cfi_cmdset_0001.c
字号:
shared->writing = chip; if (mode == FL_ERASING) shared->erasing = chip; spin_unlock(&shared->lock); } ret = chip_ready(map, chip, adr, mode); if (ret == -EAGAIN) goto retry; return ret;}static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr){ struct cfi_private *cfi = map->fldrv_priv; if (chip->priv) { struct flchip_shared *shared = chip->priv; spin_lock(&shared->lock); if (shared->writing == chip && chip->oldstate == FL_READY) { /* We own the ability to write, but we're done */ shared->writing = shared->erasing; if (shared->writing && shared->writing != chip) { /* give back ownership to who we loaned it from */ struct flchip *loaner = shared->writing; spin_lock(loaner->mutex); spin_unlock(&shared->lock); spin_unlock(chip->mutex); put_chip(map, loaner, loaner->start); spin_lock(chip->mutex); spin_unlock(loaner->mutex); wake_up(&chip->wq); return; } shared->erasing = NULL; shared->writing = NULL; } else if (shared->erasing == chip && shared->writing != chip) { /* * We own the ability to erase without the ability * to write, which means the erase was suspended * and some other partition is currently writing. * Don't let the switch below mess things up since * we don't have ownership to resume anything. */ spin_unlock(&shared->lock); wake_up(&chip->wq); return; } spin_unlock(&shared->lock); } switch(chip->oldstate) { case FL_ERASING: chip->state = chip->oldstate; /* What if one interleaved chip has finished and the other hasn't? The old code would leave the finished one in READY mode. That's bad, and caused -EROFS errors to be returned from do_erase_oneblock because that's the only bit it checked for at the time. As the state machine appears to explicitly allow sending the 0x70 (Read Status) command to an erasing chip and expecting it to be ignored, that's what we do. */ map_write(map, CMD(0xd0), adr); map_write(map, CMD(0x70), adr); chip->oldstate = FL_READY; chip->state = FL_ERASING; break; case FL_XIP_WHILE_ERASING: chip->state = chip->oldstate; chip->oldstate = FL_READY; break; case FL_READY: case FL_STATUS: case FL_JEDEC_QUERY: /* We should really make set_vpp() count, rather than doing this */ DISABLE_VPP(map); break; default: printk(KERN_ERR "%s: put_chip() called with oldstate %d!!\n", map->name, chip->oldstate); } wake_up(&chip->wq);}#ifdef CONFIG_MTD_XIP/* * No interrupt what so ever can be serviced while the flash isn't in array * mode. This is ensured by the xip_disable() and xip_enable() functions * enclosing any code path where the flash is known not to be in array mode. * And within a XIP disabled code path, only functions marked with __xipram * may be called and nothing else (it's a good thing to inspect generated * assembly to make sure inline functions were actually inlined and that gcc * didn't emit calls to its own support functions). Also configuring MTD CFI * support to a single buswidth and a single interleave is also recommended. */static void xip_disable(struct map_info *map, struct flchip *chip, unsigned long adr){ /* TODO: chips with no XIP use should ignore and return */ (void) map_read(map, adr); /* ensure mmu mapping is up to date */ local_irq_disable();}static void __xipram xip_enable(struct map_info *map, struct flchip *chip, unsigned long adr){ struct cfi_private *cfi = map->fldrv_priv; if (chip->state != FL_POINT && chip->state != FL_READY) { map_write(map, CMD(0xff), adr); chip->state = FL_READY; } (void) map_read(map, adr); xip_iprefetch(); local_irq_enable();}/* * When a delay is required for the flash operation to complete, the * xip_wait_for_operation() function is polling for both the given timeout * and pending (but still masked) hardware interrupts. Whenever there is an * interrupt pending then the flash erase or write operation is suspended, * array mode restored and interrupts unmasked. Task scheduling might also * happen at that point. The CPU eventually returns from the interrupt or * the call to schedule() and the suspended flash operation is resumed for * the remaining of the delay period. * * Warning: this function _will_ fool interrupt latency tracing tools. */static int __xipram xip_wait_for_operation( struct map_info *map, struct flchip *chip, unsigned long adr, unsigned int chip_op_time_max){ struct cfi_private *cfi = map->fldrv_priv; struct cfi_pri_intelext *cfip = cfi->cmdset_priv; map_word status, OK = CMD(0x80); unsigned long usec, suspended, start, done; flstate_t oldstate, newstate; start = xip_currtime(); usec = chip_op_time_max; if (usec == 0) usec = 500000; done = 0; do { cpu_relax(); if (xip_irqpending() && cfip && ((chip->state == FL_ERASING && (cfip->FeatureSupport&2)) || (chip->state == FL_WRITING && (cfip->FeatureSupport&4))) && (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) { /* * Let's suspend the erase or write operation when * supported. Note that we currently don't try to * suspend interleaved chips if there is already * 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); xip_iprefetch(); local_irq_enable(); spin_unlock(chip->mutex); xip_iprefetch(); 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, usec_max) \ xip_wait_for_operation(map, chip, cmd_adr, usec_max)#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, unsigned int chip_op_time_max){ struct cfi_private *cfi = map->fldrv_priv; map_word status, status_OK = CMD(0x80); int chip_state = chip->state; unsigned int timeo, sleep_time, reset_timeo; spin_unlock(chip->mutex); if (inval_len) INVALIDATE_CACHED_RANGE(map, inval_adr, inval_len); spin_lock(chip->mutex); timeo = chip_op_time_max; if (!timeo) timeo = 500000; reset_timeo = timeo; 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); } if (chip->erase_suspended || chip->write_suspended) { /* Suspend has occured while sleep: reset timeout */ timeo = reset_timeo; chip->erase_suspended = 0; chip->write_suspended = 0; } } /* Done and happy. */ chip->state = FL_STATUS; return 0;}#endif#define WAIT_TIMEOUT(map, chip, adr, udelay, udelay_max) \ INVAL_CACHE_AND_WAIT(map, chip, adr, 0, 0, udelay, udelay_max);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, void **virt, resource_size_t *phys){ struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; unsigned long ofs, last_end = 0; int chipnum; int ret = 0; if (!map->virt || (from + len > mtd->size)) return -EINVAL; /* 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); *virt = map->virt + cfi->chips[chipnum].start + ofs; *retlen = 0; if (phys) *phys = map->phys + cfi->chips[chipnum].start + ofs; while (len) { unsigned long thislen; if (chipnum >= cfi->numchips) break; /* We cannot point across chips that are virtually disjoint */ if (!last_end) last_end = cfi->chips[chipnum].start; else if (cfi->chips[chipnum].start != last_end) 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; last_end += 1 << cfi->chipshift; chipnum++; } return 0;}static void cfi_intelext_unpoint(struct mtd_info *mtd, 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? */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -