cxgb4: implement EEH
[pandora-kernel.git] / drivers / net / cxgb4 / cxgb4_main.c
index 58045b0..baf4f0a 100644 (file)
@@ -2483,6 +2483,7 @@ static void cxgb_down(struct adapter *adapter)
        t4_intr_disable(adapter);
        cancel_work_sync(&adapter->tid_release_task);
        adapter->tid_release_task_busy = false;
+       adapter->tid_release_head = NULL;
 
        if (adapter->flags & USING_MSIX) {
                free_msix_queue_irqs(adapter);
@@ -2709,6 +2710,65 @@ static void setup_memwin(struct adapter *adap)
                     WINDOW(ilog2(MEMWIN2_APERTURE) - 10));
 }
 
+static int adap_init1(struct adapter *adap, struct fw_caps_config_cmd *c)
+{
+       u32 v;
+       int ret;
+
+       /* get device capabilities */
+       memset(c, 0, sizeof(*c));
+       c->op_to_write = htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) |
+                              FW_CMD_REQUEST | FW_CMD_READ);
+       c->retval_len16 = htonl(FW_LEN16(*c));
+       ret = t4_wr_mbox(adap, 0, c, sizeof(*c), c);
+       if (ret < 0)
+               return ret;
+
+       /* select capabilities we'll be using */
+       if (c->niccaps & htons(FW_CAPS_CONFIG_NIC_VM)) {
+               if (!vf_acls)
+                       c->niccaps ^= htons(FW_CAPS_CONFIG_NIC_VM);
+               else
+                       c->niccaps = htons(FW_CAPS_CONFIG_NIC_VM);
+       } else if (vf_acls) {
+               dev_err(adap->pdev_dev, "virtualization ACLs not supported");
+               return ret;
+       }
+       c->op_to_write = htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) |
+                              FW_CMD_REQUEST | FW_CMD_WRITE);
+       ret = t4_wr_mbox(adap, 0, c, sizeof(*c), NULL);
+       if (ret < 0)
+               return ret;
+
+       ret = t4_config_glbl_rss(adap, 0,
+                                FW_RSS_GLB_CONFIG_CMD_MODE_BASICVIRTUAL,
+                                FW_RSS_GLB_CONFIG_CMD_TNLMAPEN |
+                                FW_RSS_GLB_CONFIG_CMD_TNLALLLKP);
+       if (ret < 0)
+               return ret;
+
+       ret = t4_cfg_pfvf(adap, 0, 0, 0, 64, 64, 64, 0, 0, 4, 0xf, 0xf, 16,
+                         FW_CMD_CAP_PF, FW_CMD_CAP_PF);
+       if (ret < 0)
+               return ret;
+
+       t4_sge_init(adap);
+
+       /* get basic stuff going */
+       ret = t4_early_init(adap, 0);
+       if (ret < 0)
+               return ret;
+
+       /* tweak some settings */
+       t4_write_reg(adap, TP_SHIFT_CNT, 0x64f8849);
+       t4_write_reg(adap, ULP_RX_TDDP_PSZ, HPZ0(PAGE_SHIFT - 12));
+       t4_write_reg(adap, TP_PIO_ADDR, TP_INGRESS_CONFIG);
+       v = t4_read_reg(adap, TP_PIO_DATA);
+       t4_write_reg(adap, TP_PIO_DATA, v & ~CSUM_HAS_PSEUDO_HDR);
+       setup_memwin(adap);
+       return 0;
+}
+
 /*
  * Max # of ATIDs.  The absolute HW max is 16K but we keep it lower.
  */
@@ -2746,43 +2806,6 @@ static int adap_init0(struct adapter *adap)
        if (ret < 0)
                goto bye;
 
