cxgb4: implement EEH
[pandora-kernel.git] / drivers / net / cxgb4 / cxgb4_main.c
index a7e30a2..baf4f0a 100644 (file)
@@ -240,9 +240,9 @@ static int set_addr_filters(const struct net_device *dev, bool sleep)
        u16 filt_idx[7];
        const u8 *addr[7];
        int ret, naddr = 0;
-       const struct dev_addr_list *d;
        const struct netdev_hw_addr *ha;
        int uc_cnt = netdev_uc_count(dev);
+       int mc_cnt = netdev_mc_count(dev);
        const struct port_info *pi = netdev_priv(dev);
 
        /* first do the secondary unicast addresses */
@@ -260,9 +260,9 @@ static int set_addr_filters(const struct net_device *dev, bool sleep)
        }
 
        /* next set up the multicast addresses */
-       netdev_for_each_mc_addr(d, dev) {
-               addr[naddr++] = d->dmi_addr;
-               if (naddr >= ARRAY_SIZE(addr) || d->next == NULL) {
+       netdev_for_each_mc_addr(ha, dev) {
+               addr[naddr++] = ha->addr;
+               if (--mc_cnt == 0 || naddr >= ARRAY_SIZE(addr)) {
                        ret = t4_alloc_mac_filt(pi->adapter, 0, pi->viid, free,
                                        naddr, addr, filt_idx, &mhash, sleep);
                        if (ret < 0)
@@ -290,7 +290,7 @@ static int set_rxmode(struct net_device *dev, int mtu, bool sleep_ok)
        if (ret == 0)
                ret = t4_set_rxmode(pi->adapter, 0, pi->viid, mtu,
                                    (dev->flags & IFF_PROMISC) ? 1 : 0,
-                                   (dev->flags & IFF_ALLMULTI) ? 1 : 0, 1,
+                                   (dev->flags & IFF_ALLMULTI) ? 1 : 0, 1, -1,
                                    sleep_ok);
        return ret;
 }
@@ -311,11 +311,11 @@ static int link_start(struct net_device *dev)
         * that step explicitly.
         */
        ret = t4_set_rxmode(pi->adapter, 0, pi->viid, dev->mtu, -1, -1, -1,
-                           true);
+                           pi->vlan_grp != NULL, true);
        if (ret == 0) {
                ret = t4_change_mac(pi->adapter, 0, pi->viid,
                                    pi->xact_addr_filt, dev->dev_addr, true,
-                                   false);
+                                   true);
                if (ret >= 0) {
                        pi->xact_addr_filt = ret;
                        ret = 0;
@@ -859,6 +859,8 @@ static char stats_strings[][ETH_GSTRING_LEN] = {
        "RxCsumGood         ",
        "VLANextractions    ",
        "VLANinsertions     ",
+       "GROpackets         ",
+       "GROmerged          ",
 };
 
 static int get_sset_count(struct net_device *dev, int sset)
@@ -922,6 +924,8 @@ struct queue_port_stats {
        u64 rx_csum;
        u64 vlan_ex;
        u64 vlan_ins;
+       u64 gro_pkts;
+       u64 gro_merged;
 };
 
 static void collect_sge_port_stats(const struct adapter *adap,
@@ -938,6 +942,8 @@ static void collect_sge_port_stats(const struct adapter *adap,
                s->rx_csum += rx->stats.rx_cso;
                s->vlan_ex += rx->stats.vlan_ex;
                s->vlan_ins += tx->vlan_ins;
+               s->gro_pkts += rx->stats.lro_pkts;
+               s->gro_merged += rx->stats.lro_merged;
        }
 }
 
@@ -1711,6 +1717,18 @@ static int set_tso(struct net_device *dev, u32 value)
        return 0;
 }
 
+static int set_flags(struct net_device *dev, u32 flags)
+{
+       if (flags & ~ETH_FLAG_RXHASH)
+               return -EOPNOTSUPP;
+
+       if (flags & ETH_FLAG_RXHASH)
+               dev->features |= NETIF_F_RXHASH;
+       else
+               dev->features &= ~NETIF_F_RXHASH;
+       return 0;
+}
+
 static struct ethtool_ops cxgb_ethtool_ops = {
        .get_settings      = get_settings,
        .set_settings      = set_settings,
@@ -1741,6 +1759,7 @@ static struct ethtool_ops cxgb_ethtool_ops = {
        .get_wol           = get_wol,
        .set_wol           = set_wol,
        .set_tso           = set_tso,
+       .set_flags         = set_flags,
        .flash_device      = set_flash,
 };
 
@@ -2308,6 +2327,9 @@ static void uld_attach(struct adapter *adap, unsigned int uld)
                register_netevent_notifier(&cxgb4_netevent_nb);
                netevent_registered = true;
        }
+
+       if (adap->flags & FULL_INIT_DONE)
+               ulds[uld].state_change(handle, CXGB4_STATE_UP);
 }
 
 static void attach_ulds(struct adapter *adap)
@@ -2414,23 +2436,17 @@ EXPORT_SYMBOL(cxgb4_unregister_uld);
  */
 static int cxgb_up(struct adapter *adap)
 {
-       int err = 0;
+       int err;
 
-       if (!(adap->flags & FULL_INIT_DONE)) {
-               err = setup_sge_queues(adap);
-               if (err)
-                       goto out;
-               err = setup_rss(adap);
-               if (err) {
-                       t4_free_sge_resources(adap);
-                       goto out;
-               }
-               if (adap->flags & USING_MSIX)
-                       name_msix_vecs(adap);
-               adap->flags |= FULL_INIT_DONE;
-       }
+       err = setup_sge_queues(adap);
+       if (err)
+               goto out;
+       err = setup_rss(adap);
+       if (err)
+               goto freeq;
 
        if (adap->flags & USING_MSIX) {
+               name_msix_vecs(adap);
                err = request_irq(adap->msix_info[0].vec, t4_nondata_intr, 0,
                                  adap->msix_info[0].desc, adap);
                if (err)
@@ -2451,11 +2467,14 @@ static int cxgb_up(struct adapter *adap)
        enable_rx(adap);
        t4_sge_start(adap);
        t4_intr_enable(adap);
+       adap->flags |= FULL_INIT_DONE;
        notify_ulds(adap, CXGB4_STATE_UP);
  out:
        return err;
  irq_err:
        dev_err(adap->pdev_dev, "request_irq failed, err %d\n", err);
+ freeq:
+       t4_free_sge_resources(adap);
        goto out;
 }
 
@@ -2464,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);
@@ -2471,6 +2491,9 @@ static void cxgb_down(struct adapter *adapter)
        } else
                free_irq(adapter->pdev->irq, adapter);
        quiesce_rx(adapter);
