cxgb3: use request_firmware() for the EDC registers setup
[safe/jmp/linux-2.6] / drivers / net / cxgb3 / cxgb3_main.c
index 9fdfe0b..27e7ef5 100644 (file)
@@ -37,7 +37,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/if_vlan.h>
-#include <linux/mii.h>
+#include <linux/mdio.h>
 #include <linux/sockios.h>
 #include <linux/workqueue.h>
 #include <linux/proc_fs.h>
@@ -91,6 +91,8 @@ static const struct pci_device_id cxgb3_pci_tbl[] = {
        CH_DEVICE(0x31, 3),     /* T3B20 */
        CH_DEVICE(0x32, 1),     /* T3B02 */
        CH_DEVICE(0x35, 6),     /* T3C20-derived T3C10 */
+       CH_DEVICE(0x36, 3),     /* S320E-CR */
+       CH_DEVICE(0x37, 7),     /* N320E-G2 */
        {0,}
 };
 
@@ -431,40 +433,78 @@ static int init_tp_parity(struct adapter *adap)
        for (i = 0; i < 16; i++) {
                struct cpl_smt_write_req *req;
 
-               skb = alloc_skb(sizeof(*req), GFP_KERNEL | __GFP_NOFAIL);
+               skb = alloc_skb(sizeof(*req), GFP_KERNEL);
+               if (!skb)
+                       skb = adap->nofail_skb;
+               if (!skb)
+                       goto alloc_skb_fail;
+
                req = (struct cpl_smt_write_req *)__skb_put(skb, sizeof(*req));
                memset(req, 0, sizeof(*req));
                req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
                OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SMT_WRITE_REQ, i));
                req->iff = i;
                t3_mgmt_tx(adap, skb);
+               if (skb == adap->nofail_skb) {
+                       await_mgmt_replies(adap, cnt, i + 1);
+                       adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL);
+                       if (!adap->nofail_skb)
+                               goto alloc_skb_fail;
+               }
        }
 
        for (i = 0; i < 2048; i++) {
                struct cpl_l2t_write_req *req;
 
-               skb = alloc_skb(sizeof(*req), GFP_KERNEL | __GFP_NOFAIL);
+               skb = alloc_skb(sizeof(*req), GFP_KERNEL);
+               if (!skb)
+                       skb = adap->nofail_skb;
+               if (!skb)
+                       goto alloc_skb_fail;
+
                req = (struct cpl_l2t_write_req *)__skb_put(skb, sizeof(*req));
                memset(req, 0, sizeof(*req));
                req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
                OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, i));
                req->params = htonl(V_L2T_W_IDX(i));
                t3_mgmt_tx(adap, skb);
+               if (skb == adap->nofail_skb) {
+                       await_mgmt_replies(adap, cnt, 16 + i + 1);
+                       adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL);
+                       if (!adap->nofail_skb)
+                               goto alloc_skb_fail;
+               }
        }
 
        for (i = 0; i < 2048; i++) {
                struct cpl_rte_write_req *req;
 
-               skb = alloc_skb(sizeof(*req), GFP_KERNEL | __GFP_NOFAIL);
+               skb = alloc_skb(sizeof(*req), GFP_KERNEL);
+               if (!skb)
+                       skb = adap->nofail_skb;
+               if (!skb)
+                       goto alloc_skb_fail;
+
                req = (struct cpl_rte_write_req *)__skb_put(skb, sizeof(*req));
                memset(req, 0, sizeof(*req));
                req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
                OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_RTE_WRITE_REQ, i));
                req->l2t_idx = htonl(V_L2T_W_IDX(i));
                t3_mgmt_tx(adap, skb);
+               if (skb == adap->nofail_skb) {
+                       await_mgmt_replies(adap, cnt, 16 + 2048 + i + 1);
+                       adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL);
+                       if (!adap->nofail_skb)
+                               goto alloc_skb_fail;
+               }
        }
 
-       skb = alloc_skb(sizeof(*greq), GFP_KERNEL | __GFP_NOFAIL);
+       skb = alloc_skb(sizeof(*greq), GFP_KERNEL);
+       if (!skb)
+               skb = adap->nofail_skb;
+       if (!skb)
+               goto alloc_skb_fail;
+
        greq = (struct cpl_set_tcb_field *)__skb_put(skb, sizeof(*greq));
        memset(greq, 0, sizeof(*greq));
        greq->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD));
