/* $OpenBSD: spdmem.c,v 1.32 2009/05/23 09:14:39 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); if (s->sm_data[SPDMEM_FBDIMM_PROTO] & SPDMEM_FBDIMM_PROTO_ECC) printf(" ECC"); 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; }