vxge: add support for ethtool firmware flashing
[pandora-kernel.git] / drivers / net / vxge / vxge-main.c
index 53db6a4..10549bd 100644 (file)
@@ -50,6 +50,7 @@
 #include <net/ip.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/firmware.h>
 #include "vxge-main.h"
 #include "vxge-reg.h"
 
@@ -3248,6 +3249,7 @@ static int __devinit vxge_device_register(struct __vxge_hw_device *hldev,
                "%s: Ethernet device registered",
                ndev->name);
 
+       hldev->ndev = ndev;
        *vdev_out = vdev;
 
        /* Resetting the Device stats */
@@ -3935,6 +3937,142 @@ static inline u32 vxge_get_num_vfs(u64 function_mode)
        return num_functions;
 }
 
+int vxge_fw_upgrade(struct vxgedev *vdev, char *fw_name, int override)
+{
+       struct __vxge_hw_device *hldev = vdev->devh;
+       u32 maj, min, bld, cmaj, cmin, cbld;
+       enum vxge_hw_status status;
+       const struct firmware *fw;
+       int ret;
+
+       ret = request_firmware(&fw, fw_name, &vdev->pdev->dev);
+       if (ret) {
+               vxge_debug_init(VXGE_ERR, "%s: Firmware file '%s' not found",
+                               VXGE_DRIVER_NAME, fw_name);
+               goto out;
+       }
+
+       /* Load the new firmware onto the adapter */
+       status = vxge_update_fw_image(hldev, fw->data, fw->size);
+       if (status != VXGE_HW_OK) {
+               vxge_debug_init(VXGE_ERR,
+                               "%s: FW image download to adapter failed '%s'.",
+                               VXGE_DRIVER_NAME, fw_name);
+               ret = -EIO;
+               goto out;
+       }
+
+       /* Read the version of the new firmware */
+       status = vxge_hw_upgrade_read_version(hldev, &maj, &min, &bld);
+       if (status != VXGE_HW_OK) {
+               vxge_debug_init(VXGE_ERR,
+                               "%s: Upgrade read version failed '%s'.",
+                               VXGE_DRIVER_NAME, fw_name);
+               ret = -EIO;
+               goto out;
+       }
+
+       cmaj = vdev->config.device_hw_info.fw_version.major;
+       cmin = vdev->config.device_hw_info.fw_version.minor;
+       cbld = vdev->config.device_hw_info.fw_version.build;
+       /* It's possible the version in /lib/firmware is not the latest version.
+        * If so, we could get into a loop of trying to upgrade to the latest
+        * and flashing the older version.
+        */
+       if (VXGE_FW_VER(maj, min, bld) == VXGE_FW_VER(cmaj, cmin, cbld) &&
+           !override) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       printk(KERN_NOTICE "Upgrade to firmware version %d.%d.%d commencing\n",
+              maj, min, bld);
+
+       /* Flash the adapter with the new firmware */
+       status = vxge_hw_flash_fw(hldev);
+       if (status != VXGE_HW_OK) {
+               vxge_debug_init(VXGE_ERR, "%s: Upgrade commit failed '%s'.",
+                               VXGE_DRIVER_NAME, fw_name);
+               ret = -EIO;
+               goto out;
+       }
+
+       printk(KERN_NOTICE "Upgrade of firmware successful!  Adapter must be "
+              "hard reset before using, thus requiring a system reboot or a "
+              "hotplug event.\n");
+
+out:
+       return ret;
+}
+
+static int vxge_probe_fw_update(struct vxgedev *vdev)
+{
+       u32 maj, min, bld;
+       int ret, gpxe = 0;
+       char *fw_name;
+
+       maj = vdev->config.device_hw_info.fw_version.major;
+       min = vdev->config.device_hw_info.fw_version.minor;
+       bld = vdev->config.device_hw_info.fw_version.build;
+
+       if (VXGE_FW_VER(maj, min, bld) == VXGE_CERT_FW_VER)
+               return 0;
+
+       /* Ignore the build number when determining if the current firmware is
+        * "too new" to load the driver
+        */
+       if (VXGE_FW_VER(maj, min, 0) > VXGE_CERT_FW_VER) {
+               vxge_debug_init(VXGE_ERR, "%s: Firmware newer than last known "
+                               "version, unable to load driver\n",
+                               VXGE_DRIVER_NAME);
+               return -EINVAL;
+       }
+
+       /* Firmware 1.4.4 and older cannot be upgraded, and is too ancient to
+        * work with this driver.
+        */
+       if (VXGE_FW_VER(maj, min, bld) <= VXGE_FW_DEAD_VER) {
+               vxge_debug_init(VXGE_ERR, "%s: Firmware %d.%d.%d cannot be "
+                               "upgraded\n", VXGE_DRIVER_NAME, maj, min, bld);
+               return -EINVAL;
+       }
+
+       /* If file not specified, determine gPXE or not */
+       if (VXGE_FW_VER(maj, min, bld) >= VXGE_EPROM_FW_VER) {
+               int i;
+               for (i = 0; i < VXGE_HW_MAX_ROM_IMAGES; i++)
+                       if (vdev->devh->eprom_versions[i]) {
+                               gpxe = 1;
+                               break;
+                       }
+       }
+       if (gpxe)
+               fw_name = "vxge/X3fw-pxe.ncf";
+       else
+               fw_name = "vxge/X3fw.ncf";
+
+       ret = vxge_fw_upgrade(vdev, fw_name, 0);
+       /* -EINVAL and -ENOENT are not fatal errors for flashing firmware on
+        * probe, so ignore them
+        */
+       if (ret != -EINVAL && ret != -ENOENT)
+               return -EIO;
+       else
+               ret = 0;
+
+       if (VXGE_FW_VER(VXGE_CERT_FW_VER_MAJOR, VXGE_CERT_FW_VER_MINOR, 0) >
+           VXGE_FW_VER(maj, min, 0)) {
+               vxge_debug_init(VXGE_ERR, "%s: Firmware %d.%d.%d is too old to"
+                               " be used with this driver.\n"
+                               "Please get the latest version from "
+                               "ftp://ftp.s2io.com/pub/X3100-Drivers/FIRMWARE",
+                               VXGE_DRIVER_NAME, maj, min, bld);
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
 /**
  * vxge_probe
  * @pdev : structure containing the PCI related information of the device.
@@ -4093,16 +4231,6 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre)
                goto _exit3;
        }
 
-       if (ll_config->device_hw_info.fw_version.major !=
-               VXGE_DRIVER_FW_VERSION_MAJOR) {
-               vxge_debug_init(VXGE_ERR,
-                       "%s: Incorrect firmware version."
-                       "Please upgrade the firmware to version 1.x.x",
-                       VXGE_DRIVER_NAME);
-               ret = -EINVAL;
-               goto _exit3;
-       }
-
        vpath_mask = ll_config->device_hw_info.vpath_mask;
        if (vpath_mask == 0) {
                vxge_debug_ll_config(VXGE_TRACE,
@@ -4166,6 +4294,32 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre)
                        goto _exit3;
        }
 
+       if (VXGE_FW_VER(ll_config->device_hw_info.fw_version.major,
+                       ll_config->device_hw_info.fw_version.minor,
+                       ll_config->device_hw_info.fw_version.build) >=
+           VXGE_EPROM_FW_VER) {
+               struct eprom_image img[VXGE_HW_MAX_ROM_IMAGES];
+
+               status = vxge_hw_vpath_eprom_img_ver_get(hldev, img);
+               if (status != VXGE_HW_OK) {
+                       vxge_debug_init(VXGE_ERR, "%s: Reading of EPROM failed",
+                                       VXGE_DRIVER_NAME);
+                       /* This is a non-fatal error, continue */
+               }
+
+               for (i = 0; i < VXGE_HW_MAX_ROM_IMAGES; i++) {
+                       hldev->eprom_versions[i] = img[i].version;
+                       if (!img[i].is_valid)
+                               break;
+                       vxge_debug_init(VXGE_TRACE, "%s: EPROM %d, version "
+                                       "%d.%d.%d.%d\n", VXGE_DRIVER_NAME, i,
+                                       VXGE_EPROM_IMG_MAJOR(img[i].version),
+                                       VXGE_EPROM_IMG_MINOR(img[i].version),
+                                       VXGE_EPROM_IMG_FIX(img[i].version),
+                                       VXGE_EPROM_IMG_BUILD(img[i].version));
+               }
+       }
+
        /* 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_init(VXGE_ERR,
@@ -4194,18 +4348,22 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre)
        ll_config->tx_pause_enable = VXGE_PAUSE_CTRL_ENABLE;
        ll_config->rx_pause_enable = VXGE_PAUSE_CTRL_ENABLE;
 
-       if (vxge_device_register(hldev, ll_config, high_dma, no_of_vpath,
-               &vdev)) {
+       ret = vxge_device_register(hldev, ll_config, high_dma, no_of_vpath,
+                                  &vdev);
+       if (ret) {
                ret = -EINVAL;
                goto _exit4;
        }
 
+       ret = vxge_probe_fw_update(vdev);
+       if (ret)
+               goto _exit5;
+
        vxge_hw_device_debug_set(hldev, VXGE_TRACE, VXGE_COMPONENT_LL);
        VXGE_COPY_DEBUG_INFO_TO_LL(vdev, vxge_hw_device_error_level_get(hldev),
                vxge_hw_device_trace_level_get(hldev));
 
        /* set private HW device info */
-       hldev->ndev = vdev->ndev;
        vdev->mtu = VXGE_HW_DEFAULT_MTU;
        vdev->bar0 = attr.bar0;
        vdev->max_vpath_supported = max_vpath_supported;
@@ -4307,7 +4465,7 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre)
                                "%s: mac_addr_list : memory allocation failed",
                                vdev->ndev->name);
                        ret = -EPERM;
-                       goto _exit5;
+                       goto _exit6;
                }
                macaddr = (u8 *)&entry->macaddr;
                memcpy(macaddr, vdev->ndev->dev_addr, ETH_ALEN);
@@ -4347,10 +4505,10 @@ vxge_probe(struct pci_dev *pdev, const struct pci_device_id *pre)
        kfree(ll_config);
        return 0;
 
-_exit5:
+_exit6:
        for (i = 0; i < vdev->no_of_vpath; i++)
                vxge_free_mac_add_list(&vdev->vpaths[i]);
-
+_exit5:
        vxge_device_unregister(hldev);
 _exit4:
        pci_disable_sriov(pdev);