powerpc/powernv: Drop PHB operation configure_bridge()
[deliverable/linux.git] / arch / powerpc / platforms / powernv / eeh-powernv.c
index e261869adc86d588ff620d7a6b4b5089db0fe553..a7087f4a739a5ec96a20015cc768202b64d5b002 100644 (file)
@@ -12,6 +12,7 @@
  */
 
 #include <linux/atomic.h>
+#include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/export.h>
 #include <linux/init.h>
 #include "powernv.h"
 #include "pci.h"
 
+static bool pnv_eeh_nb_init = false;
+
 /**
- * powernv_eeh_init - EEH platform dependent initialization
+ * pnv_eeh_init - EEH platform dependent initialization
  *
  * EEH platform dependent initialization on powernv
  */
-static int powernv_eeh_init(void)
+static int pnv_eeh_init(void)
 {
        struct pci_controller *hose;
        struct pnv_phb *phb;
@@ -85,35 +88,206 @@ static int powernv_eeh_init(void)
        return 0;
 }
 
+static int pnv_eeh_event(struct notifier_block *nb,
+                        unsigned long events, void *change)
+{
+       uint64_t changed_evts = (uint64_t)change;
+
+       /*
+        * We simply send special EEH event if EEH has
+        * been enabled, or clear pending events in
+        * case that we enable EEH soon
+        */
+       if (!(changed_evts & OPAL_EVENT_PCI_ERROR) ||
+           !(events & OPAL_EVENT_PCI_ERROR))
+               return 0;
+
+       if (eeh_enabled())
+               eeh_send_failure_event(NULL);
+       else
+               opal_notifier_update_evt(OPAL_EVENT_PCI_ERROR, 0x0ul);
+
+       return 0;
+}
+
+static struct notifier_block pnv_eeh_nb = {
+       .notifier_call  = pnv_eeh_event,
+       .next           = NULL,
+       .priority       = 0
+};
+
+#ifdef CONFIG_DEBUG_FS
+static ssize_t pnv_eeh_ei_write(struct file *filp,
+                               const char __user *user_buf,
+                               size_t count, loff_t *ppos)
+{
+       struct pci_controller *hose = filp->private_data;
+       struct eeh_dev *edev;
+       struct eeh_pe *pe;
+       int pe_no, type, func;
+       unsigned long addr, mask;
+       char buf[50];
+       int ret;
+
+       if (!eeh_ops || !eeh_ops->err_inject)
+               return -ENXIO;
+
+       /* Copy over argument buffer */
+       ret = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count);
+       if (!ret)
+               return -EFAULT;
+
+       /* Retrieve parameters */
+       ret = sscanf(buf, "%x:%x:%x:%lx:%lx",
+                    &pe_no, &type, &func, &addr, &mask);
+       if (ret != 5)
+               return -EINVAL;
+
+       /* Retrieve PE */
+       edev = kzalloc(sizeof(*edev), GFP_KERNEL);
+       if (!edev)
+               return -ENOMEM;
+       edev->phb = hose;
+       edev->pe_config_addr = pe_no;
+       pe = eeh_pe_get(edev);
+       kfree(edev);
+       if (!pe)
+               return -ENODEV;
+
+       /* Do error injection */
+       ret = eeh_ops->err_inject(pe, type, func, addr, mask);
+       return ret < 0 ? ret : count;
+}
+
+static const struct file_operations pnv_eeh_ei_fops = {
+       .open   = simple_open,
+       .llseek = no_llseek,
+       .write  = pnv_eeh_ei_write,
+};
+
+static int pnv_eeh_dbgfs_set(void *data, int offset, u64 val)
+{
+       struct pci_controller *hose = data;
+       struct pnv_phb *phb = hose->private_data;
+
+       out_be64(phb->regs + offset, val);
+       return 0;
+}
+
+static int pnv_eeh_dbgfs_get(void *data, int offset, u64 *val)
+{
+       struct pci_controller *hose = data;
+       struct pnv_phb *phb = hose->private_data;
+
+       *val = in_be64(phb->regs + offset);
+       return 0;
+}
+
+static int pnv_eeh_outb_dbgfs_set(void *data, u64 val)
+{
+       return pnv_eeh_dbgfs_set(data, 0xD10, val);
+}
+
+static int pnv_eeh_outb_dbgfs_get(void *data, u64 *val)
+{
+       return pnv_eeh_dbgfs_get(data, 0xD10, val);
+}
+
+static int pnv_eeh_inbA_dbgfs_set(void *data, u64 val)
+{
+       return pnv_eeh_dbgfs_set(data, 0xD90, val);
+}
+
+static int pnv_eeh_inbA_dbgfs_get(void *data, u64 *val)
+{
+       return pnv_eeh_dbgfs_get(data, 0xD90, val);
+}
+
+static int pnv_eeh_inbB_dbgfs_set(void *data, u64 val)
+{
+       return pnv_eeh_dbgfs_set(data, 0xE10, val);
+}
+
+static int pnv_eeh_inbB_dbgfs_get(void *data, u64 *val)
+{
+       return pnv_eeh_dbgfs_get(data, 0xE10, val);
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(pnv_eeh_outb_dbgfs_ops, pnv_eeh_outb_dbgfs_get,
+                       pnv_eeh_outb_dbgfs_set, "0x%llx\n");
+DEFINE_SIMPLE_ATTRIBUTE(pnv_eeh_inbA_dbgfs_ops, pnv_eeh_inbA_dbgfs_get,
+                       pnv_eeh_inbA_dbgfs_set, "0x%llx\n");
+DEFINE_SIMPLE_ATTRIBUTE(pnv_eeh_inbB_dbgfs_ops, pnv_eeh_inbB_dbgfs_get,
+                       pnv_eeh_inbB_dbgfs_set, "0x%llx\n");
+#endif /* CONFIG_DEBUG_FS */
+
 /**
- * powernv_eeh_post_init - EEH platform dependent post initialization
+ * pnv_eeh_post_init - EEH platform dependent post initialization
  *
  * EEH platform dependent post initialization on powernv. When
  * the function is called, the EEH PEs and devices should have
  * been built. If the I/O cache staff has been built, EEH is
  * ready to supply service.
  */
-static int powernv_eeh_post_init(void)
+static int pnv_eeh_post_init(void)
 {
        struct pci_controller *hose;
        struct pnv_phb *phb;
        int ret = 0;
 
+       /* Register OPAL event notifier */
+       if (!pnv_eeh_nb_init) {
+               ret = opal_notifier_register(&pnv_eeh_nb);
+               if (ret) {
+                       pr_warn("%s: Can't register OPAL event notifier (%d)\n",
+                               __func__, ret);
+                       return ret;
+               }
+
+               pnv_eeh_nb_init = true;
+       }
+
        list_for_each_entry(hose, &hose_list, list_node) {
                phb = hose->private_data;
 
-               if (phb->eeh_ops && phb->eeh_ops->post_init) {
-                       ret = phb->eeh_ops->post_init(hose);
-                       if (ret)
-                               break;
-               }
+               /*
+                * If EEH is enabled, we're going to rely on that.
+                * Otherwise, we restore to conventional mechanism
+                * to clear frozen PE during PCI config access.
+                */
+               if (eeh_enabled())
+                       phb->flags |= PNV_PHB_FLAG_EEH;
+               else
+                       phb->flags &= ~PNV_PHB_FLAG_EEH;
+
+               /* Create debugfs entries */
+#ifdef CONFIG_DEBUG_FS
+               if (phb->has_dbgfs || !phb->dbgfs)
+                       continue;
+
+               phb->has_dbgfs = 1;
+               debugfs_create_file("err_injct", 0200,
+                                   phb->dbgfs, hose,
+                                   &pnv_eeh_ei_fops);
+
+               debugfs_create_file("err_injct_outbound", 0600,
+                                   phb->dbgfs, hose,
+                                   &pnv_eeh_outb_dbgfs_ops);
+               debugfs_create_file("err_injct_inboundA", 0600,
+                                   phb->dbgfs, hose,
+                                   &pnv_eeh_inbA_dbgfs_ops);
+               debugfs_create_file("err_injct_inboundB", 0600,
+                                   phb->dbgfs, hose,
+                                   &pnv_eeh_inbB_dbgfs_ops);
+#endif /* CONFIG_DEBUG_FS */
        }
 
+
        return ret;
 }
 
 /**
- * powernv_eeh_dev_probe - Do probe on PCI device
+ * pnv_eeh_dev_probe - Do probe on PCI device
  * @dev: PCI device
  * @flag: unused
  *
@@ -129,7 +303,7 @@ static int powernv_eeh_post_init(void)
  * was possiblly triggered by EEH core, the binding between EEH device
  * and the PCI device isn't built yet.
  */
-static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag)
+static int pnv_eeh_dev_probe(struct pci_dev *dev, void *flag)
 {
        struct pci_controller *hose = pci_bus_to_host(dev->bus);
        struct pnv_phb *phb = hose->private_data;
@@ -221,7 +395,7 @@ static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag)
 }
 
 /**
- * powernv_eeh_set_option - Initialize EEH or MMIO/DMA reenable
+ * pnv_eeh_set_option - Initialize EEH or MMIO/DMA reenable
  * @pe: EEH PE
  * @option: operation to be issued
  *
@@ -229,7 +403,7 @@ static int powernv_eeh_dev_probe(struct pci_dev *dev, void *flag)
  * Currently, following options are support according to PAPR:
  * Enable EEH, Disable EEH, Enable MMIO and Enable DMA
  */
