📄 nandsim.c
字号:
{ int i, num; int busdiv = ns->busw == 8 ? 1 : 2; action &= ACTION_MASK; /* Check that page address input is correct */ if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) { NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row); return -1; } switch (action) { case ACTION_CPY: /* * Copy page data to the internal buffer. */ /* Column shouldn't be very large */ if (ns->regs.column >= (ns->geom.pgszoob - ns->regs.off)) { NS_ERR("do_state_action: column number is too large\n"); break; } num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; memcpy(ns->buf.byte, ns->mem.byte + NS_RAW_OFFSET(ns) + ns->regs.off, num); NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n", num, NS_RAW_OFFSET(ns) + ns->regs.off); if (ns->regs.off == 0) NS_LOG("read page %d\n", ns->regs.row); else if (ns->regs.off < ns->geom.pgsz) NS_LOG("read page %d (second half)\n", ns->regs.row); else NS_LOG("read OOB of page %d\n", ns->regs.row); NS_UDELAY(access_delay); NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv); break; case ACTION_SECERASE: /* * Erase sector. */ if (ns->lines.wp) { NS_ERR("do_state_action: device is write-protected, ignore sector erase\n"); return -1; } if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec || (ns->regs.row & ~(ns->geom.secsz - 1))) { NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row); return -1; } ns->regs.row = (ns->regs.row << 8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column; ns->regs.column = 0; NS_DBG("do_state_action: erase sector at address %#x, off = %d\n", ns->regs.row, NS_RAW_OFFSET(ns)); NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift)); memset(ns->mem.byte + NS_RAW_OFFSET(ns), 0xFF, ns->geom.secszoob); NS_MDELAY(erase_delay); break; case ACTION_PRGPAGE: /* * Programm page - move internal buffer data to the page. */ if (ns->lines.wp) { NS_WARN("do_state_action: device is write-protected, programm\n"); return -1; } num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; if (num != ns->regs.count) { NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n", ns->regs.count, num); return -1; } for (i = 0; i < num; i++) ns->mem.byte[NS_RAW_OFFSET(ns) + ns->regs.off + i] &= ns->buf.byte[i]; NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n", num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off); NS_LOG("programm page %d\n", ns->regs.row); NS_UDELAY(programm_delay); NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv); break; case ACTION_ZEROOFF: NS_DBG("do_state_action: set internal offset to 0\n"); ns->regs.off = 0; break; case ACTION_HALFOFF: if (!(ns->options & OPT_PAGE512_8BIT)) { NS_ERR("do_state_action: BUG! can't skip half of page for non-512" "byte page size 8x chips\n"); return -1; } NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz/2); ns->regs.off = ns->geom.pgsz/2; break; case ACTION_OOBOFF: NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz); ns->regs.off = ns->geom.pgsz; break; default: NS_DBG("do_state_action: BUG! unknown action\n"); } return 0;}/* * Switch simulator's state. */static voidswitch_state(struct nandsim *ns){ if (ns->op) { /* * The current operation have already been identified. * Just follow the states chain. */ ns->stateidx += 1; ns->state = ns->nxstate; ns->nxstate = ns->op[ns->stateidx + 1]; NS_DBG("switch_state: operation is known, switch to the next state, " "state: %s, nxstate: %s\n", get_state_name(ns->state), get_state_name(ns->nxstate)); /* See, whether we need to do some action */ if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); return; } } else { /* * We don't yet know which operation we perform. * Try to identify it. */ /* * The only event causing the switch_state function to * be called with yet unknown operation is new command. */ ns->state = get_state_by_command(ns->regs.command); NS_DBG("switch_state: operation is unknown, try to find it\n"); if (find_operation(ns, 0) != 0) return; if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); return; } } /* For 16x devices column means the page offset in words */ if ((ns->nxstate & STATE_ADDR_MASK) && ns->busw == 16) { NS_DBG("switch_state: double the column number for 16x device\n"); ns->regs.column <<= 1; } if (NS_STATE(ns->nxstate) == STATE_READY) { /* * The current state is the last. Return to STATE_READY */ u_char status = NS_STATUS_OK(ns); /* In case of data states, see if all bytes were input/output */ if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) && ns->regs.count != ns->regs.num) { NS_WARN("switch_state: not all bytes were processed, %d left\n", ns->regs.num - ns->regs.count); status = NS_STATUS_FAILED(ns); } NS_DBG("switch_state: operation complete, switch to STATE_READY state\n"); switch_to_ready_state(ns, status); return; } else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) { /* * If the next state is data input/output, switch to it now */ ns->state = ns->nxstate; ns->nxstate = ns->op[++ns->stateidx + 1]; ns->regs.num = ns->regs.count = 0; NS_DBG("switch_state: the next state is data I/O, switch, " "state: %s, nxstate: %s\n", get_state_name(ns->state), get_state_name(ns->nxstate)); /* * Set the internal register to the count of bytes which * are expected to be input or output */ switch (NS_STATE(ns->state)) { case STATE_DATAIN: case STATE_DATAOUT: ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; break; case STATE_DATAOUT_ID: ns->regs.num = ns->geom.idbytes; break; case STATE_DATAOUT_STATUS: case STATE_DATAOUT_STATUS_M: ns->regs.count = ns->regs.num = 0; break; default: NS_ERR("switch_state: BUG! unknown data state\n"); } } else if (ns->nxstate & STATE_ADDR_MASK) { /* * If the next state is address input, set the internal * register to the number of expected address bytes */ ns->regs.count = 0; switch (NS_STATE(ns->nxstate)) { case STATE_ADDR_PAGE: ns->regs.num = ns->geom.pgaddrbytes; break; case STATE_ADDR_SEC: ns->regs.num = ns->geom.secaddrbytes; break; case STATE_ADDR_ZERO: ns->regs.num = 1; break; default: NS_ERR("switch_state: BUG! unknown address state\n"); } } else { /* * Just reset internal counters. */ ns->regs.num = 0; ns->regs.count = 0; }}static voidns_hwcontrol(struct mtd_info *mtd, int cmd){ struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; switch (cmd) { /* set CLE line high */ case NAND_CTL_SETCLE: NS_DBG("ns_hwcontrol: start command latch cycles\n"); ns->lines.cle = 1; break; /* set CLE line low */ case NAND_CTL_CLRCLE: NS_DBG("ns_hwcontrol: stop command latch cycles\n"); ns->lines.cle = 0; break; /* set ALE line high */ case NAND_CTL_SETALE: NS_DBG("ns_hwcontrol: start address latch cycles\n"); ns->lines.ale = 1; break; /* set ALE line low */ case NAND_CTL_CLRALE: NS_DBG("ns_hwcontrol: stop address latch cycles\n"); ns->lines.ale = 0; break; /* set WP line high */ case NAND_CTL_SETWP: NS_DBG("ns_hwcontrol: enable write protection\n"); ns->lines.wp = 1; break; /* set WP line low */ case NAND_CTL_CLRWP: NS_DBG("ns_hwcontrol: disable write protection\n"); ns->lines.wp = 0; break; /* set CE line low */ case NAND_CTL_SETNCE: NS_DBG("ns_hwcontrol: enable chip\n"); ns->lines.ce = 1; break; /* set CE line high */ case NAND_CTL_CLRNCE: NS_DBG("ns_hwcontrol: disable chip\n"); ns->lines.ce = 0; break; default: NS_ERR("hwcontrol: unknown command\n"); } return;}static u_charns_nand_read_byte(struct mtd_info *mtd){ struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; u_char outb = 0x00; /* Sanity and correctness checks */ if (!ns->lines.ce) { NS_ERR("read_byte: chip is disabled, return %#x\n", (uint)outb); return outb; } if (ns->lines.ale || ns->lines.cle) { NS_ERR("read_byte: ALE or CLE pin is high, return %#x\n", (uint)outb); return outb; } if (!(ns->state & STATE_DATAOUT_MASK)) { NS_WARN("read_byte: unexpected data output cycle, state is %s " "return %#x\n", get_state_name(ns->state), (uint)outb); return outb; } /* Status register may be read as many times as it is wanted */ if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS) { NS_DBG("read_byte: return %#x status\n", ns->regs.status); return ns->regs.status; } /* Check if there is any data in the internal buffer which may be read */ if (ns->regs.count == ns->regs.num) { NS_WARN("read_byte: no more data to output, return %#x\n", (uint)outb); return outb; } switch (NS_STATE(ns->state)) { case STATE_DATAOUT: if (ns->busw == 8) { outb = ns->buf.byte[ns->regs.count]; ns->regs.count += 1; } else { outb = (u_char)cpu_to_le16(ns->buf.word[ns->regs.count >> 1]); ns->regs.count += 2; } break; case STATE_DATAOUT_ID: NS_DBG("read_byte: read ID byte %d, total = %d\n", ns->regs.count, ns->regs.num); outb = ns->ids[ns->regs.count]; ns->regs.count += 1; break; default: BUG(); } if (ns->regs.count == ns->regs.num) { NS_DBG("read_byte: all bytes were read\n"); /* * The OPT_AUTOINCR allows to read next conseqitive pages without * new read operation cycle. */ if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { ns->regs.count = 0; if (ns->regs.row + 1 < ns->geom.pgnum) ns->regs.row += 1; NS_DBG("read_byte: switch to the next page (%#x)\n", ns->regs.row); do_state_action(ns, ACTION_CPY);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -