Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 21/07/2009, à 19:01

coethium

Transformer son Athon XP desktop en Athlon XP mobile (Jaunty)

Préambule :

Tout d'abord, j'ai hésité entre la catégorie Trucs et Astuces ou Matériel, constatant que la cat Matériel traite plutôt de problèmes j'ai opté pour T&A.

Mal comprises ou mal maîtrisées, les manipulations indiquées dans ce post sont susceptibles d'endommager irrémédiablement votre ordinateur (plus précisément le processeur ou la carte mère). La responsabilité de leur mise en application vous incombe totalement.

Introduction :

Depuis le début de l'été j'étais assez chagriné de voir mon processeur Athlon XP 3000+ monter à 72-73°C, malgré un ventillo (de récup) en extraction sur la tour. Peu enclin à dépenser de l'argent pour un bon système de refroidissement et adepte du système D, je me suis lancé à la recherche d'information pour voir s'il n'était pas possible de modifier la fréquence de mon processeur afin qu'il chauffe moins.
Je vous livre ici la méthode qui (chez moi) a été un succès.

1/ délocker le processeur :

Je vais peu m'étendre sur ce sujet, bien que ce soit la partie la plus délicate. AMD a sorti plusieurs type de processeur Athlon XP. Ils diffèrent bien sûr en fréquence, mémoire cache, etc... mais aussi ils ont tous la possibilités de pouvoir modifier à chaud leur coefficient multiplicateur (et donc leur vitesse). Sur certain (les XP non M) cette possibilité est lockée, mais avec un peu de patience et de minutie il est possible de délocker cette fonctionnalité.

Vous trouverez de précieuses informations sur ce site :
http://fab51.com/cpu/barton/athlon-e24.html

Un bon anglais et une lecture attentive seront nécessaires. Dans mon cas l'astuce du graphite n'a pas fonctionné j'ai donc dû acheter de la colle conductrice (oui je sais autant acheter un ventilateur dans ce cas wink mais on perd en plaisir smile)
Selon votre CM et votre BIOS, une mauvaise manip peut griller le proc.

2/ Vérification :

Votre PC démarre c'est déjà une bonne nouvelle. Faire un

cat /proc/cpuinfo
processor	: 0
vendor_id	: AuthenticAMD
cpu family	: 6
model		: 10
model name	: Unknown CPU Typ
stepping	: 0
cpu MHz		: 2158
cache size	: 512 KB
fdiv_bug	: no
hlt_bug		: no
f00f_bug	: no
coma_bug	: no
fpu		: yes
fpu_exception	: yes
cpuid level	: 1
wp		: yes
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 mmx fxsr sse syscall mmxext 3dnowext 3dnow up
bogomips	: 4316
clflush size	: 32
power management: ts fid vid

On observe 2 changements depuis que le proc est délocké :
- Il ne reconnait plus le modèle de processeur (Unknown CPU type), on s'en fou.
- il y a deux nouveaux flags dans power managment : fid et vid ! ça c'est indispensable !

3/ Recompilation du noyau :

Lors du démarrage, powernow détecte bien que le processeur est capable de changer de fréquence, mais il ne trouve aucune table de fréquences (le BIOS de la CM n'en fournit pas puisque c'est un PC de bureau), il abandonne donc la possiblité d'utiliser powernow.

Cependant un dmesg nous donne un lien :
http://www.codemonkey.org.uk/projects/c … ow-k7.html

Et sur ce lien, on peut ensuite accéder à une série de patch qui permettent d'enter manuellement une table de fréquence au chargement du module powernow-k7. Sauf que sur Jaunty, powernow-k7 n'est plus en module mais dans le noyau.

a/ récupérer les sources du noyau.
b/ remplacer ./arch/x86/kernel/cpu/cpufreq/powernow-k7.c par :

