Add support for NVM/EEPROM operations for i225 series NICs. This is
mostly identical to IGC driver, with some minor differences due to the
way IGC driver was originally generated.

Signed-off-by: Anatoly Burakov <anatoly.bura...@intel.com>
---
 drivers/net/intel/e1000/base/e1000_defines.h |  40 +
 drivers/net/intel/e1000/base/e1000_hw.h      |  20 +
 drivers/net/intel/e1000/base/e1000_i225.c    | 736 +++++++++++++++++++
 drivers/net/intel/e1000/base/e1000_i225.h    |  26 +
 drivers/net/intel/e1000/base/e1000_nvm.c     |   1 +
 drivers/net/intel/e1000/base/e1000_phy.c     |  63 ++
 drivers/net/intel/e1000/base/e1000_phy.h     |   6 +
 drivers/net/intel/e1000/base/e1000_regs.h    |   9 +
 8 files changed, 901 insertions(+)

diff --git a/drivers/net/intel/e1000/base/e1000_defines.h 
b/drivers/net/intel/e1000/base/e1000_defines.h
index 0e33f27c3f..49e4f6c7cc 100644
--- a/drivers/net/intel/e1000/base/e1000_defines.h
+++ b/drivers/net/intel/e1000/base/e1000_defines.h
@@ -1055,6 +1055,15 @@
 /* Firmware code revision field word offset*/
 #define E1000_I210_FW_VER_OFFSET       328
 
+#define E1000_EECD_FLUPD_I225          0x00800000 /* Update FLASH */
+#define E1000_EECD_FLUDONE_I225                0x04000000 /* Update FLASH done 
*/
+#define E1000_EECD_FLASH_DETECTED_I225 0x00080000 /* FLASH detected */
+#define E1000_FLUDONE_ATTEMPTS         20000
+#define E1000_EERD_EEWR_MAX_COUNT      512 /* buffered EEPROM words rw */
+#define E1000_EECD_SEC1VAL_I225                0x02000000 /* Sector One Valid 
*/
+#define E1000_FLSECU_BLK_SW_ACCESS_I225        0x00000004 /* Block SW access */
+#define E1000_FWSM_FW_VALID_I225       0x8000 /* FW valid bit */
+
 #define E1000_NVM_RW_REG_DATA  16  /* Offset to data in NVM read/write regs */
 #define E1000_NVM_RW_REG_DONE  2   /* Offset to READ/WRITE done bit */
 #define E1000_NVM_RW_REG_START 1   /* Start operation */
@@ -1482,6 +1491,37 @@
 #define I210_RXPBSIZE_DEFAULT          0x000000A2 /* RXPBSIZE default */
 #define I210_TXPBSIZE_DEFAULT          0x04000014 /* TXPBSIZE default */
 
+#define E1000_STM_OPCODE               0xDB00
+#define E1000_EEPROM_FLASH_SIZE_WORD   0x11
+#define INVM_DWORD_TO_RECORD_TYPE(invm_dword) \
+       (u8)((invm_dword) & 0x7)
+#define INVM_DWORD_TO_WORD_ADDRESS(invm_dword) \
+       (u8)(((invm_dword) & 0x0000FE00) >> 9)
+#define INVM_DWORD_TO_WORD_DATA(invm_dword) \
+       (u16)(((invm_dword) & 0xFFFF0000) >> 16)
+#define E1000_INVM_RSA_KEY_SHA256_DATA_SIZE_IN_DWORDS  8
+#define E1000_INVM_CSR_AUTOLOAD_DATA_SIZE_IN_DWORDS    1
+#define E1000_INVM_ULT_BYTES_SIZE              8
+#define E1000_INVM_RECORD_SIZE_IN_BYTES        4
+#define E1000_INVM_VER_FIELD_ONE               0x1FF8
+#define E1000_INVM_VER_FIELD_TWO               0x7FE000
+#define E1000_INVM_IMGTYPE_FIELD               0x1F800000
+
+#define E1000_INVM_MAJOR_MASK  0x3F0
+#define E1000_INVM_MINOR_MASK  0xF
+#define E1000_INVM_MAJOR_SHIFT 4
+
+/* PLL Defines */
+#define E1000_PCI_PMCSR                0x44
+#define E1000_PCI_PMCSR_D3             0x03
+#define E1000_MAX_PLL_TRIES            5
+#define E1000_PHY_PLL_UNCONF           0xFF
+#define E1000_PHY_PLL_FREQ_PAGE        0xFC0000
+#define E1000_PHY_PLL_FREQ_REG         0x000E
+#define E1000_INVM_DEFAULT_AL          0x202F
+#define E1000_INVM_AUTOLOAD            0x0A
+#define E1000_INVM_PLL_WO_VAL          0x0010
+
 
 /* Proxy Filter Control */
 #define E1000_PROXYFC_D0               0x00000001 /* Enable offload in D0 */
diff --git a/drivers/net/intel/e1000/base/e1000_hw.h 
b/drivers/net/intel/e1000/base/e1000_hw.h
index a21cb070e0..c16dd1cbf7 100644
--- a/drivers/net/intel/e1000/base/e1000_hw.h
+++ b/drivers/net/intel/e1000/base/e1000_hw.h
@@ -381,6 +381,15 @@ enum e1000_serdes_link_state {
        e1000_serdes_link_forced_up
 };
 
+enum e1000_invm_structure_type {
+       e1000_invm_unitialized_structure                = 0x00,
+       e1000_invm_word_autoload_structure              = 0x01,
+       e1000_invm_csr_autoload_structure               = 0x02,
+       e1000_invm_phy_register_autoload_structure      = 0x03,
+       e1000_invm_rsa_key_sha256_structure             = 0x04,
+       e1000_invm_invalidated_structure                = 0x0f,
+};
+
 #define __le16 u16
 #define __le32 u32
 #define __le64 u64
@@ -996,6 +1005,16 @@ struct e1000_dev_spec_vf {
        u32 v2p_mailbox;
 };
 
