diff options
| author | Florian Fainelli <florian@openwrt.org> | 2011-02-25 00:01:34 +0000 | 
|---|---|---|
| committer | Scott Wood <scottwood@freescale.com> | 2011-04-01 14:49:08 -0500 | 
| commit | 0272c718ba69c60a9d719db6806971d98db98090 (patch) | |
| tree | 1b7dc349248e26f3e641871312b379b1577fbe97 /drivers/mtd/nand/nand_base.c | |
| parent | e935a374dbe5c745fdde05b2b07ced0bbe70887f (diff) | |
| download | olio-uboot-2014.01-0272c718ba69c60a9d719db6806971d98db98090.tar.xz olio-uboot-2014.01-0272c718ba69c60a9d719db6806971d98db98090.zip | |
NAND: add support for reading ONFI page table
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 <florian@openwrt.org>
Signed-off-by: Scott Wood <scottwood@freescale.com>
Diffstat (limited to 'drivers/mtd/nand/nand_base.c')
| -rw-r--r-- | drivers/mtd/nand/nand_base.c | 193 | 
1 files changed, 144 insertions, 49 deletions
| diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index b9bd3942f..fa286a8d8 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2415,15 +2415,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 */ @@ -2440,7 +2559,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 @@ -2455,10 +2574,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,16 +2585,16 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,  		type = nand_flash_ids;  	for (; type->name != NULL; type++) -		if (dev_id == type->id) +		if (*dev_id == type->id)  			break;  	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);  	} @@ -2483,35 +2602,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++) { @@ -2526,7 +2630,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); @@ -2549,15 +2653,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.  	 */ @@ -2575,7 +2670,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; @@ -2595,7 +2690,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; @@ -2605,7 +2700,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 @@ -2624,7 +2719,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 |