📄 cfi_cmdset_0002.c
字号:
}}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), adr); 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, int fast){ 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); /* * 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; int ta = 0; DECLARE_RETRY_CMD_CNT(); 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; } RETRY_CMD_LABEL; 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. */ status = cfi_read(map, adr); if (status == datum) { DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): NOP 0x%.8x == 0x%.8x\n", __func__, status, datum ); goto op_done; } ENABLE_VPP(map); if (fast) { /* Unlock bypass */ cfi_send_gen_cmd(0xA0, 0, chip->start, map, cfi, cfi->device_type, NULL); } else { /* * 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); /* * Polling toggle bits instead of reading back many times This ensures * that write operation is really completed, or tells us why it * failed. * * It may appear that the polling and decoding of error state might be * simplified. Don't do it unless you really know what you are doing. * * You must remember that JESD21-C 3.5.3 states that the status must * be read back an _additional_ two times before a failure is * determined. This is because these devices have internal state * machines that are asynchronous to the external data bus. During an * erase or write the read-back status of the polling bits might be * transitioning internaly when the external read-back occurs. This * means that the bits aren't in the final state and they might appear * to report an error as they are in a transient state: dq7 is * asynchronous with dq6 and other status bits. * * This asynchronous behaviour can cause infrequent errors that will * usually disappear the next time an erase or write happens (Try * tracking those errors down!). To ensure that the bits are not in * transition, the location must be read-back two more times and * compared against what was written - BOTH reads MUST match what was * written. Don't think this can be simplified to only the last read * matching the datum written: status bits *can* match the datum * written. * * If the final comparison fails, error state can *then* be decoded. * * - Thayne Harbaugh */ /* 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; } 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 ); /* * This only checks if dq6 is still toggling and that our * timer hasn't expired. We purposefully ignore the chip's * internal timer that will assert dq5 and leave dq6 toggling. * This is done for a variety of reasons: * * 1) Not all chips support dq5. * * 2) Dealing with asynchronous status bit and data updates * and reading a device two more times creates _messy_ logic * when trying to deal with interleaved devices - some may be * changing while others are still busy. * * 3) Checking dq5 only helps to optimize an error case that * should at worst be infrequent and at best non-existent. * * If our timeout occurs _then_ we will check dq5 to see if * the device also had an internal timeout. */ 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); } /* * Something kicked us out of the read-back loop. We'll check success * befor checking failure. Even though dq6 might be true data, it is * unkown if all of the other bits have changed to true data due to * the asynchronous nature of the internal state machine. We will * read two more times and use this to either verify that the write * completed successfully or that something really went wrong. BOTH * reads must match what was written - this certifies that bits aren't * still changing and that the status bits erroneously match the datum * that was written. */ 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; } /* * If we get to here then it means that something * is wrong and it's not a timeout. Something * is seriously wacky! Dump some debug info. */ /* * Found a clue about the chips that reach this state. * Some flash chips (SST >cough<) * are horribly broken. They do not ignore traffic that is * destined to other devices. This happens because some solutions * are on shared busses, the erase and program sequences have * have multiple commands, and the sequence is interspersed with * commands destined to other devices. A good flash chip will * examine the command and destination address and will ignore * commands that are for other devices. */ 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_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, 0); if (ret) return ret; ofs += n; buf += n; (*retlen) += n; if (ofs >> cfi->chipshift) { chipnum ++; ofs = 0; if (chipnum == cfi->numchips) return 0; } } if (cfi->fast_prog) { /* Go into unlock bypass mode */ 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); } /* 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; } ret = do_write_oneword(map, &cfi->chips[chipnum], ofs, datum, cfi->fast_prog); if (ret) { 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); } return ret; } ofs += CFIDEV_BUSWIDTH; buf += CFIDEV_BUSWIDTH; (*retlen) += CFIDEV_BUSWIDTH; len -= CFIDEV_BUSWIDTH;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -