Merge branch 'for_linus' of git://git.infradead.org/~dedekind/ubifs-2.6
[pandora-kernel.git] / arch / powerpc / platforms / pseries / eeh.c
index 5f3e6d8..c027f0a 100644 (file)
@@ -1,6 +1,8 @@
 /*
  * eeh.c
- * Copyright (C) 2001 Dave Engebretsen & Todd Inglett IBM Corporation
+ * Copyright IBM Corporation 2001, 2005, 2006
+ * Copyright Dave Engebretsen & Todd Inglett 2001
+ * Copyright Linas Vepstas 2005, 2006
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -15,6 +17,8 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ * Please address comments and feedback to Linas Vepstas <linas@austin.ibm.com>
  */
 
 #include <linux/delay.h>
@@ -25,6 +29,8 @@
 #include <linux/rbtree.h>
 #include <linux/seq_file.h>
 #include <linux/spinlock.h>
+#include <linux/of.h>
+
 #include <asm/atomic.h>
 #include <asm/eeh.h>
 #include <asm/eeh_event.h>
@@ -33,7 +39,6 @@
 #include <asm/ppc-pci.h>
 #include <asm/rtas.h>
 
-#undef DEBUG
 
 /** Overview:
  *  EEH, or "Extended Error Handling" is a PCI bridge technology for
@@ -117,7 +122,6 @@ 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)
@@ -166,6 +170,7 @@ static void rtas_slot_error_detail(struct pci_dn *pdn, int severity,
  */
 static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
 {
+       struct pci_dev *dev = pdn->pcidev;
        u32 cfg;
        int cap, i;
        int n = 0;
@@ -181,8 +186,24 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
        n += scnprintf(buf+n, len-n, "cmd/stat:%x\n", cfg);
        printk(KERN_WARNING "EEH: PCI cmd/status register: %08x\n", cfg);
 
+       if (!dev) {
+               printk(KERN_WARNING "EEH: no PCI device for this of node\n");
+               return n;
+       }
+
+       /* Gather bridge-specific registers */
+       if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) {
+               rtas_read_config(pdn, PCI_SEC_STATUS, 2, &cfg);
+               n += scnprintf(buf+n, len-n, "sec stat:%x\n", cfg);
+               printk(KERN_WARNING "EEH: Bridge secondary status: %04x\n", cfg);
+
+               rtas_read_config(pdn, PCI_BRIDGE_CONTROL, 2, &cfg);
+               n += scnprintf(buf+n, len-n, "brdg ctl:%x\n", cfg);
+               printk(KERN_WARNING "EEH: Bridge control: %04x\n", cfg);
+       }
+
        /* Dump out the PCI-X command and status regs */
-       cap = pci_find_capability(pdn->pcidev, PCI_CAP_ID_PCIX);
+       cap = pci_find_capability(dev, PCI_CAP_ID_PCIX);
        if (cap) {
                rtas_read_config(pdn, cap, 4, &cfg);
                n += scnprintf(buf+n, len-n, "pcix-cmd:%x\n", cfg);
@@ -194,7 +215,7 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
        }
 
        /* If PCI-E capable, dump PCI-E cap 10, and the AER */
-       cap = pci_find_capability(pdn->pcidev, PCI_CAP_ID_EXP);
+       cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
        if (cap) {
                n += scnprintf(buf+n, len-n, "pci-e cap10:\n");
                printk(KERN_WARNING
@@ -206,7 +227,7 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
                        printk(KERN_WARNING "EEH: PCI-E %02x: %08x\n", i, cfg);
                }
 
-               cap = pci_find_ext_capability(pdn->pcidev,PCI_EXT_CAP_ID_ERR);
+               cap = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
                if (cap) {
                        n += scnprintf(buf+n, len-n, "pci-e AER:\n");
                        printk(KERN_WARNING
@@ -219,6 +240,18 @@ static size_t gather_pci_data(struct pci_dn *pdn, char * buf, size_t len)
                        }
                }
        }