-static int powernv_eeh_set_option(struct eeh_pe *pe, int option)
+static int pnv_eeh_set_option(struct eeh_pe *pe, int option)
 {
        struct pci_controller *hose = pe->phb;
        struct pnv_phb *phb = hose->private_data;
@@ -246,19 +420,19 @@ static int powernv_eeh_set_option(struct eeh_pe *pe, int option)
 }
 
 /**
- * powernv_eeh_get_pe_addr - Retrieve PE address
+ * pnv_eeh_get_pe_addr - Retrieve PE address
  * @pe: EEH PE
  *
  * Retrieve the PE address according to the given tranditional
  * PCI BDF (Bus/Device/Function) address.
  */
-static int powernv_eeh_get_pe_addr(struct eeh_pe *pe)
+static int pnv_eeh_get_pe_addr(struct eeh_pe *pe)
 {
        return pe->addr;
 }
 
 /**
- * powernv_eeh_get_state - Retrieve PE state
+ * pnv_eeh_get_state - Retrieve PE state
  * @pe: EEH PE
  * @delay: delay while PE state is temporarily unavailable
  *
@@ -267,7 +441,7 @@ static int powernv_eeh_get_pe_addr(struct eeh_pe *pe)
  * we prefer passing down to hardware implementation to handle
  * it.
  */