/*
 *  AMD K7 Powernow driver.
 *  (C) 2003 Dave Jones on behalf of SuSE Labs.
 *  (C) 2003-2004 Dave Jones <davej@redhat.com>
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 *  Based upon datasheets & sample CPUs kindly provided by AMD.
 *
 * Errata 5: Processor may fail to execute a FID/VID change in presence of interrupt.
 * - We cli/sti on stepping A0 CPUs around the FID/VID transition.
 * Errata 15: Processors with half frequency multipliers may hang upon wakeup from disconnect.
 * - We disable half multipliers if ACPI is used on A0 stepping CPUs.
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/dmi.h>

#include <asm/msr.h>
#include <asm/timer.h>
#include <asm/timex.h>
#include <asm/io.h>
#include <asm/system.h>

#ifdef CONFIG_X86_POWERNOW_K7_ACPI
#include <linux/acpi.h>
#include <acpi/processor.h>
#endif

#include "powernow-k7.h"

#define PFX "powernow: "


struct psb_s {
	u8 signature[10];
	u8 tableversion;
	u8 flags;
	u16 settlingtime;
	u8 reserved1;
	u8 numpst;
};

struct pst_s {
	u32 cpuid;
	u8 fsbspeed;
	u8 maxfid;
	u8 startvid;
	u8 numpstates;
};

#ifdef CONFIG_X86_POWERNOW_K7_ACPI
union powernow_acpi_control_t {
	struct {
		unsigned long fid:5,
		vid:5,
		sgtc:20,
		res1:2;
	} bits;
	unsigned long val;
};
#endif

//#ifdef CONFIG_CPU_FREQ_DEBUG
/* divide by 1000 to get VCore voltage in V. */
static const int mobile_vid_table[32] = {
    2000, 1950, 1900, 1850, 1800, 1750, 1700, 1650,
    1600, 1550, 1500, 1450, 1400, 1350, 1300, 0,
    1275, 1250, 1225, 1200, 1175, 1150, 1125, 1100,
    1075, 1050, 1025, 1000, 975, 950, 925, 0,
};
//#endif

/* divide by 10 to get FID. */
static const int fid_codes[32] = {
    110, 115, 120, 125, 50, 55, 60, 65,
    70, 75, 80, 85, 90, 95, 100, 105,
    30, 190, 40, 200, 130, 135, 140, 210,
    150, 225, 160, 165, 170, 180, -1, -1,
};

/* translation table for even multiplier to fid */
static int even_multiplier[20] = {
	16, 18, 4, 6, 8, 10, 12, 14,        // 3, 4, 5, 6, 7 ,8 , 9, 10
	0, 2, 20, 22, 24, 26, 28, 29,       // 11, 12, 13, 14, 15, 16, 17, 18
	17, 19, 23, 25,                     // 19, 20, 21, 22
};

/* translation table for odd multiplier to fid*/
static int odd_multiplier[9] = {
	5, 7, 9, 11, 13, 15, 1, 3,  // 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, 11.5, 12.5
	21,                         // 13.5
};

/* This parameter is used in order to force ACPI instead of legacy method for
 * configuration purpose.
 */

static int acpi_force;

/* This parameters can be used to manually overwrite the tables */
static int overwrite_table = 0;
#define MAX_PST 10
static int multiplier_arr_size = MAX_PST;
static int voltage_arr_size = MAX_PST;
static int multiplier[MAX_PST] = {[0 ... (MAX_PST - 1)] = 0 };
static int voltage[MAX_PST] = {[0 ... (MAX_PST - 1)] = 0 };
static int switch_latency = 0;
static unsigned int fsb = 0;

static struct cpufreq_frequency_table *powernow_table;

static unsigned int can_scale_bus;
static unsigned int can_scale_vid;
static unsigned int minimum_speed=-1;
static unsigned int maximum_speed;
static unsigned int number_scales;
static unsigned int latency;
static char have_a0;

#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "powernow-k7", msg)

static int check_fsb(unsigned int fsbspeed)
{
	int delta;
	unsigned int f = fsb / 1000;

	delta = (fsbspeed > f) ? fsbspeed - f : f - fsbspeed;
	return (delta < 5);
}

static int check_powernow(void)
{
	struct cpuinfo_x86 *c = &cpu_data(0);
	unsigned int maxei, eax, ebx, ecx, edx;

	if ((c->x86_vendor != X86_VENDOR_AMD) || (c->x86 !=6)) {
#ifdef MODULE
		printk (KERN_INFO PFX "This module only works with AMD K7 CPUs\n");
#endif
		return 0;
	}

	/* Get maximum capabilities */
	maxei = cpuid_eax (0x80000000);
	if (maxei < 0x80000007) {	/* Any powernow info ? */
#ifdef MODULE
		printk (KERN_INFO PFX "No powernow capabilities detected\n");
#endif
		return 0;
	}

	if ((c->x86_model == 6) && (c->x86_mask == 0)) {
		printk (KERN_INFO PFX "K7 660[A0] core detected, enabling errata workarounds\n");
		have_a0 = 1;
	}

	cpuid(0x80000007, &eax, &ebx, &ecx, &edx);

	/* Check we can actually do something before we say anything.*/
	if (!(edx & (1 << 1 | 1 << 2)))
		return 0;

	printk (KERN_INFO PFX "PowerNOW! Technology present. Can scale: ");

	if (edx & 1 << 1) {
		printk ("frequency");
		can_scale_bus=1;
	}

	if ((edx & (1 << 1 | 1 << 2)) == 0x6)
		printk (" and ");

	if (edx & 1 << 2) {
		printk ("voltage");
		can_scale_vid=1;
	}

	printk (".\n");
	return 1;
}