+       t4_sge_stop(adapter);
+       t4_free_sge_resources(adapter);
+       adapter->flags &= ~FULL_INIT_DONE;
 }
 
 /*
@@ -2482,11 +2505,13 @@ static int cxgb_open(struct net_device *dev)
        struct port_info *pi = netdev_priv(dev);
        struct adapter *adapter = pi->adapter;
 
-       if (!adapter->open_device_map && (err = cxgb_up(adapter)) < 0)
-               return err;
+       if (!(adapter->flags & FULL_INIT_DONE)) {
+               err = cxgb_up(adapter);
+               if (err < 0)
+                       return err;
+       }
 
        dev->real_num_tx_queues = pi->nqsets;
-       set_bit(pi->tx_chan, &adapter->open_device_map);
        link_start(dev);
        netif_tx_start_all_queues(dev);
        return 0;
@@ -2494,19 +2519,12 @@ static int cxgb_open(struct net_device *dev)
 
 static int cxgb_close(struct net_device *dev)
 {
-       int ret;
        struct port_info *pi = netdev_priv(dev);
        struct adapter *adapter = pi->adapter;
 
        netif_tx_stop_all_queues(dev);
        netif_carrier_off(dev);
-       ret = t4_enable_vi(adapter, 0, pi->viid, false, false);
-
-       clear_bit(pi->tx_chan, &adapter->open_device_map);
-
-       if (!adapter->open_device_map)
-               cxgb_down(adapter);
-       return 0;
+       return t4_enable_vi(adapter, 0, pi->viid, false, false);
 }
 
 static struct net_device_stats *cxgb_get_stats(struct net_device *dev)
@@ -2601,7 +2619,7 @@ static int cxgb_change_mtu(struct net_device *dev, int new_mtu)
 
        if (new_mtu < 81 || new_mtu > MAX_MTU)         /* accommodate SACK */
                return -EINVAL;
