diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h
index d2e2c50..68dee6a 100644
--- a/drivers/net/ethernet/intel/igb/e1000_defines.h
+++ b/drivers/net/ethernet/intel/igb/e1000_defines.h
@@ -481,6 +481,7 @@
#define E1000_RAH_POOL_1 0x00040000

 /* Error Codes */
+#define E1000_SUCCESS      0
#define E1000_ERR_NVM      1
#define E1000_ERR_PHY      2
#define E1000_ERR_CONFIG   3
@@ -800,6 +801,37 @@
#define NVM_ETS_DATA_INDEX_SHIFT   8
#define NVM_ETS_DATA_HTHRESH_MASK            0x00FF

+#define E1000_REGISTER_SET_SIZE                       0x20000 /* CSR Size */
+#define E1000_FLA        0x0001C  /* Flash Access - RW */
+#define E1000_FLOP     0x0103C  /* FLASH Opcode Register */
+#define E1000_I350_BARCTRL                  0x5BFC /* BAR ctrl reg */
+#define E1000_I350_DTXMXPKTSZ                         0x355C /* Maximum sent packet size reg*/
+
+#define E1000_SRWR                   0x12018
+#define E1000_EEC_REG                            0x12010
+
+#define E1000_SHADOWINF                     0x12068
+#define E1000_FLFWUPDATE     0x12108
+
+/* os dep */
+#define E1000_READ_FLASH_REG(a, reg) (readl((a)->flash_address + (reg)))
+
+#define E1000_READ_FLASH_REG8(a, reg) ( \
+             readb(READ_ONCE((a)->flash_address) + (reg)))
+
+#define E1000_WRITE_FLASH_REG(a, reg, value) ( \
+             writel((value), ((a)->flash_address + (reg))))
+
+#define E1000_READ_REG(x, y) igb_rd32(x, y)
+#define E1000_READ_REG8(h, r) readb(READ_ONCE((h)->hw_addr) + (r))
+
+#define E1000_WRITE_REG(hw, reg, val) \
+do { \
+             u8 __iomem *hw_addr = READ_ONCE((hw)->hw_addr); \
+             if (!E1000_REMOVED(hw_addr)) \
+                            writel((val), &hw_addr[(reg)]); \
+} while (0)
+
#define E1000_NVM_CFG_DONE_PORT_0  0x040000 /* MNG config cycle done */
#define E1000_NVM_CFG_DONE_PORT_1  0x080000 /* ...for second port */
#define E1000_NVM_CFG_DONE_PORT_2  0x100000 /* ...for third port */
diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h
index 5d87957..ff034d2 100644
--- a/drivers/net/ethernet/intel/igb/e1000_hw.h
+++ b/drivers/net/ethernet/intel/igb/e1000_hw.h
@@ -50,6 +50,19 @@
#define E1000_DEV_ID_I354_SGMII                                       0x1F41
#define E1000_DEV_ID_I354_BACKPLANE_2_5GBPS         0x1F45

+/* NVM Update commands */
+#define E1000_NVMUPD_CMD_REG_READ         0x0000000B
+#define E1000_NVMUPD_CMD_REG_WRITE       0x0000000C
+
+/* NVM Update features API */
+#define E1000_NVMUPD_FEATURES_API_VER_MAJOR                  0
+#define E1000_NVMUPD_FEATURES_API_VER_MINOR                  0
+#define E1000_NVMUPD_FEATURES_API_FEATURES_ARRAY_LEN             12
+#define E1000_NVMUPD_EXEC_FEATURES                                        0xE
+#define E1000_NVMUPD_FEATURE_FLAT_NVM_SUPPORT                          BIT(0)
+#define E1000_NVMUPD_FEATURE_REGISTER_ACCESS_SUPPORT             BIT(1)
+#define E1000_NVMUPD_MOD_PNT_MASK                                      0xFF
+
#define E1000_REVISION_2 2
#define E1000_REVISION_4 4

@@ -514,6 +527,21 @@
              bool mas_capable;
};

