net: e1000: add support for writing to EEPROM
authorHannu Lounento <hannu.lounento@ge.com>
Wed, 10 Jan 2018 19:31:24 +0000 (20:31 +0100)
committerStefano Babic <sbabic@denx.de>
Sun, 4 Feb 2018 11:00:58 +0000 (12:00 +0100)
Port functions for writing to EEPROM, updating the checksum and
committing data to flash from the Linux kernel igb driver.

Functions were ported from Linux 4.8-rc2 (694d0d0bb20).

Signed-off-by: Hannu Lounento <hannu.lounento@ge.com>
CC: Joe Hershberger <joe.hershberger@ni.com>
Signed-off-by: Martyn Welch <martyn.welch@collabora.co.uk>
Reviewed-by: Stefano Babic <sbabic@denx.de>
Acked-by: Joe Hershberger <joe.hershberger@ni.com>
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.co.uk>
drivers/net/e1000.c
drivers/net/e1000.h

index 875682b1b89e1ee31b7226e646be3c108b850933..1d9f8f3ae2e433610882cb57377b6ffb45240008 100644 (file)
@@ -150,6 +150,7 @@ static int32_t e1000_check_phy_reset_block(struct e1000_hw *hw);
 
 #ifndef CONFIG_E1000_NO_NVM
 static void e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw);
+static int32_t e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw);
 static int32_t e1000_read_eeprom(struct e1000_hw *hw, uint16_t offset,
                uint16_t words,
                uint16_t *data);
@@ -861,6 +862,174 @@ e1000_read_eeprom(struct e1000_hw *hw, uint16_t offset,
        return E1000_SUCCESS;
 }
 
