Module Name:    src
Committed By:   msaitoh
Date:           Fri Sep  9 05:36:59 UTC 2016

Modified Files:
        src/sys/dev/i2c: spdmem_i2c.c
        src/sys/dev/ic: spdmem.c

Log Message:
 Add code to select page "0" for DDR4 and newer SPD ROM. If the value readed
is not suitable as SPD ROM, try to select page 0 and try again. The passed
arguments of iic_exec(SPDCTL_SPA) might not be correct and/or our API of
iic_exec() should be improved. See the comment for the detail.

Use this change until we find a better (or correct?) way.


To generate a diff of this commit:
cvs rdiff -u -r1.12 -r1.13 src/sys/dev/i2c/spdmem_i2c.c
cvs rdiff -u -r1.21 -r1.22 src/sys/dev/ic/spdmem.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/i2c/spdmem_i2c.c
diff -u src/sys/dev/i2c/spdmem_i2c.c:1.12 src/sys/dev/i2c/spdmem_i2c.c:1.13
--- src/sys/dev/i2c/spdmem_i2c.c:1.12	Tue Jan  5 11:49:32 2016
+++ src/sys/dev/i2c/spdmem_i2c.c	Fri Sep  9 05:36:59 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: spdmem_i2c.c,v 1.12 2016/01/05 11:49:32 msaitoh Exp $ */
+/* $NetBSD: spdmem_i2c.c,v 1.13 2016/09/09 05:36:59 msaitoh Exp $ */
 
 /*
  * Copyright (c) 2007 Nicolas Joly
@@ -40,7 +40,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: spdmem_i2c.c,v 1.12 2016/01/05 11:49:32 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: spdmem_i2c.c,v 1.13 2016/09/09 05:36:59 msaitoh Exp $");
 
 #include <sys/param.h>
 #include <sys/device.h>
@@ -88,6 +88,7 @@ struct spdmem_i2c_softc {
 	i2c_addr_t sc_page1;
 };
 
+static int  spdmem_reset_page(struct spdmem_i2c_softc *);
 static int  spdmem_i2c_match(device_t, cfdata_t, void *);
 static void spdmem_i2c_attach(device_t, device_t, void *);
 static int  spdmem_i2c_detach(device_t, int);
@@ -98,6 +99,79 @@ CFATTACH_DECL_NEW(spdmem_iic, sizeof(str
 static int spdmem_i2c_read(struct spdmem_softc *, uint16_t, uint8_t *);
 
 static int
+spdmem_reset_page(struct spdmem_i2c_softc *sc)
+{
+	uint8_t reg, byte0, byte2;
+	int rv;
+
+	reg = 0;
+
+	iic_acquire_bus(sc->sc_tag, 0);
+
+	/*
+	 * Try to read byte 0 and 2. If it failed, it's not spdmem or a device
+	 * doesn't exist at the address.
+	 */
+	rv = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, &reg, 1,
+	    &byte0, 1, I2C_F_POLL);
+	rv |= iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, &reg, 1,
+	    &byte2, 1, I2C_F_POLL);
+	if (rv != 0)
+		goto error;
+
+	/*
+	 * Quirk for BIOSes that leave page 1 of a 4kbit EEPROM selected.
+	 *
+	 * byte0 is the length, byte2 is the memory type. Both of them should
+	 * not be zero. If zero, the current page might be 1 (DDR4 and newer).
+	 * If page 1 is selected, offset 0 can be 0 (Module Characteristics
+	 * (Energy backup is not available)) and also offset 2 can be 0
+	 * (Megabytes, and a part of Capacity digits).
+	 *
+	 * Note: The encoding of byte0 is vary in memory type, so we check
+	 * just with zero to be simple.
+	 *
+	 * Try to see if we are not at page 0. If it's not, select page 0.
+	 */
+	if ((byte0 == 0) || (byte2 == 0)) {
+		/*
+		 * Note that SDCTL_RPA is the same as sc->sc_page0(SPDCTL_SPA0)
+		 * Write is SPA0, read is RPA.
+		 *
+		 * This call returns 0 on page 0 and returns -1 on page 1.
+		 * I don't know whether our icc_exec()'s API is good or not.
+		 */
+		rv = iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_page0,
+		    &reg, 1, NULL, 0, I2C_F_POLL);
+		if (rv != 0) {
+			/*
+			 * The possibilities are:
+			 * a) page 1 is selected.
+			 * b) The device doesn't support page select and
+			 *    it's not a SPD ROM.
+			 * Is there no way to distinguish them now?
+			 */
+			rv = iic_exec(sc->sc_tag, I2C_OP_WRITE_WITH_STOP,
+			    sc->sc_page0, &reg, 1, NULL, 0, I2C_F_POLL);
+			if (rv == 0) {
+				aprint_debug("Page 1 was selected. Page 0 is "
+				    "selected now.\n");
+			} else {
+				aprint_debug("Failed to select page 0. This "
+				    "device isn't SPD ROM\n");
+			}
+		} else {
+			/* This device isn't SPD ROM */
+			rv = -1;
+		}
+	}
+error:
+	iic_release_bus(sc->sc_tag, 0);
+
+	return rv;
+}
+
+static int
 spdmem_i2c_match(device_t parent, cfdata_t match, void *aux)
 {
 	struct i2c_attach_args *ia = aux;
@@ -122,6 +196,10 @@ spdmem_i2c_match(device_t parent, cfdata
 	sc.sc_page1 = SPDCTL_SPA1;
 	sc.sc_base.sc_read = spdmem_i2c_read;
 
+	/* Check the bank and reset to the page 0 */
+	if (spdmem_reset_page(&sc) != 0)
+		return 0;
+
 	return spdmem_common_probe(&sc.sc_base);
 }
 

Index: src/sys/dev/ic/spdmem.c
diff -u src/sys/dev/ic/spdmem.c:1.21 src/sys/dev/ic/spdmem.c:1.22
--- src/sys/dev/ic/spdmem.c:1.21	Tue Jan  5 11:49:32 2016
+++ src/sys/dev/ic/spdmem.c	Fri Sep  9 05:36:59 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: spdmem.c,v 1.21 2016/01/05 11:49:32 msaitoh Exp $ */
+/* $NetBSD: spdmem.c,v 1.22 2016/09/09 05:36:59 msaitoh Exp $ */
 
 /*
  * Copyright (c) 2007 Nicolas Joly
@@ -35,7 +35,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: spdmem.c,v 1.21 2016/01/05 11:49:32 msaitoh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: spdmem.c,v 1.22 2016/09/09 05:36:59 msaitoh Exp $");
 
 #include <sys/param.h>
 #include <sys/device.h>
@@ -185,6 +185,10 @@ spdmem_common_probe(struct spdmem_softc 
 	if ((sc->sc_read)(sc, 2, &spd_type) != 0)
 		return 0;
 
+	/* Memory type should not be 0 */
+	if (spd_type == 0x00)
+		return 0;
+
 	/* For older memory types, validate the checksum over 1st 63 bytes */
 	if (spd_type <= SPDMEM_MEMTYPE_DDR2SDRAM) {
 		for (i = 0; i < 63; i++) {
@@ -263,8 +267,7 @@ spdmem_common_probe(struct spdmem_softc 
 		 * it some other time.
 		 */
 		return 1;
-	} else
-		return 0;
+	}
 
 	/* For unrecognized memory types, don't match at all */
 	return 0;

Reply via email to