+struct e1000_dev_spec_i225 {
+       bool global_device_reset;
+       bool eee_disable;
+       bool clear_semaphore_once;
+       bool module_plugged;
+       u8 media_port;
+       bool mas_capable;
+       u32 mtu;
+};
+
 struct e1000_hw {
        void *back;
 
@@ -1020,6 +1039,7 @@ struct e1000_hw {
                struct e1000_dev_spec_ich8lan ich8lan;
                struct e1000_dev_spec_82575 _82575;
                struct e1000_dev_spec_vf vf;
+               struct e1000_dev_spec_i225 _i225;
        } dev_spec;
 
        u16 device_id;
diff --git a/drivers/net/intel/e1000/base/e1000_i225.c 
b/drivers/net/intel/e1000/base/e1000_i225.c
index a7a42e2f0e..7c8cf8552d 100644
--- a/drivers/net/intel/e1000/base/e1000_i225.c
+++ b/drivers/net/intel/e1000/base/e1000_i225.c
@@ -4,9 +4,75 @@
 
 #include "e1000_api.h"
 
+STATIC s32 e1000_init_nvm_params_i225(struct e1000_hw *hw);
 STATIC s32 e1000_init_mac_params_i225(struct e1000_hw *hw);
 STATIC s32 e1000_init_phy_params_i225(struct e1000_hw *hw);
 STATIC s32 e1000_reset_hw_i225(struct e1000_hw *hw);
+STATIC s32 e1000_acquire_nvm_i225(struct e1000_hw *hw);
+STATIC void e1000_release_nvm_i225(struct e1000_hw *hw);
+STATIC s32 e1000_get_hw_semaphore_i225(struct e1000_hw *hw);
+STATIC s32 __e1000_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words,
+                                 u16 *data);
+STATIC s32 e1000_pool_flash_update_done_i225(struct e1000_hw *hw);
+STATIC s32 e1000_valid_led_default_i225(struct e1000_hw *hw, u16 *data);
+
+/**
+ *  e1000_init_nvm_params_i225 - Init NVM func ptrs.
+ *  @hw: pointer to the HW structure
+ **/
+STATIC s32 e1000_init_nvm_params_i225(struct e1000_hw *hw)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+       u32 eecd = E1000_READ_REG(hw, E1000_EECD);
+       u16 size;
+
+       DEBUGFUNC("e1000_init_nvm_params_i225");
+
+       size = (u16)((eecd & E1000_EECD_SIZE_EX_MASK) >>
+                    E1000_EECD_SIZE_EX_SHIFT);
+       /*
+        * Added to a constant, "size" becomes the left-shift value
+        * for setting word_size.
+        */
+       size += NVM_WORD_SIZE_BASE_SHIFT;
+
+       /* Just in case size is out of range, cap it to the largest
+        * EEPROM size supported
+        */
+       if (size > 15)
+               size = 15;
+
+       nvm->word_size = 1 << size;
+       nvm->opcode_bits = 8;
+       nvm->delay_usec = 1;
+       nvm->type = e1000_nvm_eeprom_spi;
+
+
+       nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8;
+       nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ?
+                           16 : 8;
+
+       if (nvm->word_size == (1 << 15))
+               nvm->page_size = 128;
+
+       nvm->ops.acquire = e1000_acquire_nvm_i225;
+       nvm->ops.release = e1000_release_nvm_i225;
+       nvm->ops.valid_led_default = e1000_valid_led_default_i225;
+       if (e1000_get_flash_presence_i225(hw)) {
+               hw->nvm.type = e1000_nvm_flash_hw;
+               nvm->ops.read    = e1000_read_nvm_srrd_i225;
+               nvm->ops.write   = e1000_write_nvm_srwr_i225;
+               nvm->ops.validate = e1000_validate_nvm_checksum_i225;
+               nvm->ops.update   = e1000_update_nvm_checksum_i225;
+       } else {
+               hw->nvm.type = e1000_nvm_none;
+               nvm->ops.write    = e1000_null_write_nvm;
+               nvm->ops.validate = e1000_null_ops_generic;
+               nvm->ops.update   = e1000_null_ops_generic;
+       }
+
+       return E1000_SUCCESS;
+}
 
 /**
  *  e1000_init_mac_params_i225 - Init MAC func ptrs.
@@ -15,6 +81,7 @@ STATIC s32 e1000_reset_hw_i225(struct e1000_hw *hw);
 STATIC s32 e1000_init_mac_params_i225(struct e1000_hw *hw)
 {
        struct e1000_mac_info *mac = &hw->mac;
+       struct e1000_dev_spec_i225 *dev_spec = &hw->dev_spec._i225;
 
        DEBUGFUNC("e1000_init_mac_params_i225");
 
@@ -38,8 +105,13 @@ STATIC s32 e1000_init_mac_params_i225(struct e1000_hw *hw)
        mac->ops.check_for_link = e1000_check_for_copper_link_generic;
        /* link info */
        mac->ops.get_link_up_info = e1000_get_speed_and_duplex_copper_generic;
+       /* acquire SW_FW sync */
+       mac->ops.acquire_swfw_sync = e1000_acquire_swfw_sync_i225;
+       /* release SW_FW sync */
+       mac->ops.release_swfw_sync = e1000_release_swfw_sync_i225;
 
        /* Allow a single clear of the SW semaphore on I225 */
+       dev_spec->clear_semaphore_once = true;
        mac->ops.setup_physical_interface = e1000_setup_copper_link_i225;
 
        /* Set if part includes ASF firmware */
@@ -103,6 +175,8 @@ STATIC s32 e1000_init_phy_params_i225(struct e1000_hw *hw)
                goto out;
 
        E1000_WRITE_REG(hw, E1000_CTRL_EXT, ctrl_ext);
+       phy->ops.read_reg = e1000_read_phy_reg_gpy;
+       phy->ops.write_reg = e1000_write_phy_reg_gpy;
 
        ret_val = e1000_get_phy_id(hw);
 
@@ -177,6 +251,118 @@ STATIC s32 e1000_reset_hw_i225(struct e1000_hw *hw)
        return ret_val;
 }
 
