Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / drivers / net / vxge / vxge_main.c
diff --git a/qemu/roms/ipxe/src/drivers/net/vxge/vxge_main.c b/qemu/roms/ipxe/src/drivers/net/vxge/vxge_main.c
new file mode 100644 (file)
index 0000000..130eab6
--- /dev/null
@@ -0,0 +1,718 @@
+/*
+ * vxge-main.c: iPXE driver for Neterion Inc's X3100 Series 10GbE
+ *              PCIe I/O Virtualized Server Adapter.
+ *
+ * Copyright(c) 2002-2010 Neterion Inc.
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License (GPL), incorporated herein by
+ * reference.  Drivers based on or derived from this code fall under
+ * the GPL and must retain the authorship, copyright and license
+ * notice.
+ *
+ */
+
+FILE_LICENCE(GPL2_ONLY);
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ipxe/io.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/pci.h>
+#include <ipxe/malloc.h>
+#include <ipxe/if_ether.h>
+#include <ipxe/ethernet.h>
+#include <ipxe/iobuf.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/timer.h>
+#include <nic.h>
+
+#include "vxge_main.h"
+#include "vxge_reg.h"
+
+/* function modes strings */
+static char *vxge_func_mode_names[] = {
+       "Single Function - 1 func, 17 vpath",
+       "Multi Function 8 - 8 func, 2 vpath per func",
+       "SRIOV 17 - 17 VF, 1 vpath per VF",
+       "WLPEX/SharedIO 17 - 17 VH, 1 vpath/func/hierarchy",
+       "WLPEX/SharedIO 8 - 8 VH, 2 vpath/func/hierarchy",
+       "Multi Function 17 - 17 func, 1 vpath per func",
+       "SRIOV 8 - 1 PF, 7 VF, 2 vpath per VF",
+       "SRIOV 4 - 1 PF, 3 VF, 4 vpath per VF",
+       "Multi Function 2 - 2 func, 8 vpath per func",
+       "Multi Function 4 - 4 func, 4 vpath per func",
+       "WLPEX/SharedIO 4 - 17 func, 1 vpath per func (PCIe ARI)",
+       "Multi Function 8 - For ESX DirectIO - 8 func, 2 vpath per func",
+};
+
+static inline int is_vxge_card_up(struct vxgedev *vdev)
+{
+       return test_bit(__VXGE_STATE_CARD_UP, vdev->state);
+}
+
+/*
+ * vxge_xmit_compl
+ *
+ * If an interrupt was raised to indicate DMA complete of the Tx packet,
+ * this function is called. It identifies the last TxD whose buffer was
+ * freed and frees all skbs whose data have already DMA'ed into the NICs
+ * internal memory.
+ */
+enum vxge_hw_status
+vxge_xmit_compl(struct __vxge_hw_fifo *fifo_hw,
+               struct vxge_hw_fifo_txd *txdp, enum vxge_hw_fifo_tcode tcode)
+{
+       struct net_device *netdev;
+       struct io_buffer *tx_iob = NULL;
+
+       vxge_trace();
+
+       netdev = fifo_hw->vpathh->hldev->ndev;
+
+       tx_iob = (struct io_buffer *)(intptr_t)txdp->host_control;
+
+       if (tcode == VXGE_HW_FIFO_T_CODE_OK) {
+               netdev_tx_complete(netdev, tx_iob);
+       } else {
+               netdev_tx_complete_err(netdev, tx_iob, -EINVAL);
+               vxge_debug(VXGE_ERR, "%s: transmit failed, tcode %d\n",
+                               netdev->name, tcode);
+       }
+
+       memset(txdp, 0, sizeof(struct vxge_hw_fifo_txd));
+
+       return VXGE_HW_OK;
+}
+
+/* reset vpaths */
+enum vxge_hw_status vxge_reset_all_vpaths(struct vxgedev *vdev)
+{
+       enum vxge_hw_status status = VXGE_HW_OK;
+       struct __vxge_hw_virtualpath *vpath;
+
+       vxge_trace();
+
+       vpath = vdev->vpath.vpathh;
+
+       if (vpath) {
+               if ((status = vxge_hw_vpath_reset(vpath)) == VXGE_HW_OK) {
+                       if (is_vxge_card_up(vdev) &&
+                               (status = vxge_hw_vpath_recover_from_reset(
+                                       vpath)) != VXGE_HW_OK) {
+                               vxge_debug(VXGE_ERR, "vxge_hw_vpath_recover_"
+                                       "from_reset failed\n");
+                               return status;
+                       } else {
+                               status = __vxge_hw_vpath_reset_check(vpath);
+                               if (status != VXGE_HW_OK) {
+                                       vxge_debug(VXGE_ERR,
+                                       "__vxge_hw_vpath_reset_check error\n");
+                                       return status;
+                               }
+                       }
+               } else {
+                       vxge_debug(VXGE_ERR, "vxge_hw_vpath_reset failed\n");
+                       return status;
+               }
+       }
+       return status;
+}
+
+/* close vpaths */
+void vxge_close_vpaths(struct vxgedev *vdev)
+{
+
+       if (vdev->vpath.vpathh && vdev->vpath.is_open)
+               vxge_hw_vpath_close(vdev->vpath.vpathh);
+
+       vdev->vpath.is_open = 0;
+       vdev->vpath.vpathh = NULL;
+}
+
+/* open vpaths */
+int vxge_open_vpaths(struct vxgedev *vdev)
+{
+       enum vxge_hw_status status;
+       struct __vxge_hw_device *hldev;
+
+       hldev = (struct __vxge_hw_device  *)pci_get_drvdata(vdev->pdev);
+
+       vdev->vpath.vpathh = &hldev->virtual_path;
+       vdev->vpath.fifo.ndev = vdev->ndev;
+       vdev->vpath.fifo.pdev = vdev->pdev;
+       vdev->vpath.fifo.fifoh = &hldev->virtual_path.fifoh;
+       vdev->vpath.ring.ndev = vdev->ndev;
+       vdev->vpath.ring.pdev = vdev->pdev;
+       vdev->vpath.ring.ringh = &hldev->virtual_path.ringh;
+
+       status = vxge_hw_vpath_open(vdev->devh, &vdev->vpath);
+       if (status == VXGE_HW_OK) {
+               vdev->vpath.is_open = 1;
+       } else {
+               vxge_debug(VXGE_ERR,
+                       "%s: vpath: %d failed to open "
+                       "with status: %d\n",
+                       vdev->ndev->name, vdev->vpath.device_id,
+                       status);
+               vxge_close_vpaths(vdev);
+               return status;
+       }
+
+       hldev->vpaths_deployed |= vxge_mBIT(vdev->vpath.vpathh->vp_id);
+
+       return VXGE_HW_OK;
+}
+
+/** Functions that implement the iPXE driver API **/
+
+/**
+ * vxge_xmit
+ * @skb : the socket buffer containing the Tx data.
+ * @dev : device pointer.
+ *
+ * This function is the Tx entry point of the driver. Neterion NIC supports
+ * certain protocol assist features on Tx side, namely  CSO, S/G, LSO.
+ */
+static int
+vxge_xmit(struct net_device *dev, struct io_buffer *iobuf)
+{
+       struct vxge_fifo *fifo = NULL;
+       struct vxgedev *vdev = NULL;
+       struct __vxge_hw_fifo *fifoh;
+       struct vxge_hw_fifo_txd *txdp;
+
+       vxge_trace();
+
+       vdev = (struct vxgedev *)netdev_priv(dev);
+
+       if (!is_vxge_card_up(vdev)) {
+               vxge_debug(VXGE_ERR,
+                       "%s: vdev not initialized\n", dev->name);
+               return -EIO;
+       }
+
+       if (!netdev_link_ok(dev)) {
+               vxge_debug(VXGE_ERR,
+                       "%s: Link down, transmit failed\n", dev->name);
+               return -ENETDOWN;
+       }
+
+       fifo = &vdev->vpath.fifo;
+       fifoh = fifo->fifoh;
+
+       txdp = vxge_hw_fifo_free_txdl_get(fifoh);
+       if (!txdp) {
+               vxge_debug(VXGE_ERR,
+                       "%s: Out of tx descriptors\n", dev->name);
+               return -ENOBUFS;
+       }
+
+       vxge_debug(VXGE_XMIT, "%s: %s:%d fifoh offset= %d\n",
+               dev->name, __func__, __LINE__, fifoh->sw_offset);
+
+       vxge_hw_fifo_txdl_buffer_set(fifoh, txdp, iobuf);
+
+       vxge_hw_fifo_txdl_post(fifoh, txdp);
+
+       return 0;
+}
+
+/*
+ *  vxge_poll
+ *  @ndev: net device pointer
+ *
+ *  This function acks the interrupt. It polls for rx packets
+ *  and send to upper layer. It also checks for tx completion
+ *  and frees iobs.
+ */
+static void vxge_poll(struct net_device *ndev)
+{
+       struct __vxge_hw_device  *hldev;
+       struct vxgedev *vdev;
+
+       vxge_debug(VXGE_POLL, "%s:%d \n", __func__, __LINE__);
+
+       vdev = (struct vxgedev *)netdev_priv(ndev);
+       hldev = (struct __vxge_hw_device *)pci_get_drvdata(vdev->pdev);
+
+       if (!is_vxge_card_up(vdev))
+               return;
+
+       /* process alarm and acknowledge the interrupts */
+       vxge_hw_device_begin_irq(hldev);
+
+       vxge_hw_vpath_poll_tx(&hldev->virtual_path.fifoh);
+
+       vxge_hw_vpath_poll_rx(&hldev->virtual_path.ringh);
+}
+
+/*
+ * vxge_irq - enable or Disable interrupts
+ *
+ * @netdev   netdevice structure reference
+ * @action   requested interrupt action
+ */
+static void vxge_irq(struct net_device *netdev __unused, int action)
+{
+       struct __vxge_hw_device  *hldev;
+       struct vxgedev *vdev;
+
+       vxge_debug(VXGE_INFO,
+               "%s:%d action(%d)\n", __func__, __LINE__, action);
+
+       vdev = (struct vxgedev *)netdev_priv(netdev);
+       hldev = (struct __vxge_hw_device *)pci_get_drvdata(vdev->pdev);
+
+       switch (action) {
+       case DISABLE:
+               vxge_hw_device_mask_all(hldev);
+               break;
+       default:
+               vxge_hw_device_unmask_all(hldev);
+               break;
+       }
+}
+
+/**
+ * vxge_open
+ * @dev: pointer to the device structure.
+ *
+ * This function is the open entry point of the driver. It mainly calls a
+ * function to allocate Rx buffers and inserts them into the buffer
+ * descriptors and then enables the Rx part of the NIC.
+ * Return value: '0' on success and an appropriate (-)ve integer as
+ * defined in errno.h file on failure.
+ */
+int
+vxge_open(struct net_device *dev)
+{
+       enum vxge_hw_status status;
+       struct vxgedev *vdev;
+       struct __vxge_hw_device *hldev;
+       int ret = 0;
+
+       vxge_debug(VXGE_INFO, "%s: %s:%d\n",
+                       VXGE_DRIVER_NAME, __func__, __LINE__);
+
+       vdev = (struct vxgedev *)netdev_priv(dev);
+       hldev = (struct __vxge_hw_device *)pci_get_drvdata(vdev->pdev);
+
+       /* make sure you have link off by default every time Nic is
+        * initialized */
+       netdev_link_down(dev);
+
+       /* Open VPATHs */
+       status = vxge_open_vpaths(vdev);
+       if (status != VXGE_HW_OK) {
+               vxge_debug(VXGE_ERR, "%s: fatal: Vpath open failed\n",
+                               VXGE_DRIVER_NAME);
+               ret = -EPERM;
+               goto out0;
+       }
+
+       vdev->mtu = VXGE_HW_DEFAULT_MTU;
+       /* set initial mtu before enabling the device */
+       status = vxge_hw_vpath_mtu_set(vdev->vpath.vpathh, vdev->mtu);
+       if (status != VXGE_HW_OK) {
+               vxge_debug(VXGE_ERR,
+                       "%s: fatal: can not set new MTU\n", dev->name);
+               ret = -EPERM;
+               goto out2;
+       }
+       vxge_debug(VXGE_INFO,
+               "%s: MTU is %d\n", vdev->ndev->name, vdev->mtu);
+
+       set_bit(__VXGE_STATE_CARD_UP, vdev->state);
+
+       wmb();
+
+       if (vxge_hw_device_link_state_get(vdev->devh) == VXGE_HW_LINK_UP) {
+               netdev_link_up(vdev->ndev);
+               vxge_debug(VXGE_INFO, "%s: Link Up\n", vdev->ndev->name);
+       }
+
+       vxge_hw_device_intr_enable(hldev);
+
+       vxge_hw_vpath_enable(vdev->vpath.vpathh);
+       wmb();
+       vxge_hw_vpath_rx_doorbell_init(vdev->vpath.vpathh);
+
+       goto out0;
+
+out2:
+       vxge_close_vpaths(vdev);
+out0:
+       vxge_debug(VXGE_INFO, "%s: %s:%d  Exiting...\n",
+                               dev->name, __func__, __LINE__);
+       return ret;
+}
+
+/**
+ * vxge_close
+ * @dev: device pointer.
+ *
+ * This is the stop entry point of the driver. It needs to undo exactly
+ * whatever was done by the open entry point, thus it's usually referred to
+ * as the close function.Among other things this function mainly stops the
+ * Rx side of the NIC and frees all the Rx buffers in the Rx rings.
+ * Return value: '0' on success and an appropriate (-)ve integer as
+ * defined in errno.h file on failure.
+ */
+static void vxge_close(struct net_device *dev)
+{
+       struct vxgedev *vdev;
+       struct __vxge_hw_device *hldev;
+
+       vxge_debug(VXGE_INFO, "%s: %s:%d\n",
+               dev->name, __func__, __LINE__);
+
+       vdev = (struct vxgedev *)netdev_priv(dev);
+       hldev = (struct __vxge_hw_device *)pci_get_drvdata(vdev->pdev);
+
+       if (!is_vxge_card_up(vdev))
+               return;
+
+       clear_bit(__VXGE_STATE_CARD_UP, vdev->state);
+
+       vxge_hw_vpath_set_zero_rx_frm_len(hldev);
+
+       netdev_link_down(vdev->ndev);
+       vxge_debug(VXGE_INFO, "%s: Link Down\n", vdev->ndev->name);
+
+       /* Note that at this point xmit() is stopped by upper layer */
+       vxge_hw_device_intr_disable(hldev);
+
+       /* Multi function shares INTA, hence we should
+        * leave it in enabled state
+        */
+       if (is_mf(hldev->hw_info.function_mode))
+               vxge_hw_device_unmask_all(hldev);
+
+       vxge_reset_all_vpaths(vdev);
+
+       vxge_close_vpaths(vdev);
+
+       vxge_debug(VXGE_INFO,
+               "%s: %s:%d  Exiting...\n", dev->name, __func__, __LINE__);
+}
+
+static struct net_device_operations vxge_operations;
+
+int vxge_device_register(struct __vxge_hw_device *hldev,
+                               struct vxgedev **vdev_out)
+{
+       struct net_device *ndev;
+       struct vxgedev *vdev;
+       int ret = 0;
+
+       *vdev_out = NULL;
+
+       ndev = alloc_etherdev(sizeof(struct vxgedev));
+       if (ndev == NULL) {
+               vxge_debug(VXGE_ERR, "%s : device allocation failed\n",
+                               __func__);
+               ret = -ENODEV;
+               goto _out0;
+       }
+
+       vxge_debug(VXGE_INFO, "%s:%d  netdev registering\n",
+               __func__, __LINE__);
+       vdev = netdev_priv(ndev);
+       memset(vdev, 0, sizeof(struct vxgedev));
+
+       vdev->ndev = ndev;
+       vdev->devh = hldev;
+       vdev->pdev = hldev->pdev;
+
+       ndev->dev = &vdev->pdev->dev;
+       /* Associate vxge-specific network operations operations with
+        * generic network device layer */
+       netdev_init(ndev, &vxge_operations);
+
+       memcpy(ndev->hw_addr,
+               (u8 *)hldev->hw_info.mac_addrs[hldev->first_vp_id], ETH_ALEN);
+
+       if (register_netdev(ndev)) {
+               vxge_debug(VXGE_ERR, "%s : device registration failed!\n",
+                       __func__);
+               ret = -ENODEV;
+               goto _out2;
+       }
+
+       /* Leave link state as off at this point, when the link change
+        * interrupt comes the state will be automatically changed to
+        * the right state.
+        */
+
+       vxge_debug(VXGE_INFO, "%s: Ethernet device registered\n",
+               VXGE_DRIVER_NAME);
+
+       *vdev_out = vdev;
+
+       return ret;
+_out2:
+       netdev_put(ndev);
+_out0:
+       return ret;
+}
+
+/*
+ * vxge_device_unregister
+ *
+ * This function will unregister and free network device
+ */
+void
+vxge_device_unregister(struct __vxge_hw_device *hldev)
+{
+       struct net_device *ndev;
+
+       ndev = hldev->ndev;
+
+       unregister_netdev(ndev);
+       netdev_nullify(ndev);
+       netdev_put(ndev);
+
+       vxge_debug(VXGE_INFO, "%s: ethernet device unregistered\n",
+                               VXGE_DRIVER_NAME);
+}
+
+/**
+ * vxge_probe
+ * @pdev : structure containing the PCI related information of the device.
+ * @id: List of PCI devices supported by the driver listed in vxge_id_table.
+ * Description:
+ * This function is called when a new PCI device gets detected and initializes
+ * it.
+ * Return value:
+ * returns 0 on success and negative on failure.
+ *
+ */
+static int
+vxge_probe(struct pci_device *pdev)
+{
+       struct __vxge_hw_device  *hldev;
+       enum vxge_hw_status status;
+       int ret = 0;
+       u64 vpath_mask = 0;
+       struct vxgedev *vdev;
+       int i;
+       u8 revision, titan1;
+       u32 function_mode;
+       unsigned long mmio_start, mmio_len;
+       void *bar0;
+       struct vxge_hw_device_hw_info hw_info;
+       struct vxge_hw_device_version *fw_version;
+
+       vxge_debug(VXGE_INFO, "vxge_probe for device " PCI_FMT "\n",
+                       PCI_ARGS(pdev));
+
+       pci_read_config_byte(pdev, PCI_REVISION_ID, &revision);
+       titan1 = is_titan1(pdev->device, revision);
+
+       mmio_start = pci_bar_start(pdev, PCI_BASE_ADDRESS_0);
+       mmio_len   = pci_bar_size(pdev, PCI_BASE_ADDRESS_0);
+       vxge_debug(VXGE_INFO, "mmio_start: %#08lx, mmio_len: %#08lx\n",
+                       mmio_start, mmio_len);
+
+       /* sets the bus master */
+       adjust_pci_device(pdev);
+
+       bar0 = ioremap(mmio_start, mmio_len);
+       if (!bar0) {
+               vxge_debug(VXGE_ERR,
+                       "%s : cannot remap io memory bar0\n", __func__);
+               ret = -ENODEV;
+               goto _exit0;
+       }
+
+       status = vxge_hw_device_hw_info_get(pdev, bar0, &hw_info);
+       if (status != VXGE_HW_OK) {
+               vxge_debug(VXGE_ERR,
+                       "%s: Reading of hardware info failed.\n",
+                       VXGE_DRIVER_NAME);
+               ret = -EINVAL;
+               goto _exit1;
+       }
+
+       if (hw_info.func_id != 0) {
+               /* Non zero function, So do not load the driver */
+               iounmap(bar0);
+               pci_set_drvdata(pdev, NULL);
+               return -EINVAL;
+       }
+
+
+       vpath_mask = hw_info.vpath_mask;
+       if (vpath_mask == 0) {
+               vxge_debug(VXGE_ERR,
+                       "%s: No vpaths available in device\n",
+                       VXGE_DRIVER_NAME);
+               ret = -EINVAL;
+               goto _exit1;
+       }
+       vxge_debug(VXGE_INFO,
+               "%s:%d  Vpath mask = %llx\n", __func__, __LINE__,
+               (unsigned long long)vpath_mask);
+
+       fw_version = &hw_info.fw_version;
+       /* fail the driver loading if firmware is incompatible */
+       if ((fw_version->major != VXGE_CERT_FW_VER_MAJOR) ||
+               (fw_version->minor < VXGE_CERT_FW_VER_MINOR)) {
+               printf("%s: Adapter's current firmware version: %d.%d.%d\n",
+                       VXGE_DRIVER_NAME, fw_version->major,
+                       fw_version->minor, fw_version->build);
+
+               printf("%s: Upgrade firmware to version %d.%d.%d\n",
+                       VXGE_DRIVER_NAME, VXGE_CERT_FW_VER_MAJOR,
+                       VXGE_CERT_FW_VER_MINOR, VXGE_CERT_FW_VER_BUILD);
+
+               ret = -EACCES;
+               goto _exit1;
+       }
+
+       status = vxge_hw_device_initialize(&hldev, bar0, pdev, titan1);
+       if (status != VXGE_HW_OK) {
+               vxge_debug(VXGE_ERR,
+                       "Failed to initialize device (%d)\n", status);
+                       ret = -EINVAL;
+                       goto _exit1;
+       }
+       memcpy(&hldev->hw_info, &hw_info,
+               sizeof(struct vxge_hw_device_hw_info));
+
+       /* find the vpath id of the first available one */
+       for (i = 0; i < VXGE_HW_MAX_VIRTUAL_PATHS; i++)
+               if (vpath_mask & vxge_mBIT(i)) {
+                       hldev->first_vp_id = i;
+                       break;
+               }
+       /* if FCS stripping is not disabled in MAC fail driver load */
+       if (vxge_hw_vpath_strip_fcs_check(hldev, vpath_mask) != VXGE_HW_OK) {
+               vxge_debug(VXGE_ERR,
+                       "%s: FCS stripping is not disabled in MAC"
+                       " failing driver load\n", VXGE_DRIVER_NAME);
+               ret = -EINVAL;
+               goto _exit2;
+       }
+
+       /* Read function mode */
+       status = vxge_hw_get_func_mode(hldev, &function_mode);
+       if (status != VXGE_HW_OK)
+               goto _exit2;
+
+       hldev->hw_info.function_mode = function_mode;
+
+       /* set private device info */
+       pci_set_drvdata(pdev, hldev);
+
+       if (vxge_device_register(hldev, &vdev)) {
+               ret = -EINVAL;
+               goto _exit2;
+       }
+
+       /* set private HW device info */
+       hldev->ndev = vdev->ndev;
+       hldev->vdev = vdev;
+       hldev->pdev = pdev;
+       vdev->mtu = VXGE_HW_DEFAULT_MTU;
+       vdev->bar0 = bar0;
+       vdev->titan1 = titan1;
+       /* Virtual Path count */
+       vdev->vpath.device_id = hldev->first_vp_id;
+       vdev->vpath.vdev = vdev;
+       memcpy((u8 *)vdev->vpath.macaddr,
+                       (u8 *)hldev->hw_info.mac_addrs[hldev->first_vp_id],
+                       ETH_ALEN);
+
+       hldev->hw_info.serial_number[VXGE_HW_INFO_LEN - 1] = '\0';
+       hldev->hw_info.product_desc[VXGE_HW_INFO_LEN - 1] = '\0';
+       hldev->hw_info.part_number[VXGE_HW_INFO_LEN - 1] = '\0';
+
+       vxge_debug(VXGE_INFO, "%s: Neterion %s Server Adapter\n",
+               VXGE_DRIVER_NAME, hldev->hw_info.product_desc);
+       vxge_debug(VXGE_INFO, "%s: SERIAL NUMBER: %s\n",
+               VXGE_DRIVER_NAME, hldev->hw_info.serial_number);
+       vxge_debug(VXGE_INFO, "%s: PART NUMBER: %s\n",
+               VXGE_DRIVER_NAME, hldev->hw_info.part_number);
+       vxge_debug(VXGE_INFO, "%s: MAC ADDR: %s\n",
+               VXGE_DRIVER_NAME, eth_ntoa(vdev->vpath.macaddr));
+       vxge_debug(VXGE_INFO,
+               "%s: Firmware version : %s Date : %s\n", VXGE_DRIVER_NAME,
+               hldev->hw_info.fw_version.version,
+               hldev->hw_info.fw_date.date);
+       vxge_debug(VXGE_INFO, "%s: %s Enabled\n",
+                       VXGE_DRIVER_NAME, vxge_func_mode_names[function_mode]);
+
+       vxge_debug(VXGE_INFO, "%s: %s:%d  Probe Exiting...\n",
+               VXGE_DRIVER_NAME, __func__, __LINE__);
+
+       return 0;
+
+_exit2:
+       vxge_hw_device_terminate(hldev);
+_exit1:
+       iounmap(bar0);
+_exit0:
+       pci_set_drvdata(pdev, NULL);
+       printf("%s: WARNING!! Driver loading failed!!\n",
+               VXGE_DRIVER_NAME);
+
+       return ret;
+}
+
+/**
+ * vxge_remove - Free the PCI device
+ * @pdev: structure containing the PCI related information of the device.
+ * Description: This function is called by the Pci subsystem to release a
+ * PCI device and free up all resource held up by the device.
+ */
+static void
+vxge_remove(struct pci_device *pdev)
+{
+       struct __vxge_hw_device  *hldev;
+       struct vxgedev *vdev = NULL;
+       struct net_device *ndev;
+
+       vxge_debug(VXGE_INFO,
+               "%s:%d\n", __func__, __LINE__);
+       hldev = (struct __vxge_hw_device  *) pci_get_drvdata(pdev);
+       if (hldev == NULL)
+               return;
+
+       ndev = hldev->ndev;
+       vdev = netdev_priv(ndev);
+
+       iounmap(vdev->bar0);
+
+       vxge_device_unregister(hldev);
+
+       vxge_debug(VXGE_INFO,
+               "%s:%d  Device unregistered\n", __func__, __LINE__);
+
+       vxge_hw_device_terminate(hldev);
+       pci_set_drvdata(pdev, NULL);
+}
+
+/* vxge net device operations */
+static struct net_device_operations vxge_operations = {
+       .open           = vxge_open,
+       .close          = vxge_close,
+       .transmit       = vxge_xmit,
+       .poll           = vxge_poll,
+       .irq            = vxge_irq,
+};
+
+static struct pci_device_id vxge_main_nics[] = {
+       /* If you change this, also adjust vxge_nics[] in vxge.c */
+       PCI_ID(0x17d5, 0x5833, "vxge-x3100", "Neterion X3100 Series", 0),
+};
+
+struct pci_driver vxge_driver __pci_driver = {
+       .ids = vxge_main_nics,
+       .id_count = (sizeof(vxge_main_nics) / sizeof(vxge_main_nics[0])),
+       .probe = vxge_probe,
+       .remove = vxge_remove,
+};