static int get_ranges (unsigned char *pst)
{
	unsigned int j;
	unsigned int speed;
	u8 fid, vid;

	powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) * (number_scales + 1)), GFP_KERNEL);
	if (!powernow_table)
		return -ENOMEM;

	for (j=0 ; j < number_scales; j++) {
		fid = *pst++;

		powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
		powernow_table[j].index = fid; /* lower 8 bits */

		speed = powernow_table[j].frequency;

		if ((fid_codes[fid] % 10)==5) {
#ifdef CONFIG_X86_POWERNOW_K7_ACPI
			if (have_a0 == 1)
				powernow_table[j].frequency = CPUFREQ_ENTRY_INVALID;
#endif
		}

		if (speed < minimum_speed)
			minimum_speed = speed;
		if (speed > maximum_speed)
			maximum_speed = speed;

		vid = *pst++;
		powernow_table[j].index |= (vid << 8); /* upper 8 bits */

		dprintk ("   FID: 0x%x (%d.%dx [%dMHz])  "
			 "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
			 fid_codes[fid] % 10, speed/1000, vid,
			 mobile_vid_table[vid]/1000,
			 mobile_vid_table[vid]%1000);
	}
	powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
	powernow_table[number_scales].index = 0;

	return 0;
}


static void change_FID(int fid)
{
	union msr_fidvidctl fidvidctl;

	rdmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
	if (fidvidctl.bits.FID != fid) {
		fidvidctl.bits.SGTC = latency;
		fidvidctl.bits.FID = fid;
		fidvidctl.bits.VIDC = 0;
		fidvidctl.bits.FIDC = 1;
		wrmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
	}
}


static void change_VID(int vid)
{
	union msr_fidvidctl fidvidctl;

	rdmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
	if (fidvidctl.bits.VID != vid) {
		fidvidctl.bits.SGTC = latency;
		fidvidctl.bits.VID = vid;
		fidvidctl.bits.FIDC = 0;
		fidvidctl.bits.VIDC = 1;
		wrmsrl (MSR_K7_FID_VID_CTL, fidvidctl.val);
	}
}


static void change_speed (unsigned int index)
{
	u8 fid, vid;
	struct cpufreq_freqs freqs;
	union msr_fidvidstatus fidvidstatus;
	int cfid;

	/* fid are the lower 8 bits of the index we stored into
	 * the cpufreq frequency table in powernow_decode_bios,
	 * vid are the upper 8 bits.
	 */

	fid = powernow_table[index].index & 0xFF;
	vid = (powernow_table[index].index & 0xFF00) >> 8;

	freqs.cpu = 0;

	rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
	cfid = fidvidstatus.bits.CFID;
	freqs.old = fsb * fid_codes[cfid] / 10;

	freqs.new = powernow_table[index].frequency;

	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);

	/* Now do the magic poking into the MSRs.  */

	if (have_a0 == 1)	/* A0 errata 5 */
		local_irq_disable();

	if (freqs.old > freqs.new) {
		/* Going down, so change FID first */
		change_FID(fid);
		change_VID(vid);
	} else {
		/* Going up, so change VID first */
		change_VID(vid);
		change_FID(fid);
	}


	if (have_a0 == 1)
		local_irq_enable();

	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
}


#ifdef CONFIG_X86_POWERNOW_K7_ACPI

static struct acpi_processor_performance *acpi_processor_perf;