-static int powernv_eeh_get_state(struct eeh_pe *pe, int *delay)
+static int pnv_eeh_get_state(struct eeh_pe *pe, int *delay)
 {
        struct pci_controller *hose = pe->phb;
        struct pnv_phb *phb = hose->private_data;
@@ -292,13 +466,13 @@ static int powernv_eeh_get_state(struct eeh_pe *pe, int *delay)
 }
 
 /**
- * powernv_eeh_reset - Reset the specified PE
+ * pnv_eeh_reset - Reset the specified PE
  * @pe: EEH PE
  * @option: reset option
  *
  * Reset the specified PE
  */
-static int powernv_eeh_reset(struct eeh_pe *pe, int option)
+static int pnv_eeh_reset(struct eeh_pe *pe, int option)
 {
        struct pci_controller *hose = pe->phb;
        struct pnv_phb *phb = hose->private_data;
@@ -311,20 +485,20 @@ static int powernv_eeh_reset(struct eeh_pe *pe, int option)
 }
 
 /**
- * powernv_eeh_wait_state - Wait for PE state
+ * pnv_eeh_wait_state - Wait for PE state
  * @pe: EEH PE
  * @max_wait: maximal period in microsecond
  *
  * Wait for the state of associated PE. It might take some time
  * to retrieve the PE's state.
  */
