diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2012-08-01 17:49:27 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2012-08-01 17:49:27 +0200 |
commit | b1a4e722c3754ea11fc621e938196e8db57b1e39 (patch) | |
tree | 4706b4b99f1391e4db39a6e95673044d0c282828 /drivers/mtd | |
parent | 7db4b8a16a634ba1c225b918294305606f05f5e2 (diff) | |
parent | 632c45795065e6a7471ab82be38e808eb6204341 (diff) | |
download | barebox-b1a4e722c3754ea11fc621e938196e8db57b1e39.tar.gz barebox-b1a4e722c3754ea11fc621e938196e8db57b1e39.tar.xz |
Merge branch 'for-next/onfi'
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 174 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_imx.c | 98 |
2 files changed, 242 insertions, 30 deletions
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index c4eca0d2a3..a5bf757e47 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1027,6 +1027,108 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) } /* + * sanitize ONFI strings so we can safely print them + */ +static void sanitize_string(char *s, size_t len) +{ + ssize_t i; + + /* null terminate */ + s[len - 1] = 0; + + /* remove non printable chars */ + for (i = 0; i < len - 1; i++) { + if (s[i] < ' ' || s[i] > 127) + s[i] = '?'; + } + + /* remove trailing spaces */ + strim(s); +} + +static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) +{ + int i; + while (len--) { + crc ^= *p++ << 8; + for (i = 0; i < 8; i++) + crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0); + } + + return crc; +} + +/* + * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise + */ +static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, + int *busw) +{ + struct nand_onfi_params *p = &chip->onfi_params; + int i; + int val; + + /* try ONFI for unknow 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') + return 0; + + printk(KERN_INFO "ONFI flash detected ... "); + chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); + for (i = 0; i < 3; i++) { + chip->read_buf(mtd, (uint8_t *)p, sizeof(*p)); + if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == + le16_to_cpu(p->crc)) { + printk(KERN_INFO "ONFI param page %d valid\n", i); + break; + } + } + + if (i == 3) { + printk(KERN_INFO "no valid ONFI param page found\n"); + return 0; + } + + /* check version */ + val = le16_to_cpu(p->revision); + if (val & (1 << 5)) + chip->onfi_version = 23; + else if (val & (1 << 4)) + chip->onfi_version = 22; + else if (val & (1 << 3)) + chip->onfi_version = 21; + else if (val & (1 << 2)) + chip->onfi_version = 20; + else if (val & (1 << 1)) + chip->onfi_version = 10; + else + chip->onfi_version = 0; + + if (!chip->onfi_version) { + printk(KERN_INFO "unsupported ONFI version: %d\n", val); + return 0; + } + + sanitize_string(p->manufacturer, sizeof(p->manufacturer)); + sanitize_string(p->model, sizeof(p->model)); + if (!mtd->name) + mtd->name = p->model; + mtd->writesize = le32_to_cpu(p->byte_per_page); + mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize; + mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); + chip->chipsize = (uint64_t)le32_to_cpu(p->blocks_per_lun) * mtd->erasesize; + *busw = 0; + if (le16_to_cpu(p->features) & 1) + *busw = NAND_BUSWIDTH_16; + + chip->options &= ~NAND_CHIPOPTIONS_MSK; + chip->options |= NAND_NO_READRDY & NAND_CHIPOPTIONS_MSK; + + return 1; +} + +/* * Get the flash and manufacturer id and lookup if the type is supported */ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, @@ -1036,6 +1138,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, struct nand_flash_dev *type = NULL; int i, dev_id, maf_idx; int tmp_id, tmp_manf; + int ret; /* Select the device */ chip->select_chip(mtd, 0); @@ -1081,9 +1184,16 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, } } + chip->onfi_version = 0; if (!type) { - printk(KERN_ERR "NAND type unknown: %02x,%02x\n", *maf_id, dev_id); - return ERR_PTR(-ENODEV); + /* Check is chip is ONFI compliant */ + ret = nand_flash_detect_onfi(mtd, chip, &busw); + if (ret) + goto ident_done; + else { + printk(KERN_ERR "NAND type unknown: %02x,%02x\n", *maf_id, dev_id); + return ERR_PTR(-ENODEV); + } } if (!mtd->name) @@ -1126,17 +1236,39 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, break; } + /* Get chip options, preserve non chip based options */ + chip->options &= ~NAND_CHIPOPTIONS_MSK; + chip->options |= type->options & NAND_CHIPOPTIONS_MSK; + + /* Check if chip is a not a samsung device. Do not clear the + * options for chips which are not having an extended id. + */ + if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) + chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; + +ident_done: + /* + * Set chip as a default. Board drivers can override it, if necessary + */ + chip->options |= NAND_NO_AUTOINCR; + + /* Try to identify manufacturer */ + for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { + if (nand_manuf_ids[maf_idx].id == *maf_id) + break; + } + /* * Check, if buswidth is correct. Hardware drivers should set * chip correct ! */ if (busw != (chip->options & NAND_BUSWIDTH_16)) { printk(KERN_INFO "NAND device: Manufacturer ID:" - " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, - dev_id, nand_manuf_ids[maf_idx].name, mtd->name); + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, + dev_id, nand_manuf_ids[maf_idx].name, mtd->name); printk(KERN_WARNING "NAND bus width %d instead %d bit\n", - (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, - busw ? 16 : 8); + (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, + busw ? 16 : 8); return ERR_PTR(-EINVAL); } @@ -1147,27 +1279,17 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->bbt_erase_shift = chip->phys_erase_shift = ffs(mtd->erasesize) - 1; - chip->chip_shift = ffs(chip->chipsize) - 1; + if (chip->chipsize & 0xffffffff) + chip->chip_shift = ffs((unsigned)chip->chipsize) - 1; + else { + chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)); + chip->chip_shift += 32 - 1; + } /* Set the bad block position */ chip->badblockpos = mtd->writesize > 512 ? NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; - /* Get chip options, preserve non chip based options */ - chip->options &= ~NAND_CHIPOPTIONS_MSK; - chip->options |= type->options & NAND_CHIPOPTIONS_MSK; - - /* - * Set chip as a default. Board drivers can override it, if necessary - */ - chip->options |= NAND_NO_AUTOINCR; - - /* Check if chip is a not a samsung device. Do not clear the - * options for chips which are not having an extended id. - */ - if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) - chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; - #ifdef CONFIG_MTD_WRITE /* Check for AND chips with 4 page planes */ if (chip->options & NAND_4PAGE_ARRAY) @@ -1179,9 +1301,11 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp; - printk(KERN_INFO "NAND device: Manufacturer ID:" - " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id, - nand_manuf_ids[maf_idx].name, type->name); + printk("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s)," + " page size: %d, OOB size: %d\n", + *maf_id, dev_id, nand_manuf_ids[maf_idx].name, + chip->onfi_version ? chip->onfi_params.model : type->name, + mtd->writesize, mtd->oobsize); return type; } diff --git a/drivers/mtd/nand/nand_imx.c b/drivers/mtd/nand/nand_imx.c index e75ffbc819..83b49e3f29 100644 --- a/drivers/mtd/nand/nand_imx.c +++ b/drivers/mtd/nand/nand_imx.c @@ -106,6 +106,7 @@ struct imx_nand_host { void (*send_addr)(struct imx_nand_host *, uint16_t); void (*send_page)(struct imx_nand_host *, unsigned int); void (*send_read_id)(struct imx_nand_host *); + void (*send_read_param)(struct imx_nand_host *); uint16_t (*get_dev_status)(struct imx_nand_host *); int (*check_int)(struct imx_nand_host *); }; @@ -154,6 +155,31 @@ static struct nand_ecclayout nandv2_hw_eccoob_largepage = { } }; +/* OOB description for 4096 byte pages with 128 byte OOB */ +static struct nand_ecclayout nandv2_hw_eccoob_4k = { + .eccbytes = 8 * 9, + .eccpos = { + 7, 8, 9, 10, 11, 12, 13, 14, 15, + 23, 24, 25, 26, 27, 28, 29, 30, 31, + 39, 40, 41, 42, 43, 44, 45, 46, 47, + 55, 56, 57, 58, 59, 60, 61, 62, 63, + 71, 72, 73, 74, 75, 76, 77, 78, 79, + 87, 88, 89, 90, 91, 92, 93, 94, 95, + 103, 104, 105, 106, 107, 108, 109, 110, 111, + 119, 120, 121, 122, 123, 124, 125, 126, 127, + }, + .oobfree = { + {.offset = 2, .length = 4}, + {.offset = 16, .length = 7}, + {.offset = 32, .length = 7}, + {.offset = 48, .length = 7}, + {.offset = 64, .length = 7}, + {.offset = 80, .length = 7}, + {.offset = 96, .length = 7}, + {.offset = 112, .length = 7}, + } +}; + static void memcpy32(void *trg, const void *src, int size) { int i; @@ -335,6 +361,16 @@ static void send_read_id_v3(struct imx_nand_host *host) memcpy(host->data_buf, host->main_area0, 16); } +static void send_read_param_v3(struct imx_nand_host *host) +{ + /* Read ID into main buffer */ + writel(NFC_OUTPUT, NFC_V3_LAUNCH); + + wait_op_done(host); + + memcpy(host->data_buf, host->main_area0, 1024); +} + static void send_read_id_v1_v2(struct imx_nand_host *host) { struct nand_chip *this = &host->nand; @@ -363,6 +399,34 @@ static void send_read_id_v1_v2(struct imx_nand_host *host) memcpy32(host->data_buf, host->main_area0, 16); } +/* FIXME : to check on real HW */ +static void send_read_param_v1_v2(struct imx_nand_host *host) +{ + struct nand_chip *this = &host->nand; + + /* NANDFC buffer 0 is used for device ID output */ + writew(0x0, host->regs + NFC_V1_V2_BUF_ADDR); + + writew(NFC_OUTPUT, host->regs + NFC_V1_V2_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host); + + if (this->options & NAND_BUSWIDTH_16) { + volatile u16 *mainbuf = host->main_area0; + + /* + * Pack the every-other-byte result for 16-bit ID reads + * into every-byte as the generic code expects and various + * chips implement. + */ + + mainbuf[0] = (mainbuf[0] & 0xff) | ((mainbuf[1] & 0xff) << 8); + mainbuf[1] = (mainbuf[2] & 0xff) | ((mainbuf[3] & 0xff) << 8); + mainbuf[2] = (mainbuf[4] & 0xff) | ((mainbuf[5] & 0xff) << 8); + } + memcpy32(host->data_buf, host->main_area0, 1024); +} /* * This function requests the NANDFC to perform a read of the * NAND device status and returns the current status. @@ -579,6 +643,10 @@ static void imx_nand_read_buf(struct mtd_info *mtd, u_char * buf, int len) n = min(n, len); + /* handle the read param special case */ + if ((mtd->writesize == 0) && (len != 0)) + n = len; + memcpy(buf, host->data_buf + col, n); host->buf_start += n; @@ -677,8 +745,11 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) * layers perform a read/write buf operation, * we will used the saved column adress to index into * the full page. + * + * The colum address must be sent to the flash in + * order to get the ONFI header (0x20) */ - host->send_addr(host, 0); + host->send_addr(host, column); if (host->pagesize_2k) /* another col addr cycle for 2k page */ host->send_addr(host, 0); @@ -790,9 +861,11 @@ static void preset_v3(struct mtd_info *mtd) writel(0, NFC_V3_IPC); + /* if the flash has a 224 oob, the NFC must be configured to 218 */ config2 = NFC_V3_CONFIG2_ONE_CYCLE | NFC_V3_CONFIG2_2CMD_PHASES | - NFC_V3_CONFIG2_SPAS(mtd->oobsize >> 1) | + NFC_V3_CONFIG2_SPAS(((mtd->oobsize > 218) ? + 218 : mtd->oobsize) >> 1) | NFC_V3_CONFIG2_ST_CMD(0x70) | NFC_V3_CONFIG2_NUM_ADDR_PHASE0; @@ -944,8 +1017,15 @@ static void imx_nand_command(struct mtd_info *mtd, unsigned command, case NAND_CMD_READID: host->send_cmd(host, command); mxc_do_addr_cycle(mtd, column, page_addr); - host->buf_start = 0; host->send_read_id(host); + host->buf_start = 0; + break; + + case NAND_CMD_PARAM: + host->send_cmd(host, command); + mxc_do_addr_cycle(mtd, column, page_addr); + host->send_read_param(host); + host->buf_start = 0; break; case NAND_CMD_ERASE1: @@ -1024,7 +1104,7 @@ static int __init imxnd_probe(struct device_d *dev) struct mtd_info *mtd; struct imx_nand_platform_data *pdata = dev->platform_data; struct imx_nand_host *host; - struct nand_ecclayout *oob_smallpage, *oob_largepage; + struct nand_ecclayout *oob_smallpage, *oob_largepage, *oob_4kpage; int err = 0; #ifdef CONFIG_ARCH_IMX27 @@ -1047,6 +1127,7 @@ static int __init imxnd_probe(struct device_d *dev) host->send_addr = send_addr_v1_v2; host->send_page = send_page_v1_v2; host->send_read_id = send_read_id_v1_v2; + host->send_read_param = send_read_param_v1_v2; /* FIXME : to check */ host->get_dev_status = get_dev_status_v1_v2; host->check_int = check_int_v1_v2; } @@ -1059,6 +1140,7 @@ static int __init imxnd_probe(struct device_d *dev) host->spare_len = 64; oob_smallpage = &nandv2_hw_eccoob_smallpage; oob_largepage = &nandv2_hw_eccoob_largepage; + oob_4kpage = &nandv2_hw_eccoob_4k; /* FIXME : to check */ } else if (nfc_is_v1()) { host->base = dev_request_mem_region(dev, 0); host->main_area0 = host->base; @@ -1067,6 +1149,7 @@ static int __init imxnd_probe(struct device_d *dev) host->spare_len = 16; oob_smallpage = &nandv1_hw_eccoob_smallpage; oob_largepage = &nandv1_hw_eccoob_largepage; + oob_4kpage = &nandv1_hw_eccoob_smallpage; /* FIXME : to check */ } else if (nfc_is_v3_2()) { host->regs_ip = dev_request_mem_region(dev, 0); host->base = dev_request_mem_region(dev, 1); @@ -1086,10 +1169,12 @@ static int __init imxnd_probe(struct device_d *dev) host->send_addr = send_addr_v3; host->send_page = send_page_v3; host->send_read_id = send_read_id_v3; + host->send_read_param = send_read_param_v3; host->get_dev_status = get_dev_status_v3; host->check_int = check_int_v3; oob_smallpage = &nandv2_hw_eccoob_smallpage; oob_largepage = &nandv2_hw_eccoob_largepage; + oob_4kpage = &nandv2_hw_eccoob_4k; } host->dev = dev; @@ -1161,7 +1246,10 @@ static int __init imxnd_probe(struct device_d *dev) imx_nand_set_layout(mtd->writesize, pdata->width == 2 ? 16 : 8); if (mtd->writesize >= 2048) { - this->ecc.layout = oob_largepage; + if (mtd->writesize == 2048) + this->ecc.layout = oob_largepage; + else + this->ecc.layout = oob_4kpage; host->pagesize_2k = 1; if (nfc_is_v21()) writew(NFC_V2_SPAS_SPARESIZE(64), host->regs + NFC_V2_SPAS); |