+/* e1000_acquire_nvm_i225 - Request for access to EEPROM
+ * @hw: pointer to the HW structure
+ *
+ * Acquire the necessary semaphores for exclusive access to the EEPROM.
+ * Set the EEPROM access request bit and wait for EEPROM access grant bit.
+ * Return successful if access grant bit set, else clear the request for
+ * EEPROM access and return -E1000_ERR_NVM (-1).
+ */
+STATIC s32 e1000_acquire_nvm_i225(struct e1000_hw *hw)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_acquire_nvm_i225");
+
+       ret_val = e1000_acquire_swfw_sync_i225(hw, E1000_SWFW_EEP_SM);
+
+       return ret_val;
+}
+
+/* e1000_release_nvm_i225 - Release exclusive access to EEPROM
+ * @hw: pointer to the HW structure
+ *
+ * Stop any current commands to the EEPROM and clear the EEPROM request bit,
+ * then release the semaphores acquired.
+ */
+STATIC void e1000_release_nvm_i225(struct e1000_hw *hw)
+{
+       DEBUGFUNC("e1000_release_nvm_i225");
+
+       e1000_release_swfw_sync_i225(hw, E1000_SWFW_EEP_SM);
+}
+
+/* e1000_acquire_swfw_sync_i225 - Acquire SW/FW semaphore
+ * @hw: pointer to the HW structure
+ * @mask: specifies which semaphore to acquire
+ *
+ * Acquire the SW/FW semaphore to access the PHY or NVM.  The mask
+ * will also specify which port we're acquiring the lock for.
+ */
+s32 e1000_acquire_swfw_sync_i225(struct e1000_hw *hw, u16 mask)
+{
+       u32 swfw_sync;
+       u32 swmask = mask;
+       u32 fwmask = mask << 16;
+       s32 ret_val = E1000_SUCCESS;
+       s32 i = 0, timeout = 200; /* FIXME: find real value to use here */
+
+       DEBUGFUNC("e1000_acquire_swfw_sync_i225");
+
+       while (i < timeout) {
+               if (e1000_get_hw_semaphore_i225(hw)) {
+                       ret_val = -E1000_ERR_SWFW_SYNC;
+                       goto out;
+               }
+
+               swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC);
+               if (!(swfw_sync & (fwmask | swmask)))
+                       break;
+
+               /* Firmware currently using resource (fwmask)
+                * or other software thread using resource (swmask)
+                */
+               e1000_put_hw_semaphore_generic(hw);
+               msec_delay_irq(5);
+               i++;
+       }
+
+       if (i == timeout) {
+               DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n");
+               ret_val = -E1000_ERR_SWFW_SYNC;
+               goto out;
+       }
+
+       swfw_sync |= swmask;
+       E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync);
+
+       e1000_put_hw_semaphore_generic(hw);
+
+out:
+       return ret_val;
+}
+
+/* e1000_release_swfw_sync_i225 - Release SW/FW semaphore
+ * @hw: pointer to the HW structure
+ * @mask: specifies which semaphore to acquire
+ *
+ * Release the SW/FW semaphore used to access the PHY or NVM.  The mask
+ * will also specify which port we're releasing the lock for.
+ */
+void e1000_release_swfw_sync_i225(struct e1000_hw *hw, u16 mask)
+{
+       u32 swfw_sync;
+
+       DEBUGFUNC("e1000_release_swfw_sync_i225");
+
+       /* Releasing the resource requires first getting the HW semaphore.
+        * If we fail to get the semaphore, there is nothing we can do,
+        * except log an error and quit. We are not allowed to hang here
+        * indefinitely, as it may cause denial of service or system crash.
+        */
+       if (e1000_get_hw_semaphore_i225(hw) != E1000_SUCCESS) {
+               DEBUGOUT("Failed to release SW_FW_SYNC.\n");
+               return;
+       }
+
+       swfw_sync = E1000_READ_REG(hw, E1000_SW_FW_SYNC);
+       swfw_sync &= ~(u32)mask;
+       E1000_WRITE_REG(hw, E1000_SW_FW_SYNC, swfw_sync);
+
+       e1000_put_hw_semaphore_generic(hw);
+}
+
 /*
  * e1000_setup_copper_link_i225 - Configure copper link settings
  * @hw: pointer to the HW structure
@@ -207,6 +393,520 @@ s32 e1000_setup_copper_link_i225(struct e1000_hw *hw)
        return ret_val;
 }
 
+/* e1000_get_hw_semaphore_i225 - Acquire hardware semaphore
+ * @hw: pointer to the HW structure
+ *
+ * Acquire the HW semaphore to access the PHY or NVM
+ */
+STATIC s32 e1000_get_hw_semaphore_i225(struct e1000_hw *hw)
+{
+       u32 swsm;
+       s32 timeout = E1000_SWSM_TIMEOUT;
+       s32 i = 0;
+
+       DEBUGFUNC("e1000_get_hw_semaphore_i225");
+
+       /* Get the SW semaphore */
+       while (i < timeout) {
+               swsm = E1000_READ_REG(hw, E1000_SWSM);
+               if (!(swsm & E1000_SWSM_SMBI))
+                       break;
+
+               usec_delay(50);
+               i++;
+       }
+
+       if (i == timeout) {
+               /* In rare circumstances, the SW semaphore may already be held
+                * unintentionally. Clear the semaphore once before giving up.
+                */
+               if (hw->dev_spec._i225.clear_semaphore_once) {
+                       hw->dev_spec._i225.clear_semaphore_once = false;
+                       e1000_put_hw_semaphore_generic(hw);
+                       for (i = 0; i < timeout; i++) {
+                               swsm = E1000_READ_REG(hw, E1000_SWSM);
+                               if (!(swsm & E1000_SWSM_SMBI))
+                                       break;
+
+                               usec_delay(50);
+                       }
+               }
+
+               /* If we do not have the semaphore here, we have to give up. */
+               if (i == timeout) {
+                       DEBUGOUT("Driver can't access device -\n");
+                       DEBUGOUT("SMBI bit is set.\n");
+                       return -E1000_ERR_NVM;
+               }
+       }
+
+       /* Get the FW semaphore. */
+       for (i = 0; i < timeout; i++) {
+               swsm = E1000_READ_REG(hw, E1000_SWSM);
+               E1000_WRITE_REG(hw, E1000_SWSM, swsm | E1000_SWSM_SWESMBI);
+
+               /* Semaphore acquired if bit latched */
+               if (E1000_READ_REG(hw, E1000_SWSM) & E1000_SWSM_SWESMBI)
+                       break;
+
+               usec_delay(50);
+       }
+
+       if (i == timeout) {
+               /* Release semaphores */
+               e1000_put_hw_semaphore_generic(hw);
+               DEBUGOUT("Driver can't access the NVM\n");
+               return -E1000_ERR_NVM;
+       }
+
+       return E1000_SUCCESS;
+}
+
+/* e1000_read_nvm_srrd_i225 - Reads Shadow Ram using EERD register
+ * @hw: pointer to the HW structure
+ * @offset: offset of word in the Shadow Ram to read
+ * @words: number of words to read
+ * @data: word read from the Shadow Ram
+ *
+ * Reads a 16 bit word from the Shadow Ram using the EERD register.
+ * Uses necessary synchronization semaphores.
+ */
+s32 e1000_read_nvm_srrd_i225(struct e1000_hw *hw, u16 offset, u16 words,
+                            u16 *data)
+{
+       s32 status = E1000_SUCCESS;
+       u16 i, count;
+
+       DEBUGFUNC("e1000_read_nvm_srrd_i225");
+
+       /* We cannot hold synchronization semaphores for too long,
+        * because of forceful takeover procedure. However it is more efficient
+        * to read in bursts than synchronizing access for each word.
+        */
+       for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) {
+               count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ?
+                       E1000_EERD_EEWR_MAX_COUNT : (words - i);
+               if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+                       status = e1000_read_nvm_eerd(hw, offset, count,
+                                                    data + i);
+                       hw->nvm.ops.release(hw);
+               } else {
+                       status = E1000_ERR_SWFW_SYNC;
+               }
+
+               if (status != E1000_SUCCESS)
+                       break;
+       }
+
+       return status;
+}
+
+/* e1000_write_nvm_srwr_i225 - Write to Shadow RAM using EEWR
+ * @hw: pointer to the HW structure
+ * @offset: offset within the Shadow RAM to be written to
+ * @words: number of words to write
+ * @data: 16 bit word(s) to be written to the Shadow RAM
+ *
+ * Writes data to Shadow RAM at offset using EEWR register.
+ *
+ * If e1000_update_nvm_checksum is not called after this function , the
+ * data will not be committed to FLASH and also Shadow RAM will most likely
+ * contain an invalid checksum.
+ *
+ * If error code is returned, data and Shadow RAM may be inconsistent - buffer
+ * partially written.
+ */
+s32 e1000_write_nvm_srwr_i225(struct e1000_hw *hw, u16 offset, u16 words,
+                             u16 *data)
+{
+       s32 status = E1000_SUCCESS;
+       u16 i, count;
+
+       DEBUGFUNC("e1000_write_nvm_srwr_i225");
+
+       /* We cannot hold synchronization semaphores for too long,
+        * because of forceful takeover procedure. However it is more efficient
+        * to write in bursts than synchronizing access for each word.
+        */
+       for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) {
+               count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ?
+                       E1000_EERD_EEWR_MAX_COUNT : (words - i);
+               if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+                       status = __e1000_write_nvm_srwr(hw, offset, count,
+                                                       data + i);
+                       hw->nvm.ops.release(hw);
+               } else {
+                       status = E1000_ERR_SWFW_SYNC;
+               }
+
+               if (status != E1000_SUCCESS)
+                       break;
+       }
+
+       return status;
+}
+
+/* __e1000_write_nvm_srwr - Write to Shadow Ram using EEWR
+ * @hw: pointer to the HW structure
+ * @offset: offset within the Shadow Ram to be written to
+ * @words: number of words to write
+ * @data: 16 bit word(s) to be written to the Shadow Ram
+ *
+ * Writes data to Shadow Ram at offset using EEWR register.
+ *
+ * If e1000_update_nvm_checksum is not called after this function , the
+ * Shadow Ram will most likely contain an invalid checksum.
+ */
+STATIC s32 __e1000_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words,
+                                 u16 *data)
+{
+       struct e1000_nvm_info *nvm = &hw->nvm;
+       u32 i, k, eewr = 0;
+       u32 attempts = 100000;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("__e1000_write_nvm_srwr");
+
+       /* A check for invalid values:  offset too large, too many words,
+        * too many words for the offset, and not enough words.
+        */
+       if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) ||
+           (words == 0)) {
+               DEBUGOUT("nvm parameter(s) out of bounds\n");
+               ret_val = -E1000_ERR_NVM;
+               goto out;
+       }
+
+       for (i = 0; i < words; i++) {
+               ret_val = -E1000_ERR_NVM;
+               eewr = ((offset + i) << E1000_NVM_RW_ADDR_SHIFT) |
+                       (data[i] << E1000_NVM_RW_REG_DATA) |
+                       E1000_NVM_RW_REG_START;
+
+               E1000_WRITE_REG(hw, E1000_SRWR, eewr);
+
+               for (k = 0; k < attempts; k++) {
+                       if (E1000_NVM_RW_REG_DONE &
+                           E1000_READ_REG(hw, E1000_SRWR)) {
+                               ret_val = E1000_SUCCESS;
+                               break;
+                       }
+                       usec_delay(5);
+               }
+
+               if (ret_val != E1000_SUCCESS) {
+                       DEBUGOUT("Shadow RAM write EEWR timed out\n");
+                       break;
+               }
+       }
+
+out:
+       return ret_val;
+}
+
+/* e1000_validate_nvm_checksum_i225 - Validate EEPROM checksum
+ * @hw: pointer to the HW structure
+ *
+ * Calculates the EEPROM checksum by reading/adding each word of the EEPROM
+ * and then verifies that the sum of the EEPROM is equal to 0xBABA.
+ */
+s32 e1000_validate_nvm_checksum_i225(struct e1000_hw *hw)
+{
+       s32 status = E1000_SUCCESS;
+       s32 (*read_op_ptr)(struct e1000_hw *, u16, u16, u16 *);
+
+       DEBUGFUNC("e1000_validate_nvm_checksum_i225");
+
+       if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+               /* Replace the read function with semaphore grabbing with
+                * the one that skips this for a while.
+                * We have semaphore taken already here.
+                */
+               read_op_ptr = hw->nvm.ops.read;
+               hw->nvm.ops.read = e1000_read_nvm_eerd;
+
+               status = e1000_validate_nvm_checksum_generic(hw);
+
+               /* Revert original read operation. */
+               hw->nvm.ops.read = read_op_ptr;
+
+               hw->nvm.ops.release(hw);
+       } else {
+               status = E1000_ERR_SWFW_SYNC;
+       }
+
+       return status;
+}
+
+/* e1000_update_nvm_checksum_i225 - Update EEPROM checksum
+ * @hw: pointer to the HW structure
+ *
+ * Updates the EEPROM checksum by reading/adding each word of the EEPROM
+ * up to the checksum.  Then calculates the EEPROM checksum and writes the
+ * value to the EEPROM. Next commit EEPROM data onto the Flash.
+ */
+s32 e1000_update_nvm_checksum_i225(struct e1000_hw *hw)
+{
+       s32 ret_val;
+       u16 checksum = 0;
+       u16 i, nvm_data;
+
+       DEBUGFUNC("e1000_update_nvm_checksum_i225");
+
+       /* Read the first word from the EEPROM. If this times out or fails, do
+        * not continue or we could be in for a very long wait while every
+        * EEPROM read fails
+        */
+       ret_val = e1000_read_nvm_eerd(hw, 0, 1, &nvm_data);
+       if (ret_val != E1000_SUCCESS) {
+               DEBUGOUT("EEPROM read failed\n");
+               goto out;
+       }
+
+       if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) {
+               /* Do not use hw->nvm.ops.write, hw->nvm.ops.read
+                * because we do not want to take the synchronization
+                * semaphores twice here.
+                */
+
+               for (i = 0; i < NVM_CHECKSUM_REG; i++) {
+                       ret_val = e1000_read_nvm_eerd(hw, i, 1, &nvm_data);
+                       if (ret_val) {
+                               hw->nvm.ops.release(hw);
+                               DEBUGOUT("NVM Read Error while updating\n");
+                               DEBUGOUT("checksum.\n");
+                               goto out;
+                       }
+                       checksum += nvm_data;
+               }
+               checksum = (u16)NVM_SUM - checksum;
+               ret_val = __e1000_write_nvm_srwr(hw, NVM_CHECKSUM_REG, 1,
+                                                &checksum);
+               if (ret_val != E1000_SUCCESS) {
+                       hw->nvm.ops.release(hw);
+                       DEBUGOUT("NVM Write Error while updating checksum.\n");
+                       goto out;
+               }
+
+               hw->nvm.ops.release(hw);
+
+               ret_val = e1000_update_flash_i225(hw);
+       } else {
+               ret_val = E1000_ERR_SWFW_SYNC;
+       }
+out:
+       return ret_val;
+}
+
+/* e1000_get_flash_presence_i225 - Check if flash device is detected.
+ * @hw: pointer to the HW structure
+ */
+bool e1000_get_flash_presence_i225(struct e1000_hw *hw)
+{
+       u32 eec = 0;
+       bool ret_val = false;
+
+       DEBUGFUNC("e1000_get_flash_presence_i225");
+
+       eec = E1000_READ_REG(hw, E1000_EECD);
+
+       if (eec & E1000_EECD_FLASH_DETECTED_I225)
+               ret_val = true;
+
+       return ret_val;
+}
+
+/* e1000_set_flsw_flash_burst_counter_i225 - sets FLSW NVM Burst
+ * Counter in FLSWCNT register.
+ *
+ * @hw: pointer to the HW structure
+ * @burst_counter: size in bytes of the Flash burst to read or write
+ */
+s32 e1000_set_flsw_flash_burst_counter_i225(struct e1000_hw *hw,
+                                           u32 burst_counter)
+{
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_set_flsw_flash_burst_counter_i225");
+
+       /* Validate input data */
+       if (burst_counter < E1000_I225_SHADOW_RAM_SIZE) {
+               /* Write FLSWCNT - burst counter */
+               E1000_WRITE_REG(hw, E1000_I225_FLSWCNT, burst_counter);
+       } else {
+               ret_val = E1000_ERR_INVALID_ARGUMENT;
+       }
+
+       return ret_val;
+}
+
+/* e1000_write_erase_flash_command_i225 - write/erase to a sector
+ * region on a given address.
+ *
+ * @hw: pointer to the HW structure
+ * @opcode: opcode to be used for the write command
+ * @address: the offset to write into the FLASH image
+ */
+s32 e1000_write_erase_flash_command_i225(struct e1000_hw *hw, u32 opcode,
+                                        u32 address)
+{
+       u32 flswctl = 0;
+       s32 timeout = E1000_NVM_GRANT_ATTEMPTS;
+       s32 ret_val = E1000_SUCCESS;
+
+       DEBUGFUNC("e1000_write_erase_flash_command_i225");
+
+       flswctl = E1000_READ_REG(hw, E1000_I225_FLSWCTL);
+       /* Polling done bit on FLSWCTL register */
+       while (timeout) {
+               if (flswctl & E1000_FLSWCTL_DONE)
+                       break;
+               usec_delay(5);
+               flswctl = E1000_READ_REG(hw, E1000_I225_FLSWCTL);
+               timeout--;
+       }
+
+       if (!timeout) {
+               DEBUGOUT("Flash transaction was not done\n");
+               return -E1000_ERR_NVM;
+       }
+
+       /* Build and issue command on FLSWCTL register */
+       flswctl = address | opcode;
+       E1000_WRITE_REG(hw, E1000_I225_FLSWCTL, flswctl);
+
+       /* Check if issued command is valid on FLSWCTL register */
+       flswctl = E1000_READ_REG(hw, E1000_I225_FLSWCTL);
+       if (!(flswctl & E1000_FLSWCTL_CMDV)) {
+               DEBUGOUT("Write flash command failed\n");
+               ret_val = E1000_ERR_INVALID_ARGUMENT;
+       }
+
+       return ret_val;
+}
+
+/* e1000_update_flash_i225 - Commit EEPROM to the flash
+ * if fw_valid_bit is set, FW is active. setting FLUPD bit in EEC
+ * register makes the FW load the internal shadow RAM into the flash.
+ * Otherwise, fw_valid_bit is 0. if FL_SECU.block_prtotected_sw = 0
+ * then FW is not active so the SW is responsible shadow RAM dump.
+ *
+ * @hw: pointer to the HW structure
+ */
+s32 e1000_update_flash_i225(struct e1000_hw *hw)
+{
+       u16 current_offset_data = 0;
+       u32 block_sw_protect = 1;
+       u16 base_address = 0x0;
+       u32 i, fw_valid_bit;
+       u16 current_offset;
+       s32 ret_val = 0;
+       u32 flup;
+
+       DEBUGFUNC("e1000_update_flash_i225");
+
+       block_sw_protect = E1000_READ_REG(hw, E1000_I225_FLSECU) &
+                                         E1000_FLSECU_BLK_SW_ACCESS_I225;
+       fw_valid_bit = E1000_READ_REG(hw, E1000_FWSM) &
+                                     E1000_FWSM_FW_VALID_I225;
+       if (fw_valid_bit) {
+               ret_val = e1000_pool_flash_update_done_i225(hw);
+               if (ret_val == -E1000_ERR_NVM) {
+                       DEBUGOUT("Flash update time out\n");
+                       goto out;
+               }
+
+               flup = E1000_READ_REG(hw, E1000_EECD) | E1000_EECD_FLUPD_I225;
+               E1000_WRITE_REG(hw, E1000_EECD, flup);
+
+               ret_val = e1000_pool_flash_update_done_i225(hw);
+               if (ret_val == E1000_SUCCESS)
+                       DEBUGOUT("Flash update complete\n");
+               else
+                       DEBUGOUT("Flash update time out\n");
+       } else if (!block_sw_protect) {
+               /* FW is not active and security protection is disabled.
+                * therefore, SW is in charge of shadow RAM dump.
+                * Check which sector is valid. if sector 0 is valid,
+                * base address remains 0x0. otherwise, sector 1 is
+                * valid and it's base address is 0x1000
+                */
+               if (E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_SEC1VAL_I225)
+                       base_address = 0x1000;
+
+               /* Valid sector erase */
+               ret_val = e1000_write_erase_flash_command_i225(hw,
+                                                 E1000_I225_ERASE_CMD_OPCODE,
+                                                 base_address);
+               if (!ret_val) {
+                       DEBUGOUT("Sector erase failed\n");
+                       goto out;
+               }
+
+               current_offset = base_address;
+
+               /* Write */
+               for (i = 0; i < E1000_I225_SHADOW_RAM_SIZE / 2; i++) {
+                       /* Set burst write length */
+                       ret_val = e1000_set_flsw_flash_burst_counter_i225(hw,
+                                                                         0x2);
+                       if (ret_val != E1000_SUCCESS)
+                               break;
+
+                       /* Set address and opcode */
+                       ret_val = e1000_write_erase_flash_command_i225(hw,
+                                               E1000_I225_WRITE_CMD_OPCODE,
+                                               2 * current_offset);
+                       if (ret_val != E1000_SUCCESS)
+                               break;
+
+                       ret_val = e1000_read_nvm_eerd(hw, current_offset,
+                                                     1, &current_offset_data);
+                       if (ret_val) {
+                               DEBUGOUT("Failed to read from EEPROM\n");
+                               goto out;
+                       }
+
+                       /* Write CurrentOffseData to FLSWDATA register */
+                       E1000_WRITE_REG(hw, E1000_I225_FLSWDATA,
+                                       current_offset_data);
+                       current_offset++;
+
+                       /* Wait till operation has finished */
+                       ret_val = e1000_poll_eerd_eewr_done(hw,
+                                               E1000_NVM_POLL_READ);
+                       if (ret_val)
+                               break;
+
+                       usec_delay(1000);
+               }
+       }
+out:
+       return ret_val;
+}
+
+/* e1000_pool_flash_update_done_i225 - Pool FLUDONE status.
+ * @hw: pointer to the HW structure
+ */
+s32 e1000_pool_flash_update_done_i225(struct e1000_hw *hw)
+{
+       s32 ret_val = -E1000_ERR_NVM;
+       u32 i, reg;
+
+       DEBUGFUNC("e1000_pool_flash_update_done_i225");
+
+       for (i = 0; i < E1000_FLUDONE_ATTEMPTS; i++) {
+               reg = E1000_READ_REG(hw, E1000_EECD);
+               if (reg & E1000_EECD_FLUDONE_I225) {
+                       ret_val = E1000_SUCCESS;
+                       break;
+               }
+               usec_delay(5);
+       }
+
+       return ret_val;
+}
+
 /* e1000_init_function_pointers_i225 - Init func ptrs.
  * @hw: pointer to the HW structure
  *
@@ -216,10 +916,46 @@ void e1000_init_function_pointers_i225(struct e1000_hw 
*hw)
 {
        e1000_init_mac_ops_generic(hw);
        e1000_init_phy_ops_generic(hw);
+       e1000_init_nvm_ops_generic(hw);
        hw->mac.ops.init_params = e1000_init_mac_params_i225;
+       hw->nvm.ops.init_params = e1000_init_nvm_params_i225;
        hw->phy.ops.init_params = e1000_init_phy_params_i225;
 }
 
+/* e1000_valid_led_default_i225 - Verify a valid default LED config
+ * @hw: pointer to the HW structure
+ * @data: pointer to the NVM (EEPROM)
+ *
+ * Read the EEPROM for the current default LED configuration.  If the
+ * LED configuration is not valid, set to a valid LED configuration.
+ */
+STATIC s32 e1000_valid_led_default_i225(struct e1000_hw *hw, u16 *data)
+{
+       s32 ret_val;
+
+       DEBUGFUNC("e1000_valid_led_default_i225");
+
+       ret_val = hw->nvm.ops.read(hw, NVM_ID_LED_SETTINGS, 1, data);
+       if (ret_val) {
+               DEBUGOUT("NVM Read Error\n");
+               goto out;
+       }
+
+       if (*data == ID_LED_RESERVED_0000 || *data == ID_LED_RESERVED_FFFF) {
+               switch (hw->phy.media_type) {
+               case e1000_media_type_internal_serdes:
+                       *data = ID_LED_DEFAULT_I225_SERDES;
+                       break;
+               case e1000_media_type_copper:
+               default:
+                       *data = ID_LED_DEFAULT_I225;
+                       break;
+               }
+       }
+out:
+       return ret_val;
+}
+
 /* e1000_get_cfg_done_i225 - Read config done bit
  * @hw: pointer to the HW structure
  *
diff --git a/drivers/net/intel/e1000/base/e1000_i225.h 
b/drivers/net/intel/e1000/base/e1000_i225.h
index 03e0f4fb89..be3b1b76eb 100644
--- a/drivers/net/intel/e1000/base/e1000_i225.h
+++ b/drivers/net/intel/e1000/base/e1000_i225.h
@@ -5,8 +5,22 @@
 #ifndef _E1000_I225_H_
 #define _E1000_I225_H_
 
+bool e1000_get_flash_presence_i225(struct e1000_hw *hw);
+s32 e1000_update_flash_i225(struct e1000_hw *hw);
+s32 e1000_update_nvm_checksum_i225(struct e1000_hw *hw);
+s32 e1000_validate_nvm_checksum_i225(struct e1000_hw *hw);
+s32 e1000_write_nvm_srwr_i225(struct e1000_hw *hw, u16 offset,
+                             u16 words, u16 *data);
+s32 e1000_read_nvm_srrd_i225(struct e1000_hw *hw, u16 offset,
+                            u16 words, u16 *data);
+s32 e1000_set_flsw_flash_burst_counter_i225(struct e1000_hw *hw,
+                                           u32 burst_counter);
+s32 e1000_write_erase_flash_command_i225(struct e1000_hw *hw, u32 opcode,
+                                        u32 address);
 s32 e1000_id_led_init_i225(struct e1000_hw *hw);
 s32 e1000_blink_led_i225(struct e1000_hw *hw);
+s32 e1000_acquire_swfw_sync_i225(struct e1000_hw *hw, u16 mask);
+void e1000_release_swfw_sync_i225(struct e1000_hw *hw, u16 mask);
 s32 e1000_init_hw_i225(struct e1000_hw *hw);
 s32 e1000_setup_copper_link_i225(struct e1000_hw *hw);
 s32 e1000_set_d0_lplu_state_i225(struct e1000_hw *hw, bool active);
@@ -19,6 +33,18 @@ s32 e1000_set_d3_lplu_state_i225(struct e1000_hw *hw, bool 
active);
                                         (ID_LED_DEF1_DEF2 <<  4) | \
                                         (ID_LED_OFF1_ON2))
 
+/* NVM offset defaults for I225 devices */
+#define NVM_INIT_CTRL_2_DEFAULT_I225   0X7243
+#define NVM_INIT_CTRL_4_DEFAULT_I225   0x00C1
+#define NVM_LED_1_CFG_DEFAULT_I225     0x0184
+#define NVM_LED_0_2_CFG_DEFAULT_I225   0x200C
+
+#define E1000_I225_SHADOW_RAM_SIZE             4096
+#define E1000_I225_ERASE_CMD_OPCODE            0x02000000
+#define E1000_I225_WRITE_CMD_OPCODE            0x01000000
+#define E1000_FLSWCTL_DONE                     0x40000000
+#define E1000_FLSWCTL_CMDV                     0x10000000
+
 /* LED Control */
 #define E1000_GLOBAL_BLINK_MODE        0x00000020 /*Blink at 200 ms on and 200 
