qede: Add basic Network driver
authorYuval Mintz <Yuval.Mintz@qlogic.com>
Mon, 26 Oct 2015 09:02:27 +0000 (11:02 +0200)
committerDavid S. Miller <davem@davemloft.net>
Wed, 28 Oct 2015 02:34:47 +0000 (19:34 -0700)
The Qlogic Everest Driver for Ethernet is the Ethernet specific module for
QL4xxx ethernet products by Qlogic.

This patch adds a very minimal PCI driver, one that doesn't yet register
a network device, but one that does interact with qed and does a basic
initialization of the HW.

Signed-off-by: Yuval Mintz <Yuval.Mintz@qlogic.com>
Signed-off-by: Ariel Elior <Ariel.Elior@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/qlogic/Kconfig
drivers/net/ethernet/qlogic/Makefile
drivers/net/ethernet/qlogic/qede/Makefile [new file with mode: 0644]
drivers/net/ethernet/qlogic/qede/qede.h [new file with mode: 0644]
drivers/net/ethernet/qlogic/qede/qede_main.c [new file with mode: 0644]

index 58c3fb388f464a6ae2fbfb08948936a3ea900c8f..30a6f246dfc9f004ac68f4b2cc84658186dce9e6 100644 (file)
@@ -97,4 +97,9 @@ config QED
        ---help---
          This enables the support for ...
 
+config QEDE
+       tristate "QLogic QED 25/40/100Gb Ethernet NIC"
+       depends on QED
+       ---help---
+         This enables the support for ...
 endif # NET_VENDOR_QLOGIC
index 7600138268eefe8f1c4fcd783d9db4a27bf43595..cee90e05beb8fec1ee856787833802cb996a9916 100644 (file)
@@ -7,3 +7,4 @@ obj-$(CONFIG_QLCNIC) += qlcnic/
 obj-$(CONFIG_QLGE) += qlge/
 obj-$(CONFIG_NETXEN_NIC) += netxen/
 obj-$(CONFIG_QED) += qed/