+struct e1000_nvm_access {
+             u32 command;
+             u32 config;
+             u32 offset;          /* in bytes */
+             u32 data_size;    /* in bytes */
+             u8 data[1];
+};
+
+struct e1000_nvm_features {
+             u8 major;
+             u8 minor;
+             u16 size;
+             u8 features[E1000_NVMUPD_FEATURES_API_FEATURES_ARRAY_LEN];
+};
+
struct e1000_hw {
              void *back;

@@ -539,6 +567,8 @@
              u16 vendor_id;

               u8  revision_id;
+                            /* NVM Update features */
+             struct e1000_nvm_features nvmupd_features;
};

 struct net_device *igb_get_hw_dev(struct e1000_hw *hw);
diff --git a/drivers/net/ethernet/intel/igb/e1000_nvm.c b/drivers/net/ethernet/intel/igb/e1000_nvm.c
index 09f4dcb..0522e04 100644
--- a/drivers/net/ethernet/intel/igb/e1000_nvm.c
+++ b/drivers/net/ethernet/intel/igb/e1000_nvm.c
@@ -780,3 +780,17 @@
                                            | eeprom_verl;
              }
}
+
+/* NVM Update features structure initialization */
+void e1000_init_nvmupdate_features(struct e1000_hw *hw)
+{
+             hw->nvmupd_features.major = E1000_NVMUPD_FEATURES_API_VER_MAJOR;
+             hw->nvmupd_features.minor = E1000_NVMUPD_FEATURES_API_VER_MINOR;
+             hw->nvmupd_features.size = sizeof(hw->nvmupd_features);
+             memset(hw->nvmupd_features.features, 0x0,
+                    E1000_NVMUPD_FEATURES_API_FEATURES_ARRAY_LEN *
+                    sizeof(*hw->nvmupd_features.features));
+
+             hw->nvmupd_features.features[0] =
+                            E1000_NVMUPD_FEATURE_REGISTER_ACCESS_SUPPORT;
+}
diff --git a/drivers/net/ethernet/intel/igb/e1000_nvm.h b/drivers/net/ethernet/intel/igb/e1000_nvm.h
index 091cddf..2f89b0b 100644
--- a/drivers/net/ethernet/intel/igb/e1000_nvm.h
+++ b/drivers/net/ethernet/intel/igb/e1000_nvm.h
@@ -32,5 +32,6 @@
              u16 or_patch;
};
void igb_get_fw_version(struct e1000_hw *hw, struct e1000_fw_version *fw_vers);
+void e1000_init_nvmupdate_features(struct e1000_hw *hw);

 #endif
diff --git a/drivers/net/ethernet/intel/igb/e1000_regs.h b/drivers/net/ethernet/intel/igb/e1000_regs.h
index 0ad737d..7d2ea56 100644
--- a/drivers/net/ethernet/intel/igb/e1000_regs.h
+++ b/drivers/net/ethernet/intel/igb/e1000_regs.h
@@ -390,6 +390,7 @@
#define E1000_O2BGPTC             0x08FE4 /* OS2BMC packets received by BMC */
#define E1000_O2BSPC 0x0415C /* OS2BMC packets transmitted by host */

+#define E1000_EEC_REG                            0x12010
#define E1000_SRWR                    0x12018  /* Shadow Ram Write Register - RW */
#define E1000_I210_FLMNGCTL 0x12038
#define E1000_I210_FLMNGDATA            0x1203C
@@ -398,6 +399,8 @@
#define E1000_I210_FLSWCTL    0x12048
#define E1000_I210_FLSWDATA 0x1204C
#define E1000_I210_FLSWCNT   0x12050
+#define E1000_SHADOWINF                     0x12068
+#define E1000_FLFWUPDATE     0x12108

 #define E1000_I210_FLA                             0x1201C

diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h
index ca54e26..6a425c9 100644
--- a/drivers/net/ethernet/intel/igb/igb.h
+++ b/drivers/net/ethernet/intel/igb/igb.h
@@ -504,6 +504,7 @@
              u16 link_duplex;

               u8 __iomem *io_addr; /* Mainly for iounmap use */
+             u8 __iomem *flash_addr;

               struct work_struct reset_task;
              struct work_struct watchdog_task;
diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c
index c645d9e..0a82cf5 100644
--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c
+++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
@@ -712,10 +712,181 @@
                             regs_buff[727 + i] = rd32(E1000_TDWBAH(i + 4));
}

