From nobody Thu Feb 12 19:05:07 2026 Received: from mail-pg1-f170.google.com (mail-pg1-f170.google.com [209.85.215.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 966B214295; Sun, 9 Jun 2024 08:18:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717921107; cv=none; b=ZPgytznioSesbNz6xkgfaqOWmBNcuiS1Pd2CwOigHko8HXJjclrbifbIJ7BHBpeA96xjeAHFhTU1rSFGlhPFAaDdUndfwOl85T6YnhGO+MHNIQJYYvnAk6ID59FSMFQROa2r465ySWf6eHinWni3pj/b7HVLfIZsn1MtOCVPSzA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1717921107; c=relaxed/simple; bh=5ZXlynQ9saGJRJz5Ae5CSfpQKJQ/5X31lnPvQl7TVR8=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=I8GZ+CVB0+OTrSJXHBZJEai82KEzJy/5kDRXnrzVBo4uS0kIZqyUq2LuzxdR+qDraPNuZGMRIfvnX0WPQegYH6SSBEt8TqNVbaJVEvTJXas2qHbhJ5BlXofV7+WibRf++PKqcZe9hTPLlYPhM+kysag9xlRrgSKjtE9OVrqjQE4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=hQhXcfIT; arc=none smtp.client-ip=209.85.215.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hQhXcfIT" Received: by mail-pg1-f170.google.com with SMTP id 41be03b00d2f7-6e7e23b42c3so610543a12.1; Sun, 09 Jun 2024 01:18:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1717921105; x=1718525905; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=U4H+tbLQVqBpmXAVAxLwfC0DnLHNgzTP1Xv586sXQB4=; b=hQhXcfITTrUHM6pH80BImRVkgfgcVnZeTCd1IKeK5AgSSuUTEKzhlpovUukP7Dh7ib jEi72fIOV0P0HbSa0vhu0hoGzU3j+RxBUGMrPRCFO82rMdcchgLR7sIcudqhAdRrpIsp 1Jg9VL5fADUhK0Kv+ycky6jd2JTeugnrQD6drXvuyr1MaCqKCf6rpNIWWHyHdZQkWL9L 6IPARxqqquDdFN0Ob+1lFwqFTFMaY8pyhQcNXhWRkLKFFAZQGPBC30wbVHJmmtWWDpsN vmJXr8hYGcKlfynbcP7nq3bKQJUSOkJ6tpQt8TDf8a5g2JvCQC3S+e3ERc/8mKjxtPxC BWEg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1717921105; x=1718525905; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=U4H+tbLQVqBpmXAVAxLwfC0DnLHNgzTP1Xv586sXQB4=; b=efWzvHICXF865pZ6MiqTf7UVR5Q1N9EHKBfiirwKz1/7lO5kuele4+EAQ9AAee8Mas hNbkXTUVPbRMArVzpDNdr2sugoKsO7BRsoptkwsv5/lcBjDSsz9LBEPMOX9xgPzeyEed IBhutzIT6P/W6d/ukGny1h1JrgiVhL0lmc71urLihs9mNLYc5XH9jII0L5e/zawl9GVn R7Nr1aQtewwShQdzO/oITt+0rSRW11N/7Tl/EVOvKQ8g0IScWqmW22gh3Nvnjnz1T0Q1 Uv4xp+786+bYwW/BxQgZ15xmmB37/2hb4NVaWKnvyTuUzDjnUB1jfka/7O0ii45I/0DK dr4w== X-Forwarded-Encrypted: i=1; AJvYcCX83tsg7mAlPHhUO3LGdUDRJ4YGCeuXBaAPIUBxmEu6SUStTFMTaqi/SDFkhANSWhXOwD7twTckIMiWvNPfGKnW1aBdjED5kbpE4p8/RdQesqJiaBRbiR1psZnhfL7MNS1MQezd X-Gm-Message-State: AOJu0YzA/okMYkJv/F9UwetPsGTiOS1OPqimdE51xIuZlblxc/yHomwo Z7w/y8NNUEDyeQf7z/GP+CuZ3ln38SVGEl3NrgHKekJjN5+UWxH/ X-Google-Smtp-Source: AGHT+IE1oXi7G9BIe0mPmP3nLxy08GQZv5Tb54kGyNj0XIpwp9mXohfSUk9hJImC3/B0WZrDHLUiRA== X-Received: by 2002:a05:6a20:7285:b0:1af:baf9:feee with SMTP id adf61e73a8af0-1b2f9a6060dmr7417915637.26.1717921104371; Sun, 09 Jun 2024 01:18:24 -0700 (PDT) Received: from richard-1-2.. (223-137-2-26.emome-ip.hinet.net. [223.137.2.26]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-1f6bd75f2f4sm61370365ad.22.2024.06.09.01.18.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 09 Jun 2024 01:18:24 -0700 (PDT) From: Richard chien X-Google-Original-From: Richard chien To: davem@davemloft.net, edumazet@google.com, kuba@kernel.org, pabeni@redhat.com Cc: jesse.brandeburg@intel.com, anthony.l.nguyen@intel.com, intel-wired-lan@lists.osuosl.org, netdev@vger.kernel.org, linux-kernel@vger.kernel.org, Richard chien Subject: [PATCH] igb: Add support for firmware update Date: Sun, 9 Jun 2024 16:15:26 +0800 Message-Id: <20240609081526.5621-1-richard.chien@hpe.com> X-Mailer: git-send-email 2.40.1 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This patch adds support for firmware update to the in-tree igb driver and i= t is actually a port from the out-of-tree igb driver. In-band firmware update is one of the essential system maintenance tasks. T= o simplify this task, the Intel online firmware update utility provides a common interface that works across different Intel NICs = running the igb/ixgbe/i40e drivers. Unfortunately, the in-tree igb and ixgbe drivers are unable to support this firmware update ut= ility, causing problems for enterprise users who do not or cannot use out-of-distro drivers due to security and various other reaso= ns (e.g. commercial Linux distros do not provide technical support for out-of-distro drivers). As a result, getting this feature into = the in-tree igb driver is highly desirable. Signed-off-by: Richard chien --- .../net/ethernet/intel/igb/e1000_defines.h | 1 + drivers/net/ethernet/intel/igb/e1000_hw.h | 59 +++ drivers/net/ethernet/intel/igb/e1000_nvm.c | 51 +++ drivers/net/ethernet/intel/igb/e1000_nvm.h | 4 + drivers/net/ethernet/intel/igb/e1000_regs.h | 9 + drivers/net/ethernet/intel/igb/igb_ethtool.c | 378 ++++++++++++------ drivers/net/ethernet/intel/igb/igb_main.c | 34 ++ 7 files changed, 424 insertions(+), 112 deletions(-) diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/e= thernet/intel/igb/e1000_defines.h index fa0289284..2fcf7621a 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -481,6 +481,7 @@ #define E1000_RAH_POOL_1 0x00040000 =20 /* Error Codes */ +#define E1000_SUCCESS 0 #define E1000_ERR_NVM 1 #define E1000_ERR_PHY 2 #define E1000_ERR_CONFIG 3 diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethern= et/intel/igb/e1000_hw.h index 44111f65a..bbdbb7198 100644 --- a/drivers/net/ethernet/intel/igb/e1000_hw.h +++ b/drivers/net/ethernet/intel/igb/e1000_hw.h @@ -292,6 +292,35 @@ struct e1000_host_mng_command_info { #include "e1000_nvm.h" #include "e1000_mbx.h" =20 +/* NVM Update commands */ +#define E1000_NVMUPD_CMD_REG_READ 0x0000000B +#define E1000_NVMUPD_CMD_REG_WRITE 0x0000000C + +/* NVM Update features API */ +#define E1000_NVMUPD_FEATURES_API_VER_MAJOR 0 +#define E1000_NVMUPD_FEATURES_API_VER_MINOR 0 +#define E1000_NVMUPD_FEATURES_API_FEATURES_ARRAY_LEN 12 +#define E1000_NVMUPD_EXEC_FEATURES 0xE +#define E1000_NVMUPD_FEATURE_FLAT_NVM_SUPPORT (1 << 0) +#define E1000_NVMUPD_FEATURE_REGISTER_ACCESS_SUPPORT (1 << 1) + +#define E1000_NVMUPD_MOD_PNT_MASK 0xFF + +struct e1000_nvm_access { + u32 command; + u32 config; + u32 offset; /* in bytes */ + u32 data_size; /* in bytes */ + u8 data[1]; +}; + +struct e1000_nvm_features { + u8 major; + u8 minor; + u16 size; + u8 features[E1000_NVMUPD_FEATURES_API_FEATURES_ARRAY_LEN]; +}; + struct e1000_mac_operations { s32 (*check_for_link)(struct e1000_hw *); s32 (*reset_hw)(struct e1000_hw *); @@ -539,6 +568,8 @@ struct e1000_hw { u16 vendor_id; =20 u8 revision_id; + /* NVM Update features */ + struct e1000_nvm_features nvmupd_features; }; =20 struct net_device *igb_get_hw_dev(struct e1000_hw *hw); @@ -551,4 +582,32 @@ s32 igb_write_pcie_cap_reg(struct e1000_hw *hw, u32 re= g, u16 *value); =20 void igb_read_pci_cfg(struct e1000_hw *hw, u32 reg, u16 *value); void igb_write_pci_cfg(struct e1000_hw *hw, u32 reg, u16 *value); + + +u32 e1000_read_reg(struct e1000_hw *hw, u32 reg); + +#define E1000_WRITE_REG(hw, reg, val) \ +do { \ + u8 __iomem *hw_addr =3D READ_ONCE((hw)->hw_addr); \ + if (!E1000_REMOVED(hw_addr)) \ + writel((val), &hw_addr[(reg)]); \ +} while (0) + +#define E1000_READ_REG(x, y) e1000_read_reg(x, y) +#define E1000_READ_REG8(h, r) readb(READ_ONCE(h->hw_addr) + r) + +#define E1000_WRITE_FLASH_REG(a, reg, value) ( \ + writel((value), ((a)->flash_address + reg))) + +//#define E1000_READ_FLASH_REG(a, reg) (readl((a)->flash_address + reg)) + +//#define E1000_READ_FLASH_REG16(a, reg) (readw((a)->flash_address + reg)) + + +#define E1000_READ_FLASH_REG(a, reg) (readl((a)->flash_address + reg)) + +#define E1000_READ_FLASH_REG8(a, reg) ( \ + readb(READ_ONCE((a)->flash_address) + reg)) + + #endif /* _E1000_IGB_HW_H_ */ diff --git a/drivers/net/ethernet/intel/igb/e1000_nvm.c b/drivers/net/ether= net/intel/igb/e1000_nvm.c index 2dcd64d6d..e3635f3fd 100644 --- a/drivers/net/ethernet/intel/igb/e1000_nvm.c +++ b/drivers/net/ethernet/intel/igb/e1000_nvm.c @@ -778,3 +778,54 @@ void igb_get_fw_version(struct e1000_hw *hw, struct e1= 000_fw_version *fw_vers) | eeprom_verl; } } + +/** + * e1000_read_nvm - Reads NVM (EEPROM) + * @hw: pointer to the HW structure + * @offset: the word offset to read + * @words: number of 16-bit words to read + * @data: pointer to the properly sized buffer for the data. + * + * Reads 16-bit chunks of data from the NVM (EEPROM). This is a function + * pointer entry point called by drivers. + **/ +s32 e1000_read_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + if (hw->nvm.ops.read) + return hw->nvm.ops.read(hw, offset, words, data); + + return -E1000_ERR_CONFIG; +} + +/** + * e1000_write_nvm - Writes to NVM (EEPROM) + * @hw: pointer to the HW structure + * @offset: the word offset to read + * @words: number of 16-bit words to write + * @data: pointer to the properly sized buffer for the data. + * + * Writes 16-bit chunks of data to the NVM (EEPROM). This is a function + * pointer entry point called by drivers. + **/ +s32 e1000_write_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) +{ + if (hw->nvm.ops.write) + return hw->nvm.ops.write(hw, offset, words, data); + + return E1000_SUCCESS; +} + +/** + * e1000_update_nvm_checksum - Updates NVM (EEPROM) checksum + * @hw: pointer to the HW structure + * + * Updates the NVM checksum. Currently no func pointer exists and all + * implementations are handled in the generic version of this function. + **/ +s32 e1000_update_nvm_checksum(struct e1000_hw *hw) +{ + if (hw->nvm.ops.update) + return hw->nvm.ops.update(hw); + + return -E1000_ERR_CONFIG; +} diff --git a/drivers/net/ethernet/intel/igb/e1000_nvm.h b/drivers/net/ether= net/intel/igb/e1000_nvm.h index 091cddf4a..6584a0a7a 100644 --- a/drivers/net/ethernet/intel/igb/e1000_nvm.h +++ b/drivers/net/ethernet/intel/igb/e1000_nvm.h @@ -33,4 +33,8 @@ struct e1000_fw_version { }; void igb_get_fw_version(struct e1000_hw *hw, struct e1000_fw_version *fw_v= ers); =20 +s32 e1000_read_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +s32 e1000_write_nvm(struct e1000_hw *hw, u16 offset, u16 words, u16 *data); +s32 e1000_update_nvm_checksum(struct e1000_hw *hw); + #endif diff --git a/drivers/net/ethernet/intel/igb/e1000_regs.h b/drivers/net/ethe= rnet/intel/igb/e1000_regs.h index eb9f6da92..eae551959 100644 --- a/drivers/net/ethernet/intel/igb/e1000_regs.h +++ b/drivers/net/ethernet/intel/igb/e1000_regs.h @@ -9,8 +9,10 @@ #define E1000_EECD 0x00010 /* EEPROM/Flash Control - RW */ #define E1000_EERD 0x00014 /* EEPROM Read - RW */ #define E1000_CTRL_EXT 0x00018 /* Extended Device Control - RW */ +#define E1000_FLA 0x0001C /* Flash Access - RW */ #define E1000_MDIC 0x00020 /* MDI Control - RW */ #define E1000_MDICNFG 0x00E04 /* MDI Config - RW */ +#define E1000_REGISTER_SET_SIZE 0x20000 /* CSR Size */ #define E1000_SCTL 0x00024 /* SerDes Control - RW */ #define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ #define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ @@ -49,6 +51,7 @@ #define E1000_EEMNGCTL_I210 0x12030 /* MNG EEprom Control */ #define E1000_EEARBC_I210 0x12024 /* EEPROM Auto Read Bus Control */ #define E1000_EEWR 0x0102C /* EEPROM Write Register - RW */ +#define E1000_FLOP 0x0103C /* FLASH Opcode Register */ #define E1000_I2CCMD 0x01028 /* SFPI2C Command Register - RW */ #define E1000_FRTIMER 0x01048 /* Free Running Timer - RW */ #define E1000_TCPTIMER 0x0104C /* TCP Timer - RW */ @@ -66,6 +69,7 @@ #define E1000_MPHY_ADDR_CTRL 0x0024 /* GbE MPHY Address Control */ #define E1000_MPHY_DATA 0x0E10 /* GBE MPHY Data */ #define E1000_MPHY_STAT 0x0E0C /* GBE MPHY Statistics */ +#define E1000_I350_BARCTRL 0x5BFC /* BAR ctrl reg */ =20 /* IEEE 1588 TIMESYNCH */ #define E1000_TSYNCRXCTL 0x0B620 /* Rx Time Sync Control register - RW */ @@ -116,6 +120,7 @@ #define E1000_DMCRTRH 0x05DD0 /* Receive Packet Rate Threshold */ #define E1000_DMCCNT 0x05DD4 /* Current Rx Count */ #define E1000_FCRTC 0x02170 /* Flow Control Rx high watermark */ +#define E1000_PCIEMISC 0x05BB8 /* PCIE misc config register */ =20 /* TX Rate Limit Registers */ #define E1000_RTTDQSEL 0x3604 /* Tx Desc Plane Queue Select - WO */ @@ -390,6 +395,7 @@ do { \ #define E1000_O2BSPC 0x0415C /* OS2BMC packets transmitted by host */ =20 #define E1000_SRWR 0x12018 /* Shadow Ram Write Register - RW */ +#define E1000_EEC_REG 0x12010 #define E1000_I210_FLMNGCTL 0x12038 #define E1000_I210_FLMNGDATA 0x1203C #define E1000_I210_FLMNGCNT 0x12040 @@ -400,6 +406,9 @@ do { \ =20 #define E1000_I210_FLA 0x1201C =20 +#define E1000_SHADOWINF 0x12068 +#define E1000_FLFWUPDATE 0x12108 + #define E1000_I210_DTXMXPKTSZ 0x355C =20 #define E1000_I210_TXDCTL(_n) (0x0E028 + ((_n) * 0x40)) diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/eth= ernet/intel/igb/igb_ethtool.c index 61d72250c..ebed72a3e 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -724,128 +724,282 @@ static void igb_get_regs(struct net_device *netdev, regs_buff[739] =3D rd32(E1000_I210_RR2DCDELAY); } =20 -static int igb_get_eeprom_len(struct net_device *netdev) -{ - struct igb_adapter *adapter =3D netdev_priv(netdev); - return adapter->hw.nvm.word_size * 2; +static u8 igb_nvmupd_get_module(u32 val) +{ + return (u8)(val & E1000_NVMUPD_MOD_PNT_MASK); +} + +static int igb_nvmupd_validate_offset(struct igb_adapter *adapter, u32 off= set) +{ + if (offset >=3D E1000_REGISTER_SET_SIZE) + return 0; + + switch (offset) { + case E1000_CTRL: + case E1000_STATUS: + case E1000_EECD: + case E1000_EERD: + case E1000_CTRL_EXT: + case E1000_FLA: + case E1000_FLOP: + case E1000_SWSM: + case E1000_FWSM: + case E1000_SW_FW_SYNC: + case E1000_IOVTCL: + case E1000_I350_BARCTRL: + case E1000_THSTAT: + case E1000_EEC_REG: + case E1000_SRWR: + case E1000_I210_FLA: + case E1000_I210_FLSWCTL: + case E1000_I210_FLSWDATA: + case E1000_I210_FLSWCNT: + case E1000_SHADOWINF: + case E1000_FLFWUPDATE: + case E1000_RAL(0): + case E1000_RAL(1): + case E1000_RAL(2): + case E1000_RAL(3): + case E1000_RAL(4): + case E1000_RAL(5): + case E1000_RAL(6): + case E1000_RAL(7): + case E1000_RAL(8): + case E1000_RAL(9): + case E1000_RAL(10): + case E1000_RAL(11): + case E1000_RAL(12): + case E1000_RAL(13): + case E1000_RAL(14): + case E1000_RAL(15): + case E1000_RAH(0): + case E1000_RAH(1): + case E1000_RAH(2): + case E1000_RAH(3): + case E1000_RAH(4): + case E1000_RAH(5): + case E1000_RAH(6): + case E1000_RAH(7): + case E1000_RAH(8): + case E1000_RAH(9): + case E1000_RAH(10): + case E1000_RAH(11): + case E1000_RAH(12): + case E1000_RAH(13): + case E1000_RAH(14): + case E1000_RAH(15): + return 0; + default: + dev_warn(&adapter->pdev->dev, "Bad offset: %x\n", offset); + return -ENOTTY; + } +} + +static int igb_nvmupd_command(struct e1000_hw *hw, + struct e1000_nvm_access *nvm, + u8 *bytes) +{ + struct igb_adapter *adapter =3D hw->back; + resource_size_t bar0_len; + int ret_val =3D 0; + u32 command; + u8 module; + + bar0_len =3D pci_resource_len(adapter->pdev, 0); + command =3D nvm->command; + module =3D igb_nvmupd_get_module(nvm->config); + + switch (command) { + case E1000_NVMUPD_CMD_REG_READ: + switch (module) { + case E1000_NVMUPD_EXEC_FEATURES: + if (nvm->data_size =3D=3D hw->nvmupd_features.size) + memcpy(bytes, &hw->nvmupd_features, + hw->nvmupd_features.size); + else + ret_val =3D -ENOMEM; + break; + default: + if (igb_nvmupd_validate_offset(adapter, nvm->offse= t)) + return -ENOTTY; + if (nvm->offset >=3D bar0_len) { + if (hw->mac.type =3D=3D e1000_82576 && + hw->flash_address) { + if (nvm->data_size =3D=3D 1) + *bytes =3D E1000_READ_FLAS= H_REG8( + hw, + nvm->offset - bar0= _len); + else + *((u32 *)bytes) =3D + E1000_READ_FLASH_R= EG(hw, + nvm->offset - bar0= _len); + } else + ret_val =3D -EFAULT; + } else if (nvm->data_size =3D=3D 1) + *bytes =3D E1000_READ_REG8(hw, nvm->offset= ); + else + *((u32 *)bytes) =3D E1000_READ_REG(hw, + nvm->offs= et); + break; + } + break; + case E1000_NVMUPD_CMD_REG_WRITE: + if (igb_nvmupd_validate_offset(adapter, nvm->offset)) + return -ENOTTY; + if (nvm->offset >=3D bar0_len) { + if (hw->mac.type =3D=3D e1000_82576 && hw->flash_a= ddress) + E1000_WRITE_FLASH_REG(hw, + nvm->offset - bar0_l= en, + *((u32 *)bytes)); + else + ret_val =3D -EFAULT; + } else + E1000_WRITE_REG(hw, nvm->offset, *((u32 *)bytes)); + break; + } + + return ret_val; } =20 -static int igb_get_eeprom(struct net_device *netdev, - struct ethtool_eeprom *eeprom, u8 *bytes) +static int igb_get_eeprom_len(struct net_device *netdev) { - struct igb_adapter *adapter =3D netdev_priv(netdev); - struct e1000_hw *hw =3D &adapter->hw; - u16 *eeprom_buff; - int first_word, last_word; - int ret_val =3D 0; - u16 i; - - if (eeprom->len =3D=3D 0) - return -EINVAL; - - eeprom->magic =3D hw->vendor_id | (hw->device_id << 16); - - first_word =3D eeprom->offset >> 1; - last_word =3D (eeprom->offset + eeprom->len - 1) >> 1; + struct igb_adapter *adapter =3D netdev_priv(netdev); + struct pci_dev *pdev =3D adapter->pdev; =20 - eeprom_buff =3D kmalloc_array(last_word - first_word + 1, sizeof(u16), - GFP_KERNEL); - if (!eeprom_buff) - return -ENOMEM; - - if (hw->nvm.type =3D=3D e1000_nvm_eeprom_spi) - ret_val =3D hw->nvm.ops.read(hw, first_word, - last_word - first_word + 1, - eeprom_buff); - else { - for (i =3D 0; i < last_word - first_word + 1; i++) { - ret_val =3D hw->nvm.ops.read(hw, first_word + i, 1, - &eeprom_buff[i]); - if (ret_val) - break; - } - } - - /* Device's eeprom is always little-endian, word addressable */ - for (i =3D 0; i < last_word - first_word + 1; i++) - le16_to_cpus(&eeprom_buff[i]); + if (adapter->hw.mac.type =3D=3D e1000_82576) + return pci_resource_len(pdev, 0) + pci_resource_len(pdev, = 1); =20 - memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 1), - eeprom->len); - kfree(eeprom_buff); + return pci_resource_len(pdev, 0); +} =20 - return ret_val; +static int igb_get_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct igb_adapter *adapter =3D netdev_priv(netdev); + struct e1000_hw *hw =3D &adapter->hw; + u16 *eeprom_buff; + int first_word, last_word; + int ret_val =3D 0; + struct e1000_nvm_access *nvm; + u32 magic; + u16 i; + + if (eeprom->len =3D=3D 0) + return -EINVAL; + + magic =3D hw->vendor_id | (hw->device_id << 16); + if (eeprom->magic && eeprom->magic !=3D magic) { + nvm =3D (struct e1000_nvm_access *)eeprom; + ret_val =3D igb_nvmupd_command(hw, nvm, bytes); + return ret_val; + } + =20 + /* normal ethtool get_eeprom support */ + eeprom->magic =3D hw->vendor_id | (hw->device_id << 16); + + first_word =3D eeprom->offset >> 1; + last_word =3D (eeprom->offset + eeprom->len - 1) >> 1; + + eeprom_buff =3D kmalloc(sizeof(u16) * + (last_word - first_word + 1), GFP_KERNEL); + if (!eeprom_buff) + return -ENOMEM; + + if (hw->nvm.type =3D=3D e1000_nvm_eeprom_spi) + ret_val =3D e1000_read_nvm(hw, first_word, + last_word - first_word + 1, + eeprom_buff); + else { + for (i =3D 0; i < last_word - first_word + 1; i++) { + ret_val =3D e1000_read_nvm(hw, first_word + i, 1, + &eeprom_buff[i]); + if (ret_val) + break; + } + } + + /* Device's eeprom is always little-endian, word addressable */ + for (i =3D 0; i < last_word - first_word + 1; i++) + eeprom_buff[i] =3D le16_to_cpu(eeprom_buff[i]); + + memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 1), + eeprom->len); + kfree(eeprom_buff); + + return ret_val; } =20 static int igb_set_eeprom(struct net_device *netdev, - struct ethtool_eeprom *eeprom, u8 *bytes) -{ - struct igb_adapter *adapter =3D netdev_priv(netdev); - struct e1000_hw *hw =3D &adapter->hw; - u16 *eeprom_buff; - void *ptr; - int max_len, first_word, last_word, ret_val =3D 0; - u16 i; - - if (eeprom->len =3D=3D 0) - return -EOPNOTSUPP; - - if ((hw->mac.type >=3D e1000_i210) && - !igb_get_flash_presence_i210(hw)) { - return -EOPNOTSUPP; - } - - if (eeprom->magic !=3D (hw->vendor_id | (hw->device_id << 16))) - return -EFAULT; - - max_len =3D hw->nvm.word_size * 2; - - first_word =3D eeprom->offset >> 1; - last_word =3D (eeprom->offset + eeprom->len - 1) >> 1; - eeprom_buff =3D kmalloc(max_len, GFP_KERNEL); - if (!eeprom_buff) - return -ENOMEM; - - ptr =3D (void *)eeprom_buff; - - if (eeprom->offset & 1) { - /* need read/modify/write of first changed EEPROM word - * only the second byte of the word is being modified - */ - ret_val =3D hw->nvm.ops.read(hw, first_word, 1, - &eeprom_buff[0]); - ptr++; - } - if (((eeprom->offset + eeprom->len) & 1) && (ret_val =3D=3D 0)) { - /* need read/modify/write of last changed EEPROM word - * only the first byte of the word is being modified - */ - ret_val =3D hw->nvm.ops.read(hw, last_word, 1, - &eeprom_buff[last_word - first_word]); - if (ret_val) - goto out; - } - - /* Device's eeprom is always little-endian, word addressable */ - for (i =3D 0; i < last_word - first_word + 1; i++) - le16_to_cpus(&eeprom_buff[i]); - - memcpy(ptr, bytes, eeprom->len); - - for (i =3D 0; i < last_word - first_word + 1; i++) - cpu_to_le16s(&eeprom_buff[i]); - - ret_val =3D hw->nvm.ops.write(hw, first_word, - last_word - first_word + 1, eeprom_buff); - - /* Update the checksum if nvm write succeeded */ - if (ret_val =3D=3D 0) - hw->nvm.ops.update(hw); - - igb_set_fw_version(adapter); + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct igb_adapter *adapter =3D netdev_priv(netdev); + struct e1000_hw *hw =3D &adapter->hw; + u16 *eeprom_buff; + void *ptr; + int max_len, first_word, last_word, ret_val =3D 0; + struct e1000_nvm_access *nvm; + u32 magic; + u16 i; + + if (eeprom->len =3D=3D 0) + return -EOPNOTSUPP; + + magic =3D hw->vendor_id | (hw->device_id << 16); + if (eeprom->magic && eeprom->magic !=3D magic) { + nvm =3D (struct e1000_nvm_access *)eeprom; + ret_val =3D igb_nvmupd_command(hw, nvm, bytes); + return ret_val; + } + + /* normal ethtool get_eeprom support */ + if (eeprom->magic !=3D (hw->vendor_id | (hw->device_id << 16))) + return -EFAULT; + + max_len =3D hw->nvm.word_size * 2; + + first_word =3D eeprom->offset >> 1; + last_word =3D (eeprom->offset + eeprom->len - 1) >> 1; + eeprom_buff =3D kmalloc(max_len, GFP_KERNEL); + if (!eeprom_buff) + return -ENOMEM; + + ptr =3D (void *)eeprom_buff; + + if (eeprom->offset & 1) { + /* need read/modify/write of first changed EEPROM word */ + /* only the second byte of the word is being modified */ + ret_val =3D e1000_read_nvm(hw, first_word, 1, + &eeprom_buff[0]); + ptr++; + } + if (((eeprom->offset + eeprom->len) & 1) && (ret_val =3D=3D 0)) { + /* need read/modify/write of last changed EEPROM word */ + /* only the first byte of the word is being modified */ + ret_val =3D e1000_read_nvm(hw, last_word, 1, + &eeprom_buff[last_word - first_word]); + if (ret_val) + goto out; + } + + /* Device's eeprom is always little-endian, word addressable */ + for (i =3D 0; i < last_word - first_word + 1; i++) + le16_to_cpus(&eeprom_buff[i]); + + memcpy(ptr, bytes, eeprom->len); + + for (i =3D 0; i < last_word - first_word + 1; i++) + cpu_to_le16s(&eeprom_buff[i]); + + ret_val =3D e1000_write_nvm(hw, first_word, + last_word - first_word + 1, eeprom_buff); + + /* Update the checksum if write succeeded. + * and flush shadow RAM for 82573 controllers */ + if (ret_val =3D=3D 0) + e1000_update_nvm_checksum(hw); out: - kfree(eeprom_buff); - return ret_val; + kfree(eeprom_buff); + return ret_val; } =20 static void igb_get_drvinfo(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethern= et/intel/igb/igb_main.c index fce2930ae..06b97ed9a 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1955,6 +1955,28 @@ static void igb_setup_tx_mode(struct igb_adapter *ad= apter) "enabled" : "disabled"); } =20 +u32 e1000_read_reg(struct e1000_hw *hw, u32 reg) +{ + struct igb_adapter *igb =3D container_of(hw, struct igb_adapter, h= w); + u8 __iomem *hw_addr =3D READ_ONCE(hw->hw_addr); + u32 value =3D 0; + + if (E1000_REMOVED(hw_addr)) + return ~value; + + value =3D readl(&hw_addr[reg]); + + /* reads should not return all F's */ + if (!(~value) && (!reg || !(~readl(hw_addr)))) { + struct net_device *netdev =3D igb->netdev; + + hw->hw_addr =3D NULL; + netdev_err(netdev, "PCIe link lost\n"); + } + + return value; +} + /** * igb_configure - configure the hardware for RX and TX * @adapter: private board structure @@ -4091,6 +4113,18 @@ static int igb_sw_init(struct igb_adapter *adapter) adapter->flags &=3D ~IGB_FLAG_DMAC; =20 set_bit(__IGB_DOWN, &adapter->state); + + /* NVM Update features structure initialization */ + hw->nvmupd_features.major =3D E1000_NVMUPD_FEATURES_API_VER_MAJOR; + hw->nvmupd_features.minor =3D E1000_NVMUPD_FEATURES_API_VER_MINOR; + hw->nvmupd_features.size =3D sizeof(hw->nvmupd_features); + memset(hw->nvmupd_features.features, 0x0, + E1000_NVMUPD_FEATURES_API_FEATURES_ARRAY_LEN * + sizeof(*hw->nvmupd_features.features)); + + hw->nvmupd_features.features[0] =3D + E1000_NVMUPD_FEATURE_REGISTER_ACCESS_SUPPORT; + return 0; } =20 --=20 2.40.1