Module Name:    src
Committed By:   cliff
Date:           Sat Jul 23 06:27:40 UTC 2011

Modified Files:
        src/sys/dev/nor: cfi.c

Log Message:
- dump raw QRY and/or JEDEC data depending on CFI_DEBUG_QRY, CFI_DEBUG_JEDEC
- add ability to emulate CFI based on table lookup keyed by JEDEC IDs.
- warn if CFI command set is unknown


To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/sys/dev/nor/cfi.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/nor/cfi.c
diff -u src/sys/dev/nor/cfi.c:1.3 src/sys/dev/nor/cfi.c:1.4
--- src/sys/dev/nor/cfi.c:1.3	Tue Jul 19 20:52:10 2011
+++ src/sys/dev/nor/cfi.c	Sat Jul 23 06:27:40 2011
@@ -1,4 +1,4 @@
-/*	$NetBSD: cfi.c,v 1.3 2011/07/19 20:52:10 cliff Exp $	*/
+/*	$NetBSD: cfi.c,v 1.4 2011/07/23 06:27:40 cliff Exp $	*/
 /*-
  * Copyright (c) 2011 The NetBSD Foundation, Inc.
  * All rights reserved.
@@ -28,11 +28,12 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "opt_nor.h"
 #include "opt_flash.h"
+#include "opt_nor.h"
+#include "opt_cfi.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: cfi.c,v 1.3 2011/07/19 20:52:10 cliff Exp $"); 
+__KERNEL_RCSID(0, "$NetBSD: cfi.c,v 1.4 2011/07/23 06:27:40 cliff Exp $"); 
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -63,7 +64,18 @@
 static void cfi_write_buf_1(device_t, flash_off_t, const uint8_t *, size_t);
 static void cfi_write_buf_2(device_t, flash_off_t, const uint16_t *, size_t);
 static void cfi_write_buf_4(device_t, flash_off_t, const uint32_t *, size_t);
+static void cfi_jedec_id_1(struct cfi * const );
+static void cfi_jedec_id_2(struct cfi * const );
+static void cfi_jedec_id_4(struct cfi * const );
 static bool cfi_jedec_id(struct cfi * const);
+static bool cfi_emulate(struct cfi * const);
+static const struct cfi_jedec_tab * cfi_jedec_search(struct cfi *);
+static void cfi_jedec_fill(struct cfi * const,
+	const struct cfi_jedec_tab *);
+#if defined(CFI_DEBUG_JEDEC) || defined(CFI_DEBUG_QRY)
+static void cfi_hexdump(flash_off_t, void * const, u_int, u_int);
+#endif
+
 
 
 /*
@@ -99,6 +111,70 @@
 };
 
 
+#define LOG2_64K	16
+#define LOG2_128K	17
+#define LOG2_256K	18
+#define LOG2_512K	19
+#define LOG2_1M		20
+#define LOG2_2M		21
+#define LOG2_4M		22
+#define LOG2_8M		23
+#define LOG2_16M	24
+#define LOG2_32M	25
+#define LOG2_64M	26
+#define LOG2_128M	27
+#define LOG2_256M	28
+#define LOG2_512M	29
+#define LOG2_1G		30
+#define LOG2_2G		31
+const struct cfi_jedec_tab cfi_jedec_tab[] = {
+	{
+		.jt_name = "Pm39LV512",
+		.jt_mid = 0x9d,
+		.jt_did = 0x1b,
+		.jt_id_pri = 0,				/* XXX */
+		.jt_id_alt = 0,				/* XXX */
+		.jt_device_size = LOG2_64K,
+		.jt_interface_code_desc = CFI_IFCODE_X8,
+		.jt_erase_blk_regions = 1,
+		.jt_erase_blk_info = {
+			{ 4096/256, (64/4)-1 },
+		},
+		.jt_write_word_time_typ = 40,
+		.jt_write_nbyte_time_typ = 0,
+		.jt_erase_blk_time_typ = 55,
+		.jt_erase_chip_time_typ = 55,
+		.jt_write_word_time_max = 1,
+		.jt_write_nbyte_time_max = 0,
+		.jt_erase_blk_time_max = 1,
+		.jt_erase_chip_time_max = 1,
+		.jt_opmode = &cfi_opmodes_1[0],
+	},
+	{
+		.jt_name = "Pm39LV010",
+		.jt_mid = 0x9d,
+		.jt_did = 0x1c,
+		.jt_id_pri = 0,				/* XXX */
+		.jt_id_alt = 0,				/* XXX */
+		.jt_device_size = LOG2_128K,
+		.jt_interface_code_desc = CFI_IFCODE_X8,
+		.jt_erase_blk_regions = 1,
+		.jt_erase_blk_info = {
+			{ 4096/256, (128/4)-1 },
+		},
+		.jt_write_word_time_typ = 40,
+		.jt_write_nbyte_time_typ = 0,
+		.jt_erase_blk_time_typ = 55,
+		.jt_erase_chip_time_typ = 55,
+		.jt_write_word_time_max = 1,
+		.jt_write_nbyte_time_max = 0,
+		.jt_erase_blk_time_max = 1,
+		.jt_erase_chip_time_max = 1,
+		.jt_opmode = &cfi_opmodes_1[0],
+	},
+};
+
+
 const struct nor_interface nor_interface_cfi = {
 	.scan_media = cfi_scan_media,
 	.init = cfi_init,
@@ -157,11 +233,11 @@
 	(qryp)->write_word_time_typ = cfi_unpack_1(data[0x1f]);		\
 	(qryp)->write_nbyte_time_typ = cfi_unpack_1(data[0x20]);	\
 	(qryp)->erase_blk_time_typ = cfi_unpack_1(data[0x21]);		\
-	(qryp)->erase_chiptime_typ = cfi_unpack_1(data[0x22]);		\
+	(qryp)->erase_chip_time_typ = cfi_unpack_1(data[0x22]);		\
 	(qryp)->write_word_time_max = cfi_unpack_1(data[0x23]);		\
 	(qryp)->write_nbyte_time_max = cfi_unpack_1(data[0x24]);	\
 	(qryp)->erase_blk_time_max = cfi_unpack_1(data[0x25]);		\
-	(qryp)->erase_chiptime_max = cfi_unpack_1(data[0x26]);		\
+	(qryp)->erase_chip_time_max = cfi_unpack_1(data[0x26]);		\
 	(qryp)->device_size = cfi_unpack_1(data[0x27]);			\
 	(qryp)->interface_code_desc =					\
 		be16toh(cfi_unpack_2(data[0x28], data[0x29]));		\
@@ -239,6 +315,27 @@
 	}								\
     } while (0)
 
