Date: Sat, 14 Dec 2013 00:54:06 +0000 (UTC) From: Ian Lepore <ian@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r259371 - stable/10/sys/dev/nand Message-ID: <201312140054.rBE0s6eb009513@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: ian Date: Sat Dec 14 00:54:05 2013 New Revision: 259371 URL: http://svnweb.freebsd.org/changeset/base/259371 Log: MFC r257892, r258196, r258197, r258199, r258200, r258201, r258202: Add ONFI signature check. Add Micron chip found in Freescale Vybrid Family Phytec COSMIC board. The vendor specified field is 88 bytes, not 8 bytes. Update the onfi_params struct to ONFI revision 3.2 (06 12 2013). Search for and validate the ONFI params as specified in the standard. ONFI parameters are little-endian, hence we must take care to convert them to native endianness. We must also pay attention to unaligned accesses. Rework the routine that returns a pointer to the table of software ECC byte positions within the OOB area to support chips with unusual OOB sizes such as 218 or 224 bytes. Modified: stable/10/sys/dev/nand/nand.c stable/10/sys/dev/nand/nand.h stable/10/sys/dev/nand/nand_generic.c stable/10/sys/dev/nand/nand_id.c Directory Properties: stable/10/ (props changed) Modified: stable/10/sys/dev/nand/nand.c ============================================================================== --- stable/10/sys/dev/nand/nand.c Sat Dec 14 00:40:47 2013 (r259370) +++ stable/10/sys/dev/nand/nand.c Sat Dec 14 00:54:05 2013 (r259371) @@ -115,7 +115,7 @@ nand_init(struct nand_softc *nand, devic } void -nand_onfi_set_params(struct nand_chip *chip, struct onfi_params *params) +nand_onfi_set_params(struct nand_chip *chip, struct onfi_chip_params *params) { struct chip_geom *cg; @@ -309,23 +309,22 @@ nand_get_chip_param(struct nand_chip *ch static uint16_t * default_software_ecc_positions(struct nand_chip *chip) { - struct nand_ecc_data *eccd; - - eccd = &chip->nand->ecc; - - if (eccd->eccpositions) - return (eccd->eccpositions); - - switch (chip->chip_geom.oob_size) { - case 16: - return ((uint16_t *)&default_software_ecc_positions_16); - case 64: - return ((uint16_t *)&default_software_ecc_positions_64); - case 128: - return ((uint16_t *)&default_software_ecc_positions_128); - default: - return (NULL); /* No ecc bytes positions defs available */ - } + /* If positions have been set already, use them. */ + if (chip->nand->ecc.eccpositions) + return (chip->nand->ecc.eccpositions); + + /* + * XXX Note that the following logic isn't really sufficient, especially + * in the ONFI case where the number of ECC bytes can be dictated by + * values in the parameters page, and that could lead to needing more + * byte positions than exist within the tables of software-ecc defaults. + */ + if (chip->chip_geom.oob_size >= 128) + return (default_software_ecc_positions_128); + if (chip->chip_geom.oob_size >= 64) + return (default_software_ecc_positions_64); + else if (chip->chip_geom.oob_size >= 16) + return (default_software_ecc_positions_16); return (NULL); } Modified: stable/10/sys/dev/nand/nand.h ============================================================================== --- stable/10/sys/dev/nand/nand.h Sat Dec 14 00:40:47 2013 (r259370) +++ stable/10/sys/dev/nand/nand.h Sat Dec 14 00:54:05 2013 (r259371) @@ -31,6 +31,7 @@ #include <sys/bus.h> #include <sys/param.h> +#include <sys/systm.h> #include <sys/lock.h> #include <sys/sx.h> #include <sys/taskqueue.h> @@ -122,7 +123,8 @@ MALLOC_DECLARE(M_NAND); #define NAND_MAN_SAMSUNG 0xec #define NAND_MAN_HYNIX 0xad -#define NAND_MAN_STMICRO 0x20 +#define NAND_MAN_STMICRO 0x20 +#define NAND_MAN_MICRON 0x2c struct nand_id { uint8_t man_id; @@ -176,12 +178,17 @@ struct onfi_params { uint16_t rev; uint16_t features; uint16_t optional_commands; - uint8_t res1[22]; + uint8_t primary_advanced_command; + uint8_t res1; + uint16_t extended_parameter_page_length; + uint8_t parameter_page_count; + uint8_t res2[17]; char manufacturer_name[12]; char device_model[20]; uint8_t manufacturer_id; - uint16_t date; - uint8_t res2[13]; + uint8_t manufacture_date_yy; + uint8_t manufacture_date_ww; + uint8_t res3[13]; uint32_t bytes_per_page; uint16_t spare_bytes_per_page; uint32_t bytes_per_partial_page; @@ -200,7 +207,8 @@ struct onfi_params { uint8_t bits_of_ecc; uint8_t interleaved_addr_bits; uint8_t interleaved_oper_attr; - uint8_t res3[13]; + uint8_t eznand_support; + uint8_t res4[12]; uint8_t pin_capacitance; uint16_t asynch_timing_mode_support; uint16_t asynch_prog_cache_timing_mode_support; @@ -215,11 +223,31 @@ struct onfi_params { uint16_t input_capacitance; uint8_t input_capacitance_max; uint8_t driver_strength_support; - uint8_t res4[12]; + uint16_t t_r_interleaved; + uint16_t t_adl; + uint16_t t_r_eznand; + uint8_t nv_ddr2_features; + uint8_t nv_ddr2_warmup_cycles; + uint8_t res5[4]; uint16_t vendor_rev; - uint8_t vendor_spec[8]; + uint8_t vendor_spec[88]; uint16_t crc; }__attribute__((packed)); +CTASSERT(sizeof(struct onfi_params) == 256); + +struct onfi_chip_params { + uint32_t blocks_per_lun; + uint32_t pages_per_block; + uint32_t bytes_per_page; + uint32_t spare_bytes_per_page; + uint16_t t_bers; + uint16_t t_prog; + uint16_t t_r; + uint16_t t_ccs; + uint16_t features; + uint8_t address_cycles; + uint8_t luns; +}; struct nand_ecc_data { int eccsize; /* Number of data bytes per ECC step */ @@ -353,7 +381,7 @@ void nand_init(struct nand_softc *nand, void nand_detach(struct nand_softc *nand); struct nand_params *nand_get_params(struct nand_id *id); -void nand_onfi_set_params(struct nand_chip *chip, struct onfi_params *params); +void nand_onfi_set_params(struct nand_chip *chip, struct onfi_chip_params *params); void nand_set_params(struct nand_chip *chip, struct nand_params *params); int nand_init_stat(struct nand_chip *chip); void nand_destroy_stat(struct nand_chip *chip); Modified: stable/10/sys/dev/nand/nand_generic.c ============================================================================== --- stable/10/sys/dev/nand/nand_generic.c Sat Dec 14 00:40:47 2013 (r259370) +++ stable/10/sys/dev/nand/nand_generic.c Sat Dec 14 00:54:05 2013 (r259371) @@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$"); #include <sys/proc.h> #include <sys/bus.h> #include <sys/conf.h> +#include <sys/endian.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/rman.h> @@ -73,7 +74,7 @@ static int small_program_page(device_t, static int small_program_oob(device_t, uint32_t, void *, uint32_t, uint32_t); static int onfi_is_blk_bad(device_t, uint32_t, uint8_t *); -static int onfi_read_parameter(struct nand_chip *, struct onfi_params *); +static int onfi_read_parameter(struct nand_chip *, struct onfi_chip_params *); static int nand_send_address(device_t, int32_t, int32_t, int8_t); @@ -206,7 +207,7 @@ generic_nand_attach(device_t dev) { struct nand_chip *chip; struct nandbus_ivar *ivar; - struct onfi_params *onfi_params; + struct onfi_chip_params *onfi_chip_params; device_t nandbus, nfc; int err; @@ -225,25 +226,24 @@ generic_nand_attach(device_t dev) chip->nand = device_get_softc(nfc); if (ivar->is_onfi) { - onfi_params = malloc(sizeof(struct onfi_params), + onfi_chip_params = malloc(sizeof(struct onfi_chip_params), M_NAND, M_WAITOK | M_ZERO); - if (onfi_params == NULL) - return (ENXIO); + if (onfi_chip_params == NULL) + return (ENOMEM); - if (onfi_read_parameter(chip, onfi_params)) { + if (onfi_read_parameter(chip, onfi_chip_params)) { nand_debug(NDBG_GEN,"Could not read parameter page!\n"); - free(onfi_params, M_NAND); + free(onfi_chip_params, M_NAND); return (ENXIO); } - nand_onfi_set_params(chip, onfi_params); + nand_onfi_set_params(chip, onfi_chip_params); /* Set proper column and row cycles */ - ivar->cols = (onfi_params->address_cycles >> 4) & 0xf; - ivar->rows = onfi_params->address_cycles & 0xf; - free(onfi_params, M_NAND); + ivar->cols = (onfi_chip_params->address_cycles >> 4) & 0xf; + ivar->rows = onfi_chip_params->address_cycles & 0xf; + free(onfi_chip_params, M_NAND); } else { - nand_set_params(chip, ivar->params); } @@ -319,10 +319,32 @@ check_fail(device_t nandbus) return (0); } +static uint16_t +onfi_crc(const void *buf, size_t buflen) +{ + int i, j; + uint16_t crc; + const uint8_t *bufptr; + + bufptr = buf; + crc = 0x4f4e; + for (j = 0; j < buflen; j++) { + crc ^= *bufptr++ << 8; + for (i = 0; i < 8; i++) + if (crc & 0x8000) + crc = (crc << 1) ^ 0x8005; + else + crc <<= 1; + } + return crc; +} + static int -onfi_read_parameter(struct nand_chip *chip, struct onfi_params *params) +onfi_read_parameter(struct nand_chip *chip, struct onfi_chip_params *chip_params) { device_t nandbus; + struct onfi_params params; + int found, sigcount, trycopy; nand_debug(NDBG_GEN,"read parameter"); @@ -339,12 +361,44 @@ onfi_read_parameter(struct nand_chip *ch if (NANDBUS_START_COMMAND(nandbus)) return (ENXIO); - NANDBUS_READ_BUFFER(nandbus, params, sizeof(struct onfi_params)); - - /* TODO */ - /* Check for signature */ - /* Check CRC */ - /* Use redundant page if necessary */ + /* + * XXX Bogus DELAY, we really need a nandbus_wait_ready() here, but it's + * not accessible from here (static to nandbus). + */ + DELAY(1000); + + /* + * The ONFI spec mandates a minimum of three copies of the parameter + * data, so loop up to 3 times trying to find good data. Each copy is + * validated by a signature of "ONFI" and a crc. There is a very strange + * rule that the signature is valid if any 2 of the 4 bytes are correct. + */ + for (found= 0, trycopy = 0; !found && trycopy < 3; trycopy++) { + NANDBUS_READ_BUFFER(nandbus, ¶ms, sizeof(struct onfi_params)); + sigcount = params.signature[0] == 'O'; + sigcount += params.signature[1] == 'N'; + sigcount += params.signature[2] == 'F'; + sigcount += params.signature[3] == 'I'; + if (sigcount < 2) + continue; + if (onfi_crc(¶ms, 254) != params.crc) + continue; + found = 1; + } + if (!found) + return (ENXIO); + + chip_params->luns = params.luns; + chip_params->blocks_per_lun = le32dec(¶ms.blocks_per_lun); + chip_params->pages_per_block = le32dec(¶ms.pages_per_block); + chip_params->bytes_per_page = le32dec(¶ms.bytes_per_page); + chip_params->spare_bytes_per_page = le32dec(¶ms.spare_bytes_per_page); + chip_params->t_bers = le16dec(¶ms.t_bers); + chip_params->t_prog = le16dec(¶ms.t_prog); + chip_params->t_r = le16dec(¶ms.t_r); + chip_params->t_ccs = le16dec(¶ms.t_ccs); + chip_params->features = le16dec(¶ms.features); + chip_params->address_cycles = params.address_cycles; return (0); } Modified: stable/10/sys/dev/nand/nand_id.c ============================================================================== --- stable/10/sys/dev/nand/nand_id.c Sat Dec 14 00:40:47 2013 (r259370) +++ stable/10/sys/dev/nand/nand_id.c Sat Dec 14 00:54:05 2013 (r259371) @@ -47,6 +47,8 @@ struct nand_params nand_ids[] = { 0x80, 0x200, 0x10, 0x20, 0 }, { { NAND_MAN_STMICRO, 0xf1 }, "STMicro 128MB 3,3V 8-bit", 0x80, 2048, 64, 0x40, 0 }, + { { NAND_MAN_MICRON, 0xcc }, "Micron NAND 512MiB 3,3V 16-bit", + 0x200, 2048, 64, 0x40, 0 }, }; struct nand_params *nand_get_params(struct nand_id *id)
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201312140054.rBE0s6eb009513>