blob: aa82323b97734246109d88cf8b887985cef0f88e [file] [log] [blame]
/*
* 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 <cpu/x86/tsc.h>
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((dword<tsc1.lo) || (dword<tscd.lo)) {
tsc1.hi++;
}
tsc1.lo = dword;
tsc1.hi+= tscd.hi;
do {
tsc = rdtsc();
} while ((tsc.hi>tsc1.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);
msr.lo |= (curvid<<bit_offset);
wrmsr(0xc0010070, msr); // how about all copys, APIC or PCI conf space?
udelay_tsc(delay);
msr = rdmsr(0xc0010071);//status
curvid = (msr.lo >> 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);
}