Received: by 2002:ab2:68c1:0:b0:1fd:9a81:d0e4 with SMTP id e1csp405594lqp; Sun, 9 Jun 2024 01:18:40 -0700 (PDT) X-Forwarded-Encrypted: i=3; AJvYcCVOIO/r8XQKbjWThDxqMhL2QYLXuhOz0lpef2gDgPwioBvfMtCOJYTPtLwFCos4TeopmOcvdvj+IeDJoeyzDUfGICIH8ZA/vsAhxrkzpg== X-Google-Smtp-Source: AGHT+IFrrv3gjEnzMi+22g3sBGX5TCuk3cO4MEFRKlkMZ8j/BZ4rr2S+NlydJKQUDgpfOlCIo9sH X-Received: by 2002:a05:620a:4004:b0:795:813b:fa3d with SMTP id af79cd13be357-795813bfdc5mr54859785a.10.1717921119750; Sun, 09 Jun 2024 01:18:39 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1717921119; cv=pass; d=google.com; s=arc-20160816; b=HlxlZ62Jmi3tMtbkm/1GjFO+Pko9oGN5AYwoYkj1HQK5drytXwqZJMnZx3Nx3I0tuS WzuI9BW8lgmJh1nQlRxwWTxWuBRHqVRUjMayJDcC9WtpgodKjQEkdAswHVmXsGAJQiSu Dh7LeHKTaw8FwgjGCJfxjkPoavenmI4KVCaZZl0oKLV+uxlodVXhgz4F9FSGjl3jTpey jjEqQcoTi0PgdEa/H5LbbYJpjo6XJzW7aHg21gNTHCq/knnsUfgUSduTp91Tg7YSwpQd Gf8nbas9qG0pENCDTrXqff7NS4AmUTSfaR/BV5G6MtwuNQZKcSJRlEr8fbvZJvbZtU35 G9/g== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=content-transfer-encoding:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:message-id:date:subject:cc:to :from:dkim-signature; bh=U4H+tbLQVqBpmXAVAxLwfC0DnLHNgzTP1Xv586sXQB4=; fh=s/IREd6CLx6a0HkQ0YtjTj7D6fsmK9X/8PUDtiu57dE=; b=qCK4qXAaz8uWr8XtUBAJqXzevhHrdZv6MIiWc9nSTT0RLcl2TaDGhRa4tBw+HBZHVP OGIHFpdoX9qsyYw7PvECdmPnc8FYieXLJqOMf5mk9O9SthOFjkJhCHLVObEOG1BzT/56 9lu4dBICbDUg3d6XVSyM+TFBP1tavv3ao1TUuqXbd+UMJHdOqRsoIMze+k2sJeOeK6Rc fqOzMqysv/TGHsxeXx/yea8/fk8NoUb9DyQkZHiHkwhh4MRkZmY8iRsdxESNF3CG9+3V d4iLkDOPosgUFiugFqKmigd6ITbtrB7vLoen/I8JdZqlQ0FXpaYu0Z45JOYNMiR8Qisr vxfw==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@gmail.com header.s=20230601 header.b=hQhXcfIT; arc=pass (i=1 spf=pass spfdomain=gmail.com dkim=pass dkdomain=gmail.com dmarc=pass fromdomain=gmail.com); spf=pass (google.com: domain of linux-kernel+bounces-207277-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:45d1:ec00::1 as permitted sender) smtp.mailfrom="linux-kernel+bounces-207277-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ny.mirrors.kernel.org (ny.mirrors.kernel.org. [2604:1380:45d1:ec00::1]) by mx.google.com with ESMTPS id af79cd13be357-7955d698159si138338285a.300.2024.06.09.01.18.39 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 09 Jun 2024 01:18:39 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel+bounces-207277-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:45d1:ec00::1 as permitted sender) client-ip=2604:1380:45d1:ec00::1; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20230601 header.b=hQhXcfIT; arc=pass (i=1 spf=pass spfdomain=gmail.com dkim=pass dkdomain=gmail.com dmarc=pass fromdomain=gmail.com); spf=pass (google.com: domain of linux-kernel+bounces-207277-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:45d1:ec00::1 as permitted sender) smtp.mailfrom="linux-kernel+bounces-207277-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ny.mirrors.kernel.org (Postfix) with ESMTPS id 6AB7C1C20AEA for ; Sun, 9 Jun 2024 08:18:39 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 8F5851CD13; Sun, 9 Jun 2024 08:18:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hQhXcfIT" 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 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-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: 8bit This patch adds support for firmware update to the in-tree igb driver and it is actually a port from the out-of-tree igb driver. In-band firmware update is one of the essential system maintenance tasks. To 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 utility, causing problems for enterprise users who do not or cannot use out-of-distro drivers due to security and various other reasons (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/ethernet/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 /* 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/ethernet/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" +/* 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; u8 revision_id; + /* NVM Update features */ + struct e1000_nvm_features nvmupd_features; }; 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 reg, u16 *value); 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 = 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/ethernet/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 e1000_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/ethernet/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_vers); +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/ethernet/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 */ /* 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 */ /* 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 */ #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 { \ #define E1000_I210_FLA 0x1201C +#define E1000_SHADOWINF 0x12068 +#define E1000_FLFWUPDATE 0x12108 + #define E1000_I210_DTXMXPKTSZ 0x355C #define E1000_I210_TXDCTL(_n) (0x0E028 + ((_n) * 0x40)) diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/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] = rd32(E1000_I210_RR2DCDELAY); } -static int igb_get_eeprom_len(struct net_device *netdev) -{ - struct igb_adapter *adapter = 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 offset) +{ + if (offset >= 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 = hw->back; + resource_size_t bar0_len; + int ret_val = 0; + u32 command; + u8 module; + + bar0_len = pci_resource_len(adapter->pdev, 0); + command = nvm->command; + module = 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 == hw->nvmupd_features.size) + memcpy(bytes, &hw->nvmupd_features, + hw->nvmupd_features.size); + else + ret_val = -ENOMEM; + break; + default: + if (igb_nvmupd_validate_offset(adapter, nvm->offset)) + return -ENOTTY; + if (nvm->offset >= bar0_len) { + if (hw->mac.type == e1000_82576 && + hw->flash_address) { + if (nvm->data_size == 1) + *bytes = E1000_READ_FLASH_REG8( + hw, + nvm->offset - bar0_len); + else + *((u32 *)bytes) = + E1000_READ_FLASH_REG(hw, + nvm->offset - bar0_len); + } else + ret_val = -EFAULT; + } else if (nvm->data_size == 1) + *bytes = E1000_READ_REG8(hw, nvm->offset); + else + *((u32 *)bytes) = E1000_READ_REG(hw, + nvm->offset); + break; + } + break; + case E1000_NVMUPD_CMD_REG_WRITE: + if (igb_nvmupd_validate_offset(adapter, nvm->offset)) + return -ENOTTY; + if (nvm->offset >= bar0_len) { + if (hw->mac.type == e1000_82576 && hw->flash_address) + E1000_WRITE_FLASH_REG(hw, + nvm->offset - bar0_len, + *((u32 *)bytes)); + else + ret_val = -EFAULT; + } else + E1000_WRITE_REG(hw, nvm->offset, *((u32 *)bytes)); + break; + } + + return ret_val; } -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 = netdev_priv(netdev); - struct e1000_hw *hw = &adapter->hw; - u16 *eeprom_buff; - int first_word, last_word; - int ret_val = 0; - u16 i; - - if (eeprom->len == 0) - return -EINVAL; - - eeprom->magic = hw->vendor_id | (hw->device_id << 16); - - first_word = eeprom->offset >> 1; - last_word = (eeprom->offset + eeprom->len - 1) >> 1; + struct igb_adapter *adapter = netdev_priv(netdev); + struct pci_dev *pdev = adapter->pdev; - eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16), - GFP_KERNEL); - if (!eeprom_buff) - return -ENOMEM; - - if (hw->nvm.type == e1000_nvm_eeprom_spi) - ret_val = hw->nvm.ops.read(hw, first_word, - last_word - first_word + 1, - eeprom_buff); - else { - for (i = 0; i < last_word - first_word + 1; i++) { - ret_val = 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 = 0; i < last_word - first_word + 1; i++) - le16_to_cpus(&eeprom_buff[i]); + if (adapter->hw.mac.type == e1000_82576) + return pci_resource_len(pdev, 0) + pci_resource_len(pdev, 1); - memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 1), - eeprom->len); - kfree(eeprom_buff); + return pci_resource_len(pdev, 0); +} - return ret_val; +static int igb_get_eeprom(struct net_device *netdev, + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct igb_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; + u16 *eeprom_buff; + int first_word, last_word; + int ret_val = 0; + struct e1000_nvm_access *nvm; + u32 magic; + u16 i; + + if (eeprom->len == 0) + return -EINVAL; + + magic = hw->vendor_id | (hw->device_id << 16); + if (eeprom->magic && eeprom->magic != magic) { + nvm = (struct e1000_nvm_access *)eeprom; + ret_val = igb_nvmupd_command(hw, nvm, bytes); + return ret_val; + } + + /* normal ethtool get_eeprom support */ + eeprom->magic = hw->vendor_id | (hw->device_id << 16); + + first_word = eeprom->offset >> 1; + last_word = (eeprom->offset + eeprom->len - 1) >> 1; + + eeprom_buff = kmalloc(sizeof(u16) * + (last_word - first_word + 1), GFP_KERNEL); + if (!eeprom_buff) + return -ENOMEM; + + if (hw->nvm.type == e1000_nvm_eeprom_spi) + ret_val = e1000_read_nvm(hw, first_word, + last_word - first_word + 1, + eeprom_buff); + else { + for (i = 0; i < last_word - first_word + 1; i++) { + ret_val = 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 = 0; i < last_word - first_word + 1; i++) + eeprom_buff[i] = le16_to_cpu(eeprom_buff[i]); + + memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 1), + eeprom->len); + kfree(eeprom_buff); + + return ret_val; } static int igb_set_eeprom(struct net_device *netdev, - struct ethtool_eeprom *eeprom, u8 *bytes) -{ - struct igb_adapter *adapter = netdev_priv(netdev); - struct e1000_hw *hw = &adapter->hw; - u16 *eeprom_buff; - void *ptr; - int max_len, first_word, last_word, ret_val = 0; - u16 i; - - if (eeprom->len == 0) - return -EOPNOTSUPP; - - if ((hw->mac.type >= e1000_i210) && - !igb_get_flash_presence_i210(hw)) { - return -EOPNOTSUPP; - } - - if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16))) - return -EFAULT; - - max_len = hw->nvm.word_size * 2; - - first_word = eeprom->offset >> 1; - last_word = (eeprom->offset + eeprom->len - 1) >> 1; - eeprom_buff = kmalloc(max_len, GFP_KERNEL); - if (!eeprom_buff) - return -ENOMEM; - - ptr = (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 = hw->nvm.ops.read(hw, first_word, 1, - &eeprom_buff[0]); - ptr++; - } - if (((eeprom->offset + eeprom->len) & 1) && (ret_val == 0)) { - /* need read/modify/write of last changed EEPROM word - * only the first byte of the word is being modified - */ - ret_val = 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 = 0; i < last_word - first_word + 1; i++) - le16_to_cpus(&eeprom_buff[i]); - - memcpy(ptr, bytes, eeprom->len); - - for (i = 0; i < last_word - first_word + 1; i++) - cpu_to_le16s(&eeprom_buff[i]); - - ret_val = hw->nvm.ops.write(hw, first_word, - last_word - first_word + 1, eeprom_buff); - - /* Update the checksum if nvm write succeeded */ - if (ret_val == 0) - hw->nvm.ops.update(hw); - - igb_set_fw_version(adapter); + struct ethtool_eeprom *eeprom, u8 *bytes) +{ + struct igb_adapter *adapter = netdev_priv(netdev); + struct e1000_hw *hw = &adapter->hw; + u16 *eeprom_buff; + void *ptr; + int max_len, first_word, last_word, ret_val = 0; + struct e1000_nvm_access *nvm; + u32 magic; + u16 i; + + if (eeprom->len == 0) + return -EOPNOTSUPP; + + magic = hw->vendor_id | (hw->device_id << 16); + if (eeprom->magic && eeprom->magic != magic) { + nvm = (struct e1000_nvm_access *)eeprom; + ret_val = igb_nvmupd_command(hw, nvm, bytes); + return ret_val; + } + + /* normal ethtool get_eeprom support */ + if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16))) + return -EFAULT; + + max_len = hw->nvm.word_size * 2; + + first_word = eeprom->offset >> 1; + last_word = (eeprom->offset + eeprom->len - 1) >> 1; + eeprom_buff = kmalloc(max_len, GFP_KERNEL); + if (!eeprom_buff) + return -ENOMEM; + + ptr = (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 = e1000_read_nvm(hw, first_word, 1, + &eeprom_buff[0]); + ptr++; + } + if (((eeprom->offset + eeprom->len) & 1) && (ret_val == 0)) { + /* need read/modify/write of last changed EEPROM word */ + /* only the first byte of the word is being modified */ + ret_val = 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 = 0; i < last_word - first_word + 1; i++) + le16_to_cpus(&eeprom_buff[i]); + + memcpy(ptr, bytes, eeprom->len); + + for (i = 0; i < last_word - first_word + 1; i++) + cpu_to_le16s(&eeprom_buff[i]); + + ret_val = 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 == 0) + e1000_update_nvm_checksum(hw); out: - kfree(eeprom_buff); - return ret_val; + kfree(eeprom_buff); + return ret_val; } static void igb_get_drvinfo(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/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 *adapter) "enabled" : "disabled"); } +u32 e1000_read_reg(struct e1000_hw *hw, u32 reg) +{ + struct igb_adapter *igb = container_of(hw, struct igb_adapter, hw); + u8 __iomem *hw_addr = READ_ONCE(hw->hw_addr); + u32 value = 0; + + if (E1000_REMOVED(hw_addr)) + return ~value; + + value = readl(&hw_addr[reg]); + + /* reads should not return all F's */ + if (!(~value) && (!reg || !(~readl(hw_addr)))) { + struct net_device *netdev = igb->netdev; + + hw->hw_addr = 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 &= ~IGB_FLAG_DMAC; set_bit(__IGB_DOWN, &adapter->state); + + /* NVM Update features structure initialization */ + hw->nvmupd_features.major = E1000_NVMUPD_FEATURES_API_VER_MAJOR; + hw->nvmupd_features.minor = E1000_NVMUPD_FEATURES_API_VER_MINOR; + hw->nvmupd_features.size = 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] = + E1000_NVMUPD_FEATURE_REGISTER_ACCESS_SUPPORT; + return 0; } -- 2.40.1