@@ -473,8 +513,17 @@ static int init_tp_parity(struct adapter *adap)
        t3_mgmt_tx(adap, skb);
 
        i = await_mgmt_replies(adap, cnt, 16 + 2048 + 2048 + 1);
+       if (skb == adap->nofail_skb) {
+               i = await_mgmt_replies(adap, cnt, 16 + 2048 + 2048 + 1);
+               adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL);
+       }
+
        t3_tp_set_offload_mode(adap, 0);
        return i;
+
+alloc_skb_fail:
+       t3_tp_set_offload_mode(adap, 0);
+       return -ENOMEM;
 }
 
 /**
@@ -869,7 +918,12 @@ static int send_pktsched_cmd(struct adapter *adap, int sched, int qidx, int lo,
        struct mngt_pktsched_wr *req;
        int ret;
 
-       skb = alloc_skb(sizeof(*req), GFP_KERNEL | __GFP_NOFAIL);
+       skb = alloc_skb(sizeof(*req), GFP_KERNEL);
+       if (!skb)
+               skb = adap->nofail_skb;
+       if (!skb)
+               return -ENOMEM;
+
        req = (struct mngt_pktsched_wr *)skb_put(skb, sizeof(*req));
        req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_MNGT));
        req->mngt_opcode = FW_MNGTOPCODE_PKTSCHED_SET;
@@ -879,6 +933,12 @@ static int send_pktsched_cmd(struct adapter *adap, int sched, int qidx, int lo,
        req->max = hi;
        req->binding = port;
        ret = t3_mgmt_tx(adap, skb);
+       if (skb == adap->nofail_skb) {
+               adap->nofail_skb = alloc_skb(sizeof(struct cpl_set_tcb_field),
+                                            GFP_KERNEL);
+               if (!adap->nofail_skb)
+                       ret = -ENOMEM;
+       }
 
        return ret;
 }
@@ -904,6 +964,75 @@ static int bind_qsets(struct adapter *adap)
 
 #define FW_FNAME "cxgb3/t3fw-%d.%d.%d.bin"
 #define TPSRAM_NAME "cxgb3/t3%c_psram-%d.%d.%d.bin"
+#define AEL2005_OPT_EDC_NAME "cxgb3/ael2005_opt_edc.bin"
+#define AEL2005_TWX_EDC_NAME "cxgb3/ael2005_twx_edc.bin"
+#define AEL2020_TWX_EDC_NAME "cxgb3/ael2005_twx_edc.bin"
+
+static inline const char *get_edc_fw_name(int edc_idx)
+{
+       const char *fw_name = NULL;
+
+       switch (edc_idx) {
+       case EDC_OPT_AEL2005:
+               fw_name = AEL2005_OPT_EDC_NAME;
+               break;
+       case EDC_TWX_AEL2005:
+               fw_name = AEL2005_TWX_EDC_NAME;
+               break;
+       case EDC_TWX_AEL2020:
+               fw_name = AEL2020_TWX_EDC_NAME;
+               break;
+       }
+       return fw_name;
+}
+
+int t3_get_edc_fw(struct cphy *phy, int edc_idx, int size)
+{
+       struct adapter *adapter = phy->adapter;
+       const struct firmware *fw;
+       char buf[64];
+       u32 csum;
+       const __be32 *p;
+       u16 *cache = phy->phy_cache;
+       int i, ret;
+
+       snprintf(buf, sizeof(buf), get_edc_fw_name(edc_idx));
+
+       ret = request_firmware(&fw, buf, &adapter->pdev->dev);
+       if (ret < 0) {
+               dev_err(&adapter->pdev->dev,
+                       "could not upgrade firmware: unable to load %s\n",
+                       buf);
+               return ret;
+       }
+
+       /* check size, take checksum in account */
+       if (fw->size > size + 4) {
+               CH_ERR(adapter, "firmware image too large %u, expected %d\n",
+                      (unsigned int)fw->size, size + 4);
+               ret = -EINVAL;
+       }
+
+       /* compute checksum */
+       p = (const __be32 *)fw->data;
+       for (csum = 0, i = 0; i < fw->size / sizeof(csum); i++)
+               csum += ntohl(p[i]);
+
+       if (csum != 0xffffffff) {
+               CH_ERR(adapter, "corrupted firmware image, checksum %u\n",
+                      csum);
+               ret = -EINVAL;
+       }
+
+       for (i = 0; i < size / 4 ; i++) {
+               *cache++ = (be32_to_cpu(p[i]) & 0xffff0000) >> 16;
+               *cache++ = be32_to_cpu(p[i]) & 0xffff;
+       }
+
+       release_firmware(fw);
+
+       return ret;
+}
 
 static int upgrade_fw(struct adapter *adap)
 {
@@ -1117,8 +1246,8 @@ static void cxgb_down(struct adapter *adapter)
        spin_unlock_irq(&adapter->work_lock);
 
        free_irq_resources(adapter);
-       flush_workqueue(cxgb3_wq);      /* wait for external IRQ handler */
        quiesce_rx(adapter);
+       flush_workqueue(cxgb3_wq);      /* wait for external IRQ handler */
 }
 
 static void schedule_chk_task(struct adapter *adap)
