/* * 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 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); }