Add support for legacy devices not supporting the ONFI READ_PARAMETER_PAGE

command with example usage for Micron chips
This commit is contained in:
ahoka 2011-03-09 10:05:08 +00:00
parent a30ace437f
commit 14ce4ecc83
4 changed files with 150 additions and 35 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: files.nand,v 1.1 2011/02/26 18:07:31 ahoka Exp $
# $NetBSD: files.nand,v 1.2 2011/03/09 10:05:08 ahoka Exp $
define nandbus { }
@ -9,6 +9,7 @@ file dev/nand/nand_io.c nand
file dev/nand/hamming.c nand
file dev/nand/nand_bbt.c nand
file dev/nand/nand_crc.c nand
file dev/nand/nand_micron.c nand
defpseudodev nandemulator: nandbus
file dev/nand/nandemulator.c nandemulator

View File

@ -1,4 +1,4 @@
/* $NetBSD: nand.c,v 1.2 2011/03/09 07:49:15 ahoka Exp $ */
/* $NetBSD: nand.c,v 1.3 2011/03/09 10:05:08 ahoka Exp $ */
/*-
* Copyright (c) 2010 Department of Software Engineering,
@ -34,7 +34,7 @@
/* Common driver for NAND chips implementing the ONFI 2.2 specification */
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: nand.c,v 1.2 2011/03/09 07:49:15 ahoka Exp $");
__KERNEL_RCSID(0, "$NetBSD: nand.c,v 1.3 2011/03/09 10:05:08 ahoka Exp $");
#include "locators.h"
@ -117,8 +117,10 @@ nand_attach(device_t parent, device_t self, void *aux)
aprint_error("NAND chip is write protected!\n");
return;
}
if (nand_scan_media(self, chip))
if (nand_scan_media(self, chip)) {
return;
}
/* allocate cache */
chip->nc_oob_cache = kmem_alloc(chip->nc_spare_size, KM_SLEEP);
@ -281,6 +283,19 @@ nand_quirks(device_t self, struct nand_chip *chip)
}
#endif
static int
nand_read_legacy_parameters(device_t self, struct nand_chip *chip)
{
switch (chip->nc_manf_id) {
case NAND_MFR_MICRON:
return nand_read_parameters_micron(self, chip);
default:
return 1;
}
return 0;
}
/**
* scan media to determine the chip's properties
* this function resets the device
@ -296,6 +311,7 @@ nand_scan_media(device_t self, struct nand_chip *chip)
nand_command(self, ONFI_RESET);
nand_select(self, false);
/* check if the device implements the ONFI standard */
nand_select(self, true);
nand_command(self, ONFI_READ_ID);
nand_address(self, 0x20);
@ -307,23 +323,47 @@ nand_scan_media(device_t self, struct nand_chip *chip)
if (onfi_signature[0] != 'O' || onfi_signature[1] != 'N' ||
onfi_signature[2] != 'F' || onfi_signature[3] != 'I') {
aprint_error_dev(self,
"device does not support the ONFI specification\n");
chip->nc_isonfi = false;
return 1;
}
aprint_normal(": Legacy NAND Flash\n");
nand_readid(self, chip);
aprint_normal(": NAND Flash\n");
if (nand_read_legacy_parameters(self, chip)) {
aprint_error_dev(self,
"can't read device parameters for legacy chip\n");
return 1;
}
} else {
chip->nc_isonfi = true;
aprint_debug_dev(self,
aprint_normal(": ONFI NAND Flash\n");
nand_readid(self, chip);
nand_read_parameter_page(self, chip);
}
#ifdef NAND_VERBOSE
aprint_normal_dev(self,
"manufacturer id: 0x%.2x (%s), device id: 0x%.2x\n",
chip->nc_manf_id,
nand_midtoname(chip->nc_manf_id),
chip->nc_dev_id);
#endif
nand_read_parameter_page(self, chip);
aprint_normal_dev(self,
"page size: %u bytes, spare size: %u bytes, block size: %u bytes\n",
chip->nc_page_size, chip->nc_spare_size, chip->nc_block_size);
aprint_normal_dev(self,
"LUN size: %u blocks, LUNs: %u, total storage size: %u MB\n",
chip->nc_lun_blocks, chip->nc_num_luns,
chip->nc_size / 1024 / 1024);
#ifdef NAND_VERBOSE
aprint_normal_dev(self, "column cycles: %d, row cycles: %d\n",
chip->nc_addr_cycles_column, chip->nc_addr_cycles_row);
#endif
ecc = chip->nc_ecc = &sc->nand_if->ecc;
@ -388,12 +428,13 @@ nand_readid(device_t self, struct nand_chip *chip)
nand_select(self, true);
nand_command(self, ONFI_READ_ID);
nand_address(self, 0x00);
nand_read_byte(self, &chip->nc_manf_id);
nand_read_byte(self, &chip->nc_dev_id);
nand_select(self, false);
}
/* read the parameter page. TODO: check CRC! */
static void
nand_read_parameter_page(device_t self, struct nand_chip *chip)
{
@ -435,20 +476,10 @@ nand_read_parameter_page(device_t self, struct nand_chip *chip)
aprint_normal_dev(self, "vendor: %s, model: %s\n", vendor, model);
aprint_normal_dev(self,
"page size: %u bytes, spare size: %u bytes, block size: %u bytes\n",
params.param_pagesize, params.param_sparesize,
params.param_blocksize * params.param_pagesize);
aprint_normal_dev(self,
"LUN size: %u blocks, LUNs: %u, total storage size: %u MB\n",
params.param_lunsize, params.param_numluns,
params.param_blocksize * params.param_pagesize *
params.param_lunsize * params.param_numluns / 1024 / 1024);
/* XXX TODO multiple LUNs */
if (__predict_false(params.param_numluns != 1))
if (__predict_false(params.param_numluns != 1)) {
panic("more than one LUNs are not supported yet!\n");
}
chip->nc_size = params.param_pagesize * params.param_blocksize *
params.param_lunsize * params.param_numluns;
@ -457,17 +488,14 @@ nand_read_parameter_page(device_t self, struct nand_chip *chip)
chip->nc_block_pages = params.param_blocksize;
chip->nc_block_size = params.param_blocksize * params.param_pagesize;
chip->nc_spare_size = params.param_sparesize;
chip->nc_lun_blocks = params.param_lunsize;
chip->nc_num_luns = params.param_numluns;
/* the lower 4 bits contain the row address cycles */
chip->nc_addr_cycles_row = params.param_addr_cycles & 0x07;
/* the upper 4 bits contain the column address cycles */
chip->nc_addr_cycles_column = (params.param_addr_cycles & ~0x07) >> 4;
#ifdef NAND_VERBOSE
aprint_normal_dev(self, "column cycles: %d, row cycles: %d\n",
chip->nc_addr_cycles_column, chip->nc_addr_cycles_row);
#endif
if (params.param_features & ONFI_FEATURE_16BIT)
chip->nc_flags |= NC_BUSWIDTH_16;

View File

@ -1,4 +1,4 @@
/* $NetBSD: nand.h,v 1.2 2011/03/05 06:28:29 jruoho Exp $ */
/* $NetBSD: nand.h,v 1.3 2011/03/09 10:05:08 ahoka Exp $ */
/*-
* Copyright (c) 2010 Department of Software Engineering,
@ -64,7 +64,7 @@ bool nand_isbad(device_t, flash_addr_t);
void nand_markbad(device_t, size_t);
int nand_read_page(device_t, size_t, uint8_t *);
int nand_read_oob(device_t self, size_t page, void *oob);
int nand_read_oob(device_t, size_t, void *);
/*
* default functions for driver development
@ -154,6 +154,7 @@ struct nand_ecc {
* about the NAND chip.
*/
struct nand_chip {
struct nand_ecc *nc_ecc; /* ecc information */
uint8_t *nc_oob_cache; /* buffer for oob cache */
uint8_t *nc_page_cache; /* buffer for page cache */
uint8_t *nc_ecc_cache;
@ -162,19 +163,20 @@ struct nand_chip {
size_t nc_block_pages; /* block size in pages */
size_t nc_block_size; /* block size in bytes */
size_t nc_spare_size; /* spare (oob) size in bytes */
uint32_t nc_lun_blocks; /* LUN size in blocks */
uint32_t nc_flags; /* bitfield flags */
uint32_t nc_quirks; /* bitfield quirks */
unsigned int nc_page_shift; /* page shift for page alignment */
unsigned int nc_page_mask; /* page mask for page alignment */
unsigned int nc_block_shift; /* write shift */
unsigned int nc_block_mask; /* write mask */
uint8_t nc_num_luns; /* number of LUNs */
uint8_t nc_manf_id; /* manufacturer id */
uint8_t nc_dev_id; /* device id */
uint8_t nc_addr_cycles_row; /* row cycles for addressing */
uint8_t nc_addr_cycles_column; /* column cycles for addressing */
uint8_t nc_badmarker_offs; /* offset for marking bad blocks */
struct nand_ecc *nc_ecc;
bool nc_isonfi; /* if the device is onfi compliant */
};
struct nand_write_cache {
@ -450,6 +452,13 @@ struct nand_manufacturer {
extern const struct nand_manufacturer nand_mfrs[];
/*
* Manufacturer specific parameter functions
*/
int nand_read_parameters_micron(device_t, struct nand_chip *);
/* debug inlines */
static inline void
nand_dump_data(const char *name, void *data, size_t len)
{

View File

@ -0,0 +1,77 @@
/*-
* Copyright (c) 2011 Department of Software Engineering,
* University of Szeged, Hungary
* Copyright (c) 2011 Adam Hoka <ahoka@NetBSD.org>
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by the Department of Software Engineering, University of Szeged, Hungary
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Device specific functions for legacy Micron NAND chips
*
* Currently supported:
* MT29F2G08AACWP, MT29F4G08BACWP, MT29F8G08FACWP
*/
#include "nand.h"
#include "onfi.h"
int
nand_read_parameters_micron(device_t self, struct nand_chip *chip)
{
uint8_t byte;
KASSERT(chip->nc_manf_id == NAND_MFR_MICRON);
nand_select(self, true);
nand_command(self, ONFI_READ_ID);
nand_address(self, 0x00);
switch (chip->nc_manf_id) {
/* three dummy reads */
nand_read_byte(self, &byte); /* vendor */
nand_read_byte(self, &byte); /* device */
nand_read_byte(self, &byte); /* unused */
/* this is the interesting one */
nand_read_byte(self, &byte);
/* TODO actually get info */
nand_select(self, false);
return 1;
break;
default:
nand_select(self, false);
return 1;
}
chip->nc_num_luns = 1;
chip->nc_lun_blocks = chip->nc_size / chip->nc_block_size;
nand_select(self, false);
return 0;
}