[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: Rafa Bilski <rafalbilski@interia.pl> Signed-off-by: Dave Jones <davej@redhat.com>
This commit is contained in:
parent
9c9a43ed27
commit
dadb49d874
@ -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 @@
|
|||||||
#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;
|
|||||||
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)
|
|||||||
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);
|
|
||||||
longhaul->bits.SoftBusRatio = clock_ratio_index & 0xf;
|
|
||||||
longhaul->bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
|
|
||||||
longhaul->bits.EnableSoftBusRatio = 1;
|
|
||||||
longhaul->bits.RevisionKey = 0;
|
|
||||||
|
|
||||||
preempt_disable();
|
|
||||||
local_irq_save(flags);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* get current pci bus master state for all devices
|
|
||||||
* and clear bus master bit
|
|
||||||
*/
|
|
||||||
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. */
|
|
||||||
outb(0xFE,0x21); /* TMR0 only */
|
|
||||||
outb(0xFF,0x80); /* delay */
|
|
||||||
|
|
||||||
|
/* 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_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();
|
local_irq_disable();
|
||||||
|
rdmsrl(MSR_VIA_BCR2, bcr2.val);
|
||||||
|
bcr2.bits.ESOFTBF = 0;
|
||||||
|
wrmsrl(MSR_VIA_BCR2, bcr2.val);
|
||||||
|
}
|
||||||
|
|
||||||
outb(tmp_mask,0x21); /* restore mask */
|
/* For processor with Longhaul MSR */
|
||||||
|
|
||||||
/* restore pci bus master state for all devices */
|
static void do_powersaver(int cx_address, unsigned int clock_ratio_index)
|
||||||
dev = NULL;
|
{
|
||||||
i = 0;
|
union msr_longhaul longhaul;
|
||||||
do {
|
u32 t;
|
||||||
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);
|
||||||
rdmsrl(MSR_VIA_LONGHAUL, longhaul->val);
|
longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
|
||||||
longhaul->bits.EnableSoftBusRatio = 0;
|
longhaul.bits.SoftBusRatio = clock_ratio_index & 0xf;
|
||||||
longhaul->bits.RevisionKey = version;
|
longhaul.bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
|
||||||
wrmsrl(MSR_VIA_LONGHAUL, longhaul->val);
|
|
||||||
|
/* Sync to timer tick */
|
||||||
|
safe_halt();
|
||||||
|
ACPI_FLUSH_CPU_CACHE();
|
||||||
|
/* 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();
|
||||||
|
longhaul.bits.RevisionKey = longhaul.bits.RevisionID;
|
||||||
|
longhaul.bits.EnableSoftBusRatio = 0;
|
||||||
|
longhaul.bits.EnableSoftBSEL = 0;
|
||||||
|
longhaul.bits.EnableSoftVID = 0;
|
||||||
|
wrmsrl(MSR_VIA_LONGHAUL, longhaul.val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -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)
|
|||||||
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)
|
|||||||
*/
|
*/
|
||||||
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)
|
|||||||
* 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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user