+obj-$(CONFIG_QEDE)+= qede/
diff --git a/drivers/net/ethernet/qlogic/qede/Makefile b/drivers/net/ethernet/qlogic/qede/Makefile
new file mode 100644 (file)
index 0000000..bedfe9f
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_QEDE) := qede.o
+
+qede-y := qede_main.o
diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h
new file mode 100644 (file)
index 0000000..7e2bcfa
--- /dev/null
@@ -0,0 +1,73 @@
+/* QLogic qede NIC Driver
+* Copyright (c) 2015 QLogic Corporation
+*
+* This software is available under the terms of the GNU General Public License
+* (GPL) Version 2, available from the file COPYING in the main directory of
+* this source tree.
+*/
+
+#ifndef _QEDE_H_
+#define _QEDE_H_
+#include <linux/compiler.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+#include <linux/interrupt.h>
+#include <linux/bitmap.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/qed/common_hsi.h>
+#include <linux/qed/eth_common.h>
+#include <linux/qed/qed_if.h>
+#include <linux/qed/qed_chain.h>
+#include <linux/qed/qed_eth_if.h>
+
+#define QEDE_MAJOR_VERSION             8
+#define QEDE_MINOR_VERSION             4
+#define QEDE_REVISION_VERSION          0
+#define QEDE_ENGINEERING_VERSION       0
+#define DRV_MODULE_VERSION __stringify(QEDE_MAJOR_VERSION) "." \
+               __stringify(QEDE_MINOR_VERSION) "."             \
+               __stringify(QEDE_REVISION_VERSION) "."          \
+               __stringify(QEDE_ENGINEERING_VERSION)
+
+#define QEDE_ETH_INTERFACE_VERSION     300
+
+#define DRV_MODULE_SYM         qede
+
+struct qede_dev {
+       struct qed_dev                  *cdev;
+       struct net_device               *ndev;
+       struct pci_dev                  *pdev;
+
+       u32                             dp_module;
+       u8                              dp_level;
+
+       const struct qed_eth_ops        *ops;
+
+       struct qed_dev_eth_info dev_info;
+#define QEDE_MAX_RSS_CNT(edev) ((edev)->dev_info.num_queues)
+#define QEDE_MAX_TSS_CNT(edev) ((edev)->dev_info.num_queues * \
+                                (edev)->dev_info.num_tc)
+
+       u16                             num_rss;
+       u8                              num_tc;
+#define QEDE_RSS_CNT(edev)             ((edev)->num_rss)
+#define QEDE_TSS_CNT(edev)             ((edev)->num_rss *      \
+                                        (edev)->num_tc)
+#define QEDE_TSS_IDX(edev, txqidx)     ((txqidx) % (edev)->num_rss)
+#define QEDE_TC_IDX(edev, txqidx)      ((txqidx) / (edev)->num_rss)
+
+       struct qed_int_info             int_info;
+       unsigned char                   primary_mac[ETH_ALEN];
+
+       /* Smaller private varaiant of the RTNL lock */
+       struct mutex                    qede_lock;
+       u32                             state; /* Protected by qede_lock */
+};
+
+/* Debug print definitions */
+#define DP_NAME(edev) ((edev)->ndev->name)
+
+#endif /* _QEDE_H_ */
diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c
new file mode 100644 (file)
index 0000000..02ed6db
--- /dev/null
@@ -0,0 +1,354 @@
+/* QLogic qede NIC Driver
+* Copyright (c) 2015 QLogic Corporation
+*
+* This software is available under the terms of the GNU General Public License
+* (GPL) Version 2, available from the file COPYING in the main directory of
+* this source tree.
+*/
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/version.h>
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <asm/byteorder.h>
+#include <asm/param.h>
+#include <linux/io.h>
+#include <linux/netdev_features.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <net/vxlan.h>
+#include <linux/ip.h>
+#include <net/ipv6.h>
+#include <net/tcp.h>
+#include <linux/if_ether.h>
+#include <linux/if_vlan.h>
+#include <linux/pkt_sched.h>
+#include <linux/ethtool.h>
+#include <linux/in.h>
+#include <linux/random.h>
+#include <net/ip6_checksum.h>
+#include <linux/bitops.h>
+
+#include "qede.h"
+
+static const char version[] = "QLogic QL4xxx 40G/100G Ethernet Driver qede "
+                             DRV_MODULE_VERSION "\n";
+
+MODULE_DESCRIPTION("QLogic 40G/100G Ethernet Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_MODULE_VERSION);
+
+static uint debug;
+module_param(debug, uint, 0);
+MODULE_PARM_DESC(debug, " Default debug msglevel");
+
+static const struct qed_eth_ops *qed_ops;
+
+#define CHIP_NUM_57980S_40             0x1634
+#define CHIP_NUM_57980S_10             0x1635
+#define CHIP_NUM_57980S_MF             0x1636
+#define CHIP_NUM_57980S_100            0x1644
+#define CHIP_NUM_57980S_50             0x1654
+#define CHIP_NUM_57980S_25             0x1656
+
+#ifndef PCI_DEVICE_ID_NX2_57980E
+#define PCI_DEVICE_ID_57980S_40                CHIP_NUM_57980S_40
+#define PCI_DEVICE_ID_57980S_10                CHIP_NUM_57980S_10
+#define PCI_DEVICE_ID_57980S_MF                CHIP_NUM_57980S_MF
+#define PCI_DEVICE_ID_57980S_100       CHIP_NUM_57980S_100
+#define PCI_DEVICE_ID_57980S_50                CHIP_NUM_57980S_50
+#define PCI_DEVICE_ID_57980S_25                CHIP_NUM_57980S_25
+#endif
+
+static const struct pci_device_id qede_pci_tbl[] = {
+       { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_40), 0 },
+       { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_10), 0 },
+       { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_MF), 0 },
+       { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_100), 0 },
+       { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_50), 0 },
+       { PCI_VDEVICE(QLOGIC, PCI_DEVICE_ID_57980S_25), 0 },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, qede_pci_tbl);
+
+static int qede_probe(struct pci_dev *pdev, const struct pci_device_id *id);
+
+#define TX_TIMEOUT             (5 * HZ)
+
+static void qede_remove(struct pci_dev *pdev);
+
+static struct pci_driver qede_pci_driver = {
+       .name = "qede",
+       .id_table = qede_pci_tbl,
+       .probe = qede_probe,
+       .remove = qede_remove,
+};
+
+static
+int __init qede_init(void)
+{
+       int ret;
+       u32 qed_ver;
+
+       pr_notice("qede_init: %s\n", version);
+
+       qed_ver = qed_get_protocol_version(QED_PROTOCOL_ETH);
+       if (qed_ver !=  QEDE_ETH_INTERFACE_VERSION) {
+               pr_notice("Version mismatch [%08x != %08x]\n",
+                         qed_ver,
+                         QEDE_ETH_INTERFACE_VERSION);
+               return -EINVAL;
+       }
+
+       qed_ops = qed_get_eth_ops(QEDE_ETH_INTERFACE_VERSION);
+       if (!qed_ops) {
+               pr_notice("Failed to get qed ethtool operations\n");
+               return -EINVAL;
+       }
+
+       ret = pci_register_driver(&qede_pci_driver);
+       if (ret) {
+               pr_notice("Failed to register driver\n");
+               qed_put_eth_ops();
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void __exit qede_cleanup(void)
+{
+       pr_notice("qede_cleanup called\n");
+
+       pci_unregister_driver(&qede_pci_driver);
+       qed_put_eth_ops();
+}
+
+module_init(qede_init);
+module_exit(qede_cleanup);
+
+/* -------------------------------------------------------------------------
+ * START OF PROBE / REMOVE
+ * -------------------------------------------------------------------------
+ */
+
+static struct qede_dev *qede_alloc_etherdev(struct qed_dev *cdev,
+                                           struct pci_dev *pdev,
+                                           struct qed_dev_eth_info *info,
+                                           u32 dp_module,
+                                           u8 dp_level)
+{
+       struct net_device *ndev;
+       struct qede_dev *edev;
+
+       ndev = alloc_etherdev_mqs(sizeof(*edev),
+                                 info->num_queues,
+                                 info->num_queues);
+       if (!ndev) {
+               pr_err("etherdev allocation failed\n");
+               return NULL;
+       }
+
+       edev = netdev_priv(ndev);
+       edev->ndev = ndev;
+       edev->cdev = cdev;
+       edev->pdev = pdev;
+       edev->dp_module = dp_module;
+       edev->dp_level = dp_level;
+       edev->ops = qed_ops;
+
+       DP_INFO(edev, "Allocated netdev with 64 tx queues and 64 rx queues\n");
+
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+
+       memcpy(&edev->dev_info, info, sizeof(*info));
+
+       edev->num_tc = edev->dev_info.num_tc;
+
+       return edev;
+}
+
+static void qede_init_ndev(struct qede_dev *edev)
+{
+       struct net_device *ndev = edev->ndev;
+       struct pci_dev *pdev = edev->pdev;
+       u32 hw_features;
+
+       pci_set_drvdata(pdev, ndev);
+
+       ndev->mem_start = edev->dev_info.common.pci_mem_start;
+       ndev->base_addr = ndev->mem_start;
+       ndev->mem_end = edev->dev_info.common.pci_mem_end;
+       ndev->irq = edev->dev_info.common.pci_irq;
+
+       ndev->watchdog_timeo = TX_TIMEOUT;
+
+       /* user-changeble features */
+       hw_features = NETIF_F_GRO | NETIF_F_SG |
+                     NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+                     NETIF_F_TSO | NETIF_F_TSO6;
+
+       ndev->vlan_features = hw_features | NETIF_F_RXHASH | NETIF_F_RXCSUM |
+                             NETIF_F_HIGHDMA;
+       ndev->features = hw_features | NETIF_F_RXHASH | NETIF_F_RXCSUM |
+                        NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HIGHDMA |
+                        NETIF_F_HW_VLAN_CTAG_TX;
+
+       ndev->hw_features = hw_features;
+
+       /* Set network device HW mac */
+       ether_addr_copy(edev->ndev->dev_addr, edev->dev_info.common.hw_mac);
+}
+
+/* This function converts from 32b param to two params of level and module
+ * Input 32b decoding:
+ * b31 - enable all NOTICE prints. NOTICE prints are for deviation from the
+ * 'happy' flow, e.g. memory allocation failed.
+ * b30 - enable all INFO prints. INFO prints are for major steps in the flow
+ * and provide important parameters.
+ * b29-b0 - per-module bitmap, where each bit enables VERBOSE prints of that
+ * module. VERBOSE prints are for tracking the specific flow in low level.
+ *
+ * Notice that the level should be that of the lowest required logs.
+ */
+static void qede_config_debug(uint debug, u32 *p_dp_module, u8 *p_dp_level)
+{
+       *p_dp_level = QED_LEVEL_NOTICE;
+       *p_dp_module = 0;
+
+       if (debug & QED_LOG_VERBOSE_MASK) {
+               *p_dp_level = QED_LEVEL_VERBOSE;
+               *p_dp_module = (debug & 0x3FFFFFFF);
+       } else if (debug & QED_LOG_INFO_MASK) {
+               *p_dp_level = QED_LEVEL_INFO;
+       } else if (debug & QED_LOG_NOTICE_MASK) {
+               *p_dp_level = QED_LEVEL_NOTICE;
+       }
+}
+
+static void qede_update_pf_params(struct qed_dev *cdev)
+{
+       struct qed_pf_params pf_params;
+
+       /* 16 rx + 16 tx */
+       memset(&pf_params, 0, sizeof(struct qed_pf_params));
+       pf_params.eth_pf_params.num_cons = 32;
+       qed_ops->common->update_pf_params(cdev, &pf_params);
+}
+
+enum qede_probe_mode {
+       QEDE_PROBE_NORMAL,
+};
+
+static int __qede_probe(struct pci_dev *pdev, u32 dp_module, u8 dp_level,
+                       enum qede_probe_mode mode)
+{
+       struct qed_slowpath_params params;
+       struct qed_dev_eth_info dev_info;
+       struct qede_dev *edev;
+       struct qed_dev *cdev;
+       int rc;
+
+       if (unlikely(dp_level & QED_LEVEL_INFO))
+               pr_notice("Starting qede probe\n");
+
+       cdev = qed_ops->common->probe(pdev, QED_PROTOCOL_ETH,
+                                     dp_module, dp_level);
+       if (!cdev) {
+               rc = -ENODEV;
+               goto err0;
+       }
+
+       qede_update_pf_params(cdev);
+
+       /* Start the Slowpath-process */
+       memset(&params, 0, sizeof(struct qed_slowpath_params));
+       params.int_mode = QED_INT_MODE_MSIX;
+       params.drv_major = QEDE_MAJOR_VERSION;
+       params.drv_minor = QEDE_MINOR_VERSION;
+       params.drv_rev = QEDE_REVISION_VERSION;
+       params.drv_eng = QEDE_ENGINEERING_VERSION;
+       strlcpy(params.name, "qede LAN", QED_DRV_VER_STR_SIZE);
+       rc = qed_ops->common->slowpath_start(cdev, &params);
+       if (rc) {
+               pr_notice("Cannot start slowpath\n");
+               goto err1;
+       }
+
+       /* Learn information crucial for qede to progress */
+       rc = qed_ops->fill_dev_info(cdev, &dev_info);
+       if (rc)
+               goto err2;
+
+       edev = qede_alloc_etherdev(cdev, pdev, &dev_info, dp_module,
+                                  dp_level);
+       if (!edev) {
+               rc = -ENOMEM;
+               goto err2;
+       }
+
+       qede_init_ndev(edev);
+
+       edev->ops->common->set_id(cdev, edev->ndev->name, DRV_MODULE_VERSION);
+
+       DP_INFO(edev, "Ending successfully qede probe\n");
+
+       return 0;
+
+err2:
+       qed_ops->common->slowpath_stop(cdev);
+err1:
+       qed_ops->common->remove(cdev);
+err0:
+       return rc;
+}
+
+static int qede_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+       u32 dp_module = 0;
+       u8 dp_level = 0;
+
+       qede_config_debug(debug, &dp_module, &dp_level);
+
+       return __qede_probe(pdev, dp_module, dp_level,
+                           QEDE_PROBE_NORMAL);
+}
+
+enum qede_remove_mode {
+       QEDE_REMOVE_NORMAL,
+};
+
+static void __qede_remove(struct pci_dev *pdev, enum qede_remove_mode mode)
+{
+       struct net_device *ndev = pci_get_drvdata(pdev);
+       struct qede_dev *edev = netdev_priv(ndev);
+       struct qed_dev *cdev = edev->cdev;
+
+       DP_INFO(edev, "Starting qede_remove\n");
+
+       edev->ops->common->set_power_state(cdev, PCI_D0);
+
+       pci_set_drvdata(pdev, NULL);
+
+       free_netdev(ndev);
+
+       /* Use global ops since we've freed edev */
+       qed_ops->common->slowpath_stop(cdev);
+       qed_ops->common->remove(cdev);
+
+       pr_notice("Ending successfully qede_remove\n");
+}
+
+static void qede_remove(struct pci_dev *pdev)
+{
+       __qede_remove(pdev, QEDE_REMOVE_NORMAL);
+}