-       /* get device capabilities */
-       memset(&c, 0, sizeof(c));
-       c.op_to_write = htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) |
-                             FW_CMD_REQUEST | FW_CMD_READ);
-       c.retval_len16 = htonl(FW_LEN16(c));
-       ret = t4_wr_mbox(adap, 0, &c, sizeof(c), &c);
-       if (ret < 0)
-               goto bye;
-
-       /* select capabilities we'll be using */
-       if (c.niccaps & htons(FW_CAPS_CONFIG_NIC_VM)) {
-               if (!vf_acls)
-                       c.niccaps ^= htons(FW_CAPS_CONFIG_NIC_VM);
-               else
-                       c.niccaps = htons(FW_CAPS_CONFIG_NIC_VM);
-       } else if (vf_acls) {
-               dev_err(adap->pdev_dev, "virtualization ACLs not supported");
-               goto bye;
-       }
-       c.op_to_write = htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) |
-                             FW_CMD_REQUEST | FW_CMD_WRITE);
-       ret = t4_wr_mbox(adap, 0, &c, sizeof(c), NULL);
-       if (ret < 0)
-               goto bye;
-
-       ret = t4_config_glbl_rss(adap, 0,
-                                FW_RSS_GLB_CONFIG_CMD_MODE_BASICVIRTUAL,
-                                FW_RSS_GLB_CONFIG_CMD_TNLMAPEN |
-                                FW_RSS_GLB_CONFIG_CMD_TNLALLLKP);
-       if (ret < 0)
-               goto bye;
-
-       ret = t4_cfg_pfvf(adap, 0, 0, 0, 64, 64, 64, 0, 0, 4, 0xf, 0xf, 16,
-                         FW_CMD_CAP_PF, FW_CMD_CAP_PF);
-       if (ret < 0)
-               goto bye;
-
        for (v = 0; v < SGE_NTIMERS - 1; v++)
                adap->sge.timer_val[v] = min(intr_holdoff[v], MAX_SGE_TIMERVAL);
        adap->sge.timer_val[SGE_NTIMERS - 1] = MAX_SGE_TIMERVAL;
@@ -2790,10 +2813,7 @@ static int adap_init0(struct adapter *adap)
        for (v = 1; v < SGE_NCOUNTERS; v++)
                adap->sge.counter_val[v] = min(intr_cnt[v - 1],
                                               THRESHOLD_3_MASK);
-       t4_sge_init(adap);
-
-       /* get basic stuff going */
-       ret = t4_early_init(adap, 0);
+       ret = adap_init1(adap, &c);
        if (ret < 0)
                goto bye;
 
@@ -2876,14 +2896,6 @@ static int adap_init0(struct adapter *adap)
        t4_read_mtu_tbl(adap, adap->params.mtus, NULL);
        t4_load_mtus(adap, adap->params.mtus, adap->params.a_wnd,
                     adap->params.b_wnd);
