From patchwork Fri Feb 25 10:01:34 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Fainelli X-Patchwork-Id: 84531 X-Patchwork-Delegate: scottwood@freescale.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id 38277B70F2 for ; Fri, 25 Feb 2011 21:01:09 +1100 (EST) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id AC7BD2835F; Fri, 25 Feb 2011 11:01:07 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id zaMw+4tMUymZ; Fri, 25 Feb 2011 11:01:07 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id B695F28356; Fri, 25 Feb 2011 11:01:04 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 94A5928356 for ; Fri, 25 Feb 2011 11:01:02 +0100 (CET) X-Virus-Scanned: Debian amavisd-new at theia.denx.de Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id yD91O70hrLYt for ; Fri, 25 Feb 2011 11:01:01 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-ww0-f52.google.com (mail-ww0-f52.google.com [74.125.82.52]) by theia.denx.de (Postfix) with ESMTPS id 09E2328354 for ; Fri, 25 Feb 2011 11:00:59 +0100 (CET) Received: by wwi14 with SMTP id 14so1496830wwi.9 for ; Fri, 25 Feb 2011 02:00:58 -0800 (PST) Received: by 10.227.166.11 with SMTP id k11mr1866895wby.127.1298628058893; Fri, 25 Feb 2011 02:00:58 -0800 (PST) Received: from flexo.localnet (bobafett.staff.proxad.net [213.228.1.121]) by mx.google.com with ESMTPS id f27sm422427wbf.13.2011.02.25.02.00.56 (version=SSLv3 cipher=OTHER); Fri, 25 Feb 2011 02:00:57 -0800 (PST) From: Florian Fainelli Organization: OpenWrt To: u-boot@lists.denx.de Date: Fri, 25 Feb 2011 11:01:34 +0100 User-Agent: KMail/1.13.5 (Linux/2.6.35-25-server; KDE/4.5.1; x86_64; ; ) References: <1292019402-25433-1-git-send-email-florian@openwrt.org> <201102141648.01214.florian@openwrt.org> <20110214174607.2b56dca8@schlenkerla.am.freescale.net> In-Reply-To: <20110214174607.2b56dca8@schlenkerla.am.freescale.net> MIME-Version: 1.0 Message-Id: <201102251101.34075.florian@openwrt.org> Cc: Scott Wood Subject: [U-Boot] [PATCH v4] NAND: add support for reading ONFI page table X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.9 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de From: Florian Fainelli This patch adds support for reading an ONFI page parameter from a NAND device supporting it. If this is the case, struct nand_chip onfi_version member contains the supported ONFI version, 0 otherwise. This allows NAND drivers past nand_scan_ident to set the best timings for the NAND chip. Signed-off-by: Florian Fainelli --- Patch against your 'master' branch Changes since v1: - ifdef out ONFI detection code around CONFIG_SYS_NAND_ONFI_DETECTION - removed bogus comment - fixed busw variable usage - move non-ONFI detection code to its own function - fixed stylistic issues spotted by Scott Changes since v2: - reduce lenght of some lines down to 80 columns - change chip->options consistently wrt to ONFI detected or not - removed extra spaces Changes since v3: - removed chip->options modification in nand_flash_detect_onfi because it is overwritten in nand_flash_detect() later -- 1.7.1 diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 70c0593..9034945 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2409,15 +2409,134 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) chip->controller = &chip->hwcontrol; } +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION +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; + + 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\n"); + 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) + return 0; + + /* check version */ + val = le16_to_cpu(p->revision); + if (val == 1 || val > (1 << 4)) { + printk(KERN_INFO "%s: unsupported ONFI " + "version: %d\n", __func__, val); + return 0; + } + + 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 + chip->onfi_version = 10; + + 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 = le32_to_cpu(p->blocks_per_lun) * mtd->erasesize; + *busw = 0; + if (le16_to_cpu(p->features) & 1) + *busw = NAND_BUSWIDTH_16; + + return 1; +} +#else +static inline int nand_flash_detect_onfi(struct mtd_info *mtd, + struct nand_chip *chip, + int *busw) +{ + return 0; +} +#endif + +static void nand_flash_detect_non_onfi(struct mtd_info *mtd, + struct nand_chip *chip, + const struct nand_flash_dev *type, + int *busw) +{ + /* Newer devices have all the information in additional id bytes */ + if (!type->pagesize) { + int extid; + /* The 3rd id byte holds MLC / multichip data */ + chip->cellinfo = chip->read_byte(mtd); + /* The 4th id byte is the important one */ + extid = chip->read_byte(mtd); + /* Calc pagesize */ + mtd->writesize = 1024 << (extid & 0x3); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + + } else { + /* + * Old devices have chip data hardcoded in the device id table + */ + mtd->erasesize = type->erasesize; + mtd->writesize = type->pagesize; + mtd->oobsize = mtd->writesize / 32; + *busw = type->options & NAND_BUSWIDTH_16; + } +} + /* * Get the flash and manufacturer id and lookup if the type is supported */ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip, - int busw, int *maf_id, + int busw, + int *maf_id, int *dev_id, const struct nand_flash_dev *type) { - int dev_id, maf_idx; + int ret, maf_idx; int tmp_id, tmp_manf; /* Select the device */ @@ -2434,7 +2553,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, /* Read manufacturer and device IDs */ *maf_id = chip->read_byte(mtd); - dev_id = chip->read_byte(mtd); + *dev_id = chip->read_byte(mtd); /* Try again to make sure, as some systems the bus-hold or other * interface concerns can cause random data which looks like a @@ -2449,10 +2568,10 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, tmp_manf = chip->read_byte(mtd); tmp_id = chip->read_byte(mtd); - if (tmp_manf != *maf_id || tmp_id != dev_id) { + if (tmp_manf != *maf_id || tmp_id != *dev_id) { printk(KERN_INFO "%s: second ID read did not match " "%02x,%02x against %02x,%02x\n", __func__, - *maf_id, dev_id, tmp_manf, tmp_id); + *maf_id, *dev_id, tmp_manf, tmp_id); return ERR_PTR(-ENODEV); } @@ -2466,10 +2585,10 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (!type->name) { /* supress warning if there is no nand */ if (*maf_id != 0x00 && *maf_id != 0xff && - dev_id != 0x00 && dev_id != 0xff) + *dev_id != 0x00 && *dev_id != 0xff) printk(KERN_INFO "%s: unknown NAND device: " "Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n", - __func__, *maf_id, dev_id); + __func__, *maf_id, *dev_id); return ERR_PTR(-ENODEV); } @@ -2477,35 +2596,20 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, mtd->name = type->name; chip->chipsize = (uint64_t)type->chipsize << 20; + chip->onfi_version = 0; - /* Newer devices have all the information in additional id bytes */ - if (!type->pagesize) { - int extid; - /* The 3rd id byte holds MLC / multichip data */ - chip->cellinfo = chip->read_byte(mtd); - /* The 4th id byte is the important one */ - extid = chip->read_byte(mtd); - /* Calc pagesize */ - mtd->writesize = 1024 << (extid & 0x3); - extid >>= 2; - /* Calc oobsize */ - mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); - extid >>= 2; - /* Calc blocksize. Blocksize is multiples of 64KiB */ - mtd->erasesize = (64 * 1024) << (extid & 0x03); - extid >>= 2; - /* Get buswidth information */ - busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + ret = nand_flash_detect_onfi(mtd, chip, &busw); + if (!ret) + nand_flash_detect_non_onfi(mtd, chip, type, &busw); - } else { - /* - * Old devices have chip data hardcoded in the device id table - */ - mtd->erasesize = type->erasesize; - mtd->writesize = type->pagesize; - mtd->oobsize = mtd->writesize / 32; - busw = type->options & NAND_BUSWIDTH_16; - } + /* 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; /* Try to identify manufacturer */ for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { @@ -2520,7 +2624,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, 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); + *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); @@ -2543,15 +2647,6 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, 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. */ @@ -2569,7 +2664,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->cmdfunc = nand_command_lp; MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:" - " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id, + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id, nand_manuf_ids[maf_idx].name, type->name); return type; @@ -2589,7 +2684,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, int nand_scan_ident(struct mtd_info *mtd, int maxchips, const struct nand_flash_dev *table) { - int i, busw, nand_maf_id; + int i, busw, nand_maf_id, nand_dev_id; struct nand_chip *chip = mtd->priv; const struct nand_flash_dev *type; @@ -2599,7 +2694,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, nand_set_defaults(chip, busw); /* Read the flash type */ - type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, table); + type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, &nand_dev_id, table); if (IS_ERR(type)) { #ifndef CONFIG_SYS_NAND_QUIET_TEST @@ -2618,7 +2713,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ if (nand_maf_id != chip->read_byte(mtd) || - type->id != chip->read_byte(mtd)) + nand_dev_id != chip->read_byte(mtd)) break; } #ifdef DEBUG diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index d299929..987a2ec 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -217,6 +217,71 @@ typedef enum { /* Keep gcc happy */ struct nand_chip; +struct nand_onfi_params { + /* rev info and features block */ + /* 'O' 'N' 'F' 'I' */ + u8 sig[4]; + __le16 revision; + __le16 features; + __le16 opt_cmd; + u8 reserved[22]; + + /* manufacturer information block */ + char manufacturer[12]; + char model[20]; + u8 jedec_id; + __le16 date_code; + u8 reserved2[13]; + + /* memory organization block */ + __le32 byte_per_page; + __le16 spare_bytes_per_page; + __le32 data_bytes_per_ppage; + __le16 spare_bytes_per_ppage; + __le32 pages_per_block; + __le32 blocks_per_lun; + u8 lun_count; + u8 addr_cycles; + u8 bits_per_cell; + __le16 bb_per_lun; + __le16 block_endurance; + u8 guaranteed_good_blocks; + __le16 guaranteed_block_endurance; + u8 programs_per_page; + u8 ppage_attr; + u8 ecc_bits; + u8 interleaved_bits; + u8 interleaved_ops; + u8 reserved3[13]; + + /* electrical parameter block */ + u8 io_pin_capacitance_max; + __le16 async_timing_mode; + __le16 program_cache_timing_mode; + __le16 t_prog; + __le16 t_bers; + __le16 t_r; + __le16 t_ccs; + __le16 src_sync_timing_mode; + __le16 src_ssync_features; + __le16 clk_pin_capacitance_typ; + __le16 io_pin_capacitance_typ; + __le16 input_pin_capacitance_typ; + u8 input_pin_capacitance_max; + u8 driver_strenght_support; + __le16 t_int_r; + __le16 t_ald; + u8 reserved4[7]; + + /* vendor */ + u8 reserved5[90]; + + __le16 crc; +} __attribute__((packed)); + +#define ONFI_CRC_BASE 0x4F4E + + /** * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices * @lock: protection lock @@ -406,6 +471,10 @@ struct nand_chip { int subpagesize; uint8_t cellinfo; int badblockpos; + int onfi_version; +#ifdef CONFIG_SYS_NAND_ONFI_DETECTION + struct nand_onfi_params onfi_params; +#endif int state;