e1000: more error checks
[dpdk.git] / lib / librte_pmd_e1000 / e1000 / e1000_nvm.c
index e2d3974..79146c4 100644 (file)
@@ -33,7 +33,6 @@ POSSIBILITY OF SUCH DAMAGE.
 
 #include "e1000_api.h"
 
-static void e1000_stop_nvm(struct e1000_hw *hw);
 STATIC void e1000_reload_nvm_generic(struct e1000_hw *hw);
 
 /**
@@ -63,9 +62,12 @@ void e1000_init_nvm_ops_generic(struct e1000_hw *hw)
  *  e1000_null_nvm_read - No-op function, return 0
  *  @hw: pointer to the HW structure
  **/
-s32 e1000_null_read_nvm(struct e1000_hw *hw, u16 a, u16 b, u16 *c)
+s32 e1000_null_read_nvm(struct e1000_hw E1000_UNUSEDARG *hw,
+                       u16 E1000_UNUSEDARG a, u16 E1000_UNUSEDARG b,
+                       u16 E1000_UNUSEDARG *c)
 {
        DEBUGFUNC("e1000_null_read_nvm");
+       UNREFERENCED_4PARAMETER(hw, a, b, c);
        return E1000_SUCCESS;
 }
 
@@ -73,9 +75,10 @@ s32 e1000_null_read_nvm(struct e1000_hw *hw, u16 a, u16 b, u16 *c)
  *  e1000_null_nvm_generic - No-op function, return void
  *  @hw: pointer to the HW structure
  **/
-void e1000_null_nvm_generic(struct e1000_hw *hw)
+void e1000_null_nvm_generic(struct e1000_hw E1000_UNUSEDARG *hw)
 {
        DEBUGFUNC("e1000_null_nvm_generic");
+       UNREFERENCED_1PARAMETER(hw);
        return;
 }
 
@@ -83,9 +86,11 @@ void e1000_null_nvm_generic(struct e1000_hw *hw)
  *  e1000_null_led_default - No-op function, return 0
  *  @hw: pointer to the HW structure
  **/
-s32 e1000_null_led_default(struct e1000_hw *hw, u16 *data)
+s32 e1000_null_led_default(struct e1000_hw E1000_UNUSEDARG *hw,
+                          u16 E1000_UNUSEDARG *data)
 {
        DEBUGFUNC("e1000_null_led_default");
+       UNREFERENCED_2PARAMETER(hw, data);
        return E1000_SUCCESS;
 }
 
@@ -93,9 +98,12 @@ s32 e1000_null_led_default(struct e1000_hw *hw, u16 *data)
  *  e1000_null_write_nvm - No-op function, return 0
  *  @hw: pointer to the HW structure
  **/
-s32 e1000_null_write_nvm(struct e1000_hw *hw, u16 a, u16 b, u16 *c)
+s32 e1000_null_write_nvm(struct e1000_hw E1000_UNUSEDARG *hw,
+                        u16 E1000_UNUSEDARG a, u16 E1000_UNUSEDARG b,
+                        u16 E1000_UNUSEDARG *c)
 {
        DEBUGFUNC("e1000_null_write_nvm");
+       UNREFERENCED_4PARAMETER(hw, a, b, c);
        return E1000_SUCCESS;
 }
 
@@ -227,7 +235,6 @@ s32 e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int ee_reg)
 {
        u32 attempts = 100000;
        u32 i, reg = 0;
-       s32 ret_val = -E1000_ERR_NVM;
 
        DEBUGFUNC("e1000_poll_eerd_eewr_done");
 
@@ -237,15 +244,13 @@ s32 e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int ee_reg)
                else
                        reg = E1000_READ_REG(hw, E1000_EEWR);
 
-               if (reg & E1000_NVM_RW_REG_DONE) {
-                       ret_val = E1000_SUCCESS;
-                       break;
-               }
+               if (reg & E1000_NVM_RW_REG_DONE)
+                       return E1000_SUCCESS;
 
                usec_delay(5);
        }
 