@@ -1187,6 +1316,9 @@ static int offload_close(struct t3cdev *tdev)
 
        sysfs_remove_group(&tdev->lldev->dev.kobj, &offload_attr_group);
 
+       /* Flush work scheduled while releasing TIDs */
+       flush_scheduled_work();
+
        tdev->lldev = NULL;
        cxgb3_set_dummy_ops(tdev);
        t3_tp_set_offload_mode(adapter, 0);
@@ -1232,6 +1364,10 @@ static int cxgb_close(struct net_device *dev)
        struct port_info *pi = netdev_priv(dev);
        struct adapter *adapter = pi->adapter;
 
+       
+       if (!adapter->open_device_map)
+               return 0;
+
        /* Stop link fault interrupts */
        t3_xgm_intr_disable(adapter, pi->port_id);
        t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset);
@@ -1247,8 +1383,7 @@ static int cxgb_close(struct net_device *dev)
        spin_unlock_irq(&adapter->work_lock);
 
        if (!(adapter->open_device_map & PORT_MASK))
-               cancel_rearming_delayed_workqueue(cxgb3_wq,
-                                                 &adapter->adap_check_task);
+               cancel_delayed_work_sync(&adapter->adap_check_task);
 
        if (!adapter->open_device_map)
                cxgb_down(adapter);
@@ -1587,7 +1722,7 @@ static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
        }
 
        cmd->port = (cmd->supported & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE;
-       cmd->phy_address = p->phy.addr;
+       cmd->phy_address = p->phy.mdio.prtad;
        cmd->transceiver = XCVR_EXTERNAL;
        cmd->autoneg = p->link_config.autoneg;
        cmd->maxtxpkt = 0;
@@ -2302,70 +2437,25 @@ static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
        struct mii_ioctl_data *data = if_mii(req);
        struct port_info *pi = netdev_priv(dev);
        struct adapter *adapter = pi->adapter;
-       int ret, mmd;
 
        switch (cmd) {
-       case SIOCGMIIPHY:
-               data->phy_id = pi->phy.addr;
+       case SIOCGMIIREG:
+       case SIOCSMIIREG:
+               /* Convert phy_id from older PRTAD/DEVAD format */
+               if (is_10G(adapter) &&
+                   !mdio_phy_id_is_c45(data->phy_id) &&
+                   (data->phy_id & 0x1f00) &&
+                   !(data->phy_id & 0xe0e0))
+                       data->phy_id = mdio_phy_id_c45(data->phy_id >> 8,
+                                                      data->phy_id & 0x1f);
                /* FALLTHRU */
-       case SIOCGMIIREG:{
-               u32 val;
-               struct cphy *phy = &pi->phy;
-
-               if (!phy->mdio_read)
-                       return -EOPNOTSUPP;
-               if (is_10G(adapter)) {
-                       mmd = data->phy_id >> 8;
-                       if (!mmd)
-                               mmd = MDIO_DEV_PCS;
-                       else if (mmd > MDIO_DEV_VEND2)
-                               return -EINVAL;
-
-                       ret =
-                               phy->mdio_read(adapter, data->phy_id & 0x1f,
-                                               mmd, data->reg_num, &val);
-               } else
-                       ret =
-                               phy->mdio_read(adapter, data->phy_id & 0x1f,
-                                               0, data->reg_num & 0x1f,
-                                               &val);
-               if (!ret)
-                       data->val_out = val;
-               break;
-       }
-       case SIOCSMIIREG:{
-               struct cphy *phy = &pi->phy;
-
-               if (!capable(CAP_NET_ADMIN))
-                       return -EPERM;
-               if (!phy->mdio_write)
-                       return -EOPNOTSUPP;
-               if (is_10G(adapter)) {
-                       mmd = data->phy_id >> 8;
-                       if (!mmd)
-                               mmd = MDIO_DEV_PCS;
-                       else if (mmd > MDIO_DEV_VEND2)
-                               return -EINVAL;
-
-                       ret =
-                               phy->mdio_write(adapter,
-                                               data->phy_id & 0x1f, mmd,
-                                               data->reg_num,
-                                               data->val_in);
-               } else
-                       ret =
-                               phy->mdio_write(adapter,
-                                               data->phy_id & 0x1f, 0,
-                                               data->reg_num & 0x1f,
-                                               data->val_in);
-               break;
-       }
+       case SIOCGMIIPHY:
+               return mdio_mii_ioctl(&pi->phy.mdio, data, cmd);
        case SIOCCHIOCTL:
                return cxgb_extension_ioctl(dev, req->ifr_data);
        default:
                return -EOPNOTSUPP;
        }
-       return ret;
 }
 
 static int cxgb_change_mtu(struct net_device *dev, int new_mtu)
