From ddc208ed863c50de1b0049f18eda0963a62ec0ec Mon Sep 17 00:00:00 2001 From: Miod Vallat Date: Mon, 22 Mar 2010 21:20:59 +0000 Subject: Split existing spdmem@i2c code into bus-agnostic spd record decoding code, and an i2c attachment. No functional change; ok jsg@ deraadt@ --- sys/dev/i2c/files.i2c | 7 +- sys/dev/i2c/spdmem.c | 840 ----------------------------------------------- sys/dev/i2c/spdmem_i2c.c | 123 +++++++ sys/dev/spdmem.c | 794 ++++++++++++++++++++++++++++++++++++++++++++ sys/dev/spdmemvar.h | 69 ++++ 5 files changed, 989 insertions(+), 844 deletions(-) delete mode 100644 sys/dev/i2c/spdmem.c create mode 100644 sys/dev/i2c/spdmem_i2c.c create mode 100644 sys/dev/spdmem.c create mode 100644 sys/dev/spdmemvar.h (limited to 'sys/dev') diff --git a/sys/dev/i2c/files.i2c b/sys/dev/i2c/files.i2c index d1c69642cd5..ae5e5376af5 100644 --- a/sys/dev/i2c/files.i2c +++ b/sys/dev/i2c/files.i2c @@ -1,4 +1,4 @@ -# $OpenBSD: files.i2c,v 1.48 2009/08/12 20:41:21 deraadt Exp $ +# $OpenBSD: files.i2c,v 1.49 2010/03/22 21:20:58 miod Exp $ # $NetBSD: files.i2c,v 1.3 2003/10/20 16:24:10 briggs Exp $ define i2c {[addr = -1], [size = -1]} @@ -149,9 +149,8 @@ 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 +attach spdmem at i2c with spdmem_iic +file dev/i2c/spdmem_i2c.c spdmem # SO-DIMM (JC-42.4) temperature sensor device sdtemp diff --git a/sys/dev/i2c/spdmem.c b/sys/dev/i2c/spdmem.c deleted file mode 100644 index 48bc924751a..00000000000 --- a/sys/dev/i2c/spdmem.c +++ /dev/null @@ -1,840 +0,0 @@ -/* $OpenBSD: spdmem.c,v 1.33 2009/09/13 23:36:10 jsg Exp $ */ -/* $NetBSD: spdmem.c,v 1.3 2007/09/20 23:09:59 xtraeme Exp $ */ - -/* - * Copyright (c) 2007 Jonathan Gray - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* - * 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 -#include -#include - -#include - -/* Encodings of the size used/total byte for certain memory types */ -#define SPDMEM_SPDSIZE_MASK 0x0F /* SPD EEPROM Size */ - -#define SPDMEM_SPDLEN_128 0x00 /* SPD EEPROM Sizes */ -#define SPDMEM_SPDLEN_176 0x10 -#define SPDMEM_SPDLEN_256 0x20 -#define SPDMEM_SPDLEN_MASK 0x70 /* Bits 4 - 6 */ - -#define SPDMEM_SPDCRC_116 0x80 /* CRC Bytes covered */ -#define SPDMEM_SPDCRC_125 0x00 -#define SPDMEM_SPDCRC_MASK 0x80 /* Bit 7 */ - - -/* 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 -#define SPDMEM_MEMTYPE_FBDIMM 0x09 -#define SPDMEM_MEMTYPE_FBDIMM_PROBE 0x0a -#define SPDMEM_MEMTYPE_DDR3SDRAM 0x0b -#define SPDMEM_MEMTYPE_NONE 0xff - -#define SPDMEM_MEMTYPE_DIRECT_RAMBUS 0x01 -#define SPDMEM_MEMTYPE_RAMBUS 0x11 - -/* 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_CYCLE 0x06 -#define SPDMEM_SDR_BANKS_PER_CHIP 0x0e -#define SPDMEM_SDR_MOD_ATTRIB 0x12 -#define SPDMEM_SDR_SUPERSET 0x1d - -#define SPDMEM_SDR_FREQUENCY 126 -#define SPDMEM_SDR_CAS 127 -#define SPDMEM_SDR_FREQ_66 0x66 -#define SPDMEM_SDR_FREQ_100 0x64 -#define SPDMEM_SDR_FREQ_133 0x85 -#define SPDMEM_SDR_CAS2 (1 << 1) -#define SPDMEM_SDR_CAS3 (1 << 2) - -/* Rambus Direct DRAM */ -#define SPDMEM_RDR_MODULE_TYPE 0x00 -#define SPDMEM_RDR_ROWS_COLS 0x01 -#define SPDMEM_RDR_BANK 0x02 - -#define SPDMEM_RDR_TYPE_RIMM 1 -#define SPDMEM_RDR_TYPE_SORIMM 2 -#define SPDMEM_RDR_TYPE_EMBED 3 -#define SPDMEM_RDR_TYPE_RIMM32 4 - -/* 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_CAS 0x0f -#define SPDMEM_DDR_MOD_ATTRIB 0x12 -#define SPDMEM_DDR_SUPERSET 0x1d - -#define SPDMEM_DDR_ATTRIB_REG (1 << 1) - -/* 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_DIMMTYPE 0x11 -#define SPDMEM_DDR2_RANK_DENSITY 0x1c - -#define SPDMEM_DDR2_TYPE_REGMASK ((1 << 4) | (1 << 0)) -#define SPDMEM_DDR2_SODIMM (1 << 2) -#define SPDMEM_DDR2_MICRO_DIMM (1 << 3) -#define SPDMEM_DDR2_MINI_RDIMM (1 << 4) -#define SPDMEM_DDR2_MINI_UDIMM (1 << 5) - -/* DDR2 FB-DIMM SDRAM */ -#define SPDMEM_FBDIMM_ADDR 0x01 -#define SPDMEM_FBDIMM_RANKS 0x04 -#define SPDMEM_FBDIMM_MTB_DIVIDEND 0x06 -#define SPDMEM_FBDIMM_MTB_DIVISOR 0x07 -#define SPDMEM_FBDIMM_PROTO 0x4e - -#define SPDMEM_FBDIMM_RANKS_WIDTH 0x07 -#define SPDMEM_FBDIMM_ADDR_BANKS 0x02 -#define SPDMEM_FBDIMM_ADDR_COL 0x0c -#define SPDMEM_FBDIMM_ADDR_COL_SHIFT 2 -#define SPDMEM_FBDIMM_ADDR_ROW 0xe0 -#define SPDMEM_FBDIMM_ADDR_ROW_SHIFT 5 -#define SPDMEM_FBDIMM_PROTO_ECC (1 << 1) - - -/* Dual Data Rate 3 SDRAM */ -#define SPDMEM_DDR3_MODTYPE 0x00 -#define SPDMEM_DDR3_DENSITY 0x01 -#define SPDMEM_DDR3_MOD_ORG 0x04 -#define SPDMEM_DDR3_DATAWIDTH 0x05 -#define SPDMEM_DDR3_MTB_DIVIDEND 0x07 -#define SPDMEM_DDR3_MTB_DIVISOR 0x08 -#define SPDMEM_DDR3_TCKMIN 0x09 -#define SPDMEM_DDR3_THERMAL 0x1d - -#define SPDMEM_DDR3_DENSITY_CAPMASK 0x0f -#define SPDMEM_DDR3_MOD_ORG_CHIPWIDTH_MASK 0x07 -#define SPDMEM_DDR3_MOD_ORG_BANKS_SHIFT 3 -#define SPDMEM_DDR3_MOD_ORG_BANKS_MASK 0x07 -#define SPDMEM_DDR3_DATAWIDTH_ECCMASK (1 << 3) -#define SPDMEM_DDR3_DATAWIDTH_PRIMASK 0x07 -#define SPDMEM_DDR3_THERMAL_PRESENT (1 << 7) - -#define SPDMEM_DDR3_RDIMM 0x01 -#define SPDMEM_DDR3_UDIMM 0x02 -#define SPDMEM_DDR3_SODIMM 0x03 -#define SPDMEM_DDR3_MICRO_DIMM 0x04 -#define SPDMEM_DDR3_MINI_RDIMM 0x05 -#define SPDMEM_DDR3_MINI_UDIMM 0x06 - -static const uint8_t ddr2_cycle_tenths[] = { - 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 25, 33, 66, 75, 0, 0 -}; - -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; -}; - -uint16_t spdmem_crc16(struct spdmem_softc *, int); -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_sdram_decode(struct spdmem_softc *, struct spdmem *); -void spdmem_rdr_decode(struct spdmem_softc *, struct spdmem *); -void spdmem_ddr_decode(struct spdmem_softc *, struct spdmem *); -void spdmem_ddr2_decode(struct spdmem_softc *, struct spdmem *); -void spdmem_fbdimm_decode(struct spdmem_softc *, struct spdmem *); -void spdmem_ddr3_decode(struct spdmem_softc *, struct spdmem *); - -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-DIMM", - "DDR2 SDRAM FB-DIMM Probe", - "DDR3 SDRAM" -}; - -static const char *spdmem_superset_types[] = { - "unknown", - "ESDRAM", - "DDR ESDRAM", - "PEM EDO", - "PEM SDRAM" -}; - -static const char *spdmem_parity_types[] = { - "non-parity", - "data parity", - "ECC", - "data parity and ECC", - "cmd/addr parity", - "cmd/addr/data parity", - "cmd/addr parity, data ECC", - "cmd/addr/data parity, data ECC" -}; - -/* CRC functions used for certain memory types */ -uint16_t -spdmem_crc16(struct spdmem_softc *sc, int count) -{ - uint16_t crc; - int i, j; - uint8_t val; - crc = 0; - for (j = 0; j <= count; j++) { - val = spdmem_read(sc, j); - crc = crc ^ val << 8; - for (i = 0; i < 8; ++i) - if (crc & 0x8000) - crc = crc << 1 ^ 0x1021; - else - crc = crc << 1; - } - return (crc & 0xFFFF); -} - -int -spdmem_match(struct device *parent, void *match, void *aux) -{ - struct i2c_attach_args *ia = aux; - struct spdmem_softc sc; - uint8_t i, val, type; - int cksum = 0; - int spd_len, spd_crc_cover; - uint16_t crc_calc, crc_spd; - - /* clever attachments like openfirmware informed macppc */ - if (strcmp(ia->ia_name, "spd") == 0) - return (1); - - /* dumb, need sanity checks */ - if (strcmp(ia->ia_name, "eeprom") != 0) - return (0); - - sc.sc_tag = ia->ia_tag; - sc.sc_addr = ia->ia_addr; - - type = spdmem_read(&sc, 2); - /* For older memory types, validate the checksum over 1st 63 bytes */ - if (type <= SPDMEM_MEMTYPE_DDR2SDRAM) { - for (i = 0; i < 63; i++) - cksum += spdmem_read(&sc, i); - - val = spdmem_read(&sc, 63); - - if (cksum == 0 || (cksum & 0xff) != val) { - return 0; - } else - return 1; - } - - /* For DDR3 and FBDIMM, verify the CRC */ - else if (type <= SPDMEM_MEMTYPE_DDR3SDRAM) { - spd_len = spdmem_read(&sc, 0); - if (spd_len && SPDMEM_SPDCRC_116) - spd_crc_cover = 116; - else - spd_crc_cover = 125; - switch (spd_len & SPDMEM_SPDLEN_MASK) { - case SPDMEM_SPDLEN_128: - spd_len = 128; - break; - case SPDMEM_SPDLEN_176: - spd_len = 176; - break; - case SPDMEM_SPDLEN_256: - spd_len = 256; - break; - default: - return 0; - } - if (spd_crc_cover > spd_len) - return 0; - crc_calc = spdmem_crc16(&sc, spd_crc_cover); - crc_spd = spdmem_read(&sc, 127) << 8; - crc_spd |= spdmem_read(&sc, 126); - if (crc_calc != crc_spd) { - return 0; - } - return 1; - } - - return (0); -} - -void -spdmem_sdram_decode(struct spdmem_softc *sc, struct spdmem *s) -{ - const char *type; - int dimm_size, p_clk; - int num_banks, per_chip; - uint8_t rows, cols; - - type = spdmem_basic_types[s->sm_type]; - - if (s->sm_data[SPDMEM_SDR_SUPERSET] == SPDMEM_SUPERSET_SDR_PEM) - type = spdmem_superset_types[SPDMEM_SUPERSET_SDR_PEM]; - if (s->sm_data[SPDMEM_SDR_SUPERSET] == SPDMEM_SUPERSET_ESDRAM) - type = spdmem_superset_types[SPDMEM_SUPERSET_ESDRAM]; - - num_banks = s->sm_data[SPDMEM_SDR_BANKS]; - per_chip = s->sm_data[SPDMEM_SDR_BANKS_PER_CHIP]; - rows = s->sm_data[SPDMEM_SDR_ROWS] & 0x0f; - cols = s->sm_data[SPDMEM_SDR_COLS] & 0x0f; - dimm_size = (1 << (rows + cols - 17)) * num_banks * per_chip; - - if (dimm_size > 0) { - if (dimm_size < 1024) - printf(" %dMB", dimm_size); - else - printf(" %dGB", dimm_size / 1024); - } - - printf(" %s", type); - - if (s->sm_data[SPDMEM_DDR_MOD_ATTRIB] & SPDMEM_DDR_ATTRIB_REG) - printf(" registered"); - - if (s->sm_data[SPDMEM_FPM_CONFIG] < 8) - printf(" %s", - spdmem_parity_types[s->sm_data[SPDMEM_FPM_CONFIG]]); - - p_clk = 66; - if (s->sm_len >= 128) { - switch (spdmem_read(sc, SPDMEM_SDR_FREQUENCY)) { - case SPDMEM_SDR_FREQ_100: - case SPDMEM_SDR_FREQ_133: - /* We need to check ns to decide here */ - if (s->sm_data[SPDMEM_SDR_CYCLE] < 0x80) - p_clk = 133; - else - p_clk = 100; - break; - case SPDMEM_SDR_FREQ_66: - default: - p_clk = 66; - break; - } - } - printf(" PC%d", p_clk); - - /* Print CAS latency */ - if (s->sm_len < 128) - return; - if (spdmem_read(sc, SPDMEM_SDR_CAS) & SPDMEM_SDR_CAS2) - printf("CL2"); - else if (spdmem_read(sc, SPDMEM_SDR_CAS) & SPDMEM_SDR_CAS3) - printf("CL3"); -} - -void -spdmem_rdr_decode(struct spdmem_softc *sc, struct spdmem *s) -{ - int rimm_size; - uint8_t row_bits, col_bits, bank_bits; - - row_bits = s->sm_data[SPDMEM_RDR_ROWS_COLS] >> 4; - col_bits = s->sm_data[SPDMEM_RDR_ROWS_COLS] & 0x0f; - bank_bits = s->sm_data[SPDMEM_RDR_BANK] & 0x07; - - /* subtracting 13 here is a cheaper way of dividing by 8k later */ - rimm_size = 1 << (row_bits + col_bits + bank_bits - 13); - - if (rimm_size < 1024) - printf(" %dMB ", rimm_size); - else - printf(" %dGB ", rimm_size / 1024); - - switch(s->sm_data[SPDMEM_RDR_MODULE_TYPE]) { - case SPDMEM_RDR_TYPE_RIMM: - printf("RIMM"); - break; - case SPDMEM_RDR_TYPE_SORIMM: - printf("SO-RIMM"); - break; - case SPDMEM_RDR_TYPE_EMBED: - printf("Embedded Rambus"); - break; - case SPDMEM_RDR_TYPE_RIMM32: - printf("RIMM32"); - break; - } -} - -void -spdmem_ddr_decode(struct spdmem_softc *sc, struct spdmem *s) -{ - const char *type; - int dimm_size, cycle_time, d_clk, p_clk, bits; - int i, num_banks, per_chip; - uint8_t config, rows, cols, cl; - - type = spdmem_basic_types[s->sm_type]; - - if (s->sm_data[SPDMEM_DDR_SUPERSET] == SPDMEM_SUPERSET_DDR_ESDRAM) - type = spdmem_superset_types[SPDMEM_SUPERSET_DDR_ESDRAM]; - - num_banks = s->sm_data[SPDMEM_SDR_BANKS]; - per_chip = s->sm_data[SPDMEM_SDR_BANKS_PER_CHIP]; - rows = s->sm_data[SPDMEM_SDR_ROWS] & 0x0f; - cols = s->sm_data[SPDMEM_SDR_COLS] & 0x0f; - dimm_size = (1 << (rows + cols - 17)) * num_banks * per_chip; - - if (dimm_size > 0) { - if (dimm_size < 1024) - printf(" %dMB", dimm_size); - else - printf(" %dGB", dimm_size / 1024); - } - - printf(" %s", type); - - if (s->sm_data[SPDMEM_DDR_MOD_ATTRIB] & SPDMEM_DDR_ATTRIB_REG) - printf(" registered"); - - if (s->sm_data[SPDMEM_FPM_CONFIG] < 8) - printf(" %s", - spdmem_parity_types[s->sm_data[SPDMEM_FPM_CONFIG]]); - - /* cycle_time is expressed in units of 0.01 ns */ - cycle_time = (s->sm_data[SPDMEM_DDR_CYCLE] >> 4) * 100 + - (s->sm_data[SPDMEM_DDR_CYCLE] & 0x0f) * 10; - - 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. - * DDR uses dual-pumped clock - */ - d_clk = 100 * 1000 * 2; - config = s->sm_data[SPDMEM_FPM_CONFIG]; - bits = s->sm_data[SPDMEM_DDR_DATAWIDTH] | - (s->sm_data[SPDMEM_DDR_DATAWIDTH + 1] << 8); - if (config == 1 || config == 2) - bits -= 8; - - d_clk /= cycle_time; - p_clk = d_clk * bits / 8; - if ((p_clk % 100) >= 50) - p_clk += 50; - p_clk -= p_clk % 100; - printf(" PC%d", p_clk); - } - - /* Print CAS latency */ - for (i = 6; i >= 0; i--) { - if (s->sm_data[SPDMEM_DDR_CAS] & (1 << i)) { - cl = ((i * 10) / 2) + 10; - printf("CL%d.%d", cl / 10, cl % 10); - break; - } - } -} - -void -spdmem_ddr2_decode(struct spdmem_softc *sc, struct spdmem *s) -{ - const char *type; - int dimm_size, cycle_time, d_clk, p_clk, bits; - int i, num_ranks, density; - uint8_t config; - - type = spdmem_basic_types[s->sm_type]; - - num_ranks = (s->sm_data[SPDMEM_DDR2_RANKS] & 0x7) + 1; - density = (s->sm_data[SPDMEM_DDR2_RANK_DENSITY] & 0xf0) | - ((s->sm_data[SPDMEM_DDR2_RANK_DENSITY] & 0x0f) << 8); - dimm_size = num_ranks * density * 4; - - if (dimm_size > 0) { - if (dimm_size < 1024) - printf(" %dMB", dimm_size); - else - printf(" %dGB", dimm_size / 1024); - } - - printf(" %s", type); - - if (s->sm_data[SPDMEM_DDR2_DIMMTYPE] & SPDMEM_DDR2_TYPE_REGMASK) - printf(" registered"); - - if (s->sm_data[SPDMEM_FPM_CONFIG] < 8) - printf(" %s", - spdmem_parity_types[s->sm_data[SPDMEM_FPM_CONFIG]]); - - /* cycle_time is expressed in units of 0.01 ns */ - cycle_time = (s->sm_data[SPDMEM_DDR2_CYCLE] >> 4) * 100 + - ddr2_cycle_tenths[(s->sm_data[SPDMEM_DDR2_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. - * DDR2 uses quad-pumped clock - */ - d_clk = 100 * 1000 * 4; - config = s->sm_data[SPDMEM_FPM_CONFIG]; - bits = s->sm_data[SPDMEM_DDR2_DATAWIDTH]; - if ((config & 0x03) != 0) - bits -= 8; - d_clk /= cycle_time; - d_clk = (d_clk + 1) / 2; - p_clk = d_clk * bits / 8; - p_clk -= p_clk % 100; - printf(" PC2-%d", p_clk); - } - - /* Print CAS latency */ - for (i = 5; i >= 2; i--) { - if (s->sm_data[SPDMEM_DDR_CAS] & (i << i)) { - printf("CL%d", i); - break; - } - } - - switch (s->sm_data[SPDMEM_DDR2_DIMMTYPE]) { - case SPDMEM_DDR2_SODIMM: - printf(" SO-DIMM"); - break; - case SPDMEM_DDR2_MICRO_DIMM: - printf(" Micro-DIMM"); - break; - case SPDMEM_DDR2_MINI_RDIMM: - case SPDMEM_DDR2_MINI_UDIMM: - printf(" Mini-DIMM"); - break; - } -} - -void -spdmem_fbdimm_decode(struct spdmem_softc *sc, struct spdmem *s) -{ - int dimm_size, num_banks, cycle_time, d_clk, p_clk, bits; - uint8_t rows, cols, banks, dividend, divisor; - /* - * FB-DIMM is very much like DDR3 - */ - - banks = s->sm_data[SPDMEM_FBDIMM_ADDR] & SPDMEM_FBDIMM_ADDR_BANKS; - cols = (s->sm_data[SPDMEM_FBDIMM_ADDR] & SPDMEM_FBDIMM_ADDR_COL) >> - SPDMEM_FBDIMM_ADDR_COL_SHIFT; - rows = (s->sm_data[SPDMEM_FBDIMM_ADDR] & SPDMEM_FBDIMM_ADDR_ROW) >> - SPDMEM_FBDIMM_ADDR_ROW_SHIFT; - dimm_size = rows + 12 + cols + 9 - 20 - 3; - num_banks = 1 << (banks + 2); - - if (dimm_size < 1024) - printf(" %dMB", dimm_size); - else - printf(" %dGB", dimm_size / 1024); - - dividend = s->sm_data[SPDMEM_FBDIMM_MTB_DIVIDEND]; - divisor = s->sm_data[SPDMEM_FBDIMM_MTB_DIVISOR]; - - cycle_time = (1000 * dividend + (divisor / 2)) / divisor; - - if (cycle_time != 0) { - /* - * cycle time is scaled by a factor of 1000 to avoid using - * floating point. Calculate memory speed as the number - * of cycles per microsecond. - */ - d_clk = 1000 * 1000; - - /* DDR2 FB-DIMM uses a dual-pumped clock */ - d_clk *= 2; - bits = 1 << ((s->sm_data[SPDMEM_FBDIMM_RANKS] & - SPDMEM_FBDIMM_RANKS_WIDTH) + 2); - - p_clk = (d_clk * bits) / 8 / cycle_time; - d_clk = ((d_clk + cycle_time / 2) ) / cycle_time; - p_clk -= p_clk % 100; - printf(" PC2-%d", p_clk); - } -} - -void -spdmem_ddr3_decode(struct spdmem_softc *sc, struct spdmem *s) -{ - const char *type; - int dimm_size, cycle_time, d_clk, p_clk, bits; - uint8_t mtype, chipsize, dividend, divisor; - uint8_t datawidth, chipwidth, physbanks; - - type = spdmem_basic_types[s->sm_type]; - - chipsize = s->sm_data[SPDMEM_DDR3_DENSITY] & - SPDMEM_DDR3_DENSITY_CAPMASK; - datawidth = s->sm_data[SPDMEM_DDR3_DATAWIDTH] & - SPDMEM_DDR3_DATAWIDTH_PRIMASK; - chipwidth = s->sm_data[SPDMEM_DDR3_MOD_ORG] & - SPDMEM_DDR3_MOD_ORG_CHIPWIDTH_MASK; - physbanks = (s->sm_data[SPDMEM_DDR3_MOD_ORG] >> - SPDMEM_DDR3_MOD_ORG_BANKS_SHIFT) & SPDMEM_DDR3_MOD_ORG_BANKS_MASK; - - dimm_size = (chipsize + 28 - 20) - 3 + (datawidth + 3) - - (chipwidth + 2); - dimm_size = (1 << dimm_size) * (physbanks + 1); - - if (dimm_size < 1024) - printf(" %dMB", dimm_size); - else - printf(" %dGB", dimm_size / 1024); - - printf(" %s", type); - - mtype = s->sm_data[SPDMEM_DDR3_MODTYPE]; - if (mtype == SPDMEM_DDR3_RDIMM || mtype == SPDMEM_DDR3_MINI_RDIMM) - printf(" registered"); - - if (s->sm_data[SPDMEM_DDR3_DATAWIDTH] & SPDMEM_DDR3_DATAWIDTH_ECCMASK) - printf(" ECC"); - - dividend = s->sm_data[SPDMEM_DDR3_MTB_DIVIDEND]; - divisor = s->sm_data[SPDMEM_DDR3_MTB_DIVISOR]; - cycle_time = (1000 * dividend + (divisor / 2)) / divisor; - cycle_time *= s->sm_data[SPDMEM_DDR3_TCKMIN]; - - if (cycle_time != 0) { - /* - * cycle time is scaled by a factor of 1000 to avoid using - * floating point. Calculate memory speed as the number - * of cycles per microsecond. - * DDR3 uses a dual-pumped clock - */ - d_clk = 1000 * 1000; - d_clk *= 2; - bits = 1 << ((s->sm_data[SPDMEM_DDR3_DATAWIDTH] & - SPDMEM_DDR3_DATAWIDTH_PRIMASK) + 3); - /* - * Calculate p_clk first, since for DDR3 we need maximum - * significance. DDR3 rating is not rounded to a multiple - * of 100. This results in cycle_time of 1.5ns displayed - * as p_clk PC3-10666 (d_clk DDR3-1333) - */ - p_clk = (d_clk * bits) / 8 / cycle_time; - p_clk -= (p_clk % 100); - d_clk = ((d_clk + cycle_time / 2) ) / cycle_time; - printf(" PC3-%d", p_clk); - } - - switch (s->sm_data[SPDMEM_DDR3_MODTYPE]) { - case SPDMEM_DDR3_SODIMM: - printf(" SO-DIMM"); - break; - case SPDMEM_DDR3_MICRO_DIMM: - printf(" Micro-DIMM"); - break; - case SPDMEM_DDR3_MINI_RDIMM: - case SPDMEM_DDR3_MINI_UDIMM: - printf(" Mini-DIMM"); - break; - } - - if (s->sm_data[SPDMEM_DDR3_THERMAL] & SPDMEM_DDR3_THERMAL_PRESENT) - printf(" with thermal sensor"); -} - -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); - int i; - - sc->sc_tag = ia->ia_tag; - sc->sc_addr = ia->ia_addr; - - printf(":"); - - /* 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); - } - - /* - * Decode and print SPD contents - */ - if (s->sm_len < 4) { - if (s->sm_type == SPDMEM_MEMTYPE_DIRECT_RAMBUS) - spdmem_rdr_decode(sc, s); - else - printf(" no decode method for Rambus memory"); - } else { - switch(s->sm_type) { - case SPDMEM_MEMTYPE_EDO: - case SPDMEM_MEMTYPE_SDRAM: - spdmem_sdram_decode(sc, s); - break; - case SPDMEM_MEMTYPE_DDRSDRAM: - spdmem_ddr_decode(sc, s); - break; - case SPDMEM_MEMTYPE_DDR2SDRAM: - spdmem_ddr2_decode(sc, s); - break; - case SPDMEM_MEMTYPE_FBDIMM: - case SPDMEM_MEMTYPE_FBDIMM_PROBE: - spdmem_fbdimm_decode(sc, s); - break; - case SPDMEM_MEMTYPE_DDR3SDRAM: - spdmem_ddr3_decode(sc, s); - break; - case SPDMEM_MEMTYPE_NONE: - printf(" no EEPROM found"); - break; - default: - if (s->sm_type <= 10) - printf(" no decode method for %s memory", - spdmem_basic_types[s->sm_type]); - else - printf(" unknown memory type %d", s->sm_type); - break; - } - } - - printf("\n"); -} - -uint8_t -spdmem_read(struct spdmem_softc *sc, uint8_t reg) -{ - uint8_t val = 0xff; - - iic_acquire_bus(sc->sc_tag,0); - iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, - ®, sizeof reg, &val, sizeof val, 0); - iic_release_bus(sc->sc_tag, 0); - - return val; -} diff --git a/sys/dev/i2c/spdmem_i2c.c b/sys/dev/i2c/spdmem_i2c.c new file mode 100644 index 00000000000..d4df0d8b647 --- /dev/null +++ b/sys/dev/i2c/spdmem_i2c.c @@ -0,0 +1,123 @@ +/* $OpenBSD: spdmem_i2c.c,v 1.1 2010/03/22 21:20:58 miod Exp $ */ +/* $NetBSD: spdmem.c,v 1.3 2007/09/20 23:09:59 xtraeme Exp $ */ + +/* + * Copyright (c) 2007 Jonathan Gray + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * 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 +#include +#include + +#include +#include + +struct spdmem_iic_softc { + struct spdmem_softc sc_base; + i2c_tag_t sc_tag; + i2c_addr_t sc_addr; +}; + +int spdmem_iic_match(struct device *, void *, void *); +void spdmem_iic_attach(struct device *, struct device *, void *); +uint8_t spdmem_iic_read(struct spdmem_softc *, uint8_t); + +struct cfattach spdmem_iic_ca = { + sizeof(struct spdmem_iic_softc), spdmem_iic_match, spdmem_iic_attach +}; + +int +spdmem_iic_match(struct device *parent, void *match, void *aux) +{ + struct i2c_attach_args *ia = aux; + struct spdmem_iic_softc sc; + + /* clever attachments like openfirmware informed macppc */ + if (strcmp(ia->ia_name, "spd") == 0) + return (1); + + /* dumb, need sanity checks */ + if (strcmp(ia->ia_name, "eeprom") != 0) + return (0); + + sc.sc_tag = ia->ia_tag; + sc.sc_addr = ia->ia_addr; + sc.sc_base.sc_read = spdmem_iic_read; + + return spdmem_probe(&sc.sc_base); +} + +void +spdmem_iic_attach(struct device *parent, struct device *self, void *aux) +{ + struct spdmem_iic_softc *sc = (struct spdmem_iic_softc *)self; + struct i2c_attach_args *ia = aux; + + sc->sc_tag = ia->ia_tag; + sc->sc_addr = ia->ia_addr; + sc->sc_base.sc_read = spdmem_iic_read; + + printf(":"); + + spdmem_attach_common(&sc->sc_base); +} + +uint8_t +spdmem_iic_read(struct spdmem_softc *v, uint8_t reg) +{ + struct spdmem_iic_softc *sc = (struct spdmem_iic_softc *)v; + uint8_t val = 0xff; + + iic_acquire_bus(sc->sc_tag,0); + iic_exec(sc->sc_tag, I2C_OP_READ_WITH_STOP, sc->sc_addr, + ®, sizeof reg, &val, sizeof val, 0); + iic_release_bus(sc->sc_tag, 0); + + return val; +} diff --git a/sys/dev/spdmem.c b/sys/dev/spdmem.c new file mode 100644 index 00000000000..01af573d8d0 --- /dev/null +++ b/sys/dev/spdmem.c @@ -0,0 +1,794 @@ +/* $OpenBSD: spdmem.c,v 1.1 2010/03/22 21:20:56 miod Exp $ */ +/* $NetBSD: spdmem.c,v 1.3 2007/09/20 23:09:59 xtraeme Exp $ */ + +/* + * Copyright (c) 2007 Jonathan Gray + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * 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 +#include +#include + +#include + +/* Encodings of the size used/total byte for certain memory types */ +#define SPDMEM_SPDSIZE_MASK 0x0F /* SPD EEPROM Size */ + +#define SPDMEM_SPDLEN_128 0x00 /* SPD EEPROM Sizes */ +#define SPDMEM_SPDLEN_176 0x10 +#define SPDMEM_SPDLEN_256 0x20 +#define SPDMEM_SPDLEN_MASK 0x70 /* Bits 4 - 6 */ + +#define SPDMEM_SPDCRC_116 0x80 /* CRC Bytes covered */ +#define SPDMEM_SPDCRC_125 0x00 +#define SPDMEM_SPDCRC_MASK 0x80 /* Bit 7 */ + + +/* 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 +#define SPDMEM_MEMTYPE_FBDIMM 0x09 +#define SPDMEM_MEMTYPE_FBDIMM_PROBE 0x0a +#define SPDMEM_MEMTYPE_DDR3SDRAM 0x0b +#define SPDMEM_MEMTYPE_NONE 0xff + +#define SPDMEM_MEMTYPE_DIRECT_RAMBUS 0x01 +#define SPDMEM_MEMTYPE_RAMBUS 0x11 + +/* 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_CYCLE 0x06 +#define SPDMEM_SDR_BANKS_PER_CHIP 0x0e +#define SPDMEM_SDR_MOD_ATTRIB 0x12 +#define SPDMEM_SDR_SUPERSET 0x1d + +#define SPDMEM_SDR_FREQUENCY 126 +#define SPDMEM_SDR_CAS 127 +#define SPDMEM_SDR_FREQ_66 0x66 +#define SPDMEM_SDR_FREQ_100 0x64 +#define SPDMEM_SDR_FREQ_133 0x85 +#define SPDMEM_SDR_CAS2 (1 << 1) +#define SPDMEM_SDR_CAS3 (1 << 2) + +/* Rambus Direct DRAM */ +#define SPDMEM_RDR_MODULE_TYPE 0x00 +#define SPDMEM_RDR_ROWS_COLS 0x01 +#define SPDMEM_RDR_BANK 0x02 + +#define SPDMEM_RDR_TYPE_RIMM 1 +#define SPDMEM_RDR_TYPE_SORIMM 2 +#define SPDMEM_RDR_TYPE_EMBED 3 +#define SPDMEM_RDR_TYPE_RIMM32 4 + +/* 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_CAS 0x0f +#define SPDMEM_DDR_MOD_ATTRIB 0x12 +#define SPDMEM_DDR_SUPERSET 0x1d + +#define SPDMEM_DDR_ATTRIB_REG (1 << 1) + +/* 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_DIMMTYPE 0x11 +#define SPDMEM_DDR2_RANK_DENSITY 0x1c + +#define SPDMEM_DDR2_TYPE_REGMASK ((1 << 4) | (1 << 0)) +#define SPDMEM_DDR2_SODIMM (1 << 2) +#define SPDMEM_DDR2_MICRO_DIMM (1 << 3) +#define SPDMEM_DDR2_MINI_RDIMM (1 << 4) +#define SPDMEM_DDR2_MINI_UDIMM (1 << 5) + +/* DDR2 FB-DIMM SDRAM */ +#define SPDMEM_FBDIMM_ADDR 0x01 +#define SPDMEM_FBDIMM_RANKS 0x04 +#define SPDMEM_FBDIMM_MTB_DIVIDEND 0x06 +#define SPDMEM_FBDIMM_MTB_DIVISOR 0x07 +#define SPDMEM_FBDIMM_PROTO 0x4e + +#define SPDMEM_FBDIMM_RANKS_WIDTH 0x07 +#define SPDMEM_FBDIMM_ADDR_BANKS 0x02 +#define SPDMEM_FBDIMM_ADDR_COL 0x0c +#define SPDMEM_FBDIMM_ADDR_COL_SHIFT 2 +#define SPDMEM_FBDIMM_ADDR_ROW 0xe0 +#define SPDMEM_FBDIMM_ADDR_ROW_SHIFT 5 +#define SPDMEM_FBDIMM_PROTO_ECC (1 << 1) + + +/* Dual Data Rate 3 SDRAM */ +#define SPDMEM_DDR3_MODTYPE 0x00 +#define SPDMEM_DDR3_DENSITY 0x01 +#define SPDMEM_DDR3_MOD_ORG 0x04 +#define SPDMEM_DDR3_DATAWIDTH 0x05 +#define SPDMEM_DDR3_MTB_DIVIDEND 0x07 +#define SPDMEM_DDR3_MTB_DIVISOR 0x08 +#define SPDMEM_DDR3_TCKMIN 0x09 +#define SPDMEM_DDR3_THERMAL 0x1d + +#define SPDMEM_DDR3_DENSITY_CAPMASK 0x0f +#define SPDMEM_DDR3_MOD_ORG_CHIPWIDTH_MASK 0x07 +#define SPDMEM_DDR3_MOD_ORG_BANKS_SHIFT 3 +#define SPDMEM_DDR3_MOD_ORG_BANKS_MASK 0x07 +#define SPDMEM_DDR3_DATAWIDTH_ECCMASK (1 << 3) +#define SPDMEM_DDR3_DATAWIDTH_PRIMASK 0x07 +#define SPDMEM_DDR3_THERMAL_PRESENT (1 << 7) + +#define SPDMEM_DDR3_RDIMM 0x01 +#define SPDMEM_DDR3_UDIMM 0x02 +#define SPDMEM_DDR3_SODIMM 0x03 +#define SPDMEM_DDR3_MICRO_DIMM 0x04 +#define SPDMEM_DDR3_MINI_RDIMM 0x05 +#define SPDMEM_DDR3_MINI_UDIMM 0x06 + +static const uint8_t ddr2_cycle_tenths[] = { + 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 25, 33, 66, 75, 0, 0 +}; + +#define SPDMEM_TYPE_MAXLEN 16 + +uint16_t spdmem_crc16(struct spdmem_softc *, int); +static inline +uint8_t spdmem_read(struct spdmem_softc *, uint8_t); +void spdmem_sdram_decode(struct spdmem_softc *, struct spdmem *); +void spdmem_rdr_decode(struct spdmem_softc *, struct spdmem *); +void spdmem_ddr_decode(struct spdmem_softc *, struct spdmem *); +void spdmem_ddr2_decode(struct spdmem_softc *, struct spdmem *); +void spdmem_fbdimm_decode(struct spdmem_softc *, struct spdmem *); +void spdmem_ddr3_decode(struct spdmem_softc *, struct spdmem *); + +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-DIMM", + "DDR2 SDRAM FB-DIMM Probe", + "DDR3 SDRAM" +}; + +static const char *spdmem_superset_types[] = { + "unknown", + "ESDRAM", + "DDR ESDRAM", + "PEM EDO", + "PEM SDRAM" +}; + +static const char *spdmem_parity_types[] = { + "non-parity", + "data parity", + "ECC", + "data parity and ECC", + "cmd/addr parity", + "cmd/addr/data parity", + "cmd/addr parity, data ECC", + "cmd/addr/data parity, data ECC" +}; + +static inline uint8_t +spdmem_read(struct spdmem_softc *sc, uint8_t reg) +{ + return (*sc->sc_read)(sc, reg); +} + +/* CRC functions used for certain memory types */ +uint16_t +spdmem_crc16(struct spdmem_softc *sc, int count) +{ + uint16_t crc; + int i, j; + uint8_t val; + crc = 0; + for (j = 0; j <= count; j++) { + val = spdmem_read(sc, j); + crc = crc ^ val << 8; + for (i = 0; i < 8; ++i) + if (crc & 0x8000) + crc = crc << 1 ^ 0x1021; + else + crc = crc << 1; + } + return (crc & 0xFFFF); +} + +void +spdmem_sdram_decode(struct spdmem_softc *sc, struct spdmem *s) +{ + const char *type; + int dimm_size, p_clk; + int num_banks, per_chip; + uint8_t rows, cols; + + type = spdmem_basic_types[s->sm_type]; + + if (s->sm_data[SPDMEM_SDR_SUPERSET] == SPDMEM_SUPERSET_SDR_PEM) + type = spdmem_superset_types[SPDMEM_SUPERSET_SDR_PEM]; + if (s->sm_data[SPDMEM_SDR_SUPERSET] == SPDMEM_SUPERSET_ESDRAM) + type = spdmem_superset_types[SPDMEM_SUPERSET_ESDRAM]; + + num_banks = s->sm_data[SPDMEM_SDR_BANKS]; + per_chip = s->sm_data[SPDMEM_SDR_BANKS_PER_CHIP]; + rows = s->sm_data[SPDMEM_SDR_ROWS] & 0x0f; + cols = s->sm_data[SPDMEM_SDR_COLS] & 0x0f; + dimm_size = (1 << (rows + cols - 17)) * num_banks * per_chip; + + if (dimm_size > 0) { + if (dimm_size < 1024) + printf(" %dMB", dimm_size); + else + printf(" %dGB", dimm_size / 1024); + } + + printf(" %s", type); + + if (s->sm_data[SPDMEM_DDR_MOD_ATTRIB] & SPDMEM_DDR_ATTRIB_REG) + printf(" registered"); + + if (s->sm_data[SPDMEM_FPM_CONFIG] < 8) + printf(" %s", + spdmem_parity_types[s->sm_data[SPDMEM_FPM_CONFIG]]); + + p_clk = 66; + if (s->sm_len >= 128) { + switch (spdmem_read(sc, SPDMEM_SDR_FREQUENCY)) { + case SPDMEM_SDR_FREQ_100: + case SPDMEM_SDR_FREQ_133: + /* We need to check ns to decide here */ + if (s->sm_data[SPDMEM_SDR_CYCLE] < 0x80) + p_clk = 133; + else + p_clk = 100; + break; + case SPDMEM_SDR_FREQ_66: + default: + p_clk = 66; + break; + } + } + printf(" PC%d", p_clk); + + /* Print CAS latency */ + if (s->sm_len < 128) + return; + if (spdmem_read(sc, SPDMEM_SDR_CAS) & SPDMEM_SDR_CAS2) + printf("CL2"); + else if (spdmem_read(sc, SPDMEM_SDR_CAS) & SPDMEM_SDR_CAS3) + printf("CL3"); +} + +void +spdmem_rdr_decode(struct spdmem_softc *sc, struct spdmem *s) +{ + int rimm_size; + uint8_t row_bits, col_bits, bank_bits; + + row_bits = s->sm_data[SPDMEM_RDR_ROWS_COLS] >> 4; + col_bits = s->sm_data[SPDMEM_RDR_ROWS_COLS] & 0x0f; + bank_bits = s->sm_data[SPDMEM_RDR_BANK] & 0x07; + + /* subtracting 13 here is a cheaper way of dividing by 8k later */ + rimm_size = 1 << (row_bits + col_bits + bank_bits - 13); + + if (rimm_size < 1024) + printf(" %dMB ", rimm_size); + else + printf(" %dGB ", rimm_size / 1024); + + switch(s->sm_data[SPDMEM_RDR_MODULE_TYPE]) { + case SPDMEM_RDR_TYPE_RIMM: + printf("RIMM"); + break; + case SPDMEM_RDR_TYPE_SORIMM: + printf("SO-RIMM"); + break; + case SPDMEM_RDR_TYPE_EMBED: + printf("Embedded Rambus"); + break; + case SPDMEM_RDR_TYPE_RIMM32: + printf("RIMM32"); + break; + } +} + +void +spdmem_ddr_decode(struct spdmem_softc *sc, struct spdmem *s) +{ + const char *type; + int dimm_size, cycle_time, d_clk, p_clk, bits; + int i, num_banks, per_chip; + uint8_t config, rows, cols, cl; + + type = spdmem_basic_types[s->sm_type]; + + if (s->sm_data[SPDMEM_DDR_SUPERSET] == SPDMEM_SUPERSET_DDR_ESDRAM) + type = spdmem_superset_types[SPDMEM_SUPERSET_DDR_ESDRAM]; + + num_banks = s->sm_data[SPDMEM_SDR_BANKS]; + per_chip = s->sm_data[SPDMEM_SDR_BANKS_PER_CHIP]; + rows = s->sm_data[SPDMEM_SDR_ROWS] & 0x0f; + cols = s->sm_data[SPDMEM_SDR_COLS] & 0x0f; + dimm_size = (1 << (rows + cols - 17)) * num_banks * per_chip; + + if (dimm_size > 0) { + if (dimm_size < 1024) + printf(" %dMB", dimm_size); + else + printf(" %dGB", dimm_size / 1024); + } + + printf(" %s", type); + + if (s->sm_data[SPDMEM_DDR_MOD_ATTRIB] & SPDMEM_DDR_ATTRIB_REG) + printf(" registered"); + + if (s->sm_data[SPDMEM_FPM_CONFIG] < 8) + printf(" %s", + spdmem_parity_types[s->sm_data[SPDMEM_FPM_CONFIG]]); + + /* cycle_time is expressed in units of 0.01 ns */ + cycle_time = (s->sm_data[SPDMEM_DDR_CYCLE] >> 4) * 100 + + (s->sm_data[SPDMEM_DDR_CYCLE] & 0x0f) * 10; + + 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. + * DDR uses dual-pumped clock + */ + d_clk = 100 * 1000 * 2; + config = s->sm_data[SPDMEM_FPM_CONFIG]; + bits = s->sm_data[SPDMEM_DDR_DATAWIDTH] | + (s->sm_data[SPDMEM_DDR_DATAWIDTH + 1] << 8); + if (config == 1 || config == 2) + bits -= 8; + + d_clk /= cycle_time; + p_clk = d_clk * bits / 8; + if ((p_clk % 100) >= 50) + p_clk += 50; + p_clk -= p_clk % 100; + printf(" PC%d", p_clk); + } + + /* Print CAS latency */ + for (i = 6; i >= 0; i--) { + if (s->sm_data[SPDMEM_DDR_CAS] & (1 << i)) { + cl = ((i * 10) / 2) + 10; + printf("CL%d.%d", cl / 10, cl % 10); + break; + } + } +} + +void +spdmem_ddr2_decode(struct spdmem_softc *sc, struct spdmem *s) +{ + const char *type; + int dimm_size, cycle_time, d_clk, p_clk, bits; + int i, num_ranks, density; + uint8_t config; + + type = spdmem_basic_types[s->sm_type]; + + num_ranks = (s->sm_data[SPDMEM_DDR2_RANKS] & 0x7) + 1; + density = (s->sm_data[SPDMEM_DDR2_RANK_DENSITY] & 0xf0) | + ((s->sm_data[SPDMEM_DDR2_RANK_DENSITY] & 0x0f) << 8); + dimm_size = num_ranks * density * 4; + + if (dimm_size > 0) { + if (dimm_size < 1024) + printf(" %dMB", dimm_size); + else + printf(" %dGB", dimm_size / 1024); + } + + printf(" %s", type); + + if (s->sm_data[SPDMEM_DDR2_DIMMTYPE] & SPDMEM_DDR2_TYPE_REGMASK) + printf(" registered"); + + if (s->sm_data[SPDMEM_FPM_CONFIG] < 8) + printf(" %s", + spdmem_parity_types[s->sm_data[SPDMEM_FPM_CONFIG]]); + + /* cycle_time is expressed in units of 0.01 ns */ + cycle_time = (s->sm_data[SPDMEM_DDR2_CYCLE] >> 4) * 100 + + ddr2_cycle_tenths[(s->sm_data[SPDMEM_DDR2_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. + * DDR2 uses quad-pumped clock + */ + d_clk = 100 * 1000 * 4; + config = s->sm_data[SPDMEM_FPM_CONFIG]; + bits = s->sm_data[SPDMEM_DDR2_DATAWIDTH]; + if ((config & 0x03) != 0) + bits -= 8; + d_clk /= cycle_time; + d_clk = (d_clk + 1) / 2; + p_clk = d_clk * bits / 8; + p_clk -= p_clk % 100; + printf(" PC2-%d", p_clk); + } + + /* Print CAS latency */ + for (i = 5; i >= 2; i--) { + if (s->sm_data[SPDMEM_DDR_CAS] & (i << i)) { + printf("CL%d", i); + break; + } + } + + switch (s->sm_data[SPDMEM_DDR2_DIMMTYPE]) { + case SPDMEM_DDR2_SODIMM: + printf(" SO-DIMM"); + break; + case SPDMEM_DDR2_MICRO_DIMM: + printf(" Micro-DIMM"); + break; + case SPDMEM_DDR2_MINI_RDIMM: + case SPDMEM_DDR2_MINI_UDIMM: + printf(" Mini-DIMM"); + break; + } +} + +void +spdmem_fbdimm_decode(struct spdmem_softc *sc, struct spdmem *s) +{ + int dimm_size, num_banks, cycle_time, d_clk, p_clk, bits; + uint8_t rows, cols, banks, dividend, divisor; + /* + * FB-DIMM is very much like DDR3 + */ + + banks = s->sm_data[SPDMEM_FBDIMM_ADDR] & SPDMEM_FBDIMM_ADDR_BANKS; + cols = (s->sm_data[SPDMEM_FBDIMM_ADDR] & SPDMEM_FBDIMM_ADDR_COL) >> + SPDMEM_FBDIMM_ADDR_COL_SHIFT; + rows = (s->sm_data[SPDMEM_FBDIMM_ADDR] & SPDMEM_FBDIMM_ADDR_ROW) >> + SPDMEM_FBDIMM_ADDR_ROW_SHIFT; + dimm_size = rows + 12 + cols + 9 - 20 - 3; + num_banks = 1 << (banks + 2); + + if (dimm_size < 1024) + printf(" %dMB", dimm_size); + else + printf(" %dGB", dimm_size / 1024); + + dividend = s->sm_data[SPDMEM_FBDIMM_MTB_DIVIDEND]; + divisor = s->sm_data[SPDMEM_FBDIMM_MTB_DIVISOR]; + + cycle_time = (1000 * dividend + (divisor / 2)) / divisor; + + if (cycle_time != 0) { + /* + * cycle time is scaled by a factor of 1000 to avoid using + * floating point. Calculate memory speed as the number + * of cycles per microsecond. + */ + d_clk = 1000 * 1000; + + /* DDR2 FB-DIMM uses a dual-pumped clock */ + d_clk *= 2; + bits = 1 << ((s->sm_data[SPDMEM_FBDIMM_RANKS] & + SPDMEM_FBDIMM_RANKS_WIDTH) + 2); + + p_clk = (d_clk * bits) / 8 / cycle_time; + d_clk = ((d_clk + cycle_time / 2) ) / cycle_time; + p_clk -= p_clk % 100; + printf(" PC2-%d", p_clk); + } +} + +void +spdmem_ddr3_decode(struct spdmem_softc *sc, struct spdmem *s) +{ + const char *type; + int dimm_size, cycle_time, d_clk, p_clk, bits; + uint8_t mtype, chipsize, dividend, divisor; + uint8_t datawidth, chipwidth, physbanks; + + type = spdmem_basic_types[s->sm_type]; + + chipsize = s->sm_data[SPDMEM_DDR3_DENSITY] & + SPDMEM_DDR3_DENSITY_CAPMASK; + datawidth = s->sm_data[SPDMEM_DDR3_DATAWIDTH] & + SPDMEM_DDR3_DATAWIDTH_PRIMASK; + chipwidth = s->sm_data[SPDMEM_DDR3_MOD_ORG] & + SPDMEM_DDR3_MOD_ORG_CHIPWIDTH_MASK; + physbanks = (s->sm_data[SPDMEM_DDR3_MOD_ORG] >> + SPDMEM_DDR3_MOD_ORG_BANKS_SHIFT) & SPDMEM_DDR3_MOD_ORG_BANKS_MASK; + + dimm_size = (chipsize + 28 - 20) - 3 + (datawidth + 3) - + (chipwidth + 2); + dimm_size = (1 << dimm_size) * (physbanks + 1); + + if (dimm_size < 1024) + printf(" %dMB", dimm_size); + else + printf(" %dGB", dimm_size / 1024); + + printf(" %s", type); + + mtype = s->sm_data[SPDMEM_DDR3_MODTYPE]; + if (mtype == SPDMEM_DDR3_RDIMM || mtype == SPDMEM_DDR3_MINI_RDIMM) + printf(" registered"); + + if (s->sm_data[SPDMEM_DDR3_DATAWIDTH] & SPDMEM_DDR3_DATAWIDTH_ECCMASK) + printf(" ECC"); + + dividend = s->sm_data[SPDMEM_DDR3_MTB_DIVIDEND]; + divisor = s->sm_data[SPDMEM_DDR3_MTB_DIVISOR]; + cycle_time = (1000 * dividend + (divisor / 2)) / divisor; + cycle_time *= s->sm_data[SPDMEM_DDR3_TCKMIN]; + + if (cycle_time != 0) { + /* + * cycle time is scaled by a factor of 1000 to avoid using + * floating point. Calculate memory speed as the number + * of cycles per microsecond. + * DDR3 uses a dual-pumped clock + */ + d_clk = 1000 * 1000; + d_clk *= 2; + bits = 1 << ((s->sm_data[SPDMEM_DDR3_DATAWIDTH] & + SPDMEM_DDR3_DATAWIDTH_PRIMASK) + 3); + /* + * Calculate p_clk first, since for DDR3 we need maximum + * significance. DDR3 rating is not rounded to a multiple + * of 100. This results in cycle_time of 1.5ns displayed + * as p_clk PC3-10666 (d_clk DDR3-1333) + */ + p_clk = (d_clk * bits) / 8 / cycle_time; + p_clk -= (p_clk % 100); + d_clk = ((d_clk + cycle_time / 2) ) / cycle_time; + printf(" PC3-%d", p_clk); + } + + switch (s->sm_data[SPDMEM_DDR3_MODTYPE]) { + case SPDMEM_DDR3_SODIMM: + printf(" SO-DIMM"); + break; + case SPDMEM_DDR3_MICRO_DIMM: + printf(" Micro-DIMM"); + break; + case SPDMEM_DDR3_MINI_RDIMM: + case SPDMEM_DDR3_MINI_UDIMM: + printf(" Mini-DIMM"); + break; + } + + if (s->sm_data[SPDMEM_DDR3_THERMAL] & SPDMEM_DDR3_THERMAL_PRESENT) + printf(" with thermal sensor"); +} + +int +spdmem_probe(struct spdmem_softc *sc) +{ + uint8_t i, val, type; + int cksum = 0; + int spd_len, spd_crc_cover; + uint16_t crc_calc, crc_spd; + + type = spdmem_read(sc, 2); + /* For older memory types, validate the checksum over 1st 63 bytes */ + if (type <= SPDMEM_MEMTYPE_DDR2SDRAM) { + for (i = 0; i < 63; i++) + cksum += spdmem_read(sc, i); + + val = spdmem_read(sc, 63); + + if (cksum == 0 || (cksum & 0xff) != val) { + return 0; + } else + return 1; + } + + /* For DDR3 and FBDIMM, verify the CRC */ + else if (type <= SPDMEM_MEMTYPE_DDR3SDRAM) { + spd_len = spdmem_read(sc, 0); + if (spd_len && SPDMEM_SPDCRC_116) + spd_crc_cover = 116; + else + spd_crc_cover = 125; + switch (spd_len & SPDMEM_SPDLEN_MASK) { + case SPDMEM_SPDLEN_128: + spd_len = 128; + break; + case SPDMEM_SPDLEN_176: + spd_len = 176; + break; + case SPDMEM_SPDLEN_256: + spd_len = 256; + break; + default: + return 0; + } + if (spd_crc_cover > spd_len) + return 0; + crc_calc = spdmem_crc16(sc, spd_crc_cover); + crc_spd = spdmem_read(sc, 127) << 8; + crc_spd |= spdmem_read(sc, 126); + if (crc_calc != crc_spd) { + return 0; + } + return 1; + } + + return 0; +} + +void +spdmem_attach_common(struct spdmem_softc *sc) +{ + struct spdmem *s = &(sc->sc_spd_data); + int i; + + /* 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); + } + + /* + * Decode and print SPD contents + */ + if (s->sm_len < 4) { + if (s->sm_type == SPDMEM_MEMTYPE_DIRECT_RAMBUS) + spdmem_rdr_decode(sc, s); + else + printf(" no decode method for Rambus memory"); + } else { + switch(s->sm_type) { + case SPDMEM_MEMTYPE_EDO: + case SPDMEM_MEMTYPE_SDRAM: + spdmem_sdram_decode(sc, s); + break; + case SPDMEM_MEMTYPE_DDRSDRAM: + spdmem_ddr_decode(sc, s); + break; + case SPDMEM_MEMTYPE_DDR2SDRAM: + spdmem_ddr2_decode(sc, s); + break; + case SPDMEM_MEMTYPE_FBDIMM: + case SPDMEM_MEMTYPE_FBDIMM_PROBE: + spdmem_fbdimm_decode(sc, s); + break; + case SPDMEM_MEMTYPE_DDR3SDRAM: + spdmem_ddr3_decode(sc, s); + break; + case SPDMEM_MEMTYPE_NONE: + printf(" no EEPROM found"); + break; + default: + if (s->sm_type <= 10) + printf(" no decode method for %s memory", + spdmem_basic_types[s->sm_type]); + else + printf(" unknown memory type %d", s->sm_type); + break; + } + } + + printf("\n"); +} diff --git a/sys/dev/spdmemvar.h b/sys/dev/spdmemvar.h new file mode 100644 index 00000000000..a5623253675 --- /dev/null +++ b/sys/dev/spdmemvar.h @@ -0,0 +1,69 @@ +/* $OpenBSD: spdmemvar.h,v 1.1 2010/03/22 21:20:56 miod Exp $ */ +/* $NetBSD: spdmem.c,v 1.3 2007/09/20 23:09:59 xtraeme Exp $ */ + +/* + * Copyright (c) 2007 Jonathan Gray + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * 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 + */ + +struct spdmem { + uint8_t sm_len; + uint8_t sm_size; + uint8_t sm_type; + uint8_t sm_data[60]; + uint8_t sm_cksum; +} __packed; + +struct spdmem_softc { + struct device sc_dev; + uint8_t (*sc_read)(struct spdmem_softc *, uint8_t); + struct spdmem sc_spd_data; +}; + +void spdmem_attach_common(struct spdmem_softc *); +int spdmem_probe(struct spdmem_softc *); -- cgit v1.2.3