+#ifndef CONFIG_DM_ETH
+/******************************************************************************
+ *  e1000_write_eeprom_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_eeprom_checksum_i210 is not called after this function, the
+ *  Shadow Ram will most likely contain an invalid checksum.
+ *****************************************************************************/
+static int32_t e1000_write_eeprom_srwr(struct e1000_hw *hw, uint16_t offset,
+                                      uint16_t words, uint16_t *data)
+{
+       struct e1000_eeprom_info *eeprom = &hw->eeprom;
+       uint32_t i, k, eewr = 0;
+       uint32_t attempts = 100000;
+       int32_t ret_val = 0;
+
+       /* A check for invalid values:  offset too large, too many words,
+        * too many words for the offset, and not enough words.
+        */
+       if ((offset >= eeprom->word_size) ||
+           (words > (eeprom->word_size - offset)) || (words == 0)) {
+               DEBUGOUT("nvm parameter(s) out of bounds\n");
+               ret_val = -E1000_ERR_EEPROM;
+               goto out;
+       }
+
+       for (i = 0; i < words; i++) {
+               eewr = ((offset + i) << E1000_EEPROM_RW_ADDR_SHIFT)
+                               | (data[i] << E1000_EEPROM_RW_REG_DATA) |
+                               E1000_EEPROM_RW_REG_START;
+
+               E1000_WRITE_REG(hw, I210_EEWR, eewr);
+
+               for (k = 0; k < attempts; k++) {
+                       if (E1000_EEPROM_RW_REG_DONE &
+                           E1000_READ_REG(hw, I210_EEWR)) {
+                               ret_val = 0;
+                               break;
+                       }
+                       udelay(5);
+               }
+
+               if (ret_val) {
+                       DEBUGOUT("Shadow RAM write EEWR timed out\n");
+                       break;
+               }
+       }
+
+out:
+       return ret_val;
+}
+
+/******************************************************************************
+ *  e1000_pool_flash_update_done_i210 - Pool FLUDONE status.
+ *  @hw: pointer to the HW structure
+ *
+ *****************************************************************************/
+static int32_t e1000_pool_flash_update_done_i210(struct e1000_hw *hw)
+{
+       int32_t ret_val = -E1000_ERR_EEPROM;
+       uint32_t i, reg;
+
+       for (i = 0; i < E1000_FLUDONE_ATTEMPTS; i++) {
+               reg = E1000_READ_REG(hw, EECD);
+               if (reg & E1000_EECD_FLUDONE_I210) {
+                       ret_val = 0;
+                       break;
+               }
+               udelay(5);
+       }
+
+       return ret_val;
+}
+
+/******************************************************************************
+ *  e1000_update_flash_i210 - Commit EEPROM to the flash
+ *  @hw: pointer to the HW structure
+ *
+ *****************************************************************************/
+static int32_t e1000_update_flash_i210(struct e1000_hw *hw)
+{
+       int32_t ret_val = 0;
+       uint32_t flup;
+
+       ret_val = e1000_pool_flash_update_done_i210(hw);
+       if (ret_val == -E1000_ERR_EEPROM) {
+               DEBUGOUT("Flash update time out\n");
+               goto out;
+       }
+
+       flup = E1000_READ_REG(hw, EECD) | E1000_EECD_FLUPD_I210;
+       E1000_WRITE_REG(hw, EECD, flup);
+
+       ret_val = e1000_pool_flash_update_done_i210(hw);
+       if (ret_val)
+               DEBUGOUT("Flash update time out\n");
+       else
+               DEBUGOUT("Flash update complete\n");
+
+out:
+       return ret_val;
+}
+
+/******************************************************************************
+ *  e1000_update_eeprom_checksum_i210 - 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.
+ *****************************************************************************/
+static int32_t e1000_update_eeprom_checksum_i210(struct e1000_hw *hw)
+{
+       int32_t ret_val = 0;
+       uint16_t checksum = 0;
+       uint16_t i, nvm_data;
+
+       /* 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_eeprom_eerd(hw, 0, 1, &nvm_data);
+       if (ret_val) {
+               DEBUGOUT("EEPROM read failed\n");
+               goto out;
+       }
+
+       if (!(e1000_get_hw_eeprom_semaphore(hw))) {
+               /* 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 < EEPROM_CHECKSUM_REG; i++) {
+                       ret_val = e1000_read_eeprom_eerd(hw, i, 1, &nvm_data);
+                       if (ret_val) {
+                               e1000_put_hw_eeprom_semaphore(hw);
+                               DEBUGOUT("EEPROM Read Error while updating checksum.\n");
+                               goto out;
+                       }
+                       checksum += nvm_data;
+               }
+               checksum = (uint16_t)EEPROM_SUM - checksum;
+               ret_val = e1000_write_eeprom_srwr(hw, EEPROM_CHECKSUM_REG, 1,
+                                                 &checksum);
+               if (ret_val) {
+                       e1000_put_hw_eeprom_semaphore(hw);
+                       DEBUGOUT("EEPROM Write Error while updating checksum.\n");
+                       goto out;
+               }
+
+               e1000_put_hw_eeprom_semaphore(hw);
+
+               ret_val = e1000_update_flash_i210(hw);
+       } else {
+               ret_val = -E1000_ERR_SWFW_SYNC;
+       }
+
+out:
+       return ret_val;
+}
+#endif
+
 /******************************************************************************
  * Verifies that the EEPROM has a valid checksum
  *
@@ -970,7 +1139,7 @@ e1000_get_software_semaphore(struct e1000_hw *hw)
 
        DEBUGFUNC();
 
-       if (hw->mac_type != e1000_80003es2lan)
+       if (hw->mac_type != e1000_80003es2lan && hw->mac_type != e1000_igb)
                return E1000_SUCCESS;
 
        while (timeout) {
@@ -1044,7 +1213,7 @@ e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw)
        if (!hw->eeprom_semaphore_present)
                return E1000_SUCCESS;
 
-       if (hw->mac_type == e1000_80003es2lan) {
+       if (hw->mac_type == e1000_80003es2lan || hw->mac_type == e1000_igb) {
                /* Get the SW semaphore. */
                if (e1000_get_software_semaphore(hw) != E1000_SUCCESS)
                        return -E1000_ERR_EEPROM;
index fcb7df0d83fc00d20731841c7035623aa8224aeb..6376de15ada94db93598c19882dcbd683ff9f3e5 100644 (file)
@@ -1242,6 +1242,9 @@ struct e1000_hw {
 #define E1000_EECD_SELSHAD   0x00020000 /* Select Shadow RAM */
 #define E1000_EECD_INITSRAM  0x00040000 /* Initialize Shadow RAM */
 #define E1000_EECD_FLUPD     0x00080000 /* Update FLASH */
+#define E1000_EECD_FLUPD_I210       0x00800000 /* Update FLASH */
+#define E1000_EECD_FLUDONE_I210     0x04000000 /* Update FLASH done*/
+#define E1000_FLUDONE_ATTEMPTS      20000
 #define E1000_EECD_AUPDEN    0x00100000 /* Enable Autonomous FLASH update */
 #define E1000_EECD_SHADV     0x00200000 /* Shadow RAM Data Valid */
 #define E1000_EECD_SEC1VAL   0x00400000 /* Sector One Valid */