diff options
Diffstat (limited to 'roms/ipxe/src/drivers/net/vxge/vxge_main.c')
-rw-r--r-- | roms/ipxe/src/drivers/net/vxge/vxge_main.c | 718 |
1 files changed, 718 insertions, 0 deletions
diff --git a/roms/ipxe/src/drivers/net/vxge/vxge_main.c b/roms/ipxe/src/drivers/net/vxge/vxge_main.c new file mode 100644 index 00000000..130eab61 --- /dev/null +++ b/roms/ipxe/src/drivers/net/vxge/vxge_main.c @@ -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, +}; |