static int powernow_acpi_init(void)
{
	int i;
	int retval = 0;
	union powernow_acpi_control_t pc;

	if (acpi_processor_perf != NULL && powernow_table != NULL) {
		retval = -EINVAL;
		goto err0;
	}

	acpi_processor_perf = kzalloc(sizeof(struct acpi_processor_performance),
				      GFP_KERNEL);
	if (!acpi_processor_perf) {
		retval = -ENOMEM;
		goto err0;
	}

	if (acpi_processor_register_performance(acpi_processor_perf, 0)) {
		retval = -EIO;
		goto err1;
	}

	if (acpi_processor_perf->control_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) {
		retval = -ENODEV;
		goto err2;
	}

	if (acpi_processor_perf->status_register.space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) {
		retval = -ENODEV;
		goto err2;
	}

	number_scales = acpi_processor_perf->state_count;

	if (number_scales < 2) {
		retval = -ENODEV;
		goto err2;
	}

	powernow_table = kzalloc((number_scales + 1) * (sizeof(struct cpufreq_frequency_table)), GFP_KERNEL);
	if (!powernow_table) {
		retval = -ENOMEM;
		goto err2;
	}

	pc.val = (unsigned long) acpi_processor_perf->states[0].control;
	for (i = 0; i < number_scales; i++) {
		u8 fid, vid;
		struct acpi_processor_px *state =
			&acpi_processor_perf->states[i];
		unsigned int speed, speed_mhz;

		pc.val = (unsigned long) state->control;
		dprintk ("acpi:  P%d: %d MHz %d mW %d uS control %08x SGTC %d\n",
			 i,
			 (u32) state->core_frequency,
			 (u32) state->power,
			 (u32) state->transition_latency,
			 (u32) state->control,
			 pc.bits.sgtc);

		vid = pc.bits.vid;
		fid = pc.bits.fid;

		powernow_table[i].frequency = fsb * fid_codes[fid] / 10;
		powernow_table[i].index = fid; /* lower 8 bits */
		powernow_table[i].index |= (vid << 8); /* upper 8 bits */

		speed = powernow_table[i].frequency;
		speed_mhz = speed / 1000;

		/* processor_perflib will multiply the MHz value by 1000 to
		 * get a KHz value (e.g. 1266000). However, powernow-k7 works
		 * with true KHz values (e.g. 1266768). To ensure that all
		 * powernow frequencies are available, we must ensure that
		 * ACPI doesn't restrict them, so we round up the MHz value
		 * to ensure that perflib's computed KHz value is greater than
		 * or equal to powernow's KHz value.
		 */
		if (speed % 1000 > 0)
			speed_mhz++;

		if ((fid_codes[fid] % 10)==5) {
			if (have_a0 == 1)
				powernow_table[i].frequency = CPUFREQ_ENTRY_INVALID;
		}

		dprintk ("   FID: 0x%x (%d.%dx [%dMHz])  "
			 "VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
			 fid_codes[fid] % 10, speed_mhz, vid,
			 mobile_vid_table[vid]/1000,
			 mobile_vid_table[vid]%1000);

		if (state->core_frequency != speed_mhz) {
			state->core_frequency = speed_mhz;
			dprintk("   Corrected ACPI frequency to %d\n",
				speed_mhz);
		}

		if (latency < pc.bits.sgtc)
			latency = pc.bits.sgtc;

		if (speed < minimum_speed)
			minimum_speed = speed;
		if (speed > maximum_speed)
			maximum_speed = speed;
	}

	powernow_table[i].frequency = CPUFREQ_TABLE_END;
	powernow_table[i].index = 0;

	/* notify BIOS that we exist */
	acpi_processor_notify_smm(THIS_MODULE);

	return 0;

err2:
	acpi_processor_unregister_performance(acpi_processor_perf, 0);
err1:
	kfree(acpi_processor_perf);
err0:
	printk(KERN_WARNING PFX "ACPI perflib can not be used in this platform\n");
	acpi_processor_perf = NULL;
	return retval;
}
#else
static int powernow_acpi_init(void)
{
	printk(KERN_INFO PFX "no support for ACPI processor found."
	       "  Please recompile your kernel with ACPI processor\n");
	return -EINVAL;
}
#endif