-
-       /* tweak some settings */
-       t4_write_reg(adap, TP_SHIFT_CNT, 0x64f8849);
-       t4_write_reg(adap, ULP_RX_TDDP_PSZ, HPZ0(PAGE_SHIFT - 12));
-       t4_write_reg(adap, TP_PIO_ADDR, TP_INGRESS_CONFIG);
-       v = t4_read_reg(adap, TP_PIO_DATA);
-       t4_write_reg(adap, TP_PIO_DATA, v & ~CSUM_HAS_PSEUDO_HDR);
-       setup_memwin(adap);
        return 0;
 
        /*
@@ -2896,6 +2908,108 @@ bye:    if (ret != -ETIMEDOUT && ret != -EIO)
        return ret;
 }
 
+/* EEH callbacks */
+
+static pci_ers_result_t eeh_err_detected(struct pci_dev *pdev,
+                                        pci_channel_state_t state)
+{
+       int i;
+       struct adapter *adap = pci_get_drvdata(pdev);
+
+       if (!adap)
+               goto out;
+
+       rtnl_lock();
+       adap->flags &= ~FW_OK;
+       notify_ulds(adap, CXGB4_STATE_START_RECOVERY);
+       for_each_port(adap, i) {
+               struct net_device *dev = adap->port[i];
+
+               netif_device_detach(dev);
+               netif_carrier_off(dev);
+       }
+       if (adap->flags & FULL_INIT_DONE)
+               cxgb_down(adap);
+       rtnl_unlock();
+       pci_disable_device(pdev);
+out:   return state == pci_channel_io_perm_failure ?
+               PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t eeh_slot_reset(struct pci_dev *pdev)
+{
+       int i, ret;
+       struct fw_caps_config_cmd c;
+       struct adapter *adap = pci_get_drvdata(pdev);
+
+       if (!adap) {
+               pci_restore_state(pdev);
+               pci_save_state(pdev);
+               return PCI_ERS_RESULT_RECOVERED;
+       }
+
+       if (pci_enable_device(pdev)) {
+               dev_err(&pdev->dev, "cannot reenable PCI device after reset\n");
+               return PCI_ERS_RESULT_DISCONNECT;
+       }
+
+       pci_set_master(pdev);
+       pci_restore_state(pdev);
+       pci_save_state(pdev);
+       pci_cleanup_aer_uncorrect_error_status(pdev);
+
+       if (t4_wait_dev_ready(adap) < 0)
+               return PCI_ERS_RESULT_DISCONNECT;
+       if (t4_fw_hello(adap, 0, 0, MASTER_MUST, NULL))
+               return PCI_ERS_RESULT_DISCONNECT;
+       adap->flags |= FW_OK;
+       if (adap_init1(adap, &c))
+               return PCI_ERS_RESULT_DISCONNECT;
+
+       for_each_port(adap, i) {
+               struct port_info *p = adap2pinfo(adap, i);
+
+               ret = t4_alloc_vi(adap, 0, p->tx_chan, 0, 0, 1, NULL, NULL);
+               if (ret < 0)
+                       return PCI_ERS_RESULT_DISCONNECT;
+               p->viid = ret;
+               p->xact_addr_filt = -1;
+       }
+
+       t4_load_mtus(adap, adap->params.mtus, adap->params.a_wnd,
+                    adap->params.b_wnd);
+       if (cxgb_up(adap))
+               return PCI_ERS_RESULT_DISCONNECT;
+       return PCI_ERS_RESULT_RECOVERED;
+}
+
+static void eeh_resume(struct pci_dev *pdev)
+{
+       int i;
+       struct adapter *adap = pci_get_drvdata(pdev);
+
+       if (!adap)
+               return;
+
+       rtnl_lock();
+       for_each_port(adap, i) {
+               struct net_device *dev = adap->port[i];
+
+               if (netif_running(dev)) {
+                       link_start(dev);
+                       cxgb_set_rxmode(dev);
+               }
+               netif_device_attach(dev);
+       }
+       rtnl_unlock();
+}
+
+static struct pci_error_handlers cxgb4_eeh = {
+       .error_detected = eeh_err_detected,
+       .slot_reset     = eeh_slot_reset,
+       .resume         = eeh_resume,
+};
+
 static inline bool is_10g_port(const struct link_config *lc)
 {
        return (lc->supported & FW_PORT_CAP_SPEED_10G) != 0;
@@ -3143,8 +3257,10 @@ static int __devinit init_one(struct pci_dev *pdev,
 
        /* We control everything through PF 0 */
        func = PCI_FUNC(pdev->devfn);
-       if (func > 0)
+       if (func > 0) {
+               pci_save_state(pdev);        /* to restore SR-IOV later */
                goto sriov;
+       }
 
        err = pci_enable_device(pdev);
        if (err) {
@@ -3385,6 +3501,7 @@ static struct pci_driver cxgb4_driver = {
        .id_table = cxgb4_pci_tbl,
        .probe    = init_one,
        .remove   = __devexit_p(remove_one),
+       .err_handler = &cxgb4_eeh,
 };
 
 static int __init cxgb4_init_module(void)