diff options
| author | Ondrej Čerman <ondrej.cerman@gmail.com> | 2019-05-12 00:13:34 +0200 | 
|---|---|---|
| committer | Leandro A. F. Pereira <leandro@hardinfo.org> | 2019-05-13 15:35:41 -0700 | 
| commit | 89e6dbc7b9b11d15bc10c9c80e5b0142783a9820 (patch) | |
| tree | ecaa66788e3d28389e2748bd96e5e40582963e8c /modules/devices | |
| parent | 3ea7a8e67bbf5362283e9c08f8177228d77fa1bd (diff) | |
Initial DDR4 support
Diffstat (limited to 'modules/devices')
| -rw-r--r-- | modules/devices/spd-decode.c | 282 | 
1 files changed, 259 insertions, 23 deletions
| diff --git a/modules/devices/spd-decode.c b/modules/devices/spd-decode.c index c1a16dc8..02768bbd 100644 --- a/modules/devices/spd-decode.c +++ b/modules/devices/spd-decode.c @@ -1,6 +1,7 @@  /*   * spd-decode.c   * Copyright (c) 2010 Leandro A. F. Pereira + * modified by Ondrej Čerman   *   * Based on decode-dimms.pl   * Copyright 1998, 1999 Philip Edelbrock <phil@netroedge.com> @@ -42,7 +43,8 @@ typedef enum {      DDR_SGRAM,      DDR_SDRAM,      DDR2_SDRAM, -    DDR3_SDRAM +    DDR3_SDRAM, +    DDR4_SDRAM  } RamType;  char *spd_info = NULL; @@ -59,7 +61,8 @@ static const char *ram_types[] = {      "DDR SGRAM",      "DDR SDRAM",      "DDR2 SDRAM", -    "DDR3 SDRAM" +    "DDR3 SDRAM", +    "DDR4 SDRAM"  };  static const char *vendors1[] = { "AMD", "AMI", "Fairchild", "Fujitsu", @@ -603,7 +606,10 @@ static const char *vendors7[] = { "MOVEKING", "Mavrix Technology, Inc.",      "SiliconBlue Technologies", "Rambus Inc."  }; -static const char **vendors[7] = { vendors1, vendors2, vendors3, vendors4, vendors5, vendors6, +#define VENDORS_BANKS 7 +#define VENDORS_LAST_BANK_SIZE sizeof(vendors7)/sizeof(char*) + +static const char **vendors[VENDORS_BANKS] = { vendors1, vendors2, vendors3, vendors4, vendors5, vendors6,      vendors7  }; @@ -1324,6 +1330,218 @@ static void decode_module_part_number(unsigned char *bytes, char *part_number)      }  } +static char *print_spd_timings(int speed, float cas, float trcd, float trp, float tras, float ctime){ +    return g_strdup_printf("DDR4-%d=%.0f-%.0f-%.0f-%.0f\n", +                           speed, +                           cas, +                           ceil(trcd/ctime - 0.025), +                           ceil(trp/ctime - 0.025), +                           ceil(tras/ctime - 0.025)); +} + +static void decode_ddr4_module_type(unsigned char *bytes, const char **type) +{ +    switch (bytes[3]) { +        case 0x01: +            *type = "RDIMM (Registered DIMM)"; +            break; +        case 0x02: +            *type = "UDIMM (Unbuffered DIMM)"; +            break; +        case 0x03: +            *type = "SODIMM (Small Outline Unbuffered DIMM)"; +            break; +        case 0x04: +            *type = "LRDIMM (Load-Reduced DIMM)"; +            break; +        case 0x05: +            *type = "Mini-RDIMM (Mini Registered DIMM)"; +            break; +        case 0x06: +            *type = "Mini-UDIMM (Mini Unbuffered DIMM)"; +            break; +        case 0x08: +            *type = "72b-SO-RDIMM (Small Outline Registered DIMM, 72-bit data bus)"; +            break; +        case 0x09: +            *type = "72b-SO-UDIMM (Small Outline Unbuffered DIMM, 72-bit data bus)"; +            break; +        case 0x0c: +            *type = "16b-SO-UDIMM (Small Outline Unbuffered DIMM, 16-bit data bus)"; +            break; +        case 0x0d: +            *type = "32b-SO-UDIMM (Small Outline Unbuffered DIMM, 32-bit data bus)"; +            break; +        default: +            *type = _("Unknown"); +    } +} + +static float ddr4_mtb_ftb_calc(unsigned char b1, signed char b2){ +    float mtb = 0.125; +    float ftb = 0.001; +    return b1 * mtb + b2 * ftb; +} + +static void decode_ddr4_module_speed(unsigned char *bytes, float *ddr_clock, int *pc4_speed) +{ +    float ctime; +    float ddrclk; +    int tbits, pcclk; + +    ctime = ddr4_mtb_ftb_calc(bytes[18], bytes[125]); +    ddrclk = 2 * (1000 / ctime); +    tbits = 8 << (bytes[13] & 7); + +    pcclk = ddrclk * tbits / 8; +    pcclk -= pcclk % 100; + +    if (ddr_clock) { +        *ddr_clock = (int) ddrclk; +    } +    if (pc4_speed) { +        *pc4_speed = pcclk; +    } +} + +static void decode_ddr4_module_spd_timings(unsigned char *bytes, float speed, char **str){ +    float ctime, ctime_max, pctime, taa, trcd, trp, tras; +    int pcas, best_cas, base_cas, ci, i, j; +    unsigned char cas_support[] = {bytes[20], bytes[21], bytes[22], bytes[23] & 0x1f}; +    float possible_ctimes[] = {15/24.0, 15/22.0, 15/20.0, 15/18.0, 15/16.0, 15/14.0, 15/12.0}; + +    base_cas = bytes[23] & 0x80 ? 23 : 7; + +    ctime = ddr4_mtb_ftb_calc(bytes[18], bytes[125]); +    ctime_max = ddr4_mtb_ftb_calc(bytes[19], bytes[124]); + +    taa = ddr4_mtb_ftb_calc(bytes[24], bytes[123]); +    trcd = ddr4_mtb_ftb_calc(bytes[25], bytes[122]); +    trp  = ddr4_mtb_ftb_calc(bytes[26], bytes[121]); +    tras = (((bytes[27] & 0x0f) << 8) + bytes[28]) * 0.125; + +    *str = print_spd_timings((int) speed, ceil(taa/ctime - 0.025), trcd, trp, tras, ctime); + +    for (ci = 0; ci < 7; ci++){ +        best_cas = 0; +        pctime = possible_ctimes[ci]; + +        for (i = 3; i >= 0; i--){ +            for (j = 7; j >= 0; j--){ +                if ((cas_support[i] & (1 << j)) != 0){ +                    pcas = base_cas + 8*i + j; +                    if (ceil(taa/pctime) - 0.025 <= pcas){ +                        best_cas = pcas; +                    } +                } +            } +        } + +        if (best_cas > 0 && pctime <= ctime_max && pctime >= ctime){ +            *str = h_strdup_cprintf("%s\n", *str, +                    print_spd_timings((int)(2000.0 / pctime), best_cas, trcd, trp, tras, pctime)); +        } +    } +} + +static void decode_ddr4_module_size(unsigned char *bytes, int *size) +{ +    int sdrcap = 256 << (bytes[4] & 15); +    int buswidth = 8 << (bytes[13] & 7); +    int sdrwidth = 4 << (bytes[12] & 7); +    int signal_loading = bytes[6] & 3; +    int lranks_per_dimm = ((bytes[12] >> 3) & 7) + 1; + +    if (signal_loading == 2) +        lranks_per_dimm *= ((bytes[6] >> 4) & 7) + 1; + +    *size = sdrcap / 8 * buswidth / sdrwidth * lranks_per_dimm; +} + +static gchar *decode_ddr4_sdram(unsigned char *bytes, int *size) +{ +    float ddr_clock; +    int pc4_speed; +    const char *type; +    char *speed_timings = NULL; +    static gchar *out; + +    decode_ddr4_module_speed(bytes, &ddr_clock, &pc4_speed); +    decode_ddr4_module_size(bytes, size); +    decode_ddr4_module_type(bytes, &type); +    decode_ddr4_module_spd_timings(bytes, ddr_clock, &speed_timings); + +    out = g_strdup_printf("[%s]\n" +               "%s=DDR4 %.0f MHz (PC4-%d)\n" +               "%s=%d.%d\n" +               "%s=%s\n" +               "[%s]\n" +               "%s", +               _("Module Information"), +               _("Module type"), ddr_clock, pc4_speed, +               _("SPD revision"), bytes[1] >> 4, bytes[1] & 0xf, +               _("Type"), type, +               _("Supported speeds and timings"), +               speed_timings); + +    g_free(speed_timings); + +    return out; +} + +static void decode_ddr4_part_number(unsigned char *bytes, int spd_size, char *part_number) +{ +    int i; +    if (!part_number) +        return; + +    if (spd_size < 348){ +        *part_number++ = '?'; +        *part_number++ = '?'; +        *part_number++ = '?'; +        *part_number++ = '\0'; +        return; +    } + +    for (i = 329; i <= 348; i++) +        *part_number++ = bytes[i]; +    *part_number = '\0'; +} + +static void decode_ddr4_manufacturer(unsigned char count, unsigned char code, char **manufacturer) +{ +    if (!manufacturer) +        return; + +    if (code == 0x00 || code == 0xFF) { +        *manufacturer = _("Unknown"); +        return; +    } + +    if (parity(count) != 1) { +        *manufacturer = _("Invalid"); +        return; +    } + +    int bank = count & 0x7f; +    if (bank >= VENDORS_BANKS || (bank == VENDORS_BANKS - 1 && code > VENDORS_LAST_BANK_SIZE)) { +        *manufacturer = _("Unknown"); +        return; +    } + +    *manufacturer = (char *) vendors[bank][code - 1]; +} + +static void decode_ddr4_module_manufacturer(unsigned char *bytes, int spd_size, char **manufacturer){ +    if (spd_size < 321){ +        *manufacturer = _("Unknown (Missing data)"); +        return; +    } + +    decode_ddr4_manufacturer(bytes[320], bytes[321], manufacturer); +} + +  static int decode_ram_type(unsigned char *bytes)  {      if (bytes[0] < 4) { @@ -1353,14 +1571,17 @@ static int decode_ram_type(unsigned char *bytes)  	    return DDR2_SDRAM;  	case 11:  	    return DDR3_SDRAM; +	case 12: +	    return DDR4_SDRAM;  	}      }      return UNKNOWN;  } -static void read_spd(char *spd_path, int offset, size_t size, int use_sysfs, unsigned char *bytes_out) +static int read_spd(char *spd_path, int offset, size_t size, int use_sysfs, unsigned char *bytes_out)  { +    int data_size = 0;      if (use_sysfs) {  	FILE *spd;  	gchar *temp_path; @@ -1368,7 +1589,7 @@ static void read_spd(char *spd_path, int offset, size_t size, int use_sysfs, uns  	temp_path = g_strdup_printf("%s/eeprom", spd_path);  	if ((spd = fopen(temp_path, "rb"))) {  	    fseek(spd, offset, SEEK_SET); -	    fread(bytes_out, 1, size, spd); +	    data_size = fread(bytes_out, 1, size, spd);  	    fclose(spd);  	} @@ -1382,20 +1603,24 @@ static void read_spd(char *spd_path, int offset, size_t size, int use_sysfs, uns  	    temp_path = g_strdup_printf("%s/%02x", spd_path, offset + i * 16);  	    if ((spd = fopen(temp_path, "rb"))) { -		fread(bytes_out + i * 16, 1, 16, spd); +			data_size += fread(bytes_out + i * 16, 1, 16, spd);  		fclose(spd);  	    }  	    g_free(temp_path);  	}      } + +    return data_size;  } -static gchar *decode_dimms(GSList * dimm_list, gboolean use_sysfs) +static gchar *decode_dimms(GSList * dimm_list, gboolean use_sysfs, int max_size)  {      GSList *dimm;      GString *output;      gint count = 0; +    int spd_size = 0; +    guchar *bytes;      output = g_string_new(""); @@ -1405,13 +1630,13 @@ static gchar *decode_dimms(GSList * dimm_list, gboolean use_sysfs)  	gchar *detailed_info;  	gchar *moreinfo_key;  	gchar part_number[32]; -	unsigned char bytes[256];  	int module_size;  	RamType ram_type;  	shell_status_pulse(); -	read_spd(spd_path, 0, 256, use_sysfs, bytes); +	bytes = malloc(sizeof(unsigned char) * max_size); +	spd_size = read_spd(spd_path, 0, max_size, use_sysfs, bytes);  	ram_type = decode_ram_type(bytes);  	switch (ram_type) { @@ -1425,6 +1650,11 @@ static gchar *decode_dimms(GSList * dimm_list, gboolean use_sysfs)  	    decode_ddr3_part_number(bytes, part_number);  	    decode_ddr3_manufacturer(bytes, &manufacturer);  	    break; +	case DDR4_SDRAM: +	    detailed_info = decode_ddr4_sdram(bytes, &module_size); +	    decode_ddr4_part_number(bytes, spd_size, part_number); +	    decode_ddr4_module_manufacturer(bytes, spd_size, &manufacturer); +	    break;  	case DDR_SDRAM:  	    detailed_info = decode_ddr_sdram(bytes, &module_size);  	    decode_module_part_number(bytes, part_number); @@ -1440,8 +1670,6 @@ static gchar *decode_dimms(GSList * dimm_list, gboolean use_sysfs)  	    continue;  	} - -  	gchar *key = g_strdup_printf("MEM%d", count);  	moreinfo_add_with_prefix("DEV", key, g_strdup(detailed_info));  	g_free(key); @@ -1450,6 +1678,7 @@ static gchar *decode_dimms(GSList * dimm_list, gboolean use_sysfs)  	g_free(spd_path);  	g_free(detailed_info);      } +    g_free(bytes);      return g_string_free(output, FALSE);  } @@ -1458,15 +1687,24 @@ void scan_spd_do(void)  {      GDir *dir = NULL;      GSList *dimm_list = NULL; -    gboolean use_sysfs = FALSE; +    gboolean use_sysfs = TRUE;      gchar *dir_entry;      gchar *list; - -    if (g_file_test("/sys/bus/i2c/drivers/eeprom", G_FILE_TEST_EXISTS)) { -	dir = g_dir_open("/sys/bus/i2c/drivers/eeprom", 0, NULL); -	use_sysfs = TRUE; +    const gchar *dir_path = NULL; +    int max_size = 256; + +    if (g_file_test("/sys/bus/i2c/drivers/ee1004", G_FILE_TEST_EXISTS)) { +        dir_path = "/sys/bus/i2c/drivers/ee1004"; +        max_size = 512; +    } else if (g_file_test("/sys/bus/i2c/drivers/eeprom", G_FILE_TEST_EXISTS)) { +        dir_path = "/sys/bus/i2c/drivers/eeprom";      } else if (g_file_test("/proc/sys/dev/sensors", G_FILE_TEST_EXISTS)) { -	dir = g_dir_open("/proc/sys/dev/sensors", 0, NULL); +        dir_path = "/proc/sys/dev/sensors"; +        use_sysfs = FALSE; +    } + +    if (dir_path){ +        dir = g_dir_open(dir_path, 0, NULL);      }      if (!dir) { @@ -1484,16 +1722,14 @@ void scan_spd_do(void)      }      while ((dir_entry = (char *) g_dir_read_name(dir))) { -	if (use_sysfs && isdigit(dir_entry[0])) { -	    dimm_list = g_slist_prepend(dimm_list, g_strdup_printf("/sys/bus/i2c/drivers/eeprom/%s", dir_entry)); -	} else if (g_str_has_prefix(dir_entry, "eeprom-")) { -	    dimm_list = g_slist_prepend(dimm_list, g_strdup_printf("/proc/sys/dev/sensors/%s", dir_entry)); -	} +        if ((use_sysfs && isdigit(dir_entry[0])) || g_str_has_prefix(dir_entry, "eeprom-")) { +            dimm_list = g_slist_prepend(dimm_list, g_strdup_printf("%s/%s", dir_path, dir_entry)); +        }      }      g_dir_close(dir); -    list = decode_dimms(dimm_list, use_sysfs); +    list = decode_dimms(dimm_list, use_sysfs, max_size);      g_slist_free(dimm_list);      g_free(spd_info); | 