+#ifdef CFI_DEBUG_QRY
+# define CFI_DUMP_QRY(off, p, sz, stride)				\
+    do {								\
+	printf("%s: QRY data\n", __func__);				\
+	cfi_hexdump(off, p, sz, stride);				\
+    } while (0)
+#else
+# define CFI_DUMP_QRY(off, p, sz, stride)
+#endif
+
+#ifdef CFI_DEBUG_JEDEC
+# define CFI_DUMP_JEDEC(off, p, sz, stride)				\
+    do {								\
+	printf("%s: JEDEC data\n", __func__);				\
+	cfi_hexdump(off, p, sz, stride);				\
+    } while (0)
+#else
+# define CFI_DUMP_JEDEC(off, p, sz, stride)
+#endif
+
+
 /*
  * cfi_chip_query_opmode - determine operational mode based on QRY signature
  */
@@ -263,6 +360,8 @@
 	bus_space_read_region_1(cfi->cfi_bst, cfi->cfi_bsh, 0, data,
 		__arraycount(data));
 
+	CFI_DUMP_QRY(0, data, sizeof(data), 1);
+
 	bool found = cfi_chip_query_opmode(cfi, data, cfi_opmodes_1,
 		__arraycount(cfi_opmodes_1));
 
@@ -281,6 +380,8 @@
 	bus_space_read_region_2(cfi->cfi_bst, cfi->cfi_bsh, 0, data,
 		__arraycount(data));
 
+	CFI_DUMP_QRY(0, data, sizeof(data), 2);
+
 	bool found = cfi_chip_query_opmode(cfi, (uint8_t *)data,
 		cfi_opmodes_2, __arraycount(cfi_opmodes_2));
 