+
+       /* Gather status on devices under the bridge */
+       if (dev->class >> 16 == PCI_BASE_CLASS_BRIDGE) {
+               struct device_node *dn;
+
+               for_each_child_of_node(pdn->node, dn) {
+                       pdn = PCI_DN(dn);
+                       if (pdn)
+                               n += gather_pci_data(pdn, buf+n, len-n);
+               }
+       }
+
        return n;
 }
 
@@ -290,7 +323,7 @@ eeh_wait_for_slot_status(struct pci_dn *pdn, int max_wait_msecs)
 
                if (rets[2] == 0) return -1; /* permanently unavailable */
 
-               if (max_wait_msecs <= 0) return -1;
+               if (max_wait_msecs <= 0) break;
 
                mwait = rets[2];
                if (mwait <= 0) {
@@ -339,7 +372,7 @@ struct device_node * find_device_pe(struct device_node *dn)
        return dn;
 }
 
-/** Mark all devices that are peers of this device as failed.
+/** Mark all devices that are children of this device as failed.
  *  Mark the device driver too, so that it can see the failure
  *  immediately; this is critical, since some drivers poll
  *  status registers in interrupts ... If a driver is polling,
@@ -347,9 +380,11 @@ struct device_node * find_device_pe(struct device_node *dn)
  *  an interrupt context, which is bad.
  */
 
-static void __eeh_mark_slot (struct device_node *dn, int mode_flag)
+static void __eeh_mark_slot(struct device_node *parent, int mode_flag)
 {
-       while (dn) {
+       struct device_node *dn;
+
+       for_each_child_of_node(parent, dn) {
                if (PCI_DN(dn)) {
                        /* Mark the pci device driver too */
                        struct pci_dev *dev = PCI_DN(dn)->pcidev;
@@ -359,10 +394,8 @@ static void __eeh_mark_slot (struct device_node *dn, int mode_flag)
                        if (dev && dev->driver)
                                dev->error_state = pci_channel_io_frozen;
 
-                       if (dn->child)
-                               __eeh_mark_slot (dn->child, mode_flag);
+                       __eeh_mark_slot(dn, mode_flag);
                }
-               dn = dn->sibling;
        }
 }
 
@@ -382,19 +415,19 @@ void eeh_mark_slot (struct device_node *dn, int mode_flag)
        if (dev)
                dev->error_state = pci_channel_io_frozen;
 
-       __eeh_mark_slot (dn->child, mode_flag);
+       __eeh_mark_slot(dn, mode_flag);
 }
 
-static void __eeh_clear_slot (struct device_node *dn, int mode_flag)
+static void __eeh_clear_slot(struct device_node *parent, int mode_flag)
 {
-       while (dn) {
+       struct device_node *dn;
+
+       for_each_child_of_node(parent, dn) {
                if (PCI_DN(dn)) {
                        PCI_DN(dn)->eeh_mode &= ~mode_flag;
                        PCI_DN(dn)->eeh_check_count = 0;
-                       if (dn->child)
-                               __eeh_clear_slot (dn->child, mode_flag);
+                       __eeh_clear_slot(dn, mode_flag);
                }
-               dn = dn->sibling;
        }
 }
 
@@ -411,7 +444,7 @@ void eeh_clear_slot (struct device_node *dn, int mode_flag)
 
        PCI_DN(dn)->eeh_mode &= ~mode_flag;
        PCI_DN(dn)->eeh_check_count = 0;
-       __eeh_clear_slot (dn->child, mode_flag);
+       __eeh_clear_slot(dn, mode_flag);
        spin_unlock_irqrestore(&confirm_error_lock, flags);
 }
 
@@ -447,6 +480,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
                no_dn++;
                return 0;
        }
+       dn = find_device_pe(dn);
        pdn = PCI_DN(dn);
 
        /* Access to IO BARs might get this far and still not want checking. */
@@ -505,14 +539,16 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
                printk(KERN_WARNING "EEH: read_slot_reset_state() failed; rc=%d dn=%s\n",
                       ret, dn->full_name);
                false_positives++;