ms off.*/
 #define E1000_LED1_MODE_MASK   0x00000F00
diff --git a/drivers/net/intel/e1000/base/e1000_nvm.c 
b/drivers/net/intel/e1000/base/e1000_nvm.c
index 430fecaf6d..aec5e80986 100644
--- a/drivers/net/intel/e1000/base/e1000_nvm.c
+++ b/drivers/net/intel/e1000/base/e1000_nvm.c
@@ -1295,6 +1295,7 @@ void e1000_get_fw_version(struct e1000_hw *hw, struct 
e1000_fw_version *fw_vers)
                        return;
                }
                /* fall through */
+       case e1000_i225:
        case e1000_i350:
                hw->nvm.ops.read(hw, NVM_ETRACK_HIWORD, 1, &etrack_test);
                /* find combo image version */
diff --git a/drivers/net/intel/e1000/base/e1000_phy.c 
b/drivers/net/intel/e1000/base/e1000_phy.c
index a7e73112e5..6fac341d71 100644
--- a/drivers/net/intel/e1000/base/e1000_phy.c
+++ b/drivers/net/intel/e1000/base/e1000_phy.c
@@ -4171,6 +4171,69 @@ s32 e1000_read_phy_reg_gs40g(struct e1000_hw *hw, u32 
offset, u16 *data)
        return ret_val;
 }
 
