Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0213df74 authored by Dave Jones's avatar Dave Jones Committed by Linus Torvalds
Browse files

[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: default avatarMark Langsdorf <mark.langsdorf@amd.com>
Signed-off-by: default avatarDave Jones <davej@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 3078fcc1
Loading
Loading
Loading
Loading
+19 −11
Original line number Original line Diff line number Diff line
@@ -44,7 +44,7 @@


#define PFX "powernow-k8: "
#define PFX "powernow-k8: "
#define BFX PFX "BIOS error: "
#define BFX PFX "BIOS error: "
#define VERSION "version 1.50.3"
#define VERSION "version 1.50.4"
#include "powernow-k8.h"
#include "powernow-k8.h"


/* serialize freq changes  */
/* serialize freq changes  */
@@ -111,8 +111,8 @@ static int query_current_values_with_pending_wait(struct powernow_k8_data *data)
	u32 i = 0;
	u32 i = 0;


	do {
	do {
		if (i++ > 0x1000000) {
		if (i++ > 10000) {
			printk(KERN_ERR PFX "detected change pending stuck\n");
			dprintk("detected change pending stuck\n");
			return 1;
			return 1;
		}
		}
		rdmsr(MSR_FIDVID_STATUS, lo, hi);
		rdmsr(MSR_FIDVID_STATUS, lo, hi);
@@ -159,6 +159,7 @@ static int write_new_fid(struct powernow_k8_data *data, u32 fid)
{
{
	u32 lo;
	u32 lo;
	u32 savevid = data->currvid;
	u32 savevid = data->currvid;
	u32 i = 0;


	if ((fid & INVALID_FID_MASK) || (data->currvid & INVALID_VID_MASK)) {
	if ((fid & INVALID_FID_MASK) || (data->currvid & INVALID_VID_MASK)) {
		printk(KERN_ERR PFX "internal error - overflow on fid write\n");
		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)
	dprintk("writing fid 0x%x, lo 0x%x, hi 0x%x\n",
	dprintk("writing fid 0x%x, lo 0x%x, hi 0x%x\n",
		fid, lo, data->plllock * PLL_LOCK_CONVERSION);
		fid, lo, data->plllock * PLL_LOCK_CONVERSION);


	do {
		wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);
		wrmsr(MSR_FIDVID_CTL, lo, data->plllock * PLL_LOCK_CONVERSION);

		if (i++ > 100) {
	if (query_current_values_with_pending_wait(data))
			printk(KERN_ERR PFX "internal error - pending bit very stuck - no further pstate changes possible\n");
		return 1;
			retrun 1;
		}			
	} while (query_current_values_with_pending_wait(data));


	count_off_irt(data);
	count_off_irt(data);


@@ -197,6 +201,7 @@ static int write_new_vid(struct powernow_k8_data *data, u32 vid)
{
{
	u32 lo;
	u32 lo;
	u32 savefid = data->currfid;
	u32 savefid = data->currfid;
	int i = 0;


	if ((data->currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
	if ((data->currfid & INVALID_FID_MASK) || (vid & INVALID_VID_MASK)) {
		printk(KERN_ERR PFX "internal error - overflow on vid write\n");
		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)
	dprintk("writing vid 0x%x, lo 0x%x, hi 0x%x\n",
	dprintk("writing vid 0x%x, lo 0x%x, hi 0x%x\n",
		vid, lo, STOP_GRANT_5NS);
		vid, lo, STOP_GRANT_5NS);


	do {
		wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);
		wrmsr(MSR_FIDVID_CTL, lo, STOP_GRANT_5NS);

                if (i++ > 100) {
	if (query_current_values_with_pending_wait(data))
                        printk(KERN_ERR PFX "internal error - pending bit very stuck - no further pstate changes possible\n");
                        return 1;
                        return 1;
                }
	} while (query_current_values_with_pending_wait(data));


	if (savefid != data->currfid) {
	if (savefid != data->currfid) {
		printk(KERN_ERR PFX "fid changed on vid trans, old 0x%x new 0x%x\n",
		printk(KERN_ERR PFX "fid changed on vid trans, old 0x%x new 0x%x\n",