+               pdn->eeh_false_positives ++;
                rc = 0;
                goto dn_unlock;
        }
 
        /* Note that config-io to empty slots may fail;
         * they are empty when they don't have children. */
-       if ((rets[0] == 5) && (dn->child == NULL)) {
+       if ((rets[0] == 5) && (rets[2] == 0) && (dn->child == NULL)) {
                false_positives++;
+               pdn->eeh_false_positives ++;
                rc = 0;
                goto dn_unlock;
        }
@@ -522,6 +558,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
                printk(KERN_WARNING "EEH: event on unsupported device, rc=%d dn=%s\n",
                       ret, dn->full_name);
                false_positives++;
+               pdn->eeh_false_positives ++;
                rc = 0;
                goto dn_unlock;
        }
@@ -529,6 +566,7 @@ int eeh_dn_check_failure(struct device_node *dn, struct pci_dev *dev)
        /* If not the kind of error we know about, punt. */
        if (rets[0] != 1 && rets[0] != 2 && rets[0] != 4 && rets[0] != 5) {
                false_positives++;
+               pdn->eeh_false_positives ++;
                rc = 0;
                goto dn_unlock;
        }
@@ -743,12 +781,12 @@ int rtas_set_slot_reset(struct pci_dn *pdn)
                        return 0;
 
                if (rc < 0) {
-                       printk (KERN_ERR "EEH: unrecoverable slot failure %s\n",
-                               pdn->node->full_name);
+                       printk(KERN_ERR "EEH: unrecoverable slot failure %s\n",
+                              pdn->node->full_name);
                        return -1;
                }
-               printk (KERN_ERR "EEH: bus reset %d failed on slot %s\n",
-                       i+1, pdn->node->full_name);
+               printk(KERN_ERR "EEH: bus reset %d failed on slot %s, rc=%d\n",
+                      i+1, pdn->node->full_name, rc);
        }
 
        return -1;
@@ -774,6 +812,7 @@ int rtas_set_slot_reset(struct pci_dn *pdn)
 static inline void __restore_bars (struct pci_dn *pdn)
 {
        int i;
+       u32 cmd;
 
        if (NULL==pdn->phb) return;
        for (i=4; i<10; i++) {
@@ -794,6 +833,19 @@ static inline void __restore_bars (struct pci_dn *pdn)
 
        /* max latency, min grant, interrupt pin and line */
        rtas_write_config(pdn, 15*4, 4, pdn->config_space[15]);
+
+       /* Restore PERR & SERR bits, some devices require it,
+          don't touch the other command bits */
+       rtas_read_config(pdn, PCI_COMMAND, 4, &cmd);
+       if (pdn->config_space[1] & PCI_COMMAND_PARITY)
+               cmd |= PCI_COMMAND_PARITY;
+       else
+               cmd &= ~PCI_COMMAND_PARITY;
+       if (pdn->config_space[1] & PCI_COMMAND_SERR)
+               cmd |= PCI_COMMAND_SERR;
+       else
+               cmd &= ~PCI_COMMAND_SERR;
+       rtas_write_config(pdn, PCI_COMMAND, 4, cmd);
 }
 
 /**
@@ -811,11 +863,8 @@ void eeh_restore_bars(struct pci_dn *pdn)
        if ((pdn->eeh_mode & EEH_MODE_SUPPORTED) && !IS_BRIDGE(pdn->class_code))
                __restore_bars (pdn);
 
-       dn = pdn->node->child;
-       while (dn) {
+       for_each_child_of_node(pdn->node, dn)
                eeh_restore_bars (PCI_DN(dn));
-               dn = dn->sibling;
-       }
 }
 
 /**
@@ -909,7 +958,6 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
        unsigned int rets[3];
        struct eeh_early_enable_info *info = data;
        int ret;
-       const char *status = of_get_property(dn, "status", NULL);
        const u32 *class_code = of_get_property(dn, "class-code", NULL);
        const u32 *vendor_id = of_get_property(dn, "vendor-id", NULL);
        const u32 *device_id = of_get_property(dn, "device-id", NULL);
@@ -921,9 +969,10 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
        pdn->eeh_mode = 0;
        pdn->eeh_check_count = 0;
        pdn->eeh_freeze_count = 0;
+       pdn->eeh_false_positives = 0;
 
-       if (status && strcmp(status, "ok") != 0)
-               return NULL;    /* ignore devices with bad status */
+       if (!of_device_is_available(dn))
+               return NULL;
 
        /* Ignore bad nodes. */
        if (!class_code || !vendor_id || !device_id)
@@ -936,23 +985,6 @@ static void *early_enable_eeh(struct device_node *dn, void *data)
        }
        pdn->class_code = *class_code;
 
