summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorDave Jones <davej@redhat.com>2005-05-31 19:03:51 -0700
committerDave Jones <davej@redhat.com>2005-05-31 19:03:51 -0700
commit3be6a48f3c8df1b2fbbe57face95a03d167bda1b (patch)
tree8e3eda4b78879c0a10dec89fe0ef1c3aa4158256 /arch
parente131832ca7d3a3e5f9c7624bb310a7747dc2b57c (diff)
downloadop-kernel-dev-3be6a48f3c8df1b2fbbe57face95a03d167bda1b.zip
op-kernel-dev-3be6a48f3c8df1b2fbbe57face95a03d167bda1b.tar.gz
[CPUFREQ] longhaul - disable PCI mastering around transition.
The spec states that we have to do this, which is *horrid*. Based on code from: Ken Staton <ken_staton@agilent.com> Signed-off-by: Dave Jones <davej@redhat.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/i386/kernel/cpu/cpufreq/longhaul.c49
1 files changed, 45 insertions, 4 deletions
diff --git a/arch/i386/kernel/cpu/cpufreq/longhaul.c b/arch/i386/kernel/cpu/cpufreq/longhaul.c
index ab0f9f5..8ea545e 100644
--- a/arch/i386/kernel/cpu/cpufreq/longhaul.c
+++ b/arch/i386/kernel/cpu/cpufreq/longhaul.c
@@ -29,6 +29,7 @@
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/pci.h>
#include <asm/msr.h>
#include <asm/timex.h>
@@ -120,6 +121,11 @@ static void do_powersaver(union msr_longhaul *longhaul,
unsigned int clock_ratio_index)
{
int version;
+ unsigned long flags;
+ struct pci_dev *dev;
+ int i;
+ u16 pci_cmd;
+ u16 cmd_state[64];
switch (cpu_model) {
case CPU_EZRA_T:
@@ -137,17 +143,52 @@ static void do_powersaver(union msr_longhaul *longhaul,
longhaul->bits.SoftBusRatio4 = (clock_ratio_index & 0x10) >> 4;
longhaul->bits.EnableSoftBusRatio = 1;
longhaul->bits.RevisionKey = 0;
- local_irq_disable();
- wrmsrl(MSR_VIA_LONGHAUL, longhaul->val);
+
+ 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);
+
local_irq_enable();
+
+ __hlt();
+ wrmsrl(MSR_VIA_LONGHAUL, longhaul->val);
__hlt();
+ local_irq_disable();
+
+ /* restore pci bus master state for all devices */
+ dev = NULL;
+ 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;
- local_irq_disable();
wrmsrl(MSR_VIA_LONGHAUL, longhaul->val);
- local_irq_enable();
}
/**
OpenPOWER on IntegriCloud