static int powernow_manual_settings(union msr_fidvidstatus *fidvidstatus)
{
	int i,k, validentry;
	unsigned int max_multiplier, max_voltage;
	unsigned int speed, cm;
	u8 vid, fid;
	static struct cpufreq_frequency_table *powernow_table_tmp;
	
	if (switch_latency > 0) {
		if (switch_latency < 100) {
			printk (KERN_INFO PFX "Settling time passed as %d microseconds."
				"Should be at least 100. Correcting.\n", switch_latency);
			switch_latency = 100;
		}
		latency = switch_latency;
	} else {
		latency = 200;
	}
	dprintk ("Settling Time: %d microseconds.\n", latency);

	/* get number of specified multipliers */
	number_scales = multiplier_arr_size;
	for (i=0; i < multiplier_arr_size; i++) {
		if (multiplier[i] == 0) {
			number_scales=i;
			break;
		}
	}

	/* get maximum values */
	max_multiplier = fid_codes[fidvidstatus->bits.MFID];
	max_voltage = mobile_vid_table[fidvidstatus->bits.MVID];

	/* allocate memory */
	powernow_table=kmalloc((sizeof(struct cpufreq_frequency_table) * (number_scales + 1)), GFP_KERNEL);
	if (!powernow_table)
		return -ENOMEM;
	memset(powernow_table,0,(sizeof(struct cpufreq_frequency_table)*(number_scales+1)));

	k=0;
	for(i=0; i<number_scales; i++) {
		validentry = 0;
		if (multiplier[i] != 0) {
			/* fix multiplier */
			if (multiplier[i] < 30)
				multiplier[i]=multiplier[i] * 10;

			if (multiplier[i] <= max_multiplier) {
				cm = (multiplier[i]/10);

				/* check if odd or even muliplier */
				if (multiplier[i]%10) {
					/* odd multiplier */
					if (cm == 16) {
						/* hardcoded because 14.5 and 15.5 fids not possible */
						fid = 27;
						validentry = 1;
					} else if ((cm > 4) && (cm < 14)) {
						fid= odd_multiplier[cm-5];
						validentry = 1;
					}
				} else {
					/* even_multiplier */
					if ((cm < 23) && (cm > 2)) {
						fid = even_multiplier[cm-3];
						validentry=1;
					}
				}
			}
		}

		if (validentry) {
			/* if no voltage specified use CPU default */
			if (voltage[i] == 0)
				voltage[i] = max_voltage;

			/* we do not allow higher voltages than the CPU's maximum
			   925 mV is the minimum */
			if ((voltage[i] <= max_voltage) && (voltage[i] >= 925)) {
				if (voltage[i] >= 1300) {
					vid = 40 - (voltage[i]/50);
				} else {
					vid = 67 - (voltage[i]/25);
				}
				/* calculate speed */
				speed = fsb * fid_codes[fid] / 10;
				powernow_table[k].frequency = speed;
				powernow_table[k].index=fid; /*lower 8 bits*/
				powernow_table[k].index|= (vid << 8); /*upper 8 bits*/

				if (speed < minimum_speed)
					minimum_speed = speed;
				if (speed > maximum_speed)
					maximum_speed = speed;

				dprintk ("   FID: 0x%x (%d.%dx [%dMHz])\t", fid, fid_codes[fid] / 10, fid_codes[fid] % 10, (speed + 500)/1000);
				printk ("VID: 0x%x (%d.%03dV)\n", vid, mobile_vid_table[vid]/1000, mobile_vid_table[vid]%1000);
				k++;
			}
		} else {
			// invalid entry
			dprintk ("Entry %d is invalid\n", i+1);
		}
	}

	if (k < number_scales) {
		/* some entrys were invalid need to realloc table */
		number_scales = k;
		powernow_table_tmp = kmalloc((sizeof(struct cpufreq_frequency_table) * (number_scales + 1)), GFP_KERNEL);
		if (!powernow_table_tmp) {
			kfree(powernow_table);
			return -ENOMEM;
		}
		memcpy(powernow_table_tmp,powernow_table,(sizeof(struct cpufreq_frequency_table) * (number_scales + 1)));
		kfree(powernow_table);
		powernow_table=powernow_table_tmp;
	}

	/* Terminate frequency list */
	powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
	powernow_table[number_scales].index = 0;
       
	return 0;
}