@@ -299,6 +400,8 @@
 	bus_space_read_region_4(cfi->cfi_bst, cfi->cfi_bsh, 0, data,
 		__arraycount(data));
 
+	CFI_DUMP_QRY(0, data, sizeof(data), 4);
+
 	bool found = cfi_chip_query_opmode(cfi, (uint8_t *)data,
 		cfi_opmodes_4, __arraycount(cfi_opmodes_4));
 
@@ -318,6 +421,8 @@
 	bus_space_read_region_8(cfi->cfi_bst, cfi->cfi_bsh, 0, data,
 		__arraycount(data));
 
+	CFI_DUMP_QRY(0, data, sizeof(data), 8);
+
 	bool found = cfi_chip_query_opmode(cfi, (uint8_t *)data,
 		cfi_opmodes_8, __arraycount(cfi_opmodes_8));
 
@@ -372,12 +477,20 @@
 		}
 	}
 
+	if (found)
+		cfi->cfi_emulated = false;
+
 	return found;
 }
 
 /*
  * cfi_probe - search for a CFI NOR trying various port & chip widths
  *
+ * - gather CFI QRY and PRI data
+ * - gather JEDEC ID data
+ * - if cfi_chip_query() fails, emulate CFI using table data if possible,
+ *   otherwise fail.
+ *
  * NOTE:
  *   striped NOR chips design not supported yet,
  *   so force portwidth=chipwidth for now
@@ -394,10 +507,13 @@
 		cfi->cfi_portwidth = 		/* XXX */
 		cfi->cfi_chipwidth = cw;
 		found = cfi_chip_query(cfi);
+		cfi_jedec_id(cfi);
+		if (! found)
+			found = cfi_emulate(cfi);
 		if (found)
-			goto out;
+			break;
 	}
- out:
+
 	cfi_reset_default(cfi);		/* exit QRY mode */
 	return found;
 }
@@ -407,7 +523,7 @@
 {
 	const bus_space_tag_t bst = cfi->cfi_bst;
 	const bus_space_handle_t bsh = cfi->cfi_bsh;
-	bool found = true;
+	bool found;
 
 	KASSERT(cfi != NULL);
 	KASSERT(bst != NULL);
@@ -416,20 +532,8 @@
 	cfi->cfi_bst = bst;		/* restore bus space */
 	cfi->cfi_bsh = bsh;		/*  "       "   "    */
 
-	/* gather CFI PRQ and PRI data */
-	if (! cfi_probe(cfi)) {
-		aprint_debug("%s: cfi_probe failed\n", __func__);
-		found = false;
-		goto out;
-	}
-
-	/* gather ID data if possible */
-	if (! cfi_jedec_id(cfi)) {
-		aprint_debug("%s: cfi_jedec_id failed\n", __func__);
-		goto out;
-	}
+	found = cfi_probe(cfi);
 
- out:
 	cfi_reset_default(cfi);	/* exit QRY mode */
 
 	return found;
@@ -466,6 +570,8 @@
 		cfi_0002_init(sc, cfi, chip);
 		break;
 	default:
+		aprint_error_dev(self, "unsupported CFI cmdset %#04x\n",
+			cfi->cfi_qry_data.id_pri);
 		return -1;
 	}
 
@@ -607,6 +713,30 @@
 }
 
 static void
