diff --git a/sys/dev/nand/files.nand b/sys/dev/nand/files.nand index 4ab562e4103c..1f78a348f3e9 100644 --- a/sys/dev/nand/files.nand +++ b/sys/dev/nand/files.nand @@ -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 diff --git a/sys/dev/nand/nand.c b/sys/dev/nand/nand.c index 3882a7e92af2..675f6e3fa8b2 100644 --- a/sys/dev/nand/nand.c +++ b/sys/dev/nand/nand.c @@ -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 -__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,24 +323,48 @@ 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; + + aprint_normal(": Legacy NAND Flash\n"); + + nand_readid(self, chip); - return 1; + 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_normal(": ONFI NAND Flash\n"); + + nand_readid(self, chip); + nand_read_parameter_page(self, chip); } - nand_readid(self, chip); - - aprint_normal(": NAND Flash\n"); - - aprint_debug_dev(self, +#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; diff --git a/sys/dev/nand/nand.h b/sys/dev/nand/nand.h index 6b5c330c3a06..89cebf9c5073 100644 --- a/sys/dev/nand/nand.h +++ b/sys/dev/nand/nand.h @@ -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) { diff --git a/sys/dev/nand/nand_micron.c b/sys/dev/nand/nand_micron.c new file mode 100644 index 000000000000..57e85c84eb2d --- /dev/null +++ b/sys/dev/nand/nand_micron.c @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 2011 Department of Software Engineering, + * University of Szeged, Hungary + * Copyright (c) 2011 Adam Hoka + * 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; +}