-       return ret_val;
+       return -E1000_ERR_NVM;
 }
 
 /**
@@ -260,7 +265,6 @@ s32 e1000_acquire_nvm_generic(struct e1000_hw *hw)
 {
        u32 eecd = E1000_READ_REG(hw, E1000_EECD);
        s32 timeout = E1000_NVM_GRANT_ATTEMPTS;
-       s32 ret_val = E1000_SUCCESS;
 
        DEBUGFUNC("e1000_acquire_nvm_generic");
 
@@ -279,10 +283,10 @@ s32 e1000_acquire_nvm_generic(struct e1000_hw *hw)
                eecd &= ~E1000_EECD_REQ;
                E1000_WRITE_REG(hw, E1000_EECD, eecd);
                DEBUGOUT("Could not acquire NVM grant\n");
-               ret_val = -E1000_ERR_NVM;
+               return -E1000_ERR_NVM;
        }
 
-       return ret_val;
+       return E1000_SUCCESS;
 }
 
 /**
@@ -313,8 +317,7 @@ static void e1000_standby_nvm(struct e1000_hw *hw)
                usec_delay(nvm->delay_usec);
 
                e1000_lower_eec_clk(hw, &eecd);
-       } else
-       if (nvm->type == e1000_nvm_eeprom_spi) {
+       } else if (nvm->type == e1000_nvm_eeprom_spi) {
                /* Toggle CS to flush commands */
                eecd |= E1000_EECD_CS;
                E1000_WRITE_REG(hw, E1000_EECD, eecd);
@@ -333,7 +336,7 @@ static void e1000_standby_nvm(struct e1000_hw *hw)
  *
  *  Terminates the current command by inverting the EEPROM's chip select pin.
  **/
-static void e1000_stop_nvm(struct e1000_hw *hw)
+void e1000_stop_nvm(struct e1000_hw *hw)
 {
        u32 eecd;
 
@@ -382,7 +385,6 @@ static s32 e1000_ready_nvm_eeprom(struct e1000_hw *hw)
 {
        struct e1000_nvm_info *nvm = &hw->nvm;
        u32 eecd = E1000_READ_REG(hw, E1000_EECD);
-       s32 ret_val = E1000_SUCCESS;
        u8 spi_stat_reg;
 
        DEBUGFUNC("e1000_ready_nvm_eeprom");
@@ -394,24 +396,23 @@ static s32 e1000_ready_nvm_eeprom(struct e1000_hw *hw)
                /* Set CS */
                eecd |= E1000_EECD_CS;
                E1000_WRITE_REG(hw, E1000_EECD, eecd);
-       } else
-       if (nvm->type == e1000_nvm_eeprom_spi) {
+       } else if (nvm->type == e1000_nvm_eeprom_spi) {
                u16 timeout = NVM_MAX_RETRY_SPI;
 
                /* Clear SK and CS */
                eecd &= ~(E1000_EECD_CS | E1000_EECD_SK);
                E1000_WRITE_REG(hw, E1000_EECD, eecd);
+               E1000_WRITE_FLUSH(hw);
                usec_delay(1);
 
-               /*
-                * Read "Status Register" repeatedly until the LSB is cleared.
+               /* Read "Status Register" repeatedly until the LSB is cleared.
                 * The EEPROM will signal that the command has been completed
                 * by clearing bit 0 of the internal status register.  If it's
                 * not cleared within 'timeout', then error out.
                 */
                while (timeout) {
                        e1000_shift_out_eec_bits(hw, NVM_RDSR_OPCODE_SPI,
-                                                hw->nvm.opcode_bits);
+                                                hw->nvm.opcode_bits);
                        spi_stat_reg = (u8)e1000_shift_in_eec_bits(hw, 8);
                        if (!(spi_stat_reg & NVM_STATUS_RDY_SPI))
                                break;
@@ -423,13 +424,11 @@ static s32 e1000_ready_nvm_eeprom(struct e1000_hw *hw)
 
                if (!timeout) {
                        DEBUGOUT("SPI NVM Status error\n");
-                       ret_val = -E1000_ERR_NVM;
-                       goto out;
+                       return -E1000_ERR_NVM;
                }
        }
 
-out:
-       return ret_val;
+       return E1000_SUCCESS;
 }
 
 /**
@@ -451,20 +450,18 @@ s32 e1000_read_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
 
        DEBUGFUNC("e1000_read_nvm_spi");
 
-       /*
-        * A check for invalid values:  offset too large, too many words,
+       /* A check for invalid values:  offset too large, too many words,
         * 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;
+               return -E1000_ERR_NVM;
        }
 
        ret_val = nvm->ops.acquire(hw);
        if (ret_val)
-               goto out;
+               return ret_val;
 
        ret_val = e1000_ready_nvm_eeprom(hw);
        if (ret_val)
@@ -479,8 +476,7 @@ s32 e1000_read_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
        e1000_shift_out_eec_bits(hw, read_opcode, nvm->opcode_bits);
        e1000_shift_out_eec_bits(hw, (u16)(offset*2), nvm->address_bits);
 
-       /*
-        * Read the data.  SPI NVMs increment the address with each byte
+       /* Read the data.  SPI NVMs increment the address with each byte
         * read and will roll over if reading beyond the end.  This allows
         * us to read the whole NVM from any offset
         */