-static int powernv_eeh_wait_state(struct eeh_pe *pe, int max_wait)
+static int pnv_eeh_wait_state(struct eeh_pe *pe, int max_wait)
 {
        int ret;
        int mwait;
 
        while (1) {
-               ret = powernv_eeh_get_state(pe, &mwait);
+               ret = pnv_eeh_get_state(pe, &mwait);
 
                /*
                 * If the PE's state is temporarily unavailable,
@@ -348,7 +522,7 @@ static int powernv_eeh_wait_state(struct eeh_pe *pe, int max_wait)
 }
 
 /**
- * powernv_eeh_get_log - Retrieve error log
+ * pnv_eeh_get_log - Retrieve error log
  * @pe: EEH PE
  * @severity: temporary or permanent error log
  * @drv_log: driver log to be combined with retrieved error log
@@ -356,41 +530,30 @@ static int powernv_eeh_wait_state(struct eeh_pe *pe, int max_wait)
  *
  * Retrieve the temporary or permanent error from the PE.
  */
-static int powernv_eeh_get_log(struct eeh_pe *pe, int severity,
-                              char *drv_log, unsigned long len)
+static int pnv_eeh_get_log(struct eeh_pe *pe, int severity,
+                          char *drv_log, unsigned long len)
 {
-       struct pci_controller *hose = pe->phb;
-       struct pnv_phb *phb = hose->private_data;
-       int ret = -EEXIST;
-
-       if (phb->eeh_ops && phb->eeh_ops->get_log)
-               ret = phb->eeh_ops->get_log(pe, severity, drv_log, len);
+       if (!eeh_has_flag(EEH_EARLY_DUMP_LOG))
+               pnv_pci_dump_phb_diag_data(pe->phb, pe->data);
 
-       return ret;
+       return 0;
 }
 
 /**
- * powernv_eeh_configure_bridge - Configure PCI bridges in the indicated PE
+ * pnv_eeh_configure_bridge - Configure PCI bridges in the indicated PE
  * @pe: EEH PE
  *
  * The function will be called to reconfigure the bridges included
  * in the specified PE so that the mulfunctional PE would be recovered
  * again.
  */
-static int powernv_eeh_configure_bridge(struct eeh_pe *pe)
+static int pnv_eeh_configure_bridge(struct eeh_pe *pe)
 {
-       struct pci_controller *hose = pe->phb;
-       struct pnv_phb *phb = hose->private_data;
-       int ret = 0;
-
-       if (phb->eeh_ops && phb->eeh_ops->configure_bridge)
-               ret = phb->eeh_ops->configure_bridge(pe);
-
-       return ret;
+       return 0;
 }
 
 /**
- * powernv_pe_err_inject - Inject specified error to the indicated PE
+ * pnv_pe_err_inject - Inject specified error to the indicated PE
  * @pe: the indicated PE
  * @type: error type
  * @func: specific error type
@@ -401,20 +564,50 @@ static int powernv_eeh_configure_bridge(struct eeh_pe *pe)
  * determined by @type and @func, to the indicated PE for
  * testing purpose.
  */
-static int powernv_eeh_err_inject(struct eeh_pe *pe, int type, int func,
-                                 unsigned long addr, unsigned long mask)
+static int pnv_eeh_err_inject(struct eeh_pe *pe, int type, int func,
+                             unsigned long addr, unsigned long mask)
 {
        struct pci_controller *hose = pe->phb;
        struct pnv_phb *phb = hose->private_data;
-       int ret = -EEXIST;
+       s64 rc;
+
+       /* Sanity check on error type */
+       if (type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR &&
+           type != OPAL_ERR_INJECT_TYPE_IOA_BUS_ERR64) {
+               pr_warn("%s: Invalid error type %d\n",
+                       __func__, type);
+               return -ERANGE;
+       }
 
-       if (phb->eeh_ops && phb->eeh_ops->err_inject)
-               ret = phb->eeh_ops->err_inject(pe, type, func, addr, mask);
+       if (func < OPAL_ERR_INJECT_FUNC_IOA_LD_MEM_ADDR ||
+           func > OPAL_ERR_INJECT_FUNC_IOA_DMA_WR_TARGET) {
+               pr_warn("%s: Invalid error function %d\n",
+                       __func__, func);
+               return -ERANGE;
+       }
 
-       return ret;
+       /* Firmware supports error injection ? */
+       if (!opal_check_token(OPAL_PCI_ERR_INJECT)) {
+               pr_warn("%s: Firmware doesn't support error injection\n",
+                       __func__);
+               return -ENXIO;
+       }
+
+       /* Do error injection */
+       rc = opal_pci_err_inject(phb->opal_id, pe->addr,
+                                type, func, addr, mask);
+       if (rc != OPAL_SUCCESS) {
+               pr_warn("%s: Failure %lld injecting error "
+                       "%d-%d to PHB#%x-PE#%x\n",
+                       __func__, rc, type, func,
+                       hose->global_number, pe->addr);
+               return -EIO;
+       }
+
+       return 0;
 }
 
-static inline bool powernv_eeh_cfg_blocked(struct device_node *dn)
+static inline bool pnv_eeh_cfg_blocked(struct device_node *dn)
 {
        struct eeh_dev *edev = of_node_to_eeh_dev(dn);
 
@@ -427,10 +620,10 @@ static inline bool powernv_eeh_cfg_blocked(struct device_node *dn)
        return false;
 }
 
-static int powernv_eeh_read_config(struct device_node *dn,
-                                  int where, int size, u32 *val)
+static int pnv_eeh_read_config(struct device_node *dn,
+                              int where, int size, u32 *val)
 {
-       if (powernv_eeh_cfg_blocked(dn)) {
+       if (pnv_eeh_cfg_blocked(dn)) {
                *val = 0xFFFFFFFF;
                return PCIBIOS_SET_FAILED;
        }
@@ -438,22 +631,22 @@ static int powernv_eeh_read_config(struct device_node *dn,
        return pnv_pci_cfg_read(dn, where, size, val);
 }
 
-static int powernv_eeh_write_config(struct device_node *dn,
-                                   int where, int size, u32 val)
+static int pnv_eeh_write_config(struct device_node *dn,
+                               int where, int size, u32 val)
 {
-       if (powernv_eeh_cfg_blocked(dn))
+       if (pnv_eeh_cfg_blocked(dn))
                return PCIBIOS_SET_FAILED;
 
        return pnv_pci_cfg_write(dn, where, size, val);
 }
 
 /**
- * powernv_eeh_next_error - Retrieve next EEH error to handle
+ * pnv_eeh_next_error - Retrieve next EEH error to handle
  * @pe: Affected PE
  *
  * Using OPAL API, to retrieve next EEH error for EEH core to handle
  */
-static int powernv_eeh_next_error(struct eeh_pe **pe)
+static int pnv_eeh_next_error(struct eeh_pe **pe)
 {
        struct pci_controller *hose;
        struct pnv_phb *phb = NULL;
@@ -469,7 +662,7 @@ static int powernv_eeh_next_error(struct eeh_pe **pe)
        return -EEXIST;
 }
 
-static int powernv_eeh_restore_config(struct device_node *dn)
+static int pnv_eeh_restore_config(struct device_node *dn)
 {
        struct eeh_dev *edev = of_node_to_eeh_dev(dn);
        struct pnv_phb *phb;
@@ -490,24 +683,24 @@ static int powernv_eeh_restore_config(struct device_node *dn)
        return 0;
 }
 
-static struct eeh_ops powernv_eeh_ops = {
+static struct eeh_ops pnv_eeh_ops = {
        .name                   = "powernv",
-       .init                   = powernv_eeh_init,
-       .post_init              = powernv_eeh_post_init,
+       .init                   = pnv_eeh_init,
+       .post_init              = pnv_eeh_post_init,
        .of_probe               = NULL,
-       .dev_probe              = powernv_eeh_dev_probe,
-       .set_option             = powernv_eeh_set_option,
-       .get_pe_addr            = powernv_eeh_get_pe_addr,
-       .get_state              = powernv_eeh_get_state,
-       .reset                  = powernv_eeh_reset,
-       .wait_state             = powernv_eeh_wait_state,
-       .get_log                = powernv_eeh_get_log,
-       .configure_bridge       = powernv_eeh_configure_bridge,
-       .err_inject             = powernv_eeh_err_inject,
-       .read_config            = powernv_eeh_read_config,
-       .write_config           = powernv_eeh_write_config,
-       .next_error             = powernv_eeh_next_error,
-       .restore_config         = powernv_eeh_restore_config
+       .dev_probe              = pnv_eeh_dev_probe,
+       .set_option             = pnv_eeh_set_option,
+       .get_pe_addr            = pnv_eeh_get_pe_addr,
+       .get_state              = pnv_eeh_get_state,
+       .reset                  = pnv_eeh_reset,
+       .wait_state             = pnv_eeh_wait_state,
+       .get_log                = pnv_eeh_get_log,
+       .configure_bridge       = pnv_eeh_configure_bridge,
+       .err_inject             = pnv_eeh_err_inject,
+       .read_config            = pnv_eeh_read_config,
+       .write_config           = pnv_eeh_write_config,
+       .next_error             = pnv_eeh_next_error,
+       .restore_config         = pnv_eeh_restore_config
 };
 
 /**
@@ -521,7 +714,7 @@ static int __init eeh_powernv_init(void)
        int ret = -EINVAL;
 
        eeh_set_pe_aux_size(PNV_PCI_DIAG_BUF_SIZE);
-       ret = eeh_ops_register(&powernv_eeh_ops);
+       ret = eeh_ops_register(&pnv_eeh_ops);
        if (!ret)
                pr_info("EEH: PowerNV platform initialized\n");
        else
This page took 0.094458 seconds and 5 git commands to generate.