Boris, Am 12.05.2016 um 14:36 schrieb Boris Brezillon: > Hi Richard, > > On Thu, 12 May 2016 13:32:45 +0200 > Richard Weinberger <[email protected]> wrote: > >> Sometimes all you need is a NAND with a given page, erase and chip size >> to load and inspect a certain image. >> OOB, ECC sizes and other metrics don't matter much then. >> In such a situation I find myself often fiddling around with NAND IDs >> to get what I need, especially when the given NAND ID from the datasheet >> decode to something else than the NAND advertises via ONFI. >> Using the new nandsim.approx module parameter it is possible to specify >> the page size, number of pages per block and the total number of blocks. >> Nandsim will then emulate a SLC NAND with 128 bytes OOB and the given >> sizes. Nandsim achieves this by providing ONFI parameters to NAND core. > > The current nandsim implementation is indeed incomplete and does not > allow for the full NAND chain emulation. > I first considered dispatching the emulation bit in the different > components (the NAND controller, the NAND chip and the generic/standard > code), but that means introduction a new infrastructure and I'm now > unsure that this is appropriate.
Yep. But a facelifting for nandsim would not harm. ;) > The other question we should ask ourselves is whether this NAND > emulation layer belongs here. I mean, we could emulate a bunch of NAND > chips and NAND controller directly in Qemu instead of trying to teach > nandsim how to emulate advanced chips. I like nandsim a lot because I can use it without visualization. Especially for stress testing UBI/UBIFS nandsim on bare metal is very useful. >> >> Signed-off-by: Richard Weinberger <[email protected]> >> --- >> drivers/mtd/nand/nand_base.c | 12 +++++---- >> drivers/mtd/nand/nandsim.c | 62 >> +++++++++++++++++++++++++++++++++++++++----- >> include/linux/mtd/nand.h | 3 +++ >> 3 files changed, 66 insertions(+), 11 deletions(-) >> >> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c >> index 557b846..25432c2 100644 >> --- a/drivers/mtd/nand/nand_base.c >> +++ b/drivers/mtd/nand/nand_base.c >> @@ -3208,7 +3208,7 @@ static void sanitize_string(uint8_t *s, size_t len) >> strim(s); >> } >> >> -static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) >> +u16 nand_onfi_crc16(u16 crc, u8 const *p, size_t len) >> { >> int i; >> while (len--) { >> @@ -3246,7 +3246,7 @@ static int nand_flash_detect_ext_param_page(struct >> mtd_info *mtd, >> >> /* Read out the Extended Parameter Page. */ >> chip->read_buf(mtd, (uint8_t *)ep, len); >> - if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) >> + if ((nand_onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) >> != le16_to_cpu(ep->crc))) { >> pr_debug("fail in the CRC.\n"); >> goto ext_out; >> @@ -3328,14 +3328,16 @@ static int nand_flash_detect_onfi(struct mtd_info >> *mtd, struct nand_chip *chip, >> /* Try ONFI for unknown chip or LP */ >> chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); >> if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' || >> - chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') >> + chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') { >> + >> return 0; >> + } > > Hm, this change seems unnecessary. Whops, there was a debug printk. >> >> chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); >> for (i = 0; i < 3; i++) { >> for (j = 0; j < sizeof(*p); j++) >> ((uint8_t *)p)[j] = chip->read_byte(mtd); >> - if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == >> + if (nand_onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == >> le16_to_cpu(p->crc)) { >> break; >> } >> @@ -3442,7 +3444,7 @@ static int nand_flash_detect_jedec(struct mtd_info >> *mtd, struct nand_chip *chip, >> for (j = 0; j < sizeof(*p); j++) >> ((uint8_t *)p)[j] = chip->read_byte(mtd); >> >> - if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) == >> + if (nand_onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) == >> le16_to_cpu(p->crc)) >> break; >> } >> diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c >> index a58169a2..b5be38a 100644 >> --- a/drivers/mtd/nand/nandsim.c >> +++ b/drivers/mtd/nand/nandsim.c >> @@ -114,6 +114,15 @@ static u_char id_bytes[8] = { >> [3] = CONFIG_NANDSIM_FOURTH_ID_BYTE, >> [4 ... 7] = 0xFF, >> }; >> +static unsigned int approx[3]; >> + >> +static struct nand_onfi_params id_onfi = { >> + .sig = {'O', 'N', 'F', 'I'}, >> + .manufacturer = "nandsim ", >> + .model = "nandsim ", >> +}; >> + >> +static unsigned char *id_p; >> >> module_param_array(id_bytes, byte, NULL, 0400); >> module_param_named(first_id_byte, id_bytes[0], byte, 0400); >> @@ -139,6 +148,7 @@ module_param(overridesize, uint, 0400); >> module_param(cache_file, charp, 0400); >> module_param(bbt, uint, 0400); >> module_param(bch, uint, 0400); >> +module_param_array(approx, uint, NULL, 0400); >> >> MODULE_PARM_DESC(id_bytes, "The ID bytes returned by NAND Flash 'read >> ID' command"); >> MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash >> 'read ID' command (manufacturer ID) (obsolete)"); >> @@ -174,6 +184,10 @@ MODULE_PARM_DESC(cache_file, "File to use to cache >> nand pages instead of mem >> MODULE_PARM_DESC(bbt, "0 OOB, 1 BBT with marker in OOB, 2 >> BBT with marker in data area"); >> MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits >> should " >> "be correctable in 512-byte blocks"); >> +MODULE_PARM_DESC(approx, "Approximation mode, simulate a good enough >> NAND that satisfies" >> + " page size, block size and number of blocks." >> + " e.g. 4096,128,8192 will give you a NAND with >> 4KiB page size, 128 pages per" >> + " block and 8192 blocks in total."); >> >> /* The largest possible page size */ >> #define NS_LARGEST_PAGE_SIZE 4096 >> @@ -229,6 +243,7 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and >> set how many bits should " >> #define STATE_CMD_RESET 0x0000000C /* reset */ >> #define STATE_CMD_RNDOUT 0x0000000D /* random output command */ >> #define STATE_CMD_RNDOUTSTART 0x0000000E /* random output start command */ >> +#define STATE_CMD_PARAM 0x0000000F /* param output start command */ >> #define STATE_CMD_MASK 0x0000000F /* command states mask */ >> >> /* After an address is input, the simulator goes to one of these states */ >> @@ -245,7 +260,8 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and >> set how many bits should " >> #define STATE_DATAOUT 0x00001000 /* waiting for page data output */ >> #define STATE_DATAOUT_ID 0x00002000 /* waiting for ID bytes output */ >> #define STATE_DATAOUT_STATUS 0x00003000 /* waiting for status output */ >> -#define STATE_DATAOUT_MASK 0x00007000 /* data output states mask */ >> +#define STATE_DATAOUT_ONFI 0x00007000 /* waiting for ONFI output */ >> +#define STATE_DATAOUT_MASK 0x0000F000 /* data output states mask */ >> >> /* Previous operation is done, ready to accept new requests */ >> #define STATE_READY 0x00000000 >> @@ -307,7 +323,6 @@ struct nandsim { >> unsigned int nbparts; >> >> uint busw; /* flash chip bus width (8 or 16) */ >> - u_char ids[8]; /* chip's ID bytes */ >> uint32_t options; /* chip's characteristic bits */ >> uint32_t state; /* current chip state */ >> uint32_t nxstate; /* next expected state */ >> @@ -408,6 +423,8 @@ static struct nandsim_operations { >> {OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}}, >> /* Read ID */ >> {OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, >> STATE_READY}}, >> + /* Read param */ >> + {OPT_ANY, {STATE_CMD_PARAM, STATE_ADDR_ZERO, STATE_DATAOUT_ONFI, >> STATE_READY}}, >> /* Large page devices read page */ >> {OPT_LARGEPAGE, {STATE_CMD_READ0, STATE_ADDR_PAGE, STATE_CMD_READSTART >> | ACTION_CPY, >> STATE_DATAOUT, STATE_READY}}, >> @@ -1077,6 +1094,8 @@ static char *get_state_name(uint32_t state) >> return "STATE_CMD_RNDOUT"; >> case STATE_CMD_RNDOUTSTART: >> return "STATE_CMD_RNDOUTSTART"; >> + case STATE_CMD_PARAM: >> + return "STATE_CMD_PARAM"; >> case STATE_ADDR_PAGE: >> return "STATE_ADDR_PAGE"; >> case STATE_ADDR_SEC: >> @@ -1125,6 +1144,7 @@ static int check_command(int cmd) >> case NAND_CMD_RESET: >> case NAND_CMD_RNDOUT: >> case NAND_CMD_RNDOUTSTART: >> + case NAND_CMD_PARAM: >> return 0; >> >> default: >> @@ -1164,6 +1184,8 @@ static uint32_t get_state_by_command(unsigned command) >> return STATE_CMD_RNDOUT; >> case NAND_CMD_RNDOUTSTART: >> return STATE_CMD_RNDOUTSTART; >> + case NAND_CMD_PARAM: >> + return STATE_CMD_PARAM; >> } >> >> NS_ERR("get_state_by_command: unknown command, BUG\n"); >> @@ -1856,9 +1878,17 @@ static void switch_state(struct nandsim *ns) >> break; >> >> case STATE_DATAOUT_ID: >> - ns->regs.num = ns->geom.idbytes; >> + if (ns->regs.row == 0x20) { >> + ns->regs.num = sizeof(id_onfi.sig); >> + id_p = (unsigned char *)id_onfi.sig; >> + } else { >> + ns->regs.num = ns->geom.idbytes; >> + id_p = (unsigned char *)id_bytes; >> + } >> + break; >> + case STATE_DATAOUT_ONFI: >> + ns->regs.num = sizeof(id_onfi); >> break; >> - Another whitespace damage^^. >> case STATE_DATAOUT_STATUS: >> ns->regs.count = ns->regs.num = 0; >> break; >> @@ -1951,7 +1981,11 @@ static u_char ns_nand_read_byte(struct mtd_info *mtd) >> 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]; >> + outb = id_p[ns->regs.count]; >> + ns->regs.count += 1; >> + break; >> + case STATE_DATAOUT_ONFI: >> + outb = ((unsigned char *)&id_onfi)[ns->regs.count]; >> ns->regs.count += 1; >> break; >> default: >> @@ -2289,10 +2323,26 @@ static int __init ns_init_module(void) >> nand->geom.idbytes = 4; >> else >> nand->geom.idbytes = 2; >> + >> + if (approx[0] && approx[1] && approx[2]) { >> + id_onfi.byte_per_page = cpu_to_le32(approx[0]); >> + id_onfi.pages_per_block = cpu_to_le32(approx[1]); >> + id_onfi.blocks_per_lun = cpu_to_le32(approx[2]); >> + >> + id_onfi.spare_bytes_per_page = cpu_to_le32(128); >> + id_onfi.lun_count = cpu_to_le32(1); >> + id_onfi.bits_per_cell = cpu_to_le32(1); >> + id_onfi.revision = cpu_to_le32(1 << 5); >> + id_onfi.crc = cpu_to_le16(nand_onfi_crc16(ONFI_CRC_BASE, >> (uint8_t *)&id_onfi, >> + sizeof(id_onfi) - >> sizeof(id_onfi.crc))); >> + >> + nand->geom.idbytes = 2; >> + id_bytes[0] = id_bytes[1] = 0xFF; >> + } >> + >> nand->regs.status = NS_STATUS_OK(nand); >> nand->nxstate = STATE_UNKNOWN; >> nand->options |= OPT_PAGE512; /* temporary value */ >> - memcpy(nand->ids, id_bytes, sizeof(nand->ids)); >> if (bus_width == 16) { >> nand->busw = 16; >> chip->options |= NAND_BUSWIDTH_16; >> diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h >> index 56574ba..82f6db2 100644 >> --- a/include/linux/mtd/nand.h >> +++ b/include/linux/mtd/nand.h >> @@ -50,6 +50,9 @@ extern int nand_lock(struct mtd_info *mtd, loff_t ofs, >> uint64_t len); >> /* unlocks specified locked blocks */ >> extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); >> >> +/* calculate ONFI CRC */ >> +extern u16 nand_onfi_crc16(u16 crc, u8 const *p, size_t len); >> + >> /* The maximum number of NAND chips in an array */ >> #define NAND_MAX_CHIPS 8 >> > > Just a general feedback on the whole approach. If what you really want > is a solution to skip the chip detection and manually set the chip > info, then you shouldn't call nand_scan_ident() and manually fill > the mtd and nand_chip fields instead. Okay, that would work too. I was not sure whether I'm allowed to do so. Thanks, //richard