@@ -492,7 +488,6 @@ s32 e1000_read_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
 release:
        nvm->ops.release(hw);
 
-out:
        return ret_val;
 }
 
@@ -506,7 +501,7 @@ out:
  *  Reads a 16 bit word from the EEPROM.
  **/
 s32 e1000_read_nvm_microwire(struct e1000_hw *hw, u16 offset, u16 words,
-                             u16 *data)
+                            u16 *data)
 {
        struct e1000_nvm_info *nvm = &hw->nvm;
        u32 i = 0;
@@ -515,20 +510,18 @@ s32 e1000_read_nvm_microwire(struct e1000_hw *hw, u16 offset, u16 words,
 
        DEBUGFUNC("e1000_read_nvm_microwire");
 
-       /*
-        * A check for invalid values:  offset too large, too many words,
+       /* A check for invalid values:  offset too large, too many words,
         * 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;
+               return -E1000_ERR_NVM;
        }
 
        ret_val = nvm->ops.acquire(hw);
        if (ret_val)
-               goto out;
+               return ret_val;
 
        ret_val = e1000_ready_nvm_eeprom(hw);
        if (ret_val)
@@ -540,8 +533,7 @@ s32 e1000_read_nvm_microwire(struct e1000_hw *hw, u16 offset, u16 words,
                e1000_shift_out_eec_bits(hw, (u16)(offset + i),
                                        nvm->address_bits);
 
-               /*
-                * Read the data.  For microwire, each word requires the
+               /* Read the data.  For microwire, each word requires the
                 * overhead of setup and tear-down.
                 */
                data[i] = e1000_shift_in_eec_bits(hw, 16);
@@ -551,7 +543,6 @@ s32 e1000_read_nvm_microwire(struct e1000_hw *hw, u16 offset, u16 words,
 release:
        nvm->ops.release(hw);
 
-out:
        return ret_val;
 }
 
@@ -572,15 +563,13 @@ s32 e1000_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
 
        DEBUGFUNC("e1000_read_nvm_eerd");
 
-       /*
-        * A check for invalid values:  offset too large, too many words,
+       /* 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;
+               return -E1000_ERR_NVM;
        }
 
        for (i = 0; i < words; i++) {
@@ -593,10 +582,9 @@ s32 e1000_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
                        break;
 
                data[i] = (E1000_READ_REG(hw, E1000_EERD) >>
-                          E1000_NVM_RW_REG_DATA);
+                          E1000_NVM_RW_REG_DATA);
        }
 
-out:
        return ret_val;
 }
 
@@ -615,43 +603,42 @@ out:
 s32 e1000_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
 {
        struct e1000_nvm_info *nvm = &hw->nvm;
-       s32 ret_val;
+       s32 ret_val = -E1000_ERR_NVM;
        u16 widx = 0;
 
        DEBUGFUNC("e1000_write_nvm_spi");
 
-       /*
-        * A check for invalid values:  offset too large, too many words,
+       /* A check for invalid values:  offset too large, too many words,
         * 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;
+               return -E1000_ERR_NVM;
        }
 
-       ret_val = nvm->ops.acquire(hw);
-       if (ret_val)
-               goto out;
-
        while (widx < words) {
                u8 write_opcode = NVM_WRITE_OPCODE_SPI;
 
-               ret_val = e1000_ready_nvm_eeprom(hw);
+               ret_val = nvm->ops.acquire(hw);
                if (ret_val)
-                       goto release;
+                       return ret_val;
+
+               ret_val = e1000_ready_nvm_eeprom(hw);
+               if (ret_val) {
+                       nvm->ops.release(hw);
+                       return ret_val;
+               }
 
                e1000_standby_nvm(hw);
 
                /* Send the WRITE ENABLE command (8 bit opcode) */
                e1000_shift_out_eec_bits(hw, NVM_WREN_OPCODE_SPI,
-                                        nvm->opcode_bits);
+                                        nvm->opcode_bits);
 
                e1000_standby_nvm(hw);
 
-               /*
-                * Some SPI eeproms use the 8th address bit embedded in the
+               /* Some SPI eeproms use the 8th address bit embedded in the
                 * opcode
                 */
                if ((nvm->address_bits == 8) && (offset >= 128))
@@ -660,7 +647,7 @@ s32 e1000_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
                /* Send the Write command (8-bit opcode + addr) */
                e1000_shift_out_eec_bits(hw, write_opcode, nvm->opcode_bits);
                e1000_shift_out_eec_bits(hw, (u16)((offset + widx) * 2),
-                                        nvm->address_bits);
+                                        nvm->address_bits);
 
                /* Loop to allow for up to whole page write of eeprom */
                while (widx < words) {
@@ -674,13 +661,10 @@ s32 e1000_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
                                break;
                        }
                }
