mirror of
https://github.com/u-boot/u-boot.git
synced 2024-11-25 21:24:21 +08:00
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>
This commit is contained in:
parent
e935a374db
commit
0272c718ba
@ -2415,75 +2415,94 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
|
||||
chip->controller = &chip->hwcontrol;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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,
|
||||
const struct nand_flash_dev *type)
|
||||
#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
|
||||
static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
|
||||
{
|
||||
int dev_id, maf_idx;
|
||||
int tmp_id, tmp_manf;
|
||||
int i;
|
||||
|
||||
/* Select the device */
|
||||
chip->select_chip(mtd, 0);
|
||||
|
||||
/*
|
||||
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
|
||||
* after power-up
|
||||
*/
|
||||
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
||||
|
||||
/* Send the command for reading device ID */
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
||||
|
||||
/* Read manufacturer and device IDs */
|
||||
*maf_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
|
||||
* possibly credible NAND flash to appear. If the two results do
|
||||
* not match, ignore the device completely.
|
||||
*/
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
||||
|
||||
/* Read manufacturer and device IDs */
|
||||
|
||||
tmp_manf = chip->read_byte(mtd);
|
||||
tmp_id = chip->read_byte(mtd);
|
||||
|
||||
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);
|
||||
return ERR_PTR(-ENODEV);
|
||||
while (len--) {
|
||||
crc ^= *p++ << 8;
|
||||
for (i = 0; i < 8; i++)
|
||||
crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
|
||||
}
|
||||
|
||||
if (!type)
|
||||
type = nand_flash_ids;
|
||||
return crc;
|
||||
}
|
||||
|
||||
for (; type->name != NULL; type++)
|
||||
if (dev_id == type->id)
|
||||
/*
|
||||
* 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 (!type->name) {
|
||||
/* supress warning if there is no nand */
|
||||
if (*maf_id != 0x00 && *maf_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);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
}
|
||||
|
||||
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 = type->name;
|
||||
mtd->name = p->model;
|
||||
|
||||
chip->chipsize = (uint64_t)type->chipsize << 20;
|
||||
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;
|
||||
@ -2501,7 +2520,7 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
mtd->erasesize = (64 * 1024) << (extid & 0x03);
|
||||
extid >>= 2;
|
||||
/* Get buswidth information */
|
||||
busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
|
||||
*busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
|
||||
|
||||
} else {
|
||||
/*
|
||||
@ -2510,8 +2529,93 @@ static const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
||||
mtd->erasesize = type->erasesize;
|
||||
mtd->writesize = type->pagesize;
|
||||
mtd->oobsize = mtd->writesize / 32;
|
||||
busw = type->options & NAND_BUSWIDTH_16;
|
||||
*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 *dev_id,
|
||||
const struct nand_flash_dev *type)
|
||||
{
|
||||
int ret, maf_idx;
|
||||
int tmp_id, tmp_manf;
|
||||
|
||||
/* Select the device */
|
||||
chip->select_chip(mtd, 0);
|
||||
|
||||
/*
|
||||
* Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
|
||||
* after power-up
|
||||
*/
|
||||
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
|
||||
|
||||
/* Send the command for reading device ID */
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
||||
|
||||
/* Read manufacturer and device IDs */
|
||||
*maf_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
|
||||
* possibly credible NAND flash to appear. If the two results do
|
||||
* not match, ignore the device completely.
|
||||
*/
|
||||
|
||||
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
||||
|
||||
/* Read manufacturer and device IDs */
|
||||
|
||||
tmp_manf = chip->read_byte(mtd);
|
||||
tmp_id = chip->read_byte(mtd);
|
||||
|
||||
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);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
if (!type)
|
||||
type = nand_flash_ids;
|
||||
|
||||
for (; type->name != NULL; type++)
|
||||
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)
|
||||
printk(KERN_INFO "%s: unknown NAND device: "
|
||||
"Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
|
||||
__func__, *maf_id, *dev_id);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
if (!mtd->name)
|
||||
mtd->name = type->name;
|
||||
|
||||
chip->chipsize = (uint64_t)type->chipsize << 20;
|
||||
chip->onfi_version = 0;
|
||||
|
||||
ret = nand_flash_detect_onfi(mtd, chip, &busw);
|
||||
if (!ret)
|
||||
nand_flash_detect_non_onfi(mtd, chip, type, &busw);
|
||||
|
||||
/* 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
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user