static int powernow_decode_bios (int maxfid, int startvid)
{
	struct psb_s *psb;
	struct pst_s *pst;
	unsigned int i, j;
	unsigned char *p;
	unsigned int etuple;
	unsigned int ret;

	etuple = cpuid_eax(0x80000001);

	for (i=0xC0000; i < 0xffff0 ; i+=16) {

		p = phys_to_virt(i);

		if (memcmp(p, "AMDK7PNOW!",  10) == 0){
			dprintk ("Found PSB header at %p\n", p);
			psb = (struct psb_s *) p;
			dprintk ("Table version: 0x%x\n", psb->tableversion);
			if (psb->tableversion != 0x12) {
				printk (KERN_INFO PFX "Sorry, only v1.2 tables supported right now\n");
				return -ENODEV;
			}

			dprintk ("Flags: 0x%x\n", psb->flags);
			if ((psb->flags & 1)==0) {
				dprintk ("Mobile voltage regulator\n");
			} else {
				dprintk ("Desktop voltage regulator\n");
			}

			latency = psb->settlingtime;
			if (latency < 100) {
				printk(KERN_INFO PFX "BIOS set settling time to %d microseconds. "
						"Should be at least 100. Correcting.\n", latency);
				latency = 100;
			}
			dprintk ("Settling Time: %d microseconds.\n", psb->settlingtime);
			dprintk ("Has %d PST tables. (Only dumping ones relevant to this CPU).\n", psb->numpst);

			p += sizeof (struct psb_s);

			pst = (struct pst_s *) p;

			for (j=0; j<psb->numpst; j++) {
				pst = (struct pst_s *) p;
				number_scales = pst->numpstates;

				if ((etuple == pst->cpuid) && check_fsb(pst->fsbspeed) &&
				    (maxfid==pst->maxfid) && (startvid==pst->startvid))
				{
					dprintk ("PST:%d (@%p)\n", j, pst);
					dprintk (" cpuid: 0x%x  fsb: %d  maxFID: 0x%x  startvid: 0x%x\n",
						 pst->cpuid, pst->fsbspeed, pst->maxfid, pst->startvid);

					ret = get_ranges ((char *) pst + sizeof (struct pst_s));
					return ret;
				} else {
					unsigned int k;
					p = (char *) pst + sizeof (struct pst_s);
					for (k=0; k<number_scales; k++)
						p+=2;
				}
			}
			printk (KERN_INFO PFX "No PST tables match this cpuid (0x%x)\n", etuple);
			printk (KERN_INFO PFX "This is indicative of a broken BIOS.\n");

			return -EINVAL;
		}
		p++;
	}

	return -ENODEV;
}


static int powernow_target (struct cpufreq_policy *policy,
			    unsigned int target_freq,
			    unsigned int relation)
{
	unsigned int newstate;

	if (cpufreq_frequency_table_target(policy, powernow_table, target_freq, relation, &newstate))
		return -EINVAL;

	change_speed(newstate);

	return 0;
}


static int powernow_verify (struct cpufreq_policy *policy)
{
	return cpufreq_frequency_table_verify(policy, powernow_table);
}

/*
 * We use the fact that the bus frequency is somehow
 * a multiple of 100000/3 khz, then we compute sgtc according
 * to this multiple.
 * That way, we match more how AMD thinks all of that work.
 * We will then get the same kind of behaviour already tested under
 * the "well-known" other OS.
 */
static int __init fixup_sgtc(void)
{
	unsigned int sgtc;
	unsigned int m;

	m = fsb / 3333;
	if ((m % 10) >= 5)
		m += 5;

	m /= 10;

	sgtc = 100 * m * latency;
	sgtc = sgtc / 3;
	if (sgtc > 0xfffff) {
		printk(KERN_WARNING PFX "SGTC too large %d\n", sgtc);
		sgtc = 0xfffff;
	}
	return sgtc;
}

static unsigned int powernow_get(unsigned int cpu)
{
	union msr_fidvidstatus fidvidstatus;
	unsigned int cfid;

	if (cpu)
		return 0;
	rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);
	cfid = fidvidstatus.bits.CFID;

	return (fsb * fid_codes[cfid] / 10);
}


static int __init acer_cpufreq_pst(const struct dmi_system_id *d)
{
	printk(KERN_WARNING "%s laptop with broken PST tables in BIOS detected.\n", d->ident);
	printk(KERN_WARNING "You need to downgrade to 3A21 (09/09/2002), or try a newer BIOS than 3A71 (01/20/2003)\n");
	printk(KERN_WARNING "cpufreq scaling has been disabled as a result of this.\n");
	return 0;
}

/*
 * Some Athlon laptops have really fucked PST tables.
 * A BIOS update is all that can save them.
 * Mention this, and disable cpufreq.
 */
static struct dmi_system_id __initdata powernow_dmi_table[] = {
	{
		.callback = acer_cpufreq_pst,
		.ident = "Acer Aspire",
		.matches = {
			DMI_MATCH(DMI_SYS_VENDOR, "Insyde Software"),
			DMI_MATCH(DMI_BIOS_VERSION, "3A71"),
		},
	},
	{ }
};

