diff options
author | Jonathan Gray <jsg@cvs.openbsd.org> | 2007-10-07 14:57:17 +0000 |
---|---|---|
committer | Jonathan Gray <jsg@cvs.openbsd.org> | 2007-10-07 14:57:17 +0000 |
commit | eacee2a399020294ebf22ae5dbb806477c8e6f51 (patch) | |
tree | 56949798c14a2d94c3c2b6b3da1f8bed1093820a /sys/dev/i2c | |
parent | 7d92b70afc5f7262e20f6e1eb0639d74d9c8c0a5 (diff) |
Port of Nicolas Joly, Paul Goyette & Tobias Nygren's
spdmem(4) driver for Serial Presence Detect memory information.
Diffstat (limited to 'sys/dev/i2c')
-rw-r--r-- | sys/dev/i2c/files.i2c | 7 | ||||
-rw-r--r-- | sys/dev/i2c/spdmem.c | 377 |
2 files changed, 383 insertions, 1 deletions
diff --git a/sys/dev/i2c/files.i2c b/sys/dev/i2c/files.i2c index 76b5f0e0e2e..2fe83c09cd3 100644 --- a/sys/dev/i2c/files.i2c +++ b/sys/dev/i2c/files.i2c @@ -1,4 +1,4 @@ -# $OpenBSD: files.i2c,v 1.40 2007/09/09 15:59:47 deraadt Exp $ +# $OpenBSD: files.i2c,v 1.41 2007/10/07 14:57:16 jsg Exp $ # $NetBSD: files.i2c,v 1.3 2003/10/20 16:24:10 briggs Exp $ define i2c {[addr = -1], [size = -1]} @@ -137,3 +137,8 @@ file dev/i2c/asc7621.c adl device thmc attach thmc at i2c file dev/i2c/thmc50.c thmc + +# SPD Memory EEPROM +device spdmem +attach spdmem at i2c +file dev/i2c/spdmem.c spdmem diff --git a/sys/dev/i2c/spdmem.c b/sys/dev/i2c/spdmem.c new file mode 100644 index 00000000000..6a642b4a0f5 --- /dev/null +++ b/sys/dev/i2c/spdmem.c @@ -0,0 +1,377 @@ +/* $OpenBSD: spdmem.c,v 1.1 2007/10/07 14:57:16 jsg Exp $ */ +/* $NetBSD: spdmem.c,v 1.3 2007/09/20 23:09:59 xtraeme Exp $ */ + +/* + * Copyright (c) 2007 Nicolas Joly + * Copyright (c) 2007 Paul Goyette + * Copyright (c) 2007 Tobias Nygren + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Serial Presence Detect (SPD) memory identification + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <dev/i2c/i2cvar.h> + +/* possible values for the memory type */ +#define SPDMEM_MEMTYPE_FPM 0x01 +#define SPDMEM_MEMTYPE_EDO 0x02 +#define SPDMEM_MEMTYPE_PIPE_NIBBLE 0x03 +#define SPDMEM_MEMTYPE_SDRAM 0x04 +#define SPDMEM_MEMTYPE_ROM 0x05 +#define SPDMEM_MEMTYPE_DDRSGRAM 0x06 +#define SPDMEM_MEMTYPE_DDRSDRAM 0x07 +#define SPDMEM_MEMTYPE_DDR2SDRAM 0x08 + +/* possible values for the supply voltage */ +#define SPDMEM_VOLTAGE_TTL_5V 0x00 +#define SPDMEM_VOLTAGE_TTL_LV 0x01 +#define SPDMEM_VOLTAGE_HSTTL_1_5V 0x02 +#define SPDMEM_VOLTAGE_SSTL_3_3V 0x03 +#define SPDMEM_VOLTAGE_SSTL_2_5V 0x04 +#define SPDMEM_VOLTAGE_SSTL_1_8V 0x05 + +/* possible values for module configuration */ +#define SPDMEM_MODCONFIG_PARITY 0x01 +#define SPDMEM_MODCONFIG_ECC 0x02 + +/* for DDR2, module configuration is a bit-mask field */ +#define SPDMEM_MODCONFIG_HAS_DATA_PARITY 0x01 +#define SPDMEM_MODCONFIG_HAS_DATA_ECC 0x02 +#define SPDMEM_MODCONFIG_HAS_ADDR_CMD_PARITY 0x04 + +/* possible values for the refresh field */ +#define SPDMEM_REFRESH_STD 0x00 +#define SPDMEM_REFRESH_QUARTER 0x01 +#define SPDMEM_REFRESH_HALF 0x02 +#define SPDMEM_REFRESH_TWOX 0x03 +#define SPDMEM_REFRESH_FOURX 0x04 +#define SPDMEM_REFRESH_EIGHTX 0x05 +#define SPDMEM_REFRESH_SELFREFRESH 0x80 + +/* superset types */ +#define SPDMEM_SUPERSET_ESDRAM 0x01 +#define SPDMEM_SUPERSET_DDR_ESDRAM 0x02 +#define SPDMEM_SUPERSET_EDO_PEM 0x03 +#define SPDMEM_SUPERSET_SDR_PEM 0x04 + +/* FPM and EDO DIMMS */ +#define SPDMEM_FPM_ROWS 0x00 +#define SPDMEM_FPM_COLS 0x01 +#define SPDMEM_FPM_BANKS 0x02 +#define SPDMEM_FPM_CONFIG 0x08 +#define SPDMEM_FPM_REFRESH 0x09 +#define SPDMEM_FPM_SUPERSET 0x0c + +/* PC66/PC100/PC133 SDRAM */ +#define SPDMEM_SDR_ROWS 0x00 +#define SPDMEM_SDR_COLS 0x01 +#define SPDMEM_SDR_BANKS 0x02 +#define SPDMEM_SDR_BANKS_PER_CHIP 0x0e +#define SPDMEM_SDR_SUPERSET 0x1d + +/* Dual Data Rate SDRAM */ +#define SPDMEM_DDR_ROWS 0x00 +#define SPDMEM_DDR_COLS 0x01 +#define SPDMEM_DDR_RANKS 0x02 +#define SPDMEM_DDR_DATAWIDTH 0x03 +#define SPDMEM_DDR_VOLTAGE 0x05 +#define SPDMEM_DDR_CYCLE 0x06 +#define SPDMEM_DDR_REFRESH 0x09 +#define SPDMEM_DDR_BANKS_PER_CHIP 0x0e +#define SPDMEM_DDR_SUPERSET 0x1d + +/* Dual Data Rate 2 SDRAM */ +#define SPDMEM_DDR2_ROWS 0x00 +#define SPDMEM_DDR2_COLS 0x01 +#define SPDMEM_DDR2_RANKS 0x02 +#define SPDMEM_DDR2_DATAWIDTH 0x03 +#define SPDMEM_DDR2_VOLTAGE 0x05 +#define SPDMEM_DDR2_CYCLE 0x06 +#define SPDMEM_DDR2_BANKS_PER_CHIP 0x0e + +/* Direct Rambus DRAM */ +#define SPDMEM_RDR_ROWS_COLS 0x00 + +struct spdmem { + uint8_t sm_len; + uint8_t sm_size; + uint8_t sm_type; + uint8_t sm_data[60]; + uint8_t sm_cksum; +} __packed; + +#define SPDMEM_TYPE_MAXLEN 16 +struct spdmem_softc { + struct device sc_dev; + i2c_tag_t sc_tag; + i2c_addr_t sc_addr; + struct spdmem sc_spd_data; + char sc_type[SPDMEM_TYPE_MAXLEN]; +}; + +int spdmem_match(struct device *, void *, void *); +void spdmem_attach(struct device *, struct device *, void *); +uint8_t spdmem_read(struct spdmem_softc *, uint8_t); +void spdmem_hexdump(struct spdmem_softc *, int, int); + +struct cfattach spdmem_ca = { + sizeof(struct spdmem_softc), spdmem_match, spdmem_attach +}; + +struct cfdriver spdmem_cd = { + NULL, "spdmem", DV_DULL +}; + +#define IS_RAMBUS_TYPE (s->sm_len < 4) + +static const char* spdmem_basic_types[] = { + "unknown", + "FPM", + "EDO", + "Pipelined Nibble", + "SDRAM", + "ROM", + "DDR SGRAM", + "DDR SDRAM", + "DDR2 SDRAM", + "DDR2 SDRAM FB", + "DDR2 SDRAM FB Probe" +}; + +static const char* spdmem_superset_types[] = { + "unknown", + "ESDRAM", + "DDR ESDRAM", + "PEM EDO", + "PEM SDRAM" +}; + +static const char* spdmem_parity_types[] = { + "no parity or ECC", + "data parity", + "data ECC", + "data parity and ECC", + "cmd/addr parity", + "cmd/addr/data parity", + "cmd/addr parity, data ECC", + "cmd/addr/data parity, data ECC" +}; + +int +spdmem_match(struct device *parent, void *match, void *aux) +{ + struct i2c_attach_args *ia = aux; + + if (strcmp(ia->ia_name, "spd") == 0) + return (1); + return (0); +} + +void +spdmem_attach(struct device *parent, struct device *self, void *aux) +{ + struct spdmem_softc *sc = (struct spdmem_softc *)self; + struct i2c_attach_args *ia = aux; + struct spdmem *s = &(sc->sc_spd_data); + const char *type; + const char *ddr_type_string = NULL; + int num_banks = 0; + int per_chip = 0; + int dimm_size, cycle_time, d_clk, p_clk, bits; + int i; + uint8_t config, rows, cols; + + sc->sc_tag = ia->ia_tag; + sc->sc_addr = ia->ia_addr; + + /* All SPD have at least 64 bytes of data including checksum */ + for (i = 0; i < 64; i++) { + ((uint8_t *)s)[i] = spdmem_read(sc, i); + } + +#if 0 + for (i = 0; i < 64; i += 16) { + int j; + printf("\n%s: 0x%02x:", self->dv_xname, i); + for(j = 0; j < 16; j++) + printf(" %02x", ((uint8_t *)s)[i + j]); + } + printf("\n%s", self->dv_xname); +#endif + + /* + * Decode and print SPD contents + */ + if (IS_RAMBUS_TYPE) + type = "Rambus"; + else { + if (s->sm_type <= 10) + type = spdmem_basic_types[s->sm_type]; + else + type = "unknown"; + + if (s->sm_type == SPDMEM_MEMTYPE_EDO && + s->sm_data[SPDMEM_FPM_SUPERSET] == SPDMEM_SUPERSET_EDO_PEM) + type = spdmem_superset_types[SPDMEM_SUPERSET_EDO_PEM]; + if (s->sm_type == SPDMEM_MEMTYPE_SDRAM && + s->sm_data[SPDMEM_SDR_SUPERSET] == SPDMEM_SUPERSET_SDR_PEM) + type = spdmem_superset_types[SPDMEM_SUPERSET_SDR_PEM]; + if (s->sm_type == SPDMEM_MEMTYPE_DDRSDRAM && + s->sm_data[SPDMEM_DDR_SUPERSET] == + SPDMEM_SUPERSET_DDR_ESDRAM) + type = + spdmem_superset_types[SPDMEM_SUPERSET_DDR_ESDRAM]; + if (s->sm_type == SPDMEM_MEMTYPE_SDRAM && + s->sm_data[SPDMEM_SDR_SUPERSET] == SPDMEM_SUPERSET_ESDRAM) { + type = spdmem_superset_types[SPDMEM_SUPERSET_ESDRAM]; + } + } + + printf(": %s", type); + strlcpy(sc->sc_type, type, SPDMEM_TYPE_MAXLEN); + + if ((s->sm_type == SPDMEM_MEMTYPE_SDRAM || + s->sm_type == SPDMEM_MEMTYPE_DDRSDRAM || + s->sm_type == SPDMEM_MEMTYPE_DDR2SDRAM ) && + s->sm_data[SPDMEM_FPM_CONFIG] < 8) + printf(", %s", + spdmem_parity_types[s->sm_data[SPDMEM_FPM_CONFIG]]); + + dimm_size = 0; + if (IS_RAMBUS_TYPE) { + rows = s->sm_data[SPDMEM_RDR_ROWS_COLS] & 0x0f; + cols = s->sm_data[SPDMEM_RDR_ROWS_COLS] >> 4; + printf(", %dMB", 1 << (rows + cols - 13)); + } else if (s->sm_type == SPDMEM_MEMTYPE_SDRAM || + s->sm_type == SPDMEM_MEMTYPE_DDRSDRAM) { + rows = s->sm_data[SPDMEM_SDR_ROWS] & 0x0f; + cols = s->sm_data[SPDMEM_SDR_COLS] & 0x0f; + dimm_size = rows + cols - 17; + num_banks = s->sm_data[SPDMEM_SDR_BANKS]; + per_chip = s->sm_data[SPDMEM_SDR_BANKS_PER_CHIP]; + } else if (s->sm_type == SPDMEM_MEMTYPE_DDR2SDRAM) { + rows = s->sm_data[SPDMEM_DDR2_ROWS] & 0x1f; + cols = s->sm_data[SPDMEM_DDR2_COLS] & 0x0f; + dimm_size = rows + cols - 17; + num_banks = s->sm_data[SPDMEM_DDR_RANKS] + 1; + per_chip = s->sm_data[SPDMEM_DDR2_BANKS_PER_CHIP]; + } + if (!(IS_RAMBUS_TYPE) && num_banks <= 8 && per_chip <= 8 && + dimm_size > 0 && dimm_size <= 12) { + dimm_size = (1 << dimm_size) * num_banks * per_chip; + printf(", %dMB", dimm_size); + } + + /* cycle_time is expressed in units of 0.01 ns */ + cycle_time = 0; + if (s->sm_type == SPDMEM_MEMTYPE_DDRSDRAM || + s->sm_type == SPDMEM_MEMTYPE_SDRAM) + cycle_time = (s->sm_data[SPDMEM_DDR_CYCLE] >> 4) * 100 + + (s->sm_data[SPDMEM_DDR_CYCLE] & 0x0f) * 10; + else if (s->sm_type == SPDMEM_MEMTYPE_DDR2SDRAM) { + cycle_time = (s->sm_data[SPDMEM_DDR_CYCLE] >> 4) * 100 + + (s->sm_data[SPDMEM_DDR_CYCLE] & 0x0f); + } + + if (cycle_time != 0) { + /* + * cycle time is scaled by a factor of 100 to avoid using + * floating point. Calculate memory speed as the number + * of cycles per microsecond. + */ + d_clk = 100 * 1000; + config = s->sm_data[SPDMEM_FPM_CONFIG]; + switch (s->sm_type) { + case SPDMEM_MEMTYPE_DDR2SDRAM: + /* DDR2 uses quad-pumped clock */ + d_clk *= 4; + bits = s->sm_data[SPDMEM_DDR2_DATAWIDTH]; + if ((config & 0x03) != 0) + bits -= 8; + ddr_type_string = "PC2"; + break; + case SPDMEM_MEMTYPE_DDRSDRAM: + /* DDR uses dual-pumped clock */ + d_clk *= 2; + /* FALLTHROUGH */ + default: /* SPDMEM_MEMTYPE_SDRAM */ + bits = s->sm_data[SPDMEM_DDR_DATAWIDTH] | + (s->sm_data[SPDMEM_DDR_DATAWIDTH + 1] << 8); + if (config == 1 || config == 2) + bits -= 8; + ddr_type_string = "PC"; + } + d_clk /= cycle_time; + if (s->sm_type == SPDMEM_MEMTYPE_DDR2SDRAM) + d_clk = (d_clk + 1) / 2; + p_clk = d_clk * bits / 8; + if ((p_clk % 100) >= 50) + p_clk += 50; + p_clk -= p_clk % 100; + printf(", %dMHz, %s-%d", d_clk, ddr_type_string, p_clk); + } + + printf("\n"); +} + +uint8_t +spdmem_read(struct spdmem_softc *sc, uint8_t reg) +{ + uint8_t val; + + iic_acquire_bus(sc->sc_tag,0); + iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, ®, 1, + &val, 1, 0); + iic_release_bus(sc->sc_tag, 0); + + return val; +} + +void +spdmem_hexdump(struct spdmem_softc *sc, int start, int size) +{ + int i; + + uint8_t data[size]; + + for (i = 0; i < size; i++) { + data[i] = spdmem_read(sc, i); + } + + for (i = 0; i < size ; i += 16) { + int j; + printf("\n0x%02x:", start + i); + for(j = 0; j < 16; j++) + printf(" %02x", ((uint8_t *)data)[i + j]); + } + printf("\n"); +} |