+/**
+ *  e1000_write_phy_reg_gpy - Write GPY PHY register
+ *  @hw: pointer to the HW structure
+ *  @offset: register offset to write to
+ *  @data: data to write at register offset
+ *
+ *  Acquires semaphore, if necessary, then writes the data to PHY register
+ *  at the offset.  Release any acquired semaphores before exiting.
+ **/
+s32 e1000_write_phy_reg_gpy(struct e1000_hw *hw, u32 offset, u16 data)
+{
+       s32 ret_val;
+       u8 dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT;
+
+       DEBUGFUNC("e1000_write_phy_reg_gpy");
+
+       offset = offset & GPY_REG_MASK;
+
+       if (!dev_addr) {
+               ret_val = hw->phy.ops.acquire(hw);
+               if (ret_val)
+                       return ret_val;
+               ret_val = e1000_write_phy_reg_mdic(hw, offset, data);
+               hw->phy.ops.release(hw);
+       } else {
+               ret_val = e1000_write_xmdio_reg(hw, (u16)offset, dev_addr,
+                                               data);
+       }
+       return ret_val;
+}
+
+/**
+ *  e1000_read_phy_reg_gpy - Read GPY PHY register
+ *  @hw: pointer to the HW structure
+ *  @offset: lower half is register offset to read to
+ *     upper half is MMD to use.
+ *  @data: data to read at register offset
+ *
+ *  Acquires semaphore, if necessary, then reads the data in the PHY register
+ *  at the offset.  Release any acquired semaphores before exiting.
+ **/
+s32 e1000_read_phy_reg_gpy(struct e1000_hw *hw, u32 offset, u16 *data)
+{
+       s32 ret_val;
+       u8 dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT;
+
+       DEBUGFUNC("e1000_read_phy_reg_gpy");
+
+       offset = offset & GPY_REG_MASK;
+
+       if (!dev_addr) {
+               ret_val = hw->phy.ops.acquire(hw);
+               if (ret_val)
+                       return ret_val;
+               ret_val = e1000_read_phy_reg_mdic(hw, offset, data);
+               hw->phy.ops.release(hw);
+       } else {
+               ret_val = e1000_read_xmdio_reg(hw, (u16)offset, dev_addr,
+                                              data);
+       }
+       return ret_val;
+}
+
 /**
  *  e1000_read_phy_reg_mphy - Read mPHY control register
  *  @hw: pointer to the HW structure
diff --git a/drivers/net/intel/e1000/base/e1000_phy.h 
b/drivers/net/intel/e1000/base/e1000_phy.h
index 87b4d407e5..a7834240e0 100644
--- a/drivers/net/intel/e1000/base/e1000_phy.h
+++ b/drivers/net/intel/e1000/base/e1000_phy.h
@@ -86,6 +86,8 @@ s32  e1000_phy_force_speed_duplex_82577(struct e1000_hw *hw);
 s32  e1000_get_cable_length_82577(struct e1000_hw *hw);
 s32  e1000_write_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 data);
 s32  e1000_read_phy_reg_gs40g(struct e1000_hw *hw, u32 offset, u16 *data);
+s32  e1000_write_phy_reg_gpy(struct e1000_hw *hw, u32 offset, u16 data);
+s32  e1000_read_phy_reg_gpy(struct e1000_hw *hw, u32 offset, u16 *data);
 s32 e1000_read_phy_reg_mphy(struct e1000_hw *hw, u32 address, u32 *data);
 s32 e1000_write_phy_reg_mphy(struct e1000_hw *hw, u32 address, u32 data,
                             bool line_override);
@@ -131,6 +133,10 @@ s32 e1000_write_xmdio_reg(struct e1000_hw *hw, u16 addr, 
u8 dev_addr,
 #define E1000_I225_PHPM_ULP            0x0400 /* Ultra Low-Power Mode */
 #define E1000_I225_PHPM_DIS_2500       0x0800 /* Disable 2.5G globally */
 #define E1000_I225_PHPM_DIS_2500_D3    0x1000 /* Disable 2.5G in D3 */