+static u8 igb_nvmupd_get_module(u32 val)
+{
+             return (u8)(val & E1000_NVMUPD_MOD_PNT_MASK);
+}
+
+static int igb_nvmupd_validate_offset(struct igb_adapter *adapter, u32 offset)
+{
+             if (offset >= E1000_REGISTER_SET_SIZE)
+                            return 0;
+
+             switch (offset) {
+             case E1000_CTRL:
+             case E1000_STATUS:
+             case E1000_EECD:
+             case E1000_EERD:
+             case E1000_CTRL_EXT:
+             case E1000_FLA:
+             case E1000_FLOP:
+             case E1000_SWSM:
+             case E1000_FWSM:
+             case E1000_SW_FW_SYNC:
+             case E1000_IOVTCL:
+             case E1000_I350_BARCTRL:
+             case E1000_THSTAT:
+             case E1000_EEC_REG:
+             case E1000_SRWR:
+             case E1000_I210_FLA:
+             case E1000_I210_FLSWCTL:
+             case E1000_I210_FLSWDATA:
+             case E1000_I210_FLSWCNT:
+             case E1000_SHADOWINF:
+             case E1000_FLFWUPDATE:
+             case E1000_RAL(0):
+             case E1000_RAL(1):
+             case E1000_RAL(2):
+             case E1000_RAL(3):
+             case E1000_RAL(4):
+             case E1000_RAL(5):
+             case E1000_RAL(6):
+             case E1000_RAL(7):
+             case E1000_RAL(8):
+             case E1000_RAL(9):
+             case E1000_RAL(10):
+             case E1000_RAL(11):
+             case E1000_RAL(12):
+             case E1000_RAL(13):
+             case E1000_RAL(14):
+             case E1000_RAL(15):
+             case E1000_RAH(0):
+             case E1000_RAH(1):
+             case E1000_RAH(2):
+             case E1000_RAH(3):
+             case E1000_RAH(4):
+             case E1000_RAH(5):
+             case E1000_RAH(6):
+             case E1000_RAH(7):
+             case E1000_RAH(8):
+             case E1000_RAH(9):
+             case E1000_RAH(10):
+             case E1000_RAH(11):
+             case E1000_RAH(12):
+             case E1000_RAH(13):
+             case E1000_RAH(14):
+             case E1000_RAH(15):
+                            return 0;
+             default:
+                            dev_warn(&adapter->pdev->dev, "Bad offset: %x\n", offset);
+                            return -ENOTTY;
+             }
+}
+
+static int igb_nvmupd_command(struct e1000_hw *hw,
+                                                 struct e1000_nvm_access *nvm,
+                                                 u8 *bytes)
+{
+             struct igb_adapter *adapter = hw->back;
+             resource_size_t bar0_len;
+             int ret_val = 0;
+             u32 command;
+             u8 module;
+
+             bar0_len = pci_resource_len(adapter->pdev, 0);
+             command = nvm->command;
+             module = igb_nvmupd_get_module(nvm->config);
+
+             switch (command) {
+             case E1000_NVMUPD_CMD_REG_READ:
+                            switch (module) {
+                            case E1000_NVMUPD_EXEC_FEATURES:
+                                           if (nvm->data_size == hw->nvmupd_features.size)
+                                                          memcpy(bytes, &hw->nvmupd_features,
+                                                                 hw->nvmupd_features.size);
+                                           else
+                                                          ret_val = -ENOMEM;
+                            break;
+                            default:
+                                           if (igb_nvmupd_validate_offset(adapter, nvm->offset))
+                                                          return -ENOTTY;
+                                           if (nvm->offset >= bar0_len) {
+                                                          if (hw->mac.type == e1000_82576 &&
+                                                              hw->flash_address) {
+                                                                         u16 offset = nvm->offset - bar0_len;
+
+                                                                         if (nvm->data_size == 1)
+                                                                                        *bytes =
+                                                                                        E1000_READ_FLASH_REG8(hw,
+                                                                                                                            offset);
+                                                                         else
+                                                                                        *((u32 *)bytes) =
+                                                                                        E1000_READ_FLASH_REG(hw,
+                                                                                                                           offset);
+                                                          } else {
+                                                                         ret_val = -EFAULT;
+                                                          }
+                                           } else if (nvm->data_size == 1) {
+                                                          *bytes = E1000_READ_REG8(hw, nvm->offset);
+                                           } else {
+                                                          *((u32 *)bytes) = E1000_READ_REG(hw,
+                                                                                                                      nvm->offset);
+                                           }
+                            break;
+                            }
+             break;
+             case E1000_NVMUPD_CMD_REG_WRITE:
+                            if (igb_nvmupd_validate_offset(adapter, nvm->offset))
+                                           return -ENOTTY;
+                            if (nvm->offset >= bar0_len) {
+                                           if (hw->mac.type == e1000_82576 && hw->flash_address)
+                                                          E1000_WRITE_FLASH_REG(hw,
+                                                                                              nvm->offset - bar0_len,
+                                                                                              *((u32 *)bytes));
+                                           else
+                                                          ret_val = -EFAULT;
+                            } else {
+                                           E1000_WRITE_REG(hw, nvm->offset, *((u32 *)bytes));
+                            }
+             break;
+             }
+
+             return ret_val;
+}
+
static int igb_get_eeprom_len(struct net_device *netdev)
{
              struct igb_adapter *adapter = netdev_priv(netdev);
-              return adapter->hw.nvm.word_size * 2;
+             struct pci_dev *pdev = adapter->pdev;
+
+             if (adapter->hw.mac.type == e1000_82576)
+                            return pci_resource_len(pdev, 0) + pci_resource_len(pdev, 1);
+             else
+                            return pci_resource_len(pdev, 0);
+}
+
+s32 e1000_read_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
+{
+             if (hw->nvm.ops.read)
+                            return hw->nvm.ops.read(hw, offset, words, data);
+
+             return -E1000_ERR_CONFIG;
+}
+
+s32 e1000_write_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
+{
+             if (hw->nvm.ops.write)
+                            return hw->nvm.ops.write(hw, offset, words, data);
+
+             return E1000_SUCCESS;
+}
+
+s32 e1000_update_nvm_checksum(struct e1000_hw *hw)
+{
+             if (hw->nvm.ops.update)
+                            return hw->nvm.ops.update(hw);
+
+             return -E1000_ERR_CONFIG;
}

 static int igb_get_eeprom(struct net_device *netdev,
@@ -726,11 +897,21 @@
              u16 *eeprom_buff;
              int first_word, last_word;
              int ret_val = 0;
+             struct e1000_nvm_access *nvm;
+             u32 magic;
              u16 i;

               if (eeprom->len == 0)
                             return -EINVAL;

+             magic = hw->vendor_id | (hw->device_id << 16);
+             if (eeprom->magic && eeprom->magic != magic) {
+                            nvm = (struct e1000_nvm_access *)eeprom;
+                            ret_val = igb_nvmupd_command(hw, nvm, bytes);
+                            return ret_val;
+             }
+
+             /* normal ethtool get_eeprom support */
              eeprom->magic = hw->vendor_id | (hw->device_id << 16);

               first_word = eeprom->offset >> 1;
@@ -742,13 +923,13 @@
                             return -ENOMEM;

               if (hw->nvm.type == e1000_nvm_eeprom_spi)
-                             ret_val = hw->nvm.ops.read(hw, first_word,
-                                                                             last_word - first_word + 1,
-                                                                             eeprom_buff);
+                            ret_val = e1000_read_nvm(hw, first_word,
+                                                                         last_word - first_word + 1,
+                                                                         eeprom_buff);
              else {
                             for (i = 0; i < last_word - first_word + 1; i++) {
-                                            ret_val = hw->nvm.ops.read(hw, first_word + i, 1,
-                                                                                            &eeprom_buff[i]);
+                                           ret_val = e1000_read_nvm(hw, first_word + i, 1,
+                                                                                        &eeprom_buff[i]);
                                            if (ret_val)
                                                           break;
                             }
@@ -773,6 +954,8 @@
              u16 *eeprom_buff;
              void *ptr;
              int max_len, first_word, last_word, ret_val = 0;
+             struct e1000_nvm_access *nvm;
+             u32 magic;
              u16 i;

               if (eeprom->len == 0)
@@ -783,6 +966,14 @@
                             return -EOPNOTSUPP;
              }

+             magic = hw->vendor_id | (hw->device_id << 16);
+             if (eeprom->magic && eeprom->magic != magic) {
+                            nvm = (struct e1000_nvm_access *)eeprom;
+                            ret_val = igb_nvmupd_command(hw, nvm, bytes);
+                            return ret_val;
+             }
+
+             /* normal ethtool get_eeprom support */
              if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16)))
                             return -EFAULT;

