summaryrefslogtreecommitdiffstats
path: root/sys/dev/nand
diff options
context:
space:
mode:
authorian <ian@FreeBSD.org>2013-12-14 00:54:05 +0000
committerian <ian@FreeBSD.org>2013-12-14 00:54:05 +0000
commit8b635cded30beba47401118e7af15d2d31ce15ed (patch)
treefaf9308a4eac8b777678b41455af467099e10b70 /sys/dev/nand
parent3360970b016395e930ab2a2e9db97174e387e337 (diff)
downloadFreeBSD-src-8b635cded30beba47401118e7af15d2d31ce15ed.zip
FreeBSD-src-8b635cded30beba47401118e7af15d2d31ce15ed.tar.gz
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.
Diffstat (limited to 'sys/dev/nand')
-rw-r--r--sys/dev/nand/nand.c35
-rw-r--r--sys/dev/nand/nand.h44
-rw-r--r--sys/dev/nand/nand_generic.c92
-rw-r--r--sys/dev/nand/nand_id.c2
4 files changed, 128 insertions, 45 deletions
diff --git a/sys/dev/nand/nand.c b/sys/dev/nand/nand.c
index f46d0f0..fb83e67 100644
--- a/sys/dev/nand/nand.c
+++ b/sys/dev/nand/nand.c
@@ -115,7 +115,7 @@ nand_init(struct nand_softc *nand, device_t dev, int ecc_mode,
}
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 *chip, struct chip_param_io *param)
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);
}
diff --git a/sys/dev/nand/nand.h b/sys/dev/nand/nand.h
index 0d6d7b4..0e9ea41 100644
--- a/sys/dev/nand/nand.h
+++ b/sys/dev/nand/nand.h
@@ -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, device_t dev, int ecc_mode,
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);
diff --git a/sys/dev/nand/nand_generic.c b/sys/dev/nand/nand_generic.c
index 85e81be..62c61610 100644
--- a/sys/dev/nand/nand_generic.c
+++ b/sys/dev/nand/nand_generic.c
@@ -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, uint32_t, void *, uint32_t, uint32_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 *chip, struct onfi_params *params)
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, &params, 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(&params, 254) != params.crc)
+ continue;
+ found = 1;
+ }
+ if (!found)
+ return (ENXIO);
+
+ chip_params->luns = params.luns;
+ chip_params->blocks_per_lun = le32dec(&params.blocks_per_lun);
+ chip_params->pages_per_block = le32dec(&params.pages_per_block);
+ chip_params->bytes_per_page = le32dec(&params.bytes_per_page);
+ chip_params->spare_bytes_per_page = le32dec(&params.spare_bytes_per_page);
+ chip_params->t_bers = le16dec(&params.t_bers);
+ chip_params->t_prog = le16dec(&params.t_prog);
+ chip_params->t_r = le16dec(&params.t_r);
+ chip_params->t_ccs = le16dec(&params.t_ccs);
+ chip_params->features = le16dec(&params.features);
+ chip_params->address_cycles = params.address_cycles;
return (0);
}
diff --git a/sys/dev/nand/nand_id.c b/sys/dev/nand/nand_id.c
index 76c5f9f..8900808 100644
--- a/sys/dev/nand/nand_id.c
+++ b/sys/dev/nand/nand_id.c
@@ -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)
OpenPOWER on IntegriCloud