]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/powerpc/platforms/pseries/eeh.c
Merge with git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git
[linux-2.6-omap-h63xx.git] / arch / powerpc / platforms / pseries / eeh.c
index b0fa76d0c78ace1708a2bf6f2de15bc63b698f83..2ab9dcdfb41579704c55d8c1a4ddc918e832d6ee 100644 (file)
@@ -97,14 +97,16 @@ static DEFINE_SPINLOCK(slot_errbuf_lock);
 static int eeh_error_buf_size;
 
 /* System monitoring statistics */
-static DEFINE_PER_CPU(unsigned long, no_device);
-static DEFINE_PER_CPU(unsigned long, no_dn);
-static DEFINE_PER_CPU(unsigned long, no_cfg_addr);
-static DEFINE_PER_CPU(unsigned long, ignored_check);
-static DEFINE_PER_CPU(unsigned long, total_mmio_ffs);
-static DEFINE_PER_CPU(unsigned long, false_positives);
-static DEFINE_PER_CPU(unsigned long, ignored_failures);
-static DEFINE_PER_CPU(unsigned long, slot_resets);
+static unsigned long no_device;
+static unsigned long no_dn;
+static unsigned long no_cfg_addr;
+static unsigned long ignored_check;
+static unsigned long total_mmio_ffs;
+static unsigned long false_positives;
+static unsigned long ignored_failures;
+static unsigned long slot_resets;
+
+#define IS_BRIDGE(class_code) (((class_code)<<16) == PCI_BASE_CLASS_BRIDGE)
 
 /* --------------------------------------------------------------- */
 /* Below lies the EEH event infrastructure */
