Commit dadb49d8 authored by Rafa Bilski's avatar Rafa Bilski Committed by Dave Jones

[CPUFREQ] Longhaul - Hook into ACPI C states.

Minimal change necessary for hardware support.

Changes in longhaul.c:
- most important - now C3 state is causing transition,
- code responsible for clearing "bus master" bit removed,
- protect bcr2 transition in the same way as longhaul.
Signed-off-by: default avatarRafa³ Bilski <rafalbilski@interia.pl>
Signed-off-by: default avatarDave Jones <davej@redhat.com>
parent 9c9a43ed
...@@ -202,7 +202,7 @@ config X86_LONGRUN ...@@ -202,7 +202,7 @@ config X86_LONGRUN
config X86_LONGHAUL config X86_LONGHAUL
tristate "VIA Cyrix III Longhaul" tristate "VIA Cyrix III Longhaul"
select CPU_FREQ_TABLE select CPU_FREQ_TABLE
depends on BROKEN depends on ACPI_PROCESSOR
help help
This adds the CPUFreq driver for VIA Samuel/CyrixIII, This adds the CPUFreq driver for VIA Samuel/CyrixIII,
VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T VIA Cyrix Samuel/C3, VIA Cyrix Ezra and VIA Cyrix Ezra-T
......
...@@ -29,11 +29,13 @@ ...@@ -29,11 +29,13 @@
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/pci.h>
#include <asm/msr.h> #include <asm/msr.h>
#include <asm/timex.h> #include <asm/timex.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/acpi.h>
#include <linux/acpi.h>
#include <acpi/processor.h>
#include "longhaul.h" #include "longhaul.h"
...@@ -56,6 +58,8 @@ static int minvid, maxvid; ...@@ -56,6 +58,8 @@ static int minvid, maxvid;
static unsigned int minmult, maxmult; static unsigned int minmult, maxmult;
static int can_scale_voltage; static int can_scale_voltage;
static int vrmrev; static int vrmrev;
static struct acpi_processor *pr = NULL;
static struct acpi_processor_cx *cx = NULL;
/* Module parameters */ /* Module parameters */
static int dont_scale_voltage; static int dont_scale_voltage;
...@@ -118,84 +122,64 @@ static int longhaul_get_cpu_mult(void) ...@@ -118,84 +122,64 @@ static int longhaul_get_cpu_mult(void)
return eblcr_table[invalue]; return eblcr_table[invalue];
} }
/* For processor with BCR2 MSR */
static void do_powersaver(union msr_longhaul *longhaul, static void do_longhaul1(int cx_address, unsigned int clock_ratio_index)
unsigned int clock_ratio_index)
{ {
struct pci_dev *dev; union msr_bcr2 bcr2;
unsigned long flags; u32 t;
unsigned int tmp_mask;
int version;
int i;
u16 pci_cmd;
u16 cmd_state[64];
switch (cpu_model) { rdmsrl(MSR_VIA_BCR2, bcr2.val);
case CPU_EZRA_T: /* Enable software clock multiplier */
version = 3; bcr2.bits.ESOFTBF = 1;
break; bcr2.bits.CLOCKMUL = clock_ratio_index;
case CPU_NEHEMIAH:
version = 0xf;
break;
default:
return;
}
rdmsrl(MSR_VIA_LONGHAUL, longhaul->val); /* Sync to timer tick */
longhaul->bits.SoftBusRatio = clock_ratio_index & 0xf; safe_halt();
longhaul->bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4; ACPI_FLUSH_CPU_CACHE();
longhaul->bits.EnableSoftBusRatio = 1; /* Change frequency on next halt or sleep */
longhaul->bits.RevisionKey = 0; wrmsrl(MSR_VIA_BCR2, bcr2.val);
/* Invoke C3 */
inb(cx_address);
/* Dummy op - must do something useless after P_LVL3 read */
t = inl(acpi_fadt.xpm_tmr_blk.address);
/* Disable software clock multiplier */
local_irq_disable();
rdmsrl(MSR_VIA_BCR2, bcr2.val);
bcr2.bits.ESOFTBF = 0;
wrmsrl(MSR_VIA_BCR2, bcr2.val);
}
preempt_disable(); /* For processor with Longhaul MSR */
local_irq_save(flags);
/* static void do_powersaver(int cx_address, unsigned int clock_ratio_index)
* get current pci bus master state for all devices {
* and clear bus master bit union msr_longhaul longhaul;
*/ u32 t;
dev = NULL;
i = 0;
do {
dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
if (dev != NULL) {
pci_read_config_word(dev, PCI_COMMAND, &pci_cmd);
cmd_state[i++] = pci_cmd;
pci_cmd &= ~PCI_COMMAND_MASTER;
pci_write_config_word(dev, PCI_COMMAND, pci_cmd);
}
} while (dev != NULL);
tmp_mask=inb(0x21); /* works on C3. save mask. */ rdmsrl(MSR_VIA_LONGHAUL, longhaul.val);
outb(0xFE,0x21); /* TMR0 only */ longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
outb(0xFF,0x80); /* delay */ longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf;
longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
/* Sync to timer tick */
safe_halt(); safe_halt();
wrmsrl(MSR_VIA_LONGHAUL, longhaul->val); ACPI_FLUSH_CPU_CACHE();
halt(); /* Change frequency on next halt or sleep */
wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
/* Invoke C3 */
inb(cx_address);
/* Dummy op - must do something useless after P_LVL3 read */
t = inl(acpi_fadt.xpm_tmr_blk.address);
/* Disable bus ratio bit */
local_irq_disable(); local_irq_disable();
longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
outb(tmp_mask,0x21); /* restore mask */ longhaul.bits.EnableSoftBusRatio = 0;
longhaul.bits.EnableSoftBSEL = 0;
/* restore pci bus master state for all devices */ longhaul.bits.EnableSoftVID = 0;
dev = NULL; wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
i = 0;
do {
dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev);
if (dev != NULL) {
pci_cmd = cmd_state[i++];
pci_write_config_byte(dev, PCI_COMMAND, pci_cmd);
}
} while (dev != NULL);
local_irq_restore(flags);
preempt_enable();
/* disable bus ratio bit */
rdmsrl(MSR_VIA_LONGHAUL, longhaul->val);
longhaul->bits.EnableSoftBusRatio = 0;
longhaul->bits.RevisionKey = version;
wrmsrl(MSR_VIA_LONGHAUL, longhaul->val);
} }
/** /**
...@@ -209,9 +193,9 @@ static void longhaul_setstate(unsigned int clock_ratio_index) ...@@ -209,9 +193,9 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
{ {
int speed, mult; int speed, mult;
struct cpufreq_freqs freqs; struct cpufreq_freqs freqs;
union msr_longhaul longhaul;
union msr_bcr2 bcr2;
static unsigned int old_ratio=-1; static unsigned int old_ratio=-1;
unsigned long flags;
unsigned int pic1_mask, pic2_mask;
if (old_ratio == clock_ratio_index) if (old_ratio == clock_ratio_index)
return; return;
...@@ -234,6 +218,20 @@ static void longhaul_setstate(unsigned int clock_ratio_index) ...@@ -234,6 +218,20 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
dprintk ("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n", dprintk ("Setting to FSB:%dMHz Mult:%d.%dx (%s)\n",
fsb, mult/10, mult%10, print_speed(speed/1000)); fsb, mult/10, mult%10, print_speed(speed/1000));
preempt_disable();
local_irq_save(flags);
pic2_mask = inb(0xA1);
pic1_mask = inb(0x21); /* works on C3. save mask. */
outb(0xFF,0xA1); /* Overkill */
outb(0xFE,0x21); /* TMR0 only */
/* Disable bus master arbitration */
if (pr->flags.bm_check) {
acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1,
ACPI_MTX_DO_NOT_LOCK);
}
switch (longhaul_version) { switch (longhaul_version) {
/* /*
...@@ -245,20 +243,7 @@ static void longhaul_setstate(unsigned int clock_ratio_index) ...@@ -245,20 +243,7 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
*/ */
case TYPE_LONGHAUL_V1: case TYPE_LONGHAUL_V1:
case TYPE_LONGHAUL_V2: case TYPE_LONGHAUL_V2:
rdmsrl (MSR_VIA_BCR2, bcr2.val); do_longhaul1(cx->address, clock_ratio_index);
/* Enable software clock multiplier */
bcr2.bits.ESOFTBF = 1;
bcr2.bits.CLOCKMUL = clock_ratio_index;
local_irq_disable();
wrmsrl (MSR_VIA_BCR2, bcr2.val);
safe_halt();
/* Disable software clock multiplier */
rdmsrl (MSR_VIA_BCR2, bcr2.val);
bcr2.bits.ESOFTBF = 0;
local_irq_disable();
wrmsrl (MSR_VIA_BCR2, bcr2.val);
local_irq_enable();
break; break;
/* /*
...@@ -273,10 +258,22 @@ static void longhaul_setstate(unsigned int clock_ratio_index) ...@@ -273,10 +258,22 @@ static void longhaul_setstate(unsigned int clock_ratio_index)
* to work in practice. * to work in practice.
*/ */
case TYPE_POWERSAVER: case TYPE_POWERSAVER:
do_powersaver(&longhaul, clock_ratio_index); do_powersaver(cx->address, clock_ratio_index);
break; break;
} }
/* Enable bus master arbitration */
if (pr->flags.bm_check) {
acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0,
ACPI_MTX_DO_NOT_LOCK);
}
outb(pic2_mask,0xA1); /* restore mask */
outb(pic1_mask,0x21);
local_irq_restore(flags);
preempt_enable();
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
} }
...@@ -527,6 +524,18 @@ static unsigned int longhaul_get(unsigned int cpu) ...@@ -527,6 +524,18 @@ static unsigned int longhaul_get(unsigned int cpu)
return calc_speed(longhaul_get_cpu_mult()); return calc_speed(longhaul_get_cpu_mult());
} }
acpi_status longhaul_walk_callback(acpi_handle obj_handle,
u32 nesting_level,
void *context, void **return_value)
{
struct acpi_device *d;
if ( acpi_bus_get_device(obj_handle, &d) ) {
return 0;
}
*return_value = (void *)acpi_driver_data(d);
return 1;
}
static int __init longhaul_cpu_init(struct cpufreq_policy *policy) static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
{ {
...@@ -534,6 +543,15 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy) ...@@ -534,6 +543,15 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
char *cpuname=NULL; char *cpuname=NULL;
int ret; int ret;
/* Check ACPI support for C3 state */
acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
&longhaul_walk_callback, NULL, (void *)&pr);
if (pr == NULL) goto err_acpi;
cx = &pr->power.states[ACPI_STATE_C3];
if (cx == NULL || cx->latency > 1000) goto err_acpi;
/* Now check what we have on this motherboard */
switch (c->x86_model) { switch (c->x86_model) {
case 6: case 6:
cpu_model = CPU_SAMUEL; cpu_model = CPU_SAMUEL;
...@@ -634,6 +652,10 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy) ...@@ -634,6 +652,10 @@ static int __init longhaul_cpu_init(struct cpufreq_policy *policy)
cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu); cpufreq_frequency_table_get_attr(longhaul_table, policy->cpu);
return 0; return 0;
err_acpi:
printk(KERN_ERR PFX "No ACPI support for CPU frequency changes.\n");
return -ENODEV;
} }
static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy) static int __devexit longhaul_cpu_exit(struct cpufreq_policy *policy)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment