/***********************license start*********************************** * Copyright (c) 2003-2017 Cavium Inc. (support@cavium.com). All rights * reserved. * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * 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. * * * Neither the name of Cavium Inc. nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * This Software, including technical data, may be subject to U.S. export * control laws, including the U.S. Export Administration Act and its * associated regulations, and may be subject to export or import * regulations in other countries. * * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS" * AND WITH ALL FAULTS AND CAVIUM INC. MAKES NO PROMISES, REPRESENTATIONS OR * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT * TO THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY * REPRESENTATION OR DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT * DEFECTS, AND CAVIUM SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES * OF TITLE, MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR * PURPOSE, LACK OF VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, * QUIET POSSESSION OR CORRESPONDENCE TO DESCRIPTION. THE ENTIRE RISK * ARISING OUT OF USE OR PERFORMANCE OF THE SOFTWARE LIES WITH YOU. ***********************license end**************************************/ #include #include "dram-internal.h" #include #include #include #include #include #include #include #include /** * Read the entire contents of a DIMM SPD and store it in the device tree. The * current DRAM config is also updated, so future SPD accesses used the cached * copy. * * @param node Node the DRAM config is for * @param cfg Current DRAM config. Updated with SPD data * @param lmc LMC to read DIMM for * @param dimm DIMM slot for SPD to read * * @return Zero on success, negative on failure */ static uint8_t spd_bufs[4 * 256]; /* FIXME(dhendrix): storage for SPD buffers, assume DDR4 */ int read_entire_spd(bdk_node_t node, dram_config_t *cfg, int lmc, int dimm) { /* FIXME(dhendrix): hack to get around using allocated mem */ assert(dimm < 4); /* If pointer to data is provided, use it, otherwise read from SPD over twsi */ if (cfg->config[lmc].dimm_config_table[dimm].spd_ptr) return 0; if (!cfg->config[lmc].dimm_config_table[dimm].spd_addr) return -1; /* Figure out how to access the SPD */ int spd_addr = cfg->config[lmc].dimm_config_table[dimm].spd_addr; int bus = spd_addr >> 12; int address = spd_addr & 0x7f; /* Figure out the size we will read */ int64_t dev_type = bdk_twsix_read_ia(node, bus, address, DDR4_SPD_KEY_BYTE_DEVICE_TYPE, 1, 1); if (dev_type < 0) return -1; /* No DIMM */ // FIXME: prudolph: Nobody needs 512 byte SPDs... //int spd_size = (dev_type == 0x0c) ? 512 : 256; int spd_size = 256; /* * FIXME: Assume DIMM doesn't support * 'Hybrid Module Extended Function Parameters' aka only 256 Byte SPD, * as the code below is broken ... */ assert(spd_size == 256); uint8_t *spd_buf = &spd_bufs[dimm * 256]; uint32_t *ptr = (uint32_t *)spd_buf; for (int bank = 0; bank < (spd_size >> 8); bank++) { /* this should only happen for DDR4, which has a second bank of 256 bytes */ if (bank) bdk_twsix_write_ia(node, bus, 0x36 | bank, 0, 2, 1, 0); int bank_size = 256; for (int i = 0; i < bank_size; i += 4) { int64_t data = bdk_twsix_read_ia(node, bus, address, i, 4, 1); if (data < 0) { bdk_error("Failed to read SPD data at 0x%x\n", i + (bank << 8)); /* Restore the bank to zero */ if (bank) bdk_twsix_write_ia(node, bus, 0x36 | 0, 0, 2, 1, 0); return -1; } else *ptr++ = bdk_be32_to_cpu(data); } /* Restore the bank to zero */ if (bank) bdk_twsix_write_ia(node, bus, 0x36 | 0, 0, 2, 1, 0); } /* Store the SPD in the device tree */ /* FIXME(dhendrix): No need for this? cfg gets updated, so the caller * (libdram_config()) has what it needs. */ // bdk_config_set_blob(spd_size, spd_buf, BDK_CONFIG_DDR_SPD_DATA, dimm, lmc, node); cfg->config[lmc].dimm_config_table[dimm].spd_ptr = (void*)spd_buf; return 0; } /* Read an DIMM SPD value, either using TWSI to read it from the DIMM, or * from a provided array. */ int read_spd(bdk_node_t node, const dimm_config_t *dimm_config, int spd_field) { /* If pointer to data is provided, use it, otherwise read from SPD over twsi */ if (dimm_config->spd_ptr) return dimm_config->spd_ptr[spd_field]; else if (dimm_config->spd_addr) { int data; int bus = dimm_config->spd_addr >> 12; int address = dimm_config->spd_addr & 0x7f; /* this should only happen for DDR4, which has a second bank of 256 bytes */ int bank = (spd_field >> 8) & 1; if (bank) { bdk_twsix_write_ia(node, bus, 0x36 | bank, 0, 2, 1, 0); spd_field %= 256; } data = bdk_twsix_read_ia(node, bus, address, spd_field, 1, 1); /* Restore the bank to zero */ if (bank) { bdk_twsix_write_ia(node, bus, 0x36 | 0, 0, 2, 1, 0); } return data; } else return -1; } static uint16_t ddr3_crc16(uint8_t *ptr, int count) { /* From DDR3 spd specification */ int crc, i; crc = 0; while (--count >= 0) { crc = crc ^ (int)*ptr++ << 8; for (i = 0; i < 8; ++i) if (crc & 0x8000) crc = crc << 1 ^ 0x1021; else crc = crc << 1; } return crc & 0xFFFF; } static int validate_spd_checksum_ddr3(bdk_node_t node, int twsi_addr, int silent) { uint8_t spd_data[128]; int crc_bytes = 126; uint16_t crc_comp; int i; int rv; int ret = 1; for (i = 0; i < 128; i++) { rv = bdk_twsix_read_ia(node, twsi_addr >> 12, twsi_addr & 0x7f, i, 1, 1); if (rv < 0) return 0; /* TWSI read error */ spd_data[i] = (uint8_t)rv; } /* Check byte 0 to see how many bytes checksum is over */ if (spd_data[0] & 0x80) crc_bytes = 117; crc_comp = ddr3_crc16(spd_data, crc_bytes); if (spd_data[DDR3_SPD_CYCLICAL_REDUNDANCY_CODE_LOWER_NIBBLE] != (crc_comp & 0xff) || spd_data[DDR3_SPD_CYCLICAL_REDUNDANCY_CODE_UPPER_NIBBLE] != (crc_comp >> 8)) { if (!silent) { printf("DDR3 SPD CRC error, spd addr: 0x%x, calculated crc: 0x%04x, read crc: 0x%02x%02x\n", twsi_addr, crc_comp, spd_data[DDR3_SPD_CYCLICAL_REDUNDANCY_CODE_UPPER_NIBBLE], spd_data[DDR3_SPD_CYCLICAL_REDUNDANCY_CODE_LOWER_NIBBLE]); } ret = 0; } return ret; } static int validate_spd_checksum(bdk_node_t node, int twsi_addr, int silent) { int rv; debug_print("Validating DIMM at address 0x%x\n", twsi_addr); if (!twsi_addr) return 1; /* return OK if we are not doing real DIMMs */ /* Look up module type to determine if DDR3 or DDR4 */ rv = bdk_twsix_read_ia(node, twsi_addr >> 12, twsi_addr & 0x7f, 2, 1, 1); if (rv >= 0xB && rv <= 0xC) /* this is DDR3 or DDR4, do same */ return validate_spd_checksum_ddr3(node, twsi_addr, silent); if (!silent) printf("Unrecognized DIMM type: 0x%x at spd address: 0x%x\n", rv, twsi_addr); return 0; } int validate_dimm(bdk_node_t node, const dimm_config_t *dimm_config) { int spd_addr; spd_addr = dimm_config->spd_addr; debug_print("Validating dimm spd addr: 0x%02x spd ptr: %x\n", spd_addr, dimm_config->spd_ptr); // if the slot is not possible if (!spd_addr && !dimm_config->spd_ptr) return -1; { int val0, val1; int ddr_type = get_ddr_type(node, dimm_config); switch (ddr_type) { case DDR3_DRAM: /* DDR3 */ case DDR4_DRAM: /* DDR4 */ debug_print("Validating DDR%d DIMM\n", ((dimm_type >> 2) & 3) + 1); #define DENSITY_BANKS DDR4_SPD_DENSITY_BANKS // same for DDR3 and DDR4 #define ROW_COL_BITS DDR4_SPD_ADDRESSING_ROW_COL_BITS // same for DDR3 and DDR4 val0 = read_spd(node, dimm_config, DENSITY_BANKS); val1 = read_spd(node, dimm_config, ROW_COL_BITS); if (val0 < 0 && val1 < 0) { debug_print("Error reading SPD for DIMM\n"); return 0; /* Failed to read dimm */ } if (val0 == 0xff && val1 == 0xff) { ddr_print("Blank or unreadable SPD for DIMM\n"); return 0; /* Blank SPD or otherwise unreadable device */ } /* Don't treat bad checksums as fatal. */ validate_spd_checksum(node, spd_addr, 0); break; case 0x00: /* Terminator detected. Fail silently. */ return 0; default: debug_print("Unknown DIMM type 0x%x for DIMM @ 0x%x\n", dimm_type, dimm_config->spd_addr); return 0; /* Failed to read dimm */ } } return 1; } int get_dimm_part_number(char *buffer, bdk_node_t node, const dimm_config_t *dimm_config, int ddr_type) { int i; int c; int skipping = 1; int strlen = 0; #define PART_LIMIT(t) (((t) == DDR4_DRAM) ? 19 : 18) #define PART_NUMBER(t) (((t) == DDR4_DRAM) ? DDR4_SPD_MODULE_PART_NUMBER : DDR3_SPD_MODULE_PART_NUMBER) int limit = PART_LIMIT(ddr_type); int offset = PART_NUMBER(ddr_type); for (i = 0; i < limit; ++i) { c = (read_spd(node, dimm_config, offset+i) & 0xff); if (c == 0) // any null, we are done break; /* Skip leading spaces. */ if (skipping) { if (isspace(c)) continue; else skipping = 0; } /* Put non-null non-leading-space-skipped char into buffer */ buffer[strlen] = c; ++strlen; } if (strlen > 0) { i = strlen - 1; // last char put into buf while (i >= 0 && isspace((int)buffer[i])) { // still in buf and a space --i; --strlen; } } buffer[strlen] = 0; /* Insure that the string is terminated */ return strlen; } uint32_t get_dimm_serial_number(bdk_node_t node, const dimm_config_t *dimm_config, int ddr_type) { uint32_t serial_number = 0; int offset; #define SERIAL_NUMBER(t) (((t) == DDR4_DRAM) ? DDR4_SPD_MODULE_SERIAL_NUMBER : DDR3_SPD_MODULE_SERIAL_NUMBER) offset = SERIAL_NUMBER(ddr_type); for (int i = 0, j = 24; i < 4; ++i, j -= 8) { serial_number |= ((read_spd(node, dimm_config, offset + i) & 0xff) << j); } return serial_number; } static uint32_t get_dimm_checksum(bdk_node_t node, const dimm_config_t *dimm_config, int ddr_type) { uint32_t spd_chksum; #define LOWER_NIBBLE(t) (((t) == DDR4_DRAM) ? DDR4_SPD_CYCLICAL_REDUNDANCY_CODE_LOWER_NIBBLE : DDR3_SPD_CYCLICAL_REDUNDANCY_CODE_LOWER_NIBBLE) #define UPPER_NIBBLE(t) (((t) == DDR4_DRAM) ? DDR4_SPD_CYCLICAL_REDUNDANCY_CODE_UPPER_NIBBLE : DDR3_SPD_CYCLICAL_REDUNDANCY_CODE_UPPER_NIBBLE) spd_chksum = 0xff & read_spd(node, dimm_config, LOWER_NIBBLE(ddr_type)); spd_chksum |= ((0xff & read_spd(node, dimm_config, UPPER_NIBBLE(ddr_type))) << 8); return spd_chksum; } static void report_common_dimm(bdk_node_t node, const dimm_config_t *dimm_config, int dimm, const char **dimm_types, int ddr_type, const char *volt_str, int ddr_interface_num, int num_ranks, int dram_width, int dimm_size_mb) { int spd_ecc; unsigned spd_module_type; uint32_t serial_number; char part_number[21]; /* 20 bytes plus string terminator is big enough for either */ const char *sn_str; spd_module_type = get_dimm_module_type(node, dimm_config, ddr_type); spd_ecc = get_dimm_ecc(node, dimm_config, ddr_type); (void) get_dimm_part_number(part_number, node, dimm_config, ddr_type); serial_number = get_dimm_serial_number(node, dimm_config, ddr_type); if ((serial_number != 0) && (serial_number != 0xffffffff)) { sn_str = "s/n"; } else { serial_number = get_dimm_checksum(node, dimm_config, ddr_type); sn_str = "chksum"; } // FIXME: add output of DIMM rank/width, as in: 2Rx4, 1Rx8, etc printf("N%d.LMC%d.DIMM%d: %d MB, DDR%d %s %dRx%d %s, p/n: %s, %s: %u, %s\n", node, ddr_interface_num, dimm, dimm_size_mb, ddr_type, dimm_types[spd_module_type], num_ranks, dram_width, (spd_ecc ? "ECC" : "non-ECC"), part_number, sn_str, serial_number, volt_str); } const char *ddr3_dimm_types[16] = { /* 0000 */ "Undefined", /* 0001 */ "RDIMM", /* 0010 */ "UDIMM", /* 0011 */ "SO-DIMM", /* 0100 */ "Micro-DIMM", /* 0101 */ "Mini-RDIMM", /* 0110 */ "Mini-UDIMM", /* 0111 */ "Mini-CDIMM", /* 1000 */ "72b-SO-UDIMM", /* 1001 */ "72b-SO-RDIMM", /* 1010 */ "72b-SO-CDIMM", /* 1011 */ "LRDIMM", /* 1100 */ "16b-SO-DIMM", /* 1101 */ "32b-SO-DIMM", /* 1110 */ "Reserved", /* 1111 */ "Reserved" }; static void report_ddr3_dimm(bdk_node_t node, const dimm_config_t *dimm_config, int dimm, int ddr_interface_num, int num_ranks, int dram_width, int dimm_size_mb) { int spd_voltage; const char *volt_str = "unknown voltage"; spd_voltage = read_spd(node, dimm_config, DDR3_SPD_NOMINAL_VOLTAGE); if ((spd_voltage == 0) || (spd_voltage & 3)) volt_str = "1.5V"; if (spd_voltage & 2) volt_str = "1.35V"; if (spd_voltage & 4) volt_str = "1.2xV"; report_common_dimm(node, dimm_config, dimm, ddr3_dimm_types, DDR3_DRAM, volt_str, ddr_interface_num, num_ranks, dram_width, dimm_size_mb); } const char *ddr4_dimm_types[16] = { /* 0000 */ "Extended", /* 0001 */ "RDIMM", /* 0010 */ "UDIMM", /* 0011 */ "SO-DIMM", /* 0100 */ "LRDIMM", /* 0101 */ "Mini-RDIMM", /* 0110 */ "Mini-UDIMM", /* 0111 */ "Reserved", /* 1000 */ "72b-SO-RDIMM", /* 1001 */ "72b-SO-UDIMM", /* 1010 */ "Reserved", /* 1011 */ "Reserved", /* 1100 */ "16b-SO-DIMM", /* 1101 */ "32b-SO-DIMM", /* 1110 */ "Reserved", /* 1111 */ "Reserved" }; static void report_ddr4_dimm(bdk_node_t node, const dimm_config_t *dimm_config, int dimm, int ddr_interface_num, int num_ranks, int dram_width, int dimm_size_mb) { int spd_voltage; const char *volt_str = "unknown voltage"; spd_voltage = read_spd(node, dimm_config, DDR4_SPD_MODULE_NOMINAL_VOLTAGE); if ((spd_voltage == 0x01) || (spd_voltage & 0x02)) volt_str = "1.2V"; if ((spd_voltage == 0x04) || (spd_voltage & 0x08)) volt_str = "TBD1 V"; if ((spd_voltage == 0x10) || (spd_voltage & 0x20)) volt_str = "TBD2 V"; report_common_dimm(node, dimm_config, dimm, ddr4_dimm_types, DDR4_DRAM, volt_str, ddr_interface_num, num_ranks, dram_width, dimm_size_mb); } void report_dimm(bdk_node_t node, const dimm_config_t *dimm_config, int dimm, int ddr_interface_num, int num_ranks, int dram_width, int dimm_size_mb) { int ddr_type; /* ddr_type only indicates DDR4 or DDR3 */ ddr_type = get_ddr_type(node, dimm_config); if (ddr_type == DDR4_DRAM) report_ddr4_dimm(node, dimm_config, dimm, ddr_interface_num, num_ranks, dram_width, dimm_size_mb); else report_ddr3_dimm(node, dimm_config, dimm, ddr_interface_num, num_ranks, dram_width, dimm_size_mb); } static int get_ddr4_spd_speed(bdk_node_t node, const dimm_config_t *dimm_config) { int spdMTB = 125; int spdFTB = 1; int tCKAVGmin = spdMTB * read_spd(node, dimm_config, DDR4_SPD_MINIMUM_CYCLE_TIME_TCKAVGMIN) + spdFTB * (signed char) read_spd(node, dimm_config, DDR4_SPD_MIN_CYCLE_TIME_FINE_TCKAVGMIN); return pretty_psecs_to_mts(tCKAVGmin); } static int get_ddr3_spd_speed(bdk_node_t node, const dimm_config_t *dimm_config) { int spd_mtb_dividend = 0xff & read_spd(node, dimm_config, DDR3_SPD_MEDIUM_TIMEBASE_DIVIDEND); int spd_mtb_divisor = 0xff & read_spd(node, dimm_config, DDR3_SPD_MEDIUM_TIMEBASE_DIVISOR); int spd_tck_min = 0xff & read_spd(node, dimm_config, DDR3_SPD_MINIMUM_CYCLE_TIME_TCKMIN); short ftb_Dividend = read_spd(node, dimm_config, DDR3_SPD_FINE_TIMEBASE_DIVIDEND_DIVISOR) >> 4; short ftb_Divisor = read_spd(node, dimm_config, DDR3_SPD_FINE_TIMEBASE_DIVIDEND_DIVISOR) & 0xf; ftb_Divisor = (ftb_Divisor == 0) ? 1 : ftb_Divisor; /* Make sure that it is not 0 */ int mtb_psec = spd_mtb_dividend * 1000 / spd_mtb_divisor; int tCKmin = mtb_psec * spd_tck_min; tCKmin += ftb_Dividend * (signed char) read_spd(node, dimm_config, DDR3_SPD_MINIMUM_CYCLE_TIME_FINE_TCKMIN) / ftb_Divisor; return pretty_psecs_to_mts(tCKmin); } static int speed_bin_down(int speed) { if (speed == 2133) return 1866; else if (speed == 1866) return 1600; else return speed; } int dram_get_default_spd_speed(bdk_node_t node, const ddr_configuration_t *ddr_config) { int lmc, dimm; int speed, ret_speed = 0; int ddr_type = get_ddr_type(node, &ddr_config[0].dimm_config_table[0]); int dimm_speed[8], dimm_count = 0; int dimms_per_lmc = 0; memset (dimm_speed, 0, sizeof(dimm_speed)); for (lmc = 0; lmc < 4; lmc++) { for (dimm = 0; dimm < DDR_CFG_T_MAX_DIMMS; dimm++) { const dimm_config_t *dimm_config = &ddr_config[lmc].dimm_config_table[dimm]; if (dimm_config->spd_addr || dimm_config->spd_ptr) { speed = (ddr_type == DDR4_DRAM) ? get_ddr4_spd_speed(node, dimm_config) : get_ddr3_spd_speed(node, dimm_config); printf("N%d.LMC%d.DIMM%d: SPD speed %d\n", node, lmc, dimm, speed); dimm_speed[dimm_count] = speed; dimm_count++; if (lmc == 0) dimms_per_lmc++; } } } // all DIMMs must be same speed speed = dimm_speed[0]; for (dimm = 1; dimm < dimm_count; dimm++) { if (dimm_speed[dimm] != speed) { ret_speed = -1; goto finish_up; } } // if 2400 or greater, use 2133 if (speed >= 2400) speed = 2133; // use next speed down if 2DPC... if (dimms_per_lmc > 1) speed = speed_bin_down(speed); // Update the in memory config to match the automatically calculated speed bdk_config_set_int(speed, BDK_CONFIG_DDR_SPEED, node); // do filtering for our jittery PLL if (speed == 2133) speed = 2100; else if (speed == 1866) speed = 1880; // OK, return what we have... ret_speed = mts_to_hertz(speed); finish_up: printf("N%d: Returning default SPD speed %d\n", node, ret_speed); return ret_speed; }