+/* GPY211 - I225 defines */
+#define GPY_MMD_MASK                   0xFFFF0000
+#define GPY_MMD_SHIFT                  16
+#define GPY_REG_MASK                   0x0000FFFF
 /* BM/HV Specific Registers */
 #define BM_PORT_CTRL_PAGE              769
 #define BM_WUC_PAGE                    800
diff --git a/drivers/net/intel/e1000/base/e1000_regs.h 
b/drivers/net/intel/e1000/base/e1000_regs.h
index 20abeeaa46..b9d2a4484b 100644
--- a/drivers/net/intel/e1000/base/e1000_regs.h
+++ b/drivers/net/intel/e1000/base/e1000_regs.h
@@ -10,6 +10,9 @@
 #define E1000_STATUS   0x00008  /* Device Status - RO */
 #define E1000_EECD     0x00010  /* EEPROM/Flash Control - RW */
 #define E1000_EERD     0x00014  /* EEPROM Read - RW */
+#define E1000_EEWR     0x0102C  /* EEPROM Write Register - RW */
+#define E1000_EERD_V2  0x12014  /* EEprom mode read - RW */
+#define E1000_EEWR_V2  0x12018  /* EEprom mode write - RW */
 #define E1000_CTRL_EXT 0x00018  /* Extended Device Control - RW */
 #define E1000_FLA      0x0001C  /* Flash Access - RW */
 #define E1000_MDIC     0x00020  /* MDI Control - RW */
