blob: f9af195729b2564729911e8ab70ce963701bcdab [file] [log] [blame]
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
*
* Copyright (C) 2007 AMD
* Written by Yinghai Lu <yinghailu@amd.com> for AMD.
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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 <stdint.h>
#include <string.h>
#include <reset.h>
#include <device/pci_def.h>
#include <device/pci_ids.h>
#include <arch/io.h>
#include <device/pnp_def.h>
#include <cpu/x86/lapic.h>
#include <console/console.h>
#include <timestamp.h>
#include <lib.h>
#include <spd.h>
#include <cpu/amd/model_10xxx_rev.h>
#include <northbridge/amd/amdfam10/raminit.h>
#include <northbridge/amd/amdfam10/amdfam10.h>
#include "lib/delay.c"
#include <cpu/x86/lapic.h>
#include "northbridge/amd/amdfam10/reset_test.c"
#include <superio/nuvoton/common/nuvoton.h>
#include <superio/nuvoton/nct5572d/nct5572d.h>
#include <cpu/x86/bist.h>
#include <smp/spinlock.h>
// #include "northbridge/amd/amdk8/incoherent_ht.c"
#include <southbridge/amd/sb700/sb700.h>
#include <southbridge/amd/sb700/smbus.h>
#include <southbridge/amd/sr5650/sr5650.h>
#include "northbridge/amd/amdfam10/debug.c"
#include "northbridge/amd/amdfam10/setup_resource_map.c"
#define SERIAL_DEV PNP_DEV(0x2e, NCT5572D_SP1)
static void activate_spd_rom(const struct mem_controller *ctrl);
static inline int spd_read_byte(unsigned device, unsigned address)
{
return do_smbus_read_byte(SMBUS_AUX_IO_BASE, device, address);
}
#include <northbridge/amd/amdfam10/amdfam10.h>
#include "northbridge/amd/amdfam10/raminit_sysinfo_in_ram.c"
#include "northbridge/amd/amdfam10/pci.c"
#include "resourcemap.c"
#include "cpu/amd/quadcore/quadcore.c"
#include <cpu/amd/microcode.h>
#include "cpu/amd/family_10h-family_15h/init_cpus.c"
#include "northbridge/amd/amdfam10/early_ht.c"
/*
* ASUS KGPE-D16 specific SPD enable/disable magic.
*
* Setting SP5100 GPIOs 59 and 60 controls an SPI mux with four settings:
* 0: Disabled
* 1: Normal SPI access
* 2: CPU0 SPD
* 3: CPU1 SPD
*
* Disable SPD access after RAM init to allow access to standard SMBus/I2C offsets
* which is required e.g. by lm-sensors.
*/
/* Relevant GPIO register information is available in the
* AMD SP5100 Register Reference Guide rev. 3.03, page 130
*/
static void switch_spd_mux(uint8_t channel)
{
uint8_t byte;
byte = pci_read_config8(PCI_DEV(0, 0x14, 0), 0x54);
byte &= ~0xc; /* Clear SPD mux GPIOs */
byte &= ~0xc0; /* Enable SPD mux GPIO output drivers */
byte |= (channel << 2) & 0xc; /* Set SPD mux GPIOs */
pci_write_config8(PCI_DEV(0, 0x14, 0), 0x54, byte);
}
static const uint8_t spd_addr_fam15[] = {
// Socket 0 Node 0 ("Node 0")
RC00, DIMM0, DIMM1, 0, 0, DIMM2, DIMM3, 0, 0,
// Socket 0 Node 1 ("Node 1")
RC00, DIMM4, DIMM5, 0, 0, DIMM6, DIMM7, 0, 0,
// Socket 1 Node 0 ("Node 2")
RC01, DIMM0, DIMM1, 0, 0, DIMM2, DIMM3, 0, 0,
// Socket 1 Node 1 ("Node 3")
RC01, DIMM4, DIMM5, 0, 0, DIMM6, DIMM7, 0, 0,
};
static const uint8_t spd_addr_fam10[] = {
// Socket 0 Node 0 ("Node 0")
RC00, DIMM0, DIMM1, 0, 0, DIMM2, DIMM3, 0, 0,
// Socket 0 Node 1 ("Node 1")
RC00, DIMM4, DIMM5, 0, 0, DIMM6, DIMM7, 0, 0,
// Socket 1 Node 1 ("Node 2")
RC01, DIMM4, DIMM5, 0, 0, DIMM6, DIMM7, 0, 0,
// Socket 1 Node 0 ("Node 3")
RC01, DIMM0, DIMM1, 0, 0, DIMM2, DIMM3, 0, 0,
};
static void activate_spd_rom(const struct mem_controller *ctrl) {
struct sys_info *sysinfo = &sysinfo_car;
printk(BIOS_DEBUG, "activate_spd_rom() for node %02x\n", ctrl->node_id);
if (ctrl->node_id == 0) {
printk(BIOS_DEBUG, "enable_spd_node0()\n");
switch_spd_mux(0x2);
} else if (ctrl->node_id == 1) {
printk(BIOS_DEBUG, "enable_spd_node1()\n");
switch_spd_mux((is_fam15h() || (sysinfo->nodes <= 2))?0x2:0x3);
} else if (ctrl->node_id == 2) {
printk(BIOS_DEBUG, "enable_spd_node2()\n");
switch_spd_mux((is_fam15h() || (sysinfo->nodes <= 2))?0x3:0x2);
} else if (ctrl->node_id == 3) {
printk(BIOS_DEBUG, "enable_spd_node3()\n");
switch_spd_mux(0x3);
}
}
/* Voltages are specified by index
* Valid indicies for this platform are:
* 0: 1.5V
* 1: 1.35V
* 2: 1.25V
* 3: 1.15V
*/
static void set_ddr3_voltage(uint8_t node, uint8_t index) {
uint8_t byte;
uint8_t value = 0;
if (index == 0)
value = 0x0;
else if (index == 1)
value = 0x1;
else if (index == 2)
value = 0x4;
else if (index == 3)
value = 0x5;
if (node == 1)
value <<= 1;
/* Set GPIOs */
byte = pci_read_config8(PCI_DEV(0, 0x14, 3), 0xd1);
if (node == 0)
byte &= ~0x5;
if (node == 1)
byte &= ~0xa;
byte |= value;
pci_write_config8(PCI_DEV(0, 0x14, 3), 0xd1, byte);
/* Enable GPIO output drivers */
byte = pci_read_config8(PCI_DEV(0, 0x14, 3), 0xd0);
byte &= 0x0f;
pci_write_config8(PCI_DEV(0, 0x14, 3), 0xd0, byte);
printk(BIOS_DEBUG, "Node %02d DIMM voltage set to index %02x\n", node, index);
}
void DIMMSetVoltages(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstatA) {
/* This mainboard allows the DIMM voltage to be set per-socket.
* Therefore, for each socket, iterate over all DIMMs to find the
* lowest supported voltage common to all DIMMs on that socket.
*/
uint8_t nvram;
uint8_t dimm;
uint8_t node;
uint8_t socket;
uint8_t allowed_voltages = 0xf; /* The mainboard VRMs allow 1.15V, 1.25V, 1.35V, and 1.5V */
uint8_t socket_allowed_voltages = allowed_voltages;
uint32_t set_voltage = 0;
if (get_option(&nvram, "minimum_memory_voltage") == CB_SUCCESS) {
switch (nvram) {
case 2:
allowed_voltages = 0x7; /* Allow 1.25V, 1.35V, and 1.5V */
break;
case 1:
allowed_voltages = 0x3; /* Allow 1.35V and 1.5V */
break;
case 0:
default:
allowed_voltages = 0x1; /* Allow 1.5V only */
break;
}
}
for (node = 0; node < MAX_NODES_SUPPORTED; node++) {
socket = node / 2;
struct DCTStatStruc *pDCTstat;
pDCTstat = pDCTstatA + node;
/* reset socket_allowed_voltages before processing each socket */
if (!(node % 2))
socket_allowed_voltages = allowed_voltages;
if (pDCTstat->NodePresent) {
for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) {
if (pDCTstat->DIMMValid & (1 << dimm)) {
socket_allowed_voltages &= pDCTstat->DimmSupportedVoltages[dimm];
}
}
}
/* set voltage per socket after processing last contained node */
if (pDCTstat->NodePresent && (node % 2)) {
/* Set voltages */
if (socket_allowed_voltages & 0x8) {
set_voltage = 0x8;
set_ddr3_voltage(socket, 3);
} else if (socket_allowed_voltages & 0x4) {
set_voltage = 0x4;
set_ddr3_voltage(socket, 2);
} else if (socket_allowed_voltages & 0x2) {
set_voltage = 0x2;
set_ddr3_voltage(socket, 1);
} else {
set_voltage = 0x1;
set_ddr3_voltage(socket, 0);
}
/* Save final DIMM voltages for MCT and SMBIOS use */
if (pDCTstat->NodePresent) {
for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) {
pDCTstat->DimmConfiguredVoltage[dimm] = set_voltage;
}
}
pDCTstat = pDCTstatA + (node - 1);
if (pDCTstat->NodePresent) {
for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) {
pDCTstat->DimmConfiguredVoltage[dimm] = set_voltage;
}
}
}
}
/* Allow the DDR supply voltages to settle */
udelay(100000);
}
static void set_peripheral_control_lines(void) {
uint8_t byte;
uint8_t nvram;
uint8_t enable_ieee1394;
enable_ieee1394 = 1;
if (get_option(&nvram, "ieee1394_controller") == CB_SUCCESS)
enable_ieee1394 = nvram & 0x1;
if (enable_ieee1394) {
/* Enable PCICLK5 (onboard FireWire device) */
outb(0x41, 0xcd6);
outb(0x02, 0xcd7);
} else {
/* Disable PCICLK5 (onboard FireWire device) */
outb(0x41, 0xcd6);
outb(0x00, 0xcd7);
}
/* Enable the RTC AltCentury register */
outb(0x41, 0xcd6);
byte = inb(0xcd7);
byte |= 0x10;
outb(byte, 0xcd7);
}
#ifdef TEST_MEMORY
static void execute_memory_test(void)
{
/* Test DRAM functionality */
uint32_t i;
uint32_t* dataptr;
printk(BIOS_DEBUG, "Writing test patterns to memory...\n");
for (i=0; i < 0x1000000; i = i + 8) {
dataptr = (void *)(0x300000 + i);
*dataptr = 0x55555555;
dataptr = (void *)(0x300000 + i + 4);
*dataptr = 0xaaaaaaaa;
}
printk(BIOS_DEBUG, "Done!\n");
printk(BIOS_DEBUG, "Testing memory...\n");
uint32_t readback;
for (i=0; i < 0x1000000; i = i + 8) {
dataptr = (void *)(0x300000 + i);
readback = *dataptr;
if (readback != 0x55555555)
printk(BIOS_DEBUG, "%p: INCORRECT VALUE %08x (should have been %08x)\n", dataptr, readback, 0x55555555);
dataptr = (void *)(0x300000 + i + 4);
readback = *dataptr;
if (readback != 0xaaaaaaaa)
printk(BIOS_DEBUG, "%p: INCORRECT VALUE %08x (should have been %08x)\n", dataptr, readback, 0xaaaaaaaa);
}
printk(BIOS_DEBUG, "Done!\n");
}
#endif
static spinlock_t printk_spinlock CAR_GLOBAL;
spinlock_t* romstage_console_lock(void)
{
return car_get_var_ptr(&printk_spinlock);
}
void initialize_romstage_console_lock(void)
{
car_get_var(printk_spinlock) = SPIN_LOCK_UNLOCKED;
}
static spinlock_t nvram_cbfs_spinlock CAR_GLOBAL;
spinlock_t* romstage_nvram_cbfs_lock(void)
{
return car_get_var_ptr(&nvram_cbfs_spinlock);
}
void initialize_romstage_nvram_cbfs_lock(void)
{
car_get_var(nvram_cbfs_spinlock) = SPIN_LOCK_UNLOCKED;
}
void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
{
uint32_t esp;
__asm__ volatile (
"movl %%esp, %0"
: "=r" (esp)
);
struct sys_info *sysinfo = &sysinfo_car;
/* Limit the maximum HT speed to 2.6GHz to prevent lockups
* due to HT CPU <--> CPU wiring not being validated to 3.2GHz
*/
sysinfo->ht_link_cfg.ht_speed_limit = 2600;
uint32_t bsp_apicid = 0, val;
uint8_t byte;
msr_t msr;
int s3resume = acpi_is_wakeup_s3();
if (!cpu_init_detectedx && boot_cpu()) {
/* Initial timestamp */
timestamp_init(timestamp_get());
timestamp_add_now(TS_START_ROMSTAGE);
/* Initialize the printk and nvram CBFS spinlocks */
initialize_romstage_console_lock();
initialize_romstage_nvram_cbfs_lock();
/* Nothing special needs to be done to find bus 0 */
/* Allow the HT devices to be found */
set_bsp_node_CHtExtNodeCfgEn();
enumerate_ht_chain();
/* SR56x0 pcie bridges block pci_locate_device() before pcie training.
* disable all pcie bridges on SR56x0 to work around it
*/
sr5650_disable_pcie_bridge();
/* Initialize southbridge */
sb7xx_51xx_pci_port80();
/* Initialize early serial */
nuvoton_enable_serial(SERIAL_DEV, CONFIG_TTYS0_BASE);
console_init();
/* Disable LPC legacy DMA support to prevent lockup */
byte = pci_read_config8(PCI_DEV(0, 0x14, 3), 0x78);
byte &= ~(1 << 0);
pci_write_config8(PCI_DEV(0, 0x14, 3), 0x78, byte);
}
printk(BIOS_SPEW, "Initial stack pointer: %08x\n", esp);
post_code(0x30);
if (bist == 0)
bsp_apicid = init_cpus(cpu_init_detectedx, sysinfo);
post_code(0x32);
enable_sr5650_dev8();
sb7xx_51xx_lpc_init();
if (CONFIG_MAX_PHYSICAL_CPUS != 4)
printk(BIOS_WARNING, "CONFIG_MAX_PHYSICAL_CPUS is %d, but this is a dual socket AMD G34 board!\n", CONFIG_MAX_PHYSICAL_CPUS);
/* Halt if there was a built in self test failure */
report_bist_failure(bist);
val = cpuid_eax(1);
printk(BIOS_DEBUG, "BSP Family_Model: %08x\n", val);
printk(BIOS_DEBUG, "*sysinfo range: [%p,%p]\n",sysinfo,sysinfo+1);
printk(BIOS_DEBUG, "bsp_apicid = %02x\n", bsp_apicid);
printk(BIOS_DEBUG, "cpu_init_detectedx = %08lx\n", cpu_init_detectedx);
/* Setup sysinfo defaults */
set_sysinfo_in_ram(0);
update_microcode(val);
post_code(0x33);
cpuSetAMDMSR(0);
post_code(0x34);
amd_ht_init(sysinfo);
amd_ht_fixup(sysinfo);
post_code(0x35);
/* Setup nodes PCI space and start core 0 AP init. */
finalize_node_setup(sysinfo);
/* Setup any mainboard PCI settings etc. */
setup_mb_resource_map();
post_code(0x36);
/* Wait for all the APs core0 started by finalize_node_setup. */
wait_all_core0_started();
/* run _early_setup before soft-reset. */
sr5650_early_setup();
sb7xx_51xx_early_setup();
if (IS_ENABLED(CONFIG_LOGICAL_CPUS)) {
/* Core0 on each node is configured. Now setup any additional cores. */
printk(BIOS_DEBUG, "start_other_cores()\n");
start_other_cores(bsp_apicid);
post_code(0x37);
wait_all_other_cores_started(bsp_apicid);
}
if (IS_ENABLED(CONFIG_SET_FIDVID)) {
msr = rdmsr(0xc0010071);
printk(BIOS_DEBUG, "\nBegin FIDVID MSR 0xc0010071 0x%08x 0x%08x\n", msr.hi, msr.lo);
/* FIXME: The sb fid change may survive the warm reset and only need to be done once */
enable_fid_change_on_sb(sysinfo->sbbusn, sysinfo->sbdn);
post_code(0x39);
#if IS_ENABLED(CONFIG_SET_FIDVID)
if (!warm_reset_detect(0)) { // BSP is node 0
init_fidvid_bsp(bsp_apicid, sysinfo->nodes);
} else {
init_fidvid_stage2(bsp_apicid, 0); // BSP is node 0
}
#endif
post_code(0x3A);
/* show final fid and vid */
msr=rdmsr(0xc0010071);
printk(BIOS_DEBUG, "End FIDVIDMSR 0xc0010071 0x%08x 0x%08x\n", msr.hi, msr.lo);
}
post_code(0x38);
init_timer(); // Need to use TMICT to synconize FID/VID
sr5650_htinit();
/* Reset for HT, FIDVID, PLL and errata changes to take effect. */
if (!warm_reset_detect(0)) {
printk(BIOS_INFO, "...WARM RESET...\n\n\n");
soft_reset();
die("After soft_reset_x - shouldn't see this message!!!\n");
}
sr5650_htinit_dect_and_enable_isochronous_link();
/* Set default DDR memory voltage
* This will be overridden later during RAM initialization
*/
set_lpc_sticky_ctl(1); /* Retain LPC/IMC GPIO configuration during S3 sleep */
if (!s3resume) { /* Avoid supply voltage glitches while the DIMMs are retaining data */
set_ddr3_voltage(0, 0); /* Node 0 */
set_ddr3_voltage(1, 0); /* Node 1 */
}
/* Set up peripheral control lines */
set_peripheral_control_lines();
post_code(0x3B);
/* It's the time to set ctrl in sysinfo now; */
printk(BIOS_DEBUG, "fill_mem_ctrl() detected %d nodes\n", sysinfo->nodes);
if (is_fam15h())
fill_mem_ctrl(sysinfo->nodes, sysinfo->ctrl, spd_addr_fam15);
else
fill_mem_ctrl(sysinfo->nodes, sysinfo->ctrl, spd_addr_fam10);
post_code(0x3D);
#if 0
/* FIXME
* After the AMD K10 code has been converted to use
* IS_ENABLED(CONFIG_DEBUG_SMBUS) uncomment this block
*/
if (IS_ENABLED(CONFIG_DEBUG_SMBUS)) {
dump_spd_registers(&cpu[0]);
dump_smbus_registers();
}
#endif
post_code(0x40);
timestamp_add_now(TS_BEFORE_INITRAM);
printk(BIOS_DEBUG, "raminit_amdmct()\n");
raminit_amdmct(sysinfo);
timestamp_add_now(TS_AFTER_INITRAM);
#if !IS_ENABLED(CONFIG_LATE_CBMEM_INIT)
if (s3resume)
cbmem_initialize();
else
cbmem_initialize_empty();
post_code(0x41);
amdmct_cbmem_store_info(sysinfo);
#endif
printk(BIOS_DEBUG, "disable_spd()\n");
switch_spd_mux(0x1);
sr5650_before_pci_init();
sb7xx_51xx_before_pci_init();
/* Configure SP5100 GPIOs to match vendor settings */
pci_write_config16(PCI_DEV(0, 0x14, 0), 0x50, 0x0170);
pci_write_config16(PCI_DEV(0, 0x14, 0), 0x54, 0x0707);
pci_write_config16(PCI_DEV(0, 0x14, 0), 0x56, 0x0bb0);
pci_write_config16(PCI_DEV(0, 0x14, 0), 0x5a, 0x0ff0);
timestamp_add_now(TS_END_ROMSTAGE);
#ifdef TEST_MEMORY
execute_memory_test();
#endif
post_cache_as_ram(); // BSP switch stack to ram, copy then execute LB.
post_code(0x43); // Should never see this post code.
}
/**
* BOOL AMD_CB_ManualBUIDSwapList(u8 Node, u8 Link, u8 **List)
* Description:
* This routine is called every time a non-coherent chain is processed.
* BUID assignment may be controlled explicitly on a non-coherent chain. Provide a
* swap list. The first part of the list controls the BUID assignment and the
* second part of the list provides the device to device linking. Device orientation
* can be detected automatically, or explicitly. See documentation for more details.
*
* Automatic non-coherent init assigns BUIDs starting at 1 and incrementing sequentially
* based on each device's unit count.
*
* Parameters:
* @param[in] node = The node on which this chain is located
* @param[in] link = The link on the host for this chain
* @param[out] List = supply a pointer to a list
*/
BOOL AMD_CB_ManualBUIDSwapList (u8 node, u8 link, const u8 **List)
{
/* Force BUID to 0 */
static const u8 swaplist[] = {0, 0, 0xFF, 0, 0xFF};
if ((is_fam15h() && (node == 0) && (link == 1)) /* Family 15h BSP SB link */
|| (!is_fam15h() && (node == 0) && (link == 3))) { /* Family 10h BSP SB link */
*List = swaplist;
return 1;
}
return 0;
}