diff options
author | Dave Jones <davej@redhat.com> | 2005-10-21 17:21:03 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-10-21 17:28:58 -0400 |
commit | 0213df74315bbab9ccaa73146f3e11972ea6de46 (patch) | |
tree | 68a71915bb58a18168cd13744772bd447bcf8b29 /arch/i386/kernel | |
parent | 3078fcc1d18c7235b034dc889642c5300959fa20 (diff) |
[PATCH] cpufreq: fix pending powernow timer stuck condition
AMD recently discovered that on some hardware, there is a race condition
possible when a C-state change request goes onto the bus at the same
time as a P-state change request.
Both requests happen, but the southbridge hardware only acknowledges the
C-state change. The PowerNow! driver is then stuck in a loop, waiting
for the P-state change acknowledgement. The driver eventually times
out, but can no longer perform P-state changes.
It turns out the solution is to resend the P-state change, which the
southbridge will acknowledge normally.
Thanks to Johannes Winkelmann for reporting this and testing the fix.
Signed-off-by: Mark Langsdorf <mark.langsdorf@amd.com>
Signed-off-by: Dave Jones <davej@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/i386/kernel')
-rw-r--r-- | arch/i386/kernel/cpu/cpufreq/powernow-k8.c | 30 |
1 files changed, 19 insertions, 11 deletions
diff --git a/arch/i386/kernel/cpu/cpufreq/powernow-k8.c b/arch/i386/kernel/cpu/cpufreq/powernow-k8.c index ab6e0611303d..ec3a9e335aa3 100644 --- a/arch/i386/kernel/cpu/cpufreq/powernow-k8.c +++ b/arch/i386/kernel/cpu/cpufreq/powernow-k8.c | |||
@@ -44,7 +44,7 @@ | |||
44 | 44 | ||
45 | #define PFX "powernow-k8: " | 45 | #define PFX "powernow-k8: " |
46 | #define BFX PFX "BIOS error: " | 46 | #define BFX PFX "BIOS error: " |
47 | #define VERSION "version 1.50.3" | 47 | #define VERSION "version 1.50.4" |
48 | #include "powernow-k8.h" | 48 | #include "powernow-k8.h" |
49 | 49 | ||
50 | /* serialize freq changes */ | 50 | /* serialize freq changes */ |
@@ -111,8 +111,8 @@ static int query_current_values_with_pending_wait(struct powernow_k8_data *data) | |||
111 | u32 i = 0; | 111 | u32 i = 0; |
112 | 112 | ||
113 | do { | 113 | do { |
114 | if (i++ > 0x1000000) { | 114 | if (i++ > 10000) { |
115 | printk(KERN_ERR PFX "detected change pending stuck\n"); | 115 | dprintk("detected change pending stuck\n"); |
116 | return 1; | 116 | return 1; |
117 | } | 117 | } |
118 | rdmsr(MSR_FIDVID_STATUS, lo, hi); | 118 | rdmsr(MSR_FIDVID_STATUS, lo, hi); |
@@ -159,6 +159,7 @@ static int write_new_fid(struct powernow_k8_data *data, u32 fid) | |||
159 | { | 159 | { |
160 | u32 lo; | 160 | u32 lo; |
161 | u32 savevid = data->currvid; | 161 | u32 savevid = data->currvid; |
162 | u32 i = 0; | ||
162 | 163 | ||
163 | if ((fid & INVALID_FID_MASK) || (data->currvid & INVALID_VID_MASK)) { | 164 | if ((fid & INVALID_FID_MASK) || (data->currvid & INVALID_VID_MASK)) { |
164 | printk(KERN_ERR PFX "internal error - overflow on fid write\n"); | 165 | printk(KERN_ERR PFX "internal error - overflow on fid write\n"); |
@@ -170,10 +171,13 @@ static int write_new_fid(struct powernow_k8_data *data, u32 fid) | |||
170 | dprintk("writing fid 0x%x, lo 0x%x, hi 0x%x\n", | 171 | dprintk("writing fid 0x%x, lo 0x%x, hi 0x%x\n", |
171 | fid, lo, data->plllock * PLL_LOCK_CONVERSION); | 172 | fid, lo, data->plllock * PLL_LOCK_CONVERSION); |
172 | 173 | ||
173 | wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION); | 174 | do { |
174 | 175 | wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION); | |
175 | if (query_current_values_with_pending_wait(data)) | 176 | if (i++ > 100) { |
176 | return 1; | 177 | printk(KERN_ERR PFX "internal error - pending bit very stuck - no further pstate changes possible\n"); |
178 | retrun 1; | ||
179 | } | ||
180 | } while (query_current_values_with_pending_wait(data)); | ||
177 | 181 | ||
178 | count_off_irt(data); | 182 | count_off_irt(data); |
179 | 183 | ||
@@ -197,6 +201,7 @@ static int write_new_vid(struct powernow_k8_data *data, u32 vid) | |||
197 | { | 201 | { |
198 | u32 lo; | 202 | u32 lo; |
199 | u32 savefid = data->currfid; | 203 | u32 savefid = data->currfid; |
204 | int i = 0; | ||
200 | 205 | ||
201 | if ((data->currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) { | 206 | if ((data->currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) { |
202 | printk(KERN_ERR PFX "internal error - overflow on vid write\n"); | 207 | printk(KERN_ERR PFX "internal error - overflow on vid write\n"); |
@@ -208,10 +213,13 @@ static int write_new_vid(struct powernow_k8_data *data, u32 vid) | |||
208 | dprintk("writing vid 0x%x, lo 0x%x, hi 0x%x\n", | 213 | dprintk("writing vid 0x%x, lo 0x%x, hi 0x%x\n", |
209 | vid, lo, STOP_GRANT_5NS); | 214 | vid, lo, STOP_GRANT_5NS); |
210 | 215 | ||
211 | wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS); | 216 | do { |
212 | 217 | wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS); | |
213 | if (query_current_values_with_pending_wait(data)) | 218 | if (i++ > 100) { |
214 | return 1; | 219 | printk(KERN_ERR PFX "internal error - pending bit very stuck - no further pstate changes possible\n"); |
220 | return 1; | ||
221 | } | ||
222 | } while (query_current_values_with_pending_wait(data)); | ||
215 | 223 | ||
216 | if (savefid != data->currfid) { | 224 | if (savefid != data->currfid) { |
217 | printk(KERN_ERR PFX "fid changed on vid trans, old 0x%x new 0x%x\n", | 225 | printk(KERN_ERR PFX "fid changed on vid trans, old 0x%x new 0x%x\n", |