Hi Rudolf,
On 25.12.2008 22:08, Rudolf Marek wrote:
> Hello,
>
> I just fixed the generator. Now It generates right P-state tables for
> revF of k8 CPUs. I just implemented correct TDP estimations.
The generator didn't compile until I added a cpuid() function.
> The program generates a ACPI ASL code which can be include in DSDT. It
> provides p-state info for powernow-k8 driver and windows driver.
I have attached the result of your code and what the proprietary BIOS
does and the fixed^Whacked-up genpowernow.c file.
The 1600 MHz step seems to be missing completely with the proprietary
BIOS. A few other differences exist as well. I'm not sure whether the
proprietary BIOS is right.
Regards,
Carl-Daniel
--
http://www.hailfinger.org/
/* (C) Rudolf Marek 2008
* GPL v2
* from kernel, and x86info (small snippets bellow)
*/
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
//index by numcore,pwrlmt
unsigned int tdp_am2[2][16] = { {0,0,0 ,0 ,35000,45000,0 ,0 ,62000,0 ,0,0,0 ,0,0,0},
{0,0,35000,45000,0 ,65000,76000,89000,0 ,103000,0,0,125000,0,0,0}};
unsigned tdp_s1g1[2][16] = {{0,0,0,0,0,0,25000,0,0,0,0,0,35000,0,0,0},
{0,0,0,0,0,0,0 ,0,0,0,0,0,35000,0,0,0}};
unsigned int vid_from_reg(uint8_t val)
{
/* fixme 6 bits */
val &= 0x1f;
return (val == 0x1f ? 0 : 1550 - val * 25);
}
uint8_t vid_to_reg(uint32_t vid)
{
return (1550 - vid) / 25;
}
//uint8_t fidcodes[32] = { 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10,
// 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22, 0x24, 0x26,
// 0x28, 0x2a
//};
//from kernel
/* Return a frequency in MHz, given an input fid */
static uint32_t fid_to_freq(uint32_t fid)
{
return 800 + (fid * 100);
}
uint8_t freq_to_fid(uint32_t freq)
{
// /* fix the odd fids */
// return fidcodes[(freq / 200) - 4];
return (freq - 800) / 100;
}
//from kernel
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
static inline void cpuid(int core, unsigned int op, unsigned long *eax, unsigned long *ebx, unsigned long *ecx, unsigned long *edx)
{
__asm__("cpuid":"=a"(*eax), "=b"(*ebx), "=d"(*edx)
: "0"(op)
: "ecx");
return;
}
static inline unsigned int cpuid_eax(unsigned int op, int core)
{
unsigned long eax, ebx, ecx, edx;
// printf("ecx %p edx %p\n", &ecx, &edx);
cpuid (core, op, &eax, &ebx, &ecx, &edx);
return eax;
/*
__asm__("cpuid":"=a"(eax)
: "0"(op)
: "bx", "cx", "dx");
return eax;*/
}
static inline unsigned int cpuid_ebx(unsigned int op, int core)
{
unsigned long eax, ebx, ecx, edx;
// unsigned int eax, ebx;
/*
__asm__("cpuid":"=a"(eax), "=b"(ebx)
: "0"(op)
: "cx", "dx");
return ebx;
*/
cpuid (core, op, &eax, &ebx, &ecx, &edx);
return ebx;
}
static inline unsigned int cpuid_edx(unsigned int op, int core)
{
unsigned long eax, ebx, ecx, edx;
// unsigned int eax, edx;
/*
__asm__("cpuid":"=a"(eax), "=d"(edx)
: "0"(op)
: "bx", "cx");
return edx;
*/
cpuid (core, op, &eax, &ebx, &ecx, &edx);
return edx;
}
/*
* $Id: rdmsr.c,v 1.16 2003/06/09 22:15:20 davej Exp $
* This file is part of x86info.
* (C) 2001 Dave Jones.
*
* Licensed under the terms of the GNU GPL License version 2.
*
* Contributions by Arjan van de Ven & Philipp Rumpf.
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int read_msr(int cpu, unsigned int idx, unsigned long long *val)
{
char cpuname[16];
unsigned char buffer[8];
unsigned long lo, hi;
int fh;
static int nodriver = 0;
if (nodriver == 1)
return 0;
sprintf(cpuname, "/dev/cpu/%d/msr", cpu);
fh = open(cpuname, O_RDONLY);
if (fh == -1) {
// if (!silent)
perror(cpuname);
nodriver = 1;
return (0);
}
lseek(fh, idx, SEEK_CUR);
if (fh != -1) {
if (read(fh, &buffer[0], 8) != 8) {
close(fh);
return (0);
}
lo = (*(unsigned long *) buffer);
hi = (*(unsigned long *) (buffer + 4));
*val = hi;
*val = (*val << 32) | lo;
}
close(fh);
return (1);
}
#include <stdio.h>
#define MAXP 8
struct pstate {
uint32_t freqMhz;
uint32_t voltage; /* milliVolt */
uint64_t tdp; /* miliWatt */
};
struct cpuentry {
uint32_t modelnr;
uint8_t brandID;
uint32_t cpuid;
uint8_t maxFID;
uint8_t startFID;
uint8_t maxVID;
uint8_t startVID;
struct pstate pstates[MAXP];
};
/* you will need to add your CPU here, it corresponds quite well the AMD table in the thermal datasheet */
struct cpuentry entr[] = {
{165, 0x2c, 0x20f32, 0xa, 0xa, 0x8, 0xa,
{{1800, 1300, 110000}, {1000, 1100, 51600}}},
{165, 0x2c, 0x20f32, 0xa, 0xa, 0x6, 0x8,
{{1800, 1350, 110000}, {1000, 1100, 51600}}},
{170, 0x2c, 0x20f32, 0xc, 0xc, 0x8, 0xa,
{{2000, 1300, 110000}, {1800, 1300, 105600}, {1000, 1100, 51400}}},
{170, 0x2c, 0x20f32, 0xc, 0xc, 0x6, 0x8,
{{2000, 1350, 110000}, {1800, 1300, 105600}, {1000, 1100, 51400}}},
{175, 0x2c, 0x20f32, 0xe, 0xe, 0x8, 0xa,
{{2200, 1300, 110000}, {2000, 1300, 105600}, {1800, 1250, 89100},
{1000, 1100, 49000}}},
{175, 0x2c, 0x20f32, 0xe, 0xe, 0x6, 0x8,
{{2200, 1350, 110000}, {2000, 1300, 105600}, {1800, 1250, 89100},
{1000, 1100, 49000}}},
{180, 0x2c, 0x20f32, 0x10, 0x10, 0x8, 0xa,
{{2400, 1300, 110000}, {2200, 1300, 105600}, {2000, 1250, 89100},
{1800, 1200, 74800}, {1000, 1100, 46600}}},
{180, 0x2c, 0x20f32, 0x10, 0x10, 0x6, 0x8,
{{2400, 1350, 110000}, {2200, 1300, 105600}, {2000, 1250, 89100},
{1800, 1200, 74800}, {1000, 1100, 46600}}},
{44, 0x26, 0x20fc2, 0xa, 0xa, 0x4, 0x6,
{{1800, 1400, 59000}, {1000, 1100, 21700}}},
};
void genpower(struct cpuentry *entr, int core)
{
uint32_t control;
int i = 0;
/* FIXME true multiprocessor systems needs rvo = 0 */
uint8_t rvo = 2; /* RVO offset voltage 50mV */
/* for nonrevF CPUs */
if (entr->modelnr != 0xf) {
/* IRT 80us, RVO, PLL_LOCK_TIME 2us, MVS 25mv, VST 100us */
control = (3 << 30) | (rvo << 28) | (2 << 20) | (0 << 18) | (5 << 11);
} else {
/* IRT 80us, RVO, PLL_LOCK_TIME 2us, MVS 25mv, VST 40us, extended interface = 1 */
control = (1 << 27) | (3 << 30) | (rvo << 28) | (2 << 20) | (0 << 18) | (2 << 11);
}
for (i = 0; (i < MAXP) && (entr->pstates[i].freqMhz != 0); i++) {
/* count */
}
printf("Scope (\\_PR.CPU%d)\n"
"{\n"
" Name (_PCT, Package (0x02)\n"
" {\n"
" ResourceTemplate ()\n"
" {\n"
" Register (FFixedHW, \n"
" 0x00, // Bit Width\n"
" 0x00, // Bit Offset\n"
" 0x0000000000000000, // Address\n"
" ,)\n"
" }, \n"
"\n"
" ResourceTemplate ()\n"
" {\n"
" Register (FFixedHW, \n"
" 0x00, // Bit Width\n"
" 0x00, // Bit Offset\n"
" 0x0000000000000000, // Address\n"
" ,)\n"
" }\n"
" })\n", core);
//30a 38a
printf(" Name (_PSS, Package (0x%02x)\n {\n", i);
for (i = 0; (i < MAXP) && (entr->pstates[i].freqMhz != 0); i++) {
uint32_t fidvid =
(vid_to_reg(entr->pstates[i].voltage) << 6) |
(freq_to_fid(entr->pstates[i].freqMhz) & 0x3f);
printf(" /* P#%d freq %d [MHz] voltage %f [V] TDP %llu [mW] */\n", i,
entr->pstates[i].freqMhz,
entr->pstates[i].voltage / 1000.0,
(unsigned long long) entr->pstates[i].tdp);
/* bus latencies are hardcoded (recommended) */
printf
(" Package (0x06)\n {\n 0x%08x,\n"
" 0x%08x,\n 0x00000064,\n 0x00000007,\n",
entr->pstates[i].freqMhz, entr->pstates[i].tdp);
printf(" 0x%08x,\n", control | fidvid );
printf(" 0x%08x\n },\n", fidvid);
};
printf(" })\n Method (_PPC, 0, NotSerialized)\n {\n Return (0x00)\n }\n}\n");
}
struct cpu_data {
uint8_t brandID;
uint32_t cpuid;
uint8_t maxFID;
uint8_t startFID;
uint8_t maxVID;
uint8_t startVID;
uint8_t pwrlmt;
int core;
unsigned long long msr_fidvid;
};
void get_cpuDATA(struct cpu_data *cpu)
{
uint32_t brand;
unsigned long long val = 0;
if (read_msr(0, 0xC0010042, &val) != 1) {
return;
}
// printf("FID_VID_STATUS %llx ", val);
cpu->msr_fidvid = val;
cpu->startFID = (val >> 8) & 0x3f;
cpu->maxFID = (val >> 16) & 0x3f;
cpu->startVID = (val >> 40) & 0x3f;
cpu->maxVID = (val >> 48) & 0x3f;
cpu->cpuid = cpuid_eax(0x80000001, cpu->core);
brand = cpuid_ebx(0x80000001, cpu->core);
cpu->brandID = (brand >> 6) & 0x3f;
if ((cpuid_edx(0x80000007, cpu->core) & 0x6) == 0x6) {
/* pwrlmt is brand[8:6,14] */
cpu->pwrlmt = (((brand >> 6) & 0x7) << 1) | ((brand >> 14) & 0x1);
} else {
cpu->pwrlmt = 0;
}
// printf
// ("BrandID %x cpuid %x startFID %x maxFID %x startVID %x maxVID %x pwrlmt %x\n",
// cpu->brandID, cpu->cpuid, cpu->startFID, cpu->maxFID,
// cpu->startVID, cpu->maxVID, cpu->pwrlmt);
}
/* units W * 10 */
unsigned int tdp(unsigned int pwrlmt, struct pstate *p,
uint32_t freqMhzMAX, uint32_t voltageMAX)
{
unsigned long long pwr;
/* fixme here need to follow Power = (PwrLmt * P[N] Frequency * (P[N] Voltage^2))/(P[0] Frequency* (P[0] Voltage^2)). */
//PWR lmt 5 P[N] freq 2000 P[N] Voltage 1250 P[0] freq 2000 P[0] voltage 1300, PWR 4
//PWR lmt 5 P[N] freq 1800 P[N] Voltage 1250 P[0] freq 2000 P[0] voltage 1300, PWR 4
//PWR lmt 5 P[N] freq 1000 P[N] Voltage 1100 P[0] freq 2000 P[0] voltage 1300, PWR 1
//500 is half of fixed point (1000x larger so we do not need float
pwr = (p->freqMhz * 1000 * (unsigned long long) (p->voltage * p->voltage)) + 500;
pwr/= ((voltageMAX * voltageMAX * freqMhzMAX));
//fixme number of cores in PCI space F3xE8[CmpCap]
//printf("PWR %llu\n", pwr);
//remove the fixed point
pwr = ((tdp_am2[0][pwrlmt]*pwr) + 500) / 1000;
// printf("PWR lmt %d P[N] freq %d P[N] Voltage %d P[0] freq %d P[0] voltage %d, PWR %llu mW\n",pwrlmt, p->freqMhz, p->voltage,
// freqMhzMAX, voltageMAX, pwr);
return pwr;
}
void gen_revF(int core)
{
struct cpuentry entr_revF;
struct cpu_data cpu;
int i = 0, j;
uint8_t pstep;
uint32_t startVoltage;
uint32_t lowFreqMhz;
if ((cpuid_eax(0x80000001, core) & 0xf0000) < 0x40000) {
/* no revF or later */
return;
}
cpu.core = core;
get_cpuDATA(&cpu);
/* algo cannot be used */
if (cpu.pwrlmt == 0)
return;
/* construct the entry */
entr_revF.brandID = cpu.brandID;
entr_revF.modelnr = 0xf; /* for getpower */
entr_revF.brandID = cpu.brandID;
entr_revF.cpuid = cpu.cpuid;
entr_revF.startFID = cpu.startFID;
entr_revF.startVID = cpu.startVID;
entr_revF.maxVID = cpu.maxVID;
entr_revF.maxFID = cpu.maxFID;
pstep = (cpu.msr_fidvid >> 56) & 1;
startVoltage = vid_from_reg(cpu.startVID);
/* first do the max */
entr_revF.pstates[0].voltage = vid_from_reg(cpu.maxVID + 2);
/* If MaxFID = 10_1010b and MaxVID != 00_0000b */
if ((entr_revF.maxFID == 0x2a) && (entr_revF.maxVID != 0)) {
entr_revF.pstates[0].freqMhz =
fid_to_freq(cpu.startFID + 0xa);
lowFreqMhz = fid_to_freq(0x2);
} else {
entr_revF.pstates[0].freqMhz = fid_to_freq(cpu.maxFID);
lowFreqMhz = fid_to_freq(cpu.startFID);
}
/* intermediate states */
if ((cpu.msr_fidvid >> 61) & 0x1) {
/* P1 first from max */
if (cpu.maxFID & 1) {
entr_revF.pstates[1].freqMhz =
fid_to_freq(cpu.maxFID - 1);
} else {
entr_revF.pstates[1].freqMhz =
fid_to_freq(cpu.maxFID - 2);
}
if (cpu.maxVID & 1) {
entr_revF.pstates[1].voltage =
vid_from_reg(cpu.maxVID + 1);
} else {
entr_revF.pstates[1].voltage =
vid_from_reg(cpu.maxVID + (1 << pstep));
}
i = 1;
do {
i++;
/* If P[N-1] VID + 2^PstateStep >= P[Min]: */
if ((entr_revF.pstates[i - 1].voltage -
(25 << pstep)) < startVoltage) {
entr_revF.pstates[i].voltage =
entr_revF.pstates[i - 1].voltage;
} else {
entr_revF.pstates[i].voltage =
entr_revF.pstates[i - 1].voltage -
(25 << pstep);
}
entr_revF.pstates[i].freqMhz = entr_revF.pstates[i - 1].freqMhz - 200; /* P[N-1] FID - 00_0010b */
} while ((entr_revF.pstates[i].voltage > startVoltage)
&& (entr_revF.pstates[i].freqMhz - 800 >
lowFreqMhz));
}
/* min P state */
entr_revF.pstates[i].voltage = startVoltage;
entr_revF.pstates[i].freqMhz = lowFreqMhz;
/* terminate the list */
entr_revF.pstates[i+1].voltage = 0;
entr_revF.pstates[i+1].freqMhz = 0;
/* calculate the TDP estimation */
for (j = 0; j <= i; j++) {
entr_revF.pstates[j].tdp =
tdp(cpu.pwrlmt, &entr_revF.pstates[j],
entr_revF.pstates[0].freqMhz,
entr_revF.pstates[0].voltage);
}
genpower(&entr_revF, core);
}
void gen_to_revE(int core)
{
struct cpu_data cpu;
int i;
cpu.core = core;
get_cpuDATA(&cpu);
for (i = 0; i < ARRAY_SIZE(entr); i++) {
if ((entr[i].cpuid == cpu.cpuid)
&& (entr[i].startFID == cpu.startFID)
&& (entr[i].maxFID == cpu.maxFID)
&& (entr[i].startVID == cpu.startVID)
&& (entr[i].maxVID == cpu.maxVID)) {
// printf("HIT %d\n", i);
genpower(&entr[i], core);
}
}
}
int main(void)
{
int cpus = sysconf (_SC_NPROCESSORS_ONLN);
int i;
for (i=0;i<cpus;i++) {
gen_to_revE(i);
gen_revF(i);
}
return 0;
}
Scope (\_PR.CPU0)
{
Name (_PCT, Package (0x02)
{
ResourceTemplate ()
{
Register (FFixedHW,
0x00, // Bit Width
0x00, // Bit Offset
0x0000000000000000, // Address
,)
},
ResourceTemplate ()
{
Register (FFixedHW,
0x00, // Bit Width
0x00, // Bit Offset
0x0000000000000000, // Address
,)
}
})
Name (_PSS, Package (0x03)
{
/* P#0 freq 1800 [MHz] voltage 1.250000 [V] TDP 62000 [mW] */
Package (0x06)
{
0x00000708,
0x0000f230,
0x00000064,
0x00000007,
0xe820130a,
0x0000030a
},
/* P#1 freq 1600 [MHz] voltage 1.250000 [V] TDP 55056 [mW] */
Package (0x06)
{
0x00000640,
0x0000d710,
0x00000064,
0x00000007,
0xe8201308,
0x00000308
},
/* P#2 freq 1000 [MHz] voltage 1.100000 [V] TDP 26660 [mW] */
Package (0x06)
{
0x000003e8,
0x00006824,
0x00000064,
0x00000007,
0xe8201482,
0x00000482
},
})
Method (_PPC, 0, NotSerialized)
{
Return (0x00)
}
}
/*
* Intel ACPI Component Architecture
* AML Disassembler version 20080213
*
* Disassembly of SSDT.dat, Wed Dec 24 02:46:17 2008
*
*
* Original Table Header:
* Signature "SSDT"
* Length 0x000000d3 (211)
* Revision 0x01
* Checksum 0x71
* OEM ID "PTLTD "
* OEM Table ID "POWERNOW"
* OEM Revision 0x00000001 (1)
* Compiler ID " LTP"
* Compiler Version 0x00000001 (1)
*/
DefinitionBlock ("SSDT.aml", "SSDT", 1, "PTLTD ", "POWERNOW", 0x00000001)
{
External (\_PR_.C000, DeviceObj)
Scope (\_PR.C000)
{
Name (_PCT, Package (0x02)
{
ResourceTemplate ()
{
Register (FFixedHW,
0x00, // Bit Width
0x00, // Bit Offset
0x0000000000000000, // Address
,)
},
ResourceTemplate ()
{
Register (FFixedHW,
0x00, // Bit Width
0x00, // Bit Offset
0x0000000000000000, // Address
,)
}
})
Name (_PSS, Package (0x02)
{
Package (0x06)
{
0x00000708,
0x0000f230,
0x00000064,
0x00000009,
0xe8202b0a,
0x0000030a
},
Package (0x06)
{
0x000003e8,
0x00006831,
0x00000064,
0x00000009,
0xe8202c82,
0x00000482
}
})
Name (_PPC, 0x00)
Name (_PSD, Package (0x01)
{
Package (0x05)
{
0x05,
0x00,
0x00000000,
0x000000fd,
0x00000001
}
})
}
}
--
coreboot mailing list: [email protected]
http://www.coreboot.org/mailman/listinfo/coreboot