+               msec_delay(10);
+               nvm->ops.release(hw);
        }
 
-       msec_delay(10);
-release:
-       nvm->ops.release(hw);
-
-out:
        return ret_val;
 }
 
@@ -697,7 +681,7 @@ out:
  *  EEPROM will most likely contain an invalid checksum.
  **/
 s32 e1000_write_nvm_microwire(struct e1000_hw *hw, u16 offset, u16 words,
-                              u16 *data)
+                             u16 *data)
 {
        struct e1000_nvm_info *nvm = &hw->nvm;
        s32  ret_val;
@@ -707,27 +691,25 @@ s32 e1000_write_nvm_microwire(struct e1000_hw *hw, u16 offset, u16 words,
 
        DEBUGFUNC("e1000_write_nvm_microwire");
 
-       /*
-        * A check for invalid values:  offset too large, too many words,
+       /* A check for invalid values:  offset too large, too many words,
         * 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;
+               return -E1000_ERR_NVM;
        }
 
        ret_val = nvm->ops.acquire(hw);
        if (ret_val)
-               goto out;
+               return ret_val;
 
        ret_val = e1000_ready_nvm_eeprom(hw);
        if (ret_val)
                goto release;
 
        e1000_shift_out_eec_bits(hw, NVM_EWEN_OPCODE_MICROWIRE,
-                                (u16)(nvm->opcode_bits + 2));
+                                (u16)(nvm->opcode_bits + 2));
 
        e1000_shift_out_eec_bits(hw, 0, (u16)(nvm->address_bits - 2));
 
@@ -735,10 +717,10 @@ s32 e1000_write_nvm_microwire(struct e1000_hw *hw, u16 offset, u16 words,
 
        while (words_written < words) {
                e1000_shift_out_eec_bits(hw, NVM_WRITE_OPCODE_MICROWIRE,
-                                        nvm->opcode_bits);
+                                        nvm->opcode_bits);
 
                e1000_shift_out_eec_bits(hw, (u16)(offset + words_written),
-                                        nvm->address_bits);
+                                        nvm->address_bits);
 
                e1000_shift_out_eec_bits(hw, data[words_written], 16);
 
@@ -763,14 +745,13 @@ s32 e1000_write_nvm_microwire(struct e1000_hw *hw, u16 offset, u16 words,
        }
 
        e1000_shift_out_eec_bits(hw, NVM_EWDS_OPCODE_MICROWIRE,
-                                (u16)(nvm->opcode_bits + 2));
+                                (u16)(nvm->opcode_bits + 2));
 
        e1000_shift_out_eec_bits(hw, 0, (u16)(nvm->address_bits - 2));
 
 release:
        nvm->ops.release(hw);
 
-out:
        return ret_val;
 }
 
@@ -784,7 +765,7 @@ out:
  *  the value in pba_num.
  **/
 s32 e1000_read_pba_string_generic(struct e1000_hw *hw, u8 *pba_num,
-                                  u32 pba_num_size)
+                                 u32 pba_num_size)
 {
        s32 ret_val;
        u16 nvm_data;
@@ -796,32 +777,30 @@ s32 e1000_read_pba_string_generic(struct e1000_hw *hw, u8 *pba_num,
 
        if (pba_num == NULL) {
                DEBUGOUT("PBA string buffer was null\n");
-               ret_val = E1000_ERR_INVALID_ARGUMENT;
-               goto out;
+               return -E1000_ERR_INVALID_ARGUMENT;
        }
 
        ret_val = hw->nvm.ops.read(hw, NVM_PBA_OFFSET_0, 1, &nvm_data);
        if (ret_val) {
                DEBUGOUT("NVM Read Error\n");
-               goto out;
+               return ret_val;
        }
 
        ret_val = hw->nvm.ops.read(hw, NVM_PBA_OFFSET_1, 1, &pba_ptr);
        if (ret_val) {
                DEBUGOUT("NVM Read Error\n");
-               goto out;
+               return ret_val;
        }
 
-       /*
-        * if nvm_data is not ptr guard the PBA must be in legacy format which
+       /* if nvm_data is not ptr guard the PBA must be in legacy format which
         * means pba_ptr is actually our second data word for the PBA number
         * and we can decode it into an ascii string
         */
        if (nvm_data != NVM_PBA_PTR_GUARD) {
                DEBUGOUT("NVM PBA number is not stored as string\n");
 
-               /* we will need 11 characters to store the PBA */
-               if (pba_num_size < 11) {
+               /* make sure callers buffer is big enough to store the PBA */
+               if (pba_num_size < E1000_PBANUM_LENGTH) {
                        DEBUGOUT("PBA string buffer too small\n");
                        return E1000_ERR_NO_SPACE;
                }
@@ -849,25 +828,23 @@ s32 e1000_read_pba_string_generic(struct e1000_hw *hw, u8 *pba_num,
                                pba_num[offset] += 'A' - 0xA;
                }
 
-               goto out;
+               return E1000_SUCCESS;
        }
 
        ret_val = hw->nvm.ops.read(hw, pba_ptr, 1, &length);
        if (ret_val) {
                DEBUGOUT("NVM Read Error\n");
-               goto out;
+               return ret_val;
        }
 
        if (length == 0xFFFF || length == 0) {
                DEBUGOUT("NVM PBA number section invalid length\n");
-               ret_val = E1000_ERR_NVM_PBA_SECTION;
-               goto out;
+               return -E1000_ERR_NVM_PBA_SECTION;
        }
        /* check if pba_num buffer is big enough */
        if (pba_num_size < (((u32)length * 2) - 1)) {
                DEBUGOUT("PBA string buffer too small\n");
-               ret_val = E1000_ERR_NO_SPACE;
-               goto out;
+               return -E1000_ERR_NO_SPACE;
        }
 
        /* trim pba length from start of string */
@@ -878,15 +855,14 @@ s32 e1000_read_pba_string_generic(struct e1000_hw *hw, u8 *pba_num,
                ret_val = hw->nvm.ops.read(hw, pba_ptr + offset, 1, &nvm_data);
                if (ret_val) {
                        DEBUGOUT("NVM Read Error\n");
-                       goto out;
+                       return ret_val;
                }
                pba_num[offset * 2] = (u8)(nvm_data >> 8);
                pba_num[(offset * 2) + 1] = (u8)(nvm_data & 0xFF);
        }
        pba_num[offset * 2] = '\0';
 
-out:
-       return ret_val;
+       return E1000_SUCCESS;
 }
 
 /**
@@ -908,48 +884,233 @@ s32 e1000_read_pba_length_generic(struct e1000_hw *hw, u32 *pba_num_size)
 
        if (pba_num_size == NULL) {
                DEBUGOUT("PBA buffer size was null\n");
-               ret_val = E1000_ERR_INVALID_ARGUMENT;
-               goto out;
+               return -E1000_ERR_INVALID_ARGUMENT;
        }
 
        ret_val = hw->nvm.ops.read(hw, NVM_PBA_OFFSET_0, 1, &nvm_data);
        if (ret_val) {
                DEBUGOUT("NVM Read Error\n");
-               goto out;
+               return ret_val;
        }
 
        ret_val = hw->nvm.ops.read(hw, NVM_PBA_OFFSET_1, 1, &pba_ptr);
        if (ret_val) {
                DEBUGOUT("NVM Read Error\n");
-               goto out;
+               return ret_val;
        }
 
         /* if data is not ptr guard the PBA must be in legacy format */
        if (nvm_data != NVM_PBA_PTR_GUARD) {
-               *pba_num_size = 11;
-               goto out;
+               *pba_num_size = E1000_PBANUM_LENGTH;
+               return E1000_SUCCESS;
        }
 
        ret_val = hw->nvm.ops.read(hw, pba_ptr, 1, &length);
        if (ret_val) {
                DEBUGOUT("NVM Read Error\n");
-               goto out;
+               return ret_val;
        }
 
        if (length == 0xFFFF || length == 0) {
                DEBUGOUT("NVM PBA number section invalid length\n");
-               ret_val = E1000_ERR_NVM_PBA_SECTION;
-               goto out;
+               return -E1000_ERR_NVM_PBA_SECTION;
        }
 
-       /*
-        * Convert from length in u16 values to u8 chars, add 1 for NULL,
+       /* Convert from length in u16 values to u8 chars, add 1 for NULL,
         * and subtract 2 because length field is included in length.
         */
        *pba_num_size = ((u32)length * 2) - 1;
 
-out:
-       return ret_val;
+       return E1000_SUCCESS;
+}
+
+
+/**
+ *  e1000_read_pba_raw
+ *  @hw: pointer to the HW structure
+ *  @eeprom_buf: optional pointer to EEPROM image
+ *  @eeprom_buf_size: size of EEPROM image in words
+ *  @max_pba_block_size: PBA block size limit
+ *  @pba: pointer to output PBA structure
+ *
+ *  Reads PBA from EEPROM image when eeprom_buf is not NULL.
+ *  Reads PBA from physical EEPROM device when eeprom_buf is NULL.
+ *
+ **/
+s32 e1000_read_pba_raw(struct e1000_hw *hw, u16 *eeprom_buf,
+                      u32 eeprom_buf_size, u16 max_pba_block_size,
+                      struct e1000_pba *pba)
+{
+       s32 ret_val;
+       u16 pba_block_size;
+
+       if (pba == NULL)
+               return -E1000_ERR_PARAM;
+
+       if (eeprom_buf == NULL) {
+               ret_val = e1000_read_nvm(hw, NVM_PBA_OFFSET_0, 2,
+                                        &pba->word[0]);
+               if (ret_val)
+                       return ret_val;
+       } else {
+               if (eeprom_buf_size > NVM_PBA_OFFSET_1) {
+                       pba->word[0] = eeprom_buf[NVM_PBA_OFFSET_0];
+                       pba->word[1] = eeprom_buf[NVM_PBA_OFFSET_1];
+               } else {
+                       return -E1000_ERR_PARAM;
+               }
+       }
+
+       if (pba->word[0] == NVM_PBA_PTR_GUARD) {
+               if (pba->pba_block == NULL)
+                       return -E1000_ERR_PARAM;
+
+               ret_val = e1000_get_pba_block_size(hw, eeprom_buf,
+                                                  eeprom_buf_size,
+                                                  &pba_block_size);
+               if (ret_val)
+                       return ret_val;
+
+               if (pba_block_size > max_pba_block_size)
+                       return -E1000_ERR_PARAM;
+
+               if (eeprom_buf == NULL) {
+                       ret_val = e1000_read_nvm(hw, pba->word[1],
+                                                pba_block_size,
+                                                pba->pba_block);
+                       if (ret_val)
+                               return ret_val;
+               } else {
+                       if (eeprom_buf_size > (u32)(pba->word[1] +
+                                             pba->pba_block[0])) {
+                               memcpy(pba->pba_block,
+                                      &eeprom_buf[pba->word[1]],
+                                      pba_block_size * sizeof(u16));
+                       } else {
+                               return -E1000_ERR_PARAM;
+                       }
+               }
+       }
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_write_pba_raw
+ *  @hw: pointer to the HW structure
+ *  @eeprom_buf: optional pointer to EEPROM image
+ *  @eeprom_buf_size: size of EEPROM image in words
+ *  @pba: pointer to PBA structure
+ *
+ *  Writes PBA to EEPROM image when eeprom_buf is not NULL.
+ *  Writes PBA to physical EEPROM device when eeprom_buf is NULL.
+ *
+ **/
+s32 e1000_write_pba_raw(struct e1000_hw *hw, u16 *eeprom_buf,
+                       u32 eeprom_buf_size, struct e1000_pba *pba)
+{
+       s32 ret_val;
+
+       if (pba == NULL)
+               return -E1000_ERR_PARAM;
+
+       if (eeprom_buf == NULL) {
+               ret_val = e1000_write_nvm(hw, NVM_PBA_OFFSET_0, 2,
+                                         &pba->word[0]);
+               if (ret_val)
+                       return ret_val;
+       } else {
+               if (eeprom_buf_size > NVM_PBA_OFFSET_1) {
+                       eeprom_buf[NVM_PBA_OFFSET_0] = pba->word[0];
+                       eeprom_buf[NVM_PBA_OFFSET_1] = pba->word[1];
+               } else {
+                       return -E1000_ERR_PARAM;
+               }
+       }
+
+       if (pba->word[0] == NVM_PBA_PTR_GUARD) {
+               if (pba->pba_block == NULL)
+                       return -E1000_ERR_PARAM;
+
+               if (eeprom_buf == NULL) {
+                       ret_val = e1000_write_nvm(hw, pba->word[1],
+                                                 pba->pba_block[0],
+                                                 pba->pba_block);
+                       if (ret_val)
+                               return ret_val;
+               } else {
+                       if (eeprom_buf_size > (u32)(pba->word[1] +
+                                             pba->pba_block[0])) {
+                               memcpy(&eeprom_buf[pba->word[1]],
+                                      pba->pba_block,
+                                      pba->pba_block[0] * sizeof(u16));
+                       } else {
+                               return -E1000_ERR_PARAM;
+                       }
+               }
+       }
+
+       return E1000_SUCCESS;
+}
+
+/**
+ *  e1000_get_pba_block_size
+ *  @hw: pointer to the HW structure
+ *  @eeprom_buf: optional pointer to EEPROM image
+ *  @eeprom_buf_size: size of EEPROM image in words
+ *  @pba_data_size: pointer to output variable
+ *
+ *  Returns the size of the PBA block in words. Function operates on EEPROM
+ *  image if the eeprom_buf pointer is not NULL otherwise it accesses physical
+ *  EEPROM device.
+ *
+ **/
+s32 e1000_get_pba_block_size(struct e1000_hw *hw, u16 *eeprom_buf,
+                            u32 eeprom_buf_size, u16 *pba_block_size)
+{
+       s32 ret_val;
+       u16 pba_word[2];
+       u16 length;
+
+       DEBUGFUNC("e1000_get_pba_block_size");
+
+       if (eeprom_buf == NULL) {
+               ret_val = e1000_read_nvm(hw, NVM_PBA_OFFSET_0, 2, &pba_word[0]);
+               if (ret_val)
+                       return ret_val;
+       } else {
+               if (eeprom_buf_size > NVM_PBA_OFFSET_1) {
+                       pba_word[0] = eeprom_buf[NVM_PBA_OFFSET_0];
+                       pba_word[1] = eeprom_buf[NVM_PBA_OFFSET_1];
+               } else {
+                       return -E1000_ERR_PARAM;
+               }
+       }
+
+       if (pba_word[0] == NVM_PBA_PTR_GUARD) {
+               if (eeprom_buf == NULL) {
+                       ret_val = e1000_read_nvm(hw, pba_word[1] + 0, 1,
+                                                &length);
+                       if (ret_val)
+                               return ret_val;
+               } else {
+                       if (eeprom_buf_size > pba_word[1])
+                               length = eeprom_buf[pba_word[1] + 0];
+                       else
+                               return -E1000_ERR_PARAM;
+               }
+
+               if (length == 0xFFFF || length == 0)
+                       return -E1000_ERR_NVM_PBA_SECTION;
+       } else {
+               /* PBA number in legacy format, there is no PBA Block. */
+               length = 0;
+       }
+
+       if (pba_block_size != NULL)
+               *pba_block_size = length;
+
+       return E1000_SUCCESS;
 }
 
 /**
@@ -990,7 +1151,7 @@ s32 e1000_read_mac_addr_generic(struct e1000_hw *hw)
  **/
 s32 e1000_validate_nvm_checksum_generic(struct e1000_hw *hw)
 {
-       s32 ret_val = E1000_SUCCESS;
+       s32 ret_val;
        u16 checksum = 0;
        u16 i, nvm_data;
 
@@ -1000,19 +1161,17 @@ s32 e1000_validate_nvm_checksum_generic(struct e1000_hw *hw)
                ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data);
                if (ret_val) {
                        DEBUGOUT("NVM Read Error\n");
-                       goto out;
+                       return ret_val;
                }
                checksum += nvm_data;
        }
 
        if (checksum != (u16) NVM_SUM) {
                DEBUGOUT("NVM Checksum Invalid\n");
-               ret_val = -E1000_ERR_NVM;
-               goto out;
+               return -E1000_ERR_NVM;
        }
 
-out:
-       return ret_val;
+       return E1000_SUCCESS;
 }
 
 /**
@@ -1035,7 +1194,7 @@ s32 e1000_update_nvm_checksum_generic(struct e1000_hw *hw)
                ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data);
                if (ret_val) {
                        DEBUGOUT("NVM Read Error while updating checksum.\n");
-                       goto out;
+                       return ret_val;
                }
                checksum += nvm_data;
        }
@@ -1044,7 +1203,6 @@ s32 e1000_update_nvm_checksum_generic(struct e1000_hw *hw)
        if (ret_val)
                DEBUGOUT("NVM Write Error while updating checksum.\n");
 
-out:
        return ret_val;
 }
 
@@ -1068,3 +1226,79 @@ STATIC void e1000_reload_nvm_generic(struct e1000_hw *hw)
        E1000_WRITE_FLUSH(hw);
 }
 
+/**
+ *  e1000_get_fw_version - Get firmware version information
+ *  @hw: pointer to the HW structure
+ *  @fw_vers: pointer to output version structure
+ *
+ *  unsupported/not present features return 0 in version structure
+ **/
+void e1000_get_fw_version(struct e1000_hw *hw, struct e1000_fw_version *fw_vers)
+{
+       u16 eeprom_verh, eeprom_verl, fw_version;
+       u16 comb_verh, comb_verl, comb_offset;
+
+       memset(fw_vers, 0, sizeof(struct e1000_fw_version));
+
+       /* this code only applies to certain mac types */
+       switch (hw->mac.type) {
+       case e1000_i211:
+               e1000_read_invm_version(hw, fw_vers);
+               return;
+       case e1000_82575:
+       case e1000_82576:
+       case e1000_82580:
+       case e1000_i350:
+       case e1000_i210:
+               break;
+       default:
+               return;
+       }
+
+       /* basic eeprom version numbers */
+       hw->nvm.ops.read(hw, NVM_VERSION, 1, &fw_version);
+       fw_vers->eep_major = (fw_version & NVM_MAJOR_MASK) >> NVM_MAJOR_SHIFT;
+       fw_vers->eep_minor = (fw_version & NVM_MINOR_MASK);
+
+       /* etrack id */
+       hw->nvm.ops.read(hw, NVM_ETRACK_WORD, 1, &eeprom_verl);
+       hw->nvm.ops.read(hw, (NVM_ETRACK_WORD + 1), 1, &eeprom_verh);
+       fw_vers->etrack_id = (eeprom_verh << NVM_ETRACK_SHIFT) | eeprom_verl;
+
+       switch (hw->mac.type) {
+       case e1000_i210:
+       case e1000_i350:
+               /* find combo image version */
+               hw->nvm.ops.read(hw, NVM_COMB_VER_PTR, 1, &comb_offset);
+               if ((comb_offset != 0x0) &&
+                   (comb_offset != NVM_VER_INVALID)) {
+
+                       hw->nvm.ops.read(hw, (NVM_COMB_VER_OFF + comb_offset
+                                        + 1), 1, &comb_verh);
+                       hw->nvm.ops.read(hw, (NVM_COMB_VER_OFF + comb_offset),
+                                        1, &comb_verl);
+
+                       /* get Option Rom version if it exists and is valid */
+                       if ((comb_verh && comb_verl) &&
+                           ((comb_verh != NVM_VER_INVALID) &&
+                            (comb_verl != NVM_VER_INVALID))) {
+
+                               fw_vers->or_valid = true;
+                               fw_vers->or_major =
+                                       comb_verl >> NVM_COMB_VER_SHFT;
+                               fw_vers->or_build =
+                                       (comb_verl << NVM_COMB_VER_SHFT)
+                                       | (comb_verh >> NVM_COMB_VER_SHFT);
+                               fw_vers->or_patch =
+                                       comb_verh & NVM_COMB_VER_MASK;
+                       }
+               }
+               break;
+
+       default:
+               break;
+       }
+       return;
+}
+
+