Add code to select page "0" for DDR4 and newer SPD ROM. If the value read
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.
This commit is contained in:
parent
b1f13c5ca7
commit
bc91867bec
|
@ -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);
|
||||
|
@ -97,6 +98,79 @@ CFATTACH_DECL_NEW(spdmem_iic, sizeof(struct spdmem_i2c_softc),
|
|||
|
||||
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, ®, 1,
|
||||
&byte0, 1, I2C_F_POLL);
|
||||
rv |= iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, ®, 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,
|
||||
®, 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, ®, 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)
|
||||
{
|
||||
|
@ -122,6 +196,10 @@ spdmem_i2c_match(device_t parent, cfdata_t match, void *aux)
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 *sc)
|
|||
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 *sc)
|
|||
* it some other time.
|
||||
*/
|
||||
return 1;
|
||||
} else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For unrecognized memory types, don't match at all */
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue