Files
system76-coreboot/src/mainboard/google/cyan/spd/spd.c
T.H. Lin c648aac31d google/cyan: Add 2nd source memory (Micro/Samsung)
Cherry-pick from Chromium commit 3b578ef.

Cyan board use new 2nd source memory (Micro/Samsung)

Original-Change-Id: I6f4e8438faede7ac742776a622c265922e498898
Original-Signed-off-by: T.H. Lin <T.H_Lin@quantatw.com>
Original-Reviewed-by: Shawn N <shawnn@chromium.org>

Change-Id: Ie2febe4de57c00c269def15d57f2b5a6f0f378aa
Signed-off-by: Matt DeVillier <matt.devillier@gmail.com>
Reviewed-on: https://review.coreboot.org/21170
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Aaron Durbin <adurbin@chromium.org>
2017-08-25 18:58:48 +00:00

205 lines
5.4 KiB
C

/*
* This file is part of the coreboot project.
*
* Copyright (C) 2013 Google Inc.
* Copyright (C) 2015 Intel Corp.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <cbfs.h>
#include <cbmem.h>
#include <console/console.h>
#include <gpio.h>
#include <lib.h>
#include <memory_info.h>
#include <smbios.h>
#include <spd.h>
#include <soc/gpio.h>
#include <soc/romstage.h>
#include <string.h>
#define SPD_SIZE 256
/*
* 0b0000 - 4GiB total - 2 x 2GiB Samsung K4B4G1646Q-HYK0 1600MHz
* 0b0001 - 4GiB total - 2 x 2GiB Hynix H5TC4G63CFR-PBA 1600MHz
* 0b0010 - 2GiB total - 1 x 2GiB Samsung K4B4G1646Q-HYK0 1600MHz
* 0b0011 - 2GiB total - 1 x 2GiB Hynix H5TC4G63CFR-PBA 1600MHz
* 0b0110 - 2GiB total - 1 x 2GiB Samsung K4B4G1646E-BYK0 1600MHz
* 0b0111 - 2GiB total - 1 x 2GiB Micro MT41K256M16TW-107 1600MHz
*/
static const uint32_t dual_channel_config = (1 << 0) | (1 << 1);
static void *get_spd_pointer(char *spd_file_content, int total_spds, int *dual)
{
int ram_id = 0;
gpio_t spd_gpios[] = {
GP_SW_80, /* SATA_GP3,RAMID0 */
GP_SW_67, /* I2C3_SCL,RAMID1 */
GP_SE_02, /* MF_PLT_CLK1, RAMID2 */
GP_SW_64, /* I2C3_SDA RAMID3 */
};
ram_id = gpio_base2_value(spd_gpios, ARRAY_SIZE(spd_gpios));
printk(BIOS_DEBUG, "ram_id=%d, total_spds: %d\n", ram_id, total_spds);
if (ram_id >= total_spds)
return NULL;
/* Determine if this is a single or dual channel memory system */
if (dual_channel_config & (1 << ram_id))
*dual = 1;
/* Display the RAM type */
switch (ram_id) {
case 0:
printk(BIOS_DEBUG, "4GiB Samsung K4B4G1646Q-HYK0 1600MHz\n");
break;
case 2:
printk(BIOS_DEBUG, "2GiB Samsung K4B4G1646Q-HYK0 1600MHz\n");
break;
case 1:
printk(BIOS_DEBUG, "4GiB Hynix H5TC4G63CFR-PBA 1600MHz\n");
break;
case 3:
printk(BIOS_DEBUG, "2GiB Hynix H5TC4G63CFR-PBA 1600MHz\n");
break;
case 6:
printk(BIOS_DEBUG, "2GiB Samsung K4B4G1646E-BYK0 1600MHz\n");
break;
case 7:
printk(BIOS_DEBUG, "2GiB Micro MT41K256M16TW-107 1600MHz\n");
break;
}
/* Return the serial product data for the RAM */
return &spd_file_content[SPD_SIZE * ram_id];
}
/* Copy SPD data for on-board memory */
void mainboard_fill_spd_data(struct pei_data *ps)
{
char *spd_file;
size_t spd_file_len;
void *spd_content;
int dual_channel = 0;
/* Find the SPD data in CBFS. */
spd_file = cbfs_boot_map_with_leak("spd.bin", CBFS_TYPE_SPD,
&spd_file_len);
if (!spd_file)
die("SPD data not found.");
if (spd_file_len < SPD_SIZE)
die("Missing SPD data.");
/*
* Both channels are always present in SPD data. Always use matched
* DIMMs so use the same SPD data for each DIMM.
*/
spd_content = get_spd_pointer(spd_file,
spd_file_len / SPD_SIZE,
&dual_channel);
if (IS_ENABLED(CONFIG_DISPLAY_SPD_DATA) && spd_content != NULL) {
printk(BIOS_DEBUG, "SPD Data:\n");
hexdump(spd_content, SPD_SIZE);
printk(BIOS_DEBUG, "\n");
}
/*
* Set SPD and memory configuration:
* Memory type: 0=DimmInstalled,
* 1=SolderDownMemory,
* 2=DimmDisabled
*/
if (spd_content != NULL) {
ps->spd_data_ch0 = spd_content;
ps->spd_ch0_config = 1;
printk(BIOS_DEBUG, "Channel 0 DIMM soldered down\n");
if (dual_channel) {
printk(BIOS_DEBUG, "Channel 1 DIMM soldered down\n");
ps->spd_data_ch1 = spd_content;
ps->spd_ch1_config = 1;
} else {
printk(BIOS_DEBUG, "Channel 1 DIMM not installed\n");
ps->spd_ch1_config = 2;
}
}
}
static void set_dimm_info(uint32_t chips, uint8_t *spd, struct dimm_info *dimm)
{
uint16_t clock_frequency;
uint32_t log2_chips;
/* Parse the SPD data to determine the DIMM information */
dimm->ddr_type = MEMORY_TYPE_DDR3;
dimm->dimm_size = (chips << (spd[4] & 0xf)) << (28 - 3 - 20); /* MiB */
clock_frequency = 1000 * spd[11] / (spd[10] * spd[12]); /* MHz */
dimm->ddr_frequency = 2 * clock_frequency; /* Double Data Rate */
dimm->mod_type = spd[3] & 0xf;
memcpy((char *)&dimm->module_part_number[0], &spd[0x80],
sizeof(dimm->module_part_number) - 1);
dimm->mod_id = *(uint16_t *)&spd[0x94];
switch (chips) {
case 1:
log2_chips = 0;
break;
case 2:
log2_chips = 1;
break;
case 4:
log2_chips = 2;
break;
case 8:
log2_chips = 3;
break;
default:
log2_chips = 0;
}
dimm->bus_width = (uint8_t)(log2_chips + (spd[7] & 7) + 2 - 3);
}
void mainboard_save_dimm_info(struct romstage_params *params)
{
struct dimm_info *dimm;
struct memory_info *mem_info;
uint32_t chips;
/*
* Allocate CBMEM area for DIMM information used to populate SMBIOS
* table 17
*/
mem_info = cbmem_add(CBMEM_ID_MEMINFO, sizeof(*mem_info));
printk(BIOS_DEBUG, "CBMEM entry for DIMM info: 0x%p\n", mem_info);
if (mem_info == NULL)
return;
memset(mem_info, 0, sizeof(*mem_info));
/* Describe the first channel memory */
chips = 4;
dimm = &mem_info->dimm[0];
set_dimm_info(chips, params->pei_data->spd_data_ch0, dimm);
mem_info->dimm_cnt = 1;
/* Describe the second channel memory */
if (params->pei_data->spd_ch1_config == 1) {
dimm = &mem_info->dimm[1];
set_dimm_info(chips, params->pei_data->spd_data_ch1, dimm);
dimm->channel_num = 1;
mem_info->dimm_cnt = 2;
}
}