@@ -30,6 +33,11 @@
 #define E1000_FCAL     0x00028  /* Flow Control Address Low - RW */
 #define E1000_FCAH     0x0002C  /* Flow Control Address High -RW */
 #define E1000_FEXT     0x0002C  /* Future Extended - RW */
+#define E1000_I225_FLA         0x1201C /* FLASH access register */
+#define E1000_I225_FLSWCTL     0x12048 /* FLASH control register */
+#define E1000_I225_FLSWDATA    0x1204C /* FLASH data register */
+#define E1000_I225_FLSWCNT     0x12050 /* FLASH Access Counter */
+#define E1000_I225_FLSECU      0x12114 /* FLASH Security */
 #define E1000_FEXTNVM  0x00028  /* Future Extended NVM - RW */
 #define E1000_FEXTNVM3 0x0003C  /* Future Extended NVM 3 - RW */
 #define E1000_FEXTNVM4 0x00024  /* Future Extended NVM 4 - RW */
@@ -90,6 +98,7 @@
 #define E1000_EEMNGCTL_I225    0x01010  /* i225 MNG EEprom Mode Control */
 #define E1000_EEARBC   0x01024  /* EEPROM Auto Read Bus Control */
 #define E1000_EEARBC_I210      0x12024 /* EEPROM Auto Read Bus Control */
+#define E1000_EEARBC_I225      0x12024 /* EEPROM Auto Read Bus Control */
 #define E1000_FLASHT   0x01028  /* FLASH Timer Register */
 #define E1000_EEWR     0x0102C  /* EEPROM Write Register - RW */
 #define E1000_FLSWCTL  0x01030  /* FLASH control register */
-- 
2.43.5


Reply via email to