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

Reply via email to