+cfi_jedec_id_1(struct cfi * const cfi)
+{
+	struct cfi_jedec_id_data *idp = &cfi->cfi_id_data;
+	uint8_t data[0x10];
+
+	bus_space_read_region_1(cfi->cfi_bst, cfi->cfi_bsh, 0, data,
+		__arraycount(data));
+
+	CFI_DUMP_JEDEC(0, data, sizeof(data), 1);
+
+	idp->id_mid = (uint16_t)data[0];
+	idp->id_did[0] = (uint16_t)data[1];
+	idp->id_did[1] = (uint16_t)data[0xe];
+	idp->id_did[2] = (uint16_t)data[0xf];
+	idp->id_prot_state = (uint16_t)data[2];
+	idp->id_indicators = (uint16_t)data[3];
+
+	/* software bits, upper and lower */
+	idp->id_swb_lo = data[0xc];
+	idp->id_swb_hi = data[0xd];
+
+}
+
+static void
 cfi_jedec_id_2(struct cfi * const cfi)
 {
 	struct cfi_jedec_id_data *idp = &cfi->cfi_id_data;
@@ -615,6 +745,8 @@
 	bus_space_read_region_2(cfi->cfi_bst, cfi->cfi_bsh, 0, data,
 		__arraycount(data));
 
+	CFI_DUMP_JEDEC(0, data, sizeof(data), 1);
+
 	idp->id_mid = data[0];
 	idp->id_did[0] = data[1];
 	idp->id_did[1] = data[0xe];
@@ -628,22 +760,43 @@
 	 */
 	idp->id_swb_lo = data[0xc];
 	idp->id_swb_hi = data[0xd];
+
+}
+
+static void
+cfi_jedec_id_4(struct cfi * const cfi)
+{
+	struct cfi_jedec_id_data *idp = &cfi->cfi_id_data;
+	uint32_t data[0x10];
+
+	bus_space_read_region_4(cfi->cfi_bst, cfi->cfi_bsh, 0, data,
+		__arraycount(data));
+
+	CFI_DUMP_JEDEC(0, data, sizeof(data), 1);
+
+	idp->id_mid = data[0] & 0xffff;
+	idp->id_did[0] = data[1] & 0xffff;
+	idp->id_did[1] = data[0xe] & 0xffff;
+	idp->id_did[2] = data[0xf] & 0xffff;
+	idp->id_prot_state = data[2] & 0xffff;
+	idp->id_indicators = data[3] & 0xffff;
+
+	/* software bits, upper and lower
+	 * - undefined on S29GL-P
+	 * - defined   on S29GL-S
+	 */
+	idp->id_swb_lo = data[0xc] & 0xffff;
+	idp->id_swb_hi = data[0xd] & 0xffff;
+
 }
 
 /*
  * cfi_jedec_id - get JEDEC ID info
- *
- * this should be ignored altogether for CFI chips?
- * JEDEC ID is superceded by CFI info except CFI is not
- * a true superset of the JEDEC, so some info provided
- * by JEDEC is not available via CFI QRY.
- * But the JEDEC info is unreliable:
- * - different chips not distinguishaable by IDs
- * - some fields undefined (read as 0xff) on some chips
  */
 static bool
 cfi_jedec_id(struct cfi * const cfi)
 {
+
 	DPRINTF(("%s\n", __func__));
 
 	cfi_cmd(cfi, 0x555, 0xaa);
@@ -651,16 +804,16 @@
 	cfi_cmd(cfi, 0x555, 0x90);
 
 	switch(cfi->cfi_portwidth) {
-	case 1:
-		cfi_jedec_id_2(cfi);
-		break;
-#ifdef NOTYET
 	case 0:
 		cfi_jedec_id_1(cfi);
 		break;
+	case 1:
+		cfi_jedec_id_2(cfi);
+		break;
 	case 2:
 		cfi_jedec_id_4(cfi);
 		break;
+#ifdef NOTYET
 	case 3:
 		cfi_jedec_id_8(cfi);
 		break;
@@ -673,6 +826,65 @@
 	return true;
 }
 
+static bool
+cfi_emulate(struct cfi * const cfi)
+{
+	bool found = false;
+	const struct cfi_jedec_tab *jt = cfi_jedec_search(cfi);
+	if (jt != NULL) {
+		found = true;
+		cfi->cfi_emulated = true;
+		cfi_jedec_fill(cfi, jt);
+	}
+	return found;
+}
+
+/*
+ * cfi_jedec_search - search cfi_jedec_tab[] for entry matching given JEDEC IDs
+ */
+static const struct cfi_jedec_tab *
+cfi_jedec_search(struct cfi *cfi)
+{
+	struct cfi_jedec_id_data *idp = &cfi->cfi_id_data;
+
+	for (u_int i=0; i < __arraycount(cfi_jedec_tab); i++) {
+		const struct cfi_jedec_tab *jt = &cfi_jedec_tab[i];
+		if ((jt->jt_mid == idp->id_mid) &&
+		    (jt->jt_did == idp->id_did[0])) {
+			return jt;
+		}
+	}
+	return NULL;
+}
+
+/*
+ * cfi_jedec_fill - fill in cfi with info from table entry
+ */
+static void
+cfi_jedec_fill(struct cfi *cfi, const struct cfi_jedec_tab *jt)
+{
+	cfi->cfi_name = jt->jt_name;
+	cfi->cfi_opmode = jt->jt_opmode;
+	memset(&cfi->cfi_qry_data, 0, sizeof(struct cfi_query_data));
+	cfi->cfi_qry_data.id_pri = jt->jt_id_pri;
+	cfi->cfi_qry_data.id_alt = jt->jt_id_alt;
+	cfi->cfi_qry_data.interface_code_desc = jt->jt_interface_code_desc;
+	cfi->cfi_qry_data.write_word_time_typ = jt->jt_write_word_time_typ;
+	cfi->cfi_qry_data.write_nbyte_time_typ = jt->jt_write_nbyte_time_typ;
+	cfi->cfi_qry_data.erase_blk_time_typ = jt->jt_erase_blk_time_typ;
+	cfi->cfi_qry_data.erase_chip_time_typ = jt->jt_erase_chip_time_typ;
+	cfi->cfi_qry_data.write_word_time_max = jt->jt_write_word_time_max;
+	cfi->cfi_qry_data.write_nbyte_time_max = jt->jt_write_nbyte_time_max;
+	cfi->cfi_qry_data.erase_blk_time_max = jt->jt_erase_blk_time_max;
+	cfi->cfi_qry_data.erase_chip_time_max = jt->jt_erase_chip_time_max;
+	cfi->cfi_qry_data.device_size = jt->jt_device_size;
+	cfi->cfi_qry_data.interface_code_desc = jt->jt_interface_code_desc;
+	cfi->cfi_qry_data.write_nbyte_size_max = jt->jt_write_nbyte_size_max;
+	cfi->cfi_qry_data.erase_blk_regions = jt->jt_erase_blk_regions;
+	for (u_int i=0; i < 4; i++)
+		cfi->cfi_qry_data.erase_blk_info[i] = jt->jt_erase_blk_info[i];
+}
+
 void
 cfi_print(device_t self, struct cfi * const cfi)
 {
@@ -680,8 +892,14 @@
 	struct cfi_query_data * const qryp = &cfi->cfi_qry_data;
 
 	format_bytes(pbuf, sizeof(pbuf), 1 << qryp->device_size);
-	aprint_normal_dev(self, "CFI NOR flash %s %s\n", pbuf,
-		cfi_interface_desc_str(qryp->interface_code_desc));
+	if (cfi->cfi_emulated) {
+		aprint_normal_dev(self, "%s NOR flash %s %s\n",
+			cfi->cfi_name, pbuf,
+			cfi_interface_desc_str(qryp->interface_code_desc));
+	} else {
+		aprint_normal_dev(self, "CFI NOR flash %s %s\n", pbuf,
+			cfi_interface_desc_str(qryp->interface_code_desc));
+	}
 #ifdef NOR_VERBOSE
 	aprint_normal_dev(self, "manufacturer id %#x, device id %#x %#x %#x\n",
 		cfi->cfi_id_data.id_mid,
@@ -710,3 +928,26 @@
 		break;
 	}
 }
+
+#if defined(CFI_DEBUG_JEDEC) || defined(CFI_DEBUG_QRY)
+void
+cfi_hexdump(flash_off_t offset, void * const v, u_int count, u_int stride)
+{
+	uint8_t * const data = v;
+	for(int n=0; n < count; n+=16) {
+		int i;
+		printf("%08llx: ", (offset + n) / stride);
+		for(i=n; i < n+16; i++)
+			printf("%02x ", data[i]);
+		printf("\t");
+		for(i=n; i < n+16; i++) {
+			u_int c = (int)data[i];
+			if (c >= 0x20 && c < 0x7f)
+				printf("%c", c);
+			else
+				printf("%c", '.');
+		}
+		printf("\n");
+	}
+}
+#endif

Reply via email to