@@ -800,16 +991,16 @@
                             /* need read/modify/write of first changed EEPROM word
                              * only the second byte of the word is being modified
                              */
-                             ret_val = hw->nvm.ops.read(hw, first_word, 1,
-                                                                              &eeprom_buff[0]);
+                            ret_val = e1000_read_nvm(hw, first_word, 1,
+                                                                         &eeprom_buff[0]);
                             ptr++;
              }
              if (((eeprom->offset + eeprom->len) & 1) && (ret_val == 0)) {
                             /* need read/modify/write of last changed EEPROM word
                              * only the first byte of the word is being modified
                              */
-                             ret_val = hw->nvm.ops.read(hw, last_word, 1,
-                                                              &eeprom_buff[last_word - first_word]);
+                            ret_val = e1000_read_nvm(hw, last_word, 1,
+                                                                         &eeprom_buff[last_word - first_word]);
              }

               /* Device's eeprom is always little-endian, word addressable */
@@ -821,12 +1012,14 @@
              for (i = 0; i < last_word - first_word + 1; i++)
                             eeprom_buff[i] = cpu_to_le16(eeprom_buff[i]);

-              ret_val = hw->nvm.ops.write(hw, first_word,
-                                                               last_word - first_word + 1, eeprom_buff);
+             ret_val = e1000_write_nvm(hw, first_word,
+                                                            last_word - first_word + 1, eeprom_buff);