@@ -206,10 +208,11 @@ static void __eeh_mark_slot (struct device_node *dn, int mode_flag)
 {
        while (dn) {
                if (PCI_DN(dn)) {
-                       PCI_DN(dn)->eeh_mode |= mode_flag;
-
                        /* Mark the pci device driver too */
                        struct pci_dev *dev = PCI_DN(dn)->pcidev;
+
+                       PCI_DN(dn)->eeh_mode |= mode_flag;
+
                        if (dev && dev->driver)
                                dev->error_state = pci_channel_io_frozen;
 
@@ -223,6 +226,11 @@ static void __eeh_mark_slot (struct device_node *dn, int mode_flag)
 void eeh_mark_slot (struct device_node *dn, int mode_flag)
 {
        dn = find_device_pe (dn);
+
+       /* Back up one, since config addrs might be shared */
+       if (PCI_DN(dn) && PCI_DN(dn)->eeh_pe_config_addr)
+               dn = dn->parent;
+
        PCI_DN(dn)->eeh_mode |= mode_flag;
        __eeh_mark_slot (dn->child, mode_flag);
 }
@@ -244,7 +252,13 @@ void eeh_clear_slot (struct device_node *dn, int mode_flag)
 {
        unsigned long flags;
        spin_lock_irqsave(&confirm_error_lock, flags);
+       
        dn = find_device_pe (dn);
+       
+       /* Back up one, since config addrs might be shared */
+       if (PCI_DN(dn) && PCI_DN(dn)->eeh_pe_config_addr)
+               dn = dn->parent;
+
        PCI_DN(dn)->eeh_mode &= ~mode_flag;
        PCI_DN(dn)->eeh_check_count = 0;
        __eeh_clear_slot (dn->child, mode_flag);
@@ -275,13 +289,13 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
        enum pci_channel_state state;
        int rc = 0;
 
-       __get_cpu_var(total_mmio_ffs)++;
+       total_mmio_ffs++;
 
        if (!eeh_subsystem_enabled)
                return 0;
 
        if (!dn) {
-               __get_cpu_var(no_dn)++;
+               no_dn++;
                return 0;
        }
        pdn = PCI_DN(dn);
@@ -289,7 +303,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
        /* Access to IO BARs might get this far and still not want checking. */
        if (!(pdn->eeh_mode & EEH_MODE_SUPPORTED) ||
            pdn->eeh_mode & EEH_MODE_NOCHECK) {
-               __get_cpu_var(ignored_check)++;
+               ignored_check++;
 #ifdef DEBUG
                printk ("EEH:ignored check (%x) for %s %s\n", 
                        pdn->eeh_mode, pci_name (dev), dn->full_name);
@@ -298,7 +312,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
        }
 
        if (!pdn->eeh_config_addr && !pdn->eeh_pe_config_addr) {
-               __get_cpu_var(no_cfg_addr)++;
+               no_cfg_addr++;
                return 0;
        }
 
@@ -340,7 +354,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
        if (ret != 0) {
                printk(KERN_WARNING "EEH: read_slot_reset_state() failed; rc=%d dn=%s\n",
                       ret, dn->full_name);
-               __get_cpu_var(false_positives)++;
+               false_positives++;
                rc = 0;
                goto dn_unlock;
        }
@@ -349,14 +363,14 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
        if (rets[1] != 1) {
                printk(KERN_WARNING "EEH: event on unsupported device, rc=%d dn=%s\n",
                       ret, dn->full_name);
-               __get_cpu_var(false_positives)++;
+               false_positives++;
                rc = 0;
                goto dn_unlock;
        }
 
        /* If not the kind of error we know about, punt. */
        if (rets[0] != 2 && rets[0] != 4 && rets[0] != 5) {
-               __get_cpu_var(false_positives)++;
+               false_positives++;
                rc = 0;
                goto dn_unlock;
        }
@@ -364,12 +378,12 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
        /* Note that config-io to empty slots may fail;
         * we recognize empty because they don't have children. */
        if ((rets[0] == 5) && (dn->child == NULL)) {
-               __get_cpu_var(false_positives)++;
+               false_positives++;
                rc = 0;
                goto dn_unlock;
        }
 
-       __get_cpu_var(slot_resets)++;
+       slot_resets++;
  
        /* Avoid repeated reports of this failure, including problems
         * with other functions on this device, and functions under
@@ -419,7 +433,7 @@ unsigned long eeh_check_failure(const volatile void __iomem *token, unsigned lon
        addr = eeh_token_to_phys((unsigned long __force) token);
        dev = pci_get_device_by_addr(addr);
        if (!dev) {
-               __get_cpu_var(no_device)++;
+               no_device++;
                return val;
        }
 
@@ -609,7 +623,7 @@ void eeh_restore_bars(struct pci_dn *pdn)
        if (!pdn) 
                return;
        
-       if (! pdn->eeh_is_bridge)
+       if ((pdn->eeh_mode & EEH_MODE_SUPPORTED) && !IS_BRIDGE(pdn->class_code))
                __restore_bars (pdn);
 
        dn = pdn->node->child;
@@ -627,18 +641,15 @@ void eeh_restore_bars(struct pci_dn *pdn)
  * PCI devices are added individuallly; but, for the restore,
  * an entire slot is reset at a time.
  */
-void eeh_save_bars(struct pci_dev * pdev, struct pci_dn *pdn)
+static void eeh_save_bars(struct pci_dn *pdn)
 {
        int i;
 
-       if (!pdev || !pdn )
+       if (!pdn )
                return;
        
        for (i = 0; i < 16; i++)
-               pci_read_config_dword(pdev, i * 4, &pdn->config_space[i]);
-
-       if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE)
-               pdn->eeh_is_bridge = 1;
+               rtas_read_config(pdn, i * 4, 4, &pdn->config_space[i]);
 }
 
 void
@@ -688,6 +699,7 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
        int enable;
        struct pci_dn *pdn = PCI_DN(dn);
 
+       pdn->class_code = 0;
        pdn->eeh_mode = 0;
        pdn->eeh_check_count = 0;
        pdn->eeh_freeze_count = 0;
@@ -704,6 +716,7 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
                pdn->eeh_mode |= EEH_MODE_NOCHECK;
                return NULL;
        }
+       pdn->class_code = *class_code;
 
        /*
         * Now decide if we are going to "Disable" EEH checking
@@ -770,6 +783,7 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
                       dn->full_name);
        }
 
+       eeh_save_bars(pdn);
        return NULL;
 }
 
@@ -879,6 +893,20 @@ void eeh_add_device_tree_early(struct device_node *dn)
 }
 EXPORT_SYMBOL_GPL(eeh_add_device_tree_early);
 
+void eeh_add_device_tree_late(struct pci_bus *bus)
+{
+       struct pci_dev *dev;
+
+       list_for_each_entry(dev, &bus->devices, bus_list) {
+               eeh_add_device_late(dev);
+               if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+                       struct pci_bus *subbus = dev->subordinate;
+                       if (subbus)
+                               eeh_add_device_tree_late(subbus);
+               }
+       }
+}
+
 /**
  * eeh_add_device_late - perform EEH initialization for the indicated pci device
  * @dev: pci device for which to set up EEH
@@ -904,7 +932,6 @@ void eeh_add_device_late(struct pci_dev *dev)
        pdn->pcidev = dev;
 
        pci_addr_cache_insert_device (dev);
-       eeh_save_bars(dev, pdn);
 }
 EXPORT_SYMBOL_GPL(eeh_add_device_late);
 
@@ -952,25 +979,9 @@ EXPORT_SYMBOL_GPL(eeh_remove_bus_device);
 
 static int proc_eeh_show(struct seq_file *m, void *v)
 {
-       unsigned int cpu;
-       unsigned long ffs = 0, positives = 0, failures = 0;
-       unsigned long resets = 0;
-       unsigned long no_dev = 0, no_dn = 0, no_cfg = 0, no_check = 0;
-
-       for_each_cpu(cpu) {
-               ffs += per_cpu(total_mmio_ffs, cpu);
-               positives += per_cpu(false_positives, cpu);
-               failures += per_cpu(ignored_failures, cpu);
-               resets += per_cpu(slot_resets, cpu);
-               no_dev += per_cpu(no_device, cpu);
-               no_dn += per_cpu(no_dn, cpu);
-               no_cfg += per_cpu(no_cfg_addr, cpu);
-               no_check += per_cpu(ignored_check, cpu);
-       }
-
        if (0 == eeh_subsystem_enabled) {
                seq_printf(m, "EEH Subsystem is globally disabled\n");
-               seq_printf(m, "eeh_total_mmio_ffs=%ld\n", ffs);
+               seq_printf(m, "eeh_total_mmio_ffs=%ld\n", total_mmio_ffs);
        } else {
                seq_printf(m, "EEH Subsystem is enabled\n");
                seq_printf(m,
@@ -982,8 +993,10 @@ static int proc_eeh_show(struct seq_file *m, void *v)
                                "eeh_false_positives=%ld\n"
                                "eeh_ignored_failures=%ld\n"
                                "eeh_slot_resets=%ld\n",
-                               no_dev, no_dn, no_cfg, no_check,
-                               ffs, positives, failures, resets);
+                               no_device, no_dn, no_cfg_addr, 
+                               ignored_check, total_mmio_ffs, 
+                               false_positives, ignored_failures, 
+                               slot_resets);
        }
 
        return 0;