From: Tony Lindgren Following is the updated patch to make the powernow-k8 driver work on machines with buggy BIOS, such as emachines m6805. The patch overrides the PST table only if check_pst_table() fails. The minimum value for the override is 800MHz, which is the lowest value on all x86_64 systems AFAIK. The max value is the current running value. This patch should be safe to apply, even if Pavel's ACPI table check is added to the driver. --- 25-akpm/arch/i386/kernel/cpu/cpufreq/powernow-k8.c | 56 ++++++++++++++++----- 1 files changed, 43 insertions(+), 13 deletions(-) diff -puN arch/i386/kernel/cpu/cpufreq/powernow-k8.c~powernow-k8-buggy-bios-override-for-266 arch/i386/kernel/cpu/cpufreq/powernow-k8.c --- 25/arch/i386/kernel/cpu/cpufreq/powernow-k8.c~powernow-k8-buggy-bios-override-for-266 2004-05-12 21:06:38.917144056 -0700 +++ 25-akpm/arch/i386/kernel/cpu/cpufreq/powernow-k8.c 2004-05-12 21:06:38.922143296 -0700 @@ -42,6 +42,8 @@ #define VERSION "version 1.00.09b" #include "powernow-k8.h" +#define MAX_NR_PST 16 + /* serialize freq changes */ static DECLARE_MUTEX(fidvid_sem); @@ -479,6 +481,23 @@ out: } +/* + * This is the place to override the buggy BIOS values + */ +static int override_pst_table(struct powernow_k8_data *data, struct pst_s *pst) { + printk(KERN_INFO PFX "BIOS error: overriding frequency table\n"); + + /* Use a safe minimum speed 800MHz */ + pst[0].fid = 0x00; + pst[0].vid = 0x12; + + /* Use current speed as the maximum speed */ + pst[1].fid = data->currfid; + pst[1].vid = data->currvid; + + return 2; +} + static int check_pst_table(struct powernow_k8_data *data, struct pst_s *pst, u8 maxvid) { unsigned int j; @@ -536,6 +555,7 @@ static int fill_powernow_table(struct po { struct cpufreq_frequency_table *powernow_table; unsigned int j; + struct pst_s *pst_tmp; if (data->batps) { /* use ACPI support to get full speed on mains power */ printk(KERN_WARNING PFX "Only %d pstates usable (use ACPI driver for full range\n", data->batps); @@ -553,39 +573,49 @@ static int fill_powernow_table(struct po printk(KERN_ERR PFX "no p states to transition\n"); return -ENODEV; } - + + /* Must have the current values early in case of override_pst_table() */ + if (query_current_values_with_pending_wait(data)) + return -EIO; + + /* Copy the BIOS table into a temporary table in case it needs override */ + pst_tmp = kmalloc(sizeof(struct pst_s) * MAX_NR_PST, GFP_KERNEL); + if (!pst_tmp) { + printk(KERN_ERR PFX "pst_tmp memory alloc failure\n"); + return -ENOMEM; + } + memset(pst_tmp, 0, sizeof(struct pst_s) * MAX_NR_PST); + memcpy(pst_tmp, pst, sizeof(struct pst_s) * data->numps); + if (check_pst_table(data, pst, maxvid)) - return -EINVAL; + data->numps = override_pst_table(data, pst_tmp); powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table) * (data->numps + 1)), GFP_KERNEL); if (!powernow_table) { printk(KERN_ERR PFX "powernow_table memory alloc failure\n"); + kfree(pst_tmp); return -ENOMEM; } for (j = 0; j < data->numps; j++) { - powernow_table[j].index = pst[j].fid; /* lower 8 bits */ - powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */ - powernow_table[j].frequency = find_khz_freq_from_fid(pst[j].fid); + powernow_table[j].index = pst_tmp[j].fid; /* lower 8 bits */ + powernow_table[j].index |= (pst_tmp[j].vid << 8); /* upper 8 bits */ + powernow_table[j].frequency = find_khz_freq_from_fid(pst_tmp[j].fid); } powernow_table[data->numps].frequency = CPUFREQ_TABLE_END; powernow_table[data->numps].index = 0; - if (query_current_values_with_pending_wait(data)) { - kfree(powernow_table); - return -EIO; - } - dprintk(KERN_INFO PFX "cfid 0x%x, cvid 0x%x\n", data->currfid, data->currvid); data->powernow_table = powernow_table; print_basics(data); for (j = 0; j < data->numps; j++) - if ((pst[j].fid==data->currfid) && (pst[j].vid==data->currvid)) + if ((pst_tmp[j].fid==data->currfid) && (pst_tmp[j].vid==data->currvid)) return 0; dprintk(KERN_ERR PFX "currfid/vid do not match PST, ignoring\n"); + kfree(pst_tmp); return 0; } @@ -635,8 +665,8 @@ static int find_psb_table(struct powerno dprintk(KERN_DEBUG PFX "numpst: 0x%x\n", psb->numpst); if (psb->numpst != 1) { - printk(KERN_ERR BFX "numpst must be 1\n"); - return -ENODEV; + printk(KERN_WARNING BFX "numpst listed as %i " + "should be 1. Ignoring it.\n", psb->numpst); } data->plllock = psb->plllocktime; _