-              /* Update the checksum if nvm write succeeded */
+             /* Update the checksum if write succeeded.
+             * and flush shadow RAM for 82573 controllers
+             */
              if (ret_val == 0)
-                             hw->nvm.ops.update(hw);
+                            e1000_update_nvm_checksum(hw);

               igb_set_fw_version(adapter);
              kfree(eeprom_buff);
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 39f33af..dbbcda3 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -3095,6 +3095,7 @@
              memcpy(&hw->mac.ops, ei->mac_ops, sizeof(hw->mac.ops));
              memcpy(&hw->phy.ops, ei->phy_ops, sizeof(hw->phy.ops));
              memcpy(&hw->nvm.ops, ei->nvm_ops, sizeof(hw->nvm.ops));
+             e1000_init_nvmupdate_features(hw);
              /* Initialize skew-specific constants */
              err = ei->get_invariants(hw);
              if (err)
@@ -3106,6 +3107,15 @@
                             goto err_sw_init;

               igb_get_bus_info_pcie(hw);
+
+             /* 82576 accesses flash differently */
+             if (adapter->hw.mac.type == e1000_82576) {
+                            adapter->flash_addr = ioremap(pci_resource_start(pdev, 1),
+                                                                               pci_resource_len(pdev, 1));
+                            if (!adapter->flash_addr)
+                                           dev_warn(&pdev->dev, "Flash address mapping failed.");
+                            hw->flash_address = adapter->flash_addr;
+             }

               hw->phy.autoneg_wait_to_complete = false;

@@ -3475,6 +3485,8 @@
              igb_disable_sriov(pdev);
#endif
              pci_iounmap(pdev, adapter->io_addr);
+             if (adapter->flash_addr)
+                            pci_iounmap(pdev, adapter->flash_addr);
err_ioremap:
              free_netdev(netdev);
err_alloc_etherdev:
@@ -3674,10 +3686,12 @@
              unregister_netdev(netdev);

               igb_clear_interrupt_scheme(adapter);
-
-              pci_iounmap(pdev, adapter->io_addr);
-              if (hw->flash_address)
-                             iounmap(hw->flash_address);
+             if (adapter->io_addr)
+                            pci_iounmap(pdev, adapter->io_addr);
+             if (adapter->flash_addr)
+                            pci_iounmap(pdev, adapter->flash_addr);
+             if (hw->flash_address && hw->flash_address != adapter->flash_addr)
+                            pci_iounmap(pdev, hw->flash_address);
              pci_release_mem_regions(pdev);

               kfree(adapter->mac_table);