@@ -2490,14 +2580,16 @@ static void check_link_status(struct adapter *adapter)
        for_each_port(adapter, i) {
                struct net_device *dev = adapter->port[i];
                struct port_info *p = netdev_priv(dev);
+               int link_fault;
 
                spin_lock_irq(&adapter->work_lock);
-               if (p->link_fault) {
+               link_fault = p->link_fault;
+               spin_unlock_irq(&adapter->work_lock);
+
+               if (link_fault) {
                        t3_link_fault(adapter, i);
-                       spin_unlock_irq(&adapter->work_lock);
                        continue;
                }
-               spin_unlock_irq(&adapter->work_lock);
 
                if (!(p->phy.caps & SUPPORTED_IRQ) && netif_running(dev)) {
                        t3_xgm_intr_disable(adapter, i);
@@ -2821,6 +2913,9 @@ static pci_ers_result_t t3_io_error_detected(struct pci_dev *pdev,
        struct adapter *adapter = pci_get_drvdata(pdev);
        int ret;
 
+       if (state == pci_channel_io_perm_failure)
+               return PCI_ERS_RESULT_DISCONNECT;
+
        ret = t3_adapter_error(adapter, 0);
 
        /* Request a slot reset. */
@@ -2915,8 +3010,13 @@ static int __devinit cxgb_enable_msix(struct adapter *adap)
        while ((err = pci_enable_msix(adap->pdev, entries, vectors)) > 0)
                vectors = err;
 
-       if (!err && vectors < (adap->params.nports + 1))
+       if (err < 0)
+               pci_disable_msix(adap->pdev);
+
+       if (!err && vectors < (adap->params.nports + 1)) {
+               pci_disable_msix(adap->pdev);
                err = -1;
+       }
 
        if (!err) {
                for (i = 0; i < vectors; ++i)
@@ -3047,6 +3147,14 @@ static int __devinit init_one(struct pci_dev *pdev,
                goto out_disable_device;
        }
 
+       adapter->nofail_skb =
+               alloc_skb(sizeof(struct cpl_set_tcb_field), GFP_KERNEL);
+       if (!adapter->nofail_skb) {
+               dev_err(&pdev->dev, "cannot allocate nofail buffer\n");
+               err = -ENOMEM;
+               goto out_free_adapter;
+       }
+
        adapter->regs = ioremap_nocache(mmio_start, mmio_len);
        if (!adapter->regs) {
                dev_err(&pdev->dev, "cannot map device registers\n");
@@ -3090,7 +3198,6 @@ static int __devinit init_one(struct pci_dev *pdev,
                netdev->mem_start = mmio_start;
                netdev->mem_end = mmio_start + mmio_len - 1;
                netdev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
-               netdev->features |= NETIF_F_LLTX;
                netdev->features |= NETIF_F_GRO;
                if (pci_using_dac)
                        netdev->features |= NETIF_F_HIGHDMA;
@@ -3204,6 +3311,8 @@ static void __devexit remove_one(struct pci_dev *pdev)
                                free_netdev(adapter->port[i]);
 
                iounmap(adapter->regs);
+               if (adapter->nofail_skb)
+                       kfree_skb(adapter->nofail_skb);
                kfree(adapter);
                pci_release_regions(pdev);
                pci_disable_device(pdev);