/* * This file is part of the coreboot 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); }