-       /*
-        * Now decide if we are going to "Disable" EEH checking
-        * for this device.  We still run with the EEH hardware active,
-        * but we won't be checking for ff's.  This means a driver
-        * could return bad data (very bad!), an interrupt handler could
-        * hang waiting on status bits that won't change, etc.
-        * But there are a few cases like display devices that make sense.
-        */
-       enable = 1;     /* i.e. we will do checking */
-#if 0
-       if ((*class_code >> 16) == PCI_BASE_CLASS_DISPLAY)
-               enable = 0;
-#endif
-
-       if (!enable)
-               pdn->eeh_mode |= EEH_MODE_NOCHECK;
-
        /* Ok... see if this device supports EEH.  Some do, some don't,
         * and the only way to find out is to check each and every one. */
        regs = of_get_property(dn, "reg", NULL);
@@ -1109,7 +1141,8 @@ static void eeh_add_device_early(struct device_node *dn)
 void eeh_add_device_tree_early(struct device_node *dn)
 {
        struct device_node *sib;
-       for (sib = dn->child; sib; sib = sib->sibling)
+
+       for_each_child_of_node(dn, sib)
                eeh_add_device_tree_early(sib);
        eeh_add_device_early(dn);
 }
@@ -1139,7 +1172,8 @@ static void eeh_add_device_late(struct pci_dev *dev)
        pdn = PCI_DN(dn);
        pdn->pcidev = dev;
 
-       pci_addr_cache_insert_device (dev);
+       pci_addr_cache_insert_device(dev);
+       eeh_sysfs_add_device(dev);
 }
 
 void eeh_add_device_tree_late(struct pci_bus *bus)
@@ -1178,6 +1212,7 @@ static void eeh_remove_device(struct pci_dev *dev)
        printk(KERN_DEBUG "EEH: remove device %s\n", pci_name(dev));
 #endif
        pci_addr_cache_remove_device(dev);
+       eeh_sysfs_remove_device(dev);
 
        dn = pci_device_to_OF_node(dev);
        if (PCI_DN(dn)->pcidev) {
@@ -1214,11 +1249,10 @@ static int proc_eeh_show(struct seq_file *m, void *v)
                                "check not wanted=%ld\n"
                                "eeh_total_mmio_ffs=%ld\n"
                                "eeh_false_positives=%ld\n"
-                               "eeh_ignored_failures=%ld\n"
                                "eeh_slot_resets=%ld\n",
                                no_device, no_dn, no_cfg_addr, 
                                ignored_check, total_mmio_ffs, 
-                               false_positives, ignored_failures, 
+                               false_positives,
                                slot_resets);
        }
 
@@ -1239,14 +1273,8 @@ static const struct file_operations proc_eeh_operations = {
 
 static int __init eeh_init_proc(void)
 {
-       struct proc_dir_entry *e;
-
-       if (machine_is(pseries)) {
-               e = create_proc_entry("ppc64/eeh", 0, NULL);
-               if (e)
-                       e->proc_fops = &proc_eeh_operations;
-       }
-
+       if (machine_is(pseries))
+               proc_create("ppc64/eeh", 0, NULL, &proc_eeh_operations);
        return 0;
 }
 __initcall(eeh_init_proc);