static int __init powernow_cpu_init (struct cpufreq_policy *policy)
{
	union msr_fidvidstatus fidvidstatus;
	int result;

	if (policy->cpu != 0)
		return -ENODEV;

	rdmsrl (MSR_K7_FID_VID_STATUS, fidvidstatus.val);

	if (fsb) {
		unsigned int mult;

		if (fsb < 1000)
			fsb *= 1000;
		if (fsb < 1000 || fsb > 1000000) {
			printk(KERN_WARNING PFX "FSB %ukhz out of range\n", fsb);
			return -EINVAL;
		}

		mult = ((2*cpu_khz + fsb/2) / fsb) * 5;
		if (mult != fid_codes[fidvidstatus.bits.MFID])
			dprintk("MSR reported multiplier %u.%u\n", fid_codes[fidvidstatus.bits.MFID]/10, fid_codes[fidvidstatus.bits.MFID]%10);
		dprintk("Current multiplier %u.%u. CPU running at %luMHz\n", mult/10, mult%10, (cpu_khz + 500)/1000);
		if (mult < 30 || mult >= 230)
			dprintk("Multiplier %u.%u not possible\n", mult/10, mult%10);
	} else
	
	fsb = (10 * cpu_khz) / fid_codes[fidvidstatus.bits.CFID];
	if (!fsb) {
		printk(KERN_WARNING PFX "can not determine bus frequency\n");
		return -EINVAL;
	}
	dprintk("FSB: %3dMHz\n", fsb/1000);

	if (dmi_check_system(powernow_dmi_table) || acpi_force) {
		printk (KERN_INFO PFX "PSB/PST known to be broken.  Trying ACPI instead\n");
		result = powernow_acpi_init();
	} else if (overwrite_table){
		printk(KERN_INFO PFX "Overwriting PST table with manual settings\n");
		result = powernow_manual_settings(&fidvidstatus);
	} else {
		result = powernow_decode_bios(fidvidstatus.bits.MFID, fidvidstatus.bits.SVID);
		if (result) {
			printk (KERN_INFO PFX "Trying ACPI perflib\n");
			maximum_speed = 0;
			minimum_speed = -1;
			latency = 0;
			result = powernow_acpi_init();
			if (result) {
				printk (KERN_INFO PFX "ACPI and legacy methods failed\n");
				printk (KERN_INFO PFX "See http://www.codemonkey.org.uk/projects/cpufreq/powernow-k7.html\n");
			}
		} else {
			/* SGTC use the bus clock as timer */
			latency = fixup_sgtc();
			printk(KERN_INFO PFX "SGTC: %d\n", latency);
		}
	}

	if (result)
		return result;

	printk (KERN_INFO PFX "Minimum speed %d MHz. Maximum speed %d MHz.\n",
				minimum_speed/1000, maximum_speed/1000);

	policy->cpuinfo.transition_latency = cpufreq_scale(2000000UL, fsb, latency);

	policy->cur = powernow_get(0);

	cpufreq_frequency_table_get_attr(powernow_table, policy->cpu);

	return cpufreq_frequency_table_cpuinfo(policy, powernow_table);
}

static int powernow_cpu_exit (struct cpufreq_policy *policy) {
	cpufreq_frequency_table_put_attr(policy->cpu);

#ifdef CONFIG_X86_POWERNOW_K7_ACPI
	if (acpi_processor_perf) {
		acpi_processor_unregister_performance(acpi_processor_perf, 0);
		kfree(acpi_processor_perf);
	}
#endif

	kfree(powernow_table);
	return 0;
}

static struct freq_attr* powernow_table_attr[] = {
	&cpufreq_freq_attr_scaling_available_freqs,
	NULL,
};

static struct cpufreq_driver powernow_driver = {
	.verify	= powernow_verify,
	.target	= powernow_target,
	.get	= powernow_get,
	.init	= powernow_cpu_init,
	.exit	= powernow_cpu_exit,
	.name	= "powernow-k7",
	.owner	= THIS_MODULE,
	.attr	= powernow_table_attr,
};

static int __init powernow_init (void)
{
	if (check_powernow()==0)
		return -ENODEV;
	return cpufreq_register_driver(&powernow_driver);
}


static void __exit powernow_exit (void)
{
	cpufreq_unregister_driver(&powernow_driver);
}

