From 8ae8c8822068ef1722c08073ffa4ecc25633cbee Mon Sep 17 00:00:00 2001 From: Marc Jones Date: Wed, 19 Dec 2007 01:32:08 +0000 Subject: Initial AMD Barcelona support for rev Bx. These are the core files for HyperTransport, DDR2 Memory, and multi-core initialization. Signed-off-by: Marc Jones Reviewed-by: Jordan Crouse Acked-by: Myles Watson git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3014 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1 --- src/cpu/amd/car/cache_as_ram.inc | 204 ++++++++-- src/cpu/amd/microcode/microcode.c | 17 +- src/cpu/amd/model_10xxx/Config.lb | 40 ++ src/cpu/amd/model_10xxx/apic_timer.c | 55 +++ src/cpu/amd/model_10xxx/fidvid.c | 455 +++++++++++++++++++++++ src/cpu/amd/model_10xxx/fidvid_common.c | 312 ++++++++++++++++ src/cpu/amd/model_10xxx/init_cpus.c | 553 ++++++++++++++++++++++++++++ src/cpu/amd/model_10xxx/mc_patch_01000018.h | 163 ++++++++ src/cpu/amd/model_10xxx/mc_patch_01000020.h | 163 ++++++++ src/cpu/amd/model_10xxx/mc_patch_01000033.h | 163 ++++++++ src/cpu/amd/model_10xxx/mc_patch_01000035.h | 163 ++++++++ src/cpu/amd/model_10xxx/model_10xxx_init.c | 463 +++++++++++++++++++++++ src/cpu/amd/model_10xxx/pstate.c | 456 +++++++++++++++++++++++ src/cpu/amd/model_10xxx/update_microcode.c | 90 +++++ src/cpu/amd/quadcore/Config.lb | 20 + src/cpu/amd/quadcore/amd_sibling.c | 122 ++++++ src/cpu/amd/quadcore/quadcore.c | 102 +++++ src/cpu/amd/quadcore/quadcore_id.c | 79 ++++ src/cpu/amd/socket_F_1207/Config.lb | 57 +++ src/cpu/amd/socket_F_1207/chip.h | 23 ++ src/cpu/amd/socket_F_1207/socket_F_1207.c | 25 ++ 21 files changed, 3695 insertions(+), 30 deletions(-) create mode 100644 src/cpu/amd/model_10xxx/Config.lb create mode 100644 src/cpu/amd/model_10xxx/apic_timer.c create mode 100644 src/cpu/amd/model_10xxx/fidvid.c create mode 100644 src/cpu/amd/model_10xxx/fidvid_common.c create mode 100644 src/cpu/amd/model_10xxx/init_cpus.c create mode 100644 src/cpu/amd/model_10xxx/mc_patch_01000018.h create mode 100644 src/cpu/amd/model_10xxx/mc_patch_01000020.h create mode 100644 src/cpu/amd/model_10xxx/mc_patch_01000033.h create mode 100644 src/cpu/amd/model_10xxx/mc_patch_01000035.h create mode 100644 src/cpu/amd/model_10xxx/model_10xxx_init.c create mode 100644 src/cpu/amd/model_10xxx/pstate.c create mode 100644 src/cpu/amd/model_10xxx/update_microcode.c create mode 100644 src/cpu/amd/quadcore/Config.lb create mode 100644 src/cpu/amd/quadcore/amd_sibling.c create mode 100644 src/cpu/amd/quadcore/quadcore.c create mode 100644 src/cpu/amd/quadcore/quadcore_id.c create mode 100644 src/cpu/amd/socket_F_1207/Config.lb create mode 100644 src/cpu/amd/socket_F_1207/chip.h create mode 100644 src/cpu/amd/socket_F_1207/socket_F_1207.c (limited to 'src/cpu/amd') diff --git a/src/cpu/amd/car/cache_as_ram.inc b/src/cpu/amd/car/cache_as_ram.inc index 53c34303ff..748223a06b 100644 --- a/src/cpu/amd/car/cache_as_ram.inc +++ b/src/cpu/amd/car/cache_as_ram.inc @@ -1,6 +1,6 @@ /* * This file is part of the LinuxBIOS project. - * + * * Copyright (C) 2005-2007 Advanced Micro Devices, Inc. * * This program is free software; you can redistribute it and/or modify @@ -15,23 +15,30 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - + */ + #define CacheSize DCACHE_RAM_SIZE #define CacheBase (0xd0000 - CacheSize) - + /* leave some space for global variable to pass to RAM stage */ #define GlobalVarSize DCACHE_RAM_GLOBAL_VAR_SIZE +#if CAR_FAM10 == 1 +#define CacheSizeAPStack 0x400 /* 1K */ +#endif + #include #include /* Save the BIST result */ movl %eax, %ebp - /* for normal part %ebx already contain cpu_init_detected from fallback call */ + /*for normal part %ebx already contain cpu_init_detected from fallback call */ cache_as_ram_setup: + + movb $0xA0, %al + outb %al, $0x80 /* hope we can skip the double set for normal part */ #if ((HAVE_FAILOVER_BOOT == 1) && (USE_FAILOVER_IMAGE == 1)) || ((HAVE_FAILOVER_BOOT == 0) && (USE_FALLBACK_IMAGE == 1)) @@ -42,6 +49,53 @@ cache_as_ram_setup: andl $(1 << 11), %eax movl %eax, %ebx /* We store the status */ +#if CAR_FAM10 == 1 + /* for GH, CAR need to set DRAM Base/Limit Registers to direct that to node0 */ + + /* Only BSP needed, for other nodes set during HT/memory init. */ + /* So we need to check if it is BSP */ + movl $0x1b, %ecx + rdmsr + bt $8, %eax /*BSC */ + jnc CAR_FAM10_out + + /* Enable RT tables on BSP */ + movl $0x8000c06c, %eax + movw $0xcf8, %dx + outl %eax, %dx + addw $4, %dx + inl %dx, %eax + btr $0, %eax + outl %eax, %dx + + /* Setup temporary DRAM map: [0,16M) bit 0-23 */ + movl $0x8000c144, %eax + movw $0xcf8, %dx + outl %eax, %dx + addw $4, %dx + movl $0, %eax + outl %eax, %dx + + movl $0x8000c140, %eax + movw $0xcf8, %dx + outl %eax, %dx + addw $4, %dx + movl $3, %eax + outl %eax, %dx + +CAR_FAM10_out: + +#endif + +#if CAR_FAM10 == 1 + /* Errata 193: Disable clean copybacks to L3 cache to allow cached ROM. + Re-enable it in after RAM is initialized and before CAR is disabled */ + movl $0xc001102a, %ecx + rdmsr + bts $15, %eax + wrmsr +#endif + /* Set MtrrFixDramModEn for clear fixed mtrr */ enable_fixed_mtrr_dram_modify: movl $SYSCFG_MSR, %ecx @@ -53,7 +107,7 @@ enable_fixed_mtrr_dram_modify: /* Clear all MTRRs */ xorl %edx, %edx movl $fixed_mtrr_msr, %esi - + clear_fixed_var_mtrr: lodsl (%esi), %eax testl %eax, %eax @@ -68,9 +122,14 @@ clear_fixed_var_mtrr_out: #if CacheSize == 0x10000 /* enable caching for 64K using fixed mtrr */ - movl $0x268, %ecx /* fix4k_c0000 */ - movl $0x06060606, %eax /* WB IO type */ - movl %eax, %edx + movl $0x268, %ecx /* fix4k_c0000*/ + #if CAR_FAM10 == 1 + movl $0x1e1e1e1e, %edx /* WB MEM type */ + #else + movl $0x06060606, %edx /* WB IO type */ + #endif + + movl %edx, %eax wrmsr movl $0x269, %ecx wrmsr @@ -78,38 +137,62 @@ clear_fixed_var_mtrr_out: #if CacheSize == 0xc000 /* enable caching for 16K using fixed mtrr */ - movl $0x268, %ecx /* fix4k_c4000 */ - movl $0x06060606, %edx /* WB IO type */ + movl $0x268, %ecx /* fix4k_c4000*/ + #if CAR_FAM10 == 1 + movl $0x1e1e1e1e, %edx /* WB MEM type */ + #else + movl $0x06060606, %edx /* WB IO type */ + #endif xorl %eax, %eax wrmsr /* enable caching for 32K using fixed mtrr */ - movl $0x269, %ecx /* fix4k_c8000 */ - movl $0x06060606, %eax /* WB IO type */ - movl %eax, %edx + movl $0x269, %ecx /* fix4k_c8000*/ + #if CAR_FAM10 == 1 + movl $0x1e1e1e1e, %edx /* WB MEM type */ + #else + movl $0x06060606, %edx /* WB IO type */ + #endif + movl %edx, %eax wrmsr #endif #if CacheSize == 0x8000 /* enable caching for 32K using fixed mtrr */ - movl $0x269, %ecx /* fix4k_c8000 */ - movl $0x06060606, %eax /* WB IO type */ - movl %eax, %edx + movl $0x269, %ecx /* fix4k_c8000*/ + #if CAR_FAM10 == 1 + movl $0x1e1e1e1e, %edx /* WB MEM type */ + #else + movl $0x06060606, %edx /* WB IO type */ + #endif + movl %edx, %eax wrmsr #endif #if CacheSize < 0x8000 /* enable caching for 16K/8K/4K using fixed mtrr */ movl $0x269, %ecx /* fix4k_cc000*/ -#if CacheSize == 0x4000 + #if CacheSize == 0x4000 + #if CAR_FAM10 == 1 + movl $0x1e1e1e1e, %edx /* WB MEM type */ + #else movl $0x06060606, %edx /* WB IO type */ -#endif -#if CacheSize == 0x2000 + #endif + #endif + #if CacheSize == 0x2000 + #if CAR_FAM10 == 1 + movl $0x1e1e0000, %edx /* WB MEM type */ + #else movl $0x06060000, %edx /* WB IO type */ -#endif -#if CacheSize == 0x1000 + #endif + #endif + #if CacheSize == 0x1000 + #if CAR_FAM10 == 1 + movl $0x1e000000, %edx /* WB MEM type */ + #else movl $0x06000000, %edx /* WB IO type */ -#endif + #endif + #endif xorl %eax, %eax wrmsr #endif @@ -160,13 +243,27 @@ clear_fixed_var_mtrr_out: wrmsr #endif + movb $0xA1, %al + outb %al, $0x80 + /* enable cache */ movl %cr0, %eax andl $0x9fffffff, %eax movl %eax, %cr0 -#if ((HAVE_FAILOVER_BOOT == 1) && (USE_FAILOVER_IMAGE == 1)) || ((HAVE_FAILOVER_BOOT == 0) && (USE_FALLBACK_IMAGE == 1)) +#if CAR_FAM10 == 1 + /* So we need to check if it is BSP */ + movl $0x1b, %ecx + rdmsr + bt $8, %eax /*BSC */ + jnc CAR_FAM10_ap +#endif + + movb $0xA2, %al + outb %al, $0x80 + +#if ((HAVE_FAILOVER_BOOT == 1) && (USE_FAILOVER_IMAGE == 1)) || ((HAVE_FAILOVER_BOOT == 0) && (USE_FALLBACK_IMAGE == 1)) /* Read the range with lodsl*/ cld movl $CacheBase, %esi @@ -183,10 +280,64 @@ clear_fixed_var_mtrr_out: /* set up the stack pointer */ movl $(CacheBase + CacheSize - GlobalVarSize), %eax movl %eax, %esp + + movb $0xA3, %al + outb %al, $0x80 + +#if CAR_FAM10 == 1 + + jmp CAR_FAM10_ap_out +CAR_FAM10_ap: + /* need to set stack pointer for AP */ + /* it will be from CacheBase + (CacheSize - GlobalVarSize)/2 - (NodeID< #include #include @@ -24,6 +26,8 @@ #include #include +#endif + struct microcode { u32 date_code; u32 patch_id; @@ -56,8 +60,11 @@ struct microcode { static int need_apply_patch(struct microcode *m, u32 equivalent_processor_rev_id) { - if (m->processor_rev_id != equivalent_processor_rev_id) return 0; - + if (m->processor_rev_id != equivalent_processor_rev_id) { + printk_debug("microcode: rev id does not match this patch.\n"); + printk_debug("microcode: Not updated! Fix microcode_updates[] \n"); + return 0; + } if (m->nb_dev_id) { //look at the device id, if not found return; //if(m->nb_rev_id != installed_nb_rev_id) return 0; @@ -86,7 +93,7 @@ void amd_update_microcode(void *microcode_updates, u32 equivalent_processor_rev_ msr = rdmsr(0x8b); patch_id = msr.lo; - printk_debug("microcode: equivalent processor rev id = 0x%04x, patch id = 0x%08x\n", equivalent_processor_rev_id, patch_id); + printk_debug("microcode: equivalent rev id = 0x%04x, current patch id = 0x%08x\n", equivalent_processor_rev_id, patch_id); m = microcode_updates; @@ -100,13 +107,13 @@ void amd_update_microcode(void *microcode_updates, u32 equivalent_processor_rev_ wrmsr(0xc0010020, msr); - printk_debug("microcode: patch id that want to apply= 0x%08x\n", m->patch_id); + printk_debug("microcode: patch id to apply = 0x%08x\n", m->patch_id); //read the patch_id again msr = rdmsr(0x8b); new_patch_id = msr.lo; - printk_debug("microcode: updated to patch id = 0x%08x %s\r\n", new_patch_id , (new_patch_id == m->patch_id)?" success":" fail" ); + printk_debug("microcode: updated to patch id = 0x%08x %s\n", new_patch_id , (new_patch_id == m->patch_id)?" success\n":" fail\n" ); break; } c += 2048; diff --git a/src/cpu/amd/model_10xxx/Config.lb b/src/cpu/amd/model_10xxx/Config.lb new file mode 100644 index 0000000000..afd03603b2 --- /dev/null +++ b/src/cpu/amd/model_10xxx/Config.lb @@ -0,0 +1,40 @@ +# +# This file is part of the LinuxBIOS project. +# +# Copyright (C) 2007 Advanced Micro Devices, Inc. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +uses HAVE_INIT_TIMER +uses HAVE_MOVNTI +uses CPU_ADDR_BITS + +default HAVE_INIT_TIMER=1 +default HAVE_MOVNTI=1 +default CPU_ADDR_BITS=48 +dir /cpu/x86/tsc +dir /cpu/x86/fpu +dir /cpu/x86/mmx +dir /cpu/x86/sse +dir /cpu/x86/lapic +dir /cpu/x86/cache +dir /cpu/x86/pae +dir /cpu/amd/mtrr +dir /cpu/amd/quadcore +dir /cpu/amd/microcode +driver model_10xxx_init.o +object update_microcode.o +object apic_timer.o +object pstate.o diff --git a/src/cpu/amd/model_10xxx/apic_timer.c b/src/cpu/amd/model_10xxx/apic_timer.c new file mode 100644 index 0000000000..46cff61584 --- /dev/null +++ b/src/cpu/amd/model_10xxx/apic_timer.c @@ -0,0 +1,55 @@ +/* + * This file is part of the LinuxBIOS project. + * + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include + +/* NOTE: We use the APIC TIMER register is to hold flags for AP init during + * pre-memory init (ROMCC). Don't use init_timer() and udelay is redirected + * to udelay_tsc(). + */ + + +void init_timer(void) +{ + /* Set the apic timer to no interrupts and periodic mode */ + lapic_write(LAPIC_LVTT, (1 << 17)|(1<< 16)|(0 << 12)|(0 << 0)); + + /* Set the divider to 1, no divider */ + lapic_write(LAPIC_TDCR, LAPIC_TDR_DIV_1); + + /* Set the initial counter to 0xffffffff */ + lapic_write(LAPIC_TMICT, 0xffffffff); + +} + + +void udelay(u32 usecs) +{ + u32 start, value, ticks; + /* Calculate the number of ticks to run, our FSB runs a 200Mhz */ + ticks = usecs * 200; + start = lapic_read(LAPIC_TMCCT); + do { + value = lapic_read(LAPIC_TMCCT); + } while((start - value) < ticks); + +} diff --git a/src/cpu/amd/model_10xxx/fidvid.c b/src/cpu/amd/model_10xxx/fidvid.c new file mode 100644 index 0000000000..8b2169b32d --- /dev/null +++ b/src/cpu/amd/model_10xxx/fidvid.c @@ -0,0 +1,455 @@ +/* + * This file is part of the LinuxBIOS project. + * + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if FAM10_SET_FIDVID == 1 + +#define FAM10_SET_FIDVID_DEBUG 1 + +// if we are tight of CAR stack, disable it +#define FAM10_SET_FIDVID_STORE_AP_APICID_AT_FIRST 1 + +static inline void print_debug_fv(const char *str, u32 val) +{ +#if FAM10_SET_FIDVID_DEBUG == 1 + printk_debug("%s%x\n", str, val); +#endif +} + +static inline void print_debug_fv_8(const char *str, u8 val) +{ +#if FAM10_SET_FIDVID_DEBUG == 1 + printk_debug("%s%02x\n", str, val); +#endif +} + +static inline void print_debug_fv_64(const char *str, u32 val, u32 val2) +{ +#if FAM10_SET_FIDVID_DEBUG == 1 + printk_debug("%s%x%x\n", str, val, val2); +#endif +} + + +static void enable_fid_change(u8 fid) +{ + u32 dword; + u32 nodes; + device_t dev; + int i; + + nodes = ((pci_read_config32(PCI_DEV(CBB, CDB, 0), 0x60) >> 4) & 7) + 1; + + for(i = 0; i < nodes; i++) { + dev = NODE_PCI(i,3); + dword = pci_read_config32(dev, 0xd4); + dword &= ~0x1F; + dword |= (u32) fid & 0x1F; + dword |= 1 << 5; // enable + pci_write_config32(dev, 0xd4, dword); + printk_debug("FID Change Node:%02x, F3xD4: %08x \n", i, dword); + } +} + +static void prep_fid_change(void) +{ + u32 dword; + u32 nodes; + device_t dev; + int i; + + /* This needs to be run before any Pstate changes are requested */ + + nodes = ((pci_read_config32(PCI_DEV(CBB, CDB, 0), 0x60) >> 4) & 7) + 1; + + for(i = 0; i < nodes; i++) { + printk_debug("Node:%02x \n", i); + dev = NODE_PCI(i,3); + + dword = pci_read_config32(dev, 0xa0); + dword &= ~(1<<29); + dword |= ((~dword >> 8) & 1) << 29; // SlamVidMode is the inverse to the PviMode + dword |= PLLLOCK_DFT_L; /* Force per BKDG */ + pci_write_config32(dev, 0xa0, dword); + printk_debug(" F3xA0: %08x \n", dword); + + dword = pci_read_config32(dev, 0xd8); + dword &= ~0x77; + dword |= (1<<4) | 6; // VSRampTime, and VSSlamTime + dword |= 3 << 24; // ReConDel set to 3 per BKDG + pci_write_config32(dev, 0xd8, dword); + printk_debug(" F3xD8: %08x \n", dword); + + dword = pci_read_config32(dev, 0xd4); + dword &= 0x1F; + dword |= 0xC331AF00; // per BKDG + pci_write_config32(dev, 0xd4, dword); + printk_debug(" F3xD4: %08x \n", dword); + + dword = pci_read_config32(dev, 0xdc); + dword |= 0x5 << 12; // NbsynPtrAdj set to 0x5 per BKDG (needs reset) + pci_write_config32(dev, 0xdc, dword); + printk_debug(" F3xDC: %08x \n", dword); + + // Rev B settings - FIXME: support other revs. + dword = 0xA0E641E6; + pci_write_config32(dev, 0x84, dword); + printk_debug(" F3x84: %08x \n", dword); + + dword = 0xE600A681; + pci_write_config32(dev, 0x80, dword); + printk_debug(" F3x80: %08x \n", dword); + + } +} + + +#include "fidvid_common.c" + + + +static void init_fidvid_ap(u32 bsp_apicid, u32 apicid, u32 nodeid, u32 coreid) +{ + + msr_t msr; + device_t dev; + u8 vid_max; + u8 fid_max; + u8 startup_pstate; + u8 nb_cof_vid_update; + u8 pvimode; + u32 reg1fc; + u32 dword; + u32 send; + + printk_debug("FIDVID on AP: %02x\n", apicid); + + /* Only support single plane system at this time. */ + /* Steps 1-6 of BIOS NB COF and VID Configuration + * for Single-Plane PVI Systems + */ + dev = NODE_PCI(nodeid,3); + reg1fc = pci_read_config32(dev, 0x1FC); + nb_cof_vid_update = reg1fc & 1; + if (nb_cof_vid_update) { + /* Get fused settings */ + dword = pci_read_config32(dev, 0xa0); + pvimode = (dword >> 8) & 1; + + vid_max = (reg1fc >> 7) & 0x7F; // per node + fid_max = (reg1fc >> 2) & 0x1F; // per system + + if (pvimode) { + /* FIXME: support daul plane mode */ + die("PVImode not supported\n"); + /* fidmax = vidmax - (reg1fc >> 17) & 0x1F; + fidmax = fidmax + (reg1fc >> 14) & 0x03; + */ + } + + } else { + /* Use current values */ + msr = rdmsr(0xc0010071); + fid_max = ((msr.hi >> (59-32)) & 0x1f); //max nb fid + vid_max = ((msr.hi >> (35-32)) & 0x7f); //max vid + } + + /* Note this is the single plane setup. Need to add dual plane path */ + msr = rdmsr(0xc0010071); + startup_pstate = (msr.hi >> (32-32)) & 0x07; + + + /* Copy startup pstate to P1 and P0 MSRs. Set the maxvid for this node in P0. + Then transition to P1 for corex and P0 for core0. */ + msr = rdmsr(0xC0010064 + startup_pstate); + wrmsr(0xC0010065, msr); + wrmsr(0xC0010064, msr); + + msr.lo &= ~0xFE000000; // clear nbvid + msr.lo |= vid_max << 25; + wrmsr(0xC0010064, msr); + + // Transition to P1 for all APs and P0 for core0. + msr = rdmsr(0xC0010062); + msr.lo = (msr.lo & ~0x07) | 1; + wrmsr(0xC0010062, msr); + + // Wait for P1 to set. + do { + msr = rdmsr(0xC0010063); + } while (msr.lo != 1); + + if (coreid == 0) { + msr.lo = msr.lo & ~0x07; + wrmsr(0xC0010062, msr); + // Wait for P0 to set. + do { + msr = rdmsr(0xC0010063); + } while (msr.lo != 0); + } + + + send = (nb_cof_vid_update << 16) | (fid_max << 8); + send |= (apicid << 24); // ap apicid + + // Send signal to BSP about this AP max fid + // This also indicates this AP is ready for warm reset (if required). + lapic_write(LAPIC_MSG_REG, send | 1); +} + +static u32 calc_common_fid(u32 fid_packed, u32 fid_packed_new) +{ + u32 fidmax; + u32 fidmax_new; + + fidmax = (fid_packed >> 8) & 0xFF; + + fidmax_new = (fid_packed_new >> 8) & 0xFF; + + if(fidmax > fidmax_new) { + fidmax = fidmax_new; + } + + fid_packed &= 0xFF << 16; + fid_packed |= (fidmax << 8); + fid_packed |= fid_packed_new & (0xFF << 16); // set nb_cof_vid_update + + return fid_packed; +} + +struct fidvid_st { + u32 common_fid; +}; + +static void init_fidvid_bsp_stage1(u32 ap_apicid, void *gp ) +{ + u32 readback = 0; + u32 timeout = 1; + + struct fidvid_st *fvp = gp; + int loop; + + print_debug_fv("Wait for AP stage 1: ap_apicid = ", ap_apicid); + + loop = 100000; + while(--loop > 0) { + if(lapic_remote_read(ap_apicid, LAPIC_MSG_REG, &readback) != 0) continue; + if((readback & 0x3f) == 1) { + timeout = 0; + break; //target ap is in stage 1 + } + } + + if(timeout) { + print_initcpu8("fidvid_bsp_stage1: time out while reading from ap ", ap_apicid); + return; + } + + print_debug_fv("\treadback = ", readback); + + fvp->common_fid = calc_common_fid(fvp->common_fid, readback); + + print_debug_fv("\tcommon_fid(packed) = ", fvp->common_fid); + +} + + +static void init_fidvid_stage2(u32 apicid, u32 nodeid) +{ + msr_t msr; + device_t dev; + u32 reg1fc; + u8 StartupPstate; + u8 nbvid; + int i; + + /* After warm reset finish the fid/vid setup for all cores. */ + dev = NODE_PCI(nodeid,3); + reg1fc = pci_read_config32(dev, 0x1FC); + nbvid = (reg1fc >> 7) & 0x7F; + + if (reg1fc & 0x02) { // NbVidUpdateAll ? + for( i = 0; i < 5; i++) { + msr = rdmsr(0xC0010064 + i); + if ((msr.hi >> 31) & 1) { // PstateEn? + msr.lo &= ~(0x7F << 25); + msr.lo |= (nbvid & 0x7F) << 25; + } + } + } else { + for( i = 0; i < 5; i++) { + msr = rdmsr(0xC0010064 + i); + if (((msr.hi >> 31) & 1) && (((msr.lo >> 22) & 1) == 0)) { // PstateEn and PDid == 0? + msr.lo &= ~(0x7F << 25); + msr.lo |= (nbvid & 0x7F) << 25; + } + } + } + + // For each processor in the system, transition all cores to StartupPstate + msr = rdmsr(0xC0010071); + StartupPstate = msr.hi >> (32-32) & 0x03; + msr = rdmsr(0xC0010062); + msr.lo = StartupPstate; + wrmsr(0xC0010062, msr); +} + + +#if FAM10_SET_FIDVID_STORE_AP_APICID_AT_FIRST == 1 +struct ap_apicid_st { + u32 num; + // it could use 256 bytes for 64 node quad core system + u8 apicid[NODE_NUMS * 4]; +}; + +static void store_ap_apicid(unsigned ap_apicid, void *gp) +{ + struct ap_apicid_st *p = gp; + + p->apicid[p->num++] = ap_apicid; + +} +#endif + + +static int init_fidvid_bsp(u32 bsp_apicid, u32 nodes) +{ +#if FAM10_SET_FIDVID_STORE_AP_APICID_AT_FIRST == 1 + struct ap_apicid_st ap_apicidx; + u32 i; +#endif + struct fidvid_st fv; + msr_t msr; + device_t dev; + u8 vid_max; + u8 fid_max; + u8 startup_pstate; + u8 nb_cof_vid_update; + u32 reg1fc; + u32 dword; + u8 pvimode; + + printk_debug("FIDVID on BSP, APIC_id: %02x\n", bsp_apicid); + + /* FIXME: Only support single plane system at this time. */ + /* Steps 1-6 of BIOS NB COF and VID Configuration + * for Single-Plane PVI Systems + */ + dev = NODE_PCI(0,3); // nodeid for the BSP is 0 + reg1fc = pci_read_config32(dev, 0x1FC); + nb_cof_vid_update = reg1fc & 1; + if (nb_cof_vid_update) { + /* Get fused settings */ + dword = pci_read_config32(dev, 0xa0); + pvimode = (dword >> 8) & 1; + + vid_max = (reg1fc >> 7) & 0x7F; // per node + fid_max = (reg1fc >> 2) & 0x1F; // per system + + if (pvimode) { + /* FIXME: support daul plane mode */ + die("PVImode not supported\n"); + /* fidmax = vidmax - (reg1fc >> 17) & 0x1F; + fidmax = fidmax + (reg1fc >> 14) & 0x03; + */ + } + + } else { + /* Use current values */ + msr = rdmsr(0xc0010071); + fid_max = ((msr.hi >> (59-32)) & 0x1f); //max nb fid + vid_max = ((msr.hi >> (35-32)) & 0x7f); //max vid + } + + /* Note this is the single plane setup. Need to add dual plane path */ + msr = rdmsr(0xc0010071); + startup_pstate = (msr.hi >> (32-32)) & 0x07; + + + /* Copy startup pstate to P1 and P0 MSRs. Set the maxvid for this node in P0. + Then transition to P1 for corex and P0 for core0. */ + msr = rdmsr(0xC0010064 + startup_pstate); + wrmsr(0xC0010065, msr); + wrmsr(0xC0010064, msr); + + msr.lo &= ~0xFE000000; // clear nbvid + msr.lo |= vid_max << 25; + wrmsr(0xC0010064, msr); + + // Transition to P1 and then P0 for core0. + msr = rdmsr(0xC0010062); + msr.lo = (msr.lo & ~0x07) | 1; + wrmsr(0xC0010062, msr); + + // Wait for P1 to set. + do { + msr = rdmsr(0xC0010063); + } while (msr.lo != 1); + + msr.lo = msr.lo & ~0x07; + wrmsr(0xC0010062, msr); + // Wait for P0 to set. + do { + msr = rdmsr(0xC0010063); + } while (msr.lo != 0); + + + fv.common_fid = (nb_cof_vid_update << 16) | (fid_max << 8) ; + print_debug_fv("BSP fid = ", fv.common_fid); + +#if FAM10_SET_FIDVID_STORE_AP_APICID_AT_FIRST == 1 && FAM10_SET_FIDVID_CORE0_ONLY == 0 + /* For all APs (We know the APIC ID of all APs even when the APIC ID + is lifted) remote read from AP LAPIC_MSG_REG about max fid. + Then calculate the common max fid that can be used for all + APs and BSP */ + ap_apicidx.num = 0; + + for_each_ap(bsp_apicid, FAM10_SET_FIDVID_CORE_RANGE, store_ap_apicid, &ap_apicidx); + + for(i = 0; i < ap_apicidx.num; i++) { + init_fidvid_bsp_stage1(ap_apicidx.apicid[i], &fv); + } +#else + for_each_ap(bsp_apicid, FAM10_SET_FIDVID_CORE0_ONLY, init_fidvid_bsp_stage1, &fv); +#endif + + print_debug_fv("common_fid = ", fv.common_fid); + + if (fv.common_fid & ~(0xFF << 16)) { // check nb_cof_vid_update + + // Enable the common fid and other settings. + enable_fid_change((fv.common_fid >> 8) & 0x1F); + + // nbfid change need warm reset, so reset at first + return 1; + } + + return 0; // No FID/VID changes. Don't reset +} +static void set_p0(void) +{ + msr_t msr; + + // Transition P0 for calling core. + msr = rdmsr(0xC0010062); + msr.lo = (msr.lo & ~0x07); + wrmsr(0xC0010062, msr); + + // Don't bother to wait around for the P state to change. +} +#endif diff --git a/src/cpu/amd/model_10xxx/fidvid_common.c b/src/cpu/amd/model_10xxx/fidvid_common.c new file mode 100644 index 0000000000..c863334eda --- /dev/null +++ b/src/cpu/amd/model_10xxx/fidvid_common.c @@ -0,0 +1,312 @@ +/* + * This file is part of the LinuxBIOS project. + * + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include + + +static u32 get_vstime(u32 nodeid, u32 slam) +{ + u32 val; + u32 v; + device_t dev; + +#if defined(__ROMCC__) + dev = NODE_PCI(nodeid, 3); +#else + dev = get_node_pci(nodeid, 3); +#endif + + val = pci_read_config32(dev, 0xd8); + + val >>= slam?0:4; + val &= 7; + + switch (val) { + case 4: v = 60; break; + case 5: v = 100; break; + case 6: v = 200; break; + case 7: v = 500; break; + default: + v = (val+1)*10; // in us + } + + return v; +} + +static void udelay_tsc(u32 us) +{ + /* Use TSC to delay because it is fixed, ie. it will not changed with p-states. + * Also, We use the APIC TIMER register is to hold flags for AP init. + */ + u32 dword; + tsc_t tsc, tsc1, tscd; + u32 d = 0x00000200; //800Mhz or 200Mhz or 1.6G or get the NBFID at first + u32 dn = 0x1000000/2; // howmany us need to use hi + + tscd.hi = us/dn; + tscd.lo = (us - tscd.hi * dn) * d; + + tsc1 = rdtsc(); + dword = tsc1.lo + tscd.lo; + if((dwordtsc1.hi) || ((tsc.hi==tsc1.hi) && (tsc.lo>tsc1.lo))); + +} + +#ifdef __ROMCC__ +void udelay(u32 usecs) +{ + udelay_tsc(usecs); +} +#endif + +static u32 set_vid(u32 newvid, u32 bit_offset, u32 nodeid, u32 coreid) +{ + u32 val; + msr_t msr; + u32 curvid; + u32 slam; + u32 delay; + u32 count = 3; + device_t dev; + + msr = rdmsr(0xc0010071);//status + curvid = (msr.lo >> bit_offset) & 0x7f; // seven bits + + if(newvid == curvid) return curvid; + +#if defined(__ROMCC__) + dev = NODE_PCI(nodeid, 3); +#else + dev = get_node_pci(nodeid, 3); +#endif + + val = pci_read_config32(dev, 0xa0); + + slam = (val >> 29) & 1; + delay = get_vstime(nodeid, slam); + + if(!slam) { + if(curvid>newvid) { + count = (curvid - newvid) * 2; + } else { + count = (newvid - curvid) * 2; + } + } + + while(count-->0) { + if(slam) { + curvid = newvid; + } + else { //ramp + if(curvid>newvid) { + curvid--; + } else { + curvid++; + } + } + + msr = rdmsr(0xc0010070); //control + msr.lo &= ~(0x7f<> bit_offset) & 0x7f; // seven bits + + if(curvid == newvid) break; + + } + + return curvid; +} + + +static u32 set_nb_vid(u32 newvid, u32 nodeid, u32 coreid) +{ + return set_vid(newvid, 25, nodeid, coreid); +} + + +static u32 set_core_vid(u32 newvid, u32 nodeid, u32 coreid) +{ + return set_vid(newvid, 9, nodeid, coreid); +} + + +static unsigned set_cof(u32 val, u32 mask, u32 nodeid, u32 coreid) +{ + msr_t msr; + int count = 3; + + val &= mask; + + // FIXME: What is count for? Why 3 times? What about node and core id? + while(count-- > 0) { + + msr = rdmsr(0xc0010071); + msr.lo &= mask; + if(msr.lo == val) break; + + msr = rdmsr(0xc0010070); + msr.lo &= ~(mask); + msr.lo |= val; + wrmsr(0xc0010070, msr); + } + + return msr.lo; +} + +static u32 set_core_cof(u32 fid, u32 did, u32 nodeid, u32 coreid) +{ + u32 val; + u32 mask; + + mask = (7<<6) | 0x3f; + val = ((did & 7)<<6) | (fid & 0x3f); + + return set_cof(val, mask, nodeid, coreid); + +} + + +static u32 set_nb_cof(u32 did, u32 nodeid, u32 coreid) // fid need warmreset +{ + u32 val; + u32 mask; + + mask = 1<<22; + val = (did & 1)<<22; + + return set_cof(val, mask, nodeid, coreid); + +} + + +/* set vid and cof for core and nb after warm reset is not started by BIOS */ +static void set_core_nb_max_pstate_after_other_warm_reset(u32 nodeid, u32 coreid) // P0 +{ + msr_t msr; + u32 val; + u32 vid; + u32 mask; + u32 did; + device_t dev; + + msr = rdmsr(0xc0010064); + +#if defined(__ROMCC__) + dev = NODE_PCI(nodeid, 3); +#else + dev = get_node_pci(nodeid, 3); +#endif + + val = pci_read_config32(dev, 0xa0); + if((val>>8) & 1) { // PVI + vid = (msr.lo >> 25) & 0x7f; + } else { //SVI + vid = (msr.lo >> 9) & 0x7f; + } + set_core_vid(vid, nodeid, coreid); + + mask = (0x7<<6) | 0x3f; + val = msr.lo & mask; + set_cof(val, mask, nodeid, coreid); + + //set nb cof and vid + did = (msr.lo >> 22) & 1; + vid = (msr.lo >> 25) & 0x7f; + if(did) { + set_nb_cof(did, nodeid, coreid); + set_nb_vid(vid, nodeid, coreid); + } else { + set_nb_vid(vid, nodeid, coreid); + set_nb_cof(did, nodeid, coreid); + } + + //set the p state + msr.hi = 0; + msr.lo = 0; + wrmsr(0xc0010062, msr); + +} + + +/* set vid and cof for core and nb after warm reset is not started by BIOS */ +static void set_core_nb_min_pstate_after_other_warm_reset(u32 nodeid, u32 coreid) // Px +{ + msr_t msr; + u32 val; + u32 vid; + u32 mask; + u32 did; + u32 pstate; + device_t dev; + +#if defined(__ROMCC__) + dev = NODE_PCI(nodeid, 3); +#else + dev = get_node_pci(nodeid, 3); +#endif + + + val = pci_read_config32(dev, 0xdc); //PstateMaxVal + + pstate = (val >> 8) & 0x7; + + msr = rdmsr(0xc0010064 + pstate); + + mask = (7<<6) | 0x3f; + val = msr.lo & mask; + set_cof(val, mask, nodeid, coreid); + + val = pci_read_config32(dev, 0xa0); + if((val>>8) & 1) { // PVI + vid = (msr.lo>>25) & 0x7f; + } else { //SVI + vid = (msr.lo>>9) & 0x7f; + } + set_core_vid(vid, nodeid, coreid); + + //set nb cof and vid + did = (msr.lo >> 22) & 1; + vid = (msr.lo >> 25) & 0x7f; + if(did) { + set_nb_cof(did, nodeid, coreid); + set_nb_vid(vid, nodeid, coreid); + } else { + set_nb_vid(vid, nodeid, coreid); + set_nb_cof(did, nodeid, coreid); + } + + //set the p state + msr.hi = 0; + msr.lo = pstate; + wrmsr(0xc0010062, msr); +} diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c new file mode 100644 index 0000000000..8a0513cf18 --- /dev/null +++ b/src/cpu/amd/model_10xxx/init_cpus.c @@ -0,0 +1,553 @@ +/* + * This file is part of the LinuxBIOS project. + * + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +//it takes the ENABLE_APIC_EXT_ID and APIC_ID_OFFSET and LIFT_BSP_APIC_ID +#ifndef FAM10_SET_FIDVID + #define FAM10_SET_FIDVID 1 +#endif + +#ifndef FAM10_SET_FIDVID_CORE0_ONLY + /* MSR FIDVID_CTL and FIDVID_STATUS are shared by cores, + Need to do every AP to set common FID/VID*/ + #define FAM10_SET_FIDVID_CORE0_ONLY 0 +#endif + + +static inline void print_initcpu8 (const char *strval, u8 val) +{ + printk_debug("%s%02x\n", strval, val); +} + +static inline void print_initcpu8_nocr (const char *strval, u8 val) +{ + printk_debug("%s%02x", strval, val); +} + + +static inline void print_initcpu16 (const char *strval, u16 val) +{ + printk_debug("%s%04x\n", strval, val); +} + + +static inline void print_initcpu(const char *strval, u32 val) +{ + printk_debug("%s%08x\n", strval, val); +} + + +static void prep_fid_change(void); +static void init_fidvid_stage2(u32 apicid, u32 nodeid); + +#if PCI_IO_CFG_EXT == 1 +static inline void set_EnableCf8ExtCfg(void) +{ + // set the NB_CFG[46]=1; + msr_t msr; + msr = rdmsr(NB_CFG_MSR); + // EnableCf8ExtCfg: We need that to access PCI_IO_CFG_EXT 4K range + msr.hi |= (1<<(46-32)); + wrmsr(NB_CFG_MSR, msr); +} +#else +static inline void set_EnableCf8ExtCfg(void) { } +#endif + + +/*[39:8] */ +#define PCI_MMIO_BASE 0xfe000000 +/* because we will use gs to store hi, so need to make sure lo can start + from 0, So PCI_MMIO_BASE & 0x00ffffff should be equal to 0*/ + +static inline void set_pci_mmio_conf_reg(void) +{ +#if MMCONF_SUPPORT + msr_t msr; + msr = rdmsr(0xc0010058); + msr.lo &= ~(0xfff00000 | (0xf << 2)); + // 256 bus per segment, MMIO reg will be 4G , enable MMIO Config space + msr.lo |= ((8+PCI_BUS_SEGN_BITS) << 2) | (1 << 0); + msr.hi &= ~(0x0000ffff); + msr.hi |= (PCI_MMIO_BASE >> (32-8)); + wrmsr(0xc0010058, msr); // MMIO Config Base Address Reg + + //mtrr for that range? +// set_var_mtrr_x(7, PCI_MMIO_BASE<<8, PCI_MMIO_BASE>>(32-8), 0x00000000, 0x01, MTRR_TYPE_UNCACHEABLE); + + set_wrap32dis(); + + msr.hi = (PCI_MMIO_BASE >> (32-8)); + msr.lo = 0; + wrmsr(0xc0000101, msr); //GS_Base Reg + + + +#endif +} + + +typedef void (*process_ap_t)(u32 apicid, void *gp); + +//core_range = 0 : all cores +//core range = 1 : core 0 only +//core range = 2 : cores other than core0 + +static void for_each_ap(u32 bsp_apicid, u32 core_range, + process_ap_t process_ap, void *gp) +{ + // here assume the OS don't change our apicid + u32 ap_apicid; + + u32 nodes; + u32 siblings; + u32 disable_siblings; + u32 cores_found; + u32 nb_cfg_54; + int i,j; + u32 ApicIdCoreIdSize; + + /* get_nodes define in ht_wrapper.c */ + nodes = get_nodes(); + + disable_siblings = !CONFIG_LOGICAL_CPUS; + +#if CONFIG_LOGICAL_CPUS == 1 + if(read_option(CMOS_VSTART_quad_core, CMOS_VLEN_quad_core, 0) != 0) { // 0 mean quad core + disable_siblings = 1; + } +#endif + + /* Assume that all node are same stepping, otherwise we can use use + nb_cfg_54 from bsp for all nodes */ + nb_cfg_54 = read_nb_cfg_54(); + + ApicIdCoreIdSize = (cpuid_ecx(0x80000008) >> 12 & 0xf); + if(ApicIdCoreIdSize) { + siblings = ((1 << ApicIdCoreIdSize) - 1); + } else { + siblings = 3; //quad core + } + + for (i = 0; i < nodes; i++) { + cores_found = get_core_num_in_bsp(i); + + u32 jstart, jend; + + if (core_range == 2) { + jstart = 1; + } else { + jstart = 0; + } + + if (disable_siblings || (core_range==1)) { + jend = 0; + } else { + jend = cores_found; + } + + + for (j = jstart; j <= jend; j++) { + ap_apicid = i * (nb_cfg_54 ? (siblings + 1):1) + j * (nb_cfg_54 ? 1:64); + + #if (ENABLE_APIC_EXT_ID == 1) && (APIC_ID_OFFSET > 0) + #if LIFT_BSP_APIC_ID == 0 + if( (i != 0) || (j != 0)) /* except bsp */ + #endif + ap_apicid += APIC_ID_OFFSET; + #endif + + if(ap_apicid == bsp_apicid) continue; + + process_ap(ap_apicid, gp); + + } + } +} + +/* FIXME: Duplicate of what is in lapic.h? */ +static inline int lapic_remote_read(int apicid, int reg, u32 *pvalue) +{ + int timeout; + u32 status; + int result; + lapic_wait_icr_idle(); + lapic_write(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(apicid)); + lapic_write(LAPIC_ICR, LAPIC_DM_REMRD | (reg >> 4)); + timeout = 0; + + do { + status = lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY; + } while (status == LAPIC_ICR_BUSY && timeout++ < 1000); + + timeout = 0; + do { + status = lapic_read(LAPIC_ICR) & LAPIC_ICR_RR_MASK; + } while (status == LAPIC_ICR_RR_INPROG && timeout++ < 1000); + + result = -1; + + if (status == LAPIC_ICR_RR_VALID) { + *pvalue = lapic_read(LAPIC_RRR); + result = 0; + } + return result; +} + + +/* Use the LAPIC timer count register to hold each cores init status */ +#define LAPIC_MSG_REG 0x380 + + +#if FAM10_SET_FIDVID == 1 +static void init_fidvid_ap(u32 bsp_apicid, u32 apicid, u32 nodeid, u32 coreid); +#endif + +static inline __attribute__((always_inline)) void print_apicid_nodeid_coreid(u32 apicid, struct node_core_id id, const char *str) +{ + printk_debug("%s --- { APICID = %02x NODEID = %02x COREID = %02x} ---\n", str, apicid, id.nodeid, id.coreid); +} + + +static unsigned wait_cpu_state(u32 apicid, u32 state) +{ + u32 readback = 0; + u32 timeout = 1; + int loop = 4000000; + while (--loop > 0) { + if (lapic_remote_read(apicid, LAPIC_MSG_REG, &readback) != 0) continue; + if ((readback & 0x3f) == state) { + timeout = 0; + break; //target cpu is in stage started + } + } + if (timeout) { + if (readback) { + timeout = readback; + } + } + + return timeout; +} + + +static void wait_ap_started(u32 ap_apicid, void *gp ) +{ + u32 timeout; + timeout = wait_cpu_state(ap_apicid, 0x13); // started + if(timeout) { + print_initcpu8_nocr("* AP ", ap_apicid); + print_initcpu(" didn't start timeout:", timeout); + } + else { + print_initcpu8_nocr("AP started: ", ap_apicid); + } +} + + +static void wait_all_aps_started(u32 bsp_apicid) +{ + for_each_ap(bsp_apicid, 0 , wait_ap_started, (void *)0); +} + + +static void wait_all_other_cores_started(u32 bsp_apicid) +{ + // all aps other than core0 + print_debug("started ap apicid: "); + for_each_ap(bsp_apicid, 2 , wait_ap_started, (void *)0); + print_debug("\n"); +} + + +static void allow_all_aps_stop(u32 bsp_apicid) +{ + /* Called by the BSP to indicate AP can stop */ + + /* FIXME Do APs use this? + Looks like wait_till_sysinfo_in_ram is used instead. */ + + // allow aps to stop use 6 bits for state + lapic_write(LAPIC_MSG_REG, (bsp_apicid << 24) | 0x14); +} + + +static void STOP_CAR_AND_CPU() +{ + disable_cache_as_ram(); // inline + stop_this_cpu(); +} + + +#ifndef MEM_TRAIN_SEQ +#define MEM_TRAIN_SEQ 0 +#endif + +#if RAMINIT_SYSINFO == 1 +static u32 init_cpus(u32 cpu_init_detectedx ,struct sys_info *sysinfo) +#else +static u32 init_cpus(u32 cpu_init_detectedx) +#endif +{ + u32 bsp_apicid = 0; + u32 apicid; + struct node_core_id id; + + /* + * already set early mtrr in cache_as_ram.inc + */ + + /* enable access pci conf via mmio*/ + set_pci_mmio_conf_reg(); + + /* that is from initial apicid, we need nodeid and coreid + later */ + id = get_node_core_id_x(); + + /* NB_CFG MSR is shared between cores, so we need make sure + core0 is done at first --- use wait_all_core0_started */ + if(id.coreid == 0) { + set_apicid_cpuid_lo(); /* only set it on core0 */ + set_EnableCf8ExtCfg(); /* only set it on core0 */ + #if (ENABLE_APIC_EXT_ID == 1) + enable_apic_ext_id(id.nodeid); + #endif + } + + enable_lapic(); + + +#if (ENABLE_APIC_EXT_ID == 1) && (APIC_ID_OFFSET > 0) + u32 initial_apicid = get_initial_apicid(); + + #if LIFT_BSP_APIC_ID == 0 + if( initial_apicid != 0 ) // other than bsp + #endif + { + /* use initial apic id to lift it */ + u32 dword = lapic_read(LAPIC_ID); + dword &= ~(0xff << 24); + dword |= (((initial_apicid + APIC_ID_OFFSET) & 0xff) << 24); + + lapic_write(LAPIC_ID, dword); + } + + #if LIFT_BSP_APIC_ID == 1 + bsp_apicid += APIC_ID_OFFSET; + #endif + +#endif + + /* get the apicid, it may be lifted already */ + apicid = lapicid(); + + // show our apicid, nodeid, and coreid + if( id.coreid==0 ) { + if (id.nodeid!=0) //all core0 except bsp + print_apicid_nodeid_coreid(apicid, id, " core0: "); + } + else { //all other cores + print_apicid_nodeid_coreid(apicid, id, " corex: "); + } + + + if (cpu_init_detectedx) { + print_apicid_nodeid_coreid(apicid, id, "\n\n\nINIT detected from "); + print_debug("\nIssuing SOFT_RESET...\n"); + soft_reset(); + } + + if(id.coreid == 0) { + if(!(warm_reset_detect(id.nodeid))) //FIXME: INIT is checked above but check for more resets? + distinguish_cpu_resets(id.nodeid); // Also indicates we are started + } + + // Mark the core as started. + lapic_write(LAPIC_MSG_REG, (apicid << 24) | 0x13); + + + if(apicid != bsp_apicid) { +#if FAM10_SET_FIDVID == 1 + #if (CONFIG_LOGICAL_CPUS == 1) && (FAM10_SET_FIDVID_CORE0_ONLY == 1) + // Run on all AP for proper FID/VID setup. + if(id.coreid == 0 ) // only need set fid for core0 + #endif + { + // check warm(bios) reset to call stage2 otherwise do stage1 + if (warm_reset_detect(id.nodeid)) { + printk_debug("init_fidvid_stage2 apicid: %02x\n", apicid); + init_fidvid_stage2(apicid, id.nodeid); + } else { + printk_debug("init_fidvid_ap(stage1) apicid: %02x\n", apicid); + init_fidvid_ap(bsp_apicid, apicid, id.nodeid, id.coreid); + } + } +#endif + + /* AP is ready, Wait for the BSP to get memory configured */ + /* FIXME: many cores spinning on node0 pci register seems to be bad. + * Why do we need to wait? These APs are just going to go sit in a hlt. + */ + //wait_till_sysinfo_in_ram(); + + set_init_ram_access(); + + STOP_CAR_AND_CPU(); + printk_debug("\nAP %02x should be halted but you are reading this....\n", apicid); + } + + return bsp_apicid; +} + + +static u32 is_core0_started(u32 nodeid) +{ + u32 htic; + device_t device; + device = NODE_PCI(nodeid, 0); + htic = pci_read_config32(device, HT_INIT_CONTROL); + htic &= HTIC_ColdR_Detect; + return htic; +} + + +static void wait_all_core0_started(void) +{ + /* When core0 is started, it will distingush_cpu_resets + . So wait for that to finish */ + u32 i; + u32 nodes = get_nodes(); + + printk_debug("Wait all core0s started \n"); + for(i=1;i 1 +/** + * void start_node(u32 node) + * + * start the core0 in node, so it can generate HT packet to feature code. + * + * This function starts the AP nodes core0s. wait_all_core0_started() in + * cache_as_ram_auto.c waits for all the AP to be finished before continuing + * system init. + */ +static void start_node(u8 node) +{ + u32 val; + + /* Enable routing table */ + printk_debug("Start node %02x", node); + +#if CAR_FAM10 == 1 + /* For CAR_FAM10 support, we need to set Dram base/limit for the new node */ + pci_write_config32(NODE_MP(node), 0x44, 0); + pci_write_config32(NODE_MP(node), 0x40, 3); +#endif + + /* Allow APs to make requests (ROM fetch) */ + val=pci_read_config32(NODE_HT(node), 0x6c); + val &= ~(1 << 1); + pci_write_config32(NODE_HT(node), 0x6c, val); + + printk_debug(" done.\n"); +} + + +/** + * static void setup_remote_node(u32 node) + * + * Copy the BSP Adress Map to each AP. + */ +static void setup_remote_node(u8 node) +{ + /* There registers can be used with F1x114_x Address Map at the + same time, So must set them even 32 node */ + static const u16 pci_reg[] = { + /* DRAM Base/Limits Registers */ + 0x44, 0x4c, 0x54, 0x5c, 0x64, 0x6c, 0x74, 0x7c, + 0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78, + 0x144, 0x14c, 0x154, 0x15c, 0x164, 0x16c, 0x174, 0x17c, + 0x140, 0x148, 0x150, 0x158, 0x160, 0x168, 0x170, 0x178, + /* MMIO Base/Limits Registers */ + 0x84, 0x8c, 0x94, 0x9c, 0xa4, 0xac, 0xb4, 0xbc, + 0x80, 0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, + /* IO Base/Limits Registers */ + 0xc4, 0xcc, 0xd4, 0xdc, + 0xc0, 0xc8, 0xd0, 0xd8, + /* Configuration Map Registers */ + 0xe0, 0xe4, 0xe8, 0xec, + }; + u16 i; + + printk_debug("setup_remote_node: %02x", node); + + /* copy the default resource map from node 0 */ + for(i = 0; i < sizeof(pci_reg)/sizeof(pci_reg[0]); i++) { + u32 value; + u16 reg; + reg = pci_reg[i]; + value = pci_read_config32(NODE_MP(0), reg); + pci_write_config32(NODE_MP(node), reg, value); + + } + printk_debug(" done\n"); +} +#endif +/** + * finalize_node_setup() + * + * Do any additional post HT init + * + * This could really be moved to cache_as_ram_auto.c since it really isn't HT init. + */ +void finalize_node_setup(struct sys_info *sysinfo) +{ + u8 i; + u8 nodes = get_nodes(); + u32 reg; + +#if RAMINIT_SYSINFO == 1 + /* read Node0 F0_0x64 bit [8:10] to find out SbLink # */ + reg = pci_read_config32(NODE_HT(0), 0x64); + sysinfo->sblk = (reg>>8) & 7; + sysinfo->sbbusn = 0; + sysinfo->nodes = nodes; + sysinfo->sbdn = get_sbdn(sysinfo->sbbusn); +#endif + + setup_link_trans_cntrl(); + +#if FAM10_SET_FIDVID == 1 + // Prep each node for FID/VID setup. + prep_fid_change(); +#endif + +#if CONFIG_MAX_PHYSICAL_CPUS > 1 + /* Skip the BSP, start at node 1 */ + for(i=1; i +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../../northbridge/amd/amdfam10/amdfam10.h" + +#include +#include +#include +#include +#include + +#include + +#include + +extern void prep_pstates_all(void); +extern void init_pstates(device_t dev, u32 nodeid, u32 coreid); +extern device_t get_node_pci(u32 nodeid, u32 fn); + + + +void cpus_ready_for_init(void) +{ + prep_pstates_all(); +#if MEM_TRAIN_SEQ == 1 + struct sys_info *sysinfox = (struct sys_info *)((CONFIG_LB_MEM_TOPK<<10) - DCACHE_RAM_GLOBAL_VAR_SIZE); + // wait for ap memory to trained + wait_all_core0_mem_trained(sysinfox); +#endif +} + + +#define MCI_STATUS 0x401 + + +static inline msr_t rdmsr_amd(u32 index) +{ + msr_t result; + __asm__ __volatile__ ( + "rdmsr" + : "=a" (result.lo), "=d" (result.hi) + : "c" (index), "D" (0x9c5a203a) + ); + return result; +} + + +static inline void wrmsr_amd(u32 index, msr_t msr) +{ + __asm__ __volatile__ ( + "wrmsr" + : /* No outputs */ + : "c" (index), "a" (msr.lo), "d" (msr.hi), "D" (0x9c5a203a) + ); +} + + +#define MTRR_COUNT 8 +#define ZERO_CHUNK_KB 0x800UL /* 2M */ +#define TOLM_KB 0x400000UL + +struct mtrr { + msr_t base; + msr_t mask; +}; + + +struct mtrr_state { + struct mtrr mtrrs[MTRR_COUNT]; + msr_t top_mem, top_mem2; + msr_t def_type; +}; + + +static void save_mtrr_state(struct mtrr_state *state) +{ + int i; + for(i = 0; i < MTRR_COUNT; i++) { + state->mtrrs[i].base = rdmsr(MTRRphysBase_MSR(i)); + state->mtrrs[i].mask = rdmsr(MTRRphysMask_MSR(i)); + } + state->top_mem = rdmsr(TOP_MEM); + state->top_mem2 = rdmsr(TOP_MEM2); + state->def_type = rdmsr(MTRRdefType_MSR); +} + + +static void restore_mtrr_state(struct mtrr_state *state) +{ + int i; + disable_cache(); + + for(i = 0; i < MTRR_COUNT; i++) { + wrmsr(MTRRphysBase_MSR(i), state->mtrrs[i].base); + wrmsr(MTRRphysMask_MSR(i), state->mtrrs[i].mask); + } + wrmsr(TOP_MEM, state->top_mem); + wrmsr(TOP_MEM2, state->top_mem2); + wrmsr(MTRRdefType_MSR, state->def_type); + + enable_cache(); +} + + +#if 0 +static void print_mtrr_state(struct mtrr_state *state) +{ + int i; + for(i = 0; i < MTRR_COUNT; i++) { + printk_debug("var mtrr %d: %08x%08x mask: %08x%08x\n", + i, + state->mtrrs[i].base.hi, state->mtrrs[i].base.lo, + state->mtrrs[i].mask.hi, state->mtrrs[i].mask.lo); + } + printk_debug("top_mem: %08x%08x\n", + state->top_mem.hi, state->top_mem.lo); + printk_debug("top_mem2: %08x%08x\n", + state->top_mem2.hi, state->top_mem2.lo); + printk_debug("def_type: %08x%08x\n", + state->def_type.hi, state->def_type.lo); +} +#endif + + +static void set_init_ecc_mtrrs(void) +{ + msr_t msr; + int i; + disable_cache(); + + /* First clear all of the msrs to be safe */ + for(i = 0; i < MTRR_COUNT; i++) { + msr_t zero; + zero.lo = zero.hi = 0; + wrmsr(MTRRphysBase_MSR(i), zero); + wrmsr(MTRRphysMask_MSR(i), zero); + } + + /* Write back cache the first 1MB */ + msr.hi = 0x00000000; + msr.lo = 0x00000000 | MTRR_TYPE_WRBACK; + wrmsr(MTRRphysBase_MSR(0), msr); + msr.hi = 0x000000ff; + msr.lo = ~((CONFIG_LB_MEM_TOPK << 10) - 1) | 0x800; + wrmsr(MTRRphysMask_MSR(0), msr); + + /* Set the default type to write combining */ + msr.hi = 0x00000000; + msr.lo = 0xc00 | MTRR_TYPE_WRCOMB; + wrmsr(MTRRdefType_MSR, msr); + + /* Set TOP_MEM to 4G */ + msr.hi = 0x00000001; + msr.lo = 0x00000000; + wrmsr(TOP_MEM, msr); + + enable_cache(); +} + + +static inline void clear_2M_ram(unsigned long basek, struct mtrr_state *mtrr_state) +{ + unsigned long limitk; + unsigned long size; + void *addr; + + /* Report every 64M */ + if ((basek % (64*1024)) == 0) { + + /* Restore the normal state */ + map_2M_page(0); + restore_mtrr_state(mtrr_state); + enable_lapic(); + + /* Print a status message */ + printk_debug("%c", (basek >= TOLM_KB)?'+':'-'); + + /* Return to the initialization state */ + set_init_ecc_mtrrs(); + disable_lapic(); + + } + + limitk = (basek + ZERO_CHUNK_KB) & ~(ZERO_CHUNK_KB - 1); + + size = (limitk - basek) << 10; + addr = map_2M_page(basek >> 11); + if (addr == MAPPING_ERROR) { + printk_err("Cannot map page: %x\n", basek >> 11); + return; + } + + /* clear memory 2M (limitk - basek) */ + addr = (void *)(((u32)addr) | ((basek & 0x7ff) << 10)); + clear_memory(addr, size); +} + + +static void init_ecc_memory(u32 node_id) +{ + unsigned long startk, begink, endk; + unsigned long hole_startk = 0; + unsigned long basek; + struct mtrr_state mtrr_state; + + device_t f1_dev, f2_dev, f3_dev; + int enable_scrubbing; + u32 dcl; + + f1_dev = get_node_pci(node_id, 1); + + if (!f1_dev) { + die("Cannot find cpu function 1\n"); + } + f2_dev = get_node_pci(node_id, 2); + if (!f2_dev) { + die("Cannot find cpu function 2\n"); + } + f3_dev = get_node_pci(node_id, 3); + if (!f3_dev) { + die("Cannot find cpu function 3\n"); + } + + /* See if we scrubbing should be enabled */ + enable_scrubbing = 1; + get_option(&enable_scrubbing, "hw_scrubber"); + + /* Enable cache scrubbing at the lowest possible rate */ + if (enable_scrubbing) { + pci_write_config32(f3_dev, DRAM_SCRUB_RATE_CTRL, + (SCRUB_84ms << 16) | (SCRUB_84ms << 8) | (SCRUB_NONE << 0)); + } else { + pci_write_config32(f3_dev, DRAM_SCRUB_RATE_CTRL, + (SCRUB_NONE << 16) | (SCRUB_NONE << 8) | (SCRUB_NONE << 0)); + printk_debug("Scrubbing Disabled\n"); + } + + + /* If ecc support is not enabled don't touch memory */ + dcl = pci_read_config32(f2_dev, DRAM_CONFIG_LOW); + if (!(dcl & DCL_DimmEccEn)) { + printk_debug("ECC Disabled\n"); + return; + } + + startk = (pci_read_config32(f1_dev, 0x40 + (node_id*8)) & 0xffff0000) >> 2; + endk = ((pci_read_config32(f1_dev, 0x44 + (node_id*8)) & 0xffff0000) >> 2) + 0x4000; + +#if HW_MEM_HOLE_SIZEK != 0 + u32 val; + val = pci_read_config32(f1_dev, 0xf0); + if(val & 1) { + hole_startk = ((val & (0xff<<24)) >> 10); + } +#endif + + + /* Don't start too early */ + begink = startk; + if (begink < CONFIG_LB_MEM_TOPK) { + begink = CONFIG_LB_MEM_TOPK; + } + + printk_debug("Clearing memory %uK - %uK: ", begink, endk); + + /* Save the normal state */ + save_mtrr_state(&mtrr_state); + + /* Switch to the init ecc state */ + set_init_ecc_mtrrs(); + disable_lapic(); + + /* Walk through 2M chunks and zero them */ +#if HW_MEM_HOLE_SIZEK != 0 + /* here hole_startk can not be equal to begink, never. Also hole_startk is in 2M boundary, 64M? */ + if ( (hole_startk != 0) && ((begink < hole_startk) && (endk>(4*1024*1024)))) { + for(basek = begink; basek < hole_startk; + basek = ((basek + ZERO_CHUNK_KB) & ~(ZERO_CHUNK_KB - 1))) + { + clear_2M_ram(basek, &mtrr_state); + } + for(basek = 4*1024*1024; basek < endk; + basek = ((basek + ZERO_CHUNK_KB) & ~(ZERO_CHUNK_KB - 1))) + { + clear_2M_ram(basek, &mtrr_state); + } + } else +#endif + for(basek = begink; basek < endk; + basek = ((basek + ZERO_CHUNK_KB) & ~(ZERO_CHUNK_KB - 1))) { + clear_2M_ram(basek, &mtrr_state); + } + + + /* Restore the normal state */ + map_2M_page(0); + restore_mtrr_state(&mtrr_state); + enable_lapic(); + + /* Set the scrub base address registers */ + pci_write_config32(f3_dev, DRAM_SCRUB_ADDR_LOW, startk << 10); + pci_write_config32(f3_dev, DRAM_SCRUB_ADDR_HIGH, startk >> 22); + + /* Enable the scrubber? */ + if (enable_scrubbing) { + /* Enable scrubbing at the lowest possible rate */ + pci_write_config32(f3_dev, DRAM_SCRUB_RATE_CTRL, + (SCRUB_84ms << 16) | (SCRUB_84ms << 8) | (SCRUB_84ms << 0)); + } + + printk_debug(" done\n"); +} + + +static inline void fam10_errata(void) +{ + msr_t msr; + /* FIXME: Is doing errata here too late? */ + + /* 298 : FIXME: Fixed in B3/C1 */ +/* msr = rdmsr(0xC0010015); + msr.lo |= 1 << 3; + wrmsr(0xC0010015, msr); + + msr = rdmsr(0xC0010023); + msr.lo |= 1 << 1; + wrmsr(0xC0010023, msr); +*/ +} + +static void smash1Gpages(void) +{ + msr_t msr; + + /* 1G pages are smashed and installed in the TLB as 2M pages. + BIOS must set this bit for revision B. */ + /* FIXME: What about RevC? */ + + msr = rdmsr(0xC001102A); + msr.lo |= 1 << 29; + wrmsr(0xC001102A, msr); + +} + +extern void update_microcode(u32 cpu_deviceid); + + +void model_10xxx_init(device_t dev) +{ + unsigned long i; + msr_t msr; + struct node_core_id id; +#if CONFIG_LOGICAL_CPUS == 1 + unsigned siblings; +#endif + + /* Turn on caching if we haven't already */ + x86_enable_cache(); + amd_setup_mtrrs(); + x86_mtrr_check(); + + + /* Update the microcode */ + update_microcode(dev->device); + + disable_cache(); + + /* zero the machine check error status registers */ + msr.lo = 0; + msr.hi = 0; + for(i=0; i < 5; i++) { + wrmsr(MCI_STATUS + (i * 4),msr); + } + + fam10_errata(); + + enable_cache(); + + /* Enable the local cpu apics */ + setup_lapic(); + +#if CONFIG_LOGICAL_CPUS == 1 + siblings = cpuid_ecx(0x80000008) & 0xff; + + if (siblings > 0) { + msr = rdmsr_amd(CPU_ID_FEATURES_MSR); + msr.lo |= 1 << 28; + wrmsr_amd(CPU_ID_FEATURES_MSR, msr); + + msr = rdmsr_amd(LOGICAL_CPUS_NUM_MSR); + msr.lo = (siblings+1)<<16; + wrmsr_amd(LOGICAL_CPUS_NUM_MSR, msr); + + msr = rdmsr_amd(CPU_ID_EXT_FEATURES_MSR); + msr.hi |= 1<<(33-32); + wrmsr_amd(CPU_ID_EXT_FEATURES_MSR, msr); + } + printk_debug("siblings = %02d, ", siblings); +#endif + + id = get_node_core_id(read_nb_cfg_54()); // pre e0 nb_cfg_54 can not be set + + printk_debug("nodeid = %02d, coreid = %02d\n", id.nodeid, id.coreid); + + init_pstates(dev, id.nodeid, id.coreid); // is it a good place? some cores are clearing their ram + + /* Is this a bad location? In particular can another node prefecth + * data from this node before we have initialized it? + */ + if (id.coreid == 0) init_ecc_memory(id.nodeid); // only do it for core 0 + +#if CONFIG_LOGICAL_CPUS==1 + /* Start up my cpu siblings */ +// if(id.coreid==0) amd_sibling_init(dev); // Don't need core1 is already be put in the CPU BUS in bus_cpu_scan +#endif + + smash1Gpages(); +} + +static struct device_operations cpu_dev_ops = { + .init = model_10xxx_init, +}; +static struct cpu_device_id cpu_table[] = { +//AMD_GH_SUPPORT + { X86_VENDOR_AMD, 0x100f00 }, /* SH-F0 L1 */ + { X86_VENDOR_AMD, 0x100f10 }, /* M2 */ + { X86_VENDOR_AMD, 0x100f20 }, /* S1g1 */ + { X86_VENDOR_AMD, 0x100f21 }, + { X86_VENDOR_AMD, 0x100f2A }, + { X86_VENDOR_AMD, 0x100f22 }, + { 0, 0 }, +}; +static struct cpu_driver model_10xxx __cpu_driver = { + .ops = &cpu_dev_ops, + .id_table = cpu_table, +}; diff --git a/src/cpu/amd/model_10xxx/pstate.c b/src/cpu/amd/model_10xxx/pstate.c new file mode 100644 index 0000000000..a3545a0d74 --- /dev/null +++ b/src/cpu/amd/model_10xxx/pstate.c @@ -0,0 +1,456 @@ +/* + * This file is part of the LinuxBIOS project. + * + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../../northbridge/amd/amdfam10/amdfam10.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include + +extern device_t get_node_pci(u32 nodeid, u32 fn); + +#include "fidvid_common.c" + +#define PSTATES_DEBUG 0 + + + +static void inline dump_msr_pstates(u32 nodes) +{ +#if PSTATES_DEBUG==1 + int i, j; + for(j=0; j<5; j++) { + printk_debug("P%d:", j); + for(i=0;i>8)&3) { + case 0: times = 1000; break; + case 1: times = 100; break; + case 2: times = 10; break; + default: + //error + times = 1; + } + + return (val & 0xff) * times; + +} + + +static u32 get_powerstep(u32 val) +{ + u32 time; + if(val<4) {time = (4 - val)*100;} + else if(val<8) { time = (9+4-val)*10;} + else { time = (10+8-val) * 5; } + + return time; + +} + + +static u32 get_plllocktime(u32 val) +{ + u32 time; + switch(val) { + case 0: + case 1: + case 2: + case 3: + time = val+1; break; + case 4: time = 8; break; + case 5: time = 16; break; + default: + //erro2 + time = 1; + } + return time; +} + + +static void disable_pstate(u32 nodes, u32 *p) +{ + int i; + + for(i=0;i> 31); + if(!enable) { + disable_pstate(nodes, p); + return; + } + corecof_min = ((sysconf.msr_pstate[0*5+p[0]].lo & 0x3f) + 0x10)>>((sysconf.msr_pstate[0*5+p[0]].lo>>6) & 7); + pwrval_max = sysconf.msr_pstate[0*5+p[0]].hi & 0x3ff; + pwrvalue_max = get_pwrvalue(pwrval_max); + + for(i=1; i> 31); + if(!enable) { + disable_pstate(nodes, p); + return; + } + + u32 coredid = ((sysconf.msr_pstate[i*5+p[i]].lo>>6) & 7); + u32 corecof = ((sysconf.msr_pstate[i*5+p[i]].lo & 0x3f) + 0x10)>>coredid; + if(corecofpwrvalue_max) { + pwrvalue_max = pwrvalue; + pwrval_max = pwrval; + } + } + + for(i=0; i>6) & 7); + u32 corefid = (corecof_min<>7) & 0x3f; + for(j=0; j<5; j++) { + val = pci_read_config32(f4_dev[i], 0x1e0 + (j<<2)); + nbdid = ((val>>16) & 1); + sysconf.msr_pstate[i*5+j].lo = (val & 0xffff) | (nbdid<<22) | ((nbdid?nbvid1:nbvid0)<<25); + sysconf.msr_pstate[i*5+j].hi = (((val>>17) & 0x3ff) << (32-32)) | (((val>>27) & 1)<<(63-32)); + } + } + + dump_msr_pstates(nodes); + + sysconf.needs_update_pstate_msrs = 0; // normal case for all sockets are installed same conf CPU + + for(i=1; (i>28) & 7)); + if(p_htc[i] == 0) { + val |= 1<<28; + pci_write_config32(f3_dev[i], 0x64, val); + val = pci_read_config32(f3_dev[i], 0x68); //stc + val &= ~(7<<28); + val |= (1<<28); + pci_write_config32(f3_dev[i], 0x68, val); + + p_htc[i] = 1; + } + } + if(htc_cap) { + match_pstate(nodes, p_htc); + + dump_p("P_htc\n", nodes, p_htc); + dump_msr_pstates(nodes); + } + + //p_lowest + for(i=0;i((val>>8) & 7)) { + val &= ~(7<<8); + val |= (p_lowest[i])<<8; + pci_write_config32(f3_dev[i], 0xdc, val); + } + else { + p_lowest[i] = (val>>8) & 7; + } + } + if(htc_cap) { + for(i=0;ihi & (1<<(63-32)) )) continue; + if((msr_pstate->lo & 0x3f) != corefid) { + corefid_equal = 0; + break; + } + } + for(j=0; j<5; j++) { + struct p_state_t *p_state; + msr_t *msr_pstate; + msr_pstate = &sysconf.msr_pstate[i*5+j]; + if(!(msr_pstate->hi & (1<<(63-32)) )) continue; + p_state = &sysconf.p_state[i*5+sysconf.p_state_num]; + u32 coredid = ((msr_pstate->lo>>6) & 7); + u32 corecof = ((msr_pstate->lo & 0x3f) + 0x10)>>coredid; + p_state->corefreq = corecof; + + u32 pwrval, pwrvalue; + pwrval = msr_pstate->hi & 0x3ff; + pwrvalue = get_pwrvalue(pwrval); + p_state->power = pwrvalue; + + u32 lat; + val = pci_read_config32(f3_dev[i], 0xd4); + lat = 15 * (get_powerstep((val>>24)& 0xf)+get_powerstep((val>>20)& 0xf)) /1000; + if(!corefid_equal) { + val = pci_read_config32(f3_dev[i], 0xa0); + lat += get_plllocktime((val >> 11 ) & 7); + } + p_state->transition_lat = lat; + p_state->busmaster_lat = lat; + + p_state->control = j; + p_state->status = j; + + sysconf.p_state_num++; + } + // don't need look at other nodes + if(!sysconf.p_state_num) break; + } +} + + +//it will update pstates info from ram into MSR +void init_pstates(device_t dev, u32 nodeid, u32 coreid) +{ + int j; + msr_t msr; + + if(sysconf.needs_update_pstate_msrs) { + for(j=0; j < 5; j++) { + wrmsr(0xC0010064 + j, sysconf.msr_pstate[nodeid * 5 + j]); + } + } + + /* Set TSC Freq Select: TSC increments at the rate of the core P-state 0 */ + msr = rdmsr(0xC0010015); + msr.lo |= 1 << 24; + wrmsr(0xC0010015, msr); + + // Enter the state P0 + //FIXME I don't think that this works correctly. May depend on early fid/vid setup. + if(sysconf.p_state_num) + set_core_nb_max_pstate_after_other_warm_reset(nodeid, coreid); + +} diff --git a/src/cpu/amd/model_10xxx/update_microcode.c b/src/cpu/amd/model_10xxx/update_microcode.c new file mode 100644 index 0000000000..285abfadf6 --- /dev/null +++ b/src/cpu/amd/model_10xxx/update_microcode.c @@ -0,0 +1,90 @@ +/* + * This file is part of the LinuxBIOS project. + * + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef __ROMCC__ +#include +#include +#include +#include + +#include +#endif + +static const u8 microcode_updates[] __attribute__ ((aligned(16))) = { + +#ifdef __ROMCC__ + + // Barcelona revAx +// #include "mc_patch_01000020.h" + + // Barcelona revBx + #include "mc_patch_01000033.h" + + // Barcelona rev Cx?? +// #include "mc_patch_01000035.h" + +#endif + /* Dummy terminator */ + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, +}; + +static u32 get_equivalent_processor_rev_id(u32 orig_id) { + static unsigned id_mapping_table[] = { + 0x100f00, 0x1000, + 0x100f01, 0x1000, + 0x100f02, 0x1000, + 0x100f20, 0x1020, + 0x100f21, 0x1020, + 0x100f2A, 0x1020, + 0x100f22, 0x1022, + }; + + u32 new_id; + int i; + + new_id = 0; + + for (i = 0; i < sizeof(id_mapping_table); i += 2 ) { + if(id_mapping_table[i]==orig_id) { + new_id = id_mapping_table[i + 1]; + break; + } + } + + return new_id; + +} + +void update_microcode(u32 cpu_deviceid) +{ + u32 equivalent_processor_rev_id; + + /* Update the microcode */ + equivalent_processor_rev_id = get_equivalent_processor_rev_id(cpu_deviceid ); + if (equivalent_processor_rev_id != 0) { + amd_update_microcode((void *) microcode_updates, equivalent_processor_rev_id); + } else { + printk_debug("microcode: rev id not found. Skipping microcode patch!\n"); + } + +} diff --git a/src/cpu/amd/quadcore/Config.lb b/src/cpu/amd/quadcore/Config.lb new file mode 100644 index 0000000000..d0dbaadd94 --- /dev/null +++ b/src/cpu/amd/quadcore/Config.lb @@ -0,0 +1,20 @@ +# +# This file is part of the LinuxBIOS project. +# +# Copyright (C) 2007 Advanced Micro Devices, Inc. +# +# 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. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# + +object amd_sibling.o diff --git a/src/cpu/amd/quadcore/amd_sibling.c b/src/cpu/amd/quadcore/amd_sibling.c new file mode 100644 index 0000000000..4ed770d981 --- /dev/null +++ b/src/cpu/amd/quadcore/amd_sibling.c @@ -0,0 +1,122 @@ +/* + * This file is part of the LinuxBIOS project. + * + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern device_t get_node_pci(u32 nodeid, u32 fn); + +#if 0 +static int first_time = 1; +#endif + +#include "quadcore_id.c" + +static u32 get_max_siblings(u32 nodes) +{ + device_t dev; + u32 nodeid; + u32 siblings=0; + + //get max siblings from all the nodes + for(nodeid=0; nodeid> 12) & 3; + if(siblings < j) { + siblings = j; + } + } + + return siblings; +} + + +static void enable_apic_ext_id(u32 nodes) +{ + device_t dev; + u32 nodeid; + + //enable APIC_EXIT_ID all the nodes + for(nodeid=0; nodeid 0) { // io apic could start from 0 + return 0; + } else if (sysconf.enabled_apic_ext_id) { // enabled ext id but bsp = 0 + return 1; + } + + nb_cfg_54 = read_nb_cfg_54(); + + + //contruct apicid_base + + if((!disable_siblings) && (siblings>0) ) { + /* for 8 way dual core, we will used up apicid 16:16, actualy + 16 is not allowed by current kernel and the kernel will try + to get one that is small than 16 to make io apic work. I don't + know when the kernel can support 256 apic id. + (APIC_EXT_ID is enabled) */ + + //4:10 for two way 8:12 for four way 16:16 for eight way + //Use CONFIG_MAX_PHYSICAL_CPUS instead of nodes for better consistency? + apicid_base = nb_cfg_54 ? (siblings+1) * sysconf.nodes : 8 * siblings + sysconf.nodes; + + } else { + apicid_base = sysconf.nodes; + } + + if((apicid_base+ioapic_num-1)>0xf) { + // We need to enable APIC EXT ID + printk_spew("if the IO APIC device doesn't support 256 apic id, \r\n you need to set ENABLE_APIC_EXT_ID in MB Option.lb so you can spare 16 id for ioapic\r\n"); + enable_apic_ext_id(sysconf.nodes); + } + + return apicid_base; +} diff --git a/src/cpu/amd/quadcore/quadcore.c b/src/cpu/amd/quadcore/quadcore.c new file mode 100644 index 0000000000..c08b2090ae --- /dev/null +++ b/src/cpu/amd/quadcore/quadcore.c @@ -0,0 +1,102 @@ +/* + * This file is part of the LinuxBIOS project. + * + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef SET_NB_CFG_54 + #define SET_NB_CFG_54 1 +#endif + +#include "cpu/amd/quadcore/quadcore_id.c" + +static inline u32 get_core_num_in_bsp(u32 nodeid) +{ + u32 dword; + dword = pci_read_config32(NODE_PCI(nodeid, 3), 0xe8); + dword >>= 12; + dword &= 3; + return dword; +} + +#if SET_NB_CFG_54 == 1 +static inline u8 set_apicid_cpuid_lo(void) +{ + // set the NB_CFG[54]=1; why the OS will be happy with that ??? + msr_t msr; + msr = rdmsr(NB_CFG_MSR); + msr.hi |= (1<<(54-32)); // InitApicIdCpuIdLo + wrmsr(NB_CFG_MSR, msr); + + return 1; +} +#else + +static inline void set_apicid_cpuid_lo(void) { } + +#endif + + +static inline void real_start_other_core(u32 nodeid, u32 cores) +{ + u32 dword; + + printk_debug("Start other core - nodeid: %02x cores: %02x\n", nodeid, cores); + + /* set PCI_DEV(0, 0x18+nodeid, 3), 0x44 bit 27 to redirect all MC4 + accesses and error logging to core0 */ + dword = pci_read_config32(NODE_PCI(nodeid, 3), 0x44); + dword |= 1 << 27; // NbMcaToMstCpuEn bit + pci_write_config32(NODE_PCI(nodeid, 3), 0x44, dword); + // set PCI_DEV(0, 0x18+nodeid, 0), 0x68 bit 5 to start core1 + dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x68); + dword |= 1 << 5; + pci_write_config32(NODE_PCI(nodeid, 0), 0x68, dword); + + if(cores > 1) { + dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x168); + dword |= (1 << 0); // core2 + if(cores > 2) { // core3 + dword |= (1 << 1); + } + pci_write_config32(NODE_PCI(nodeid, 0), 0x168, dword); + } +} + +//it is running on core0 of node0 +static inline void start_other_cores(void) +{ + u32 nodes; + u32 nodeid; + + // disable quad_core + if (read_option(CMOS_VSTART_quad_core, CMOS_VLEN_quad_core, 0) != 0) { + printk_debug("Skip additional core init\n"); + return; + } + + nodes = get_nodes(); + + for (nodeid = 0; nodeid < nodes; nodeid++) { + u32 cores = get_core_num_in_bsp(nodeid); + printk_debug("init node: %02x cores: %02x \n", nodeid, cores); + if (cores > 0) { + real_start_other_core(nodeid, cores); + } + } + +} diff --git a/src/cpu/amd/quadcore/quadcore_id.c b/src/cpu/amd/quadcore/quadcore_id.c new file mode 100644 index 0000000000..e556d699ab --- /dev/null +++ b/src/cpu/amd/quadcore/quadcore_id.c @@ -0,0 +1,79 @@ +/* + * This file is part of the LinuxBIOS project. + * + * Copyright (C) 2007 Advanced Micro Devices, Inc. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#include +#include +#ifdef __ROMCC__ +#include +#endif + +//called by bus_cpu_scan too +u32 read_nb_cfg_54(void) +{ + msr_t msr; + msr = rdmsr(NB_CFG_MSR); + return ( ( msr.hi >> (54-32)) & 1); +} + +static inline u32 get_initial_apicid(void) +{ + return ((cpuid_ebx(1) >> 24) & 0xff); +} + +//called by amd_siblings too +#define CORE_ID_BIT 2 +#define NODE_ID_BIT 6 +struct node_core_id get_node_core_id(u32 nb_cfg_54) +{ + struct node_core_id id; + u32 core_id_bits; + + u32 ApicIdCoreIdSize = (cpuid_ecx(0x80000008)>>12 & 0xf); + if(ApicIdCoreIdSize) { + core_id_bits = ApicIdCoreIdSize; + } else { + core_id_bits = CORE_ID_BIT; //quad core + } + + // get the apicid via cpuid(1) ebx[31:24] + if( nb_cfg_54) { + // when NB_CFG[54] is set, nodeid = ebx[31:26], coreid = ebx[25:24] + id.coreid = (cpuid_ebx(1) >> 24) & 0xff; + id.nodeid = (id.coreid>>core_id_bits); + id.coreid &= ((1<> 24) & 0xff; + id.coreid = (id.nodeid>>NODE_ID_BIT); + id.nodeid &= ((1< +#include "chip.h" + +struct chip_operations cpu_amd_socket_F_1207_ops = { + CHIP_NAME("socket F_1207") +}; -- cgit v1.2.3