* [Intel-wired-lan] [PATCH v3 08/11] igc: Add NVM support
@ 2018-06-24 8:45 Sasha Neftin
2018-06-24 16:39 ` kbuild test robot
2018-06-28 23:35 ` Shannon Nelson
0 siblings, 2 replies; 4+ messages in thread
From: Sasha Neftin @ 2018-06-24 8:45 UTC (permalink / raw)
To: intel-wired-lan
Add code for NVM support and get MAC address, complete probe
method.
Sasha Neftin (v2):
minor cosmetic changes
Alexander Duyck (v3):
NVM access code optimization
Signed-off-by: Sasha Neftin <sasha.neftin@intel.com>
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
---
drivers/net/ethernet/intel/igc/Makefile | 2 +-
drivers/net/ethernet/intel/igc/e1000_base.c | 118 ++++++++-
drivers/net/ethernet/intel/igc/e1000_defines.h | 81 ++++++
drivers/net/ethernet/intel/igc/e1000_hw.h | 5 +-
drivers/net/ethernet/intel/igc/e1000_i225.c | 351 +++++++++++++++++++++++++
drivers/net/ethernet/intel/igc/e1000_i225.h | 3 +
drivers/net/ethernet/intel/igc/e1000_mac.c | 173 ++++++++++++
drivers/net/ethernet/intel/igc/e1000_mac.h | 13 +-
drivers/net/ethernet/intel/igc/e1000_nvm.c | 219 +++++++++++++++
drivers/net/ethernet/intel/igc/e1000_nvm.h | 16 ++
drivers/net/ethernet/intel/igc/e1000_regs.h | 3 +
drivers/net/ethernet/intel/igc/igc.h | 6 +
drivers/net/ethernet/intel/igc/igc_main.c | 20 +-
13 files changed, 1000 insertions(+), 10 deletions(-)
create mode 100644 drivers/net/ethernet/intel/igc/e1000_nvm.c
create mode 100644 drivers/net/ethernet/intel/igc/e1000_nvm.h
diff --git a/drivers/net/ethernet/intel/igc/Makefile b/drivers/net/ethernet/intel/igc/Makefile
index cb260bedfa37..5147bca1a900 100644
--- a/drivers/net/ethernet/intel/igc/Makefile
+++ b/drivers/net/ethernet/intel/igc/Makefile
@@ -7,4 +7,4 @@
obj-$(CONFIG_IGC) += igc.o
-igc-objs := igc_main.o e1000_mac.o e1000_i225.o e1000_base.o
+igc-objs := igc_main.o e1000_mac.o e1000_i225.o e1000_base.o e1000_nvm.o
diff --git a/drivers/net/ethernet/intel/igc/e1000_base.c b/drivers/net/ethernet/intel/igc/e1000_base.c
index 395d7ce70bb4..6db31349daa4 100644
--- a/drivers/net/ethernet/intel/igc/e1000_base.c
+++ b/drivers/net/ethernet/intel/igc/e1000_base.c
@@ -11,9 +11,49 @@
/* forward declaration */
static s32 igc_get_invariants_base(struct e1000_hw *);
+static s32 igc_check_for_link_base(struct e1000_hw *);
static s32 igc_init_hw_base(struct e1000_hw *);
static s32 igc_reset_hw_base(struct e1000_hw *);
static s32 igc_set_pcie_completion_timeout(struct e1000_hw *hw);
+static s32 igc_read_mac_addr_base(struct e1000_hw *hw);
+
+/**
+ * igc_init_nvm_params_base - Init NVM func ptrs.
+ * @hw: pointer to the HW structure
+ **/
+static s32 igc_init_nvm_params_base(struct e1000_hw *hw)
+{
+ struct e1000_nvm_info *nvm = &hw->nvm;
+ u32 eecd = rd32(E1000_EECD);
+ u16 size;
+
+ 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 = BIT(size);
+ nvm->opcode_bits = 8;
+ nvm->delay_usec = 1;
+
+ nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8;
+ nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ?
+ 16 : 8;
+
+ if (nvm->word_size == BIT(15))
+ nvm->page_size = 128;
+
+ return 0;
+}
/**
* igc_init_mac_params_base - Init MAC func ptrs.
@@ -22,6 +62,7 @@ static s32 igc_set_pcie_completion_timeout(struct e1000_hw *hw);
static s32 igc_init_mac_params_base(struct e1000_hw *hw)
{
struct e1000_mac_info *mac = &hw->mac;
+ struct e1000_dev_spec_base *dev_spec = &hw->dev_spec._base;
/* Set mta register count */
mac->mta_reg_count = 128;
@@ -33,6 +74,10 @@ static s32 igc_init_mac_params_base(struct e1000_hw *hw)
mac->ops.acquire_swfw_sync = igc_acquire_swfw_sync_i225;
mac->ops.release_swfw_sync = igc_release_swfw_sync_i225;
+ /* Allow a single clear of the SW semaphore on I225 */
+ if (mac->type == e1000_i225)
+ dev_spec->clear_semaphore_once = true;
+
return 0;
}
@@ -50,11 +95,60 @@ static s32 igc_get_invariants_base(struct e1000_hw *hw)
if (ret_val)
goto out;
+ /* NVM initialization */
+ ret_val = igc_init_nvm_params_base(hw);
+ switch (hw->mac.type) {
+ case e1000_i225:
+ ret_val = igc_init_nvm_params_i225(hw);
+ break;
+ default:
+ break;
+ }
+
+ if (ret_val)
+ goto out;
+
out:
return ret_val;
}
/**
+ * igc_get_link_up_info_base - Get link speed/duplex info
+ * @hw: pointer to the HW structure
+ * @speed: stores the current speed
+ * @duplex: stores the current duplex
+ *
+ * This is a wrapper function, if using the serial gigabit media independent
+ * interface, use PCS to retrieve the link speed and duplex information.
+ * Otherwise, use the generic function to get the link speed and duplex info.
+ **/
+static s32 igc_get_link_up_info_base(struct e1000_hw *hw, u16 *speed,
+ u16 *duplex)
+{
+ s32 ret_val;
+
+ ret_val = igc_get_speed_and_duplex_copper(hw, speed, duplex);
+
+ return ret_val;
+}
+
+/**
+ * igc_check_for_link_base - Check for link
+ * @hw: pointer to the HW structure
+ *
+ * If sgmii is enabled, then use the pcs register to determine link, otherwise
+ * use the generic interface for determining link.
+ **/
+static s32 igc_check_for_link_base(struct e1000_hw *hw)
+{
+ s32 ret_val = 0;
+
+ ret_val = igc_check_for_copper_link(hw);
+
+ return ret_val;
+}
+
+/**
* igc_init_hw_base - Initialize hardware
* @hw: pointer to the HW structure
*
@@ -93,6 +187,19 @@ static s32 igc_init_hw_base(struct e1000_hw *hw)
}
/**
+ * igc_read_mac_addr_base - Read device MAC address
+ * @hw: pointer to the HW structure
+ **/
+static s32 igc_read_mac_addr_base(struct e1000_hw *hw)
+{
+ s32 ret_val = 0;
+
+ ret_val = igc_read_mac_addr(hw);
+
+ return ret_val;
+}
+
+/**
* igc_rx_fifo_flush_base - Clean rx fifo after Rx enable
* @hw: pointer to the HW structure
*
@@ -169,12 +276,17 @@ void igc_rx_fifo_flush_base(struct e1000_hw *hw)
}
static struct e1000_mac_operations e1000_mac_ops_base = {
- .init_hw = igc_init_hw_base,
+ .init_hw = igc_init_hw_base,
+ .check_for_link = igc_check_for_link_base,
+ .rar_set = igc_rar_set,
+ .read_mac_addr = igc_read_mac_addr_base,
+ .get_speed_and_duplex = igc_get_link_up_info_base,
};
const struct e1000_info e1000_base_info = {
- .get_invariants = igc_get_invariants_base,
- .mac_ops = &e1000_mac_ops_base,
+ .get_invariants = igc_get_invariants_base,
+ .mac_ops = &e1000_mac_ops_base,
+ /* TODO phy_ops */
};
/**
diff --git a/drivers/net/ethernet/intel/igc/e1000_defines.h b/drivers/net/ethernet/intel/igc/e1000_defines.h
index 5613806742b1..31bc85cfa149 100644
--- a/drivers/net/ethernet/intel/igc/e1000_defines.h
+++ b/drivers/net/ethernet/intel/igc/e1000_defines.h
@@ -55,6 +55,8 @@
*/
#define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */
#define E1000_RAH_POOL_1 0x00040000
+#define E1000_RAL_MAC_ADDR_LEN 4
+#define E1000_RAH_MAC_ADDR_LEN 2
/* Error Codes */
#define E1000_SUCCESS 0
@@ -77,13 +79,84 @@
#define E1000_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */
#define E1000_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */
+/* SWFW_SYNC Definitions */
+#define E1000_SWFW_EEP_SM 0x1
+#define E1000_SWFW_PHY0_SM 0x2
+#define E1000_SWFW_PHY1_SM 0x4
+#define E1000_SWFW_PHY2_SM 0x20
+#define E1000_SWFW_PHY3_SM 0x40
+
/* NVM Control */
+#define E1000_EECD_SK 0x00000001 /* NVM Clock */
+#define E1000_EECD_CS 0x00000002 /* NVM Chip Select */
+#define E1000_EECD_DI 0x00000004 /* NVM Data In */
+#define E1000_EECD_DO 0x00000008 /* NVM Data Out */
+#define E1000_EECD_REQ 0x00000040 /* NVM Access Request */
+#define E1000_EECD_GNT 0x00000080 /* NVM Access Grant */
#define E1000_EECD_PRES 0x00000100 /* NVM Present */
+/* NVM Addressing bits based on type 0=small, 1=large */
+#define E1000_EECD_ADDR_BITS 0x00000400
+#define E1000_NVM_GRANT_ATTEMPTS 1000 /* NVM # attempts to gain grant */
+#define E1000_EECD_AUTO_RD 0x00000200 /* NVM Auto Read done */
+#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* NVM Size */
+#define E1000_EECD_SIZE_EX_SHIFT 11
+#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 */
/* Number of milliseconds for NVM auto read done after MAC reset. */
#define AUTO_READ_DONE_TIMEOUT 10
#define E1000_EECD_AUTO_RD 0x00000200 /* NVM Auto Read done */
+/* Offset to data in NVM read/write registers */
+#define E1000_NVM_RW_REG_DATA 16
+#define E1000_NVM_RW_REG_DONE 2 /* Offset to READ/WRITE done bit */
+#define E1000_NVM_RW_REG_START 1 /* Start operation */
+#define E1000_NVM_RW_ADDR_SHIFT 2 /* Shift to the address bits */
+#define E1000_NVM_POLL_READ 0 /* Flag for polling for read complete */
+
+/* NVM Word Offsets */
+#define NVM_COMPAT 0x0003
+#define NVM_ID_LED_SETTINGS 0x0004 /* SERDES output amplitude */
+#define NVM_VERSION 0x0005
+#define NVM_INIT_CONTROL2_REG 0x000F
+#define NVM_INIT_CONTROL3_PORT_B 0x0014
+#define NVM_INIT_CONTROL3_PORT_A 0x0024
+#define NVM_ALT_MAC_ADDR_PTR 0x0037
+#define NVM_CHECKSUM_REG 0x003F
+#define NVM_COMPATIBILITY_REG_3 0x0003
+#define NVM_COMPATIBILITY_BIT_MASK 0x8000
+#define NVM_MAC_ADDR 0x0000
+#define NVM_SUB_DEV_ID 0x000B
+#define NVM_SUB_VEN_ID 0x000C
+#define NVM_DEV_ID 0x000D
+#define NVM_VEN_ID 0x000E
+#define NVM_INIT_CTRL_2 0x000F
+#define NVM_INIT_CTRL_4 0x0013
+#define NVM_LED_1_CFG 0x001C
+#define NVM_LED_0_2_CFG 0x001F
+#define NVM_ETRACK_WORD 0x0042
+#define NVM_ETRACK_HIWORD 0x0043
+#define NVM_COMB_VER_OFF 0x0083
+#define NVM_COMB_VER_PTR 0x003d
+
+/* For checksumming, the sum of all words in the NVM should equal 0xBABA. */
+#define NVM_SUM 0xBABA
+
+#define NVM_PBA_OFFSET_0 8
+#define NVM_PBA_OFFSET_1 9
+#define NVM_RESERVED_WORD 0xFFFF
+#define NVM_PBA_PTR_GUARD 0xFAFA
+#define NVM_WORD_SIZE_BASE_SHIFT 6
+
+/* Collision related configuration parameters */
+#define E1000_COLLISION_THRESHOLD 15
+#define E1000_CT_SHIFT 4
+#define E1000_COLLISION_DISTANCE 63
+#define E1000_COLD_SHIFT 12
+
/* Device Status */
#define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */
#define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */
@@ -93,6 +166,14 @@
#define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */
#define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */
#define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */
+#define E1000_STATUS_SPEED_2500 0x00400000 /* Speed 2.5Gb/s */
+
+#define SPEED_10 10
+#define SPEED_100 100
+#define SPEED_1000 1000
+#define SPEED_2500 2500
+#define HALF_DUPLEX 1
+#define FULL_DUPLEX 2
/* Interrupt Cause Read */
#define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */
diff --git a/drivers/net/ethernet/intel/igc/e1000_hw.h b/drivers/net/ethernet/intel/igc/e1000_hw.h
index debeadd61b45..ab630e5b3d97 100644
--- a/drivers/net/ethernet/intel/igc/e1000_hw.h
+++ b/drivers/net/ethernet/intel/igc/e1000_hw.h
@@ -11,6 +11,7 @@
#include "e1000_regs.h"
#include "e1000_defines.h"
#include "e1000_mac.h"
+#include "e1000_nvm.h"
#include "e1000_i225.h"
#include "e1000_base.h"
@@ -81,6 +82,8 @@ struct e1000_info {
struct e1000_nvm_operations *nvm_ops;
};
+extern const struct e1000_info e1000_base_info;
+
struct e1000_mac_info {
struct e1000_mac_operations ops;
@@ -118,7 +121,7 @@ struct e1000_mac_info {
struct e1000_nvm_operations {
s32 (*acquire)(struct e1000_hw *hw);
- s32 (*read)(struct e1000_hw *hw, u16 offset, u16 i, u16 *data);
+ s32 (*read)(struct e1000_hw *hw, u16 offset, u16 words, u16 *data);
void (*release)(struct e1000_hw *hw);
s32 (*write)(struct e1000_hw *hw, u16 offset, u16 i, u16 *data);
s32 (*update)(struct e1000_hw *hw);
diff --git a/drivers/net/ethernet/intel/igc/e1000_i225.c b/drivers/net/ethernet/intel/igc/e1000_i225.c
index 11d797c77619..d1fa45f600dc 100644
--- a/drivers/net/ethernet/intel/igc/e1000_i225.c
+++ b/drivers/net/ethernet/intel/igc/e1000_i225.c
@@ -6,6 +6,32 @@
#include "e1000_hw.h"
/**
+ * igc_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 igc_acquire_nvm_i225(struct e1000_hw *hw)
+{
+ return igc_acquire_swfw_sync_i225(hw, E1000_SWFW_EEP_SM);
+}
+
+/**
+ * igc_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 igc_release_nvm_i225(struct e1000_hw *hw)
+{
+ igc_release_swfw_sync_i225(hw, E1000_SWFW_EEP_SM);
+}
+
+/**
* igc_get_hw_semaphore_i225 - Acquire hardware semaphore
* @hw: pointer to the HW structure
*
@@ -139,3 +165,328 @@ void igc_release_swfw_sync_i225(struct e1000_hw *hw, u16 mask)
igc_put_hw_semaphore(hw);
}
+
+/**
+ * igc_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.
+ **/
+static s32 igc_read_nvm_srrd_i225(struct e1000_hw *hw, u16 offset, u16 words,
+ u16 *data)
+{
+ s32 status = 0;
+ u16 i, count;
+
+ /* 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))) {
+ status = igc_read_nvm_eerd(hw, offset, count,
+ data + i);
+ hw->nvm.ops.release(hw);
+ } else {
+ status = E1000_ERR_SWFW_SYNC;
+ }
+
+ if (status)
+ break;
+ }
+
+ return status;
+}
+
+/**
+ * igc_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 igc_update_nvm_checksum is not called after this function , the
+ * Shadow Ram will most likely contain an invalid checksum.
+ **/
+static s32 igc_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 = 0;
+
+ /* 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) {
+ hw_dbg("nvm parameter(s) out of bounds\n");
+ ret_val = -E1000_ERR_NVM;
+ goto out;
+ }
+
+ for (i = 0; i < words; i++) {
+ eewr = ((offset + i) << E1000_NVM_RW_ADDR_SHIFT) |
+ (data[i] << E1000_NVM_RW_REG_DATA) |
+ E1000_NVM_RW_REG_START;
+
+ wr32(E1000_SRWR, eewr);
+
+ for (k = 0; k < attempts; k++) {
+ if (E1000_NVM_RW_REG_DONE &
+ rd32(E1000_SRWR)) {
+ ret_val = 0;
+ break;
+ }
+ udelay(5);
+ }
+
+ if (ret_val) {
+ hw_dbg("Shadow RAM write EEWR timed out\n");
+ break;
+ }
+ }
+
+out:
+ return ret_val;
+}
+
+/**
+ * igc_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@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.
+ **/
+static s32 igc_write_nvm_srwr_i225(struct e1000_hw *hw, u16 offset, u16 words,
+ u16 *data)
+{
+ s32 status = 0;
+ u16 i, count;
+
+ /* 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))) {
+ status = igc_write_nvm_srwr(hw, offset, count,
+ data + i);
+ hw->nvm.ops.release(hw);
+ } else {
+ status = E1000_ERR_SWFW_SYNC;
+ }
+
+ if (status)
+ break;
+ }
+
+ return status;
+}
+
+/**
+ * igc_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.
+ **/
+static s32 igc_validate_nvm_checksum_i225(struct e1000_hw *hw)
+{
+ s32 status = 0;
+ s32 (*read_op_ptr)(struct e1000_hw *hw, u16 offset, u16 count,
+ u16 *data);
+
+ if (!(hw->nvm.ops.acquire(hw))) {
+ /* 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 = igc_read_nvm_eerd;
+
+ status = igc_validate_nvm_checksum(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;
+}
+
+/**
+ * igc_pool_flash_update_done_i225 - Pool FLUDONE status
+ * @hw: pointer to the HW structure
+ **/
+static s32 igc_pool_flash_update_done_i225(struct e1000_hw *hw)
+{
+ s32 ret_val = -E1000_ERR_NVM;
+ u32 i, reg;
+
+ for (i = 0; i < E1000_FLUDONE_ATTEMPTS; i++) {
+ reg = rd32(E1000_EECD);
+ if (reg & E1000_EECD_FLUDONE_I225) {
+ ret_val = 0;
+ break;
+ }
+ udelay(5);
+ }
+
+ return ret_val;
+}
+
+/**
+ * igc_update_flash_i225 - Commit EEPROM to the flash
+ * @hw: pointer to the HW structure
+ **/
+static s32 igc_update_flash_i225(struct e1000_hw *hw)
+{
+ s32 ret_val = 0;
+ u32 flup;
+
+ ret_val = igc_pool_flash_update_done_i225(hw);
+ if (ret_val == -E1000_ERR_NVM) {
+ hw_dbg("Flash update time out\n");
+ goto out;
+ }
+
+ flup = rd32(E1000_EECD) | E1000_EECD_FLUPD_I225;
+ wr32(E1000_EECD, flup);
+
+ ret_val = igc_pool_flash_update_done_i225(hw);
+ if (ret_val)
+ hw_dbg("Flash update time out\n");
+ else
+ hw_dbg("Flash update complete\n");
+
+out:
+ return ret_val;
+}
+
+/**
+ * igc_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.
+ **/
+static s32 igc_update_nvm_checksum_i225(struct e1000_hw *hw)
+{
+ s32 ret_val = 0;
+ u16 checksum = 0;
+ u16 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 = igc_read_nvm_eerd(hw, 0, 1, &nvm_data);
+ if (ret_val) {
+ hw_dbg("EEPROM read failed\n");
+ goto out;
+ }
+
+ if (!(hw->nvm.ops.acquire(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 < NVM_CHECKSUM_REG; i++) {
+ ret_val = igc_read_nvm_eerd(hw, i, 1, &nvm_data);
+ if (ret_val) {
+ hw->nvm.ops.release(hw);
+ hw_dbg("NVM Read Error while updating checksum.\n");
+ goto out;
+ }
+ checksum += nvm_data;
+ }
+ checksum = (u16)NVM_SUM - checksum;
+ ret_val = igc_write_nvm_srwr(hw, NVM_CHECKSUM_REG, 1,
+ &checksum);
+ if (ret_val) {
+ hw->nvm.ops.release(hw);
+ hw_dbg("NVM Write Error while updating checksum.\n");
+ goto out;
+ }
+
+ hw->nvm.ops.release(hw);
+
+ ret_val = igc_update_flash_i225(hw);
+ } else {
+ ret_val = -E1000_ERR_SWFW_SYNC;
+ }
+
+out:
+ return ret_val;
+}
+
+/**
+ * igc_get_flash_presence_i225 - Check if flash device is detected
+ * @hw: pointer to the HW structure
+ **/
+bool igc_get_flash_presence_i225(struct e1000_hw *hw)
+{
+ u32 eec = 0;
+ bool ret_val = false;
+
+ eec = rd32(E1000_EECD);
+ if (eec & E1000_EECD_FLASH_DETECTED_I225)
+ ret_val = true;
+
+ return ret_val;
+}
+
+/**
+ * igc_init_nvm_params_i225 - Init NVM func ptrs.
+ * @hw: pointer to the HW structure
+ **/
+s32 igc_init_nvm_params_i225(struct e1000_hw *hw)
+{
+ s32 ret_val = 0;
+ struct e1000_nvm_info *nvm = &hw->nvm;
+
+ nvm->ops.acquire = igc_acquire_nvm_i225;
+ nvm->ops.release = igc_release_nvm_i225;
+
+ /* NVM Function Pointers */
+ if (igc_get_flash_presence_i225(hw)) {
+ hw->nvm.type = e1000_nvm_flash_hw;
+ nvm->ops.read = igc_read_nvm_srrd_i225;
+ nvm->ops.write = igc_write_nvm_srwr_i225;
+ nvm->ops.validate = igc_validate_nvm_checksum_i225;
+ nvm->ops.update = igc_update_nvm_checksum_i225;
+ } else {
+ hw->nvm.type = e1000_nvm_invm;
+ nvm->ops.read = igc_read_nvm_eerd;
+ nvm->ops.write = NULL;
+ nvm->ops.validate = NULL;
+ nvm->ops.update = NULL;
+ }
+ return ret_val;
+}
diff --git a/drivers/net/ethernet/intel/igc/e1000_i225.h b/drivers/net/ethernet/intel/igc/e1000_i225.h
index 749a50c34534..c8c0398d9ff0 100644
--- a/drivers/net/ethernet/intel/igc/e1000_i225.h
+++ b/drivers/net/ethernet/intel/igc/e1000_i225.h
@@ -7,4 +7,7 @@
s32 igc_acquire_swfw_sync_i225(struct e1000_hw *hw, u16 mask);
void igc_release_swfw_sync_i225(struct e1000_hw *hw, u16 mask);
+s32 igc_init_nvm_params_i225(struct e1000_hw *hw);
+bool igc_get_flash_presence_i225(struct e1000_hw *hw);
+
#endif
diff --git a/drivers/net/ethernet/intel/igc/e1000_mac.c b/drivers/net/ethernet/intel/igc/e1000_mac.c
index 27e478a9854c..5681c0e3f0c8 100644
--- a/drivers/net/ethernet/intel/igc/e1000_mac.c
+++ b/drivers/net/ethernet/intel/igc/e1000_mac.c
@@ -326,6 +326,132 @@ void igc_clear_hw_cntrs_base(struct e1000_hw *hw)
}
/**
+ * igc_rar_set - Set receive address register
+ * @hw: pointer to the HW structure
+ * @addr: pointer to the receive address
+ * @index: receive address array register
+ *
+ * Sets the receive address array register at index to the address passed
+ * in by addr.
+ **/
+void igc_rar_set(struct e1000_hw *hw, u8 *addr, u32 index)
+{
+ u32 rar_low, rar_high;
+
+ /* HW expects these in little endian so we reverse the byte order
+ * from network order (big endian) to little endian
+ */
+ rar_low = ((u32)addr[0] |
+ ((u32)addr[1] << 8) |
+ ((u32)addr[2] << 16) | ((u32)addr[3] << 24));
+
+ rar_high = ((u32)addr[4] | ((u32)addr[5] << 8));
+
+ /* If MAC address zero, no need to set the AV bit */
+ if (rar_low || rar_high)
+ rar_high |= E1000_RAH_AV;
+
+ /* Some bridges will combine consecutive 32-bit writes into
+ * a single burst write, which will malfunction on some parts.
+ * The flushes avoid this.
+ */
+ wr32(E1000_RAL(index), rar_low);
+ wrfl();
+ wr32(E1000_RAH(index), rar_high);
+ wrfl();
+}
+
+/**
+ * igc_check_for_copper_link - Check for link (Copper)
+ * @hw: pointer to the HW structure
+ *
+ * Checks to see of the link status of the hardware has changed. If a
+ * change in link status has been detected, then we read the PHY registers
+ * to get the current speed/duplex if link exists.
+ **/
+s32 igc_check_for_copper_link(struct e1000_hw *hw)
+{
+ struct e1000_mac_info *mac = &hw->mac;
+ s32 ret_val;
+ bool link;
+
+ /* We only want to go out to the PHY registers to see if Auto-Neg
+ * has completed and/or if our link status has changed. The
+ * get_link_status flag is set upon receiving a Link Status
+ * Change or Rx Sequence Error interrupt.
+ */
+ if (!mac->get_link_status) {
+ ret_val = 0;
+ goto out;
+ }
+
+ /* First we want to see if the MII Status Register reports
+ * link. If so, then we want to get the current speed/duplex
+ * of the PHY.
+ */
+ /* TODO ret_val = igc_phy_has_link(hw, 1, 0, &link); */
+ if (ret_val)
+ goto out;
+
+ if (!link)
+ goto out; /* No link detected */
+
+ mac->get_link_status = false;
+
+ /* Check if there was DownShift, must be checked
+ * immediately after link-up
+ */
+ /* TODO igc_check_downshift(hw); */
+
+ /* If we are forcing speed/duplex, then we simply return since
+ * we have already determined whether we have link or not.
+ */
+ if (!mac->autoneg) {
+ ret_val = -E1000_ERR_CONFIG;
+ goto out;
+ }
+
+ /* Auto-Neg is enabled. Auto Speed Detection takes care
+ * of MAC speed/duplex configuration. So we only need to
+ * configure Collision Distance in the MAC.
+ */
+ igc_config_collision_dist(hw);
+
+ /* Configure Flow Control now that Auto-Neg has completed.
+ * First, we need to restore the desired flow control
+ * settings because we may have had to re-autoneg with a
+ * different link partner.
+ */
+ /* TODO ret_val = igc_config_fc_after_link_up(hw); */
+ if (ret_val)
+ hw_dbg("Error configuring flow control\n");
+
+out:
+ return ret_val;
+}
+
+/**
+ * igc_config_collision_dist - Configure collision distance
+ * @hw: pointer to the HW structure
+ *
+ * Configures the collision distance to the default value and is used
+ * during link setup. Currently no func pointer exists and all
+ * implementations are handled in the generic version of this function.
+ **/
+void igc_config_collision_dist(struct e1000_hw *hw)
+{
+ u32 tctl;
+
+ tctl = rd32(E1000_TCTL);
+
+ tctl &= ~E1000_TCTL_COLD;
+ tctl |= E1000_COLLISION_DISTANCE << E1000_COLD_SHIFT;
+
+ wr32(E1000_TCTL, tctl);
+ wrfl();
+}
+
+/**
* igc_get_auto_rd_done - Check for auto read completion
* @hw: pointer to the HW structure
*
@@ -354,6 +480,53 @@ s32 igc_get_auto_rd_done(struct e1000_hw *hw)
}
/**
+ * igc_get_speed_and_duplex_copper - Retrieve current speed/duplex
+ * @hw: pointer to the HW structure
+ * @speed: stores the current speed
+ * @duplex: stores the current duplex
+ *
+ * Read the status register for the current speed/duplex and store the current
+ * speed and duplex for copper connections.
+ **/
+s32 igc_get_speed_and_duplex_copper(struct e1000_hw *hw, u16 *speed,
+ u16 *duplex)
+{
+ u32 status;
+
+ status = rd32(E1000_STATUS);
+ if (status & E1000_STATUS_SPEED_1000) {
+ /* For I225, STATUS will indicate 1G speed in both 1 Gbps
+ * and 2.5 Gbps link modes. An additional bit is used
+ * to differentiate between 1 Gbps and 2.5 Gbps.
+ */
+ if (hw->mac.type == e1000_i225 &&
+ (status & E1000_STATUS_SPEED_2500)) {
+ *speed = SPEED_2500;
+ hw_dbg("2500 Mbs, ");
+ } else {
+ *speed = SPEED_1000;
+ hw_dbg("1000 Mbs, ");
+ }
+ } else if (status & E1000_STATUS_SPEED_100) {
+ *speed = SPEED_100;
+ hw_dbg("100 Mbs, ");
+ } else {
+ *speed = SPEED_10;
+ hw_dbg("10 Mbs, ");
+ }
+
+ if (status & E1000_STATUS_FD) {
+ *duplex = FULL_DUPLEX;
+ hw_dbg("Full Duplex\n");
+ } else {
+ *duplex = HALF_DUPLEX;
+ hw_dbg("Half Duplex\n");
+ }
+
+ return 0;
+}
+
+/**
* igc_put_hw_semaphore - Release hardware semaphore
* @hw: pointer to the HW structure
*
diff --git a/drivers/net/ethernet/intel/igc/e1000_mac.h b/drivers/net/ethernet/intel/igc/e1000_mac.h
index 2f8dbf17800a..f18f5221199f 100644
--- a/drivers/net/ethernet/intel/igc/e1000_mac.h
+++ b/drivers/net/ethernet/intel/igc/e1000_mac.h
@@ -12,13 +12,20 @@
#endif /* E1000_REMOVED */
/* forward declaration */
-s32 igc_disable_pcie_master(struct e1000_hw *hw);
+s32 igc_check_for_copper_link(struct e1000_hw *hw);
+
+s32 igc_disable_pcie_master(struct e1000_hw *hw);
void igc_init_rx_addrs(struct e1000_hw *hw, u16 rar_count);
-s32 igc_setup_link(struct e1000_hw *hw);
+s32 igc_setup_link(struct e1000_hw *hw);
void igc_clear_hw_cntrs_base(struct e1000_hw *hw);
s32 igc_get_auto_rd_done(struct e1000_hw *hw);
void igc_put_hw_semaphore(struct e1000_hw *hw);
+void igc_rar_set(struct e1000_hw *hw, u8 *addr, u32 index);
+
+void igc_config_collision_dist(struct e1000_hw *hw);
-s32 igc_get_bus_info_pcie(struct e1000_hw *hw);
+s32 igc_get_bus_info_pcie(struct e1000_hw *hw);
+s32 igc_get_speed_and_duplex_copper(struct e1000_hw *hw, u16 *speed,
+ u16 *duplex);
#endif
diff --git a/drivers/net/ethernet/intel/igc/e1000_nvm.c b/drivers/net/ethernet/intel/igc/e1000_nvm.c
new file mode 100644
index 000000000000..62a4ae7f224b
--- /dev/null
+++ b/drivers/net/ethernet/intel/igc/e1000_nvm.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018 Intel Corporation */
+
+#include "e1000_mac.h"
+#include "e1000_nvm.h"
+
+/**
+ * igc_poll_eerd_eewr_done - Poll for EEPROM read/write completion
+ * @hw: pointer to the HW structure
+ * @ee_reg: EEPROM flag for polling
+ *
+ * Polls the EEPROM status bit for either read or write completion based
+ * upon the value of 'ee_reg'.
+ **/
+static s32 igc_poll_eerd_eewr_done(struct e1000_hw *hw, int ee_reg)
+{
+ u32 attempts = 100000;
+ u32 i, reg = 0;
+ s32 ret_val = -E1000_ERR_NVM;
+
+ for (i = 0; i < attempts; i++) {
+ if (ee_reg == E1000_NVM_POLL_READ)
+ reg = rd32(E1000_EERD);
+ else
+ reg = rd32(E1000_EEWR);
+
+ if (reg & E1000_NVM_RW_REG_DONE) {
+ ret_val = 0;
+ break;
+ }
+
+ udelay(5);
+ }
+
+ return ret_val;
+}
+
+/**
+ * igc_acquire_nvm - Generic request for access to EEPROM
+ * @hw: pointer to the HW structure
+ *
+ * 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).
+ **/
+s32 igc_acquire_nvm(struct e1000_hw *hw)
+{
+ u32 eecd = rd32(E1000_EECD);
+ s32 timeout = E1000_NVM_GRANT_ATTEMPTS;
+ s32 ret_val = 0;
+
+ wr32(E1000_EECD, eecd | E1000_EECD_REQ);
+ eecd = rd32(E1000_EECD);
+
+ while (timeout) {
+ if (eecd & E1000_EECD_GNT)
+ break;
+ udelay(5);
+ eecd = rd32(E1000_EECD);
+ timeout--;
+ }
+
+ if (!timeout) {
+ eecd &= ~E1000_EECD_REQ;
+ wr32(E1000_EECD, eecd);
+ hw_dbg("Could not acquire NVM grant\n");
+ ret_val = -E1000_ERR_NVM;
+ }
+
+ return ret_val;
+}
+
+/**
+ * igc_release_nvm - Release exclusive access to EEPROM
+ * @hw: pointer to the HW structure
+ *
+ * Stop any current commands to the EEPROM and clear the EEPROM request bit.
+ **/
+void igc_release_nvm(struct e1000_hw *hw)
+{
+ u32 eecd;
+
+ eecd = rd32(E1000_EECD);
+ eecd &= ~E1000_EECD_REQ;
+ wr32(E1000_EECD, eecd);
+}
+
+/**
+ * igc_read_nvm_eerd - Reads EEPROM using EERD register
+ * @hw: pointer to the HW structure
+ * @offset: offset of word in the EEPROM to read
+ * @words: number of words to read
+ * @data: word read from the EEPROM
+ *
+ * Reads a 16 bit word from the EEPROM using the EERD register.
+ **/
+s32 igc_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
+{
+ struct e1000_nvm_info *nvm = &hw->nvm;
+ u32 i, eerd = 0;
+ s32 ret_val = 0;
+
+ /* 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) {
+ hw_dbg("nvm parameter(s) out of bounds\n");
+ ret_val = -E1000_ERR_NVM;
+ goto out;
+ }
+
+ for (i = 0; i < words; i++) {
+ eerd = ((offset + i) << E1000_NVM_RW_ADDR_SHIFT) +
+ E1000_NVM_RW_REG_START;
+
+ wr32(E1000_EERD, eerd);
+ ret_val = igc_poll_eerd_eewr_done(hw, E1000_NVM_POLL_READ);
+ if (ret_val)
+ break;
+
+ data[i] = (rd32(E1000_EERD) >> E1000_NVM_RW_REG_DATA);
+ }
+
+out:
+ return ret_val;
+}
+
+/**
+ * igc_read_mac_addr - Read device MAC address
+ * @hw: pointer to the HW structure
+ *
+ * Reads the device MAC address from the EEPROM and stores the value.
+ * Since devices with two ports use the same EEPROM, we increment the
+ * last bit in the MAC address for the second port.
+ **/
+s32 igc_read_mac_addr(struct e1000_hw *hw)
+{
+ u32 rar_high;
+ u32 rar_low;
+ u16 i;
+
+ rar_high = rd32(E1000_RAH(0));
+ rar_low = rd32(E1000_RAL(0));
+
+ for (i = 0; i < E1000_RAL_MAC_ADDR_LEN; i++)
+ hw->mac.perm_addr[i] = (u8)(rar_low >> (i * 8));
+
+ for (i = 0; i < E1000_RAH_MAC_ADDR_LEN; i++)
+ hw->mac.perm_addr[i + 4] = (u8)(rar_high >> (i * 8));
+
+ for (i = 0; i < ETH_ALEN; i++)
+ hw->mac.addr[i] = hw->mac.perm_addr[i];
+
+ return 0;
+}
+
+/**
+ * igc_validate_nvm_checksum - 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 igc_validate_nvm_checksum(struct e1000_hw *hw)
+{
+ s32 ret_val = 0;
+ u16 checksum = 0;
+ u16 i, nvm_data;
+
+ for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) {
+ ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data);
+ if (ret_val) {
+ hw_dbg("NVM Read Error\n");
+ goto out;
+ }
+ checksum += nvm_data;
+ }
+
+ if (checksum != (u16)NVM_SUM) {
+ hw_dbg("NVM Checksum Invalid\n");
+ ret_val = -E1000_ERR_NVM;
+ goto out;
+ }
+
+out:
+ return ret_val;
+}
+
+/**
+ * igc_update_nvm_checksum - 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.
+ **/
+s32 igc_update_nvm_checksum(struct e1000_hw *hw)
+{
+ s32 ret_val;
+ u16 checksum = 0;
+ u16 i, nvm_data;
+
+ for (i = 0; i < NVM_CHECKSUM_REG; i++) {
+ ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data);
+ if (ret_val) {
+ hw_dbg("NVM Read Error while updating checksum.\n");
+ goto out;
+ }
+ checksum += nvm_data;
+ }
+ checksum = (u16)NVM_SUM - checksum;
+ ret_val = hw->nvm.ops.write(hw, NVM_CHECKSUM_REG, 1, &checksum);
+ if (ret_val)
+ hw_dbg("NVM Write Error while updating checksum.\n");
+
+out:
+ return ret_val;
+}
diff --git a/drivers/net/ethernet/intel/igc/e1000_nvm.h b/drivers/net/ethernet/intel/igc/e1000_nvm.h
new file mode 100644
index 000000000000..b6237bd515e9
--- /dev/null
+++ b/drivers/net/ethernet/intel/igc/e1000_nvm.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2018 Intel Corporation */
+
+#ifndef _E1000_NVM_H_
+#define _E1000_NVM_H_
+
+s32 igc_acquire_nvm(struct e1000_hw *hw);
+void igc_release_nvm(struct e1000_hw *hw);
+s32 igc_read_mac_addr(struct e1000_hw *hw);
+
+s32 igc_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data);
+
+s32 igc_validate_nvm_checksum(struct e1000_hw *hw);
+s32 igc_update_nvm_checksum(struct e1000_hw *hw);
+
+#endif
diff --git a/drivers/net/ethernet/intel/igc/e1000_regs.h b/drivers/net/ethernet/intel/igc/e1000_regs.h
index 66d5c757dae8..698ce0cac757 100644
--- a/drivers/net/ethernet/intel/igc/e1000_regs.h
+++ b/drivers/net/ethernet/intel/igc/e1000_regs.h
@@ -259,6 +259,9 @@
/* Management registers */
#define E1000_MANC 0x05820 /* Management Control - RW */
+/* Shadow Ram Write Register - RW */
+#define E1000_SRWR 0x12018
+
/* forward declaration */
struct e1000_hw;
u32 igc_rd32(struct e1000_hw *hw, u32 reg);
diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h
index c61212ccb60e..735a5e3d0717 100644
--- a/drivers/net/ethernet/intel/igc/igc.h
+++ b/drivers/net/ethernet/intel/igc/igc.h
@@ -133,6 +133,10 @@ enum igc_tx_flags {
IGC_TX_FLAGS_CSUM = 0x20,
};
+enum igc_boards {
+ board_base,
+};
+
/** The largest size we can write to the descriptor is 65535. In order to
* maintain a power of two alignment we have to limit ourselves to 32K.
**/
@@ -348,6 +352,8 @@ struct igc_adapter {
spinlock_t nfc_lock;
struct igc_mac_addr *mac_table;
+
+ struct e1000_info ei;
};
/* igc_desc_unused - calculate if we have unused descriptors */
diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
index 9a99c7d68796..dcc7e700074f 100644
--- a/drivers/net/ethernet/intel/igc/igc_main.c
+++ b/drivers/net/ethernet/intel/igc/igc_main.c
@@ -19,9 +19,13 @@ static const char igc_driver_string[] = DRV_SUMMARY;
static const char igc_copyright[] =
"Copyright(c) 2018 Intel Corporation.";
+static const struct e1000_info *igc_info_tbl[] = {
+ [board_base] = &e1000_base_info,
+};
+
static const struct pci_device_id igc_pci_tbl[] = {
- { PCI_VDEVICE(INTEL, E1000_DEV_ID_I225_LM) },
- { PCI_VDEVICE(INTEL, E1000_DEV_ID_I225_V) },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_I225_LM), board_base },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_I225_V), board_base },
/* required last entry */
{0, }
};
@@ -3334,6 +3338,7 @@ static int igc_probe(struct pci_dev *pdev,
struct net_device *netdev;
struct igc_adapter *adapter;
struct e1000_hw *hw;
+ const struct e1000_info *ei = igc_info_tbl[ent->driver_data];
int err, pci_using_dac;
err = pci_enable_device_mem(pdev);
@@ -3417,6 +3422,14 @@ static int igc_probe(struct pci_dev *pdev,
hw->subsystem_vendor_id = pdev->subsystem_vendor;
hw->subsystem_device_id = pdev->subsystem_device;
+ /* Copy the default MAC and PHY function pointers */
+ memcpy(&hw->mac.ops, ei->mac_ops, sizeof(hw->mac.ops));
+
+ /* Initialize skew-specific constants */
+ err = ei->get_invariants(hw);
+ if (err)
+ goto err_sw_init;
+
/* setup the private structure */
err = igc_sw_init(adapter);
if (err)
@@ -3452,6 +3465,9 @@ static int igc_probe(struct pci_dev *pdev,
/* carrier off reporting is important to ethtool even BEFORE open */
netif_carrier_off(netdev);
+ /* Check if Media Autosense is enabled */
+ adapter->ei = *ei;
+
dev_info(&pdev->dev, "@SUMMARY@");
/* print bus type/speed/width info */
dev_info(&pdev->dev, "%s: (PCIe:%s:%s) ",
--
2.11.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [Intel-wired-lan] [PATCH v3 08/11] igc: Add NVM support
2018-06-24 8:45 [Intel-wired-lan] [PATCH v3 08/11] igc: Add NVM support Sasha Neftin
@ 2018-06-24 16:39 ` kbuild test robot
2018-06-28 23:35 ` Shannon Nelson
1 sibling, 0 replies; 4+ messages in thread
From: kbuild test robot @ 2018-06-24 16:39 UTC (permalink / raw)
To: intel-wired-lan
Hi Sasha,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on jkirsher-next-queue/dev-queue]
[also build test WARNING on v4.18-rc2 next-20180622]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Sasha-Neftin/igc-Add-skeletal-frame-for-Intel-R-2-5G-Ethernet-Controller-support/20180624-164739
base: https://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next-queue.git dev-queue
reproduce:
# apt-get install sparse
make ARCH=x86_64 allmodconfig
make C=1 CF=-D__CHECK_ENDIAN__
sparse warnings: (new ones prefixed by >>)
>> drivers/net/ethernet/intel/igc/e1000_nvm.c:52:9: sparse: incorrect type in initializer (different address spaces) @@ expected unsigned char [noderef] [usertype] <asn:2>*hw_addr @@ got deref] [usertype] <asn:2>*hw_addr @@
drivers/net/ethernet/intel/igc/e1000_nvm.c:52:9: expected unsigned char [noderef] [usertype] <asn:2>*hw_addr
drivers/net/ethernet/intel/igc/e1000_nvm.c:52:9: got unsigned char [usertype] *__val
drivers/net/ethernet/intel/igc/e1000_nvm.c:65:17: sparse: incorrect type in initializer (different address spaces) @@ expected unsigned char [noderef] [usertype] <asn:2>*hw_addr @@ got deref] [usertype] <asn:2>*hw_addr @@
drivers/net/ethernet/intel/igc/e1000_nvm.c:65:17: expected unsigned char [noderef] [usertype] <asn:2>*hw_addr
drivers/net/ethernet/intel/igc/e1000_nvm.c:65:17: got unsigned char [usertype] *__val
drivers/net/ethernet/intel/igc/e1000_nvm.c:85:9: sparse: incorrect type in initializer (different address spaces) @@ expected unsigned char [noderef] [usertype] <asn:2>*hw_addr @@ got deref] [usertype] <asn:2>*hw_addr @@
drivers/net/ethernet/intel/igc/e1000_nvm.c:85:9: expected unsigned char [noderef] [usertype] <asn:2>*hw_addr
drivers/net/ethernet/intel/igc/e1000_nvm.c:85:9: got unsigned char [usertype] *__val
drivers/net/ethernet/intel/igc/e1000_nvm.c:117:17: sparse: incorrect type in initializer (different address spaces) @@ expected unsigned char [noderef] [usertype] <asn:2>*hw_addr @@ got deref] [usertype] <asn:2>*hw_addr @@
drivers/net/ethernet/intel/igc/e1000_nvm.c:117:17: expected unsigned char [noderef] [usertype] <asn:2>*hw_addr
drivers/net/ethernet/intel/igc/e1000_nvm.c:117:17: got unsigned char [usertype] *__val
vim +52 drivers/net/ethernet/intel/igc/e1000_nvm.c
37
38 /**
39 * igc_acquire_nvm - Generic request for access to EEPROM
40 * @hw: pointer to the HW structure
41 *
42 * Set the EEPROM access request bit and wait for EEPROM access grant bit.
43 * Return successful if access grant bit set, else clear the request for
44 * EEPROM access and return -E1000_ERR_NVM (-1).
45 **/
46 s32 igc_acquire_nvm(struct e1000_hw *hw)
47 {
48 u32 eecd = rd32(E1000_EECD);
49 s32 timeout = E1000_NVM_GRANT_ATTEMPTS;
50 s32 ret_val = 0;
51
> 52 wr32(E1000_EECD, eecd | E1000_EECD_REQ);
53 eecd = rd32(E1000_EECD);
54
55 while (timeout) {
56 if (eecd & E1000_EECD_GNT)
57 break;
58 udelay(5);
59 eecd = rd32(E1000_EECD);
60 timeout--;
61 }
62
63 if (!timeout) {
64 eecd &= ~E1000_EECD_REQ;
65 wr32(E1000_EECD, eecd);
66 hw_dbg("Could not acquire NVM grant\n");
67 ret_val = -E1000_ERR_NVM;
68 }
69
70 return ret_val;
71 }
72
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
^ permalink raw reply [flat|nested] 4+ messages in thread
* [Intel-wired-lan] [PATCH v3 08/11] igc: Add NVM support
2018-06-24 8:45 [Intel-wired-lan] [PATCH v3 08/11] igc: Add NVM support Sasha Neftin
2018-06-24 16:39 ` kbuild test robot
@ 2018-06-28 23:35 ` Shannon Nelson
2018-07-08 12:52 ` Neftin, Sasha
1 sibling, 1 reply; 4+ messages in thread
From: Shannon Nelson @ 2018-06-28 23:35 UTC (permalink / raw)
To: intel-wired-lan
On 6/24/2018 1:45 AM, Sasha Neftin wrote:
> Add code for NVM support and get MAC address, complete probe
> method.
>
> Sasha Neftin (v2):
> minor cosmetic changes
>
> Alexander Duyck (v3):
> NVM access code optimization
>
> Signed-off-by: Sasha Neftin <sasha.neftin@intel.com>
> Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
> ---
> drivers/net/ethernet/intel/igc/Makefile | 2 +-
> drivers/net/ethernet/intel/igc/e1000_base.c | 118 ++++++++-
> drivers/net/ethernet/intel/igc/e1000_defines.h | 81 ++++++
> drivers/net/ethernet/intel/igc/e1000_hw.h | 5 +-
> drivers/net/ethernet/intel/igc/e1000_i225.c | 351 +++++++++++++++++++++++++
> drivers/net/ethernet/intel/igc/e1000_i225.h | 3 +
> drivers/net/ethernet/intel/igc/e1000_mac.c | 173 ++++++++++++
> drivers/net/ethernet/intel/igc/e1000_mac.h | 13 +-
> drivers/net/ethernet/intel/igc/e1000_nvm.c | 219 +++++++++++++++
> drivers/net/ethernet/intel/igc/e1000_nvm.h | 16 ++
> drivers/net/ethernet/intel/igc/e1000_regs.h | 3 +
> drivers/net/ethernet/intel/igc/igc.h | 6 +
> drivers/net/ethernet/intel/igc/igc_main.c | 20 +-
> 13 files changed, 1000 insertions(+), 10 deletions(-)
> create mode 100644 drivers/net/ethernet/intel/igc/e1000_nvm.c
> create mode 100644 drivers/net/ethernet/intel/igc/e1000_nvm.h
>
> diff --git a/drivers/net/ethernet/intel/igc/Makefile b/drivers/net/ethernet/intel/igc/Makefile
> index cb260bedfa37..5147bca1a900 100644
> --- a/drivers/net/ethernet/intel/igc/Makefile
> +++ b/drivers/net/ethernet/intel/igc/Makefile
> @@ -7,4 +7,4 @@
>
> obj-$(CONFIG_IGC) += igc.o
>
> -igc-objs := igc_main.o e1000_mac.o e1000_i225.o e1000_base.o
> +igc-objs := igc_main.o e1000_mac.o e1000_i225.o e1000_base.o e1000_nvm.o
> diff --git a/drivers/net/ethernet/intel/igc/e1000_base.c b/drivers/net/ethernet/intel/igc/e1000_base.c
> index 395d7ce70bb4..6db31349daa4 100644
> --- a/drivers/net/ethernet/intel/igc/e1000_base.c
> +++ b/drivers/net/ethernet/intel/igc/e1000_base.c
> @@ -11,9 +11,49 @@
>
> /* forward declaration */
> static s32 igc_get_invariants_base(struct e1000_hw *);
> +static s32 igc_check_for_link_base(struct e1000_hw *);
> static s32 igc_init_hw_base(struct e1000_hw *);
> static s32 igc_reset_hw_base(struct e1000_hw *);
> static s32 igc_set_pcie_completion_timeout(struct e1000_hw *hw);
> +static s32 igc_read_mac_addr_base(struct e1000_hw *hw);
> +
> +/**
> + * igc_init_nvm_params_base - Init NVM func ptrs.
> + * @hw: pointer to the HW structure
> + **/
> +static s32 igc_init_nvm_params_base(struct e1000_hw *hw)
> +{
> + struct e1000_nvm_info *nvm = &hw->nvm;
> + u32 eecd = rd32(E1000_EECD);
> + u16 size;
> +
> + 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 = BIT(size);
> + nvm->opcode_bits = 8;
> + nvm->delay_usec = 1;
> +
> + nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8;
> + nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ?
> + 16 : 8;
> +
> + if (nvm->word_size == BIT(15))
> + nvm->page_size = 128;
> +
> + return 0;
> +}
>
> /**
> * igc_init_mac_params_base - Init MAC func ptrs.
> @@ -22,6 +62,7 @@ static s32 igc_set_pcie_completion_timeout(struct e1000_hw *hw);
> static s32 igc_init_mac_params_base(struct e1000_hw *hw)
> {
> struct e1000_mac_info *mac = &hw->mac;
> + struct e1000_dev_spec_base *dev_spec = &hw->dev_spec._base;
reverse xmas tree
>
> /* Set mta register count */
> mac->mta_reg_count = 128;
> @@ -33,6 +74,10 @@ static s32 igc_init_mac_params_base(struct e1000_hw *hw)
> mac->ops.acquire_swfw_sync = igc_acquire_swfw_sync_i225;
> mac->ops.release_swfw_sync = igc_release_swfw_sync_i225;
>
> + /* Allow a single clear of the SW semaphore on I225 */
> + if (mac->type == e1000_i225)
> + dev_spec->clear_semaphore_once = true;
> +
> return 0;
> }
>
> @@ -50,11 +95,60 @@ static s32 igc_get_invariants_base(struct e1000_hw *hw)
> if (ret_val)
> goto out;
>
> + /* NVM initialization */
> + ret_val = igc_init_nvm_params_base(hw);
> + switch (hw->mac.type) {
> + case e1000_i225:
> + ret_val = igc_init_nvm_params_i225(hw);
> + break;
> + default:
> + break;
> + }
> +
> + if (ret_val)
> + goto out;
> +
> out:
> return ret_val;
> }
>
> /**
> + * igc_get_link_up_info_base - Get link speed/duplex info
> + * @hw: pointer to the HW structure
> + * @speed: stores the current speed
> + * @duplex: stores the current duplex
> + *
> + * This is a wrapper function, if using the serial gigabit media independent
> + * interface, use PCS to retrieve the link speed and duplex information.
> + * Otherwise, use the generic function to get the link speed and duplex info.
> + **/
> +static s32 igc_get_link_up_info_base(struct e1000_hw *hw, u16 *speed,
> + u16 *duplex)
> +{
> + s32 ret_val;
> +
> + ret_val = igc_get_speed_and_duplex_copper(hw, speed, duplex);
> +
> + return ret_val;
> +}
> +
> +/**
> + * igc_check_for_link_base - Check for link
> + * @hw: pointer to the HW structure
> + *
> + * If sgmii is enabled, then use the pcs register to determine link, otherwise
> + * use the generic interface for determining link.
> + **/
> +static s32 igc_check_for_link_base(struct e1000_hw *hw)
> +{
> + s32 ret_val = 0;
> +
> + ret_val = igc_check_for_copper_link(hw);
> +
> + return ret_val;
> +}
> +
> +/**
> * igc_init_hw_base - Initialize hardware
> * @hw: pointer to the HW structure
> *
> @@ -93,6 +187,19 @@ static s32 igc_init_hw_base(struct e1000_hw *hw)
> }
>
> /**
> + * igc_read_mac_addr_base - Read device MAC address
> + * @hw: pointer to the HW structure
> + **/
> +static s32 igc_read_mac_addr_base(struct e1000_hw *hw)
> +{
> + s32 ret_val = 0;
> +
> + ret_val = igc_read_mac_addr(hw);
> +
> + return ret_val;
> +}
> +
> +/**
> * igc_rx_fifo_flush_base - Clean rx fifo after Rx enable
> * @hw: pointer to the HW structure
> *
> @@ -169,12 +276,17 @@ void igc_rx_fifo_flush_base(struct e1000_hw *hw)
> }
>
> static struct e1000_mac_operations e1000_mac_ops_base = {
> - .init_hw = igc_init_hw_base,
> + .init_hw = igc_init_hw_base,
> + .check_for_link = igc_check_for_link_base,
> + .rar_set = igc_rar_set,
> + .read_mac_addr = igc_read_mac_addr_base,
Extra space after the '='
> + .get_speed_and_duplex = igc_get_link_up_info_base,
> };
>
> const struct e1000_info e1000_base_info = {
> - .get_invariants = igc_get_invariants_base,
> - .mac_ops = &e1000_mac_ops_base,
> + .get_invariants = igc_get_invariants_base,
> + .mac_ops = &e1000_mac_ops_base,
> + /* TODO phy_ops */
> };
>
> /**
> diff --git a/drivers/net/ethernet/intel/igc/e1000_defines.h b/drivers/net/ethernet/intel/igc/e1000_defines.h
> index 5613806742b1..31bc85cfa149 100644
> --- a/drivers/net/ethernet/intel/igc/e1000_defines.h
> +++ b/drivers/net/ethernet/intel/igc/e1000_defines.h
> @@ -55,6 +55,8 @@
> */
> #define E1000_RAH_AV 0x80000000 /* Receive descriptor valid */
> #define E1000_RAH_POOL_1 0x00040000
> +#define E1000_RAL_MAC_ADDR_LEN 4
> +#define E1000_RAH_MAC_ADDR_LEN 2
>
> /* Error Codes */
> #define E1000_SUCCESS 0
> @@ -77,13 +79,84 @@
> #define E1000_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */
> #define E1000_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */
>
> +/* SWFW_SYNC Definitions */
> +#define E1000_SWFW_EEP_SM 0x1
> +#define E1000_SWFW_PHY0_SM 0x2
> +#define E1000_SWFW_PHY1_SM 0x4
> +#define E1000_SWFW_PHY2_SM 0x20
> +#define E1000_SWFW_PHY3_SM 0x40
> +
> /* NVM Control */
> +#define E1000_EECD_SK 0x00000001 /* NVM Clock */
> +#define E1000_EECD_CS 0x00000002 /* NVM Chip Select */
> +#define E1000_EECD_DI 0x00000004 /* NVM Data In */
> +#define E1000_EECD_DO 0x00000008 /* NVM Data Out */
> +#define E1000_EECD_REQ 0x00000040 /* NVM Access Request */
> +#define E1000_EECD_GNT 0x00000080 /* NVM Access Grant */
> #define E1000_EECD_PRES 0x00000100 /* NVM Present */
> +/* NVM Addressing bits based on type 0=small, 1=large */
> +#define E1000_EECD_ADDR_BITS 0x00000400
> +#define E1000_NVM_GRANT_ATTEMPTS 1000 /* NVM # attempts to gain grant */
> +#define E1000_EECD_AUTO_RD 0x00000200 /* NVM Auto Read done */
> +#define E1000_EECD_SIZE_EX_MASK 0x00007800 /* NVM Size */
> +#define E1000_EECD_SIZE_EX_SHIFT 11
> +#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 */
>
> /* Number of milliseconds for NVM auto read done after MAC reset. */
> #define AUTO_READ_DONE_TIMEOUT 10
> #define E1000_EECD_AUTO_RD 0x00000200 /* NVM Auto Read done */
>
> +/* Offset to data in NVM read/write registers */
> +#define E1000_NVM_RW_REG_DATA 16
> +#define E1000_NVM_RW_REG_DONE 2 /* Offset to READ/WRITE done bit */
> +#define E1000_NVM_RW_REG_START 1 /* Start operation */
> +#define E1000_NVM_RW_ADDR_SHIFT 2 /* Shift to the address bits */
> +#define E1000_NVM_POLL_READ 0 /* Flag for polling for read complete */
> +
> +/* NVM Word Offsets */
> +#define NVM_COMPAT 0x0003
> +#define NVM_ID_LED_SETTINGS 0x0004 /* SERDES output amplitude */
> +#define NVM_VERSION 0x0005
> +#define NVM_INIT_CONTROL2_REG 0x000F
> +#define NVM_INIT_CONTROL3_PORT_B 0x0014
> +#define NVM_INIT_CONTROL3_PORT_A 0x0024
> +#define NVM_ALT_MAC_ADDR_PTR 0x0037
> +#define NVM_CHECKSUM_REG 0x003F
> +#define NVM_COMPATIBILITY_REG_3 0x0003
> +#define NVM_COMPATIBILITY_BIT_MASK 0x8000
> +#define NVM_MAC_ADDR 0x0000
> +#define NVM_SUB_DEV_ID 0x000B
> +#define NVM_SUB_VEN_ID 0x000C
> +#define NVM_DEV_ID 0x000D
> +#define NVM_VEN_ID 0x000E
> +#define NVM_INIT_CTRL_2 0x000F
> +#define NVM_INIT_CTRL_4 0x0013
> +#define NVM_LED_1_CFG 0x001C
> +#define NVM_LED_0_2_CFG 0x001F
> +#define NVM_ETRACK_WORD 0x0042
> +#define NVM_ETRACK_HIWORD 0x0043
> +#define NVM_COMB_VER_OFF 0x0083
> +#define NVM_COMB_VER_PTR 0x003d
> +
> +/* For checksumming, the sum of all words in the NVM should equal 0xBABA. */
> +#define NVM_SUM 0xBABA
> +
> +#define NVM_PBA_OFFSET_0 8
> +#define NVM_PBA_OFFSET_1 9
> +#define NVM_RESERVED_WORD 0xFFFF
> +#define NVM_PBA_PTR_GUARD 0xFAFA
> +#define NVM_WORD_SIZE_BASE_SHIFT 6
> +
> +/* Collision related configuration parameters */
> +#define E1000_COLLISION_THRESHOLD 15
> +#define E1000_CT_SHIFT 4
> +#define E1000_COLLISION_DISTANCE 63
> +#define E1000_COLD_SHIFT 12
> +
> /* Device Status */
> #define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */
> #define E1000_STATUS_LU 0x00000002 /* Link up.0=no,1=link */
> @@ -93,6 +166,14 @@
> #define E1000_STATUS_TXOFF 0x00000010 /* transmission paused */
> #define E1000_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */
> #define E1000_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */
> +#define E1000_STATUS_SPEED_2500 0x00400000 /* Speed 2.5Gb/s */
> +
> +#define SPEED_10 10
> +#define SPEED_100 100
> +#define SPEED_1000 1000
> +#define SPEED_2500 2500
> +#define HALF_DUPLEX 1
> +#define FULL_DUPLEX 2
>
> /* Interrupt Cause Read */
> #define E1000_ICR_TXDW 0x00000001 /* Transmit desc written back */
> diff --git a/drivers/net/ethernet/intel/igc/e1000_hw.h b/drivers/net/ethernet/intel/igc/e1000_hw.h
> index debeadd61b45..ab630e5b3d97 100644
> --- a/drivers/net/ethernet/intel/igc/e1000_hw.h
> +++ b/drivers/net/ethernet/intel/igc/e1000_hw.h
> @@ -11,6 +11,7 @@
> #include "e1000_regs.h"
> #include "e1000_defines.h"
> #include "e1000_mac.h"
> +#include "e1000_nvm.h"
> #include "e1000_i225.h"
> #include "e1000_base.h"
>
> @@ -81,6 +82,8 @@ struct e1000_info {
> struct e1000_nvm_operations *nvm_ops;
> };
>
> +extern const struct e1000_info e1000_base_info;
> +
> struct e1000_mac_info {
> struct e1000_mac_operations ops;
>
> @@ -118,7 +121,7 @@ struct e1000_mac_info {
>
> struct e1000_nvm_operations {
> s32 (*acquire)(struct e1000_hw *hw);
> - s32 (*read)(struct e1000_hw *hw, u16 offset, u16 i, u16 *data);
> + s32 (*read)(struct e1000_hw *hw, u16 offset, u16 words, u16 *data);
> void (*release)(struct e1000_hw *hw);
> s32 (*write)(struct e1000_hw *hw, u16 offset, u16 i, u16 *data);
> s32 (*update)(struct e1000_hw *hw);
> diff --git a/drivers/net/ethernet/intel/igc/e1000_i225.c b/drivers/net/ethernet/intel/igc/e1000_i225.c
> index 11d797c77619..d1fa45f600dc 100644
> --- a/drivers/net/ethernet/intel/igc/e1000_i225.c
> +++ b/drivers/net/ethernet/intel/igc/e1000_i225.c
> @@ -6,6 +6,32 @@
> #include "e1000_hw.h"
>
> /**
> + * igc_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 igc_acquire_nvm_i225(struct e1000_hw *hw)
> +{
> + return igc_acquire_swfw_sync_i225(hw, E1000_SWFW_EEP_SM);
> +}
> +
> +/**
> + * igc_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 igc_release_nvm_i225(struct e1000_hw *hw)
> +{
> + igc_release_swfw_sync_i225(hw, E1000_SWFW_EEP_SM);
> +}
> +
> +/**
> * igc_get_hw_semaphore_i225 - Acquire hardware semaphore
> * @hw: pointer to the HW structure
> *
> @@ -139,3 +165,328 @@ void igc_release_swfw_sync_i225(struct e1000_hw *hw, u16 mask)
>
> igc_put_hw_semaphore(hw);
> }
> +
> +/**
> + * igc_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.
> + **/
> +static s32 igc_read_nvm_srrd_i225(struct e1000_hw *hw, u16 offset, u16 words,
> + u16 *data)
> +{
> + s32 status = 0;
> + u16 i, count;
> +
> + /* 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))) {
> + status = igc_read_nvm_eerd(hw, offset, count,
> + data + i);
> + hw->nvm.ops.release(hw);
> + } else {
> + status = E1000_ERR_SWFW_SYNC;
Is this missing the '-' on purpose?
> + }
> +
> + if (status)
> + break;
This code seem rather awkward and harder to read than necessary ...
maybe something more like this?
status = hw->nvm.ops.acquire(hw);
if (status)
break;
status = igc_read_nvm_eerd(hw, offset, count, data + i);
hw->nvm.ops.release(hw);
if (status)
break;
> + }
> +
> + return status;
> +}
> +
> +/**
> + * igc_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 igc_update_nvm_checksum is not called after this function , the
> + * Shadow Ram will most likely contain an invalid checksum.
> + **/
> +static s32 igc_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 = 0;
> +
> + /* 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) {
> + hw_dbg("nvm parameter(s) out of bounds\n");
> + ret_val = -E1000_ERR_NVM;
> + goto out;
> + }
> +
> + for (i = 0; i < words; i++) {
> + eewr = ((offset + i) << E1000_NVM_RW_ADDR_SHIFT) |
> + (data[i] << E1000_NVM_RW_REG_DATA) |
> + E1000_NVM_RW_REG_START;
> +
> + wr32(E1000_SRWR, eewr);
> +
> + for (k = 0; k < attempts; k++) {
> + if (E1000_NVM_RW_REG_DONE &
> + rd32(E1000_SRWR)) {
> + ret_val = 0;
> + break;
> + }
> + udelay(5);
> + }
> +
> + if (ret_val) {
> + hw_dbg("Shadow RAM write EEWR timed out\n");
> + break;
> + }
> + }
> +
> +out:
> + return ret_val;
> +}
> +
> +/**
> + * igc_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.
> + **/
> +static s32 igc_write_nvm_srwr_i225(struct e1000_hw *hw, u16 offset, u16 words,
> + u16 *data)
> +{
> + s32 status = 0;
> + u16 i, count;
> +
> + /* 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))) {
> + status = igc_write_nvm_srwr(hw, offset, count,
> + data + i);
> + hw->nvm.ops.release(hw);
> + } else {
> + status = E1000_ERR_SWFW_SYNC;
Is this missing the '-' on purpose?
> + }
> +
> + if (status)
> + break;
Same as earlier comment - this code can be made clearer
> + }
> +
> + return status;
> +}
> +
> +/**
> + * igc_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.
> + **/
> +static s32 igc_validate_nvm_checksum_i225(struct e1000_hw *hw)
> +{
> + s32 status = 0;
> + s32 (*read_op_ptr)(struct e1000_hw *hw, u16 offset, u16 count,
> + u16 *data);
> +
> + if (!(hw->nvm.ops.acquire(hw))) {
Again, this can be simpler
status = hw->nvm.ops.acquire(hw);
if (status)
return status;
and now the next chunk doesn't need to be indented
> + /* 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 = igc_read_nvm_eerd;
> +
> + status = igc_validate_nvm_checksum(hw);
> +
> + /* Revert original read operation. */
> + hw->nvm.ops.read = read_op_ptr;
> +
> + hw->nvm.ops.release(hw);
> + } else {
> + status = E1000_ERR_SWFW_SYNC;
Should this have a minus sign? Or, this line is not needed if the code
is simplified as suggested above.
> + }
> +
> + return status;
> +}
> +
> +/**
> + * igc_pool_flash_update_done_i225 - Pool FLUDONE status
> + * @hw: pointer to the HW structure
> + **/
> +static s32 igc_pool_flash_update_done_i225(struct e1000_hw *hw)
> +{
> + s32 ret_val = -E1000_ERR_NVM;
> + u32 i, reg;
> +
> + for (i = 0; i < E1000_FLUDONE_ATTEMPTS; i++) {
> + reg = rd32(E1000_EECD);
> + if (reg & E1000_EECD_FLUDONE_I225) {
> + ret_val = 0;
> + break;
> + }
> + udelay(5);
> + }
> +
> + return ret_val;
> +}
> +
> +/**
> + * igc_update_flash_i225 - Commit EEPROM to the flash
> + * @hw: pointer to the HW structure
> + **/
> +static s32 igc_update_flash_i225(struct e1000_hw *hw)
> +{
> + s32 ret_val = 0;
> + u32 flup;
> +
> + ret_val = igc_pool_flash_update_done_i225(hw);
> + if (ret_val == -E1000_ERR_NVM) {
> + hw_dbg("Flash update time out\n");
> + goto out;
> + }
> +
> + flup = rd32(E1000_EECD) | E1000_EECD_FLUPD_I225;
> + wr32(E1000_EECD, flup);
> +
> + ret_val = igc_pool_flash_update_done_i225(hw);
> + if (ret_val)
> + hw_dbg("Flash update time out\n");
> + else
> + hw_dbg("Flash update complete\n");
> +
> +out:
> + return ret_val;
> +}
> +
> +/**
> + * igc_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.
> + **/
> +static s32 igc_update_nvm_checksum_i225(struct e1000_hw *hw)
> +{
> + s32 ret_val = 0;
> + u16 checksum = 0;
> + u16 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 = igc_read_nvm_eerd(hw, 0, 1, &nvm_data);
> + if (ret_val) {
> + hw_dbg("EEPROM read failed\n");
> + goto out;
May as well do a return here rather than goto a return
> + }
> +
> + if (!(hw->nvm.ops.acquire(hw))) {
See earlier comments
> + /* 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 = igc_read_nvm_eerd(hw, i, 1, &nvm_data);
> + if (ret_val) {
> + hw->nvm.ops.release(hw);
> + hw_dbg("NVM Read Error while updating checksum.\n");
> + goto out;
> + }
> + checksum += nvm_data;
> + }
> + checksum = (u16)NVM_SUM - checksum;
> + ret_val = igc_write_nvm_srwr(hw, NVM_CHECKSUM_REG, 1,
> + &checksum);
> + if (ret_val) {
> + hw->nvm.ops.release(hw);
> + hw_dbg("NVM Write Error while updating checksum.\n");
> + goto out;
> + }
> +
> + hw->nvm.ops.release(hw);
> +
> + ret_val = igc_update_flash_i225(hw);
> + } else {
> + ret_val = -E1000_ERR_SWFW_SYNC;
> + }
> +
> +out:
> + return ret_val;
> +}
> +
> +/**
> + * igc_get_flash_presence_i225 - Check if flash device is detected
> + * @hw: pointer to the HW structure
> + **/
> +bool igc_get_flash_presence_i225(struct e1000_hw *hw)
> +{
> + u32 eec = 0;
> + bool ret_val = false;
> +
> + eec = rd32(E1000_EECD);
> + if (eec & E1000_EECD_FLASH_DETECTED_I225)
> + ret_val = true;
> +
> + return ret_val;
> +}
> +
> +/**
> + * igc_init_nvm_params_i225 - Init NVM func ptrs.
> + * @hw: pointer to the HW structure
> + **/
> +s32 igc_init_nvm_params_i225(struct e1000_hw *hw)
> +{
> + s32 ret_val = 0;
> + struct e1000_nvm_info *nvm = &hw->nvm;
> +
> + nvm->ops.acquire = igc_acquire_nvm_i225;
> + nvm->ops.release = igc_release_nvm_i225;
> +
> + /* NVM Function Pointers */
> + if (igc_get_flash_presence_i225(hw)) {
> + hw->nvm.type = e1000_nvm_flash_hw;
> + nvm->ops.read = igc_read_nvm_srrd_i225;
> + nvm->ops.write = igc_write_nvm_srwr_i225;
> + nvm->ops.validate = igc_validate_nvm_checksum_i225;
> + nvm->ops.update = igc_update_nvm_checksum_i225;
> + } else {
> + hw->nvm.type = e1000_nvm_invm;
> + nvm->ops.read = igc_read_nvm_eerd;
> + nvm->ops.write = NULL;
> + nvm->ops.validate = NULL;
> + nvm->ops.update = NULL;
> + }
> + return ret_val;
> +}
> diff --git a/drivers/net/ethernet/intel/igc/e1000_i225.h b/drivers/net/ethernet/intel/igc/e1000_i225.h
> index 749a50c34534..c8c0398d9ff0 100644
> --- a/drivers/net/ethernet/intel/igc/e1000_i225.h
> +++ b/drivers/net/ethernet/intel/igc/e1000_i225.h
> @@ -7,4 +7,7 @@
> s32 igc_acquire_swfw_sync_i225(struct e1000_hw *hw, u16 mask);
> void igc_release_swfw_sync_i225(struct e1000_hw *hw, u16 mask);
>
> +s32 igc_init_nvm_params_i225(struct e1000_hw *hw);
> +bool igc_get_flash_presence_i225(struct e1000_hw *hw);
> +
> #endif
> diff --git a/drivers/net/ethernet/intel/igc/e1000_mac.c b/drivers/net/ethernet/intel/igc/e1000_mac.c
> index 27e478a9854c..5681c0e3f0c8 100644
> --- a/drivers/net/ethernet/intel/igc/e1000_mac.c
> +++ b/drivers/net/ethernet/intel/igc/e1000_mac.c
> @@ -326,6 +326,132 @@ void igc_clear_hw_cntrs_base(struct e1000_hw *hw)
> }
>
> /**
> + * igc_rar_set - Set receive address register
> + * @hw: pointer to the HW structure
> + * @addr: pointer to the receive address
> + * @index: receive address array register
> + *
> + * Sets the receive address array register at index to the address passed
> + * in by addr.
> + **/
> +void igc_rar_set(struct e1000_hw *hw, u8 *addr, u32 index)
> +{
> + u32 rar_low, rar_high;
> +
> + /* HW expects these in little endian so we reverse the byte order
> + * from network order (big endian) to little endian
> + */
> + rar_low = ((u32)addr[0] |
> + ((u32)addr[1] << 8) |
> + ((u32)addr[2] << 16) | ((u32)addr[3] << 24));
> +
> + rar_high = ((u32)addr[4] | ((u32)addr[5] << 8));
> +
> + /* If MAC address zero, no need to set the AV bit */
> + if (rar_low || rar_high)
> + rar_high |= E1000_RAH_AV;
if the mac address is zero, there's no need to do the swap
> +
> + /* Some bridges will combine consecutive 32-bit writes into
> + * a single burst write, which will malfunction on some parts.
Is this malfunction in the new devices? Is this comment still needed?
> + * The flushes avoid this.
> + */
> + wr32(E1000_RAL(index), rar_low);
> + wrfl();
> + wr32(E1000_RAH(index), rar_high);
> + wrfl();
> +}
> +
> +/**
> + * igc_check_for_copper_link - Check for link (Copper)
> + * @hw: pointer to the HW structure
> + *
> + * Checks to see of the link status of the hardware has changed. If a
> + * change in link status has been detected, then we read the PHY registers
> + * to get the current speed/duplex if link exists.
> + **/
> +s32 igc_check_for_copper_link(struct e1000_hw *hw)
> +{
> + struct e1000_mac_info *mac = &hw->mac;
> + s32 ret_val;
> + bool link;
> +
> + /* We only want to go out to the PHY registers to see if Auto-Neg
> + * has completed and/or if our link status has changed. The
> + * get_link_status flag is set upon receiving a Link Status
> + * Change or Rx Sequence Error interrupt.
> + */
> + if (!mac->get_link_status) {
> + ret_val = 0;
> + goto out;
> + }
> +
> + /* First we want to see if the MII Status Register reports
> + * link. If so, then we want to get the current speed/duplex
> + * of the PHY.
> + */
> + /* TODO ret_val = igc_phy_has_link(hw, 1, 0, &link); */
> + if (ret_val)
> + goto out;
> +
> + if (!link)
> + goto out; /* No link detected */
> +
> + mac->get_link_status = false;
> +
> + /* Check if there was DownShift, must be checked
> + * immediately after link-up
> + */
> + /* TODO igc_check_downshift(hw); */
> +
> + /* If we are forcing speed/duplex, then we simply return since
> + * we have already determined whether we have link or not.
> + */
> + if (!mac->autoneg) {
> + ret_val = -E1000_ERR_CONFIG;
> + goto out;
> + }
> +
> + /* Auto-Neg is enabled. Auto Speed Detection takes care
> + * of MAC speed/duplex configuration. So we only need to
> + * configure Collision Distance in the MAC.
> + */
> + igc_config_collision_dist(hw);
> +
> + /* Configure Flow Control now that Auto-Neg has completed.
> + * First, we need to restore the desired flow control
> + * settings because we may have had to re-autoneg with a
> + * different link partner.
> + */
> + /* TODO ret_val = igc_config_fc_after_link_up(hw); */
> + if (ret_val)
> + hw_dbg("Error configuring flow control\n");
> +
> +out:
> + return ret_val;
> +}
> +
> +/**
> + * igc_config_collision_dist - Configure collision distance
> + * @hw: pointer to the HW structure
> + *
> + * Configures the collision distance to the default value and is used
> + * during link setup. Currently no func pointer exists and all
> + * implementations are handled in the generic version of this function.
> + **/
> +void igc_config_collision_dist(struct e1000_hw *hw)
> +{
> + u32 tctl;
> +
> + tctl = rd32(E1000_TCTL);
> +
> + tctl &= ~E1000_TCTL_COLD;
> + tctl |= E1000_COLLISION_DISTANCE << E1000_COLD_SHIFT;
> +
> + wr32(E1000_TCTL, tctl);
> + wrfl();
> +}
> +
> +/**
> * igc_get_auto_rd_done - Check for auto read completion
> * @hw: pointer to the HW structure
> *
> @@ -354,6 +480,53 @@ s32 igc_get_auto_rd_done(struct e1000_hw *hw)
> }
>
> /**
> + * igc_get_speed_and_duplex_copper - Retrieve current speed/duplex
> + * @hw: pointer to the HW structure
> + * @speed: stores the current speed
> + * @duplex: stores the current duplex
> + *
> + * Read the status register for the current speed/duplex and store the current
> + * speed and duplex for copper connections.
> + **/
> +s32 igc_get_speed_and_duplex_copper(struct e1000_hw *hw, u16 *speed,
> + u16 *duplex)
> +{
> + u32 status;
> +
> + status = rd32(E1000_STATUS);
> + if (status & E1000_STATUS_SPEED_1000) {
> + /* For I225, STATUS will indicate 1G speed in both 1 Gbps
> + * and 2.5 Gbps link modes. An additional bit is used
> + * to differentiate between 1 Gbps and 2.5 Gbps.
> + */
> + if (hw->mac.type == e1000_i225 &&
> + (status & E1000_STATUS_SPEED_2500)) {
> + *speed = SPEED_2500;
> + hw_dbg("2500 Mbs, ");
> + } else {
> + *speed = SPEED_1000;
> + hw_dbg("1000 Mbs, ");
> + }
> + } else if (status & E1000_STATUS_SPEED_100) {
> + *speed = SPEED_100;
> + hw_dbg("100 Mbs, ");
> + } else {
> + *speed = SPEED_10;
> + hw_dbg("10 Mbs, ");
> + }
> +
> + if (status & E1000_STATUS_FD) {
> + *duplex = FULL_DUPLEX;
> + hw_dbg("Full Duplex\n");
> + } else {
> + *duplex = HALF_DUPLEX;
> + hw_dbg("Half Duplex\n");
> + }
> +
> + return 0;
> +}
> +
> +/**
> * igc_put_hw_semaphore - Release hardware semaphore
> * @hw: pointer to the HW structure
> *
> diff --git a/drivers/net/ethernet/intel/igc/e1000_mac.h b/drivers/net/ethernet/intel/igc/e1000_mac.h
> index 2f8dbf17800a..f18f5221199f 100644
> --- a/drivers/net/ethernet/intel/igc/e1000_mac.h
> +++ b/drivers/net/ethernet/intel/igc/e1000_mac.h
> @@ -12,13 +12,20 @@
> #endif /* E1000_REMOVED */
>
> /* forward declaration */
> -s32 igc_disable_pcie_master(struct e1000_hw *hw);
> +s32 igc_check_for_copper_link(struct e1000_hw *hw);
> +
> +s32 igc_disable_pcie_master(struct e1000_hw *hw);
> void igc_init_rx_addrs(struct e1000_hw *hw, u16 rar_count);
> -s32 igc_setup_link(struct e1000_hw *hw);
> +s32 igc_setup_link(struct e1000_hw *hw);
> void igc_clear_hw_cntrs_base(struct e1000_hw *hw);
> s32 igc_get_auto_rd_done(struct e1000_hw *hw);
> void igc_put_hw_semaphore(struct e1000_hw *hw);
> +void igc_rar_set(struct e1000_hw *hw, u8 *addr, u32 index);
> +
> +void igc_config_collision_dist(struct e1000_hw *hw);
>
> -s32 igc_get_bus_info_pcie(struct e1000_hw *hw);
> +s32 igc_get_bus_info_pcie(struct e1000_hw *hw);
> +s32 igc_get_speed_and_duplex_copper(struct e1000_hw *hw, u16 *speed,
> + u16 *duplex);
>
> #endif
> diff --git a/drivers/net/ethernet/intel/igc/e1000_nvm.c b/drivers/net/ethernet/intel/igc/e1000_nvm.c
> new file mode 100644
> index 000000000000..62a4ae7f224b
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/igc/e1000_nvm.c
> @@ -0,0 +1,219 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2018 Intel Corporation */
> +
> +#include "e1000_mac.h"
> +#include "e1000_nvm.h"
> +
> +/**
> + * igc_poll_eerd_eewr_done - Poll for EEPROM read/write completion
> + * @hw: pointer to the HW structure
> + * @ee_reg: EEPROM flag for polling
> + *
> + * Polls the EEPROM status bit for either read or write completion based
> + * upon the value of 'ee_reg'.
> + **/
> +static s32 igc_poll_eerd_eewr_done(struct e1000_hw *hw, int ee_reg)
> +{
> + u32 attempts = 100000;
> + u32 i, reg = 0;
> + s32 ret_val = -E1000_ERR_NVM;
> +
> + for (i = 0; i < attempts; i++) {
> + if (ee_reg == E1000_NVM_POLL_READ)
> + reg = rd32(E1000_EERD);
> + else
> + reg = rd32(E1000_EEWR);
> +
> + if (reg & E1000_NVM_RW_REG_DONE) {
> + ret_val = 0;
> + break;
> + }
> +
> + udelay(5);
> + }
> +
> + return ret_val;
> +}
> +
> +/**
> + * igc_acquire_nvm - Generic request for access to EEPROM
> + * @hw: pointer to the HW structure
> + *
> + * 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).
> + **/
> +s32 igc_acquire_nvm(struct e1000_hw *hw)
> +{
> + u32 eecd = rd32(E1000_EECD);
> + s32 timeout = E1000_NVM_GRANT_ATTEMPTS;
> + s32 ret_val = 0;
> +
> + wr32(E1000_EECD, eecd | E1000_EECD_REQ);
> + eecd = rd32(E1000_EECD);
> +
> + while (timeout) {
> + if (eecd & E1000_EECD_GNT)
> + break;
> + udelay(5);
> + eecd = rd32(E1000_EECD);
> + timeout--;
> + }
> +
> + if (!timeout) {
> + eecd &= ~E1000_EECD_REQ;
> + wr32(E1000_EECD, eecd);
> + hw_dbg("Could not acquire NVM grant\n");
> + ret_val = -E1000_ERR_NVM;
> + }
> +
> + return ret_val;
> +}
> +
> +/**
> + * igc_release_nvm - Release exclusive access to EEPROM
> + * @hw: pointer to the HW structure
> + *
> + * Stop any current commands to the EEPROM and clear the EEPROM request bit.
> + **/
> +void igc_release_nvm(struct e1000_hw *hw)
> +{
> + u32 eecd;
> +
> + eecd = rd32(E1000_EECD);
> + eecd &= ~E1000_EECD_REQ;
> + wr32(E1000_EECD, eecd);
> +}
> +
> +/**
> + * igc_read_nvm_eerd - Reads EEPROM using EERD register
> + * @hw: pointer to the HW structure
> + * @offset: offset of word in the EEPROM to read
> + * @words: number of words to read
> + * @data: word read from the EEPROM
> + *
> + * Reads a 16 bit word from the EEPROM using the EERD register.
> + **/
> +s32 igc_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data)
> +{
> + struct e1000_nvm_info *nvm = &hw->nvm;
> + u32 i, eerd = 0;
> + s32 ret_val = 0;
> +
> + /* 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) {
> + hw_dbg("nvm parameter(s) out of bounds\n");
> + ret_val = -E1000_ERR_NVM;
> + goto out;
> + }
> +
> + for (i = 0; i < words; i++) {
> + eerd = ((offset + i) << E1000_NVM_RW_ADDR_SHIFT) +
> + E1000_NVM_RW_REG_START;
> +
> + wr32(E1000_EERD, eerd);
> + ret_val = igc_poll_eerd_eewr_done(hw, E1000_NVM_POLL_READ);
> + if (ret_val)
> + break;
> +
> + data[i] = (rd32(E1000_EERD) >> E1000_NVM_RW_REG_DATA);
> + }
> +
> +out:
> + return ret_val;
> +}
> +
> +/**
> + * igc_read_mac_addr - Read device MAC address
> + * @hw: pointer to the HW structure
> + *
> + * Reads the device MAC address from the EEPROM and stores the value.
> + * Since devices with two ports use the same EEPROM, we increment the
> + * last bit in the MAC address for the second port.
> + **/
> +s32 igc_read_mac_addr(struct e1000_hw *hw)
> +{
> + u32 rar_high;
> + u32 rar_low;
> + u16 i;
> +
> + rar_high = rd32(E1000_RAH(0));
> + rar_low = rd32(E1000_RAL(0));
> +
> + for (i = 0; i < E1000_RAL_MAC_ADDR_LEN; i++)
> + hw->mac.perm_addr[i] = (u8)(rar_low >> (i * 8));
> +
> + for (i = 0; i < E1000_RAH_MAC_ADDR_LEN; i++)
> + hw->mac.perm_addr[i + 4] = (u8)(rar_high >> (i * 8));
> +
> + for (i = 0; i < ETH_ALEN; i++)
> + hw->mac.addr[i] = hw->mac.perm_addr[i];
Is there supposed to be an increment here for the second port as
mentioned in the function comments?
> +
> + return 0;
Can this be a void function?
> +}
> +
> +/**
> + * igc_validate_nvm_checksum - 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 igc_validate_nvm_checksum(struct e1000_hw *hw)
> +{
> + s32 ret_val = 0;
> + u16 checksum = 0;
> + u16 i, nvm_data;
> +
> + for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) {
> + ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data);
> + if (ret_val) {
> + hw_dbg("NVM Read Error\n");
> + goto out;
> + }
> + checksum += nvm_data;
> + }
This looks slow, reading one word at a time. Can you read a buffer at a
time to speed things up a bit?
> +
> + if (checksum != (u16)NVM_SUM) {
> + hw_dbg("NVM Checksum Invalid\n");
> + ret_val = -E1000_ERR_NVM;
> + goto out;
> + }
> +
> +out:
> + return ret_val;
> +}
> +
> +/**
> + * igc_update_nvm_checksum - 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.
> + **/
> +s32 igc_update_nvm_checksum(struct e1000_hw *hw)
> +{
> + s32 ret_val;
> + u16 checksum = 0;
> + u16 i, nvm_data;
> +
> + for (i = 0; i < NVM_CHECKSUM_REG; i++) {
> + ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data);
> + if (ret_val) {
> + hw_dbg("NVM Read Error while updating checksum.\n");
> + goto out;
> + }
> + checksum += nvm_data;
Again, maybe do buffer reads rather than one word at a time?
> + }
> + checksum = (u16)NVM_SUM - checksum;
> + ret_val = hw->nvm.ops.write(hw, NVM_CHECKSUM_REG, 1, &checksum);
> + if (ret_val)
> + hw_dbg("NVM Write Error while updating checksum.\n");
> +
> +out:
> + return ret_val;
> +}
> diff --git a/drivers/net/ethernet/intel/igc/e1000_nvm.h b/drivers/net/ethernet/intel/igc/e1000_nvm.h
> new file mode 100644
> index 000000000000..b6237bd515e9
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/igc/e1000_nvm.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (c) 2018 Intel Corporation */
> +
> +#ifndef _E1000_NVM_H_
> +#define _E1000_NVM_H_
> +
> +s32 igc_acquire_nvm(struct e1000_hw *hw);
> +void igc_release_nvm(struct e1000_hw *hw);
> +s32 igc_read_mac_addr(struct e1000_hw *hw);
> +
> +s32 igc_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data);
> +
> +s32 igc_validate_nvm_checksum(struct e1000_hw *hw);
> +s32 igc_update_nvm_checksum(struct e1000_hw *hw);
> +
> +#endif
> diff --git a/drivers/net/ethernet/intel/igc/e1000_regs.h b/drivers/net/ethernet/intel/igc/e1000_regs.h
> index 66d5c757dae8..698ce0cac757 100644
> --- a/drivers/net/ethernet/intel/igc/e1000_regs.h
> +++ b/drivers/net/ethernet/intel/igc/e1000_regs.h
> @@ -259,6 +259,9 @@
> /* Management registers */
> #define E1000_MANC 0x05820 /* Management Control - RW */
>
> +/* Shadow Ram Write Register - RW */
> +#define E1000_SRWR 0x12018
> +
> /* forward declaration */
> struct e1000_hw;
> u32 igc_rd32(struct e1000_hw *hw, u32 reg);
> diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h
> index c61212ccb60e..735a5e3d0717 100644
> --- a/drivers/net/ethernet/intel/igc/igc.h
> +++ b/drivers/net/ethernet/intel/igc/igc.h
> @@ -133,6 +133,10 @@ enum igc_tx_flags {
> IGC_TX_FLAGS_CSUM = 0x20,
> };
>
> +enum igc_boards {
> + board_base,
> +};
> +
> /** The largest size we can write to the descriptor is 65535. In order to
> * maintain a power of two alignment we have to limit ourselves to 32K.
> **/
> @@ -348,6 +352,8 @@ struct igc_adapter {
> spinlock_t nfc_lock;
>
> struct igc_mac_addr *mac_table;
> +
> + struct e1000_info ei;
> };
>
> /* igc_desc_unused - calculate if we have unused descriptors */
> diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c
> index 9a99c7d68796..dcc7e700074f 100644
> --- a/drivers/net/ethernet/intel/igc/igc_main.c
> +++ b/drivers/net/ethernet/intel/igc/igc_main.c
> @@ -19,9 +19,13 @@ static const char igc_driver_string[] = DRV_SUMMARY;
> static const char igc_copyright[] =
> "Copyright(c) 2018 Intel Corporation.";
>
> +static const struct e1000_info *igc_info_tbl[] = {
> + [board_base] = &e1000_base_info,
> +};
> +
> static const struct pci_device_id igc_pci_tbl[] = {
> - { PCI_VDEVICE(INTEL, E1000_DEV_ID_I225_LM) },
> - { PCI_VDEVICE(INTEL, E1000_DEV_ID_I225_V) },
> + { PCI_VDEVICE(INTEL, E1000_DEV_ID_I225_LM), board_base },
> + { PCI_VDEVICE(INTEL, E1000_DEV_ID_I225_V), board_base },
> /* required last entry */
> {0, }
> };
> @@ -3334,6 +3338,7 @@ static int igc_probe(struct pci_dev *pdev,
> struct net_device *netdev;
> struct igc_adapter *adapter;
> struct e1000_hw *hw;
> + const struct e1000_info *ei = igc_info_tbl[ent->driver_data];
> int err, pci_using_dac;
>
> err = pci_enable_device_mem(pdev);
> @@ -3417,6 +3422,14 @@ static int igc_probe(struct pci_dev *pdev,
> hw->subsystem_vendor_id = pdev->subsystem_vendor;
> hw->subsystem_device_id = pdev->subsystem_device;
>
> + /* Copy the default MAC and PHY function pointers */
> + memcpy(&hw->mac.ops, ei->mac_ops, sizeof(hw->mac.ops));
> +
> + /* Initialize skew-specific constants */
> + err = ei->get_invariants(hw);
> + if (err)
> + goto err_sw_init;
> +
> /* setup the private structure */
> err = igc_sw_init(adapter);
> if (err)
> @@ -3452,6 +3465,9 @@ static int igc_probe(struct pci_dev *pdev,
> /* carrier off reporting is important to ethtool even BEFORE open */
> netif_carrier_off(netdev);
>
> + /* Check if Media Autosense is enabled */
> + adapter->ei = *ei;
> +
> dev_info(&pdev->dev, "@SUMMARY@");
> /* print bus type/speed/width info */
> dev_info(&pdev->dev, "%s: (PCIe:%s:%s) ",
>
^ permalink raw reply [flat|nested] 4+ messages in thread
* [Intel-wired-lan] [PATCH v3 08/11] igc: Add NVM support
2018-06-28 23:35 ` Shannon Nelson
@ 2018-07-08 12:52 ` Neftin, Sasha
0 siblings, 0 replies; 4+ messages in thread
From: Neftin, Sasha @ 2018-07-08 12:52 UTC (permalink / raw)
To: intel-wired-lan
On 6/29/2018 02:35, Shannon Nelson wrote:
> On 6/24/2018 1:45 AM, Sasha Neftin wrote:
>> Add code for NVM support and get MAC address, complete probe
>> method.
>>
>> Sasha Neftin (v2):
>> minor cosmetic changes
>>
>> Alexander Duyck (v3):
>> NVM access code optimization
>>
>> Signed-off-by: Sasha Neftin <sasha.neftin@intel.com>
>> Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
>> ---
>> ? drivers/net/ethernet/intel/igc/Makefile??????? |?? 2 +-
>> ? drivers/net/ethernet/intel/igc/e1000_base.c??? | 118 ++++++++-
>> ? drivers/net/ethernet/intel/igc/e1000_defines.h |? 81 ++++++
>> ? drivers/net/ethernet/intel/igc/e1000_hw.h????? |?? 5 +-
>> ? drivers/net/ethernet/intel/igc/e1000_i225.c??? | 351
>> +++++++++++++++++++++++++
>> ? drivers/net/ethernet/intel/igc/e1000_i225.h??? |?? 3 +
>> ? drivers/net/ethernet/intel/igc/e1000_mac.c???? | 173 ++++++++++++
>> ? drivers/net/ethernet/intel/igc/e1000_mac.h???? |? 13 +-
>> ? drivers/net/ethernet/intel/igc/e1000_nvm.c???? | 219 +++++++++++++++
>> ? drivers/net/ethernet/intel/igc/e1000_nvm.h???? |? 16 ++
>> ? drivers/net/ethernet/intel/igc/e1000_regs.h??? |?? 3 +
>> ? drivers/net/ethernet/intel/igc/igc.h?????????? |?? 6 +
>> ? drivers/net/ethernet/intel/igc/igc_main.c????? |? 20 +-
>> ? 13 files changed, 1000 insertions(+), 10 deletions(-)
>> ? create mode 100644 drivers/net/ethernet/intel/igc/e1000_nvm.c
>> ? create mode 100644 drivers/net/ethernet/intel/igc/e1000_nvm.h
>>
>> diff --git a/drivers/net/ethernet/intel/igc/Makefile
>> b/drivers/net/ethernet/intel/igc/Makefile
>> index cb260bedfa37..5147bca1a900 100644
>> --- a/drivers/net/ethernet/intel/igc/Makefile
>> +++ b/drivers/net/ethernet/intel/igc/Makefile
>> @@ -7,4 +7,4 @@
>> ? obj-$(CONFIG_IGC) += igc.o
>> -igc-objs := igc_main.o e1000_mac.o e1000_i225.o e1000_base.o
>> +igc-objs := igc_main.o e1000_mac.o e1000_i225.o e1000_base.o e1000_nvm.o
>> diff --git a/drivers/net/ethernet/intel/igc/e1000_base.c
>> b/drivers/net/ethernet/intel/igc/e1000_base.c
>> index 395d7ce70bb4..6db31349daa4 100644
>> --- a/drivers/net/ethernet/intel/igc/e1000_base.c
>> +++ b/drivers/net/ethernet/intel/igc/e1000_base.c
>> @@ -11,9 +11,49 @@
>> ? /* forward declaration */
>> ? static s32 igc_get_invariants_base(struct e1000_hw *);
>> +static s32 igc_check_for_link_base(struct e1000_hw *);
>> ? static s32 igc_init_hw_base(struct e1000_hw *);
>> ? static s32 igc_reset_hw_base(struct e1000_hw *);
>> ? static s32 igc_set_pcie_completion_timeout(struct e1000_hw *hw);
>> +static s32 igc_read_mac_addr_base(struct e1000_hw *hw);
>> +
>> +/**
>> + *? igc_init_nvm_params_base - Init NVM func ptrs.
>> + *? @hw: pointer to the HW structure
>> + **/
>> +static s32 igc_init_nvm_params_base(struct e1000_hw *hw)
>> +{
>> +??? struct e1000_nvm_info *nvm = &hw->nvm;
>> +??? u32 eecd = rd32(E1000_EECD);
>> +??? u16 size;
>> +
>> +??? 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 = BIT(size);
>> +??? nvm->opcode_bits = 8;
>> +??? nvm->delay_usec = 1;
>> +
>> +??? nvm->page_size = eecd & E1000_EECD_ADDR_BITS ? 32 : 8;
>> +??? nvm->address_bits = eecd & E1000_EECD_ADDR_BITS ?
>> +??????????????? 16 : 8;
>> +
>> +??? if (nvm->word_size == BIT(15))
>> +??????? nvm->page_size = 128;
>> +
>> +??? return 0;
>> +}
>> ? /**
>> ?? *? igc_init_mac_params_base - Init MAC func ptrs.
>> @@ -22,6 +62,7 @@ static s32 igc_set_pcie_completion_timeout(struct
>> e1000_hw *hw);
>> ? static s32 igc_init_mac_params_base(struct e1000_hw *hw)
>> ? {
>> ????? struct e1000_mac_info *mac = &hw->mac;
>> +??? struct e1000_dev_spec_base *dev_spec = &hw->dev_spec._base;
>
> reverse xmas tree
>
good. will be applied in v4.
>> ????? /* Set mta register count */
>> ????? mac->mta_reg_count = 128;
>> @@ -33,6 +74,10 @@ static s32 igc_init_mac_params_base(struct e1000_hw
>> *hw)
>> ????? mac->ops.acquire_swfw_sync = igc_acquire_swfw_sync_i225;
>> ????? mac->ops.release_swfw_sync = igc_release_swfw_sync_i225;
>> +??? /* Allow a single clear of the SW semaphore on I225 */
>> +??? if (mac->type == e1000_i225)
>> +??????? dev_spec->clear_semaphore_once = true;
>> +
>> ????? return 0;
>> ? }
>> @@ -50,11 +95,60 @@ static s32 igc_get_invariants_base(struct e1000_hw
>> *hw)
>> ????? if (ret_val)
>> ????????? goto out;
>> +??? /* NVM initialization */
>> +??? ret_val = igc_init_nvm_params_base(hw);
>> +??? switch (hw->mac.type) {
>> +??? case e1000_i225:
>> +??????? ret_val = igc_init_nvm_params_i225(hw);
>> +??????? break;
>> +??? default:
>> +??????? break;
>> +??? }
>> +
>> +??? if (ret_val)
>> +??????? goto out;
>> +
>> ? out:
>> ????? return ret_val;
>> ? }
>> ? /**
>> + *? igc_get_link_up_info_base - Get link speed/duplex info
>> + *? @hw: pointer to the HW structure
>> + *? @speed: stores the current speed
>> + *? @duplex: stores the current duplex
>> + *
>> + *? This is a wrapper function, if using the serial gigabit media
>> independent
>> + *? interface, use PCS to retrieve the link speed and duplex
>> information.
>> + *? Otherwise, use the generic function to get the link speed and
>> duplex info.
>> + **/
>> +static s32 igc_get_link_up_info_base(struct e1000_hw *hw, u16 *speed,
>> +???????????????????? u16 *duplex)
>> +{
>> +??? s32 ret_val;
>> +
>> +??? ret_val = igc_get_speed_and_duplex_copper(hw, speed, duplex);
>> +
>> +??? return ret_val;
>> +}
>> +
>> +/**
>> + *? igc_check_for_link_base - Check for link
>> + *? @hw: pointer to the HW structure
>> + *
>> + *? If sgmii is enabled, then use the pcs register to determine link,
>> otherwise
>> + *? use the generic interface for determining link.
>> + **/
>> +static s32 igc_check_for_link_base(struct e1000_hw *hw)
>> +{
>> +??? s32 ret_val = 0;
>> +
>> +??? ret_val = igc_check_for_copper_link(hw);
>> +
>> +??? return ret_val;
>> +}
>> +
>> +/**
>> ?? *? igc_init_hw_base - Initialize hardware
>> ?? *? @hw: pointer to the HW structure
>> ?? *
>> @@ -93,6 +187,19 @@ static s32 igc_init_hw_base(struct e1000_hw *hw)
>> ? }
>> ? /**
>> + *? igc_read_mac_addr_base - Read device MAC address
>> + *? @hw: pointer to the HW structure
>> + **/
>> +static s32 igc_read_mac_addr_base(struct e1000_hw *hw)
>> +{
>> +??? s32 ret_val = 0;
>> +
>> +??? ret_val = igc_read_mac_addr(hw);
>> +
>> +??? return ret_val;
>> +}
>> +
>> +/**
>> ?? *? igc_rx_fifo_flush_base - Clean rx fifo after Rx enable
>> ?? *? @hw: pointer to the HW structure
>> ?? *
>> @@ -169,12 +276,17 @@ void igc_rx_fifo_flush_base(struct e1000_hw *hw)
>> ? }
>> ? static struct e1000_mac_operations e1000_mac_ops_base = {
>> -??? .init_hw??? = igc_init_hw_base,
>> +??? .init_hw??????? = igc_init_hw_base,
>> +??? .check_for_link??????? = igc_check_for_link_base,
>> +??? .rar_set??????? = igc_rar_set,
>> +??? .read_mac_addr??????? =? igc_read_mac_addr_base,
>
> Extra space after the '='
>
right. will be fixed in v4.
>> +??? .get_speed_and_duplex??? = igc_get_link_up_info_base,
>> ? };
>> ? const struct e1000_info e1000_base_info = {
>> -??? .get_invariants = igc_get_invariants_base,
>> -??? .mac_ops = &e1000_mac_ops_base,
>> +??? .get_invariants??? = igc_get_invariants_base,
>> +??? .mac_ops??? = &e1000_mac_ops_base,
>> +??? /* TODO phy_ops */
>> ? };
>> ? /**
>> diff --git a/drivers/net/ethernet/intel/igc/e1000_defines.h
>> b/drivers/net/ethernet/intel/igc/e1000_defines.h
>> index 5613806742b1..31bc85cfa149 100644
>> --- a/drivers/net/ethernet/intel/igc/e1000_defines.h
>> +++ b/drivers/net/ethernet/intel/igc/e1000_defines.h
>> @@ -55,6 +55,8 @@
>> ?? */
>> ? #define E1000_RAH_AV??????? 0x80000000 /* Receive descriptor valid */
>> ? #define E1000_RAH_POOL_1??? 0x00040000
>> +#define E1000_RAL_MAC_ADDR_LEN??? 4
>> +#define E1000_RAH_MAC_ADDR_LEN??? 2
>> ? /* Error Codes */
>> ? #define E1000_SUCCESS??????????????? 0
>> @@ -77,13 +79,84 @@
>> ? #define E1000_SWSM_SMBI??????? 0x00000001 /* Driver Semaphore bit */
>> ? #define E1000_SWSM_SWESMBI??? 0x00000002 /* FW Semaphore bit */
>> +/* SWFW_SYNC Definitions */
>> +#define E1000_SWFW_EEP_SM??? 0x1
>> +#define E1000_SWFW_PHY0_SM??? 0x2
>> +#define E1000_SWFW_PHY1_SM??? 0x4
>> +#define E1000_SWFW_PHY2_SM??? 0x20
>> +#define E1000_SWFW_PHY3_SM??? 0x40
>> +
>> ? /* NVM Control */
>> +#define E1000_EECD_SK??????? 0x00000001 /* NVM Clock */
>> +#define E1000_EECD_CS??????? 0x00000002 /* NVM Chip Select */
>> +#define E1000_EECD_DI??????? 0x00000004 /* NVM Data In */
>> +#define E1000_EECD_DO??????? 0x00000008 /* NVM Data Out */
>> +#define E1000_EECD_REQ??????? 0x00000040 /* NVM Access Request */
>> +#define E1000_EECD_GNT??????? 0x00000080 /* NVM Access Grant */
>> ? #define E1000_EECD_PRES??????? 0x00000100 /* NVM Present */
>> +/* NVM Addressing bits based on type 0=small, 1=large */
>> +#define E1000_EECD_ADDR_BITS??????? 0x00000400
>> +#define E1000_NVM_GRANT_ATTEMPTS??? 1000 /* NVM # attempts to gain
>> grant */
>> +#define E1000_EECD_AUTO_RD??????? 0x00000200? /* NVM Auto Read done */
>> +#define E1000_EECD_SIZE_EX_MASK??????? 0x00007800? /* NVM Size */
>> +#define E1000_EECD_SIZE_EX_SHIFT??? 11
>> +#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 */
>> ? /* Number of milliseconds for NVM auto read done after MAC reset. */
>> ? #define AUTO_READ_DONE_TIMEOUT??????? 10
>> ? #define E1000_EECD_AUTO_RD??? 0x00000200? /* NVM Auto Read done */
>> +/* Offset to data in NVM read/write registers */
>> +#define E1000_NVM_RW_REG_DATA??? 16
>> +#define E1000_NVM_RW_REG_DONE??? 2??? /* Offset to READ/WRITE done
>> bit */
>> +#define E1000_NVM_RW_REG_START??? 1??? /* Start operation */
>> +#define E1000_NVM_RW_ADDR_SHIFT??? 2??? /* Shift to the address bits */
>> +#define E1000_NVM_POLL_READ??? 0??? /* Flag for polling for read
>> complete */
>> +
>> +/* NVM Word Offsets */
>> +#define NVM_COMPAT??????????? 0x0003
>> +#define NVM_ID_LED_SETTINGS??????? 0x0004 /* SERDES output amplitude */
>> +#define NVM_VERSION??????????? 0x0005
>> +#define NVM_INIT_CONTROL2_REG??????? 0x000F
>> +#define NVM_INIT_CONTROL3_PORT_B??? 0x0014
>> +#define NVM_INIT_CONTROL3_PORT_A??? 0x0024
>> +#define NVM_ALT_MAC_ADDR_PTR??????? 0x0037
>> +#define NVM_CHECKSUM_REG??????? 0x003F
>> +#define NVM_COMPATIBILITY_REG_3??????? 0x0003
>> +#define NVM_COMPATIBILITY_BIT_MASK??? 0x8000
>> +#define NVM_MAC_ADDR??????????? 0x0000
>> +#define NVM_SUB_DEV_ID??????????? 0x000B
>> +#define NVM_SUB_VEN_ID??????????? 0x000C
>> +#define NVM_DEV_ID??????????? 0x000D
>> +#define NVM_VEN_ID??????????? 0x000E
>> +#define NVM_INIT_CTRL_2??????????? 0x000F
>> +#define NVM_INIT_CTRL_4??????????? 0x0013
>> +#define NVM_LED_1_CFG??????????? 0x001C
>> +#define NVM_LED_0_2_CFG??????????? 0x001F
>> +#define NVM_ETRACK_WORD??????????? 0x0042
>> +#define NVM_ETRACK_HIWORD??????? 0x0043
>> +#define NVM_COMB_VER_OFF??????? 0x0083
>> +#define NVM_COMB_VER_PTR??????? 0x003d
>> +
>> +/* For checksumming, the sum of all words in the NVM should equal
>> 0xBABA. */
>> +#define NVM_SUM??????????????? 0xBABA
>> +
>> +#define NVM_PBA_OFFSET_0??????? 8
>> +#define NVM_PBA_OFFSET_1??????? 9
>> +#define NVM_RESERVED_WORD??????? 0xFFFF
>> +#define NVM_PBA_PTR_GUARD??????? 0xFAFA
>> +#define NVM_WORD_SIZE_BASE_SHIFT??? 6
>> +
>> +/* Collision related configuration parameters */
>> +#define E1000_COLLISION_THRESHOLD??? 15
>> +#define E1000_CT_SHIFT??????????? 4
>> +#define E1000_COLLISION_DISTANCE??? 63
>> +#define E1000_COLD_SHIFT??????? 12
>> +
>> ? /* Device Status */
>> ? #define E1000_STATUS_FD??????? 0x00000001????? /* Full
>> duplex.0=half,1=full */
>> ? #define E1000_STATUS_LU??????? 0x00000002????? /* Link
>> up.0=no,1=link */
>> @@ -93,6 +166,14 @@
>> ? #define E1000_STATUS_TXOFF??? 0x00000010????? /* transmission paused */
>> ? #define E1000_STATUS_SPEED_100??? 0x00000040????? /* Speed 100Mb/s */
>> ? #define E1000_STATUS_SPEED_1000??? 0x00000080????? /* Speed 1000Mb/s */
>> +#define E1000_STATUS_SPEED_2500??? 0x00400000??? /* Speed 2.5Gb/s */
>> +
>> +#define SPEED_10??????? 10
>> +#define SPEED_100??????? 100
>> +#define SPEED_1000??????? 1000
>> +#define SPEED_2500??????? 2500
>> +#define HALF_DUPLEX??????? 1
>> +#define FULL_DUPLEX??????? 2
>> ? /* Interrupt Cause Read */
>> ? #define E1000_ICR_TXDW??????? 0x00000001 /* Transmit desc written
>> back */
>> diff --git a/drivers/net/ethernet/intel/igc/e1000_hw.h
>> b/drivers/net/ethernet/intel/igc/e1000_hw.h
>> index debeadd61b45..ab630e5b3d97 100644
>> --- a/drivers/net/ethernet/intel/igc/e1000_hw.h
>> +++ b/drivers/net/ethernet/intel/igc/e1000_hw.h
>> @@ -11,6 +11,7 @@
>> ? #include "e1000_regs.h"
>> ? #include "e1000_defines.h"
>> ? #include "e1000_mac.h"
>> +#include "e1000_nvm.h"
>> ? #include "e1000_i225.h"
>> ? #include "e1000_base.h"
>> @@ -81,6 +82,8 @@ struct e1000_info {
>> ????? struct e1000_nvm_operations *nvm_ops;
>> ? };
>> +extern const struct e1000_info e1000_base_info;
>> +
>> ? struct e1000_mac_info {
>> ????? struct e1000_mac_operations ops;
>> @@ -118,7 +121,7 @@ struct e1000_mac_info {
>> ? struct e1000_nvm_operations {
>> ????? s32 (*acquire)(struct e1000_hw *hw);
>> -??? s32 (*read)(struct e1000_hw *hw, u16 offset, u16 i, u16 *data);
>> +??? s32 (*read)(struct e1000_hw *hw, u16 offset, u16 words, u16 *data);
>> ????? void (*release)(struct e1000_hw *hw);
>> ????? s32 (*write)(struct e1000_hw *hw, u16 offset, u16 i, u16 *data);
>> ????? s32 (*update)(struct e1000_hw *hw);
>> diff --git a/drivers/net/ethernet/intel/igc/e1000_i225.c
>> b/drivers/net/ethernet/intel/igc/e1000_i225.c
>> index 11d797c77619..d1fa45f600dc 100644
>> --- a/drivers/net/ethernet/intel/igc/e1000_i225.c
>> +++ b/drivers/net/ethernet/intel/igc/e1000_i225.c
>> @@ -6,6 +6,32 @@
>> ? #include "e1000_hw.h"
>> ? /**
>> + *? igc_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 igc_acquire_nvm_i225(struct e1000_hw *hw)
>> +{
>> +??? return igc_acquire_swfw_sync_i225(hw, E1000_SWFW_EEP_SM);
>> +}
>> +
>> +/**
>> + *? igc_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 igc_release_nvm_i225(struct e1000_hw *hw)
>> +{
>> +??? igc_release_swfw_sync_i225(hw, E1000_SWFW_EEP_SM);
>> +}
>> +
>> +/**
>> ?? *? igc_get_hw_semaphore_i225 - Acquire hardware semaphore
>> ?? *? @hw: pointer to the HW structure
>> ?? *
>> @@ -139,3 +165,328 @@ void igc_release_swfw_sync_i225(struct e1000_hw
>> *hw, u16 mask)
>> ????? igc_put_hw_semaphore(hw);
>> ? }
>> +
>> +/**
>> + *? igc_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.
>> + **/
>> +static s32 igc_read_nvm_srrd_i225(struct e1000_hw *hw, u16 offset,
>> u16 words,
>> +????????????????? u16 *data)
>> +{
>> +??? s32 status = 0;
>> +??? u16 i, count;
>> +
>> +??? /* 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))) {
>> +??????????? status = igc_read_nvm_eerd(hw, offset, count,
>> +?????????????????????????? data + i);
>> +??????????? hw->nvm.ops.release(hw);
>> +??????? } else {
>> +??????????? status = E1000_ERR_SWFW_SYNC;
>
> Is this missing the '-' on purpose?
>
>> +??????? }
>> +
>> +??????? if (status)
>> +??????????? break;
>
> This code seem rather awkward and harder to read than necessary ...
> maybe something more like this?
>
> ??????? status = hw->nvm.ops.acquire(hw);
> ??????? if (status)
> ??????????? break;
>
> ??????? status = igc_read_nvm_eerd(hw, offset, count, data + i);
> ??????? hw->nvm.ops.release(hw);
>
> ??????? if (status)
> ??????????? break;
>
I think this is good suggestion. I will implement it in v4.
>> +??? }
>> +
>> +??? return status;
>> +}
>> +
>> +/**
>> + *? igc_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 igc_update_nvm_checksum is not called after this function , the
>> + *? Shadow Ram will most likely contain an invalid checksum.
>> + **/
>> +static s32 igc_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 = 0;
>> +
>> +??? /* 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) {
>> +??????? hw_dbg("nvm parameter(s) out of bounds\n");
>> +??????? ret_val = -E1000_ERR_NVM;
>> +??????? goto out;
>> +??? }
>> +
>> +??? for (i = 0; i < words; i++) {
>> +??????? eewr = ((offset + i) << E1000_NVM_RW_ADDR_SHIFT) |
>> +??????????? (data[i] << E1000_NVM_RW_REG_DATA) |
>> +??????????? E1000_NVM_RW_REG_START;
>> +
>> +??????? wr32(E1000_SRWR, eewr);
>> +
>> +??????? for (k = 0; k < attempts; k++) {
>> +??????????? if (E1000_NVM_RW_REG_DONE &
>> +??????????????? rd32(E1000_SRWR)) {
>> +??????????????? ret_val = 0;
>> +??????????????? break;
>> +??????????? }
>> +??????????? udelay(5);
>> +??????? }
>> +
>> +??????? if (ret_val) {
>> +??????????? hw_dbg("Shadow RAM write EEWR timed out\n");
>> +??????????? break;
>> +??????? }
>> +??? }
>> +
>> +out:
>> +??? return ret_val;
>> +}
>> +
>> +/**
>> + *? igc_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.
>> + **/
>> +static s32 igc_write_nvm_srwr_i225(struct e1000_hw *hw, u16 offset,
>> u16 words,
>> +?????????????????? u16 *data)
>> +{
>> +??? s32 status = 0;
>> +??? u16 i, count;
>> +
>> +??? /* 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))) {
>> +??????????? status = igc_write_nvm_srwr(hw, offset, count,
>> +??????????????????????????? data + i);
>> +??????????? hw->nvm.ops.release(hw);
>> +??????? } else {
>> +??????????? status = E1000_ERR_SWFW_SYNC;
>
> Is this missing the '-' on purpose?
>
>> +??????? }
>> +
>> +??????? if (status)
>> +??????????? break;
>
> Same as earlier comment - this code can be made clearer
>
Also, as above.I think this is good suggestion. I will implement it in v4.
>> +??? }
>> +
>> +??? return status;
>> +}
>> +
>> +/**
>> + *? igc_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.
>> + **/
>> +static s32 igc_validate_nvm_checksum_i225(struct e1000_hw *hw)
>> +{
>> +??? s32 status = 0;
>> +??? s32 (*read_op_ptr)(struct e1000_hw *hw, u16 offset, u16 count,
>> +?????????????? u16 *data);
>> +
>> +??? if (!(hw->nvm.ops.acquire(hw))) {
>
> Again, this can be simpler
>
> ????status = hw->nvm.ops.acquire(hw);
> ????if (status)
> ??????? return status;
>
> and now the next chunk doesn't need to be indented
>
>> +??????? /* 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 = igc_read_nvm_eerd;
>> +
>> +??????? status = igc_validate_nvm_checksum(hw);
>> +
>> +??????? /* Revert original read operation. */
>> +??????? hw->nvm.ops.read = read_op_ptr;
>> +
>> +??????? hw->nvm.ops.release(hw);
>> +??? } else {
>> +??????? status = E1000_ERR_SWFW_SYNC;
>
> Should this have a minus sign?? Or, this line is not needed if the code
> is simplified as suggested above.
>
Also, as above.I think this is good suggestion. I will implement it in v4.
>> +??? }
>> +
>> +??? return status;
>> +}
>> +
>> +/**
>> + *? igc_pool_flash_update_done_i225 - Pool FLUDONE status
>> + *? @hw: pointer to the HW structure
>> + **/
>> +static s32 igc_pool_flash_update_done_i225(struct e1000_hw *hw)
>> +{
>> +??? s32 ret_val = -E1000_ERR_NVM;
>> +??? u32 i, reg;
>> +
>> +??? for (i = 0; i < E1000_FLUDONE_ATTEMPTS; i++) {
>> +??????? reg = rd32(E1000_EECD);
>> +??????? if (reg & E1000_EECD_FLUDONE_I225) {
>> +??????????? ret_val = 0;
>> +??????????? break;
>> +??????? }
>> +??????? udelay(5);
>> +??? }
>> +
>> +??? return ret_val;
>> +}
>> +
>> +/**
>> + *? igc_update_flash_i225 - Commit EEPROM to the flash
>> + *? @hw: pointer to the HW structure
>> + **/
>> +static s32 igc_update_flash_i225(struct e1000_hw *hw)
>> +{
>> +??? s32 ret_val = 0;
>> +??? u32 flup;
>> +
>> +??? ret_val = igc_pool_flash_update_done_i225(hw);
>> +??? if (ret_val == -E1000_ERR_NVM) {
>> +??????? hw_dbg("Flash update time out\n");
>> +??????? goto out;
>> +??? }
>> +
>> +??? flup = rd32(E1000_EECD) | E1000_EECD_FLUPD_I225;
>> +??? wr32(E1000_EECD, flup);
>> +
>> +??? ret_val = igc_pool_flash_update_done_i225(hw);
>> +??? if (ret_val)
>> +??????? hw_dbg("Flash update time out\n");
>> +??? else
>> +??????? hw_dbg("Flash update complete\n");
>> +
>> +out:
>> +??? return ret_val;
>> +}
>> +
>> +/**
>> + *? igc_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.
>> + **/
>> +static s32 igc_update_nvm_checksum_i225(struct e1000_hw *hw)
>> +{
>> +??? s32 ret_val = 0;
>> +??? u16 checksum = 0;
>> +??? u16 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 = igc_read_nvm_eerd(hw, 0, 1, &nvm_data);
>> +??? if (ret_val) {
>> +??????? hw_dbg("EEPROM read failed\n");
>> +??????? goto out;
>
> May as well do a return here rather than goto a return
>
just to be consistent. I don't see reason change it.
>> +??? }
>> +
>> +??? if (!(hw->nvm.ops.acquire(hw))) {
>
> See earlier comments
>
Also, as above.I think this is good suggestion. I will implement it in v4.
>> +??????? /* 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 = igc_read_nvm_eerd(hw, i, 1, &nvm_data);
>> +??????????? if (ret_val) {
>> +??????????????? hw->nvm.ops.release(hw);
>> +??????????????? hw_dbg("NVM Read Error while updating checksum.\n");
>> +??????????????? goto out;
>> +??????????? }
>> +??????????? checksum += nvm_data;
>> +??????? }
>> +??????? checksum = (u16)NVM_SUM - checksum;
>> +??????? ret_val = igc_write_nvm_srwr(hw, NVM_CHECKSUM_REG, 1,
>> +???????????????????????? &checksum);
>> +??????? if (ret_val) {
>> +??????????? hw->nvm.ops.release(hw);
>> +??????????? hw_dbg("NVM Write Error while updating checksum.\n");
>> +??????????? goto out;
>> +??????? }
>> +
>> +??????? hw->nvm.ops.release(hw);
>> +
>> +??????? ret_val = igc_update_flash_i225(hw);
>> +??? } else {
>> +??????? ret_val = -E1000_ERR_SWFW_SYNC;
>> +??? }
>> +
>> +out:
>> +??? return ret_val;
>> +}
>> +
>> +/**
>> + *? igc_get_flash_presence_i225 - Check if flash device is detected
>> + *? @hw: pointer to the HW structure
>> + **/
>> +bool igc_get_flash_presence_i225(struct e1000_hw *hw)
>> +{
>> +??? u32 eec = 0;
>> +??? bool ret_val = false;
>> +
>> +??? eec = rd32(E1000_EECD);
>> +??? if (eec & E1000_EECD_FLASH_DETECTED_I225)
>> +??????? ret_val = true;
>> +
>> +??? return ret_val;
>> +}
>> +
>> +/**
>> + *? igc_init_nvm_params_i225 - Init NVM func ptrs.
>> + *? @hw: pointer to the HW structure
>> + **/
>> +s32 igc_init_nvm_params_i225(struct e1000_hw *hw)
>> +{
>> +??? s32 ret_val = 0;
>> +??? struct e1000_nvm_info *nvm = &hw->nvm;
>> +
>> +??? nvm->ops.acquire = igc_acquire_nvm_i225;
>> +??? nvm->ops.release = igc_release_nvm_i225;
>> +
>> +??? /* NVM Function Pointers */
>> +??? if (igc_get_flash_presence_i225(hw)) {
>> +??????? hw->nvm.type = e1000_nvm_flash_hw;
>> +??????? nvm->ops.read = igc_read_nvm_srrd_i225;
>> +??????? nvm->ops.write = igc_write_nvm_srwr_i225;
>> +??????? nvm->ops.validate = igc_validate_nvm_checksum_i225;
>> +??????? nvm->ops.update = igc_update_nvm_checksum_i225;
>> +??? } else {
>> +??????? hw->nvm.type = e1000_nvm_invm;
>> +??????? nvm->ops.read = igc_read_nvm_eerd;
>> +??????? nvm->ops.write = NULL;
>> +??????? nvm->ops.validate = NULL;
>> +??????? nvm->ops.update = NULL;
>> +??? }
>> +??? return ret_val;
>> +}
>> diff --git a/drivers/net/ethernet/intel/igc/e1000_i225.h
>> b/drivers/net/ethernet/intel/igc/e1000_i225.h
>> index 749a50c34534..c8c0398d9ff0 100644
>> --- a/drivers/net/ethernet/intel/igc/e1000_i225.h
>> +++ b/drivers/net/ethernet/intel/igc/e1000_i225.h
>> @@ -7,4 +7,7 @@
>> ? s32 igc_acquire_swfw_sync_i225(struct e1000_hw *hw, u16 mask);
>> ? void igc_release_swfw_sync_i225(struct e1000_hw *hw, u16 mask);
>> +s32 igc_init_nvm_params_i225(struct e1000_hw *hw);
>> +bool igc_get_flash_presence_i225(struct e1000_hw *hw);
>> +
>> ? #endif
>> diff --git a/drivers/net/ethernet/intel/igc/e1000_mac.c
>> b/drivers/net/ethernet/intel/igc/e1000_mac.c
>> index 27e478a9854c..5681c0e3f0c8 100644
>> --- a/drivers/net/ethernet/intel/igc/e1000_mac.c
>> +++ b/drivers/net/ethernet/intel/igc/e1000_mac.c
>> @@ -326,6 +326,132 @@ void igc_clear_hw_cntrs_base(struct e1000_hw *hw)
>> ? }
>> ? /**
>> + *? igc_rar_set - Set receive address register
>> + *? @hw: pointer to the HW structure
>> + *? @addr: pointer to the receive address
>> + *? @index: receive address array register
>> + *
>> + *? Sets the receive address array register at index to the address
>> passed
>> + *? in by addr.
>> + **/
>> +void igc_rar_set(struct e1000_hw *hw, u8 *addr, u32 index)
>> +{
>> +??? u32 rar_low, rar_high;
>> +
>> +??? /* HW expects these in little endian so we reverse the byte order
>> +???? * from network order (big endian) to little endian
>> +???? */
>> +??? rar_low = ((u32)addr[0] |
>> +?????????? ((u32)addr[1] << 8) |
>> +?????????? ((u32)addr[2] << 16) | ((u32)addr[3] << 24));
>> +
>> +??? rar_high = ((u32)addr[4] | ((u32)addr[5] << 8));
>> +
>> +??? /* If MAC address zero, no need to set the AV bit */
>> +??? if (rar_low || rar_high)
>> +??????? rar_high |= E1000_RAH_AV;
>
> if the mac address is zero, there's no need to do the swap
>
i don't know. I need check it with our design. Also, I believe we need
validate this.
>> +
>> +??? /* Some bridges will combine consecutive 32-bit writes into
>> +???? * a single burst write, which will malfunction on some parts.
>
> Is this malfunction in the new devices?? Is this comment still needed?
>
comments is obsolete. fix will be applied in v4.
>> +???? * The flushes avoid this.
>> +???? */
>> +??? wr32(E1000_RAL(index), rar_low);
>> +??? wrfl();
>> +??? wr32(E1000_RAH(index), rar_high);
>> +??? wrfl();
>> +}
>> +
>> +/**
>> + *? igc_check_for_copper_link - Check for link (Copper)
>> + *? @hw: pointer to the HW structure
>> + *
>> + *? Checks to see of the link status of the hardware has changed.? If a
>> + *? change in link status has been detected, then we read the PHY
>> registers
>> + *? to get the current speed/duplex if link exists.
>> + **/
>> +s32 igc_check_for_copper_link(struct e1000_hw *hw)
>> +{
>> +??? struct e1000_mac_info *mac = &hw->mac;
>> +??? s32 ret_val;
>> +??? bool link;
>> +
>> +??? /* We only want to go out to the PHY registers to see if Auto-Neg
>> +???? * has completed and/or if our link status has changed.? The
>> +???? * get_link_status flag is set upon receiving a Link Status
>> +???? * Change or Rx Sequence Error interrupt.
>> +???? */
>> +??? if (!mac->get_link_status) {
>> +??????? ret_val = 0;
>> +??????? goto out;
>> +??? }
>> +
>> +??? /* First we want to see if the MII Status Register reports
>> +???? * link.? If so, then we want to get the current speed/duplex
>> +???? * of the PHY.
>> +???? */
>> +??? /* TODO ret_val = igc_phy_has_link(hw, 1, 0, &link); */
>> +??? if (ret_val)
>> +??????? goto out;
>> +
>> +??? if (!link)
>> +??????? goto out; /* No link detected */
>> +
>> +??? mac->get_link_status = false;
>> +
>> +??? /* Check if there was DownShift, must be checked
>> +???? * immediately after link-up
>> +???? */
>> +??? /* TODO igc_check_downshift(hw); */
>> +
>> +??? /* If we are forcing speed/duplex, then we simply return since
>> +???? * we have already determined whether we have link or not.
>> +???? */
>> +??? if (!mac->autoneg) {
>> +??????? ret_val = -E1000_ERR_CONFIG;
>> +??????? goto out;
>> +??? }
>> +
>> +??? /* Auto-Neg is enabled.? Auto Speed Detection takes care
>> +???? * of MAC speed/duplex configuration.? So we only need to
>> +???? * configure Collision Distance in the MAC.
>> +???? */
>> +??? igc_config_collision_dist(hw);
>> +
>> +??? /* Configure Flow Control now that Auto-Neg has completed.
>> +???? * First, we need to restore the desired flow control
>> +???? * settings because we may have had to re-autoneg with a
>> +???? * different link partner.
>> +???? */
>> +??? /* TODO ret_val = igc_config_fc_after_link_up(hw); */
>> +??? if (ret_val)
>> +??????? hw_dbg("Error configuring flow control\n");
>> +
>> +out:
>> +??? return ret_val;
>> +}
>> +
>> +/**
>> + *? igc_config_collision_dist - Configure collision distance
>> + *? @hw: pointer to the HW structure
>> + *
>> + *? Configures the collision distance to the default value and is used
>> + *? during link setup. Currently no func pointer exists and all
>> + *? implementations are handled in the generic version of this function.
>> + **/
>> +void igc_config_collision_dist(struct e1000_hw *hw)
>> +{
>> +??? u32 tctl;
>> +
>> +??? tctl = rd32(E1000_TCTL);
>> +
>> +??? tctl &= ~E1000_TCTL_COLD;
>> +??? tctl |= E1000_COLLISION_DISTANCE << E1000_COLD_SHIFT;
>> +
>> +??? wr32(E1000_TCTL, tctl);
>> +??? wrfl();
>> +}
>> +
>> +/**
>> ?? *? igc_get_auto_rd_done - Check for auto read completion
>> ?? *? @hw: pointer to the HW structure
>> ?? *
>> @@ -354,6 +480,53 @@ s32 igc_get_auto_rd_done(struct e1000_hw *hw)
>> ? }
>> ? /**
>> + *? igc_get_speed_and_duplex_copper - Retrieve current speed/duplex
>> + *? @hw: pointer to the HW structure
>> + *? @speed: stores the current speed
>> + *? @duplex: stores the current duplex
>> + *
>> + *? Read the status register for the current speed/duplex and store
>> the current
>> + *? speed and duplex for copper connections.
>> + **/
>> +s32 igc_get_speed_and_duplex_copper(struct e1000_hw *hw, u16 *speed,
>> +??????????????????? u16 *duplex)
>> +{
>> +??? u32 status;
>> +
>> +??? status = rd32(E1000_STATUS);
>> +??? if (status & E1000_STATUS_SPEED_1000) {
>> +??????? /* For I225, STATUS will indicate 1G speed in both 1 Gbps
>> +???????? * and 2.5 Gbps link modes. An additional bit is used
>> +???????? * to differentiate between 1 Gbps and 2.5 Gbps.
>> +???????? */
>> +??????? if (hw->mac.type == e1000_i225 &&
>> +??????????? (status & E1000_STATUS_SPEED_2500)) {
>> +??????????? *speed = SPEED_2500;
>> +??????????? hw_dbg("2500 Mbs, ");
>> +??????? } else {
>> +??????????? *speed = SPEED_1000;
>> +??????????? hw_dbg("1000 Mbs, ");
>> +??????? }
>> +??? } else if (status & E1000_STATUS_SPEED_100) {
>> +??????? *speed = SPEED_100;
>> +??????? hw_dbg("100 Mbs, ");
>> +??? } else {
>> +??????? *speed = SPEED_10;
>> +??????? hw_dbg("10 Mbs, ");
>> +??? }
>> +
>> +??? if (status & E1000_STATUS_FD) {
>> +??????? *duplex = FULL_DUPLEX;
>> +??????? hw_dbg("Full Duplex\n");
>> +??? } else {
>> +??????? *duplex = HALF_DUPLEX;
>> +??????? hw_dbg("Half Duplex\n");
>> +??? }
>> +
>> +??? return 0;
>> +}
>> +
>> +/**
>> ?? *? igc_put_hw_semaphore - Release hardware semaphore
>> ?? *? @hw: pointer to the HW structure
>> ?? *
>> diff --git a/drivers/net/ethernet/intel/igc/e1000_mac.h
>> b/drivers/net/ethernet/intel/igc/e1000_mac.h
>> index 2f8dbf17800a..f18f5221199f 100644
>> --- a/drivers/net/ethernet/intel/igc/e1000_mac.h
>> +++ b/drivers/net/ethernet/intel/igc/e1000_mac.h
>> @@ -12,13 +12,20 @@
>> ? #endif /* E1000_REMOVED */
>> ? /* forward declaration */
>> -s32? igc_disable_pcie_master(struct e1000_hw *hw);
>> +s32 igc_check_for_copper_link(struct e1000_hw *hw);
>> +
>> +s32 igc_disable_pcie_master(struct e1000_hw *hw);
>> ? void igc_init_rx_addrs(struct e1000_hw *hw, u16 rar_count);
>> -s32? igc_setup_link(struct e1000_hw *hw);
>> +s32 igc_setup_link(struct e1000_hw *hw);
>> ? void igc_clear_hw_cntrs_base(struct e1000_hw *hw);
>> ? s32 igc_get_auto_rd_done(struct e1000_hw *hw);
>> ? void igc_put_hw_semaphore(struct e1000_hw *hw);
>> +void igc_rar_set(struct e1000_hw *hw, u8 *addr, u32 index);
>> +
>> +void igc_config_collision_dist(struct e1000_hw *hw);
>> -s32? igc_get_bus_info_pcie(struct e1000_hw *hw);
>> +s32 igc_get_bus_info_pcie(struct e1000_hw *hw);
>> +s32 igc_get_speed_and_duplex_copper(struct e1000_hw *hw, u16 *speed,
>> +??????????????????? u16 *duplex);
>> ? #endif
>> diff --git a/drivers/net/ethernet/intel/igc/e1000_nvm.c
>> b/drivers/net/ethernet/intel/igc/e1000_nvm.c
>> new file mode 100644
>> index 000000000000..62a4ae7f224b
>> --- /dev/null
>> +++ b/drivers/net/ethernet/intel/igc/e1000_nvm.c
>> @@ -0,0 +1,219 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/* Copyright (c)? 2018 Intel Corporation */
>> +
>> +#include "e1000_mac.h"
>> +#include "e1000_nvm.h"
>> +
>> +/**
>> + *? igc_poll_eerd_eewr_done - Poll for EEPROM read/write completion
>> + *? @hw: pointer to the HW structure
>> + *? @ee_reg: EEPROM flag for polling
>> + *
>> + *? Polls the EEPROM status bit for either read or write completion
>> based
>> + *? upon the value of 'ee_reg'.
>> + **/
>> +static s32 igc_poll_eerd_eewr_done(struct e1000_hw *hw, int ee_reg)
>> +{
>> +??? u32 attempts = 100000;
>> +??? u32 i, reg = 0;
>> +??? s32 ret_val = -E1000_ERR_NVM;
>> +
>> +??? for (i = 0; i < attempts; i++) {
>> +??????? if (ee_reg == E1000_NVM_POLL_READ)
>> +??????????? reg = rd32(E1000_EERD);
>> +??????? else
>> +??????????? reg = rd32(E1000_EEWR);
>> +
>> +??????? if (reg & E1000_NVM_RW_REG_DONE) {
>> +??????????? ret_val = 0;
>> +??????????? break;
>> +??????? }
>> +
>> +??????? udelay(5);
>> +??? }
>> +
>> +??? return ret_val;
>> +}
>> +
>> +/**
>> + *? igc_acquire_nvm - Generic request for access to EEPROM
>> + *? @hw: pointer to the HW structure
>> + *
>> + *? 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).
>> + **/
>> +s32 igc_acquire_nvm(struct e1000_hw *hw)
>> +{
>> +??? u32 eecd = rd32(E1000_EECD);
>> +??? s32 timeout = E1000_NVM_GRANT_ATTEMPTS;
>> +??? s32 ret_val = 0;
>> +
>> +??? wr32(E1000_EECD, eecd | E1000_EECD_REQ);
>> +??? eecd = rd32(E1000_EECD);
>> +
>> +??? while (timeout) {
>> +??????? if (eecd & E1000_EECD_GNT)
>> +??????????? break;
>> +??????? udelay(5);
>> +??????? eecd = rd32(E1000_EECD);
>> +??????? timeout--;
>> +??? }
>> +
>> +??? if (!timeout) {
>> +??????? eecd &= ~E1000_EECD_REQ;
>> +??????? wr32(E1000_EECD, eecd);
>> +??????? hw_dbg("Could not acquire NVM grant\n");
>> +??????? ret_val = -E1000_ERR_NVM;
>> +??? }
>> +
>> +??? return ret_val;
>> +}
>> +
>> +/**
>> + *? igc_release_nvm - Release exclusive access to EEPROM
>> + *? @hw: pointer to the HW structure
>> + *
>> + *? Stop any current commands to the EEPROM and clear the EEPROM
>> request bit.
>> + **/
>> +void igc_release_nvm(struct e1000_hw *hw)
>> +{
>> +??? u32 eecd;
>> +
>> +??? eecd = rd32(E1000_EECD);
>> +??? eecd &= ~E1000_EECD_REQ;
>> +??? wr32(E1000_EECD, eecd);
>> +}
>> +
>> +/**
>> + *? igc_read_nvm_eerd - Reads EEPROM using EERD register
>> + *? @hw: pointer to the HW structure
>> + *? @offset: offset of word in the EEPROM to read
>> + *? @words: number of words to read
>> + *? @data: word read from the EEPROM
>> + *
>> + *? Reads a 16 bit word from the EEPROM using the EERD register.
>> + **/
>> +s32 igc_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16
>> *data)
>> +{
>> +??? struct e1000_nvm_info *nvm = &hw->nvm;
>> +??? u32 i, eerd = 0;
>> +??? s32 ret_val = 0;
>> +
>> +??? /* 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) {
>> +??????? hw_dbg("nvm parameter(s) out of bounds\n");
>> +??????? ret_val = -E1000_ERR_NVM;
>> +??????? goto out;
>> +??? }
>> +
>> +??? for (i = 0; i < words; i++) {
>> +??????? eerd = ((offset + i) << E1000_NVM_RW_ADDR_SHIFT) +
>> +??????????? E1000_NVM_RW_REG_START;
>> +
>> +??????? wr32(E1000_EERD, eerd);
>> +??????? ret_val = igc_poll_eerd_eewr_done(hw, E1000_NVM_POLL_READ);
>> +??????? if (ret_val)
>> +??????????? break;
>> +
>> +??????? data[i] = (rd32(E1000_EERD) >> E1000_NVM_RW_REG_DATA);
>> +??? }
>> +
>> +out:
>> +??? return ret_val;
>> +}
>> +
>> +/**
>> + *? igc_read_mac_addr - Read device MAC address
>> + *? @hw: pointer to the HW structure
>> + *
>> + *? Reads the device MAC address from the EEPROM and stores the value.
>> + *? Since devices with two ports use the same EEPROM, we increment the
>> + *? last bit in the MAC address for the second port.
>> + **/
>> +s32 igc_read_mac_addr(struct e1000_hw *hw)
>> +{
>> +??? u32 rar_high;
>> +??? u32 rar_low;
>> +??? u16 i;
>> +
>> +??? rar_high = rd32(E1000_RAH(0));
>> +??? rar_low = rd32(E1000_RAL(0));
>> +
>> +??? for (i = 0; i < E1000_RAL_MAC_ADDR_LEN; i++)
>> +??????? hw->mac.perm_addr[i] = (u8)(rar_low >> (i * 8));
>> +
>> +??? for (i = 0; i < E1000_RAH_MAC_ADDR_LEN; i++)
>> +??????? hw->mac.perm_addr[i + 4] = (u8)(rar_high >> (i * 8));
>> +
>> +??? for (i = 0; i < ETH_ALEN; i++)
>> +??????? hw->mac.addr[i] = hw->mac.perm_addr[i];
>
> Is there supposed to be an increment here for the second port as
> mentioned in the function comments?
>
Ah... No. Currently we has been impelmented EERD and EEWR registers
access and this is by 16 bit (word) by design.
>> +
>> +??? return 0;
>
> Can this be a void function?
>
Currently no. Because our pointer initialization is required s32 type,
otherwise it will be throw error as incompatible value type. I agree
that it is not good style. I will think how we can implement it by more
smart way.
>> +}
>> +
>> +/**
>> + *? igc_validate_nvm_checksum - 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 igc_validate_nvm_checksum(struct e1000_hw *hw)
>> +{
>> +??? s32 ret_val = 0;
>> +??? u16 checksum = 0;
>> +??? u16 i, nvm_data;
>> +
>> +??? for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) {
>> +??????? ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data);
>> +??????? if (ret_val) {
>> +??????????? hw_dbg("NVM Read Error\n");
>> +??????????? goto out;
>> +??????? }
>> +??????? checksum += nvm_data;
>> +??? }
>
> This looks slow, reading one word at a time.? Can you read a buffer at a
> time to speed things up a bit?
>
Same answer as above. EERD and EEWR is 16 bit (word) mechanism.
>> +
>> +??? if (checksum != (u16)NVM_SUM) {
>> +??????? hw_dbg("NVM Checksum Invalid\n");
>> +??????? ret_val = -E1000_ERR_NVM;
>> +??????? goto out;
>> +??? }
>> +
>> +out:
>> +??? return ret_val;
>> +}
>> +
>> +/**
>> + *? igc_update_nvm_checksum - 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.
>> + **/
>> +s32 igc_update_nvm_checksum(struct e1000_hw *hw)
>> +{
>> +??? s32? ret_val;
>> +??? u16 checksum = 0;
>> +??? u16 i, nvm_data;
>> +
>> +??? for (i = 0; i < NVM_CHECKSUM_REG; i++) {
>> +??????? ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data);
>> +??????? if (ret_val) {
>> +??????????? hw_dbg("NVM Read Error while updating checksum.\n");
>> +??????????? goto out;
>> +??????? }
>> +??????? checksum += nvm_data;
>
> Again, maybe do buffer reads rather than one word at a time?
>
Same as above.
>> +??? }
>> +??? checksum = (u16)NVM_SUM - checksum;
>> +??? ret_val = hw->nvm.ops.write(hw, NVM_CHECKSUM_REG, 1, &checksum);
>> +??? if (ret_val)
>> +??????? hw_dbg("NVM Write Error while updating checksum.\n");
>> +
>> +out:
>> +??? return ret_val;
>> +}
>> diff --git a/drivers/net/ethernet/intel/igc/e1000_nvm.h
>> b/drivers/net/ethernet/intel/igc/e1000_nvm.h
>> new file mode 100644
>> index 000000000000..b6237bd515e9
>> --- /dev/null
>> +++ b/drivers/net/ethernet/intel/igc/e1000_nvm.h
>> @@ -0,0 +1,16 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/* Copyright (c)? 2018 Intel Corporation */
>> +
>> +#ifndef _E1000_NVM_H_
>> +#define _E1000_NVM_H_
>> +
>> +s32 igc_acquire_nvm(struct e1000_hw *hw);
>> +void igc_release_nvm(struct e1000_hw *hw);
>> +s32 igc_read_mac_addr(struct e1000_hw *hw);
>> +
>> +s32 igc_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16
>> *data);
>> +
>> +s32? igc_validate_nvm_checksum(struct e1000_hw *hw);
>> +s32? igc_update_nvm_checksum(struct e1000_hw *hw);
>> +
>> +#endif
>> diff --git a/drivers/net/ethernet/intel/igc/e1000_regs.h
>> b/drivers/net/ethernet/intel/igc/e1000_regs.h
>> index 66d5c757dae8..698ce0cac757 100644
>> --- a/drivers/net/ethernet/intel/igc/e1000_regs.h
>> +++ b/drivers/net/ethernet/intel/igc/e1000_regs.h
>> @@ -259,6 +259,9 @@
>> ? /* Management registers */
>> ? #define E1000_MANC??? 0x05820? /* Management Control - RW */
>> +/* Shadow Ram Write Register - RW */
>> +#define E1000_SRWR??? 0x12018
>> +
>> ? /* forward declaration */
>> ? struct e1000_hw;
>> ? u32 igc_rd32(struct e1000_hw *hw, u32 reg);
>> diff --git a/drivers/net/ethernet/intel/igc/igc.h
>> b/drivers/net/ethernet/intel/igc/igc.h
>> index c61212ccb60e..735a5e3d0717 100644
>> --- a/drivers/net/ethernet/intel/igc/igc.h
>> +++ b/drivers/net/ethernet/intel/igc/igc.h
>> @@ -133,6 +133,10 @@ enum igc_tx_flags {
>> ????? IGC_TX_FLAGS_CSUM?????? = 0x20,
>> ? };
>> +enum igc_boards {
>> +??? board_base,
>> +};
>> +
>> ? /** The largest size we can write to the descriptor is 65535.? In
>> order to
>> ?? * maintain a power of two alignment we have to limit ourselves to 32K.
>> ?? **/
>> @@ -348,6 +352,8 @@ struct igc_adapter {
>> ????? spinlock_t nfc_lock;
>> ????? struct igc_mac_addr *mac_table;
>> +
>> +??? struct e1000_info ei;
>> ? };
>> ? /* igc_desc_unused - calculate if we have unused descriptors */
>> diff --git a/drivers/net/ethernet/intel/igc/igc_main.c
>> b/drivers/net/ethernet/intel/igc/igc_main.c
>> index 9a99c7d68796..dcc7e700074f 100644
>> --- a/drivers/net/ethernet/intel/igc/igc_main.c
>> +++ b/drivers/net/ethernet/intel/igc/igc_main.c
>> @@ -19,9 +19,13 @@ static const char igc_driver_string[] = DRV_SUMMARY;
>> ? static const char igc_copyright[] =
>> ????? "Copyright(c) 2018 Intel Corporation.";
>> +static const struct e1000_info *igc_info_tbl[] = {
>> +??? [board_base] = &e1000_base_info,
>> +};
>> +
>> ? static const struct pci_device_id igc_pci_tbl[] = {
>> -??? { PCI_VDEVICE(INTEL, E1000_DEV_ID_I225_LM) },
>> -??? { PCI_VDEVICE(INTEL, E1000_DEV_ID_I225_V) },
>> +??? { PCI_VDEVICE(INTEL, E1000_DEV_ID_I225_LM), board_base },
>> +??? { PCI_VDEVICE(INTEL, E1000_DEV_ID_I225_V), board_base },
>> ????? /* required last entry */
>> ????? {0, }
>> ? };
>> @@ -3334,6 +3338,7 @@ static int igc_probe(struct pci_dev *pdev,
>> ????? struct net_device *netdev;
>> ????? struct igc_adapter *adapter;
>> ????? struct e1000_hw *hw;
>> +??? const struct e1000_info *ei = igc_info_tbl[ent->driver_data];
>> ????? int err, pci_using_dac;
>> ????? err = pci_enable_device_mem(pdev);
>> @@ -3417,6 +3422,14 @@ static int igc_probe(struct pci_dev *pdev,
>> ????? hw->subsystem_vendor_id = pdev->subsystem_vendor;
>> ????? hw->subsystem_device_id = pdev->subsystem_device;
>> +??? /* Copy the default MAC and PHY function pointers */
>> +??? memcpy(&hw->mac.ops, ei->mac_ops, sizeof(hw->mac.ops));
>> +
>> +??? /* Initialize skew-specific constants */
>> +??? err = ei->get_invariants(hw);
>> +??? if (err)
>> +??????? goto err_sw_init;
>> +
>> ????? /* setup the private structure */
>> ????? err = igc_sw_init(adapter);
>> ????? if (err)
>> @@ -3452,6 +3465,9 @@ static int igc_probe(struct pci_dev *pdev,
>> ?????? /* carrier off reporting is important to ethtool even BEFORE
>> open */
>> ????? netif_carrier_off(netdev);
>> +??? /* Check if Media Autosense is enabled */
>> +??? adapter->ei = *ei;
>> +
>> ????? dev_info(&pdev->dev, "@SUMMARY@");
>> ????? /* print bus type/speed/width info */
>> ????? dev_info(&pdev->dev, "%s: (PCIe:%s:%s) ",
>>
Thanks you very much for your comments.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2018-07-08 12:52 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-06-24 8:45 [Intel-wired-lan] [PATCH v3 08/11] igc: Add NVM support Sasha Neftin
2018-06-24 16:39 ` kbuild test robot
2018-06-28 23:35 ` Shannon Nelson
2018-07-08 12:52 ` Neftin, Sasha
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.