[CPUFREQ] longhaul - disable PCI mastering around transition.
authorDave Jones <davej@redhat.com>
Wed, 1 Jun 2005 02:03:51 +0000 (19:03 -0700)
committerDave Jones <davej@redhat.com>
Wed, 1 Jun 2005 02:03:51 +0000 (19:03 -0700)
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>
arch/i386/kernel/cpu/cpufreq/longhaul.c

index ab0f9f5aac1168075b02b36cf2fdeda1dc23cf2f..8ea545e35b3a57ce7eda144f55eb5fc329162d22 100644 (file)
@@ -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();
 }
 
 /**