[PATCH] NAND: add support for reading ONFI parameters from NAND device
Florian Fainelli
ffainelli at freebox.fr
Wed Jul 28 18:47:05 EDT 2010
A nand_chip which has valid ONFI parameters gets its options field updated
with the NAND_ONFI flag. In that case both the ONFI version (in BCD format)
as well as the complete page parameters is available in the struct nand_chip.
This allows for better detection of some new devices, as well as fine tuning of
NAND driver timings. This patch only adds support for ONFI 1.0 parameters.
Signed-off-by: Maxime Bizon <mbizon at freebox.fr>
Signed-off-by: Florian Fainelli <ffainelli at freebox.fr>
---
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 4a7b864..c255cec 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -2773,6 +2773,83 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
}
/*
+ * sanitize ONFI strings so we can safely print them
+ */
+static void sanitize_string(uint8_t *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 */
+ for (i = len - 1; i >= 0; i--) {
+ if (s[i] && s[i] != ' ')
+ break;
+ s[i] = 0;
+ }
+}
+
+/*
+ * Check whether flash support ONFI and read ONFI parameters in that
+ * case
+ */
+static void nand_read_onfi(struct mtd_info *mtd, struct nand_chip *chip)
+{
+ struct nand_onfi_params *p;
+ uint8_t sig[4];
+ uint16_t val;
+
+ if (!chip->cmdfunc || !chip->read_buf)
+ return;
+
+ /* read ONFI signature */
+ chip->cmdfunc(mtd, NAND_CMD_READID, NAND_ADDR_ONFI_ID, -1);
+ chip->read_buf(mtd, sig, sizeof(sig));
+
+ if (memcmp(sig, "ONFI", sizeof(sig)))
+ return;
+
+ /* ONFI seems supported */
+ chip->cmdfunc(mtd, NAND_CMD_READ_ONFI_PARAMS, 0, -1);
+ p = &chip->onfi_params;
+ chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
+
+ /* recheck signature */
+ if (memcmp(p->sig, "ONFI", sizeof(p->sig))) {
+ printk(KERN_INFO "%s: bad ONFI params signature\n", __func__);
+ return;
+ }
+
+ /* check version */
+ val = le16_to_cpu(p->revision);
+ if (!is_power_of_2(val) || val == 1 || val > (1 << 4)) {
+ printk(KERN_INFO "%s: unsupported ONFI version\n", __func__);
+ return;
+ }
+
+ if (val & (1 << 1))
+ chip->onfi_version = 10;
+ else if (val & (1 << 2))
+ chip->onfi_version = 20;
+ else if (val & (1 << 3))
+ chip->onfi_version = 21;
+ else
+ chip->onfi_version = 22;
+
+ chip->options |= NAND_ONFI;
+
+ sanitize_string(p->manufacturer, sizeof(p->manufacturer));
+ sanitize_string(p->model, sizeof(p->model));
+}
+
+/*
* 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,
@@ -2958,10 +3035,19 @@ 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;
+ nand_read_onfi(mtd, chip);
+
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);
+ if (chip->options & NAND_ONFI)
+ printk(KERN_INFO "NAND is ONFI %d.%d compliant "
+ "(man:%s model:%s)\n",
+ chip->onfi_version / 10, chip->onfi_version % 10,
+ chip->onfi_params.manufacturer,
+ chip->onfi_params.model);
+
return type;
}
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index a81b185..ad7f58f 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -118,6 +118,10 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
#define NAND_CMD_STATUS_RESET 0x7f
#define NAND_CMD_STATUS_CLEAR 0xff
+/* Extended commands for ONFI devices */
+#define NAND_CMD_READ_ONFI_PARAMS 0xEC
+#define NAND_ADDR_ONFI_ID 0x20
+
#define NAND_CMD_NONE -1
/* Status bits */
@@ -190,6 +194,9 @@ typedef enum {
/* Device behaves just like nand, but is readonly */
#define NAND_ROM 0x00000800
+/* Chip supports ONFI */
+#define NAND_ONFI 0x00001000
+
/* Options valid for Samsung large page devices */
#define NAND_SAMSUNG_LP_OPTIONS \
(NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
@@ -229,6 +236,61 @@ typedef enum {
/* Keep gcc happy */
struct nand_chip;
+struct nand_onfi_params {
+ /* rev info and features block */
+ uint8_t sig[4]; /* 'O' 'N' 'F' 'I' */
+ uint16_t revision;
+ uint16_t features;
+ uint16_t opt_cmd;
+ uint8_t reserved[22];
+
+ /* manufacturer information block */
+ char manufacturer[12];
+ char model[20];
+ uint8_t jedec_id;
+ uint16_t date_code;
+ uint8_t reserved2[13];
+
+ /* memory organization block */
+ uint32_t byte_per_page;
+ uint16_t spare_bytes_per_page;
+ uint32_t data_bytes_per_ppage;
+ uint16_t sparre_bytes_per_ppage;
+ uint32_t pages_per_block;
+ uint32_t blocks_per_lun;
+ uint8_t lun_count;
+ uint8_t addr_cycles;
+ uint8_t bits_per_cell;
+ uint16_t bb_per_lun;
+ uint16_t block_endurance;
+ uint8_t guaranteed_good_blocks;
+ uint16_t guaranteed_block_endurance;
+ uint8_t programs_per_page;
+ uint8_t ppage_attr;
+ uint8_t ecc_bits;
+ uint8_t interleaved_bits;
+ uint8_t interleaved_ops;
+ uint8_t reserved3[13];
+ uint8_t io_pin_capacitance_max;
+
+ /* electrical parameter block */
+ uint16_t async_timing_mode;
+ uint16_t program_cache_timing_mode;
+ uint16_t t_prog;
+ uint16_t t_bers;
+ uint16_t t_r;
+ uint16_t t_ccs;
+ uint16_t src_sync_timing_mode;
+ uint16_t src_ssync_features;
+ uint16_t clk_pin_capacitance_typ;
+ uint16_t io_pin_capacitance_typ;
+ uint16_t input_pin_capacitance_typ;
+ uint8_t input_pin_capacitance_max;
+ uint8_t driver_strenght_support;
+ uint16_t t_int_r;
+ uint16_t t_ald;
+} __attribute__((packed));
+
/**
* struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
* @lock: protection lock
@@ -354,6 +416,8 @@ struct nand_buffers {
* @chip_shift: [INTERN] number of address bits in one chip
* @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
* special functionality. See the defines for further explanation
+ * @onfi_version: Supported ONFI version (10 => ONFI 1.0)
+ * @onfi_params: ONFI parameters
* @badblockpos: [INTERN] position of the bad block marker in the oob area
* @cellinfo: [INTERN] MLC/multichip data from chip ident
* @numchips: [INTERN] number of physical chips
@@ -413,6 +477,9 @@ struct nand_chip {
int badblockpos;
int badblockbits;
+ int onfi_version;
+ struct nand_onfi_params onfi_params;
+
flstate_t state;
uint8_t *oob_poi;
More information about the linux-mtd
mailing list