-       ret = t4_set_rxmode(pi->adapter, 0, pi->viid, new_mtu, -1, -1, -1,
+       ret = t4_set_rxmode(pi->adapter, 0, pi->viid, new_mtu, -1, -1, -1, -1,
                            true);
        if (!ret)
                dev->mtu = new_mtu;
@@ -2632,7 +2650,8 @@ static void vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
        struct port_info *pi = netdev_priv(dev);
 
        pi->vlan_grp = grp;
-       t4_set_vlan_accel(pi->adapter, 1 << pi->tx_chan, grp != NULL);
+       t4_set_rxmode(pi->adapter, 0, pi->viid, -1, -1, -1, -1, grp != NULL,
+                     true);
 }
 
 #ifdef CONFIG_NET_POLL_CONTROLLER
@@ -2691,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.
  */
@@ -2728,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;
@@ -2772,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;
 
@@ -2858,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;
 
        /*
@@ -2878,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;
@@ -3066,6 +3198,12 @@ static void __devinit print_port_info(struct adapter *adap)
 
        int i;
        char buf[80];
+       const char *spd = "";
+
+       if (adap->params.pci.speed == PCI_EXP_LNKSTA_CLS_2_5GB)
+               spd = " 2.5 GT/s";
+       else if (adap->params.pci.speed == PCI_EXP_LNKSTA_CLS_5_0GB)
+               spd = " 5 GT/s";
 
        for_each_port(adap, i) {
                struct net_device *dev = adap->port[i];
@@ -3085,10 +3223,10 @@ static void __devinit print_port_info(struct adapter *adap)
                        --bufp;
                sprintf(bufp, "BASE-%s", base[pi->port_type]);
 
-               netdev_info(dev, "Chelsio %s rev %d %s %sNIC PCIe x%d%s\n",
+               netdev_info(dev, "Chelsio %s rev %d %s %sNIC PCIe x%d%s%s\n",
                            adap->params.vpd.id, adap->params.rev,
                            buf, is_offload(adap) ? "R" : "",
-                           adap->params.pci.width,
+                           adap->params.pci.width, spd,
                            (adap->flags & USING_MSIX) ? " MSI-X" :
                            (adap->flags & USING_MSI) ? " MSI" : "");
                if (adap->name == dev->name)
@@ -3119,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) {
@@ -3203,7 +3343,7 @@ static int __devinit init_one(struct pci_dev *pdev,
 
                netdev->features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6;
                netdev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
-               netdev->features |= NETIF_F_GRO | highdma;
+               netdev->features |= NETIF_F_GRO | NETIF_F_RXHASH | highdma;
                netdev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
                netdev->vlan_features = netdev->features & VLAN_FEAT;
 
@@ -3334,8 +3474,8 @@ static void __devexit remove_one(struct pci_dev *pdev)
                if (adapter->debugfs_root)
                        debugfs_remove_recursive(adapter->debugfs_root);
 
-               t4_sge_stop(adapter);
-               t4_free_sge_resources(adapter);
+               if (adapter->flags & FULL_INIT_DONE)
+                       cxgb_down(adapter);
                t4_free_mem(adapter->l2t);
                t4_free_mem(adapter->tids.tid_tab);
                disable_msi(adapter);
@@ -3361,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)