module_param(acpi_force,  int, 0444);
MODULE_PARM_DESC(acpi_force, "Force ACPI to be used.");

module_param(overwrite_table,int,0444);
MODULE_PARM_DESC(overwrite_table, "overwrite table with manually settings");
module_param(fsb, uint, 0444);
MODULE_PARM_DESC(fsb, "(MHz) overrides the calculated FSB");
module_param_array(multiplier, int, &multiplier_arr_size, 0444);
MODULE_PARM_DESC(multiplier, "Specifiy up to 10 multipliers, multiply them by 10: 5->50, 5.5->55");
module_param_array(voltage, int, &voltage_arr_size, 0444);
MODULE_PARM_DESC(voltage, "Specify voltages in respect to the given multipliers, specify them in mV: 1.275V -> 1275");
module_param(switch_latency,  int, 0444);
MODULE_PARM_DESC(switch_latency, "Set state transition latency in microseconds (default 200us)");

MODULE_AUTHOR ("Dave Jones <davej@redhat.com>");
MODULE_DESCRIPTION ("Powernow driver for AMD K7 processors.");
MODULE_LICENSE ("GPL");

late_initcall(powernow_init);
module_exit(powernow_exit);

c/ modifier le début du ./Makefile (un mystère pour moi, bien que ce soit le noyau 2.6.28-13.45 selon Ubuntu, il le compile comme un 2.6.9, ce qui avait posé soucis ensuite :

VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 28
EXTRAVERSION = -13-k7

d/ récupérer le .config dans /boot/config-.26.28-13-generic ; puis le modifier afin que powernow-k7 soit compilé en module et non dans le noyau.

e/ compiler le noyau
make bzImage

f/ compiler uniquement le module powernow-k7 (inutile de recopiler les autres, les anciens seront compatibles) :
make ./arch/x86/kernel/cpu/cpufreq/powernow-k7

g/ recopier les fichiers :

cp ./arch/x86/boot/bzImage /boot/vmlinuz-2.6.28-13-k7
cp ./System.map /boot/System.map-2.6.28-13-k7
cp ./arch/x86/kernel/cpu/cpufreq/powernow-k7.ko /lib/modules/2.6.28-13-generic/kernel/arch/cpu/cpufreq/

h/ créer un lien pour les modules

ln -s /lib/modules/2.6.28-13-generic /lib/modules/2.6.28-13-k7

i/ Mettre à jour grub :

update-grub && grub-install hd0

j/ reboot

4/ Finalisation
après avoir redémarré, finir la prise en compte :

update-initramfs -u
depmod -a

5/ Mise en fonctionnement
avant de mettre le module en chargement automatique dans, je vous conseille de faire quelques tests via modprobe :

modprobe powernow-k7 overwrite_table=1 multiplier=7,8,9,10,11,12,13 switch_latency=400

explications :
* overwrite_table=1 pour signaler au module qu'on va lui fournir manuellement une table de multiplicateurs
* multiplier=x,x,x liste des coefficients multiplicateurs à prendre en compte. Chez moi en dessous de 7 le PC fige, effectuez une série de test par tatonnement. J'ai mis 13 en max car c'était le coef inital de mon proc, bien sûr par cette méthode il est aussi possible d'overclocker du coup, à vous de voir.
* switch_latency délai pour effectuer les changements de fréquence, en µs. Chez moi en dessous de 400 le PC fige lors d'un changement, là encore essayez par tatonnement.

Pour savoir si cela fonctionne :

echo ondemand > /sys/device/system/cpu/cpu0/cpufreq/scaling_governor

Pour vérifier que le processeur tourne moins vite qu'avant lorsque le systèmùe ne fait rien :

cat /proc/cpuinfo

6/ Astuce :
Vous pouvez ensuite automatiser le chargement du module en l'insérant avec ses paramètres corrects dans /etc/modules
Dans gnome, vous pouvez utilsier l'applet de Monitoring de la fréquence du processeur

7/ Conclusion
J'ai gagné 10°C au repos

Hors ligne

#2 Le 25/07/2009, à 08:13

coethium

Re : Transformer son Athon XP desktop en Athlon XP mobile (Jaunty)

Addendum :

En augmentant la valeur du switch_latency, j'ai pu ajouter les multiplicateurs les plus faibles à la liste sans que je système fige. Ce qui a fait gagner 7°C supplémentaires au repos.

Hors ligne