diff options
author | Timothy Pearson <tpearson@raptorengineeringinc.com> | 2015-11-24 14:12:08 -0600 |
---|---|---|
committer | Martin Roth <martinroth@google.com> | 2016-02-05 22:28:38 +0100 |
commit | 4551b68c83e7693ae0b079dce9e4dcaf35050fa2 (patch) | |
tree | c468054755eac45fe3c2655f7dbaf25944c32c4a /src/mainboard/asus/kcma-d8/romstage.c | |
parent | f098a7310a6e1d1e86f3720d10eaa7c0b0687935 (diff) | |
download | coreboot-4551b68c83e7693ae0b079dce9e4dcaf35050fa2.tar.xz |
mainboard/asus/kcma-d8: Copy ASUS KGPE-D16 for initial support work
Also updated KGPE-D16 strings to KCMA-D8 throughout the copy to work
around Jenkins failures caused by an unmodified clone.
Change-Id: I943e81c8c2987a8333fc2a1cdb3675abf2d90cf1
Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com>
Reviewed-on: https://review.coreboot.org/13521
Tested-by: build bot (Jenkins)
Reviewed-by: Martin Roth <martinroth@google.com>
Diffstat (limited to 'src/mainboard/asus/kcma-d8/romstage.c')
-rw-r--r-- | src/mainboard/asus/kcma-d8/romstage.c | 594 |
1 files changed, 594 insertions, 0 deletions
diff --git a/src/mainboard/asus/kcma-d8/romstage.c b/src/mainboard/asus/kcma-d8/romstage.c new file mode 100644 index 0000000000..4210a92134 --- /dev/null +++ b/src/mainboard/asus/kcma-d8/romstage.c @@ -0,0 +1,594 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2015 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 <delay.h> +#include <cpu/x86/lapic.h> +#include "northbridge/amd/amdfam10/reset_test.c" +#include <superio/winbond/common/winbond.h> +#include <superio/winbond/w83667hg-a/w83667hg-a.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, W83667HG_A_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 KCMA-D8 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) { + /* Nothing needs to be done as there is no SPD mux on this board */ + printk(BIOS_DEBUG, "activate_spd_rom() for node %02x\n", ctrl->node_id); +} + +/* 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; +} + +static spinlock_t microcode_cbfs_spinlock CAR_GLOBAL; + +spinlock_t* romstage_microcode_cbfs_lock(void) +{ + return car_get_var_ptr(µcode_cbfs_spinlock); +} + +void initialize_romstage_microcode_cbfs_lock(void) +{ + car_get_var(microcode_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, nvram CBFS, and microcode CBFS spinlocks */ + initialize_romstage_console_lock(); + initialize_romstage_nvram_cbfs_lock(); + initialize_romstage_microcode_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 */ + winbond_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 != 2) + printk(BIOS_WARNING, "CONFIG_MAX_PHYSICAL_CPUS is %d, but this is a dual socket AMD C32 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); + + /* Wait for all APs to be stopped, otherwise ram initialization may hang */ + if (IS_ENABLED(CONFIG_LOGICAL_CPUS)) + wait_all_other_cores_stopped(bsp_apicid); + + /* 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); + +#ifdef TEST_MEMORY + execute_memory_test(); +#endif + +#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); + + 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; +} |