From 1f2507c5a7e760a5afe835660551c7b80ef65424 Mon Sep 17 00:00:00 2001
From: CentOS Sources <bugs@centos.org>
Date: Sep 27 2022 20:01:33 +0000
Subject: import ethtool-5.13-2.el8


---

diff --git a/SOURCES/0001-sff-8636-Fix-parsing-of-Page-03h-in-IOCTL-path.patch b/SOURCES/0001-sff-8636-Fix-parsing-of-Page-03h-in-IOCTL-path.patch
new file mode 100644
index 0000000..d08ab1e
--- /dev/null
+++ b/SOURCES/0001-sff-8636-Fix-parsing-of-Page-03h-in-IOCTL-path.patch
@@ -0,0 +1,110 @@
+From bb89624c1a62de701f87d7deb669e40586c920d2 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 14 Sep 2021 14:27:34 +0300
+Subject: [PATCH 01/35] sff-8636: Fix parsing of Page 03h in IOCTL path
+
+The offset of Page 03h compared to the base address of the Lower Memory
+is 512 bytes. However, all the offsets to the page start at address 128,
+which is the address that separates Lower and Upper memory (see Figure
+6-1 in SFF-8636). Therefore, reading these offsets compared to the start
+of Page 03h results in incorrect memory accesses as can be seen in the
+output below.
+
+Instead, pass Page 03h with the correct offset.
+
+This is a temporary solution until SFF-8636 is refactored to use a
+memory map for parsing.
+
+Before patch:
+
+ # ethtool -m swp13
+ ...
+ Laser bias current high alarm threshold   : 16.448 mA
+ Laser bias current low alarm threshold    : 16.500 mA
+ Laser bias current high warning threshold : 16.480 mA
+ Laser bias current low warning threshold  : 61.538 mA
+ Laser output power high alarm threshold   : 1.2576 mW / 1.00 dBm
+ Laser output power low alarm threshold    : 1.0321 mW / 0.14 dBm
+ Laser output power high warning threshold : 2.1318 mW / 3.29 dBm
+ Laser output power low warning threshold  : 2.0530 mW / 3.12 dBm
+ Module temperature high alarm threshold   : 0.00 degrees C / 32.00 degrees F
+ Module temperature low alarm threshold    : 0.00 degrees C / 32.00 degrees F
+ Module temperature high warning threshold : 0.00 degrees C / 32.00 degrees F
+ Module temperature low warning threshold  : 0.00 degrees C / 32.00 degrees F
+ Module voltage high alarm threshold       : 0.2377 V
+ Module voltage low alarm threshold        : 2.5701 V
+ Module voltage high warning threshold     : 2.8276 V
+ Module voltage low warning threshold      : 2.6982 V
+ Laser rx power high alarm threshold       : 0.8224 mW / -0.85 dBm
+ Laser rx power low alarm threshold        : 0.8224 mW / -0.85 dBm
+ Laser rx power high warning threshold     : 0.8224 mW / -0.85 dBm
+ Laser rx power low warning threshold      : 0.8224 mW / -0.85 dBm
+
+After patch:
+
+ # ethtool -m swp13
+ ...
+ Laser bias current high alarm threshold   : 8.500 mA
+ Laser bias current low alarm threshold    : 5.492 mA
+ Laser bias current high warning threshold : 8.000 mA
+ Laser bias current low warning threshold  : 6.000 mA
+ Laser output power high alarm threshold   : 3.4673 mW / 5.40 dBm
+ Laser output power low alarm threshold    : 0.0724 mW / -11.40 dBm
+ Laser output power high warning threshold : 1.7378 mW / 2.40 dBm
+ Laser output power low warning threshold  : 0.1445 mW / -8.40 dBm
+ Module temperature high alarm threshold   : 80.00 degrees C / 176.00 degrees F
+ Module temperature low alarm threshold    : -10.00 degrees C / 14.00 degrees F
+ Module temperature high warning threshold : 70.00 degrees C / 158.00 degrees F
+ Module temperature low warning threshold  : 0.00 degrees C / 32.00 degrees F
+ Module voltage high alarm threshold       : 3.5000 V
+ Module voltage low alarm threshold        : 3.1000 V
+ Module voltage high warning threshold     : 3.4650 V
+ Module voltage low warning threshold      : 3.1350 V
+ Laser rx power high alarm threshold       : 3.4673 mW / 5.40 dBm
+ Laser rx power low alarm threshold        : 0.0467 mW / -13.31 dBm
+ Laser rx power high warning threshold     : 1.7378 mW / 2.40 dBm
+ Laser rx power low warning threshold      : 0.0933 mW / -10.30 dBm
+
+The following AddressSanitizer report is fixed:
+
+==44670==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x617000000320 at pc 0x00000047ad93 bp 0x7ffcb4dc0070 sp 0x7ffcb4dc0068
+READ of size 1 at 0x617000000320 thread T0
+    #0 0x47ad92 in sff8636_dom_parse qsfp.c:683
+    #1 0x47c5d6 in sff8636_show_dom qsfp.c:771
+    #2 0x47d21f in sff8636_show_all qsfp.c:870
+    #3 0x42130b in do_getmodule ethtool.c:4908
+    #4 0x42a38a in main ethtool.c:6383
+    #5 0x7f500bf421e1 in __libc_start_main (/lib64/libc.so.6+0x281e1)
+    #6 0x40258d in _start (ethtool+0x40258d)
+
+0x617000000320 is located 16 bytes to the right of 656-byte region [0x617000000080,0x617000000310)
+allocated by thread T0 here:
+    #0 0x7f500c2d6527 in __interceptor_calloc (/lib64/libasan.so.6+0xab527)
+    #1 0x420d8c in do_getmodule ethtool.c:4859
+    #2 0x42a38a in main ethtool.c:6383
+    #3 0x7f500bf421e1 in __libc_start_main (/lib64/libc.so.6+0x281e1)
+
+SUMMARY: AddressSanitizer: heap-buffer-overflow qsfp.c:683 in sff8636_dom_parse
+
+Fixes: fc47fdb7c364 ("ethtool: Refactor human-readable module EEPROM output for new API")
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ qsfp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/qsfp.c b/qsfp.c
+index 644fe148a5aa..e84226bc1554 100644
+--- a/qsfp.c
++++ b/qsfp.c
+@@ -867,7 +867,7 @@ void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
+ 		(id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_PLUS) ||
+ 		(id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP28)) {
+ 		sff6836_show_page_zero(id);
+-		sff8636_show_dom(id, id + SFF8636_PAGE03H_OFFSET, eeprom_len);
++		sff8636_show_dom(id, id + 3 * 0x80, eeprom_len);
+ 	}
+ }
+ 
+-- 
+2.35.1
+
diff --git a/SOURCES/0002-cmis-Fix-invalid-memory-access-in-IOCTL-path.patch b/SOURCES/0002-cmis-Fix-invalid-memory-access-in-IOCTL-path.patch
new file mode 100644
index 0000000..4be2b97
--- /dev/null
+++ b/SOURCES/0002-cmis-Fix-invalid-memory-access-in-IOCTL-path.patch
@@ -0,0 +1,55 @@
+From 1c14a6d8ebad07bc6ff090164ca15ab7656e7167 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 14 Sep 2021 14:27:35 +0300
+Subject: [PATCH 02/35] cmis: Fix invalid memory access in IOCTL path
+
+Page 01h is an optional page that is not available for flat memory
+modules. Trying to blindly access it results in the following report
+from AddressSanitizer [1].
+
+Instead, pass the base address of the Lower Memory. This results in
+wrong information being parsed, but this never worked correctly since
+CMIS support first appeared in cited commit.
+
+The information will be parsed correctly in a follow-up submission that
+reworks the EEPROM parsing code to use a memory map with pointers to
+individual pages instead of passing one large buffer.
+
+[1]
+==968785==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6120000001d4 at pc 0x0000004806ee bp 0x7ffefbc977a0 sp 0x7ffefbc97798
+READ of size 1 at 0x6120000001d4 thread T0
+    #0 0x4806ed in cmis_print_smf_cbl_len cmis.c:127
+    #1 0x48113e in cmis_show_link_len_from_page cmis.c:279
+    #2 0x4811e3 in cmis_show_link_len cmis.c:300
+    #3 0x481358 in qsfp_dd_show_all cmis.c:336
+    #4 0x47d190 in sff8636_show_all qsfp.c:861
+    #5 0x42130b in do_getmodule ethtool.c:4908
+    #6 0x42a38a in main ethtool.c:6383
+    #7 0x7f11db6c51e1 in __libc_start_main (/lib64/libc.so.6+0x281e1)
+    #8 0x40258d in _start (ethtool+0x40258d)
+
+Address 0x6120000001d4 is a wild pointer.
+SUMMARY: AddressSanitizer: heap-buffer-overflow cmis.c:127 in cmis_print_smf_cbl_len
+
+Fixes: 88ca347ef35a ("Add QSFP-DD support").
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ cmis.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/cmis.c b/cmis.c
+index 361b721f332f..1a91e798e4b8 100644
+--- a/cmis.c
++++ b/cmis.c
+@@ -297,7 +297,7 @@ static void cmis_show_link_len_from_page(const __u8 *page_one_data)
+  */
+ static void cmis_show_link_len(const __u8 *id)
+ {
+-	cmis_show_link_len_from_page(id + PAG01H_UPPER_OFFSET);
++	cmis_show_link_len_from_page(id);
+ }
+ 
+ /**
+-- 
+2.35.1
+
diff --git a/SOURCES/0003-netlink-eeprom-Fallback-to-IOCTL-when-a-complete-hex.patch b/SOURCES/0003-netlink-eeprom-Fallback-to-IOCTL-when-a-complete-hex.patch
new file mode 100644
index 0000000..1122e32
--- /dev/null
+++ b/SOURCES/0003-netlink-eeprom-Fallback-to-IOCTL-when-a-complete-hex.patch
@@ -0,0 +1,87 @@
+From 04d36d7c373db7069554a6d21ece628e2cf6b21c Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 14 Sep 2021 14:27:36 +0300
+Subject: [PATCH 03/35] netlink: eeprom: Fallback to IOCTL when a complete
+ hex/raw dump is requested
+
+The IOCTL backend provides a complete hex/raw dump of the module EEPROM
+contents:
+
+ # ethtool -m swp11 hex on | wc -l
+ 34
+
+ # ethtool -m swp11 raw on | wc -c
+ 512
+
+With the netlink backend, only the first 128 bytes from I2C address 0x50
+are dumped:
+
+ # ethtool -m swp11 hex on | wc -l
+ 10
+
+ # ethtool -m swp11 raw on | wc -c
+ 128
+
+The presence of optional / banked pages is unknown without parsing the
+EEPROM contents which is unavailable when pretty printing is disabled
+(i.e., configure --disable-pretty-dump). With the IOCTL backend, this
+parsing happens inside the kernel.
+
+Therefore, when a complete hex/raw dump is requested, fallback to the
+IOCTL backend.
+
+After the patch:
+
+ # ethtool -m swp11 hex on | wc -l
+ 34
+
+ # ethtool -m swp11 raw on | wc -c
+ 512
+
+This avoids breaking users that are relying on current behavior.
+
+If users want a hex/raw dump of optional/banked pages that are not
+returned with the IOCTL backend, they will be required to request these
+explicitly via the netlink backend. For example:
+
+ # ethtool -m swp11 hex on page 0x2
+
+This is desirable as that way there is no ambiguity regarding the
+location of optional/banked pages in the dump.
+
+Another way to implement the above would be to use the 'nlchk' callback
+added in commit 67a9ef551661 ("ethtool: add nlchk for redirecting to
+netlink"). However, it is called before the netlink instance is
+initialized and before the command line parameters are parsed via
+nl_parser().
+
+Fixes: 25b64c66f58d ("ethtool: Add netlink handler for getmodule (-m)")
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ netlink/module-eeprom.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c
+index 38e7d2cd6cf3..e9a122df3259 100644
+--- a/netlink/module-eeprom.c
++++ b/netlink/module-eeprom.c
+@@ -365,6 +365,16 @@ int nl_getmodule(struct cmd_context *ctx)
+ 		return -EINVAL;
+ 	}
+ 
++	/* When complete hex/raw dump of the EEPROM is requested, fallback to
++	 * ioctl. Netlink can only request specific pages.
++	 */
++	if ((getmodule_cmd_params.dump_hex || getmodule_cmd_params.dump_raw) &&
++	    !getmodule_cmd_params.page && !getmodule_cmd_params.bank &&
++	    !getmodule_cmd_params.i2c_address) {
++		nlctx->ioctl_fallback = true;
++		return -EOPNOTSUPP;
++	}
++
+ 	request.i2c_address = ETH_I2C_ADDRESS_LOW;
+ 	request.length = 128;
+ 	ret = page_fetch(nlctx, &request);
+-- 
+2.35.1
+
diff --git a/SOURCES/0004-ethtool-Fix-compilation-warning-when-pretty-dump-is-.patch b/SOURCES/0004-ethtool-Fix-compilation-warning-when-pretty-dump-is-.patch
new file mode 100644
index 0000000..050bd91
--- /dev/null
+++ b/SOURCES/0004-ethtool-Fix-compilation-warning-when-pretty-dump-is-.patch
@@ -0,0 +1,84 @@
+From 3960c91ade7b1ca9979eec5200a90dc11f339cfd Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 14 Sep 2021 14:27:37 +0300
+Subject: [PATCH 04/35] ethtool: Fix compilation warning when pretty dump is
+ disabled
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When pretty dump is disabled (i.e., configure --disable-pretty-dump),
+gcc 11.2.1 emits the following warning:
+
+ethtool.c: In function ‘dump_regs’:
+ethtool.c:1160:31: warning: comparison is always false due to limited range of data type [-Wtype-limits]
+ 1160 |                 for (i = 0; i < ARRAY_SIZE(driver_list); i++)
+      |                               ^
+
+Fix it by avoiding iterating over 'driver_list' when pretty dump is
+disabled.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ ethtool.c | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/ethtool.c b/ethtool.c
+index 33a0a492cb15..1b79e9f8d958 100644
+--- a/ethtool.c
++++ b/ethtool.c
+@@ -1089,12 +1089,12 @@ static int parse_hkey(char **rss_hkey, u32 key_size,
+ 	return 0;
+ }
+ 
++#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
+ static const struct {
+ 	const char *name;
+ 	int (*func)(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+ 
+ } driver_list[] = {
+-#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
+ 	{ "8139cp", realtek_dump_regs },
+ 	{ "8139too", realtek_dump_regs },
+ 	{ "r8169", realtek_dump_regs },
+@@ -1129,8 +1129,8 @@ static const struct {
+ 	{ "fec", fec_dump_regs },
+ 	{ "igc", igc_dump_regs },
+ 	{ "bnxt_en", bnxt_dump_regs },
+-#endif
+ };
++#endif
+ 
+ void dump_hex(FILE *file, const u8 *data, int len, int offset)
+ {
+@@ -1149,14 +1149,15 @@ void dump_hex(FILE *file, const u8 *data, int len, int offset)
+ static int dump_regs(int gregs_dump_raw, int gregs_dump_hex,
+ 		     struct ethtool_drvinfo *info, struct ethtool_regs *regs)
+ {
+-	unsigned int i;
+-
+ 	if (gregs_dump_raw) {
+ 		fwrite(regs->data, regs->len, 1, stdout);
+ 		goto nested;
+ 	}
+ 
+-	if (!gregs_dump_hex)
++#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
++	if (!gregs_dump_hex) {
++		unsigned int i;
++
+ 		for (i = 0; i < ARRAY_SIZE(driver_list); i++)
+ 			if (!strncmp(driver_list[i].name, info->driver,
+ 				     ETHTOOL_BUSINFO_LEN)) {
+@@ -1168,6 +1169,8 @@ static int dump_regs(int gregs_dump_raw, int gregs_dump_hex,
+ 				 */
+ 				break;
+ 			}
++	}
++#endif
+ 
+ 	dump_hex(stdout, regs->data, regs->len, 0);
+ 
+-- 
+2.35.1
+
diff --git a/SOURCES/0005-netlink-eeprom-Fix-compilation-when-pretty-dump-is-d.patch b/SOURCES/0005-netlink-eeprom-Fix-compilation-when-pretty-dump-is-d.patch
new file mode 100644
index 0000000..e5f1b9a
--- /dev/null
+++ b/SOURCES/0005-netlink-eeprom-Fix-compilation-when-pretty-dump-is-d.patch
@@ -0,0 +1,62 @@
+From 08d9f72f5e4ce12e2cc1fc47d1ffde9aa1326c8c Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 14 Sep 2021 14:27:38 +0300
+Subject: [PATCH 05/35] netlink: eeprom: Fix compilation when pretty dump is
+ disabled
+
+When pretty dump is disabled (i.e., configure --disable-pretty-dump),
+the following errors are emitted:
+
+/usr/bin/ld: netlink/module-eeprom.o: in function `decoder_print':
+netlink/module-eeprom.c:330: undefined reference to `sff8636_show_all_paged'
+netlink/module-eeprom.c:334: undefined reference to `cmis_show_all'
+netlink/module-eeprom.c:325: undefined reference to `sff8079_show_all'
+
+The else clause is unreachable when pretty dump is disabled, so wrap it
+with ifdef directive.
+
+This will be re-worked in future patches where the netlink code only
+queries the SFF-8024 Identifier Value and defers page requests to
+individual parsers.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ netlink/module-eeprom.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c
+index e9a122df3259..48cd2cc55bee 100644
+--- a/netlink/module-eeprom.c
++++ b/netlink/module-eeprom.c
+@@ -275,6 +275,7 @@ static int page_fetch(struct nl_context *nlctx, const struct ethtool_module_eepr
+ 	return nlsock_process_reply(nlsock, nomsg_reply_cb, NULL);
+ }
+ 
++#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
+ static int decoder_prefetch(struct nl_context *nlctx)
+ {
+ 	struct ethtool_module_eeprom *page_zero_lower = cache_get(0, 0, ETH_I2C_ADDRESS_LOW);
+@@ -338,6 +339,7 @@ static void decoder_print(void)
+ 		break;
+ 	}
+ }
++#endif
+ 
+ int nl_getmodule(struct cmd_context *ctx)
+ {
+@@ -414,10 +416,12 @@ int nl_getmodule(struct cmd_context *ctx)
+ 		else
+ 			dump_hex(stdout, eeprom_data, dump_length, request.offset);
+ 	} else {
++#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
+ 		ret = decoder_prefetch(nlctx);
+ 		if (ret)
+ 			goto cleanup;
+ 		decoder_print();
++#endif
+ 	}
+ 
+ cleanup:
+-- 
+2.35.1
+
diff --git a/SOURCES/0006-cmis-Fix-CLEI-code-parsing.patch b/SOURCES/0006-cmis-Fix-CLEI-code-parsing.patch
new file mode 100644
index 0000000..826fc0f
--- /dev/null
+++ b/SOURCES/0006-cmis-Fix-CLEI-code-parsing.patch
@@ -0,0 +1,66 @@
+From 5b46ca06c9888c663a74bdd804b0ecb7199cfb62 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Fri, 1 Oct 2021 18:06:21 +0300
+Subject: [PATCH 06/35] cmis: Fix CLEI code parsing
+
+In CMIS, unlike SFF-8636, there is no presence indication for the CLEI
+code (Common Language Equipment Identification) field. The field is
+always present, but might not be supported. In which case, "a value of
+all ASCII 20h (spaces) shall be entered".
+
+Therefore, remove the erroneous check which seems to be influenced from
+SFF-8636 and only print the string if it is supported and has a non-zero
+length.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ cmis.c | 8 +++++---
+ cmis.h | 4 ++--
+ 2 files changed, 7 insertions(+), 5 deletions(-)
+
+diff --git a/cmis.c b/cmis.c
+index 1a91e798e4b8..499355d0e024 100644
+--- a/cmis.c
++++ b/cmis.c
+@@ -307,6 +307,8 @@ static void cmis_show_link_len(const __u8 *id)
+  */
+ static void cmis_show_vendor_info(const __u8 *id)
+ {
++	const char *clei = (const char *)(id + CMIS_CLEI_START_OFFSET);
++
+ 	sff_show_ascii(id, CMIS_VENDOR_NAME_START_OFFSET,
+ 		       CMIS_VENDOR_NAME_END_OFFSET, "Vendor name");
+ 	cmis_show_oui(id);
+@@ -319,9 +321,9 @@ static void cmis_show_vendor_info(const __u8 *id)
+ 	sff_show_ascii(id, CMIS_DATE_YEAR_OFFSET,
+ 		       CMIS_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
+ 
+-	if (id[CMIS_CLEI_PRESENT_BYTE] & CMIS_CLEI_PRESENT_MASK)
+-		sff_show_ascii(id, CMIS_CLEI_START_OFFSET,
+-			       CMIS_CLEI_END_OFFSET, "CLEI code");
++	if (*clei && strncmp(clei, CMIS_CLEI_BLANK, CMIS_CLEI_LEN))
++		sff_show_ascii(id, CMIS_CLEI_START_OFFSET, CMIS_CLEI_END_OFFSET,
++			       "CLEI code");
+ }
+ 
+ void qsfp_dd_show_all(const __u8 *id)
+diff --git a/cmis.h b/cmis.h
+index 78ee1495bc33..cfac08f42904 100644
+--- a/cmis.h
++++ b/cmis.h
+@@ -34,10 +34,10 @@
+ #define CMIS_DATE_VENDOR_LOT_OFFSET		0xBC
+ 
+ /* CLEI Code (Page 0) */
+-#define CMIS_CLEI_PRESENT_BYTE			0x02
+-#define CMIS_CLEI_PRESENT_MASK			0x20
+ #define CMIS_CLEI_START_OFFSET			0xBE
+ #define CMIS_CLEI_END_OFFSET			0xC7
++#define CMIS_CLEI_BLANK				"          "
++#define CMIS_CLEI_LEN				0x0A
+ 
+ /* Cable assembly length */
+ #define CMIS_CBL_ASM_LEN_OFFSET			0xCA
+-- 
+2.35.1
+
diff --git a/SOURCES/0007-cmis-Fix-wrong-define-name.patch b/SOURCES/0007-cmis-Fix-wrong-define-name.patch
new file mode 100644
index 0000000..cbd337d
--- /dev/null
+++ b/SOURCES/0007-cmis-Fix-wrong-define-name.patch
@@ -0,0 +1,42 @@
+From a934091a0b42cd7c71c9e71a235e57af718c9952 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Fri, 1 Oct 2021 18:06:22 +0300
+Subject: [PATCH 07/35] cmis: Fix wrong define name
+
+Offset 0x10 in the Lower Memory stores the "VccMonVoltage".
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ cmis.c | 2 +-
+ cmis.h | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/cmis.c b/cmis.c
+index 499355d0e024..408db6f26c3b 100644
+--- a/cmis.c
++++ b/cmis.c
+@@ -271,7 +271,7 @@ static void cmis_show_mod_lvl_monitors(const __u8 *id)
+ 	PRINT_TEMP("Module temperature",
+ 		   OFFSET_TO_TEMP(CMIS_CURR_TEMP_OFFSET));
+ 	PRINT_VCC("Module voltage",
+-		  OFFSET_TO_U16(CMIS_CURR_CURR_OFFSET));
++		  OFFSET_TO_U16(CMIS_CURR_VCC_OFFSET));
+ }
+ 
+ static void cmis_show_link_len_from_page(const __u8 *page_one_data)
+diff --git a/cmis.h b/cmis.h
+index cfac08f42904..e3012ccfdd79 100644
+--- a/cmis.h
++++ b/cmis.h
+@@ -11,7 +11,7 @@
+ 
+ /* Module-Level Monitors (Page 0) */
+ #define CMIS_CURR_TEMP_OFFSET			0x0E
+-#define CMIS_CURR_CURR_OFFSET			0x10
++#define CMIS_CURR_VCC_OFFSET			0x10
+ 
+ #define CMIS_CTOR_OFFSET			0xCB
+ 
+-- 
+2.35.1
+
diff --git a/SOURCES/0008-cmis-Correct-comment.patch b/SOURCES/0008-cmis-Correct-comment.patch
new file mode 100644
index 0000000..8f163be
--- /dev/null
+++ b/SOURCES/0008-cmis-Correct-comment.patch
@@ -0,0 +1,29 @@
+From 030ba06f12761d13722a12773bb8063748242b50 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Fri, 1 Oct 2021 18:06:23 +0300
+Subject: [PATCH 08/35] cmis: Correct comment
+
+The file is concerned with CMIS support, not QSFP-DD which is the
+physical form factor.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ cmis.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/cmis.c b/cmis.c
+index 408db6f26c3b..591cc72953b7 100644
+--- a/cmis.c
++++ b/cmis.c
+@@ -1,7 +1,7 @@
+ /**
+  * Description:
+  *
+- * This module adds QSFP-DD support to ethtool. The changes are similar to
++ * This module adds CMIS support to ethtool. The changes are similar to
+  * the ones already existing in qsfp.c, but customized to use the memory
+  * addresses and logic as defined in the specification's document.
+  *
+-- 
+2.35.1
+
diff --git a/SOURCES/0009-sff-8636-Remove-incorrect-comment.patch b/SOURCES/0009-sff-8636-Remove-incorrect-comment.patch
new file mode 100644
index 0000000..9e6145c
--- /dev/null
+++ b/SOURCES/0009-sff-8636-Remove-incorrect-comment.patch
@@ -0,0 +1,30 @@
+From f1b4bafbb30e77fedb10a23d1b70119b9059f684 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Fri, 1 Oct 2021 18:06:24 +0300
+Subject: [PATCH 09/35] sff-8636: Remove incorrect comment
+
+The comment was copied from SFF-8472 (i.e., sfpdiag.c) where the
+diagnostic page is at I2C address 0x51. SFF-8636 only uses I2C address
+0x50.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ qsfp.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/qsfp.c b/qsfp.c
+index e84226bc1554..263cf188377d 100644
+--- a/qsfp.c
++++ b/qsfp.c
+@@ -64,7 +64,7 @@
+ 
+ static struct sff8636_aw_flags {
+ 	const char *str;        /* Human-readable string, null at the end */
+-	int offset;             /* A2-relative address offset */
++	int offset;
+ 	__u8 value;             /* Alarm is on if (offset & value) != 0. */
+ } sff8636_aw_flags[] = {
+ 	{ "Laser bias current high alarm   (Chan 1)",
+-- 
+2.35.1
+
diff --git a/SOURCES/0010-sff-8636-Fix-incorrect-function-name.patch b/SOURCES/0010-sff-8636-Fix-incorrect-function-name.patch
new file mode 100644
index 0000000..d97a379
--- /dev/null
+++ b/SOURCES/0010-sff-8636-Fix-incorrect-function-name.patch
@@ -0,0 +1,48 @@
+From dc4a75242e8674da02a579e6c875ad4ac77a8c20 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Fri, 1 Oct 2021 18:06:25 +0300
+Subject: [PATCH 10/35] sff-8636: Fix incorrect function name
+
+The specification is called SFF-8636, not SFF-6836.
+
+Rename the function accordingly.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ qsfp.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/qsfp.c b/qsfp.c
+index 263cf188377d..3401db84352d 100644
+--- a/qsfp.c
++++ b/qsfp.c
+@@ -820,7 +820,7 @@ static void sff8636_show_dom(const __u8 *id, const __u8 *page_three, __u32 eepro
+ }
+ 
+ 
+-static void sff6836_show_page_zero(const __u8 *id)
++static void sff8636_show_page_zero(const __u8 *id)
+ {
+ 	sff8636_show_ext_identifier(id);
+ 	sff8636_show_connector(id);
+@@ -866,7 +866,7 @@ void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
+ 	if ((id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP) ||
+ 		(id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_PLUS) ||
+ 		(id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP28)) {
+-		sff6836_show_page_zero(id);
++		sff8636_show_page_zero(id);
+ 		sff8636_show_dom(id, id + 3 * 0x80, eeprom_len);
+ 	}
+ }
+@@ -875,7 +875,7 @@ void sff8636_show_all_paged(const struct ethtool_module_eeprom *page_zero,
+ 			    const struct ethtool_module_eeprom *page_three)
+ {
+ 	sff8636_show_identifier(page_zero->data);
+-	sff6836_show_page_zero(page_zero->data);
++	sff8636_show_page_zero(page_zero->data);
+ 	if (page_three)
+ 		sff8636_show_dom(page_zero->data, page_three->data - 0x80,
+ 				 ETH_MODULE_SFF_8636_MAX_LEN);
+-- 
+2.35.1
+
diff --git a/SOURCES/0011-sff-8636-Convert-if-statement-to-switch-case.patch b/SOURCES/0011-sff-8636-Convert-if-statement-to-switch-case.patch
new file mode 100644
index 0000000..6bad605
--- /dev/null
+++ b/SOURCES/0011-sff-8636-Convert-if-statement-to-switch-case.patch
@@ -0,0 +1,37 @@
+From 9d6316a62b6ab24dfd3cb800841df7fbbdc648ae Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Fri, 1 Oct 2021 18:06:26 +0300
+Subject: [PATCH 11/35] sff-8636: Convert if statement to switch-case
+
+The indentation is wrong and the statement can be more clearly
+represented using a switch-case statement. Convert it.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ qsfp.c | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/qsfp.c b/qsfp.c
+index 3401db84352d..d1464cb50fdc 100644
+--- a/qsfp.c
++++ b/qsfp.c
+@@ -863,11 +863,13 @@ void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
+ 	}
+ 
+ 	sff8636_show_identifier(id);
+-	if ((id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP) ||
+-		(id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_PLUS) ||
+-		(id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP28)) {
++	switch (id[SFF8636_ID_OFFSET]) {
++	case SFF8024_ID_QSFP:
++	case SFF8024_ID_QSFP_PLUS:
++	case SFF8024_ID_QSFP28:
+ 		sff8636_show_page_zero(id);
+ 		sff8636_show_dom(id, id + 3 * 0x80, eeprom_len);
++		break;
+ 	}
+ }
+ 
+-- 
+2.35.1
+
diff --git a/SOURCES/0012-sff-8636-Remove-extra-blank-lines.patch b/SOURCES/0012-sff-8636-Remove-extra-blank-lines.patch
new file mode 100644
index 0000000..e52f6a7
--- /dev/null
+++ b/SOURCES/0012-sff-8636-Remove-extra-blank-lines.patch
@@ -0,0 +1,35 @@
+From 539a255fab8401a197e2a98c0c3dd39800ca65e5 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Fri, 1 Oct 2021 18:06:27 +0300
+Subject: [PATCH 12/35] sff-8636: Remove extra blank lines
+
+Not needed, so remove them.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ qsfp.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/qsfp.c b/qsfp.c
+index d1464cb50fdc..3f37f1036e96 100644
+--- a/qsfp.c
++++ b/qsfp.c
+@@ -738,7 +738,6 @@ static void sff8636_dom_parse(const __u8 *id, const __u8 *page_three, struct sff
+ 		sd->scd[i].rx_power = OFFSET_TO_U16(rx_power_offset);
+ 		sd->scd[i].tx_power = OFFSET_TO_U16(tx_power_offset);
+ 	}
+-
+ }
+ 
+ static void sff8636_show_dom(const __u8 *id, const __u8 *page_three, __u32 eeprom_len)
+@@ -819,7 +818,6 @@ static void sff8636_show_dom(const __u8 *id, const __u8 *page_three, __u32 eepro
+ 	}
+ }
+ 
+-
+ static void sff8636_show_page_zero(const __u8 *id)
+ {
+ 	sff8636_show_ext_identifier(id);
+-- 
+2.35.1
+
diff --git a/SOURCES/0013-cmis-Rename-CMIS-parsing-functions.patch b/SOURCES/0013-cmis-Rename-CMIS-parsing-functions.patch
new file mode 100644
index 0000000..6974ac8
--- /dev/null
+++ b/SOURCES/0013-cmis-Rename-CMIS-parsing-functions.patch
@@ -0,0 +1,95 @@
+From 56c6dc7ab5f9170c6d399c12a87bbdb4c8de8958 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 12 Oct 2021 16:25:12 +0300
+Subject: [PATCH 13/35] cmis: Rename CMIS parsing functions
+
+Currently, there are two CMIS parsing functions. qsfp_dd_show_all() and
+cmis_show_all(). The former is called from the IOCTL path with a buffer
+containing EEPROM contents and the latter is called from the netlink
+path with pointer to individual EEPROM pages.
+
+Rename them with '_ioctl' and '_nl' suffixes to make the distinction
+clear.
+
+In subsequent patches, these two functions will only differ in the way
+they initialize the CMIS memory map for parsing, while the parsing code
+itself will be shared between the two.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ cmis.c                  | 6 +++---
+ cmis.h                  | 6 +++---
+ netlink/module-eeprom.c | 2 +-
+ qsfp.c                  | 2 +-
+ 4 files changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/cmis.c b/cmis.c
+index 591cc72953b7..68c5b2d3277b 100644
+--- a/cmis.c
++++ b/cmis.c
+@@ -326,7 +326,7 @@ static void cmis_show_vendor_info(const __u8 *id)
+ 			       "CLEI code");
+ }
+ 
+-void qsfp_dd_show_all(const __u8 *id)
++void cmis_show_all_ioctl(const __u8 *id)
+ {
+ 	cmis_show_identifier(id);
+ 	cmis_show_power_info(id);
+@@ -340,8 +340,8 @@ void qsfp_dd_show_all(const __u8 *id)
+ 	cmis_show_rev_compliance(id);
+ }
+ 
+-void cmis_show_all(const struct ethtool_module_eeprom *page_zero,
+-		   const struct ethtool_module_eeprom *page_one)
++void cmis_show_all_nl(const struct ethtool_module_eeprom *page_zero,
++		      const struct ethtool_module_eeprom *page_one)
+ {
+ 	const __u8 *page_zero_data = page_zero->data;
+ 
+diff --git a/cmis.h b/cmis.h
+index e3012ccfdd79..734b90f4ddb4 100644
+--- a/cmis.h
++++ b/cmis.h
+@@ -120,9 +120,9 @@
+ #define YESNO(x) (((x) != 0) ? "Yes" : "No")
+ #define ONOFF(x) (((x) != 0) ? "On" : "Off")
+ 
+-void qsfp_dd_show_all(const __u8 *id);
++void cmis_show_all_ioctl(const __u8 *id);
+ 
+-void cmis_show_all(const struct ethtool_module_eeprom *page_zero,
+-		   const struct ethtool_module_eeprom *page_one);
++void cmis_show_all_nl(const struct ethtool_module_eeprom *page_zero,
++		      const struct ethtool_module_eeprom *page_one);
+ 
+ #endif /* CMIS_H__ */
+diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c
+index 48cd2cc55bee..fc4ef1a53aff 100644
+--- a/netlink/module-eeprom.c
++++ b/netlink/module-eeprom.c
+@@ -332,7 +332,7 @@ static void decoder_print(void)
+ 		break;
+ 	case SFF8024_ID_QSFP_DD:
+ 	case SFF8024_ID_DSFP:
+-		cmis_show_all(page_zero, page_one);
++		cmis_show_all_nl(page_zero, page_one);
+ 		break;
+ 	default:
+ 		dump_hex(stdout, page_zero->data, page_zero->length, page_zero->offset);
+diff --git a/qsfp.c b/qsfp.c
+index 3f37f1036e96..27fdd3bd1771 100644
+--- a/qsfp.c
++++ b/qsfp.c
+@@ -856,7 +856,7 @@ static void sff8636_show_page_zero(const __u8 *id)
+ void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
+ {
+ 	if (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_DD) {
+-		qsfp_dd_show_all(id);
++		cmis_show_all_ioctl(id);
+ 		return;
+ 	}
+ 
+-- 
+2.35.1
+
diff --git a/SOURCES/0014-cmis-Initialize-CMIS-memory-map.patch b/SOURCES/0014-cmis-Initialize-CMIS-memory-map.patch
new file mode 100644
index 0000000..86cd1d2
--- /dev/null
+++ b/SOURCES/0014-cmis-Initialize-CMIS-memory-map.patch
@@ -0,0 +1,145 @@
+From 912115ebf8ca6eb76dfdadbe8881b7c348743f27 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 12 Oct 2021 16:25:13 +0300
+Subject: [PATCH 14/35] cmis: Initialize CMIS memory map
+
+The CMIS memory map [1] consists of Lower Memory and Upper Memory.
+
+The content of the Lower Memory is fixed and can be addressed using an
+offset between 0 and 127 (inclusive).
+
+The Upper Memory is variable and optional and can be addressed by
+specifying a bank number, a page number and an offset between 128 and
+255 (inclusive).
+
+Create a structure describing this memory map and initialize it with
+pointers to available pages.
+
+In the IOCTL path, the structure holds pointers to regions of the
+continuous buffer passed to user space via the 'ETHTOOL_GMODULEEEPROM'
+command.
+
+In the netlink path, the structure holds pointers to individual pages
+passed to user space via the 'MODULE_EEPROM_GET' message.
+
+This structure will later allow us to consolidate the IOCTL and netlink
+parsing code paths and also easily support additional EEPROM pages.
+
+[1] CMIS Rev. 5, pag. 97, section 8.1.1, Figure 8-1
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ cmis.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ cmis.h |  2 ++
+ 2 files changed, 65 insertions(+)
+
+diff --git a/cmis.c b/cmis.c
+index 68c5b2d3277b..8a6788416a00 100644
+--- a/cmis.c
++++ b/cmis.c
+@@ -13,6 +13,15 @@
+ #include "sff-common.h"
+ #include "cmis.h"
+ 
++struct cmis_memory_map {
++	const __u8 *lower_memory;
++	const __u8 *upper_memory[1][2];	/* Bank, Page */
++#define page_00h upper_memory[0x0][0x0]
++#define page_01h upper_memory[0x0][0x1]
++};
++
++#define CMIS_PAGE_SIZE		0x80
++
+ static void cmis_show_identifier(const __u8 *id)
+ {
+ 	sff8024_show_identifier(id, CMIS_ID_OFFSET);
+@@ -326,8 +335,34 @@ static void cmis_show_vendor_info(const __u8 *id)
+ 			       "CLEI code");
+ }
+ 
++static void cmis_memory_map_init_buf(struct cmis_memory_map *map,
++				     const __u8 *id)
++{
++	/* Lower Memory and Page 00h are always present.
++	 *
++	 * Offset into Upper Memory is between page size and twice the page
++	 * size. Therefore, set the base address of each page to base address
++	 * plus page size multiplied by the page number.
++	 */
++	map->lower_memory = id;
++	map->page_00h = id;
++
++	/* Page 01h is only present when the module memory model is paged and
++	 * not flat.
++	 */
++	if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] &
++	    CMIS_MEMORY_MODEL_MASK)
++		return;
++
++	map->page_01h = id + CMIS_PAGE_SIZE;
++}
++
+ void cmis_show_all_ioctl(const __u8 *id)
+ {
++	struct cmis_memory_map map = {};
++
++	cmis_memory_map_init_buf(&map, id);
++
+ 	cmis_show_identifier(id);
+ 	cmis_show_power_info(id);
+ 	cmis_show_connector(id);
+@@ -340,10 +375,38 @@ void cmis_show_all_ioctl(const __u8 *id)
+ 	cmis_show_rev_compliance(id);
+ }
+ 
++static void
++cmis_memory_map_init_pages(struct cmis_memory_map *map,
++			   const struct ethtool_module_eeprom *page_zero,
++			   const struct ethtool_module_eeprom *page_one)
++{
++	/* Lower Memory and Page 00h are always present.
++	 *
++	 * Offset into Upper Memory is between page size and twice the page
++	 * size. Therefore, set the base address of each page to its base
++	 * address minus page size. For Page 00h, this is the address of the
++	 * Lower Memory.
++	 */
++	map->lower_memory = page_zero->data;
++	map->page_00h = page_zero->data;
++
++	/* Page 01h is only present when the module memory model is paged and
++	 * not flat.
++	 */
++	if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] &
++	    CMIS_MEMORY_MODEL_MASK)
++		return;
++
++	map->page_01h = page_one->data - CMIS_PAGE_SIZE;
++}
++
+ void cmis_show_all_nl(const struct ethtool_module_eeprom *page_zero,
+ 		      const struct ethtool_module_eeprom *page_one)
+ {
+ 	const __u8 *page_zero_data = page_zero->data;
++	struct cmis_memory_map map = {};
++
++	cmis_memory_map_init_pages(&map, page_zero, page_one);
+ 
+ 	cmis_show_identifier(page_zero_data);
+ 	cmis_show_power_info(page_zero_data);
+diff --git a/cmis.h b/cmis.h
+index 734b90f4ddb4..53cbb5f57127 100644
+--- a/cmis.h
++++ b/cmis.h
+@@ -4,6 +4,8 @@
+ /* Identifier and revision compliance (Page 0) */
+ #define CMIS_ID_OFFSET				0x00
+ #define CMIS_REV_COMPLIANCE_OFFSET		0x01
++#define CMIS_MEMORY_MODEL_OFFSET		0x02
++#define CMIS_MEMORY_MODEL_MASK			0x80
+ 
+ #define CMIS_MODULE_TYPE_OFFSET			0x55
+ #define CMIS_MT_MMF				0x01
+-- 
+2.35.1
+
diff --git a/SOURCES/0015-cmis-Use-memory-map-during-parsing.patch b/SOURCES/0015-cmis-Use-memory-map-during-parsing.patch
new file mode 100644
index 0000000..b4f5122
--- /dev/null
+++ b/SOURCES/0015-cmis-Use-memory-map-during-parsing.patch
@@ -0,0 +1,394 @@
+From 6e6aed12948d2d191660252a4be9bb33dc283bed Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 12 Oct 2021 16:25:14 +0300
+Subject: [PATCH 15/35] cmis: Use memory map during parsing
+
+Instead of passing one large buffer to the individual parsing functions,
+use the memory map structure from the previous patch.
+
+This has the added benefit of checking which optional pages are actually
+available and it will also allow us to consolidate the IOCTL and netlink
+parsing code paths.
+
+Tested by making sure that the only differences in output in both the
+IOCTL and netlink paths before and after the patch are in a few
+registers in Page 01h that were previously parsed from Page 00h.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ cmis.c | 175 +++++++++++++++++++++++++++++----------------------------
+ cmis.h |   1 -
+ 2 files changed, 88 insertions(+), 88 deletions(-)
+
+diff --git a/cmis.c b/cmis.c
+index 8a6788416a00..2e01446b2315 100644
+--- a/cmis.c
++++ b/cmis.c
+@@ -22,19 +22,19 @@ struct cmis_memory_map {
+ 
+ #define CMIS_PAGE_SIZE		0x80
+ 
+-static void cmis_show_identifier(const __u8 *id)
++static void cmis_show_identifier(const struct cmis_memory_map *map)
+ {
+-	sff8024_show_identifier(id, CMIS_ID_OFFSET);
++	sff8024_show_identifier(map->lower_memory, CMIS_ID_OFFSET);
+ }
+ 
+-static void cmis_show_connector(const __u8 *id)
++static void cmis_show_connector(const struct cmis_memory_map *map)
+ {
+-	sff8024_show_connector(id, CMIS_CTOR_OFFSET);
++	sff8024_show_connector(map->page_00h, CMIS_CTOR_OFFSET);
+ }
+ 
+-static void cmis_show_oui(const __u8 *id)
++static void cmis_show_oui(const struct cmis_memory_map *map)
+ {
+-	sff8024_show_oui(id, CMIS_VENDOR_OUI_OFFSET);
++	sff8024_show_oui(map->page_00h, CMIS_VENDOR_OUI_OFFSET);
+ }
+ 
+ /**
+@@ -42,9 +42,9 @@ static void cmis_show_oui(const __u8 *id)
+  * [1] CMIS Rev. 3, pag. 45, section 1.7.2.1, Table 18
+  * [2] CMIS Rev. 4, pag. 81, section 8.2.1, Table 8-2
+  */
+-static void cmis_show_rev_compliance(const __u8 *id)
++static void cmis_show_rev_compliance(const struct cmis_memory_map *map)
+ {
+-	__u8 rev = id[CMIS_REV_COMPLIANCE_OFFSET];
++	__u8 rev = map->lower_memory[CMIS_REV_COMPLIANCE_OFFSET];
+ 	int major = (rev >> 4) & 0x0F;
+ 	int minor = rev & 0x0F;
+ 
+@@ -58,17 +58,17 @@ static void cmis_show_rev_compliance(const __u8 *id)
+  * [2] CMIS Rev. 4, pag. 94, section 8.3.9, Table 8-18
+  * [3] QSFP-DD Hardware Rev 5.0, pag. 22, section 4.2.1
+  */
+-static void cmis_show_power_info(const __u8 *id)
++static void cmis_show_power_info(const struct cmis_memory_map *map)
+ {
+ 	float max_power = 0.0f;
+ 	__u8 base_power = 0;
+ 	__u8 power_class;
+ 
+ 	/* Get the power class (first 3 most significat bytes) */
+-	power_class = (id[CMIS_PWR_CLASS_OFFSET] >> 5) & 0x07;
++	power_class = (map->page_00h[CMIS_PWR_CLASS_OFFSET] >> 5) & 0x07;
+ 
+ 	/* Get the base power in multiples of 0.25W */
+-	base_power = id[CMIS_PWR_MAX_POWER_OFFSET];
++	base_power = map->page_00h[CMIS_PWR_MAX_POWER_OFFSET];
+ 	max_power = base_power * 0.25f;
+ 
+ 	printf("\t%-41s : %d\n", "Power class", power_class + 1);
+@@ -83,20 +83,20 @@ static void cmis_show_power_info(const __u8 *id)
+  * [1] CMIS Rev. 3, pag. 59, section 1.7.3.10, Table 31
+  * [2] CMIS Rev. 4, pag. 94, section 8.3.10, Table 8-19
+  */
+-static void cmis_show_cbl_asm_len(const __u8 *id)
++static void cmis_show_cbl_asm_len(const struct cmis_memory_map *map)
+ {
+ 	static const char *fn = "Cable assembly length";
+ 	float mul = 1.0f;
+ 	float val = 0.0f;
+ 
+ 	/* Check if max length */
+-	if (id[CMIS_CBL_ASM_LEN_OFFSET] == CMIS_6300M_MAX_LEN) {
++	if (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] == CMIS_6300M_MAX_LEN) {
+ 		printf("\t%-41s : > 6.3km\n", fn);
+ 		return;
+ 	}
+ 
+ 	/* Get the multiplier from the first two bits */
+-	switch (id[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_MUL_MASK) {
++	switch (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_MUL_MASK) {
+ 	case CMIS_MULTIPLIER_00:
+ 		mul = 0.1f;
+ 		break;
+@@ -114,7 +114,7 @@ static void cmis_show_cbl_asm_len(const __u8 *id)
+ 	}
+ 
+ 	/* Get base value from first 6 bits and multiply by mul */
+-	val = (id[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_VAL_MASK);
++	val = (map->page_00h[CMIS_CBL_ASM_LEN_OFFSET] & CMIS_LEN_VAL_MASK);
+ 	val = (float)val * mul;
+ 	printf("\t%-41s : %0.2fm\n", fn, val);
+ }
+@@ -126,14 +126,17 @@ static void cmis_show_cbl_asm_len(const __u8 *id)
+  * [1] CMIS Rev. 3, pag. 63, section 1.7.4.2, Table 39
+  * [2] CMIS Rev. 4, pag. 99, section 8.4.2, Table 8-27
+  */
+-static void cmis_print_smf_cbl_len(const __u8 *id)
++static void cmis_print_smf_cbl_len(const struct cmis_memory_map *map)
+ {
+ 	static const char *fn = "Length (SMF)";
+ 	float mul = 1.0f;
+ 	float val = 0.0f;
+ 
++	if (!map->page_01h)
++		return;
++
+ 	/* Get the multiplier from the first two bits */
+-	switch (id[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_MUL_MASK) {
++	switch (map->page_01h[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_MUL_MASK) {
+ 	case CMIS_MULTIPLIER_00:
+ 		mul = 0.1f;
+ 		break;
+@@ -145,7 +148,7 @@ static void cmis_print_smf_cbl_len(const __u8 *id)
+ 	}
+ 
+ 	/* Get base value from first 6 bits and multiply by mul */
+-	val = (id[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_VAL_MASK);
++	val = (map->page_01h[CMIS_SMF_LEN_OFFSET] & CMIS_LEN_VAL_MASK);
+ 	val = (float)val * mul;
+ 	printf("\t%-41s : %0.2fkm\n", fn, val);
+ }
+@@ -155,21 +158,24 @@ static void cmis_print_smf_cbl_len(const __u8 *id)
+  * [1] CMIS Rev. 3, pag. 71, section 1.7.4.10, Table 46
+  * [2] CMIS Rev. 4, pag. 105, section 8.4.10, Table 8-34
+  */
+-static void cmis_show_sig_integrity(const __u8 *id)
++static void cmis_show_sig_integrity(const struct cmis_memory_map *map)
+ {
++	if (!map->page_01h)
++		return;
++
+ 	/* CDR Bypass control: 2nd bit from each byte */
+ 	printf("\t%-41s : ", "Tx CDR bypass control");
+-	printf("%s\n", YESNO(id[CMIS_SIG_INTEG_TX_OFFSET] & 0x02));
++	printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_TX_OFFSET] & 0x02));
+ 
+ 	printf("\t%-41s : ", "Rx CDR bypass control");
+-	printf("%s\n", YESNO(id[CMIS_SIG_INTEG_RX_OFFSET] & 0x02));
++	printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_RX_OFFSET] & 0x02));
+ 
+ 	/* CDR Implementation: 1st bit from each byte */
+ 	printf("\t%-41s : ", "Tx CDR");
+-	printf("%s\n", YESNO(id[CMIS_SIG_INTEG_TX_OFFSET] & 0x01));
++	printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_TX_OFFSET] & 0x01));
+ 
+ 	printf("\t%-41s : ", "Rx CDR");
+-	printf("%s\n", YESNO(id[CMIS_SIG_INTEG_RX_OFFSET] & 0x01));
++	printf("%s\n", YESNO(map->page_01h[CMIS_SIG_INTEG_RX_OFFSET] & 0x01));
+ }
+ 
+ /**
+@@ -182,14 +188,14 @@ static void cmis_show_sig_integrity(const __u8 *id)
+  * --> pag. 98, section 8.4, Table 8-25
+  * --> page 100, section 8.4.3, 8.4.4
+  */
+-static void cmis_show_mit_compliance(const __u8 *id)
++static void cmis_show_mit_compliance(const struct cmis_memory_map *map)
+ {
+ 	static const char *cc = " (Copper cable,";
+ 
+ 	printf("\t%-41s : 0x%02x", "Transmitter technology",
+-	       id[CMIS_MEDIA_INTF_TECH_OFFSET]);
++	       map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET]);
+ 
+-	switch (id[CMIS_MEDIA_INTF_TECH_OFFSET]) {
++	switch (map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET]) {
+ 	case CMIS_850_VCSEL:
+ 		printf(" (850 nm VCSEL)\n");
+ 		break;
+@@ -240,22 +246,22 @@ static void cmis_show_mit_compliance(const __u8 *id)
+ 		break;
+ 	}
+ 
+-	if (id[CMIS_MEDIA_INTF_TECH_OFFSET] >= CMIS_COPPER_UNEQUAL) {
++	if (map->page_00h[CMIS_MEDIA_INTF_TECH_OFFSET] >= CMIS_COPPER_UNEQUAL) {
+ 		printf("\t%-41s : %udb\n", "Attenuation at 5GHz",
+-		       id[CMIS_COPPER_ATT_5GHZ]);
++		       map->page_00h[CMIS_COPPER_ATT_5GHZ]);
+ 		printf("\t%-41s : %udb\n", "Attenuation at 7GHz",
+-		       id[CMIS_COPPER_ATT_7GHZ]);
++		       map->page_00h[CMIS_COPPER_ATT_7GHZ]);
+ 		printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz",
+-		       id[CMIS_COPPER_ATT_12P9GHZ]);
++		       map->page_00h[CMIS_COPPER_ATT_12P9GHZ]);
+ 		printf("\t%-41s : %udb\n", "Attenuation at 25.8GHz",
+-		       id[CMIS_COPPER_ATT_25P8GHZ]);
+-	} else {
++		       map->page_00h[CMIS_COPPER_ATT_25P8GHZ]);
++	} else if (map->page_01h) {
+ 		printf("\t%-41s : %.3lfnm\n", "Laser wavelength",
+-		       (((id[CMIS_NOM_WAVELENGTH_MSB] << 8) |
+-				id[CMIS_NOM_WAVELENGTH_LSB]) * 0.05));
++		       (((map->page_01h[CMIS_NOM_WAVELENGTH_MSB] << 8) |
++			  map->page_01h[CMIS_NOM_WAVELENGTH_LSB]) * 0.05));
+ 		printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance",
+-		       (((id[CMIS_WAVELENGTH_TOL_MSB] << 8) |
+-		       id[CMIS_WAVELENGTH_TOL_LSB]) * 0.005));
++		       (((map->page_01h[CMIS_WAVELENGTH_TOL_MSB] << 8) |
++			  map->page_01h[CMIS_WAVELENGTH_TOL_LSB]) * 0.005));
+ 	}
+ }
+ 
+@@ -275,28 +281,16 @@ static void cmis_show_mit_compliance(const __u8 *id)
+  * [2] CMIS Rev. 4:
+  * --> pag. 84, section 8.2.4, Table 8-6
+  */
+-static void cmis_show_mod_lvl_monitors(const __u8 *id)
++static void cmis_show_mod_lvl_monitors(const struct cmis_memory_map *map)
+ {
++	const __u8 *id = map->lower_memory;
++
+ 	PRINT_TEMP("Module temperature",
+ 		   OFFSET_TO_TEMP(CMIS_CURR_TEMP_OFFSET));
+ 	PRINT_VCC("Module voltage",
+ 		  OFFSET_TO_U16(CMIS_CURR_VCC_OFFSET));
+ }
+ 
+-static void cmis_show_link_len_from_page(const __u8 *page_one_data)
+-{
+-	cmis_print_smf_cbl_len(page_one_data);
+-	sff_show_value_with_unit(page_one_data, CMIS_OM5_LEN_OFFSET,
+-				 "Length (OM5)", 2, "m");
+-	sff_show_value_with_unit(page_one_data, CMIS_OM4_LEN_OFFSET,
+-				 "Length (OM4)", 2, "m");
+-	sff_show_value_with_unit(page_one_data, CMIS_OM3_LEN_OFFSET,
+-				 "Length (OM3 50/125um)", 2, "m");
+-	sff_show_value_with_unit(page_one_data, CMIS_OM2_LEN_OFFSET,
+-				 "Length (OM2 50/125um)", 1, "m");
+-}
+-
+-
+ /**
+  * Print relevant info about the maximum supported fiber media length
+  * for each type of fiber media at the maximum module-supported bit rate.
+@@ -304,9 +298,19 @@ static void cmis_show_link_len_from_page(const __u8 *page_one_data)
+  * [1] CMIS Rev. 3, page 64, section 1.7.4.2, Table 39
+  * [2] CMIS Rev. 4, page 99, section 8.4.2, Table 8-27
+  */
+-static void cmis_show_link_len(const __u8 *id)
++static void cmis_show_link_len(const struct cmis_memory_map *map)
+ {
+-	cmis_show_link_len_from_page(id);
++	cmis_print_smf_cbl_len(map);
++	if (!map->page_01h)
++		return;
++	sff_show_value_with_unit(map->page_01h, CMIS_OM5_LEN_OFFSET,
++				 "Length (OM5)", 2, "m");
++	sff_show_value_with_unit(map->page_01h, CMIS_OM4_LEN_OFFSET,
++				 "Length (OM4)", 2, "m");
++	sff_show_value_with_unit(map->page_01h, CMIS_OM3_LEN_OFFSET,
++				 "Length (OM3 50/125um)", 2, "m");
++	sff_show_value_with_unit(map->page_01h, CMIS_OM2_LEN_OFFSET,
++				 "Length (OM2 50/125um)", 1, "m");
+ }
+ 
+ /**
+@@ -314,25 +318,26 @@ static void cmis_show_link_len(const __u8 *id)
+  * [1] CMIS Rev. 3, page 56, section 1.7.3, Table 27
+  * [2] CMIS Rev. 4, page 91, section 8.2, Table 8-15
+  */
+-static void cmis_show_vendor_info(const __u8 *id)
++static void cmis_show_vendor_info(const struct cmis_memory_map *map)
+ {
+-	const char *clei = (const char *)(id + CMIS_CLEI_START_OFFSET);
++	const char *clei;
+ 
+-	sff_show_ascii(id, CMIS_VENDOR_NAME_START_OFFSET,
++	sff_show_ascii(map->page_00h, CMIS_VENDOR_NAME_START_OFFSET,
+ 		       CMIS_VENDOR_NAME_END_OFFSET, "Vendor name");
+-	cmis_show_oui(id);
+-	sff_show_ascii(id, CMIS_VENDOR_PN_START_OFFSET,
++	cmis_show_oui(map);
++	sff_show_ascii(map->page_00h, CMIS_VENDOR_PN_START_OFFSET,
+ 		       CMIS_VENDOR_PN_END_OFFSET, "Vendor PN");
+-	sff_show_ascii(id, CMIS_VENDOR_REV_START_OFFSET,
++	sff_show_ascii(map->page_00h, CMIS_VENDOR_REV_START_OFFSET,
+ 		       CMIS_VENDOR_REV_END_OFFSET, "Vendor rev");
+-	sff_show_ascii(id, CMIS_VENDOR_SN_START_OFFSET,
++	sff_show_ascii(map->page_00h, CMIS_VENDOR_SN_START_OFFSET,
+ 		       CMIS_VENDOR_SN_END_OFFSET, "Vendor SN");
+-	sff_show_ascii(id, CMIS_DATE_YEAR_OFFSET,
++	sff_show_ascii(map->page_00h, CMIS_DATE_YEAR_OFFSET,
+ 		       CMIS_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
+ 
++	clei = (const char *)(map->page_00h + CMIS_CLEI_START_OFFSET);
+ 	if (*clei && strncmp(clei, CMIS_CLEI_BLANK, CMIS_CLEI_LEN))
+-		sff_show_ascii(id, CMIS_CLEI_START_OFFSET, CMIS_CLEI_END_OFFSET,
+-			       "CLEI code");
++		sff_show_ascii(map->page_00h, CMIS_CLEI_START_OFFSET,
++			       CMIS_CLEI_END_OFFSET, "CLEI code");
+ }
+ 
+ static void cmis_memory_map_init_buf(struct cmis_memory_map *map,
+@@ -363,16 +368,16 @@ void cmis_show_all_ioctl(const __u8 *id)
+ 
+ 	cmis_memory_map_init_buf(&map, id);
+ 
+-	cmis_show_identifier(id);
+-	cmis_show_power_info(id);
+-	cmis_show_connector(id);
+-	cmis_show_cbl_asm_len(id);
+-	cmis_show_sig_integrity(id);
+-	cmis_show_mit_compliance(id);
+-	cmis_show_mod_lvl_monitors(id);
+-	cmis_show_link_len(id);
+-	cmis_show_vendor_info(id);
+-	cmis_show_rev_compliance(id);
++	cmis_show_identifier(&map);
++	cmis_show_power_info(&map);
++	cmis_show_connector(&map);
++	cmis_show_cbl_asm_len(&map);
++	cmis_show_sig_integrity(&map);
++	cmis_show_mit_compliance(&map);
++	cmis_show_mod_lvl_monitors(&map);
++	cmis_show_link_len(&map);
++	cmis_show_vendor_info(&map);
++	cmis_show_rev_compliance(&map);
+ }
+ 
+ static void
+@@ -403,22 +408,18 @@ cmis_memory_map_init_pages(struct cmis_memory_map *map,
+ void cmis_show_all_nl(const struct ethtool_module_eeprom *page_zero,
+ 		      const struct ethtool_module_eeprom *page_one)
+ {
+-	const __u8 *page_zero_data = page_zero->data;
+ 	struct cmis_memory_map map = {};
+ 
+ 	cmis_memory_map_init_pages(&map, page_zero, page_one);
+ 
+-	cmis_show_identifier(page_zero_data);
+-	cmis_show_power_info(page_zero_data);
+-	cmis_show_connector(page_zero_data);
+-	cmis_show_cbl_asm_len(page_zero_data);
+-	cmis_show_sig_integrity(page_zero_data);
+-	cmis_show_mit_compliance(page_zero_data);
+-	cmis_show_mod_lvl_monitors(page_zero_data);
+-
+-	if (page_one)
+-		cmis_show_link_len_from_page(page_one->data - 0x80);
+-
+-	cmis_show_vendor_info(page_zero_data);
+-	cmis_show_rev_compliance(page_zero_data);
++	cmis_show_identifier(&map);
++	cmis_show_power_info(&map);
++	cmis_show_connector(&map);
++	cmis_show_cbl_asm_len(&map);
++	cmis_show_sig_integrity(&map);
++	cmis_show_mit_compliance(&map);
++	cmis_show_mod_lvl_monitors(&map);
++	cmis_show_link_len(&map);
++	cmis_show_vendor_info(&map);
++	cmis_show_rev_compliance(&map);
+ }
+diff --git a/cmis.h b/cmis.h
+index 53cbb5f57127..c878e3bc5afd 100644
+--- a/cmis.h
++++ b/cmis.h
+@@ -100,7 +100,6 @@
+  * that are unique to active modules and cable assemblies.
+  * GlobalOffset = 2 * 0x80 + LocalOffset
+  */
+-#define PAG01H_UPPER_OFFSET			(0x02 * 0x80)
+ 
+ /* Supported Link Length (Page 1) */
+ #define CMIS_SMF_LEN_OFFSET			0x84
+-- 
+2.35.1
+
diff --git a/SOURCES/0016-cmis-Consolidate-code-between-IOCTL-and-netlink-path.patch b/SOURCES/0016-cmis-Consolidate-code-between-IOCTL-and-netlink-path.patch
new file mode 100644
index 0000000..98e6680
--- /dev/null
+++ b/SOURCES/0016-cmis-Consolidate-code-between-IOCTL-and-netlink-path.patch
@@ -0,0 +1,77 @@
+From 284886fbb4a85103cd82d061ebe4d1c93730b783 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 12 Oct 2021 16:25:15 +0300
+Subject: [PATCH 16/35] cmis: Consolidate code between IOCTL and netlink paths
+
+Now that both the netlink and IOCTL paths use the same memory map
+structure for parsing, the code can be easily consolidated.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ cmis.c | 38 ++++++++++++++++----------------------
+ 1 file changed, 16 insertions(+), 22 deletions(-)
+
+diff --git a/cmis.c b/cmis.c
+index 2e01446b2315..eb7791dd59df 100644
+--- a/cmis.c
++++ b/cmis.c
+@@ -340,6 +340,20 @@ static void cmis_show_vendor_info(const struct cmis_memory_map *map)
+ 			       CMIS_CLEI_END_OFFSET, "CLEI code");
+ }
+ 
++static void cmis_show_all_common(const struct cmis_memory_map *map)
++{
++	cmis_show_identifier(map);
++	cmis_show_power_info(map);
++	cmis_show_connector(map);
++	cmis_show_cbl_asm_len(map);
++	cmis_show_sig_integrity(map);
++	cmis_show_mit_compliance(map);
++	cmis_show_mod_lvl_monitors(map);
++	cmis_show_link_len(map);
++	cmis_show_vendor_info(map);
++	cmis_show_rev_compliance(map);
++}
++
+ static void cmis_memory_map_init_buf(struct cmis_memory_map *map,
+ 				     const __u8 *id)
+ {
+@@ -367,17 +381,7 @@ void cmis_show_all_ioctl(const __u8 *id)
+ 	struct cmis_memory_map map = {};
+ 
+ 	cmis_memory_map_init_buf(&map, id);
+-
+-	cmis_show_identifier(&map);
+-	cmis_show_power_info(&map);
+-	cmis_show_connector(&map);
+-	cmis_show_cbl_asm_len(&map);
+-	cmis_show_sig_integrity(&map);
+-	cmis_show_mit_compliance(&map);
+-	cmis_show_mod_lvl_monitors(&map);
+-	cmis_show_link_len(&map);
+-	cmis_show_vendor_info(&map);
+-	cmis_show_rev_compliance(&map);
++	cmis_show_all_common(&map);
+ }
+ 
+ static void
+@@ -411,15 +415,5 @@ void cmis_show_all_nl(const struct ethtool_module_eeprom *page_zero,
+ 	struct cmis_memory_map map = {};
+ 
+ 	cmis_memory_map_init_pages(&map, page_zero, page_one);
+-
+-	cmis_show_identifier(&map);
+-	cmis_show_power_info(&map);
+-	cmis_show_connector(&map);
+-	cmis_show_cbl_asm_len(&map);
+-	cmis_show_sig_integrity(&map);
+-	cmis_show_mit_compliance(&map);
+-	cmis_show_mod_lvl_monitors(&map);
+-	cmis_show_link_len(&map);
+-	cmis_show_vendor_info(&map);
+-	cmis_show_rev_compliance(&map);
++	cmis_show_all_common(&map);
+ }
+-- 
+2.35.1
+
diff --git a/SOURCES/0017-sff-8636-Rename-SFF-8636-parsing-functions.patch b/SOURCES/0017-sff-8636-Rename-SFF-8636-parsing-functions.patch
new file mode 100644
index 0000000..ec866f5
--- /dev/null
+++ b/SOURCES/0017-sff-8636-Rename-SFF-8636-parsing-functions.patch
@@ -0,0 +1,97 @@
+From a8419e965891901217756254e0ed1a3351b2a3cb Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 12 Oct 2021 16:25:16 +0300
+Subject: [PATCH 17/35] sff-8636: Rename SFF-8636 parsing functions
+
+Currently, there are two SFF-8636 parsing functions. sff8636_show_all()
+and sff8636_show_all_paged(). The former is called from the IOCTL path
+with a buffer containing EEPROM contents and the latter is called from
+the netlink path with pointer to individual EEPROM pages.
+
+Rename them with '_ioctl' and '_nl' suffixes to make the distinction
+clear.
+
+In subsequent patches, these two functions will only differ in the way
+they initialize the SFF-8636 memory map for parsing, while the parsing
+code itself will be shared between the two.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ ethtool.c               | 4 ++--
+ internal.h              | 6 +++---
+ netlink/module-eeprom.c | 2 +-
+ qsfp.c                  | 6 +++---
+ 4 files changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/ethtool.c b/ethtool.c
+index 1b79e9f8d958..6c744ff84eb9 100644
+--- a/ethtool.c
++++ b/ethtool.c
+@@ -4908,8 +4908,8 @@ static int do_getmodule(struct cmd_context *ctx)
+ 				break;
+ 			case ETH_MODULE_SFF_8436:
+ 			case ETH_MODULE_SFF_8636:
+-				sff8636_show_all(eeprom->data,
+-						 modinfo.eeprom_len);
++				sff8636_show_all_ioctl(eeprom->data,
++						       modinfo.eeprom_len);
+ 				break;
+ #endif
+ 			default:
+diff --git a/internal.h b/internal.h
+index 33e619b3ac53..7ca6066d4e12 100644
+--- a/internal.h
++++ b/internal.h
+@@ -390,9 +390,9 @@ void sff8079_show_all(const __u8 *id);
+ void sff8472_show_all(const __u8 *id);
+ 
+ /* QSFP Optics diagnostics */
+-void sff8636_show_all(const __u8 *id, __u32 eeprom_len);
+-void sff8636_show_all_paged(const struct ethtool_module_eeprom *page_zero,
+-			    const struct ethtool_module_eeprom *page_three);
++void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len);
++void sff8636_show_all_nl(const struct ethtool_module_eeprom *page_zero,
++			 const struct ethtool_module_eeprom *page_three);
+ 
+ /* FUJITSU Extended Socket network device */
+ int fjes_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c
+index fc4ef1a53aff..18b1abbe1252 100644
+--- a/netlink/module-eeprom.c
++++ b/netlink/module-eeprom.c
+@@ -328,7 +328,7 @@ static void decoder_print(void)
+ 	case SFF8024_ID_QSFP:
+ 	case SFF8024_ID_QSFP28:
+ 	case SFF8024_ID_QSFP_PLUS:
+-		sff8636_show_all_paged(page_zero, page_three);
++		sff8636_show_all_nl(page_zero, page_three);
+ 		break;
+ 	case SFF8024_ID_QSFP_DD:
+ 	case SFF8024_ID_DSFP:
+diff --git a/qsfp.c b/qsfp.c
+index 27fdd3bd1771..dc6407d3ef6f 100644
+--- a/qsfp.c
++++ b/qsfp.c
+@@ -853,7 +853,7 @@ static void sff8636_show_page_zero(const __u8 *id)
+ 
+ }
+ 
+-void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
++void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len)
+ {
+ 	if (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_DD) {
+ 		cmis_show_all_ioctl(id);
+@@ -871,8 +871,8 @@ void sff8636_show_all(const __u8 *id, __u32 eeprom_len)
+ 	}
+ }
+ 
+-void sff8636_show_all_paged(const struct ethtool_module_eeprom *page_zero,
+-			    const struct ethtool_module_eeprom *page_three)
++void sff8636_show_all_nl(const struct ethtool_module_eeprom *page_zero,
++			 const struct ethtool_module_eeprom *page_three)
+ {
+ 	sff8636_show_identifier(page_zero->data);
+ 	sff8636_show_page_zero(page_zero->data);
+-- 
+2.35.1
+
diff --git a/SOURCES/0018-sff-8636-Initialize-SFF-8636-memory-map.patch b/SOURCES/0018-sff-8636-Initialize-SFF-8636-memory-map.patch
new file mode 100644
index 0000000..36709da
--- /dev/null
+++ b/SOURCES/0018-sff-8636-Initialize-SFF-8636-memory-map.patch
@@ -0,0 +1,137 @@
+From c77b5adfb3f94762a08554d1b4d75f6cbd8a6abe Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 12 Oct 2021 16:25:17 +0300
+Subject: [PATCH 18/35] sff-8636: Initialize SFF-8636 memory map
+
+The SFF-8636 memory map [1] consists of Lower Memory and Upper Memory.
+
+The content of the Lower Memory is fixed and can be addressed using an
+offset between 0 and 127 (inclusive).
+
+The Upper Memory is variable and optional and can be addressed by
+specifying a page number and an offset between 128 and 255 (inclusive).
+
+Create a structure describing this memory map and initialize it with
+pointers to available pages.
+
+In the IOCTL path, the structure holds pointers to regions of the
+continuous buffer passed to user space via the 'ETHTOOL_GMODULEEEPROM'
+command.
+
+In the netlink path, the structure holds pointers to individual pages
+passed to user space via the 'MODULE_EEPROM_GET' message.
+
+This structure will later allow us to consolidate the IOCTL and netlink
+parsing code paths and also easily support additional EEPROM pages, when
+needed.
+
+[1] SFF-8636 Rev. 2.10a, pag. 30, section 6.1, Figure 6-1
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ qsfp.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 65 insertions(+)
+
+diff --git a/qsfp.c b/qsfp.c
+index dc6407d3ef6f..80000d40f6e8 100644
+--- a/qsfp.c
++++ b/qsfp.c
+@@ -60,6 +60,15 @@
+ #include "qsfp.h"
+ #include "cmis.h"
+ 
++struct sff8636_memory_map {
++	const __u8 *lower_memory;
++	const __u8 *upper_memory[4];
++#define page_00h upper_memory[0x0]
++#define page_03h upper_memory[0x3]
++};
++
++#define SFF8636_PAGE_SIZE	0x80
++
+ #define MAX_DESC_SIZE	42
+ 
+ static struct sff8636_aw_flags {
+@@ -853,13 +862,40 @@ static void sff8636_show_page_zero(const __u8 *id)
+ 
+ }
+ 
++static void sff8636_memory_map_init_buf(struct sff8636_memory_map *map,
++					const __u8 *id, __u32 eeprom_len)
++{
++	/* Lower Memory and Page 00h are always present.
++	 *
++	 * Offset into Upper Memory is between page size and twice the page
++	 * size. Therefore, set the base address of each page to base address
++	 * plus page size multiplied by the page number.
++	 */
++	map->lower_memory = id;
++	map->page_00h = id;
++
++	/* Page 03h is only present when the module memory model is paged and
++	 * not flat and when we got a big enough buffer from the kernel.
++	 */
++	if (map->lower_memory[SFF8636_STATUS_2_OFFSET] &
++	    SFF8636_STATUS_PAGE_3_PRESENT ||
++	    eeprom_len != ETH_MODULE_SFF_8636_MAX_LEN)
++		return;
++
++	map->page_03h = id + 3 * SFF8636_PAGE_SIZE;
++}
++
+ void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len)
+ {
++	struct sff8636_memory_map map = {};
++
+ 	if (id[SFF8636_ID_OFFSET] == SFF8024_ID_QSFP_DD) {
+ 		cmis_show_all_ioctl(id);
+ 		return;
+ 	}
+ 
++	sff8636_memory_map_init_buf(&map, id, eeprom_len);
++
+ 	sff8636_show_identifier(id);
+ 	switch (id[SFF8636_ID_OFFSET]) {
+ 	case SFF8024_ID_QSFP:
+@@ -871,9 +907,38 @@ void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len)
+ 	}
+ }
+ 
++static void
++sff8636_memory_map_init_pages(struct sff8636_memory_map *map,
++			      const struct ethtool_module_eeprom *page_zero,
++			      const struct ethtool_module_eeprom *page_three)
++{
++	/* Lower Memory and Page 00h are always present.
++	 *
++	 * Offset into Upper Memory is between page size and twice the page
++	 * size. Therefore, set the base address of each page to its base
++	 * address minus page size. For Page 00h, this is the address of the
++	 * Lower Memory.
++	 */
++	map->lower_memory = page_zero->data;
++	map->page_00h = page_zero->data;
++
++	/* Page 03h is only present when the module memory model is paged and
++	 * not flat.
++	 */
++	if (map->lower_memory[SFF8636_STATUS_2_OFFSET] &
++	    SFF8636_STATUS_PAGE_3_PRESENT)
++		return;
++
++	map->page_03h = page_three->data - SFF8636_PAGE_SIZE;
++}
++
+ void sff8636_show_all_nl(const struct ethtool_module_eeprom *page_zero,
+ 			 const struct ethtool_module_eeprom *page_three)
+ {
++	struct sff8636_memory_map map = {};
++
++	sff8636_memory_map_init_pages(&map, page_zero, page_three);
++
+ 	sff8636_show_identifier(page_zero->data);
+ 	sff8636_show_page_zero(page_zero->data);
+ 	if (page_three)
+-- 
+2.35.1
+
diff --git a/SOURCES/0019-sff-8636-Use-memory-map-during-parsing.patch b/SOURCES/0019-sff-8636-Use-memory-map-during-parsing.patch
new file mode 100644
index 0000000..158073b
--- /dev/null
+++ b/SOURCES/0019-sff-8636-Use-memory-map-during-parsing.patch
@@ -0,0 +1,607 @@
+From 85023fa5dd7e79ce46a92ab567b4b675c3145775 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 12 Oct 2021 16:25:18 +0300
+Subject: [PATCH 19/35] sff-8636: Use memory map during parsing
+
+Instead of passing one large buffer to the individual parsing functions,
+use the memory map structure from the previous patch.
+
+This has the added benefit of checking which optional pages are actually
+available and it will also allow us to consolidate the IOCTL and netlink
+parsing code paths.
+
+Tested by making sure that there are no differences in output in both
+the IOCTL and netlink paths before and after the patch.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ qsfp.c | 368 +++++++++++++++++++++++++++++++--------------------------
+ 1 file changed, 201 insertions(+), 167 deletions(-)
+
+diff --git a/qsfp.c b/qsfp.c
+index 80000d40f6e8..354b3b1ce9ff 100644
+--- a/qsfp.c
++++ b/qsfp.c
+@@ -205,20 +205,21 @@ static struct sff8636_aw_flags {
+ 	{ NULL, 0, 0 },
+ };
+ 
+-static void sff8636_show_identifier(const __u8 *id)
++static void sff8636_show_identifier(const struct sff8636_memory_map *map)
+ {
+-	sff8024_show_identifier(id, SFF8636_ID_OFFSET);
++	sff8024_show_identifier(map->lower_memory, SFF8636_ID_OFFSET);
+ }
+ 
+-static void sff8636_show_ext_identifier(const __u8 *id)
++static void sff8636_show_ext_identifier(const struct sff8636_memory_map *map)
+ {
+ 	printf("\t%-41s : 0x%02x\n", "Extended identifier",
+-			id[SFF8636_EXT_ID_OFFSET]);
++	       map->page_00h[SFF8636_EXT_ID_OFFSET]);
+ 
+ 	static const char *pfx =
+ 		"\tExtended identifier description           :";
+ 
+-	switch (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_PWR_CLASS_MASK) {
++	switch (map->page_00h[SFF8636_EXT_ID_OFFSET] &
++		SFF8636_EXT_ID_PWR_CLASS_MASK) {
+ 	case SFF8636_EXT_ID_PWR_CLASS_1:
+ 		printf("%s 1.5W max. Power consumption\n", pfx);
+ 		break;
+@@ -233,17 +234,18 @@ static void sff8636_show_ext_identifier(const __u8 *id)
+ 		break;
+ 	}
+ 
+-	if (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_TX_MASK)
++	if (map->page_00h[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_TX_MASK)
+ 		printf("%s CDR present in TX,", pfx);
+ 	else
+ 		printf("%s No CDR in TX,", pfx);
+ 
+-	if (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_RX_MASK)
++	if (map->page_00h[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_CDR_RX_MASK)
+ 		printf(" CDR present in RX\n");
+ 	else
+ 		printf(" No CDR in RX\n");
+ 
+-	switch (id[SFF8636_EXT_ID_OFFSET] & SFF8636_EXT_ID_EPWR_CLASS_MASK) {
++	switch (map->page_00h[SFF8636_EXT_ID_OFFSET] &
++		SFF8636_EXT_ID_EPWR_CLASS_MASK) {
+ 	case SFF8636_EXT_ID_PWR_CLASS_LEGACY:
+ 		printf("%s", pfx);
+ 		break;
+@@ -257,18 +259,19 @@ static void sff8636_show_ext_identifier(const __u8 *id)
+ 		printf("%s 5.0W max. Power consumption, ", pfx);
+ 		break;
+ 	}
+-	if (id[SFF8636_PWR_MODE_OFFSET] & SFF8636_HIGH_PWR_ENABLE)
++	if (map->lower_memory[SFF8636_PWR_MODE_OFFSET] &
++	    SFF8636_HIGH_PWR_ENABLE)
+ 		printf(" High Power Class (> 3.5 W) enabled\n");
+ 	else
+ 		printf(" High Power Class (> 3.5 W) not enabled\n");
+ }
+ 
+-static void sff8636_show_connector(const __u8 *id)
++static void sff8636_show_connector(const struct sff8636_memory_map *map)
+ {
+-	sff8024_show_connector(id, SFF8636_CTOR_OFFSET);
++	sff8024_show_connector(map->page_00h, SFF8636_CTOR_OFFSET);
+ }
+ 
+-static void sff8636_show_transceiver(const __u8 *id)
++static void sff8636_show_transceiver(const struct sff8636_memory_map *map)
+ {
+ 	static const char *pfx =
+ 		"\tTransceiver type                          :";
+@@ -276,33 +279,41 @@ static void sff8636_show_transceiver(const __u8 *id)
+ 	printf("\t%-41s : 0x%02x 0x%02x 0x%02x " \
+ 			"0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ 			"Transceiver codes",
+-			id[SFF8636_ETHERNET_COMP_OFFSET],
+-			id[SFF8636_SONET_COMP_OFFSET],
+-			id[SFF8636_SAS_COMP_OFFSET],
+-			id[SFF8636_GIGE_COMP_OFFSET],
+-			id[SFF8636_FC_LEN_OFFSET],
+-			id[SFF8636_FC_TECH_OFFSET],
+-			id[SFF8636_FC_TRANS_MEDIA_OFFSET],
+-			id[SFF8636_FC_SPEED_OFFSET]);
++			map->page_00h[SFF8636_ETHERNET_COMP_OFFSET],
++			map->page_00h[SFF8636_SONET_COMP_OFFSET],
++			map->page_00h[SFF8636_SAS_COMP_OFFSET],
++			map->page_00h[SFF8636_GIGE_COMP_OFFSET],
++			map->page_00h[SFF8636_FC_LEN_OFFSET],
++			map->page_00h[SFF8636_FC_TECH_OFFSET],
++			map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET],
++			map->page_00h[SFF8636_FC_SPEED_OFFSET]);
+ 
+ 	/* 10G/40G Ethernet Compliance Codes */
+-	if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_10G_LRM)
++	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
++	    SFF8636_ETHERNET_10G_LRM)
+ 		printf("%s 10G Ethernet: 10G Base-LRM\n", pfx);
+-	if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_10G_LR)
++	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
++	    SFF8636_ETHERNET_10G_LR)
+ 		printf("%s 10G Ethernet: 10G Base-LR\n", pfx);
+-	if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_10G_SR)
++	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
++	    SFF8636_ETHERNET_10G_SR)
+ 		printf("%s 10G Ethernet: 10G Base-SR\n", pfx);
+-	if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_CR4)
++	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
++	    SFF8636_ETHERNET_40G_CR4)
+ 		printf("%s 40G Ethernet: 40G Base-CR4\n", pfx);
+-	if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_SR4)
++	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
++	    SFF8636_ETHERNET_40G_SR4)
+ 		printf("%s 40G Ethernet: 40G Base-SR4\n", pfx);
+-	if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_LR4)
++	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
++	    SFF8636_ETHERNET_40G_LR4)
+ 		printf("%s 40G Ethernet: 40G Base-LR4\n", pfx);
+-	if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_40G_ACTIVE)
++	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
++	    SFF8636_ETHERNET_40G_ACTIVE)
+ 		printf("%s 40G Ethernet: 40G Active Cable (XLPPI)\n", pfx);
+ 	/* Extended Specification Compliance Codes from SFF-8024 */
+-	if (id[SFF8636_ETHERNET_COMP_OFFSET] & SFF8636_ETHERNET_RSRVD) {
+-		switch (id[SFF8636_OPTION_1_OFFSET]) {
++	if (map->page_00h[SFF8636_ETHERNET_COMP_OFFSET] &
++	    SFF8636_ETHERNET_RSRVD) {
++		switch (map->page_00h[SFF8636_OPTION_1_OFFSET]) {
+ 		case SFF8636_ETHERNET_UNSPECIFIED:
+ 			printf("%s (reserved or unknown)\n", pfx);
+ 			break;
+@@ -493,113 +504,122 @@ static void sff8636_show_transceiver(const __u8 *id)
+ 	}
+ 
+ 	/* SONET Compliance Codes */
+-	if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_40G_OTN))
++	if (map->page_00h[SFF8636_SONET_COMP_OFFSET] &
++	    (SFF8636_SONET_40G_OTN))
+ 		printf("%s 40G OTN (OTU3B/OTU3C)\n", pfx);
+-	if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_LR))
++	if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_LR))
+ 		printf("%s SONET: OC-48, long reach\n", pfx);
+-	if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_IR))
++	if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_IR))
+ 		printf("%s SONET: OC-48, intermediate reach\n", pfx);
+-	if (id[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_SR))
++	if (map->page_00h[SFF8636_SONET_COMP_OFFSET] & (SFF8636_SONET_OC48_SR))
+ 		printf("%s SONET: OC-48, short reach\n", pfx);
+ 
+ 	/* SAS/SATA Compliance Codes */
+-	if (id[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_6G))
++	if (map->page_00h[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_6G))
+ 		printf("%s SAS 6.0G\n", pfx);
+-	if (id[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_3G))
++	if (map->page_00h[SFF8636_SAS_COMP_OFFSET] & (SFF8636_SAS_3G))
+ 		printf("%s SAS 3.0G\n", pfx);
+ 
+ 	/* Ethernet Compliance Codes */
+-	if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_T)
++	if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_T)
+ 		printf("%s Ethernet: 1000BASE-T\n", pfx);
+-	if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_CX)
++	if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_CX)
+ 		printf("%s Ethernet: 1000BASE-CX\n", pfx);
+-	if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_LX)
++	if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_LX)
+ 		printf("%s Ethernet: 1000BASE-LX\n", pfx);
+-	if (id[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_SX)
++	if (map->page_00h[SFF8636_GIGE_COMP_OFFSET] & SFF8636_GIGE_1000_BASE_SX)
+ 		printf("%s Ethernet: 1000BASE-SX\n", pfx);
+ 
+ 	/* Fibre Channel link length */
+-	if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_VERY_LONG)
++	if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_VERY_LONG)
+ 		printf("%s FC: very long distance (V)\n", pfx);
+-	if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_SHORT)
++	if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_SHORT)
+ 		printf("%s FC: short distance (S)\n", pfx);
+-	if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_INT)
++	if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_INT)
+ 		printf("%s FC: intermediate distance (I)\n", pfx);
+-	if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_LONG)
++	if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_LONG)
+ 		printf("%s FC: long distance (L)\n", pfx);
+-	if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_MED)
++	if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_LEN_MED)
+ 		printf("%s FC: medium distance (M)\n", pfx);
+ 
+ 	/* Fibre Channel transmitter technology */
+-	if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_LONG_LC)
++	if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_LONG_LC)
+ 		printf("%s FC: Longwave laser (LC)\n", pfx);
+-	if (id[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_ELEC_INTER)
++	if (map->page_00h[SFF8636_FC_LEN_OFFSET] & SFF8636_FC_TECH_ELEC_INTER)
+ 		printf("%s FC: Electrical inter-enclosure (EL)\n", pfx);
+-	if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_ELEC_INTRA)
++	if (map->page_00h[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_ELEC_INTRA)
+ 		printf("%s FC: Electrical intra-enclosure (EL)\n", pfx);
+-	if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_SHORT_WO_OFC)
++	if (map->page_00h[SFF8636_FC_TECH_OFFSET] &
++	    SFF8636_FC_TECH_SHORT_WO_OFC)
+ 		printf("%s FC: Shortwave laser w/o OFC (SN)\n", pfx);
+-	if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_SHORT_W_OFC)
++	if (map->page_00h[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_SHORT_W_OFC)
+ 		printf("%s FC: Shortwave laser with OFC (SL)\n", pfx);
+-	if (id[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_LONG_LL)
++	if (map->page_00h[SFF8636_FC_TECH_OFFSET] & SFF8636_FC_TECH_LONG_LL)
+ 		printf("%s FC: Longwave laser (LL)\n", pfx);
+ 
+ 	/* Fibre Channel transmission media */
+-	if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_TW)
++	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
++	    SFF8636_FC_TRANS_MEDIA_TW)
+ 		printf("%s FC: Twin Axial Pair (TW)\n", pfx);
+-	if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_TP)
++	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
++	    SFF8636_FC_TRANS_MEDIA_TP)
+ 		printf("%s FC: Twisted Pair (TP)\n", pfx);
+-	if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_MI)
++	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
++	    SFF8636_FC_TRANS_MEDIA_MI)
+ 		printf("%s FC: Miniature Coax (MI)\n", pfx);
+-	if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_TV)
++	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
++	    SFF8636_FC_TRANS_MEDIA_TV)
+ 		printf("%s FC: Video Coax (TV)\n", pfx);
+-	if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_M6)
++	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
++	    SFF8636_FC_TRANS_MEDIA_M6)
+ 		printf("%s FC: Multimode, 62.5m (M6)\n", pfx);
+-	if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_M5)
++	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
++	    SFF8636_FC_TRANS_MEDIA_M5)
+ 		printf("%s FC: Multimode, 50m (M5)\n", pfx);
+-	if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_OM3)
++	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
++	    SFF8636_FC_TRANS_MEDIA_OM3)
+ 		printf("%s FC: Multimode, 50um (OM3)\n", pfx);
+-	if (id[SFF8636_FC_TRANS_MEDIA_OFFSET] & SFF8636_FC_TRANS_MEDIA_SM)
++	if (map->page_00h[SFF8636_FC_TRANS_MEDIA_OFFSET] &
++	    SFF8636_FC_TRANS_MEDIA_SM)
+ 		printf("%s FC: Single Mode (SM)\n", pfx);
+ 
+ 	/* Fibre Channel speed */
+-	if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1200_MBPS)
++	if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1200_MBPS)
+ 		printf("%s FC: 1200 MBytes/sec\n", pfx);
+-	if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_800_MBPS)
++	if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_800_MBPS)
+ 		printf("%s FC: 800 MBytes/sec\n", pfx);
+-	if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1600_MBPS)
++	if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_1600_MBPS)
+ 		printf("%s FC: 1600 MBytes/sec\n", pfx);
+-	if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_400_MBPS)
++	if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_400_MBPS)
+ 		printf("%s FC: 400 MBytes/sec\n", pfx);
+-	if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_200_MBPS)
++	if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_200_MBPS)
+ 		printf("%s FC: 200 MBytes/sec\n", pfx);
+-	if (id[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_100_MBPS)
++	if (map->page_00h[SFF8636_FC_SPEED_OFFSET] & SFF8636_FC_SPEED_100_MBPS)
+ 		printf("%s FC: 100 MBytes/sec\n", pfx);
+ }
+ 
+-static void sff8636_show_encoding(const __u8 *id)
++static void sff8636_show_encoding(const struct sff8636_memory_map *map)
+ {
+-	sff8024_show_encoding(id, SFF8636_ENCODING_OFFSET, ETH_MODULE_SFF_8636);
++	sff8024_show_encoding(map->page_00h, SFF8636_ENCODING_OFFSET,
++			      ETH_MODULE_SFF_8636);
+ }
+ 
+-static void sff8636_show_rate_identifier(const __u8 *id)
++static void sff8636_show_rate_identifier(const struct sff8636_memory_map *map)
+ {
+ 	/* TODO: Need to fix rate select logic */
+ 	printf("\t%-41s : 0x%02x\n", "Rate identifier",
+-			id[SFF8636_EXT_RS_OFFSET]);
++	       map->page_00h[SFF8636_EXT_RS_OFFSET]);
+ }
+ 
+-static void sff8636_show_oui(const __u8 *id, int id_offset)
+-{
+-	sff8024_show_oui(id, id_offset);
+-}
+-
+-static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id)
++static void
++sff8636_show_wavelength_or_copper_compliance(const struct sff8636_memory_map *map)
+ {
+ 	printf("\t%-41s : 0x%02x", "Transmitter technology",
+-		(id[SFF8636_DEVICE_TECH_OFFSET] & SFF8636_TRANS_TECH_MASK));
++	       map->page_00h[SFF8636_DEVICE_TECH_OFFSET] &
++	       SFF8636_TRANS_TECH_MASK);
+ 
+-	switch (id[SFF8636_DEVICE_TECH_OFFSET] & SFF8636_TRANS_TECH_MASK) {
++	switch (map->page_00h[SFF8636_DEVICE_TECH_OFFSET] &
++		SFF8636_TRANS_TECH_MASK) {
+ 	case SFF8636_TRANS_850_VCSEL:
+ 		printf(" (850 nm VCSEL)\n");
+ 		break;
+@@ -650,31 +670,26 @@ static void sff8636_show_wavelength_or_copper_compliance(const __u8 *id)
+ 		break;
+ 	}
+ 
+-	if ((id[SFF8636_DEVICE_TECH_OFFSET] & SFF8636_TRANS_TECH_MASK)
+-			>= SFF8636_TRANS_COPPER_PAS_UNEQUAL) {
++	if ((map->page_00h[SFF8636_DEVICE_TECH_OFFSET] &
++	     SFF8636_TRANS_TECH_MASK) >= SFF8636_TRANS_COPPER_PAS_UNEQUAL) {
+ 		printf("\t%-41s : %udb\n", "Attenuation at 2.5GHz",
+-			id[SFF8636_WAVELEN_HIGH_BYTE_OFFSET]);
++			map->page_00h[SFF8636_WAVELEN_HIGH_BYTE_OFFSET]);
+ 		printf("\t%-41s : %udb\n", "Attenuation at 5.0GHz",
+-			id[SFF8636_WAVELEN_LOW_BYTE_OFFSET]);
++			map->page_00h[SFF8636_WAVELEN_LOW_BYTE_OFFSET]);
+ 		printf("\t%-41s : %udb\n", "Attenuation at 7.0GHz",
+-			id[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET]);
++			map->page_00h[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET]);
+ 		printf("\t%-41s : %udb\n", "Attenuation at 12.9GHz",
+-			id[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET]);
++		       map->page_00h[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET]);
+ 	} else {
+ 		printf("\t%-41s : %.3lfnm\n", "Laser wavelength",
+-			(((id[SFF8636_WAVELEN_HIGH_BYTE_OFFSET] << 8) |
+-				id[SFF8636_WAVELEN_LOW_BYTE_OFFSET])*0.05));
++		       (((map->page_00h[SFF8636_WAVELEN_HIGH_BYTE_OFFSET] << 8) |
++			 map->page_00h[SFF8636_WAVELEN_LOW_BYTE_OFFSET]) * 0.05));
+ 		printf("\t%-41s : %.3lfnm\n", "Laser wavelength tolerance",
+-			(((id[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET] << 8) |
+-			id[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET])*0.005));
++		       (((map->page_00h[SFF8636_WAVE_TOL_HIGH_BYTE_OFFSET] << 8) |
++			 map->page_00h[SFF8636_WAVE_TOL_LOW_BYTE_OFFSET]) * 0.005));
+ 	}
+ }
+ 
+-static void sff8636_show_revision_compliance(const __u8 *id)
+-{
+-	sff_show_revision_compliance(id, SFF8636_REV_COMPLIANCE_OFFSET);
+-}
+-
+ /*
+  * 2-byte internal temperature conversions:
+  * First byte is a signed 8-bit integer, which is the temp decimal part
+@@ -683,39 +698,65 @@ static void sff8636_show_revision_compliance(const __u8 *id)
+ #define SFF8636_OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset))
+ #define OFFSET_TO_U16_PTR(ptr, offset) (ptr[offset] << 8 | ptr[(offset) + 1])
+ 
+-static void sff8636_dom_parse(const __u8 *id, const __u8 *page_three, struct sff_diags *sd)
++static void sff8636_dom_parse(const struct sff8636_memory_map *map,
++			      struct sff_diags *sd)
+ {
++	const __u8 *id = map->lower_memory;
+ 	int i = 0;
+ 
+ 	/* Monitoring Thresholds for Alarms and Warnings */
+ 	sd->sfp_voltage[MCURR] = OFFSET_TO_U16_PTR(id, SFF8636_VCC_CURR);
+-	sd->sfp_voltage[HALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_VCC_HALRM);
+-	sd->sfp_voltage[LALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_VCC_LALRM);
+-	sd->sfp_voltage[HWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_VCC_HWARN);
+-	sd->sfp_voltage[LWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_VCC_LWARN);
+-
+ 	sd->sfp_temp[MCURR] = SFF8636_OFFSET_TO_TEMP(SFF8636_TEMP_CURR);
+-	sd->sfp_temp[HALRM] = (__s16)OFFSET_TO_U16_PTR(page_three, SFF8636_TEMP_HALRM);
+-	sd->sfp_temp[LALRM] = (__s16)OFFSET_TO_U16_PTR(page_three, SFF8636_TEMP_LALRM);
+-	sd->sfp_temp[HWARN] = (__s16)OFFSET_TO_U16_PTR(page_three, SFF8636_TEMP_HWARN);
+-	sd->sfp_temp[LWARN] = (__s16)OFFSET_TO_U16_PTR(page_three, SFF8636_TEMP_LWARN);
+-
+-	sd->bias_cur[HALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_BIAS_HALRM);
+-	sd->bias_cur[LALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_BIAS_LALRM);
+-	sd->bias_cur[HWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_BIAS_HWARN);
+-	sd->bias_cur[LWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_BIAS_LWARN);
+-
+-	sd->tx_power[HALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_PWR_HALRM);
+-	sd->tx_power[LALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_PWR_LALRM);
+-	sd->tx_power[HWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_PWR_HWARN);
+-	sd->tx_power[LWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_TX_PWR_LWARN);
+-
+-	sd->rx_power[HALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_RX_PWR_HALRM);
+-	sd->rx_power[LALRM] = OFFSET_TO_U16_PTR(page_three, SFF8636_RX_PWR_LALRM);
+-	sd->rx_power[HWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_RX_PWR_HWARN);
+-	sd->rx_power[LWARN] = OFFSET_TO_U16_PTR(page_three, SFF8636_RX_PWR_LWARN);
+-
+ 
++	if (!map->page_03h)
++		goto out;
++
++	sd->sfp_voltage[HALRM] = OFFSET_TO_U16_PTR(map->page_03h,
++						   SFF8636_VCC_HALRM);
++	sd->sfp_voltage[LALRM] = OFFSET_TO_U16_PTR(map->page_03h,
++						   SFF8636_VCC_LALRM);
++	sd->sfp_voltage[HWARN] = OFFSET_TO_U16_PTR(map->page_03h,
++						   SFF8636_VCC_HWARN);
++	sd->sfp_voltage[LWARN] = OFFSET_TO_U16_PTR(map->page_03h,
++						   SFF8636_VCC_LWARN);
++
++	sd->sfp_temp[HALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_03h,
++						       SFF8636_TEMP_HALRM);
++	sd->sfp_temp[LALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_03h,
++						       SFF8636_TEMP_LALRM);
++	sd->sfp_temp[HWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_03h,
++						       SFF8636_TEMP_HWARN);
++	sd->sfp_temp[LWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_03h,
++						       SFF8636_TEMP_LWARN);
++
++	sd->bias_cur[HALRM] = OFFSET_TO_U16_PTR(map->page_03h,
++						SFF8636_TX_BIAS_HALRM);
++	sd->bias_cur[LALRM] = OFFSET_TO_U16_PTR(map->page_03h,
++						SFF8636_TX_BIAS_LALRM);
++	sd->bias_cur[HWARN] = OFFSET_TO_U16_PTR(map->page_03h,
++						SFF8636_TX_BIAS_HWARN);
++	sd->bias_cur[LWARN] = OFFSET_TO_U16_PTR(map->page_03h,
++						SFF8636_TX_BIAS_LWARN);
++
++	sd->tx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_03h,
++						SFF8636_TX_PWR_HALRM);
++	sd->tx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_03h,
++						SFF8636_TX_PWR_LALRM);
++	sd->tx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_03h,
++						SFF8636_TX_PWR_HWARN);
++	sd->tx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_03h,
++						SFF8636_TX_PWR_LWARN);
++
++	sd->rx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_03h,
++						SFF8636_RX_PWR_HALRM);
++	sd->rx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_03h,
++						SFF8636_RX_PWR_LALRM);
++	sd->rx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_03h,
++						SFF8636_RX_PWR_HWARN);
++	sd->rx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_03h,
++						SFF8636_RX_PWR_LWARN);
++
++out:
+ 	/* Channel Specific Data */
+ 	for (i = 0; i < MAX_CHANNEL_NUM; i++) {
+ 		u8 rx_power_offset, tx_bias_offset;
+@@ -749,7 +790,7 @@ static void sff8636_dom_parse(const __u8 *id, const __u8 *page_three, struct sff
+ 	}
+ }
+ 
+-static void sff8636_show_dom(const __u8 *id, const __u8 *page_three, __u32 eeprom_len)
++static void sff8636_show_dom(const struct sff8636_memory_map *map)
+ {
+ 	struct sff_diags sd = {0};
+ 	char *rx_power_string = NULL;
+@@ -763,20 +804,15 @@ static void sff8636_show_dom(const __u8 *id, const __u8 *page_three, __u32 eepro
+ 	 * and thresholds
+ 	 * If pagging support exists, then supports_alarms is marked as 1
+ 	 */
++	if (map->page_03h)
++		sd.supports_alarms = 1;
+ 
+-	if (eeprom_len == ETH_MODULE_SFF_8636_MAX_LEN) {
+-		if (!(id[SFF8636_STATUS_2_OFFSET] &
+-					SFF8636_STATUS_PAGE_3_PRESENT)) {
+-			sd.supports_alarms = 1;
+-		}
+-	}
++	sd.rx_power_type = map->page_00h[SFF8636_DIAG_TYPE_OFFSET] &
++			   SFF8636_RX_PWR_TYPE_MASK;
++	sd.tx_power_type = map->page_00h[SFF8636_DIAG_TYPE_OFFSET] &
++			   SFF8636_RX_PWR_TYPE_MASK;
+ 
+-	sd.rx_power_type = id[SFF8636_DIAG_TYPE_OFFSET] &
+-						SFF8636_RX_PWR_TYPE_MASK;
+-	sd.tx_power_type = id[SFF8636_DIAG_TYPE_OFFSET] &
+-						SFF8636_RX_PWR_TYPE_MASK;
+-
+-	sff8636_dom_parse(id, page_three, &sd);
++	sff8636_dom_parse(map, &sd);
+ 
+ 	PRINT_TEMP("Module temperature", sd.sfp_temp[MCURR]);
+ 	PRINT_VCC("Module voltage", sd.sfp_voltage[MCURR]);
+@@ -819,7 +855,7 @@ static void sff8636_show_dom(const __u8 *id, const __u8 *page_three, __u32 eepro
+ 	if (sd.supports_alarms) {
+ 		for (i = 0; sff8636_aw_flags[i].str; ++i) {
+ 			printf("\t%-41s : %s\n", sff8636_aw_flags[i].str,
+-			       id[sff8636_aw_flags[i].offset]
++			       map->lower_memory[sff8636_aw_flags[i].offset]
+ 			       & sff8636_aw_flags[i].value ? "On" : "Off");
+ 		}
+ 
+@@ -827,39 +863,39 @@ static void sff8636_show_dom(const __u8 *id, const __u8 *page_three, __u32 eepro
+ 	}
+ }
+ 
+-static void sff8636_show_page_zero(const __u8 *id)
++static void sff8636_show_page_zero(const struct sff8636_memory_map *map)
+ {
+-	sff8636_show_ext_identifier(id);
+-	sff8636_show_connector(id);
+-	sff8636_show_transceiver(id);
+-	sff8636_show_encoding(id);
+-	sff_show_value_with_unit(id, SFF8636_BR_NOMINAL_OFFSET,
+-			"BR, Nominal", 100, "Mbps");
+-	sff8636_show_rate_identifier(id);
+-	sff_show_value_with_unit(id, SFF8636_SM_LEN_OFFSET,
+-		     "Length (SMF,km)", 1, "km");
+-	sff_show_value_with_unit(id, SFF8636_OM3_LEN_OFFSET,
+-			"Length (OM3 50um)", 2, "m");
+-	sff_show_value_with_unit(id, SFF8636_OM2_LEN_OFFSET,
+-			"Length (OM2 50um)", 1, "m");
+-	sff_show_value_with_unit(id, SFF8636_OM1_LEN_OFFSET,
+-		     "Length (OM1 62.5um)", 1, "m");
+-	sff_show_value_with_unit(id, SFF8636_CBL_LEN_OFFSET,
+-		     "Length (Copper or Active cable)", 1, "m");
+-	sff8636_show_wavelength_or_copper_compliance(id);
+-	sff_show_ascii(id, SFF8636_VENDOR_NAME_START_OFFSET,
++	sff8636_show_ext_identifier(map);
++	sff8636_show_connector(map);
++	sff8636_show_transceiver(map);
++	sff8636_show_encoding(map);
++	sff_show_value_with_unit(map->page_00h, SFF8636_BR_NOMINAL_OFFSET,
++				 "BR, Nominal", 100, "Mbps");
++	sff8636_show_rate_identifier(map);
++	sff_show_value_with_unit(map->page_00h, SFF8636_SM_LEN_OFFSET,
++				 "Length (SMF,km)", 1, "km");
++	sff_show_value_with_unit(map->page_00h, SFF8636_OM3_LEN_OFFSET,
++				 "Length (OM3 50um)", 2, "m");
++	sff_show_value_with_unit(map->page_00h, SFF8636_OM2_LEN_OFFSET,
++				 "Length (OM2 50um)", 1, "m");
++	sff_show_value_with_unit(map->page_00h, SFF8636_OM1_LEN_OFFSET,
++				 "Length (OM1 62.5um)", 1, "m");
++	sff_show_value_with_unit(map->page_00h, SFF8636_CBL_LEN_OFFSET,
++				 "Length (Copper or Active cable)", 1, "m");
++	sff8636_show_wavelength_or_copper_compliance(map);
++	sff_show_ascii(map->page_00h, SFF8636_VENDOR_NAME_START_OFFSET,
+ 		       SFF8636_VENDOR_NAME_END_OFFSET, "Vendor name");
+-	sff8636_show_oui(id, SFF8636_VENDOR_OUI_OFFSET);
+-	sff_show_ascii(id, SFF8636_VENDOR_PN_START_OFFSET,
++	sff8024_show_oui(map->page_00h, SFF8636_VENDOR_OUI_OFFSET);
++	sff_show_ascii(map->page_00h, SFF8636_VENDOR_PN_START_OFFSET,
+ 		       SFF8636_VENDOR_PN_END_OFFSET, "Vendor PN");
+-	sff_show_ascii(id, SFF8636_VENDOR_REV_START_OFFSET,
++	sff_show_ascii(map->page_00h, SFF8636_VENDOR_REV_START_OFFSET,
+ 		       SFF8636_VENDOR_REV_END_OFFSET, "Vendor rev");
+-	sff_show_ascii(id, SFF8636_VENDOR_SN_START_OFFSET,
++	sff_show_ascii(map->page_00h, SFF8636_VENDOR_SN_START_OFFSET,
+ 		       SFF8636_VENDOR_SN_END_OFFSET, "Vendor SN");
+-	sff_show_ascii(id, SFF8636_DATE_YEAR_OFFSET,
++	sff_show_ascii(map->page_00h, SFF8636_DATE_YEAR_OFFSET,
+ 		       SFF8636_DATE_VENDOR_LOT_OFFSET + 1, "Date code");
+-	sff8636_show_revision_compliance(id);
+-
++	sff_show_revision_compliance(map->lower_memory,
++				     SFF8636_REV_COMPLIANCE_OFFSET);
+ }
+ 
+ static void sff8636_memory_map_init_buf(struct sff8636_memory_map *map,
+@@ -896,13 +932,13 @@ void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len)
+ 
+ 	sff8636_memory_map_init_buf(&map, id, eeprom_len);
+ 
+-	sff8636_show_identifier(id);
+-	switch (id[SFF8636_ID_OFFSET]) {
++	sff8636_show_identifier(&map);
++	switch (map.lower_memory[SFF8636_ID_OFFSET]) {
+ 	case SFF8024_ID_QSFP:
+ 	case SFF8024_ID_QSFP_PLUS:
+ 	case SFF8024_ID_QSFP28:
+-		sff8636_show_page_zero(id);
+-		sff8636_show_dom(id, id + 3 * 0x80, eeprom_len);
++		sff8636_show_page_zero(&map);
++		sff8636_show_dom(&map);
+ 		break;
+ 	}
+ }
+@@ -939,9 +975,7 @@ void sff8636_show_all_nl(const struct ethtool_module_eeprom *page_zero,
+ 
+ 	sff8636_memory_map_init_pages(&map, page_zero, page_three);
+ 
+-	sff8636_show_identifier(page_zero->data);
+-	sff8636_show_page_zero(page_zero->data);
+-	if (page_three)
+-		sff8636_show_dom(page_zero->data, page_three->data - 0x80,
+-				 ETH_MODULE_SFF_8636_MAX_LEN);
++	sff8636_show_identifier(&map);
++	sff8636_show_page_zero(&map);
++	sff8636_show_dom(&map);
+ }
+-- 
+2.35.1
+
diff --git a/SOURCES/0020-sff-8636-Consolidate-code-between-IOCTL-and-netlink-.patch b/SOURCES/0020-sff-8636-Consolidate-code-between-IOCTL-and-netlink-.patch
new file mode 100644
index 0000000..3ab7d8a
--- /dev/null
+++ b/SOURCES/0020-sff-8636-Consolidate-code-between-IOCTL-and-netlink-.patch
@@ -0,0 +1,73 @@
+From 2e122ad9aa2aa0259df1035e3ec2765d8e008394 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 12 Oct 2021 16:25:19 +0300
+Subject: [PATCH 20/35] sff-8636: Consolidate code between IOCTL and netlink
+ paths
+
+Now that both the netlink and IOCTL paths use the same memory map
+structure for parsing, the code can be easily consolidated.
+
+Note that the switch-case statement is not necessary for the netlink
+path, as the netlink code (i.e., netlink/module-eeprom.c) already
+performed the check, but it is required for the IOCTL path.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ qsfp.c | 29 +++++++++++++++--------------
+ 1 file changed, 15 insertions(+), 14 deletions(-)
+
+diff --git a/qsfp.c b/qsfp.c
+index 354b3b1ce9ff..4aa49351e6b7 100644
+--- a/qsfp.c
++++ b/qsfp.c
+@@ -898,6 +898,19 @@ static void sff8636_show_page_zero(const struct sff8636_memory_map *map)
+ 				     SFF8636_REV_COMPLIANCE_OFFSET);
+ }
+ 
++static void sff8636_show_all_common(const struct sff8636_memory_map *map)
++{
++	sff8636_show_identifier(map);
++	switch (map->lower_memory[SFF8636_ID_OFFSET]) {
++	case SFF8024_ID_QSFP:
++	case SFF8024_ID_QSFP_PLUS:
++	case SFF8024_ID_QSFP28:
++		sff8636_show_page_zero(map);
++		sff8636_show_dom(map);
++		break;
++	}
++}
++
+ static void sff8636_memory_map_init_buf(struct sff8636_memory_map *map,
+ 					const __u8 *id, __u32 eeprom_len)
+ {
+@@ -931,16 +944,7 @@ void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len)
+ 	}
+ 
+ 	sff8636_memory_map_init_buf(&map, id, eeprom_len);
+-
+-	sff8636_show_identifier(&map);
+-	switch (map.lower_memory[SFF8636_ID_OFFSET]) {
+-	case SFF8024_ID_QSFP:
+-	case SFF8024_ID_QSFP_PLUS:
+-	case SFF8024_ID_QSFP28:
+-		sff8636_show_page_zero(&map);
+-		sff8636_show_dom(&map);
+-		break;
+-	}
++	sff8636_show_all_common(&map);
+ }
+ 
+ static void
+@@ -974,8 +978,5 @@ void sff8636_show_all_nl(const struct ethtool_module_eeprom *page_zero,
+ 	struct sff8636_memory_map map = {};
+ 
+ 	sff8636_memory_map_init_pages(&map, page_zero, page_three);
+-
+-	sff8636_show_identifier(&map);
+-	sff8636_show_page_zero(&map);
+-	sff8636_show_dom(&map);
++	sff8636_show_all_common(&map);
+ }
+-- 
+2.35.1
+
diff --git a/SOURCES/0021-sff-8079-Split-SFF-8079-parsing-function.patch b/SOURCES/0021-sff-8079-Split-SFF-8079-parsing-function.patch
new file mode 100644
index 0000000..89af1b3
--- /dev/null
+++ b/SOURCES/0021-sff-8079-Split-SFF-8079-parsing-function.patch
@@ -0,0 +1,101 @@
+From 1f20fb8e8b94d049672e48388ae57f89e89e880b Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 12 Oct 2021 16:25:20 +0300
+Subject: [PATCH 21/35] sff-8079: Split SFF-8079 parsing function
+
+SFF-8079, unlike CMIS and SFF-8636, only has a single page and therefore
+its parsing function (i.e., sff8079_show_all()) is called from both the
+IOCTL and netlink paths with a buffer pointing to that single page.
+
+In future patches, the netlink code (i.e., netlink/module-eeprom.c) will
+no longer call the SFF-8079 code with a buffer pointing to the first 128
+bytes of the EEPROM. Instead, the SFF-8079 code will need to request the
+needed EEPROM data, as will be done in CMIS and SFF-8636.
+
+Therefore, as a preparation for this change, split the main parsing
+function into IOCTL and netlink variants.
+
+No functional changes intended.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ ethtool.c               |  4 ++--
+ internal.h              |  3 ++-
+ netlink/module-eeprom.c |  2 +-
+ sfpid.c                 | 12 +++++++++++-
+ 4 files changed, 16 insertions(+), 5 deletions(-)
+
+diff --git a/ethtool.c b/ethtool.c
+index 6c744ff84eb9..5d4b5afbfd47 100644
+--- a/ethtool.c
++++ b/ethtool.c
+@@ -4900,10 +4900,10 @@ static int do_getmodule(struct cmd_context *ctx)
+ 			switch (modinfo.type) {
+ #ifdef ETHTOOL_ENABLE_PRETTY_DUMP
+ 			case ETH_MODULE_SFF_8079:
+-				sff8079_show_all(eeprom->data);
++				sff8079_show_all_ioctl(eeprom->data);
+ 				break;
+ 			case ETH_MODULE_SFF_8472:
+-				sff8079_show_all(eeprom->data);
++				sff8079_show_all_ioctl(eeprom->data);
+ 				sff8472_show_all(eeprom->data);
+ 				break;
+ 			case ETH_MODULE_SFF_8436:
+diff --git a/internal.h b/internal.h
+index 7ca6066d4e12..a77efd385698 100644
+--- a/internal.h
++++ b/internal.h
+@@ -384,7 +384,8 @@ int rxclass_rule_ins(struct cmd_context *ctx,
+ int rxclass_rule_del(struct cmd_context *ctx, __u32 loc);
+ 
+ /* Module EEPROM parsing code */
+-void sff8079_show_all(const __u8 *id);
++void sff8079_show_all_ioctl(const __u8 *id);
++void sff8079_show_all_nl(const __u8 *id);
+ 
+ /* Optics diagnostics */
+ void sff8472_show_all(const __u8 *id);
+diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c
+index 18b1abbe1252..101d5943c2bc 100644
+--- a/netlink/module-eeprom.c
++++ b/netlink/module-eeprom.c
+@@ -323,7 +323,7 @@ static void decoder_print(void)
+ 
+ 	switch (module_id) {
+ 	case SFF8024_ID_SFP:
+-		sff8079_show_all(page_zero->data);
++		sff8079_show_all_nl(page_zero->data);
+ 		break;
+ 	case SFF8024_ID_QSFP:
+ 	case SFF8024_ID_QSFP28:
+diff --git a/sfpid.c b/sfpid.c
+index da2b3f4df3d2..c214820226d1 100644
+--- a/sfpid.c
++++ b/sfpid.c
+@@ -396,7 +396,7 @@ static void sff8079_show_options(const __u8 *id)
+ 		printf("%s Power level 3 requirement\n", pfx);
+ }
+ 
+-void sff8079_show_all(const __u8 *id)
++static void sff8079_show_all_common(const __u8 *id)
+ {
+ 	sff8079_show_identifier(id);
+ 	if (((id[0] == 0x02) || (id[0] == 0x03)) && (id[1] == 0x04)) {
+@@ -439,3 +439,13 @@ void sff8079_show_all(const __u8 *id)
+ 		sff8079_show_ascii(id, 84, 91, "Date code");
+ 	}
+ }
++
++void sff8079_show_all_ioctl(const __u8 *id)
++{
++	sff8079_show_all_common(id);
++}
++
++void sff8079_show_all_nl(const __u8 *id)
++{
++	sff8079_show_all_common(id);
++}
+-- 
+2.35.1
+
diff --git a/SOURCES/0022-netlink-eeprom-Export-a-function-to-request-an-EEPRO.patch b/SOURCES/0022-netlink-eeprom-Export-a-function-to-request-an-EEPRO.patch
new file mode 100644
index 0000000..69cb7c3
--- /dev/null
+++ b/SOURCES/0022-netlink-eeprom-Export-a-function-to-request-an-EEPRO.patch
@@ -0,0 +1,174 @@
+From fdb457a0ebb57c99fb987d0e34b2549f10dd4161 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 12 Oct 2021 16:25:21 +0300
+Subject: [PATCH 22/35] netlink: eeprom: Export a function to request an EEPROM
+ page
+
+The function will be used by the EEPROM parsing code (e.g., cmis.c) to
+request a specific page for parsing.
+
+All the data buffers used to store EEPROM page contents are stored on a
+linked list that is flushed on exit. This relieves callers from the need
+to explicitly free the requested pages.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ netlink/extapi.h        |  11 +++++
+ netlink/module-eeprom.c | 105 ++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 116 insertions(+)
+
+diff --git a/netlink/extapi.h b/netlink/extapi.h
+index 91bf02b5e3be..129e2931d01d 100644
+--- a/netlink/extapi.h
++++ b/netlink/extapi.h
+@@ -48,6 +48,9 @@ int nl_getmodule(struct cmd_context *ctx);
+ 
+ void nl_monitor_usage(void);
+ 
++int nl_get_eeprom_page(struct cmd_context *ctx,
++		       struct ethtool_module_eeprom *request);
++
+ #else /* ETHTOOL_ENABLE_NETLINK */
+ 
+ static inline void netlink_run_handler(struct cmd_context *ctx __maybe_unused,
+@@ -73,6 +76,14 @@ static inline void nl_monitor_usage(void)
+ {
+ }
+ 
++static inline int
++nl_get_eeprom_page(struct cmd_context *ctx __maybe_unused,
++		   struct ethtool_module_eeprom *request __maybe_unused)
++{
++	fprintf(stderr, "Netlink not supported by ethtool.\n");
++	return -EOPNOTSUPP;
++}
++
+ #define nl_gset			NULL
+ #define nl_sset			NULL
+ #define nl_permaddr		NULL
+diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c
+index 101d5943c2bc..ee5508840157 100644
+--- a/netlink/module-eeprom.c
++++ b/netlink/module-eeprom.c
+@@ -341,6 +341,110 @@ static void decoder_print(void)
+ }
+ #endif
+ 
++static struct list_head eeprom_page_list = LIST_HEAD_INIT(eeprom_page_list);
++
++struct eeprom_page_entry {
++	struct list_head list;	/* Member of eeprom_page_list */
++	void *data;
++};
++
++static int eeprom_page_list_add(void *data)
++{
++	struct eeprom_page_entry *entry;
++
++	entry = malloc(sizeof(*entry));
++	if (!entry)
++		return -ENOMEM;
++
++	entry->data = data;
++	list_add(&entry->list, &eeprom_page_list);
++
++	return 0;
++}
++
++static void eeprom_page_list_flush(void)
++{
++	struct eeprom_page_entry *entry;
++	struct list_head *head, *next;
++
++	list_for_each_safe(head, next, &eeprom_page_list) {
++		entry = (struct eeprom_page_entry *) head;
++		free(entry->data);
++		list_del(head);
++		free(entry);
++	}
++}
++
++static int get_eeprom_page_reply_cb(const struct nlmsghdr *nlhdr, void *data)
++{
++	const struct nlattr *tb[ETHTOOL_A_MODULE_EEPROM_DATA + 1] = {};
++	struct ethtool_module_eeprom *request = data;
++	DECLARE_ATTR_TB_INFO(tb);
++	u8 *eeprom_data;
++	int ret;
++
++	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
++	if (ret < 0)
++		return ret;
++
++	if (!tb[ETHTOOL_A_MODULE_EEPROM_DATA])
++		return MNL_CB_ERROR;
++
++	eeprom_data = mnl_attr_get_payload(tb[ETHTOOL_A_MODULE_EEPROM_DATA]);
++	request->data = malloc(request->length);
++	if (!request->data)
++		return MNL_CB_ERROR;
++	memcpy(request->data, eeprom_data, request->length);
++
++	ret = eeprom_page_list_add(request->data);
++	if (ret < 0)
++		goto err_list_add;
++
++	return MNL_CB_OK;
++
++err_list_add:
++	free(request->data);
++	return MNL_CB_ERROR;
++}
++
++int nl_get_eeprom_page(struct cmd_context *ctx,
++		       struct ethtool_module_eeprom *request)
++{
++	struct nl_context *nlctx = ctx->nlctx;
++	struct nl_socket *nlsock;
++	struct nl_msg_buff *msg;
++	int ret;
++
++	if (!request || request->i2c_address > ETH_I2C_MAX_ADDRESS)
++		return -EINVAL;
++
++	nlsock = nlctx->ethnl_socket;
++	msg = &nlsock->msgbuff;
++
++	ret = nlsock_prep_get_request(nlsock, ETHTOOL_MSG_MODULE_EEPROM_GET,
++				      ETHTOOL_A_MODULE_EEPROM_HEADER, 0);
++	if (ret < 0)
++		return ret;
++
++	if (ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_LENGTH,
++			   request->length) ||
++	    ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_OFFSET,
++			   request->offset) ||
++	    ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_PAGE,
++			  request->page) ||
++	    ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_BANK,
++			  request->bank) ||
++	    ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS,
++			  request->i2c_address))
++		return -EMSGSIZE;
++
++	ret = nlsock_sendmsg(nlsock, NULL);
++	if (ret < 0)
++		return ret;
++	return nlsock_process_reply(nlsock, get_eeprom_page_reply_cb,
++				    (void *)request);
++}
++
+ int nl_getmodule(struct cmd_context *ctx)
+ {
+ 	struct cmd_params getmodule_cmd_params = {};
+@@ -425,6 +529,7 @@ int nl_getmodule(struct cmd_context *ctx)
+ 	}
+ 
+ cleanup:
++	eeprom_page_list_flush();
+ 	cache_free();
+ 	return ret;
+ }
+-- 
+2.35.1
+
diff --git a/SOURCES/0023-cmis-Request-specific-pages-for-parsing-in-netlink-p.patch b/SOURCES/0023-cmis-Request-specific-pages-for-parsing-in-netlink-p.patch
new file mode 100644
index 0000000..5dbd998
--- /dev/null
+++ b/SOURCES/0023-cmis-Request-specific-pages-for-parsing-in-netlink-p.patch
@@ -0,0 +1,194 @@
+From 5f45f370e132f144cdbab9ea718393bd37ee23db Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 12 Oct 2021 16:25:22 +0300
+Subject: [PATCH 23/35] cmis: Request specific pages for parsing in netlink
+ path
+
+In the netlink path, unlike the IOCTL path, user space requests specific
+EEPROM pages from the kernel. The presence of optional and banked pages
+is advertised via various bits in the EEPROM contents.
+
+Currently, for CMIS, the Lower Memory, Page 00h and the optional Page
+01h are requested by the netlink code (i.e., netlink/module-eeprom.c)
+and passed to the CMIS code (i.e., cmis.c) as two arguments for parsing.
+
+This is problematic for several reasons. First, this approach is not
+very scaleable as CMIS supports a lot of optional and banked pages.
+Passing them as separate arguments to the CMIS code is not going to
+work.
+
+Second, the knowledge of which optional and banked pages are available
+is encapsulated in the CMIS parsing code. As such, the common netlink
+code has no business of fetching optional and banked pages that might be
+invalid.
+
+Instead, pass the command context to the CMIS parsing function and allow
+it to fetch only valid pages via the 'MODULE_EEPROM_GET' netlink
+message.
+
+Tested by making sure that the output of 'ethtool -m' does not change
+before and after the patch.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ cmis.c                  | 60 ++++++++++++++++++++++++++++++++---------
+ cmis.h                  |  3 +--
+ netlink/module-eeprom.c |  7 +++--
+ 3 files changed, 51 insertions(+), 19 deletions(-)
+
+diff --git a/cmis.c b/cmis.c
+index eb7791dd59df..4798fd4c7d68 100644
+--- a/cmis.c
++++ b/cmis.c
+@@ -9,9 +9,11 @@
+ 
+ #include <stdio.h>
+ #include <math.h>
++#include <errno.h>
+ #include "internal.h"
+ #include "sff-common.h"
+ #include "cmis.h"
++#include "netlink/extapi.h"
+ 
+ struct cmis_memory_map {
+ 	const __u8 *lower_memory;
+@@ -21,6 +23,7 @@ struct cmis_memory_map {
+ };
+ 
+ #define CMIS_PAGE_SIZE		0x80
++#define CMIS_I2C_ADDRESS	0x50
+ 
+ static void cmis_show_identifier(const struct cmis_memory_map *map)
+ {
+@@ -384,36 +387,67 @@ void cmis_show_all_ioctl(const __u8 *id)
+ 	cmis_show_all_common(&map);
+ }
+ 
+-static void
+-cmis_memory_map_init_pages(struct cmis_memory_map *map,
+-			   const struct ethtool_module_eeprom *page_zero,
+-			   const struct ethtool_module_eeprom *page_one)
++static void cmis_request_init(struct ethtool_module_eeprom *request, u8 bank,
++			      u8 page, u32 offset)
+ {
++	request->offset = offset;
++	request->length = CMIS_PAGE_SIZE;
++	request->page = page;
++	request->bank = bank;
++	request->i2c_address = CMIS_I2C_ADDRESS;
++	request->data = NULL;
++}
++
++static int
++cmis_memory_map_init_pages(struct cmd_context *ctx,
++			   struct cmis_memory_map *map)
++{
++	struct ethtool_module_eeprom request;
++	int ret;
++
+ 	/* Lower Memory and Page 00h are always present.
+ 	 *
+ 	 * Offset into Upper Memory is between page size and twice the page
+ 	 * size. Therefore, set the base address of each page to its base
+-	 * address minus page size. For Page 00h, this is the address of the
+-	 * Lower Memory.
++	 * address minus page size.
+ 	 */
+-	map->lower_memory = page_zero->data;
+-	map->page_00h = page_zero->data;
++	cmis_request_init(&request, 0, 0x0, 0);
++	ret = nl_get_eeprom_page(ctx, &request);
++	if (ret < 0)
++		return ret;
++	map->lower_memory = request.data;
++
++	cmis_request_init(&request, 0, 0x0, CMIS_PAGE_SIZE);
++	ret = nl_get_eeprom_page(ctx, &request);
++	if (ret < 0)
++		return ret;
++	map->page_00h = request.data - CMIS_PAGE_SIZE;
+ 
+ 	/* Page 01h is only present when the module memory model is paged and
+ 	 * not flat.
+ 	 */
+ 	if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] &
+ 	    CMIS_MEMORY_MODEL_MASK)
+-		return;
++		return 0;
++
++	cmis_request_init(&request, 0, 0x1, CMIS_PAGE_SIZE);
++	ret = nl_get_eeprom_page(ctx, &request);
++	if (ret < 0)
++		return ret;
++	map->page_01h = request.data - CMIS_PAGE_SIZE;
+ 
+-	map->page_01h = page_one->data - CMIS_PAGE_SIZE;
++	return 0;
+ }
+ 
+-void cmis_show_all_nl(const struct ethtool_module_eeprom *page_zero,
+-		      const struct ethtool_module_eeprom *page_one)
++int cmis_show_all_nl(struct cmd_context *ctx)
+ {
+ 	struct cmis_memory_map map = {};
++	int ret;
+ 
+-	cmis_memory_map_init_pages(&map, page_zero, page_one);
++	ret = cmis_memory_map_init_pages(ctx, &map);
++	if (ret < 0)
++		return ret;
+ 	cmis_show_all_common(&map);
++
++	return 0;
+ }
+diff --git a/cmis.h b/cmis.h
+index c878e3bc5afd..911491dc5c8f 100644
+--- a/cmis.h
++++ b/cmis.h
+@@ -123,7 +123,6 @@
+ 
+ void cmis_show_all_ioctl(const __u8 *id);
+ 
+-void cmis_show_all_nl(const struct ethtool_module_eeprom *page_zero,
+-		      const struct ethtool_module_eeprom *page_one);
++int cmis_show_all_nl(struct cmd_context *ctx);
+ 
+ #endif /* CMIS_H__ */
+diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c
+index ee5508840157..a8e2662e0b8c 100644
+--- a/netlink/module-eeprom.c
++++ b/netlink/module-eeprom.c
+@@ -314,11 +314,10 @@ static int decoder_prefetch(struct nl_context *nlctx)
+ 	return page_fetch(nlctx, &request);
+ }
+ 
+-static void decoder_print(void)
++static void decoder_print(struct cmd_context *ctx)
+ {
+ 	struct ethtool_module_eeprom *page_three = cache_get(3, 0, ETH_I2C_ADDRESS_LOW);
+ 	struct ethtool_module_eeprom *page_zero = cache_get(0, 0, ETH_I2C_ADDRESS_LOW);
+-	struct ethtool_module_eeprom *page_one = cache_get(1, 0, ETH_I2C_ADDRESS_LOW);
+ 	u8 module_id = page_zero->data[SFF8636_ID_OFFSET];
+ 
+ 	switch (module_id) {
+@@ -332,7 +331,7 @@ static void decoder_print(void)
+ 		break;
+ 	case SFF8024_ID_QSFP_DD:
+ 	case SFF8024_ID_DSFP:
+-		cmis_show_all_nl(page_zero, page_one);
++		cmis_show_all_nl(ctx);
+ 		break;
+ 	default:
+ 		dump_hex(stdout, page_zero->data, page_zero->length, page_zero->offset);
+@@ -524,7 +523,7 @@ int nl_getmodule(struct cmd_context *ctx)
+ 		ret = decoder_prefetch(nlctx);
+ 		if (ret)
+ 			goto cleanup;
+-		decoder_print();
++		decoder_print(ctx);
+ #endif
+ 	}
+ 
+-- 
+2.35.1
+
diff --git a/SOURCES/0024-sff-8636-Request-specific-pages-for-parsing-in-netli.patch b/SOURCES/0024-sff-8636-Request-specific-pages-for-parsing-in-netli.patch
new file mode 100644
index 0000000..143d915
--- /dev/null
+++ b/SOURCES/0024-sff-8636-Request-specific-pages-for-parsing-in-netli.patch
@@ -0,0 +1,181 @@
+From fdae2732b25090f9d41e192a5dd47a45a6516a94 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 12 Oct 2021 16:25:23 +0300
+Subject: [PATCH 24/35] sff-8636: Request specific pages for parsing in netlink
+ path
+
+In the netlink path, unlike the IOCTL path, user space requests specific
+EEPROM pages from the kernel. The presence of optional pages is
+advertised via various bits in the EEPROM contents.
+
+Currently, for SFF-8636, the Lower Memory, Page 00h and the optional
+Page 03h are requested by the netlink code (i.e.,
+netlink/module-eeprom.c) and passed to the SFF-8636 code (i.e., qsfp.c)
+as two arguments for parsing.
+
+This is problematic for several reasons. First, this approach is not
+very scaleable as SFF-8636 supports a lot of optional pages. Passing
+them as separate arguments to the SFF-8636 code is not going to work.
+
+Second, the knowledge of which optional pages are available is
+encapsulated in the SFF-8636 parsing code. As such, the common netlink
+code has no business of fetching optional pages that might be invalid.
+
+Instead, pass the command context to the SFF-8636 parsing function and
+allow it to fetch only valid pages via the 'MODULE_EEPROM_GET' netlink
+message.
+
+Tested by making sure that the output of 'ethtool -m' does not change
+before and after the patch.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ internal.h              |  3 +--
+ netlink/module-eeprom.c |  3 +--
+ qsfp.c                  | 60 ++++++++++++++++++++++++++++++++---------
+ 3 files changed, 49 insertions(+), 17 deletions(-)
+
+diff --git a/internal.h b/internal.h
+index a77efd385698..2407d3c223fa 100644
+--- a/internal.h
++++ b/internal.h
+@@ -392,8 +392,7 @@ void sff8472_show_all(const __u8 *id);
+ 
+ /* QSFP Optics diagnostics */
+ void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len);
+-void sff8636_show_all_nl(const struct ethtool_module_eeprom *page_zero,
+-			 const struct ethtool_module_eeprom *page_three);
++int sff8636_show_all_nl(struct cmd_context *ctx);
+ 
+ /* FUJITSU Extended Socket network device */
+ int fjes_dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs);
+diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c
+index a8e2662e0b8c..f04f8e134223 100644
+--- a/netlink/module-eeprom.c
++++ b/netlink/module-eeprom.c
+@@ -316,7 +316,6 @@ static int decoder_prefetch(struct nl_context *nlctx)
+ 
+ static void decoder_print(struct cmd_context *ctx)
+ {
+-	struct ethtool_module_eeprom *page_three = cache_get(3, 0, ETH_I2C_ADDRESS_LOW);
+ 	struct ethtool_module_eeprom *page_zero = cache_get(0, 0, ETH_I2C_ADDRESS_LOW);
+ 	u8 module_id = page_zero->data[SFF8636_ID_OFFSET];
+ 
+@@ -327,7 +326,7 @@ static void decoder_print(struct cmd_context *ctx)
+ 	case SFF8024_ID_QSFP:
+ 	case SFF8024_ID_QSFP28:
+ 	case SFF8024_ID_QSFP_PLUS:
+-		sff8636_show_all_nl(page_zero, page_three);
++		sff8636_show_all_nl(ctx);
+ 		break;
+ 	case SFF8024_ID_QSFP_DD:
+ 	case SFF8024_ID_DSFP:
+diff --git a/qsfp.c b/qsfp.c
+index 4aa49351e6b7..e7c2f51cd9c6 100644
+--- a/qsfp.c
++++ b/qsfp.c
+@@ -55,10 +55,12 @@
+  **/
+ #include <stdio.h>
+ #include <math.h>
++#include <errno.h>
+ #include "internal.h"
+ #include "sff-common.h"
+ #include "qsfp.h"
+ #include "cmis.h"
++#include "netlink/extapi.h"
+ 
+ struct sff8636_memory_map {
+ 	const __u8 *lower_memory;
+@@ -68,6 +70,7 @@ struct sff8636_memory_map {
+ };
+ 
+ #define SFF8636_PAGE_SIZE	0x80
++#define SFF8636_I2C_ADDRESS	0x50
+ 
+ #define MAX_DESC_SIZE	42
+ 
+@@ -947,36 +950,67 @@ void sff8636_show_all_ioctl(const __u8 *id, __u32 eeprom_len)
+ 	sff8636_show_all_common(&map);
+ }
+ 
+-static void
+-sff8636_memory_map_init_pages(struct sff8636_memory_map *map,
+-			      const struct ethtool_module_eeprom *page_zero,
+-			      const struct ethtool_module_eeprom *page_three)
++static void sff8636_request_init(struct ethtool_module_eeprom *request, u8 page,
++				 u32 offset)
++{
++	request->offset = offset;
++	request->length = SFF8636_PAGE_SIZE;
++	request->page = page;
++	request->bank = 0;
++	request->i2c_address = SFF8636_I2C_ADDRESS;
++	request->data = NULL;
++}
++
++static int
++sff8636_memory_map_init_pages(struct cmd_context *ctx,
++			      struct sff8636_memory_map *map)
+ {
++	struct ethtool_module_eeprom request;
++	int ret;
++
+ 	/* Lower Memory and Page 00h are always present.
+ 	 *
+ 	 * Offset into Upper Memory is between page size and twice the page
+ 	 * size. Therefore, set the base address of each page to its base
+-	 * address minus page size. For Page 00h, this is the address of the
+-	 * Lower Memory.
++	 * address minus page size.
+ 	 */
+-	map->lower_memory = page_zero->data;
+-	map->page_00h = page_zero->data;
++	sff8636_request_init(&request, 0x0, 0);
++	ret = nl_get_eeprom_page(ctx, &request);
++	if (ret < 0)
++		return ret;
++	map->lower_memory = request.data;
++
++	sff8636_request_init(&request, 0x0, SFF8636_PAGE_SIZE);
++	ret = nl_get_eeprom_page(ctx, &request);
++	if (ret < 0)
++		return ret;
++	map->page_00h = request.data - SFF8636_PAGE_SIZE;
+ 
+ 	/* Page 03h is only present when the module memory model is paged and
+ 	 * not flat.
+ 	 */
+ 	if (map->lower_memory[SFF8636_STATUS_2_OFFSET] &
+ 	    SFF8636_STATUS_PAGE_3_PRESENT)
+-		return;
++		return 0;
+ 
+-	map->page_03h = page_three->data - SFF8636_PAGE_SIZE;
++	sff8636_request_init(&request, 0x3, SFF8636_PAGE_SIZE);
++	ret = nl_get_eeprom_page(ctx, &request);
++	if (ret < 0)
++		return ret;
++	map->page_03h = request.data - SFF8636_PAGE_SIZE;
++
++	return 0;
+ }
+ 
+-void sff8636_show_all_nl(const struct ethtool_module_eeprom *page_zero,
+-			 const struct ethtool_module_eeprom *page_three)
++int sff8636_show_all_nl(struct cmd_context *ctx)
+ {
+ 	struct sff8636_memory_map map = {};
++	int ret;
+ 
+-	sff8636_memory_map_init_pages(&map, page_zero, page_three);
++	ret = sff8636_memory_map_init_pages(ctx, &map);
++	if (ret < 0)
++		return ret;
+ 	sff8636_show_all_common(&map);
++
++	return 0;
+ }
+-- 
+2.35.1
+
diff --git a/SOURCES/0025-sff-8079-Request-specific-pages-for-parsing-in-netli.patch b/SOURCES/0025-sff-8079-Request-specific-pages-for-parsing-in-netli.patch
new file mode 100644
index 0000000..b013d36
--- /dev/null
+++ b/SOURCES/0025-sff-8079-Request-specific-pages-for-parsing-in-netli.patch
@@ -0,0 +1,92 @@
+From bda06d81f41bb1cde16a319728d479cda4b9f295 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 12 Oct 2021 16:25:24 +0300
+Subject: [PATCH 25/35] sff-8079: Request specific pages for parsing in netlink
+ path
+
+Convert the SFF-8079 code to request the required EEPROM contents in the
+netlink path as was done for CMIS and SFF-8636. It will allow us to
+remove standard-specific code from the netlink code (i.e.,
+netlink/module-eeprom.c).
+
+In addition, in the future, it will allow the netlink path to support
+parsing of SFF-8472.
+
+Tested by making sure that the output of 'ethtool -m' does not change
+before and after the patch.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ internal.h              |  2 +-
+ netlink/module-eeprom.c |  2 +-
+ sfpid.c                 | 20 ++++++++++++++++++--
+ 3 files changed, 20 insertions(+), 4 deletions(-)
+
+diff --git a/internal.h b/internal.h
+index 2407d3c223fa..0d9d816ab563 100644
+--- a/internal.h
++++ b/internal.h
+@@ -385,7 +385,7 @@ int rxclass_rule_del(struct cmd_context *ctx, __u32 loc);
+ 
+ /* Module EEPROM parsing code */
+ void sff8079_show_all_ioctl(const __u8 *id);
+-void sff8079_show_all_nl(const __u8 *id);
++int sff8079_show_all_nl(struct cmd_context *ctx);
+ 
+ /* Optics diagnostics */
+ void sff8472_show_all(const __u8 *id);
+diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c
+index f04f8e134223..6d76b8a96461 100644
+--- a/netlink/module-eeprom.c
++++ b/netlink/module-eeprom.c
+@@ -321,7 +321,7 @@ static void decoder_print(struct cmd_context *ctx)
+ 
+ 	switch (module_id) {
+ 	case SFF8024_ID_SFP:
+-		sff8079_show_all_nl(page_zero->data);
++		sff8079_show_all_nl(ctx);
+ 		break;
+ 	case SFF8024_ID_QSFP:
+ 	case SFF8024_ID_QSFP28:
+diff --git a/sfpid.c b/sfpid.c
+index c214820226d1..621d1e86c278 100644
+--- a/sfpid.c
++++ b/sfpid.c
+@@ -8,8 +8,13 @@
+  */
+ 
+ #include <stdio.h>
++#include <errno.h>
+ #include "internal.h"
+ #include "sff-common.h"
++#include "netlink/extapi.h"
++
++#define SFF8079_PAGE_SIZE	0x80
++#define SFF8079_I2C_ADDRESS_LOW	0x50
+ 
+ static void sff8079_show_identifier(const __u8 *id)
+ {
+@@ -445,7 +450,18 @@ void sff8079_show_all_ioctl(const __u8 *id)
+ 	sff8079_show_all_common(id);
+ }
+ 
+-void sff8079_show_all_nl(const __u8 *id)
++int sff8079_show_all_nl(struct cmd_context *ctx)
+ {
+-	sff8079_show_all_common(id);
++	struct ethtool_module_eeprom request = {
++		.length = SFF8079_PAGE_SIZE,
++		.i2c_address = SFF8079_I2C_ADDRESS_LOW,
++	};
++	int ret;
++
++	ret = nl_get_eeprom_page(ctx, &request);
++	if (ret < 0)
++		return ret;
++	sff8079_show_all_common(request.data);
++
++	return 0;
+ }
+-- 
+2.35.1
+
diff --git a/SOURCES/0026-netlink-eeprom-Defer-page-requests-to-individual-par.patch b/SOURCES/0026-netlink-eeprom-Defer-page-requests-to-individual-par.patch
new file mode 100644
index 0000000..99dc9a1
--- /dev/null
+++ b/SOURCES/0026-netlink-eeprom-Defer-page-requests-to-individual-par.patch
@@ -0,0 +1,428 @@
+From b6005ecf2ce2aaeb86995fa50df97e7384f46f99 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 12 Oct 2021 16:25:25 +0300
+Subject: [PATCH 26/35] netlink: eeprom: Defer page requests to individual
+ parsers
+
+The individual EEPROM parsers (e.g., CMIS, SFF-8636) now request the
+EEPROM pages they intend to parse and populate their memory maps before
+parsing them.
+
+Therefore, there is no need for the common netlink code to request
+potentially invalid pages and pass them as blobs to these parsers.
+
+Instead, only query the SFF-8024 Identifier Value which is located at
+I2C address 0x50, byte 0 and dispatch to the relevant EEPROM parser.
+
+Tested by making sure that the output of 'ethtool -m' does not change
+for SFF-8079, SFF-8636 and CMIS before and after the patch.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ netlink/module-eeprom.c | 347 +++++++---------------------------------
+ 1 file changed, 59 insertions(+), 288 deletions(-)
+
+diff --git a/netlink/module-eeprom.c b/netlink/module-eeprom.c
+index 6d76b8a96461..f359aeec4ddf 100644
+--- a/netlink/module-eeprom.c
++++ b/netlink/module-eeprom.c
+@@ -19,7 +19,6 @@
+ #include "parser.h"
+ 
+ #define ETH_I2C_ADDRESS_LOW	0x50
+-#define ETH_I2C_ADDRESS_HIGH	0x51
+ #define ETH_I2C_MAX_ADDRESS	0x7F
+ 
+ struct cmd_params {
+@@ -78,267 +77,6 @@ static const struct param_parser getmodule_params[] = {
+ 	{}
+ };
+ 
+-struct page_entry {
+-	struct list_head link;
+-	struct ethtool_module_eeprom *page;
+-};
+-
+-static struct list_head page_list = LIST_HEAD_INIT(page_list);
+-
+-static int cache_add(struct ethtool_module_eeprom *page)
+-{
+-	struct page_entry *list_element;
+-
+-	if (!page)
+-		return -1;
+-	list_element = malloc(sizeof(*list_element));
+-	if (!list_element)
+-		return -ENOMEM;
+-	list_element->page = page;
+-
+-	list_add(&list_element->link, &page_list);
+-	return 0;
+-}
+-
+-static void page_free(struct ethtool_module_eeprom *page)
+-{
+-	free(page->data);
+-	free(page);
+-}
+-
+-static void cache_del(struct ethtool_module_eeprom *page)
+-{
+-	struct ethtool_module_eeprom *entry;
+-	struct list_head *head, *next;
+-
+-	list_for_each_safe(head, next, &page_list) {
+-		entry = ((struct page_entry *)head)->page;
+-		if (entry == page) {
+-			list_del(head);
+-			free(head);
+-			page_free(entry);
+-			break;
+-		}
+-	}
+-}
+-
+-static void cache_free(void)
+-{
+-	struct ethtool_module_eeprom *entry;
+-	struct list_head *head, *next;
+-
+-	list_for_each_safe(head, next, &page_list) {
+-		entry = ((struct page_entry *)head)->page;
+-		list_del(head);
+-		free(head);
+-		page_free(entry);
+-	}
+-}
+-
+-static struct ethtool_module_eeprom *page_join(struct ethtool_module_eeprom *page_a,
+-					       struct ethtool_module_eeprom *page_b)
+-{
+-	struct ethtool_module_eeprom *joined_page;
+-	u32 total_length;
+-
+-	if (!page_a || !page_b ||
+-	    page_a->page != page_b->page ||
+-	    page_a->bank != page_b->bank ||
+-	    page_a->i2c_address != page_b->i2c_address)
+-		return NULL;
+-
+-	total_length = page_a->length + page_b->length;
+-	joined_page = calloc(1, sizeof(*joined_page));
+-	joined_page->data = calloc(1, total_length);
+-	joined_page->page = page_a->page;
+-	joined_page->bank = page_a->bank;
+-	joined_page->length = total_length;
+-	joined_page->i2c_address = page_a->i2c_address;
+-
+-	if (page_a->offset < page_b->offset) {
+-		memcpy(joined_page->data, page_a->data, page_a->length);
+-		memcpy(joined_page->data + page_a->length, page_b->data, page_b->length);
+-		joined_page->offset = page_a->offset;
+-	} else {
+-		memcpy(joined_page->data, page_b->data, page_b->length);
+-		memcpy(joined_page->data + page_b->length, page_a->data, page_a->length);
+-		joined_page->offset = page_b->offset;
+-	}
+-
+-	return joined_page;
+-}
+-
+-static struct ethtool_module_eeprom *cache_get(u32 page, u32 bank, u8 i2c_address)
+-{
+-	struct ethtool_module_eeprom *entry;
+-	struct list_head *head, *next;
+-
+-	list_for_each_safe(head, next, &page_list) {
+-		entry = ((struct page_entry *)head)->page;
+-		if (entry->page == page && entry->bank == bank &&
+-		    entry->i2c_address == i2c_address)
+-			return entry;
+-	}
+-
+-	return NULL;
+-}
+-
+-static int getmodule_page_fetch_reply_cb(const struct nlmsghdr *nlhdr,
+-					 void *data)
+-{
+-	const struct nlattr *tb[ETHTOOL_A_MODULE_EEPROM_DATA + 1] = {};
+-	DECLARE_ATTR_TB_INFO(tb);
+-	struct ethtool_module_eeprom *lower_page;
+-	struct ethtool_module_eeprom *response;
+-	struct ethtool_module_eeprom *request;
+-	struct ethtool_module_eeprom *joined;
+-	u8 *eeprom_data;
+-	int ret;
+-
+-	ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+-	if (ret < 0)
+-		return ret;
+-
+-	if (!tb[ETHTOOL_A_MODULE_EEPROM_DATA]) {
+-		fprintf(stderr, "Malformed netlink message (getmodule)\n");
+-		return MNL_CB_ERROR;
+-	}
+-
+-	response = calloc(1, sizeof(*response));
+-	if (!response)
+-		return -ENOMEM;
+-
+-	request = (struct ethtool_module_eeprom *)data;
+-	response->offset = request->offset;
+-	response->page = request->page;
+-	response->bank = request->bank;
+-	response->i2c_address = request->i2c_address;
+-	response->length = mnl_attr_get_payload_len(tb[ETHTOOL_A_MODULE_EEPROM_DATA]);
+-	eeprom_data = mnl_attr_get_payload(tb[ETHTOOL_A_MODULE_EEPROM_DATA]);
+-
+-	response->data = malloc(response->length);
+-	if (!response->data) {
+-		free(response);
+-		return -ENOMEM;
+-	}
+-	memcpy(response->data, eeprom_data, response->length);
+-
+-	if (!request->page) {
+-		lower_page = cache_get(request->page, request->bank, response->i2c_address);
+-		if (lower_page) {
+-			joined = page_join(lower_page, response);
+-			page_free(response);
+-			cache_del(lower_page);
+-			return cache_add(joined);
+-		}
+-	}
+-
+-	return cache_add(response);
+-}
+-
+-static int page_fetch(struct nl_context *nlctx, const struct ethtool_module_eeprom *request)
+-{
+-	struct nl_socket *nlsock = nlctx->ethnl_socket;
+-	struct nl_msg_buff *msg = &nlsock->msgbuff;
+-	struct ethtool_module_eeprom *page;
+-	int ret;
+-
+-	if (!request || request->i2c_address > ETH_I2C_MAX_ADDRESS)
+-		return -EINVAL;
+-
+-	/* Satisfy request right away, if region is already in cache */
+-	page = cache_get(request->page, request->bank, request->i2c_address);
+-	if (page && page->offset <= request->offset &&
+-	    page->offset + page->length >= request->offset + request->length) {
+-		return 0;
+-	}
+-
+-	ret = nlsock_prep_get_request(nlsock, ETHTOOL_MSG_MODULE_EEPROM_GET,
+-				      ETHTOOL_A_MODULE_EEPROM_HEADER, 0);
+-	if (ret < 0)
+-		return ret;
+-
+-	if (ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_LENGTH, request->length) ||
+-	    ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_OFFSET, request->offset) ||
+-	    ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_PAGE, request->page) ||
+-	    ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_BANK, request->bank) ||
+-	    ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS, request->i2c_address))
+-		return -EMSGSIZE;
+-
+-	ret = nlsock_sendmsg(nlsock, NULL);
+-	if (ret < 0)
+-		return ret;
+-	ret = nlsock_process_reply(nlsock, getmodule_page_fetch_reply_cb, (void *)request);
+-	if (ret < 0)
+-		return ret;
+-
+-	return nlsock_process_reply(nlsock, nomsg_reply_cb, NULL);
+-}
+-
+-#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
+-static int decoder_prefetch(struct nl_context *nlctx)
+-{
+-	struct ethtool_module_eeprom *page_zero_lower = cache_get(0, 0, ETH_I2C_ADDRESS_LOW);
+-	struct ethtool_module_eeprom request = {0};
+-	u8 module_id = page_zero_lower->data[0];
+-	int err = 0;
+-
+-	/* Fetch rest of page 00 */
+-	request.i2c_address = ETH_I2C_ADDRESS_LOW;
+-	request.offset = 128;
+-	request.length = 128;
+-	err = page_fetch(nlctx, &request);
+-	if (err)
+-		return err;
+-
+-	switch (module_id) {
+-	case SFF8024_ID_QSFP:
+-	case SFF8024_ID_QSFP28:
+-	case SFF8024_ID_QSFP_PLUS:
+-		memset(&request, 0, sizeof(request));
+-		request.i2c_address = ETH_I2C_ADDRESS_LOW;
+-		request.offset = 128;
+-		request.length = 128;
+-		request.page = 3;
+-		break;
+-	case SFF8024_ID_QSFP_DD:
+-	case SFF8024_ID_DSFP:
+-		memset(&request, 0, sizeof(request));
+-		request.i2c_address = ETH_I2C_ADDRESS_LOW;
+-		request.offset = 128;
+-		request.length = 128;
+-		request.page = 1;
+-		break;
+-	}
+-
+-	return page_fetch(nlctx, &request);
+-}
+-
+-static void decoder_print(struct cmd_context *ctx)
+-{
+-	struct ethtool_module_eeprom *page_zero = cache_get(0, 0, ETH_I2C_ADDRESS_LOW);
+-	u8 module_id = page_zero->data[SFF8636_ID_OFFSET];
+-
+-	switch (module_id) {
+-	case SFF8024_ID_SFP:
+-		sff8079_show_all_nl(ctx);
+-		break;
+-	case SFF8024_ID_QSFP:
+-	case SFF8024_ID_QSFP28:
+-	case SFF8024_ID_QSFP_PLUS:
+-		sff8636_show_all_nl(ctx);
+-		break;
+-	case SFF8024_ID_QSFP_DD:
+-	case SFF8024_ID_DSFP:
+-		cmis_show_all_nl(ctx);
+-		break;
+-	default:
+-		dump_hex(stdout, page_zero->data, page_zero->length, page_zero->offset);
+-		break;
+-	}
+-}
+-#endif
+-
+ static struct list_head eeprom_page_list = LIST_HEAD_INIT(eeprom_page_list);
+ 
+ struct eeprom_page_entry {
+@@ -443,14 +181,64 @@ int nl_get_eeprom_page(struct cmd_context *ctx,
+ 				    (void *)request);
+ }
+ 
++static int eeprom_dump_hex(struct cmd_context *ctx)
++{
++	struct ethtool_module_eeprom request = {
++		.length = 128,
++		.i2c_address = ETH_I2C_ADDRESS_LOW,
++	};
++	int ret;
++
++	ret = nl_get_eeprom_page(ctx, &request);
++	if (ret < 0)
++		return ret;
++
++	dump_hex(stdout, request.data, request.length, request.offset);
++
++	return 0;
++}
++
++static int eeprom_parse(struct cmd_context *ctx)
++{
++	struct ethtool_module_eeprom request = {
++		.length = 1,
++		.i2c_address = ETH_I2C_ADDRESS_LOW,
++	};
++	int ret;
++
++	/* Fetch the SFF-8024 Identifier Value. For all supported standards, it
++	 * is located at I2C address 0x50, byte 0. See section 4.1 in SFF-8024,
++	 * revision 4.9.
++	 */
++	ret = nl_get_eeprom_page(ctx, &request);
++	if (ret < 0)
++		return ret;
++
++	switch (request.data[0]) {
++#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
++	case SFF8024_ID_SFP:
++		return sff8079_show_all_nl(ctx);
++	case SFF8024_ID_QSFP:
++	case SFF8024_ID_QSFP28:
++	case SFF8024_ID_QSFP_PLUS:
++		return sff8636_show_all_nl(ctx);
++	case SFF8024_ID_QSFP_DD:
++	case SFF8024_ID_DSFP:
++		return cmis_show_all_nl(ctx);
++#endif
++	default:
++		/* If we cannot recognize the memory map, default to dumping
++		 * the first 128 bytes in hex.
++		 */
++		return eeprom_dump_hex(ctx);
++	}
++}
++
+ int nl_getmodule(struct cmd_context *ctx)
+ {
+ 	struct cmd_params getmodule_cmd_params = {};
+ 	struct ethtool_module_eeprom request = {0};
+-	struct ethtool_module_eeprom *reply_page;
+ 	struct nl_context *nlctx = ctx->nlctx;
+-	u32 dump_length;
+-	u8 *eeprom_data;
+ 	int ret;
+ 
+ 	if (netlink_cmd_check(ctx, ETHTOOL_MSG_MODULE_EEPROM_GET, false))
+@@ -479,12 +267,6 @@ int nl_getmodule(struct cmd_context *ctx)
+ 		return -EOPNOTSUPP;
+ 	}
+ 
+-	request.i2c_address = ETH_I2C_ADDRESS_LOW;
+-	request.length = 128;
+-	ret = page_fetch(nlctx, &request);
+-	if (ret)
+-		goto cleanup;
+-
+ #ifdef ETHTOOL_ENABLE_PRETTY_DUMP
+ 	if (getmodule_cmd_params.page || getmodule_cmd_params.bank ||
+ 	    getmodule_cmd_params.offset || getmodule_cmd_params.length)
+@@ -501,33 +283,22 @@ int nl_getmodule(struct cmd_context *ctx)
+ 		request.offset = 128;
+ 
+ 	if (getmodule_cmd_params.dump_hex || getmodule_cmd_params.dump_raw) {
+-		ret = page_fetch(nlctx, &request);
++		ret = nl_get_eeprom_page(ctx, &request);
+ 		if (ret < 0)
+ 			goto cleanup;
+-		reply_page = cache_get(request.page, request.bank, request.i2c_address);
+-		if (!reply_page) {
+-			ret = -EINVAL;
+-			goto cleanup;
+-		}
+ 
+-		eeprom_data = reply_page->data + (request.offset - reply_page->offset);
+-		dump_length = reply_page->length < request.length ? reply_page->length
+-								  : request.length;
+ 		if (getmodule_cmd_params.dump_raw)
+-			fwrite(eeprom_data, 1, request.length, stdout);
++			fwrite(request.data, 1, request.length, stdout);
+ 		else
+-			dump_hex(stdout, eeprom_data, dump_length, request.offset);
++			dump_hex(stdout, request.data, request.length,
++				 request.offset);
+ 	} else {
+-#ifdef ETHTOOL_ENABLE_PRETTY_DUMP
+-		ret = decoder_prefetch(nlctx);
+-		if (ret)
++		ret = eeprom_parse(ctx);
++		if (ret < 0)
+ 			goto cleanup;
+-		decoder_print(ctx);
+-#endif
+ 	}
+ 
+ cleanup:
+ 	eeprom_page_list_flush();
+-	cache_free();
+ 	return ret;
+ }
+-- 
+2.35.1
+
diff --git a/SOURCES/0027-sff-8636-Use-an-SFF-8636-specific-define-for-maximum.patch b/SOURCES/0027-sff-8636-Use-an-SFF-8636-specific-define-for-maximum.patch
new file mode 100644
index 0000000..1dd08a9
--- /dev/null
+++ b/SOURCES/0027-sff-8636-Use-an-SFF-8636-specific-define-for-maximum.patch
@@ -0,0 +1,84 @@
+From 37093971b0f645542c4bff603f41f807e8023bd3 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 23 Nov 2021 19:40:55 +0200
+Subject: [PATCH 27/35] sff-8636: Use an SFF-8636 specific define for maximum
+ number of channels
+
+'MAX_CHANNEL_NUM' is defined in the common SFF code as 4 and used to set
+the size of the per-channel diagnostics array in the common 'sff_diags'
+structure.
+
+The CMIS parsing code is also going to use the structure, but it can
+have up to 32 channels, unlike SFF-8636 that only has 4.
+
+Therefore, set 'MAX_CHANNEL_NUM' to 32 and change the SFF-8636 code to
+use an SFF-8636 specific define instead of the common 'MAX_CHANNEL_NUM'.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ qsfp.c       | 9 +++++----
+ sff-common.h | 2 +-
+ 2 files changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/qsfp.c b/qsfp.c
+index e7c2f51cd9c6..58c4c4775e9b 100644
+--- a/qsfp.c
++++ b/qsfp.c
+@@ -71,6 +71,7 @@ struct sff8636_memory_map {
+ 
+ #define SFF8636_PAGE_SIZE	0x80
+ #define SFF8636_I2C_ADDRESS	0x50
++#define SFF8636_MAX_CHANNEL_NUM	4
+ 
+ #define MAX_DESC_SIZE	42
+ 
+@@ -761,7 +762,7 @@ static void sff8636_dom_parse(const struct sff8636_memory_map *map,
+ 
+ out:
+ 	/* Channel Specific Data */
+-	for (i = 0; i < MAX_CHANNEL_NUM; i++) {
++	for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) {
+ 		u8 rx_power_offset, tx_bias_offset;
+ 		u8 tx_power_offset;
+ 
+@@ -832,13 +833,13 @@ static void sff8636_show_dom(const struct sff8636_memory_map *map)
+ 	printf("\t%-41s : %s\n", "Alarm/warning flags implemented",
+ 		(sd.supports_alarms ? "Yes" : "No"));
+ 
+-	for (i = 0; i < MAX_CHANNEL_NUM; i++) {
++	for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) {
+ 		snprintf(power_string, MAX_DESC_SIZE, "%s (Channel %d)",
+ 					"Laser tx bias current", i+1);
+ 		PRINT_BIAS(power_string, sd.scd[i].bias_cur);
+ 	}
+ 
+-	for (i = 0; i < MAX_CHANNEL_NUM; i++) {
++	for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) {
+ 		snprintf(power_string, MAX_DESC_SIZE, "%s (Channel %d)",
+ 					"Transmit avg optical power", i+1);
+ 		PRINT_xX_PWR(power_string, sd.scd[i].tx_power);
+@@ -849,7 +850,7 @@ static void sff8636_show_dom(const struct sff8636_memory_map *map)
+ 	else
+ 		rx_power_string = "Rcvr signal avg optical power";
+ 
+-	for (i = 0; i < MAX_CHANNEL_NUM; i++) {
++	for (i = 0; i < SFF8636_MAX_CHANNEL_NUM; i++) {
+ 		snprintf(power_string, MAX_DESC_SIZE, "%s(Channel %d)",
+ 					rx_power_string, i+1);
+ 		PRINT_xX_PWR(power_string, sd.scd[i].rx_power);
+diff --git a/sff-common.h b/sff-common.h
+index 2183f41ff9c9..aab306e0b74f 100644
+--- a/sff-common.h
++++ b/sff-common.h
+@@ -160,7 +160,7 @@ struct sff_channel_diags {
+ /* Module Monitoring Fields */
+ struct sff_diags {
+ 
+-#define MAX_CHANNEL_NUM 4
++#define MAX_CHANNEL_NUM 32
+ #define LWARN 0
+ #define HWARN 1
+ #define LALRM 2
+-- 
+2.35.1
+
diff --git a/SOURCES/0028-sff-common-Move-OFFSET_TO_U16_PTR-to-common-header-f.patch b/SOURCES/0028-sff-common-Move-OFFSET_TO_U16_PTR-to-common-header-f.patch
new file mode 100644
index 0000000..9149cfc
--- /dev/null
+++ b/SOURCES/0028-sff-common-Move-OFFSET_TO_U16_PTR-to-common-header-f.patch
@@ -0,0 +1,45 @@
+From 2dcf6b9dc1c1874705b9e71e13e00dde9f7f576c Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 23 Nov 2021 19:40:56 +0200
+Subject: [PATCH 28/35] sff-common: Move OFFSET_TO_U16_PTR() to common header
+ file
+
+The define is also useful for CMIS, so move it from SFF-8636 to the
+common header file.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ qsfp.c       | 1 -
+ sff-common.h | 4 ++--
+ 2 files changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/qsfp.c b/qsfp.c
+index 58c4c4775e9b..b3c9e1516af9 100644
+--- a/qsfp.c
++++ b/qsfp.c
+@@ -700,7 +700,6 @@ sff8636_show_wavelength_or_copper_compliance(const struct sff8636_memory_map *ma
+  * Second byte are 1/256th of degree, which are added to the dec part.
+  */
+ #define SFF8636_OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset))
+-#define OFFSET_TO_U16_PTR(ptr, offset) (ptr[offset] << 8 | ptr[(offset) + 1])
+ 
+ static void sff8636_dom_parse(const struct sff8636_memory_map *map,
+ 			      struct sff_diags *sd)
+diff --git a/sff-common.h b/sff-common.h
+index aab306e0b74f..9e323008ba19 100644
+--- a/sff-common.h
++++ b/sff-common.h
+@@ -126,8 +126,8 @@
+ #define  SFF8024_ENCODING_PAM4			0x08
+ 
+ /* Most common case: 16-bit unsigned integer in a certain unit */
+-#define OFFSET_TO_U16(offset) \
+-		(id[offset] << 8 | id[(offset) + 1])
++#define OFFSET_TO_U16_PTR(ptr, offset) (ptr[offset] << 8 | ptr[(offset) + 1])
++#define OFFSET_TO_U16(offset) OFFSET_TO_U16_PTR(id, offset)
+ 
+ # define PRINT_xX_PWR(string, var)                             \
+ 		printf("\t%-41s : %.4f mW / %.2f dBm\n", (string),         \
+-- 
+2.35.1
+
diff --git a/SOURCES/0029-cmis-Initialize-Page-02h-in-memory-map.patch b/SOURCES/0029-cmis-Initialize-Page-02h-in-memory-map.patch
new file mode 100644
index 0000000..dd5c145
--- /dev/null
+++ b/SOURCES/0029-cmis-Initialize-Page-02h-in-memory-map.patch
@@ -0,0 +1,59 @@
+From 86853162d53b47cd0f6bcb926810aa0fd68b8898 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 23 Nov 2021 19:40:57 +0200
+Subject: [PATCH 29/35] cmis: Initialize Page 02h in memory map
+
+Page 02h stores module and lane thresholds that are going to be parsed
+and displayed in subsequent patches.
+
+Request it via the 'MODULE_EEPROM_GET' netlink message and initialize it
+in the memory map.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ cmis.c | 13 ++++++++++---
+ 1 file changed, 10 insertions(+), 3 deletions(-)
+
+diff --git a/cmis.c b/cmis.c
+index 4798fd4c7d68..55b9d1b959cd 100644
+--- a/cmis.c
++++ b/cmis.c
+@@ -17,9 +17,10 @@
+ 
+ struct cmis_memory_map {
+ 	const __u8 *lower_memory;
+-	const __u8 *upper_memory[1][2];	/* Bank, Page */
++	const __u8 *upper_memory[1][3];	/* Bank, Page */
+ #define page_00h upper_memory[0x0][0x0]
+ #define page_01h upper_memory[0x0][0x1]
++#define page_02h upper_memory[0x0][0x2]
+ };
+ 
+ #define CMIS_PAGE_SIZE		0x80
+@@ -423,8 +424,8 @@ cmis_memory_map_init_pages(struct cmd_context *ctx,
+ 		return ret;
+ 	map->page_00h = request.data - CMIS_PAGE_SIZE;
+ 
+-	/* Page 01h is only present when the module memory model is paged and
+-	 * not flat.
++	/* Pages 01h and 02h are only present when the module memory model is
++	 * paged and not flat.
+ 	 */
+ 	if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] &
+ 	    CMIS_MEMORY_MODEL_MASK)
+@@ -436,6 +437,12 @@ cmis_memory_map_init_pages(struct cmd_context *ctx,
+ 		return ret;
+ 	map->page_01h = request.data - CMIS_PAGE_SIZE;
+ 
++	cmis_request_init(&request, 0, 0x2, CMIS_PAGE_SIZE);
++	ret = nl_get_eeprom_page(ctx, &request);
++	if (ret < 0)
++		return ret;
++	map->page_02h = request.data - CMIS_PAGE_SIZE;
++
+ 	return 0;
+ }
+ 
+-- 
+2.35.1
+
diff --git a/SOURCES/0030-cmis-Initialize-Banked-Page-11h-in-memory-map.patch b/SOURCES/0030-cmis-Initialize-Banked-Page-11h-in-memory-map.patch
new file mode 100644
index 0000000..54090bf
--- /dev/null
+++ b/SOURCES/0030-cmis-Initialize-Banked-Page-11h-in-memory-map.patch
@@ -0,0 +1,122 @@
+From 21367ae2b8ebbe5173cbed22dfa51680a3fe48d2 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 23 Nov 2021 19:40:58 +0200
+Subject: [PATCH 30/35] cmis: Initialize Banked Page 11h in memory map
+
+Banked Page 11h stores, among other things, lane-specific flags and
+monitors that are going to be parsed and displayed in subsequent
+patches.
+
+Request it via the 'MODULE_EEPROM_GET' netlink message and initialize it
+in the memory map.
+
+Only initialize it in supported Banks.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ cmis.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
+ cmis.h |  7 +++++++
+ 2 files changed, 54 insertions(+), 2 deletions(-)
+
+diff --git a/cmis.c b/cmis.c
+index 55b9d1b959cd..83ced4d253ae 100644
+--- a/cmis.c
++++ b/cmis.c
+@@ -15,9 +15,17 @@
+ #include "cmis.h"
+ #include "netlink/extapi.h"
+ 
++/* The maximum number of supported Banks. Relevant documents:
++ * [1] CMIS Rev. 5, page. 128, section 8.4.4, Table 8-40
++ */
++#define CMIS_MAX_BANKS	4
++
++/* We are not parsing further than Page 11h. */
++#define CMIS_MAX_PAGES	18
++
+ struct cmis_memory_map {
+ 	const __u8 *lower_memory;
+-	const __u8 *upper_memory[1][3];	/* Bank, Page */
++	const __u8 *upper_memory[CMIS_MAX_BANKS][CMIS_MAX_PAGES];
+ #define page_00h upper_memory[0x0][0x0]
+ #define page_01h upper_memory[0x0][0x1]
+ #define page_02h upper_memory[0x0][0x2]
+@@ -399,12 +407,33 @@ static void cmis_request_init(struct ethtool_module_eeprom *request, u8 bank,
+ 	request->data = NULL;
+ }
+ 
++static int cmis_num_banks_get(const struct cmis_memory_map *map,
++			      int *p_num_banks)
++{
++	switch (map->page_01h[CMIS_PAGES_ADVER_OFFSET] &
++		CMIS_BANKS_SUPPORTED_MASK) {
++	case CMIS_BANK_0_SUPPORTED:
++		*p_num_banks = 1;
++		break;
++	case CMIS_BANK_0_1_SUPPORTED:
++		*p_num_banks = 2;
++		break;
++	case CMIS_BANK_0_3_SUPPORTED:
++		*p_num_banks = 4;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
+ static int
+ cmis_memory_map_init_pages(struct cmd_context *ctx,
+ 			   struct cmis_memory_map *map)
+ {
+ 	struct ethtool_module_eeprom request;
+-	int ret;
++	int num_banks, i, ret;
+ 
+ 	/* Lower Memory and Page 00h are always present.
+ 	 *
+@@ -443,6 +472,22 @@ cmis_memory_map_init_pages(struct cmd_context *ctx,
+ 		return ret;
+ 	map->page_02h = request.data - CMIS_PAGE_SIZE;
+ 
++	/* Bank 0 of Page 11h provides lane-specific registers for the first 8
++	 * lanes, and each additional Banks provides support for an additional
++	 * 8 lanes. Only initialize supported Banks.
++	 */
++	ret = cmis_num_banks_get(map, &num_banks);
++	if (ret < 0)
++		return ret;
++
++	for (i = 0; i < num_banks; i++) {
++		cmis_request_init(&request, i, 0x11, CMIS_PAGE_SIZE);
++		ret = nl_get_eeprom_page(ctx, &request);
++		if (ret < 0)
++			return ret;
++		map->upper_memory[i][0x11] = request.data - CMIS_PAGE_SIZE;
++	}
++
+ 	return 0;
+ }
+ 
+diff --git a/cmis.h b/cmis.h
+index 911491dc5c8f..8d90a04756ad 100644
+--- a/cmis.h
++++ b/cmis.h
+@@ -114,6 +114,13 @@
+ #define CMIS_WAVELENGTH_TOL_MSB			0x8C
+ #define CMIS_WAVELENGTH_TOL_LSB			0x8D
+ 
++/* Supported Pages Advertising (Page 1) */
++#define CMIS_PAGES_ADVER_OFFSET			0x8E
++#define CMIS_BANKS_SUPPORTED_MASK		0x03
++#define CMIS_BANK_0_SUPPORTED			0x00
++#define CMIS_BANK_0_1_SUPPORTED			0x01
++#define CMIS_BANK_0_3_SUPPORTED			0x02
++
+ /* Signal integrity controls */
+ #define CMIS_SIG_INTEG_TX_OFFSET		0xA1
+ #define CMIS_SIG_INTEG_RX_OFFSET		0xA2
+-- 
+2.35.1
+
diff --git a/SOURCES/0031-cmis-Parse-and-print-diagnostic-information.patch b/SOURCES/0031-cmis-Parse-and-print-diagnostic-information.patch
new file mode 100644
index 0000000..c9d6293
--- /dev/null
+++ b/SOURCES/0031-cmis-Parse-and-print-diagnostic-information.patch
@@ -0,0 +1,631 @@
+From 086662c0b884c2b2e44ec472566d56c68a4330e0 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 23 Nov 2021 19:40:59 +0200
+Subject: [PATCH 31/35] cmis: Parse and print diagnostic information
+
+Like SFF-8636, CMIS has module-level monitors such as temperature and
+voltage and channel-level monitors such as Tx optical power.
+
+These monitors have thresholds and flags that are set when the monitors
+cross the thresholds.
+
+Print and parse these values in a similar fashion to SFF-8636.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ cmis.c | 466 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
+ cmis.h |  79 ++++++++++
+ 2 files changed, 518 insertions(+), 27 deletions(-)
+
+diff --git a/cmis.c b/cmis.c
+index 83ced4d253ae..d7b7097139b3 100644
+--- a/cmis.c
++++ b/cmis.c
+@@ -19,6 +19,8 @@
+  * [1] CMIS Rev. 5, page. 128, section 8.4.4, Table 8-40
+  */
+ #define CMIS_MAX_BANKS	4
++#define CMIS_CHANNELS_PER_BANK	8
++#define CMIS_MAX_CHANNEL_NUM	(CMIS_MAX_BANKS * CMIS_CHANNELS_PER_BANK)
+ 
+ /* We are not parsing further than Page 11h. */
+ #define CMIS_MAX_PAGES	18
+@@ -34,6 +36,80 @@ struct cmis_memory_map {
+ #define CMIS_PAGE_SIZE		0x80
+ #define CMIS_I2C_ADDRESS	0x50
+ 
++static struct {
++	const char *str;
++	int offset;
++	__u8 value;	/* Alarm is on if (offset & value) != 0. */
++} cmis_aw_mod_flags[] = {
++	{ "Module temperature high alarm",
++	  CMIS_TEMP_AW_OFFSET, CMIS_TEMP_HALARM_STATUS },
++	{ "Module temperature low alarm",
++	  CMIS_TEMP_AW_OFFSET, CMIS_TEMP_LALARM_STATUS },
++	{ "Module temperature high warning",
++	  CMIS_TEMP_AW_OFFSET, CMIS_TEMP_HWARN_STATUS },
++	{ "Module temperature low warning",
++	  CMIS_TEMP_AW_OFFSET, CMIS_TEMP_LWARN_STATUS },
++
++	{ "Module voltage high alarm",
++	  CMIS_VCC_AW_OFFSET, CMIS_VCC_HALARM_STATUS },
++	{ "Module voltage low alarm",
++	  CMIS_VCC_AW_OFFSET, CMIS_VCC_LALARM_STATUS },
++	{ "Module voltage high warning",
++	  CMIS_VCC_AW_OFFSET, CMIS_VCC_HWARN_STATUS },
++	{ "Module voltage low warning",
++	  CMIS_VCC_AW_OFFSET, CMIS_VCC_LWARN_STATUS },
++
++	{ NULL, 0, 0 },
++};
++
++static struct {
++	const char *fmt_str;
++	int offset;
++	int adver_offset;	/* In Page 01h. */
++	__u8 adver_value;	/* Supported if (offset & value) != 0. */
++} cmis_aw_chan_flags[] = {
++	{ "Laser bias current high alarm   (Chan %d)",
++	  CMIS_TX_BIAS_AW_HALARM_OFFSET,
++	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK },
++	{ "Laser bias current low alarm    (Chan %d)",
++	  CMIS_TX_BIAS_AW_LALARM_OFFSET,
++	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK },
++	{ "Laser bias current high warning (Chan %d)",
++	  CMIS_TX_BIAS_AW_HWARN_OFFSET,
++	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK },
++	{ "Laser bias current low warning  (Chan %d)",
++	  CMIS_TX_BIAS_AW_LWARN_OFFSET,
++	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_BIAS_MON_MASK },
++
++	{ "Laser tx power high alarm   (Channel %d)",
++	  CMIS_TX_PWR_AW_HALARM_OFFSET,
++	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK },
++	{ "Laser tx power low alarm    (Channel %d)",
++	  CMIS_TX_PWR_AW_LALARM_OFFSET,
++	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK },
++	{ "Laser tx power high warning (Channel %d)",
++	  CMIS_TX_PWR_AW_HWARN_OFFSET,
++	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK },
++	{ "Laser tx power low warning  (Channel %d)",
++	  CMIS_TX_PWR_AW_LWARN_OFFSET,
++	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_TX_PWR_MON_MASK },
++
++	{ "Laser rx power high alarm   (Channel %d)",
++	  CMIS_RX_PWR_AW_HALARM_OFFSET,
++	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK },
++	{ "Laser rx power low alarm    (Channel %d)",
++	  CMIS_RX_PWR_AW_LALARM_OFFSET,
++	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK },
++	{ "Laser rx power high warning (Channel %d)",
++	  CMIS_RX_PWR_AW_HWARN_OFFSET,
++	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK },
++	{ "Laser rx power low warning  (Channel %d)",
++	  CMIS_RX_PWR_AW_LWARN_OFFSET,
++	  CMIS_DIAG_CHAN_ADVER_OFFSET, CMIS_RX_PWR_MON_MASK },
++
++	{ NULL, 0, 0, 0 },
++};
++
+ static void cmis_show_identifier(const struct cmis_memory_map *map)
+ {
+ 	sff8024_show_identifier(map->lower_memory, CMIS_ID_OFFSET);
+@@ -277,32 +353,6 @@ static void cmis_show_mit_compliance(const struct cmis_memory_map *map)
+ 	}
+ }
+ 
+-/*
+- * 2-byte internal temperature conversions:
+- * First byte is a signed 8-bit integer, which is the temp decimal part
+- * Second byte is a multiple of 1/256th of a degree, which is added to
+- * the dec part.
+- */
+-#define OFFSET_TO_TEMP(offset) ((__s16)OFFSET_TO_U16(offset))
+-
+-/**
+- * Print relevant module level monitoring values. Relevant documents:
+- * [1] CMIS Rev. 3:
+- * --> pag. 50, section 1.7.2.4, Table 22
+- *
+- * [2] CMIS Rev. 4:
+- * --> pag. 84, section 8.2.4, Table 8-6
+- */
+-static void cmis_show_mod_lvl_monitors(const struct cmis_memory_map *map)
+-{
+-	const __u8 *id = map->lower_memory;
+-
+-	PRINT_TEMP("Module temperature",
+-		   OFFSET_TO_TEMP(CMIS_CURR_TEMP_OFFSET));
+-	PRINT_VCC("Module voltage",
+-		  OFFSET_TO_U16(CMIS_CURR_VCC_OFFSET));
+-}
+-
+ /**
+  * Print relevant info about the maximum supported fiber media length
+  * for each type of fiber media at the maximum module-supported bit rate.
+@@ -352,6 +402,368 @@ static void cmis_show_vendor_info(const struct cmis_memory_map *map)
+ 			       CMIS_CLEI_END_OFFSET, "CLEI code");
+ }
+ 
++static void cmis_parse_dom_power_type(const struct cmis_memory_map *map,
++				      struct sff_diags *sd)
++{
++	sd->rx_power_type = map->page_01h[CMIS_DIAG_TYPE_OFFSET] &
++			    CMIS_RX_PWR_TYPE_MASK;
++	sd->tx_power_type = map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] &
++			    CMIS_TX_PWR_MON_MASK;
++}
++
++static void cmis_parse_dom_mod_lvl_monitors(const struct cmis_memory_map *map,
++					    struct sff_diags *sd)
++{
++	sd->sfp_voltage[MCURR] = OFFSET_TO_U16_PTR(map->lower_memory,
++						   CMIS_CURR_VCC_OFFSET);
++	sd->sfp_temp[MCURR] = (__s16)OFFSET_TO_U16_PTR(map->lower_memory,
++						       CMIS_CURR_TEMP_OFFSET);
++}
++
++static void cmis_parse_dom_mod_lvl_thresh(const struct cmis_memory_map *map,
++					  struct sff_diags *sd)
++{
++	/* Page is not present in IOCTL path. */
++	if (!map->page_02h)
++		return;
++	sd->supports_alarms = 1;
++
++	sd->sfp_voltage[HALRM] = OFFSET_TO_U16_PTR(map->page_02h,
++						   CMIS_VCC_HALRM_OFFSET);
++	sd->sfp_voltage[LALRM] = OFFSET_TO_U16_PTR(map->page_02h,
++						   CMIS_VCC_LALRM_OFFSET);
++	sd->sfp_voltage[HWARN] = OFFSET_TO_U16_PTR(map->page_02h,
++						   CMIS_VCC_HWARN_OFFSET);
++	sd->sfp_voltage[LWARN] = OFFSET_TO_U16_PTR(map->page_02h,
++						   CMIS_VCC_LWARN_OFFSET);
++
++	sd->sfp_temp[HALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_02h,
++						       CMIS_TEMP_HALRM_OFFSET);
++	sd->sfp_temp[LALRM] = (__s16)OFFSET_TO_U16_PTR(map->page_02h,
++						       CMIS_TEMP_LALRM_OFFSET);
++	sd->sfp_temp[HWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_02h,
++						       CMIS_TEMP_HWARN_OFFSET);
++	sd->sfp_temp[LWARN] = (__s16)OFFSET_TO_U16_PTR(map->page_02h,
++						       CMIS_TEMP_LWARN_OFFSET);
++}
++
++static __u8 cmis_tx_bias_mul(const struct cmis_memory_map *map)
++{
++	switch (map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] &
++		CMIS_TX_BIAS_MUL_MASK) {
++	case CMIS_TX_BIAS_MUL_1:
++		return 0;
++	case CMIS_TX_BIAS_MUL_2:
++		return 1;
++	case CMIS_TX_BIAS_MUL_4:
++		return 2;
++	}
++
++	return 0;
++}
++
++static void
++cmis_parse_dom_chan_lvl_monitors_bank(const struct cmis_memory_map *map,
++				      struct sff_diags *sd, int bank)
++{
++	const __u8 *page_11h = map->upper_memory[bank][0x11];
++	int i;
++
++	if (!page_11h)
++		return;
++
++	for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) {
++		__u8 tx_bias_offset, rx_power_offset, tx_power_offset;
++		int chan = bank * CMIS_CHANNELS_PER_BANK + i;
++		__u8 bias_mul = cmis_tx_bias_mul(map);
++
++		tx_bias_offset = CMIS_TX_BIAS_OFFSET + i * sizeof(__u16);
++		rx_power_offset = CMIS_RX_PWR_OFFSET + i * sizeof(__u16);
++		tx_power_offset = CMIS_TX_PWR_OFFSET + i * sizeof(__u16);
++
++		sd->scd[chan].bias_cur = OFFSET_TO_U16_PTR(page_11h,
++							   tx_bias_offset);
++		sd->scd[chan].bias_cur >>= bias_mul;
++		sd->scd[chan].rx_power = OFFSET_TO_U16_PTR(page_11h,
++							   rx_power_offset);
++		sd->scd[chan].tx_power = OFFSET_TO_U16_PTR(page_11h,
++							   tx_power_offset);
++	}
++}
++
++static void cmis_parse_dom_chan_lvl_monitors(const struct cmis_memory_map *map,
++					     struct sff_diags *sd)
++{
++	int i;
++
++	for (i = 0; i < CMIS_MAX_BANKS; i++)
++		cmis_parse_dom_chan_lvl_monitors_bank(map, sd, i);
++}
++
++static void cmis_parse_dom_chan_lvl_thresh(const struct cmis_memory_map *map,
++					   struct sff_diags *sd)
++{
++	__u8 bias_mul = cmis_tx_bias_mul(map);
++
++	if (!map->page_02h)
++		return;
++
++	sd->bias_cur[HALRM] = OFFSET_TO_U16_PTR(map->page_02h,
++						CMIS_TX_BIAS_HALRM_OFFSET);
++	sd->bias_cur[HALRM] >>= bias_mul;
++	sd->bias_cur[LALRM] = OFFSET_TO_U16_PTR(map->page_02h,
++						CMIS_TX_BIAS_LALRM_OFFSET);
++	sd->bias_cur[LALRM] >>= bias_mul;
++	sd->bias_cur[HWARN] = OFFSET_TO_U16_PTR(map->page_02h,
++						CMIS_TX_BIAS_HWARN_OFFSET);
++	sd->bias_cur[HWARN] >>= bias_mul;
++	sd->bias_cur[LWARN] = OFFSET_TO_U16_PTR(map->page_02h,
++						CMIS_TX_BIAS_LWARN_OFFSET);
++	sd->bias_cur[LWARN] >>= bias_mul;
++
++	sd->tx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_02h,
++						CMIS_TX_PWR_HALRM_OFFSET);
++	sd->tx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_02h,
++						CMIS_TX_PWR_LALRM_OFFSET);
++	sd->tx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_02h,
++						CMIS_TX_PWR_HWARN_OFFSET);
++	sd->tx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_02h,
++						CMIS_TX_PWR_LWARN_OFFSET);
++
++	sd->rx_power[HALRM] = OFFSET_TO_U16_PTR(map->page_02h,
++						CMIS_RX_PWR_HALRM_OFFSET);
++	sd->rx_power[LALRM] = OFFSET_TO_U16_PTR(map->page_02h,
++						CMIS_RX_PWR_LALRM_OFFSET);
++	sd->rx_power[HWARN] = OFFSET_TO_U16_PTR(map->page_02h,
++						CMIS_RX_PWR_HWARN_OFFSET);
++	sd->rx_power[LWARN] = OFFSET_TO_U16_PTR(map->page_02h,
++						CMIS_RX_PWR_LWARN_OFFSET);
++}
++
++static void cmis_parse_dom(const struct cmis_memory_map *map,
++			   struct sff_diags *sd)
++{
++	cmis_parse_dom_power_type(map, sd);
++	cmis_parse_dom_mod_lvl_monitors(map, sd);
++	cmis_parse_dom_mod_lvl_thresh(map, sd);
++	cmis_parse_dom_chan_lvl_monitors(map, sd);
++	cmis_parse_dom_chan_lvl_thresh(map, sd);
++}
++
++/* Print module-level monitoring values. Relevant documents:
++ * [1] CMIS Rev. 5, page 110, section 8.2.5, Table 8-9
++ */
++static void cmis_show_dom_mod_lvl_monitors(const struct sff_diags *sd)
++{
++	PRINT_TEMP("Module temperature", sd->sfp_temp[MCURR]);
++	PRINT_VCC("Module voltage", sd->sfp_voltage[MCURR]);
++}
++
++/* Print channel Tx laser bias current. Relevant documents:
++ * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79
++ */
++static void
++cmis_show_dom_chan_lvl_tx_bias_bank(const struct cmis_memory_map *map,
++				    const struct sff_diags *sd, int bank)
++{
++	const __u8 *page_11h = map->upper_memory[bank][0x11];
++	int i;
++
++	if (!page_11h)
++		return;
++
++	for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) {
++		int chan = bank * CMIS_CHANNELS_PER_BANK + i;
++		char fmt_str[80];
++
++		snprintf(fmt_str, 80, "%s (Channel %d)",
++			 "Laser tx bias current", chan + 1);
++		PRINT_BIAS(fmt_str, sd->scd[chan].bias_cur);
++	}
++}
++
++static void cmis_show_dom_chan_lvl_tx_bias(const struct cmis_memory_map *map,
++					   const struct sff_diags *sd)
++{
++	int i;
++
++	if(!(map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] &
++	     CMIS_TX_BIAS_MON_MASK))
++		return;
++
++	for (i = 0; i < CMIS_MAX_BANKS; i++)
++		cmis_show_dom_chan_lvl_tx_bias_bank(map, sd, i);
++}
++
++/* Print channel Tx average optical power. Relevant documents:
++ * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79
++ */
++static void
++cmis_show_dom_chan_lvl_tx_power_bank(const struct cmis_memory_map *map,
++				     const struct sff_diags *sd, int bank)
++{
++	const __u8 *page_11h = map->upper_memory[bank][0x11];
++	int i;
++
++	if (!page_11h)
++		return;
++
++	for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) {
++		int chan = bank * CMIS_CHANNELS_PER_BANK + i;
++		char fmt_str[80];
++
++		snprintf(fmt_str, 80, "%s (Channel %d)",
++			 "Transmit avg optical power", chan + 1);
++		PRINT_xX_PWR(fmt_str, sd->scd[chan].tx_power);
++	}
++}
++
++static void cmis_show_dom_chan_lvl_tx_power(const struct cmis_memory_map *map,
++					    const struct sff_diags *sd)
++{
++	int i;
++
++	if (!sd->tx_power_type)
++		return;
++
++	for (i = 0; i < CMIS_MAX_BANKS; i++)
++		cmis_show_dom_chan_lvl_tx_power_bank(map, sd, i);
++}
++
++/* Print channel Rx input optical power. Relevant documents:
++ * [1] CMIS Rev. 5, page 165, section 8.9.4, Table 8-79
++ */
++static void
++cmis_show_dom_chan_lvl_rx_power_bank(const struct cmis_memory_map *map,
++				     const struct sff_diags *sd, int bank)
++{
++	const __u8 *page_11h = map->upper_memory[bank][0x11];
++	int i;
++
++	if (!page_11h)
++		return;
++
++	for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) {
++		int chan = bank * CMIS_CHANNELS_PER_BANK + i;
++		char *rx_power_str;
++		char fmt_str[80];
++
++		if (!sd->rx_power_type)
++			rx_power_str = "Receiver signal OMA";
++		else
++			rx_power_str = "Rcvr signal avg optical power";
++
++		snprintf(fmt_str, 80, "%s (Channel %d)", rx_power_str,
++			 chan + 1);
++		PRINT_xX_PWR(fmt_str, sd->scd[chan].rx_power);
++	}
++}
++
++static void cmis_show_dom_chan_lvl_rx_power(const struct cmis_memory_map *map,
++					    const struct sff_diags *sd)
++{
++	int i;
++
++	if(!(map->page_01h[CMIS_DIAG_CHAN_ADVER_OFFSET] & CMIS_RX_PWR_MON_MASK))
++		return;
++
++	for (i = 0; i < CMIS_MAX_BANKS; i++)
++		cmis_show_dom_chan_lvl_rx_power_bank(map, sd, i);
++}
++
++static void cmis_show_dom_chan_lvl_monitors(const struct cmis_memory_map *map,
++					    const struct sff_diags *sd)
++{
++	cmis_show_dom_chan_lvl_tx_bias(map, sd);
++	cmis_show_dom_chan_lvl_tx_power(map, sd);
++	cmis_show_dom_chan_lvl_rx_power(map, sd);
++}
++
++/* Print module-level flags. Relevant documents:
++ * [1] CMIS Rev. 5, page 109, section 8.2.4, Table 8-8
++ */
++static void cmis_show_dom_mod_lvl_flags(const struct cmis_memory_map *map)
++{
++	int i;
++
++	for (i = 0; cmis_aw_mod_flags[i].str; i++) {
++		printf("\t%-41s : %s\n", cmis_aw_mod_flags[i].str,
++		       map->lower_memory[cmis_aw_mod_flags[i].offset] &
++		       cmis_aw_mod_flags[i].value ? "On" : "Off");
++	}
++}
++
++/* Print channel-level flags. Relevant documents:
++ * [1] CMIS Rev. 5, page 162, section 8.9.3, Table 8-77
++ * [1] CMIS Rev. 5, page 164, section 8.9.3, Table 8-78
++ */
++static void cmis_show_dom_chan_lvl_flags_chan(const struct cmis_memory_map *map,
++					      int bank, int chan)
++{
++	const __u8 *page_11h = map->upper_memory[bank][0x11];
++	int i;
++
++	for (i = 0; cmis_aw_chan_flags[i].fmt_str; i++) {
++		char str[80];
++
++		if (!(map->page_01h[cmis_aw_chan_flags[i].adver_offset] &
++		      cmis_aw_chan_flags[i].adver_value))
++			continue;
++
++		snprintf(str, 80, cmis_aw_chan_flags[i].fmt_str, chan + 1);
++		printf("\t%-41s : %s\n", str,
++		       page_11h[cmis_aw_chan_flags[i].offset] & chan ?
++		       "On" : "Off");
++	}
++}
++
++static void
++cmis_show_dom_chan_lvl_flags_bank(const struct cmis_memory_map *map,
++				  int bank)
++{
++	const __u8 *page_11h = map->upper_memory[bank][0x11];
++	int i;
++
++	if (!page_11h)
++		return;
++
++	for (i = 0; i < CMIS_CHANNELS_PER_BANK; i++) {
++		int chan = bank * CMIS_CHANNELS_PER_BANK + i;
++
++		cmis_show_dom_chan_lvl_flags_chan(map, bank, chan);
++	}
++}
++
++static void cmis_show_dom_chan_lvl_flags(const struct cmis_memory_map *map)
++{
++	int i;
++
++	for (i = 0; i < CMIS_MAX_BANKS; i++)
++		cmis_show_dom_chan_lvl_flags_bank(map, i);
++}
++
++
++static void cmis_show_dom(const struct cmis_memory_map *map)
++{
++	struct sff_diags sd = {};
++
++	/* Diagnostic information is only relevant when the module memory
++	 * model is paged and not flat.
++	 */
++	if (map->lower_memory[CMIS_MEMORY_MODEL_OFFSET] &
++	    CMIS_MEMORY_MODEL_MASK)
++		return;
++
++	cmis_parse_dom(map, &sd);
++
++	cmis_show_dom_mod_lvl_monitors(&sd);
++	cmis_show_dom_chan_lvl_monitors(map, &sd);
++	cmis_show_dom_mod_lvl_flags(map);
++	cmis_show_dom_chan_lvl_flags(map);
++	if (sd.supports_alarms)
++		sff_show_thresholds(sd);
++}
++
+ static void cmis_show_all_common(const struct cmis_memory_map *map)
+ {
+ 	cmis_show_identifier(map);
+@@ -360,10 +772,10 @@ static void cmis_show_all_common(const struct cmis_memory_map *map)
+ 	cmis_show_cbl_asm_len(map);
+ 	cmis_show_sig_integrity(map);
+ 	cmis_show_mit_compliance(map);
+-	cmis_show_mod_lvl_monitors(map);
+ 	cmis_show_link_len(map);
+ 	cmis_show_vendor_info(map);
+ 	cmis_show_rev_compliance(map);
++	cmis_show_dom(map);
+ }
+ 
+ static void cmis_memory_map_init_buf(struct cmis_memory_map *map,
+diff --git a/cmis.h b/cmis.h
+index 8d90a04756ad..310697b0ef32 100644
+--- a/cmis.h
++++ b/cmis.h
+@@ -7,6 +7,18 @@
+ #define CMIS_MEMORY_MODEL_OFFSET		0x02
+ #define CMIS_MEMORY_MODEL_MASK			0x80
+ 
++/* Module Flags (Page 0) */
++#define CMIS_VCC_AW_OFFSET			0x09
++#define CMIS_VCC_LWARN_STATUS			0x80
++#define CMIS_VCC_HWARN_STATUS			0x40
++#define CMIS_VCC_LALARM_STATUS			0x20
++#define CMIS_VCC_HALARM_STATUS			0x10
++#define CMIS_TEMP_AW_OFFSET			0x09
++#define CMIS_TEMP_LWARN_STATUS			0x08
++#define CMIS_TEMP_HWARN_STATUS			0x04
++#define CMIS_TEMP_LALARM_STATUS			0x02
++#define CMIS_TEMP_HALARM_STATUS			0x01
++
+ #define CMIS_MODULE_TYPE_OFFSET			0x55
+ #define CMIS_MT_MMF				0x01
+ #define CMIS_MT_SMF				0x02
+@@ -121,10 +133,77 @@
+ #define CMIS_BANK_0_1_SUPPORTED			0x01
+ #define CMIS_BANK_0_3_SUPPORTED			0x02
+ 
++/* Module Characteristics Advertising (Page 1) */
++#define CMIS_DIAG_TYPE_OFFSET			0x97
++#define CMIS_RX_PWR_TYPE_MASK			0x10
++
++/* Supported Monitors Advertisement (Page 1) */
++#define CMIS_DIAG_CHAN_ADVER_OFFSET		0xA0
++#define CMIS_TX_BIAS_MON_MASK			0x01
++#define CMIS_TX_PWR_MON_MASK			0x02
++#define CMIS_RX_PWR_MON_MASK			0x04
++#define CMIS_TX_BIAS_MUL_MASK			0x18
++#define CMIS_TX_BIAS_MUL_1			0x00
++#define CMIS_TX_BIAS_MUL_2			0x08
++#define CMIS_TX_BIAS_MUL_4			0x10
++
+ /* Signal integrity controls */
+ #define CMIS_SIG_INTEG_TX_OFFSET		0xA1
+ #define CMIS_SIG_INTEG_RX_OFFSET		0xA2
+ 
++/*-----------------------------------------------------------------------
++ * Upper Memory Page 0x02: Optional Page that informs about module-defined
++ * thresholds for module-level and lane-specific threshold crossing monitors.
++ */
++
++/* Module-Level Monitor Thresholds (Page 2) */
++#define CMIS_TEMP_HALRM_OFFSET			0x80
++#define CMIS_TEMP_LALRM_OFFSET			0x82
++#define CMIS_TEMP_HWARN_OFFSET			0x84
++#define CMIS_TEMP_LWARN_OFFSET			0x86
++#define CMIS_VCC_HALRM_OFFSET			0x88
++#define CMIS_VCC_LALRM_OFFSET			0x8A
++#define CMIS_VCC_HWARN_OFFSET			0x8C
++#define CMIS_VCC_LWARN_OFFSET			0x8E
++
++/* Lane-Related Monitor Thresholds (Page 2) */
++#define CMIS_TX_PWR_HALRM_OFFSET		0xB0
++#define CMIS_TX_PWR_LALRM_OFFSET		0xB2
++#define CMIS_TX_PWR_HWARN_OFFSET		0xB4
++#define CMIS_TX_PWR_LWARN_OFFSET		0xB6
++#define CMIS_TX_BIAS_HALRM_OFFSET		0xB8
++#define CMIS_TX_BIAS_LALRM_OFFSET		0xBA
++#define CMIS_TX_BIAS_HWARN_OFFSET		0xBC
++#define CMIS_TX_BIAS_LWARN_OFFSET		0xBE
++#define CMIS_RX_PWR_HALRM_OFFSET		0xC0
++#define CMIS_RX_PWR_LALRM_OFFSET		0xC2
++#define CMIS_RX_PWR_HWARN_OFFSET		0xC4
++#define CMIS_RX_PWR_LWARN_OFFSET		0xC6
++
++/*-----------------------------------------------------------------------
++ * Upper Memory Page 0x11: Optional Page that contains lane dynamic status
++ * bytes.
++ */
++
++/* Media Lane-Specific Flags (Page 0x11) */
++#define CMIS_TX_PWR_AW_HALARM_OFFSET		0x8B
++#define CMIS_TX_PWR_AW_LALARM_OFFSET		0x8C
++#define CMIS_TX_PWR_AW_HWARN_OFFSET		0x8D
++#define CMIS_TX_PWR_AW_LWARN_OFFSET		0x8E
++#define CMIS_TX_BIAS_AW_HALARM_OFFSET		0x8F
++#define CMIS_TX_BIAS_AW_LALARM_OFFSET		0x90
++#define CMIS_TX_BIAS_AW_HWARN_OFFSET		0x91
++#define CMIS_TX_BIAS_AW_LWARN_OFFSET		0x92
++#define CMIS_RX_PWR_AW_HALARM_OFFSET		0x95
++#define CMIS_RX_PWR_AW_LALARM_OFFSET		0x96
++#define CMIS_RX_PWR_AW_HWARN_OFFSET		0x97
++#define CMIS_RX_PWR_AW_LWARN_OFFSET		0x98
++
++/* Media Lane-Specific Monitors (Page 0x11) */
++#define CMIS_TX_PWR_OFFSET			0x9A
++#define CMIS_TX_BIAS_OFFSET			0xAA
++#define CMIS_RX_PWR_OFFSET			0xBA
++
+ #define YESNO(x) (((x) != 0) ? "Yes" : "No")
+ #define ONOFF(x) (((x) != 0) ? "On" : "Off")
+ 
+-- 
+2.35.1
+
diff --git a/SOURCES/0032-cmis-Print-Module-State-and-Fault-Cause.patch b/SOURCES/0032-cmis-Print-Module-State-and-Fault-Cause.patch
new file mode 100644
index 0000000..87b279d
--- /dev/null
+++ b/SOURCES/0032-cmis-Print-Module-State-and-Fault-Cause.patch
@@ -0,0 +1,155 @@
+From 4a86034f138e1a96e54047b8036b0a4425e99944 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 23 Nov 2021 19:41:00 +0200
+Subject: [PATCH 32/35] cmis: Print Module State and Fault Cause
+
+Print the CMIS Module State when dumping EEPROM contents via the '-m'
+option. It can be used, for example, to test module power mode settings.
+
+Example output:
+
+ # ethtool -m swp11
+ Identifier                                : 0x18 (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628))
+ ...
+ Module State                              : 0x03 (ModuleReady)
+
+ # ethtool --set-module swp11 power-mode-policy auto
+
+ # ethtool -m swp11
+ Identifier                                : 0x18 (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628))
+ ...
+ Module State                              : 0x01 (ModuleLowPwr)
+
+In case the module is in fault state, print the CMIS Module Fault Cause.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ cmis.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ cmis.h | 16 ++++++++++++++
+ 2 files changed, 86 insertions(+)
+
+diff --git a/cmis.c b/cmis.c
+index d7b7097139b3..a32cc9f8b1f6 100644
+--- a/cmis.c
++++ b/cmis.c
+@@ -402,6 +402,74 @@ static void cmis_show_vendor_info(const struct cmis_memory_map *map)
+ 			       CMIS_CLEI_END_OFFSET, "CLEI code");
+ }
+ 
++/* Print the current Module State. Relevant documents:
++ * [1] CMIS Rev. 5, pag. 57, section 6.3.2.2, Figure 6-3
++ * [2] CMIS Rev. 5, pag. 60, section 6.3.2.3, Figure 6-4
++ * [3] CMIS Rev. 5, pag. 107, section 8.2.2, Table 8-6
++ */
++static void cmis_show_mod_state(const struct cmis_memory_map *map)
++{
++	__u8 mod_state;
++
++	mod_state = (map->lower_memory[CMIS_MODULE_STATE_OFFSET] &
++		     CMIS_MODULE_STATE_MASK) >> 1;
++	printf("\t%-41s : 0x%02x", "Module State", mod_state);
++	switch (mod_state) {
++	case CMIS_MODULE_STATE_MODULE_LOW_PWR:
++		printf(" (ModuleLowPwr)\n");
++		break;
++	case CMIS_MODULE_STATE_MODULE_PWR_UP:
++		printf(" (ModulePwrUp)\n");
++		break;
++	case CMIS_MODULE_STATE_MODULE_READY:
++		printf(" (ModuleReady)\n");
++		break;
++	case CMIS_MODULE_STATE_MODULE_PWR_DN:
++		printf(" (ModulePwrDn)\n");
++		break;
++	case CMIS_MODULE_STATE_MODULE_FAULT:
++		printf(" (ModuleFault)\n");
++		break;
++	default:
++		printf(" (reserved or unknown)\n");
++		break;
++	}
++}
++
++/* Print the Module Fault Information. Relevant documents:
++ * [1] CMIS Rev. 5, pag. 64, section 6.3.2.12
++ * [2] CMIS Rev. 5, pag. 115, section 8.2.10, Table 8-15
++ */
++static void cmis_show_mod_fault_cause(const struct cmis_memory_map *map)
++{
++	__u8 mod_state, fault_cause;
++
++	mod_state = (map->lower_memory[CMIS_MODULE_STATE_OFFSET] &
++		     CMIS_MODULE_STATE_MASK) >> 1;
++	if (mod_state != CMIS_MODULE_STATE_MODULE_FAULT)
++		return;
++
++	fault_cause = map->lower_memory[CMIS_MODULE_FAULT_OFFSET];
++	printf("\t%-41s : 0x%02x", "Module Fault Cause", fault_cause);
++	switch (fault_cause) {
++	case CMIS_MODULE_FAULT_NO_FAULT:
++		printf(" (No fault detected / not supported)\n");
++		break;
++	case CMIS_MODULE_FAULT_TEC_RUNAWAY:
++		printf(" (TEC runaway)\n");
++		break;
++	case CMIS_MODULE_FAULT_DATA_MEM_CORRUPTED:
++		printf(" (Data memory corrupted)\n");
++		break;
++	case CMIS_MODULE_FAULT_PROG_MEM_CORRUPTED:
++		printf(" (Program memory corrupted)\n");
++		break;
++	default:
++		printf(" (reserved or unknown)\n");
++		break;
++	}
++}
++
+ static void cmis_parse_dom_power_type(const struct cmis_memory_map *map,
+ 				      struct sff_diags *sd)
+ {
+@@ -775,6 +843,8 @@ static void cmis_show_all_common(const struct cmis_memory_map *map)
+ 	cmis_show_link_len(map);
+ 	cmis_show_vendor_info(map);
+ 	cmis_show_rev_compliance(map);
++	cmis_show_mod_state(map);
++	cmis_show_mod_fault_cause(map);
+ 	cmis_show_dom(map);
+ }
+ 
+diff --git a/cmis.h b/cmis.h
+index 310697b0ef32..2c67ad5640ab 100644
+--- a/cmis.h
++++ b/cmis.h
+@@ -7,6 +7,15 @@
+ #define CMIS_MEMORY_MODEL_OFFSET		0x02
+ #define CMIS_MEMORY_MODEL_MASK			0x80
+ 
++/* Global Status Information (Page 0) */
++#define CMIS_MODULE_STATE_OFFSET		0x03
++#define CMIS_MODULE_STATE_MASK			0x0E
++#define CMIS_MODULE_STATE_MODULE_LOW_PWR	0x01
++#define CMIS_MODULE_STATE_MODULE_PWR_UP		0x02
++#define CMIS_MODULE_STATE_MODULE_READY		0x03
++#define CMIS_MODULE_STATE_MODULE_PWR_DN		0x04
++#define CMIS_MODULE_STATE_MODULE_FAULT		0x05
++
+ /* Module Flags (Page 0) */
+ #define CMIS_VCC_AW_OFFSET			0x09
+ #define CMIS_VCC_LWARN_STATUS			0x80
+@@ -27,6 +36,13 @@
+ #define CMIS_CURR_TEMP_OFFSET			0x0E
+ #define CMIS_CURR_VCC_OFFSET			0x10
+ 
++/* Module Fault Information (Page 0) */
++#define CMIS_MODULE_FAULT_OFFSET		0x29
++#define CMIS_MODULE_FAULT_NO_FAULT		0x00
++#define CMIS_MODULE_FAULT_TEC_RUNAWAY		0x01
++#define CMIS_MODULE_FAULT_DATA_MEM_CORRUPTED	0x02
++#define CMIS_MODULE_FAULT_PROG_MEM_CORRUPTED	0x03
++
+ #define CMIS_CTOR_OFFSET			0xCB
+ 
+ /* Vendor related information (Page 0) */
+-- 
+2.35.1
+
diff --git a/SOURCES/0033-cmis-Print-Module-Level-Controls.patch b/SOURCES/0033-cmis-Print-Module-Level-Controls.patch
new file mode 100644
index 0000000..f41a573
--- /dev/null
+++ b/SOURCES/0033-cmis-Print-Module-Level-Controls.patch
@@ -0,0 +1,196 @@
+From 0b45f392c6c92e9823d6332622d7a45e7a36365e Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 23 Nov 2021 19:41:01 +0200
+Subject: [PATCH 33/35] cmis: Print Module-Level Controls
+
+Print the CMIS Module-Level Controls when dumping EEPROM contents via
+the '-m' option. It can be used to understand low power mode enforcement
+by the host.
+
+Example output:
+
+ # ethtool -m swp11
+ Identifier                                : 0x18 (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628))
+ ...
+ Module State                              : 0x03 (ModuleReady)
+ LowPwrAllowRequestHW                      : Off
+ LowPwrRequestSW                           : Off
+ ...
+ Transmit avg optical power (Channel 1)    : 1.3222 mW / 1.21 dBm
+ Transmit avg optical power (Channel 2)    : 1.2666 mW / 1.03 dBm
+ Transmit avg optical power (Channel 3)    : 1.2860 mW / 1.09 dBm
+ Transmit avg optical power (Channel 4)    : 1.2988 mW / 1.14 dBm
+ Transmit avg optical power (Channel 5)    : 1.2828 mW / 1.08 dBm
+ Transmit avg optical power (Channel 6)    : 1.2913 mW / 1.11 dBm
+ Transmit avg optical power (Channel 7)    : 1.2636 mW / 1.02 dBm
+ Transmit avg optical power (Channel 8)    : 1.3408 mW / 1.27 dBm
+ Transmit avg optical power (Channel 9)    : 1.3222 mW / 1.21 dBm
+ Transmit avg optical power (Channel 10)   : 1.2666 mW / 1.03 dBm
+ Transmit avg optical power (Channel 11)   : 1.2860 mW / 1.09 dBm
+ Transmit avg optical power (Channel 12)   : 1.2988 mW / 1.14 dBm
+ Transmit avg optical power (Channel 13)   : 1.2828 mW / 1.08 dBm
+ Transmit avg optical power (Channel 14)   : 1.2913 mW / 1.11 dBm
+ Transmit avg optical power (Channel 15)   : 1.2636 mW / 1.02 dBm
+ Transmit avg optical power (Channel 16)   : 1.3408 mW / 1.27 dBm
+ Rcvr signal avg optical power (Channel 1) : 1.1351 mW / 0.55 dBm
+ Rcvr signal avg optical power (Channel 2) : 1.1603 mW / 0.65 dBm
+ Rcvr signal avg optical power (Channel 3) : 1.1529 mW / 0.62 dBm
+ Rcvr signal avg optical power (Channel 4) : 1.1670 mW / 0.67 dBm
+ Rcvr signal avg optical power (Channel 5) : 1.1759 mW / 0.70 dBm
+ Rcvr signal avg optical power (Channel 6) : 1.1744 mW / 0.70 dBm
+ Rcvr signal avg optical power (Channel 7) : 1.1188 mW / 0.49 dBm
+ Rcvr signal avg optical power (Channel 8) : 1.1640 mW / 0.66 dBm
+ Rcvr signal avg optical power (Channel 9) : 1.1351 mW / 0.55 dBm
+ Rcvr signal avg optical power (Channel 10) : 1.1603 mW / 0.65 dBm
+ Rcvr signal avg optical power (Channel 11) : 1.1529 mW / 0.62 dBm
+ Rcvr signal avg optical power (Channel 12) : 1.1670 mW / 0.67 dBm
+ Rcvr signal avg optical power (Channel 13) : 1.1759 mW / 0.70 dBm
+ Rcvr signal avg optical power (Channel 14) : 1.1744 mW / 0.70 dBm
+ Rcvr signal avg optical power (Channel 15) : 1.1188 mW / 0.49 dBm
+ Rcvr signal avg optical power (Channel 16) : 1.1640 mW / 0.66 dBm
+
+ # ethtool --set-module swp11 power-mode-policy auto
+
+ # ethtool -m swp11
+ Identifier                                : 0x18 (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628))
+ ...
+ Module State                              : 0x01 (ModuleLowPwr)
+ LowPwrAllowRequestHW                      : Off
+ LowPwrRequestSW                           : On
+ ...
+ Transmit avg optical power (Channel 1)    : 0.0001 mW / -40.00 dBm
+ Transmit avg optical power (Channel 2)    : 0.0001 mW / -40.00 dBm
+ Transmit avg optical power (Channel 3)    : 0.0001 mW / -40.00 dBm
+ Transmit avg optical power (Channel 4)    : 0.0001 mW / -40.00 dBm
+ Transmit avg optical power (Channel 5)    : 0.0001 mW / -40.00 dBm
+ Transmit avg optical power (Channel 6)    : 0.0001 mW / -40.00 dBm
+ Transmit avg optical power (Channel 7)    : 0.0001 mW / -40.00 dBm
+ Transmit avg optical power (Channel 8)    : 0.0001 mW / -40.00 dBm
+ Transmit avg optical power (Channel 9)    : 0.0001 mW / -40.00 dBm
+ Transmit avg optical power (Channel 10)   : 0.0001 mW / -40.00 dBm
+ Transmit avg optical power (Channel 11)   : 0.0001 mW / -40.00 dBm
+ Transmit avg optical power (Channel 12)   : 0.0001 mW / -40.00 dBm
+ Transmit avg optical power (Channel 13)   : 0.0001 mW / -40.00 dBm
+ Transmit avg optical power (Channel 14)   : 0.0001 mW / -40.00 dBm
+ Transmit avg optical power (Channel 15)   : 0.0001 mW / -40.00 dBm
+ Transmit avg optical power (Channel 16)   : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 1) : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 2) : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 3) : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 4) : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 5) : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 6) : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 7) : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 8) : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 9) : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 10) : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 11) : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 12) : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 13) : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 14) : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 15) : 0.0001 mW / -40.00 dBm
+ Rcvr signal avg optical power (Channel 16) : 0.0001 mW / -40.00 dBm
+
+ # ethtool --set-module swp11 power-mode-policy high
+
+ # ethtool -m swp11
+ Identifier                                : 0x18 (QSFP-DD Double Density 8X Pluggable Transceiver (INF-8628))
+ ...
+ Module State                              : 0x03 (ModuleReady)
+ LowPwrAllowRequestHW                      : Off
+ LowPwrRequestSW                           : Off
+ ...
+ Transmit avg optical power (Channel 1)    : 1.3690 mW / 1.36 dBm
+ Transmit avg optical power (Channel 2)    : 1.3036 mW / 1.15 dBm
+ Transmit avg optical power (Channel 3)    : 1.3358 mW / 1.26 dBm
+ Transmit avg optical power (Channel 4)    : 1.3509 mW / 1.31 dBm
+ Transmit avg optical power (Channel 5)    : 1.3193 mW / 1.20 dBm
+ Transmit avg optical power (Channel 6)    : 1.3314 mW / 1.24 dBm
+ Transmit avg optical power (Channel 7)    : 1.3042 mW / 1.15 dBm
+ Transmit avg optical power (Channel 8)    : 1.3919 mW / 1.44 dBm
+ Transmit avg optical power (Channel 9)    : 1.3690 mW / 1.36 dBm
+ Transmit avg optical power (Channel 10)   : 1.3036 mW / 1.15 dBm
+ Transmit avg optical power (Channel 11)   : 1.3358 mW / 1.26 dBm
+ Transmit avg optical power (Channel 12)   : 1.3509 mW / 1.31 dBm
+ Transmit avg optical power (Channel 13)   : 1.3193 mW / 1.20 dBm
+ Transmit avg optical power (Channel 14)   : 1.3314 mW / 1.24 dBm
+ Transmit avg optical power (Channel 15)   : 1.3042 mW / 1.15 dBm
+ Transmit avg optical power (Channel 16)   : 1.3919 mW / 1.44 dBm
+ Rcvr signal avg optical power (Channel 1) : 1.1299 mW / 0.53 dBm
+ Rcvr signal avg optical power (Channel 2) : 1.1566 mW / 0.63 dBm
+ Rcvr signal avg optical power (Channel 3) : 1.1484 mW / 0.60 dBm
+ Rcvr signal avg optical power (Channel 4) : 1.1655 mW / 0.67 dBm
+ Rcvr signal avg optical power (Channel 5) : 1.1751 mW / 0.70 dBm
+ Rcvr signal avg optical power (Channel 6) : 1.1595 mW / 0.64 dBm
+ Rcvr signal avg optical power (Channel 7) : 1.1158 mW / 0.48 dBm
+ Rcvr signal avg optical power (Channel 8) : 1.1595 mW / 0.64 dBm
+ Rcvr signal avg optical power (Channel 9) : 1.1299 mW / 0.53 dBm
+ Rcvr signal avg optical power (Channel 10) : 1.1566 mW / 0.63 dBm
+ Rcvr signal avg optical power (Channel 11) : 1.1484 mW / 0.60 dBm
+ Rcvr signal avg optical power (Channel 12) : 1.1655 mW / 0.67 dBm
+ Rcvr signal avg optical power (Channel 13) : 1.1751 mW / 0.70 dBm
+ Rcvr signal avg optical power (Channel 14) : 1.1595 mW / 0.64 dBm
+ Rcvr signal avg optical power (Channel 15) : 1.1158 mW / 0.48 dBm
+ Rcvr signal avg optical power (Channel 16) : 1.1595 mW / 0.64 dBm
+
+In the above example, the LowPwrRequestHW signal is ignored and low
+power mode is controlled via software only.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ cmis.c | 15 +++++++++++++++
+ cmis.h |  5 +++++
+ 2 files changed, 20 insertions(+)
+
+diff --git a/cmis.c b/cmis.c
+index a32cc9f8b1f6..d0b62728e998 100644
+--- a/cmis.c
++++ b/cmis.c
+@@ -470,6 +470,20 @@ static void cmis_show_mod_fault_cause(const struct cmis_memory_map *map)
+ 	}
+ }
+ 
++/* Print the current Module-Level Controls. Relevant documents:
++ * [1] CMIS Rev. 5, pag. 58, section 6.3.2.2, Table 6-12
++ * [2] CMIS Rev. 5, pag. 111, section 8.2.6, Table 8-10
++ */
++static void cmis_show_mod_lvl_controls(const struct cmis_memory_map *map)
++{
++	printf("\t%-41s : ", "LowPwrAllowRequestHW");
++	printf("%s\n", ONOFF(map->lower_memory[CMIS_MODULE_CONTROL_OFFSET] &
++			     CMIS_LOW_PWR_ALLOW_REQUEST_HW_MASK));
++	printf("\t%-41s : ", "LowPwrRequestSW");
++	printf("%s\n", ONOFF(map->lower_memory[CMIS_MODULE_CONTROL_OFFSET] &
++			     CMIS_LOW_PWR_REQUEST_SW_MASK));
++}
++
+ static void cmis_parse_dom_power_type(const struct cmis_memory_map *map,
+ 				      struct sff_diags *sd)
+ {
+@@ -845,6 +859,7 @@ static void cmis_show_all_common(const struct cmis_memory_map *map)
+ 	cmis_show_rev_compliance(map);
+ 	cmis_show_mod_state(map);
+ 	cmis_show_mod_fault_cause(map);
++	cmis_show_mod_lvl_controls(map);
+ 	cmis_show_dom(map);
+ }
+ 
+diff --git a/cmis.h b/cmis.h
+index 2c67ad5640ab..46797081f13c 100644
+--- a/cmis.h
++++ b/cmis.h
+@@ -36,6 +36,11 @@
+ #define CMIS_CURR_TEMP_OFFSET			0x0E
+ #define CMIS_CURR_VCC_OFFSET			0x10
+ 
++/* Module Global Controls (Page 0) */
++#define CMIS_MODULE_CONTROL_OFFSET		0x1A
++#define CMIS_LOW_PWR_ALLOW_REQUEST_HW_MASK	0x40
++#define CMIS_LOW_PWR_REQUEST_SW_MASK		0x10
++
+ /* Module Fault Information (Page 0) */
+ #define CMIS_MODULE_FAULT_OFFSET		0x29
+ #define CMIS_MODULE_FAULT_NO_FAULT		0x00
+-- 
+2.35.1
+
diff --git a/SOURCES/0034-sff-8636-Print-Power-set-and-Power-override-bits.patch b/SOURCES/0034-sff-8636-Print-Power-set-and-Power-override-bits.patch
new file mode 100644
index 0000000..38b7f4c
--- /dev/null
+++ b/SOURCES/0034-sff-8636-Print-Power-set-and-Power-override-bits.patch
@@ -0,0 +1,108 @@
+From 48391fa16592b47d37ef63466111c751a10c3e56 Mon Sep 17 00:00:00 2001
+From: Ido Schimmel <idosch@nvidia.com>
+Date: Tue, 23 Nov 2021 19:41:02 +0200
+Subject: [PATCH 34/35] sff-8636: Print Power set and Power override bits
+
+Print the SFF-8636 Power set and Power override bits when dumping EEPROM
+contents via the '-m' option. They can be used to understand low power
+mode enforcement by the host.
+
+The 'SFF8636_LOW_PWR_MODE' define is renamed to 'SFF8636_LOW_PWR_SET' to
+reflect its naming in the standard for QSFP+/QSFP28.
+
+Example output:
+
+ # ethtool -m swp13
+ Identifier                                : 0x11 (QSFP28)
+ ...
+ Extended identifier description           : 5.0W max. Power consumption,  High Power Class (> 3.5 W) enabled
+ Power set                                 : Off
+ Power override                            : On
+ ...
+ Transmit avg optical power (Channel 1)    : 0.7633 mW / -1.17 dBm
+ Transmit avg optical power (Channel 2)    : 0.7649 mW / -1.16 dBm
+ Transmit avg optical power (Channel 3)    : 0.7696 mW / -1.14 dBm
+ Transmit avg optical power (Channel 4)    : 0.7739 mW / -1.11 dBm
+ Rcvr signal avg optical power(Channel 1)  : 0.9240 mW / -0.34 dBm
+ Rcvr signal avg optical power(Channel 2)  : 0.9129 mW / -0.40 dBm
+ Rcvr signal avg optical power(Channel 3)  : 0.9194 mW / -0.36 dBm
+ Rcvr signal avg optical power(Channel 4)  : 0.8708 mW / -0.60 dBm
+
+ # ethtool --set-module swp13 power-mode-policy auto
+
+ # ethtool -m swp13
+ Identifier                                : 0x11 (QSFP28)
+ ...
+ Extended identifier description           : 5.0W max. Power consumption,  High Power Class (> 3.5 W) not enabled
+ Power set                                 : On
+ Power override                            : On
+ ...
+ Transmit avg optical power (Channel 1)    : 0.0000 mW / -inf dBm
+ Transmit avg optical power (Channel 2)    : 0.0000 mW / -inf dBm
+ Transmit avg optical power (Channel 3)    : 0.0000 mW / -inf dBm
+ Transmit avg optical power (Channel 4)    : 0.0000 mW / -inf dBm
+ Rcvr signal avg optical power(Channel 1)  : 0.0000 mW / -inf dBm
+ Rcvr signal avg optical power(Channel 2)  : 0.0000 mW / -inf dBm
+ Rcvr signal avg optical power(Channel 3)  : 0.0000 mW / -inf dBm
+ Rcvr signal avg optical power(Channel 4)  : 0.0000 mW / -inf dBm
+
+ # ethtool --set-module swp13 power-mode-policy high
+
+ # ethtool -m swp13
+ Identifier                                : 0x11 (QSFP28)
+ ...
+ Extended identifier description           : 5.0W max. Power consumption,  High Power Class (> 3.5 W) enabled
+ Power set                                 : Off
+ Power override                            : On
+ ...
+ Transmit avg optical power (Channel 1)    : 0.7733 mW / -1.12 dBm
+ Transmit avg optical power (Channel 2)    : 0.7754 mW / -1.10 dBm
+ Transmit avg optical power (Channel 3)    : 0.7885 mW / -1.03 dBm
+ Transmit avg optical power (Channel 4)    : 0.7886 mW / -1.03 dBm
+ Rcvr signal avg optical power(Channel 1)  : 0.9248 mW / -0.34 dBm
+ Rcvr signal avg optical power(Channel 2)  : 0.9129 mW / -0.40 dBm
+ Rcvr signal avg optical power(Channel 3)  : 0.9187 mW / -0.37 dBm
+ Rcvr signal avg optical power(Channel 4)  : 0.8785 mW / -0.56 dBm
+
+In the above example, the LPMode signal is ignored (Power override is
+always on) and low power mode is controlled via software only.
+
+Signed-off-by: Ido Schimmel <idosch@nvidia.com>
+---
+ qsfp.c | 6 ++++++
+ qsfp.h | 2 +-
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/qsfp.c b/qsfp.c
+index b3c9e1516af9..57aac86bd5f6 100644
+--- a/qsfp.c
++++ b/qsfp.c
+@@ -268,6 +268,12 @@ static void sff8636_show_ext_identifier(const struct sff8636_memory_map *map)
+ 		printf(" High Power Class (> 3.5 W) enabled\n");
+ 	else
+ 		printf(" High Power Class (> 3.5 W) not enabled\n");
++	printf("\t%-41s : ", "Power set");
++	printf("%s\n", ONOFF(map->lower_memory[SFF8636_PWR_MODE_OFFSET] &
++			     SFF8636_LOW_PWR_SET));
++	printf("\t%-41s : ", "Power override");
++	printf("%s\n", ONOFF(map->lower_memory[SFF8636_PWR_MODE_OFFSET] &
++			     SFF8636_PWR_OVERRIDE));
+ }
+ 
+ static void sff8636_show_connector(const struct sff8636_memory_map *map)
+diff --git a/qsfp.h b/qsfp.h
+index 1d8f24b5cbc2..aabf09fdc623 100644
+--- a/qsfp.h
++++ b/qsfp.h
+@@ -180,7 +180,7 @@
+ 
+ #define	SFF8636_PWR_MODE_OFFSET		0x5D
+ #define	 SFF8636_HIGH_PWR_ENABLE		(1 << 2)
+-#define	 SFF8636_LOW_PWR_MODE			(1 << 1)
++#define	 SFF8636_LOW_PWR_SET			(1 << 1)
+ #define	 SFF8636_PWR_OVERRIDE			(1 << 0)
+ 
+ #define	SFF8636_TX_APP_SELECT_4_OFFSET	0x5E
+-- 
+2.35.1
+
diff --git a/SOURCES/0035-sff-8079-8472-Fix-missing-sff-8472-output-in-netlink.patch b/SOURCES/0035-sff-8079-8472-Fix-missing-sff-8472-output-in-netlink.patch
new file mode 100644
index 0000000..667a5d4
--- /dev/null
+++ b/SOURCES/0035-sff-8079-8472-Fix-missing-sff-8472-output-in-netlink.patch
@@ -0,0 +1,107 @@
+From 206cd00caf9b71ae20f897075b4bd261e923e563 Mon Sep 17 00:00:00 2001
+From: Ivan Vecera <ivecera@redhat.com>
+Date: Tue, 31 May 2022 20:55:03 +0200
+Subject: [PATCH 35/35] sff-8079/8472: Fix missing sff-8472 output in netlink
+ path
+
+Commit 25b64c66f58d ("ethtool: Add netlink handler for
+getmodule (-m)") provided a netlink variant for getmodule
+but also introduced a regression as netlink output is different
+from ioctl output that provides information from A2h page
+via sff8472_show_all().
+
+To fix this the netlink path should check a presence of A2h page
+by value of bit 6 in byte 92 of page A0h and if it is set then
+get A2h page and call sff8472_show_all().
+
+Fixes: 25b64c66f58d ("ethtool: Add netlink handler for getmodule (-m)")
+Tested-by: Daniel Juarez <djuarezg@cern.ch>
+Tested-by: Ido Schimmel <idosch@nvidia.com>
+Reviewed-by: Ido Schimmel <idosch@nvidia.com>
+Co-authored-by: Ido Schimmel <idosch@nvidia.com>
+Signed-off-by: Ivan Vecera <ivecera@redhat.com>
+---
+ sfpid.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++--------
+ 1 file changed, 46 insertions(+), 8 deletions(-)
+
+diff --git a/sfpid.c b/sfpid.c
+index 621d1e86c278..1bc45c183770 100644
+--- a/sfpid.c
++++ b/sfpid.c
+@@ -13,8 +13,9 @@
+ #include "sff-common.h"
+ #include "netlink/extapi.h"
+ 
+-#define SFF8079_PAGE_SIZE	0x80
+-#define SFF8079_I2C_ADDRESS_LOW	0x50
++#define SFF8079_PAGE_SIZE		0x80
++#define SFF8079_I2C_ADDRESS_LOW		0x50
++#define SFF8079_I2C_ADDRESS_HIGH	0x51
+ 
+ static void sff8079_show_identifier(const __u8 *id)
+ {
+@@ -450,18 +451,55 @@ void sff8079_show_all_ioctl(const __u8 *id)
+ 	sff8079_show_all_common(id);
+ }
+ 
+-int sff8079_show_all_nl(struct cmd_context *ctx)
++static int sff8079_get_eeprom_page(struct cmd_context *ctx, u8 i2c_address,
++				   __u8 *buf)
+ {
+ 	struct ethtool_module_eeprom request = {
+ 		.length = SFF8079_PAGE_SIZE,
+-		.i2c_address = SFF8079_I2C_ADDRESS_LOW,
++		.i2c_address = i2c_address,
+ 	};
+ 	int ret;
+ 
+ 	ret = nl_get_eeprom_page(ctx, &request);
+-	if (ret < 0)
+-		return ret;
+-	sff8079_show_all_common(request.data);
++	if (!ret)
++		memcpy(buf, request.data, SFF8079_PAGE_SIZE);
++
++	return ret;
++}
++
++int sff8079_show_all_nl(struct cmd_context *ctx)
++{
++	u8 *buf;
++	int ret;
++
++	/* The SFF-8472 parser expects a single buffer that contains the
++	 * concatenation of the first 256 bytes from addresses A0h and A2h,
++	 * respectively.
++	 */
++	buf = calloc(1, ETH_MODULE_SFF_8472_LEN);
++	if (!buf)
++		return -ENOMEM;
++
++	/* Read A0h page */
++	ret = sff8079_get_eeprom_page(ctx, SFF8079_I2C_ADDRESS_LOW, buf);
++	if (ret)
++		goto out;
++
++	sff8079_show_all_common(buf);
++
++	/* Finish if A2h page is not present */
++	if (!(buf[92] & (1 << 6)))
++		goto out;
++
++	/* Read A2h page */
++	ret = sff8079_get_eeprom_page(ctx, SFF8079_I2C_ADDRESS_HIGH,
++				      buf + ETH_MODULE_SFF_8079_LEN);
++	if (ret)
++		goto out;
++
++	sff8472_show_all(buf);
++out:
++	free(buf);
+ 
+-	return 0;
++	return ret;
+ }
+-- 
+2.35.1
+
diff --git a/SPECS/ethtool.spec b/SPECS/ethtool.spec
index aa6e62c..3174f49 100644
--- a/SPECS/ethtool.spec
+++ b/SPECS/ethtool.spec
@@ -1,7 +1,7 @@
 Name:		ethtool
 Epoch:		2
 Version:	5.13
-Release:	1%{?dist}
+Release:	2%{?dist}
 Summary:	Settings tool for Ethernet NICs
 License:	GPLv2
 Group:		Applications/System
@@ -10,6 +10,42 @@ Source0:	https://www.kernel.org/pub/software/network/%{name}/%{name}-%{version}.
 BuildRequires:	libmnl-devel
 Conflicts:	filesystem < 3
 
+Patch1:		0001-sff-8636-Fix-parsing-of-Page-03h-in-IOCTL-path.patch
+Patch2:		0002-cmis-Fix-invalid-memory-access-in-IOCTL-path.patch
+Patch3:		0003-netlink-eeprom-Fallback-to-IOCTL-when-a-complete-hex.patch
+Patch4:		0004-ethtool-Fix-compilation-warning-when-pretty-dump-is-.patch
+Patch5:		0005-netlink-eeprom-Fix-compilation-when-pretty-dump-is-d.patch
+Patch6:		0006-cmis-Fix-CLEI-code-parsing.patch
+Patch7:		0007-cmis-Fix-wrong-define-name.patch
+Patch8:		0008-cmis-Correct-comment.patch
+Patch9:		0009-sff-8636-Remove-incorrect-comment.patch
+Patch10:	0010-sff-8636-Fix-incorrect-function-name.patch
+Patch11:	0011-sff-8636-Convert-if-statement-to-switch-case.patch
+Patch12:	0012-sff-8636-Remove-extra-blank-lines.patch
+Patch13:	0013-cmis-Rename-CMIS-parsing-functions.patch
+Patch14:	0014-cmis-Initialize-CMIS-memory-map.patch
+Patch15:	0015-cmis-Use-memory-map-during-parsing.patch
+Patch16:	0016-cmis-Consolidate-code-between-IOCTL-and-netlink-path.patch
+Patch17:	0017-sff-8636-Rename-SFF-8636-parsing-functions.patch
+Patch18:	0018-sff-8636-Initialize-SFF-8636-memory-map.patch
+Patch19:	0019-sff-8636-Use-memory-map-during-parsing.patch
+Patch20:	0020-sff-8636-Consolidate-code-between-IOCTL-and-netlink-.patch
+Patch21:	0021-sff-8079-Split-SFF-8079-parsing-function.patch
+Patch22:	0022-netlink-eeprom-Export-a-function-to-request-an-EEPRO.patch
+Patch23:	0023-cmis-Request-specific-pages-for-parsing-in-netlink-p.patch
+Patch24:	0024-sff-8636-Request-specific-pages-for-parsing-in-netli.patch
+Patch25:	0025-sff-8079-Request-specific-pages-for-parsing-in-netli.patch
+Patch26:	0026-netlink-eeprom-Defer-page-requests-to-individual-par.patch
+Patch27:	0027-sff-8636-Use-an-SFF-8636-specific-define-for-maximum.patch
+Patch28:	0028-sff-common-Move-OFFSET_TO_U16_PTR-to-common-header-f.patch
+Patch29:	0029-cmis-Initialize-Page-02h-in-memory-map.patch
+Patch30:	0030-cmis-Initialize-Banked-Page-11h-in-memory-map.patch
+Patch31:	0031-cmis-Parse-and-print-diagnostic-information.patch
+Patch32:	0032-cmis-Print-Module-State-and-Fault-Cause.patch
+Patch33:	0033-cmis-Print-Module-Level-Controls.patch
+Patch34:	0034-sff-8636-Print-Power-set-and-Power-override-bits.patch
+Patch35:	0035-sff-8079-8472-Fix-missing-sff-8472-output-in-netlink.patch
+
 %description
 This utility allows querying and changing settings such as speed,
 port, auto-negotiation, PCI locations and checksum offload on many
@@ -17,6 +53,41 @@ network devices, especially of Ethernet devices.
 
 %prep
 %setup -q
+%patch1 -p1
+%patch2 -p1
+%patch3 -p1
+%patch4 -p1
+%patch5 -p1
+%patch6 -p1
+%patch7 -p1
+%patch8 -p1
+%patch9 -p1
+%patch10 -p1
+%patch11 -p1
+%patch12 -p1
+%patch13 -p1
+%patch14 -p1
+%patch15 -p1
+%patch16 -p1
+%patch17 -p1
+%patch18 -p1
+%patch19 -p1
+%patch20 -p1
+%patch21 -p1
+%patch22 -p1
+%patch23 -p1
+%patch24 -p1
+%patch25 -p1
+%patch26 -p1
+%patch27 -p1
+%patch28 -p1
+%patch29 -p1
+%patch30 -p1
+%patch31 -p1
+%patch32 -p1
+%patch33 -p1
+%patch34 -p1
+%patch35 -p1
 
 %build
 %configure
@@ -33,6 +104,9 @@ make DESTDIR=%{buildroot} INSTALL='install -p' install
 %{_datadir}/bash-completion/completions/ethtool
 
 %changelog
+* Thu Jun 02 2022 Ivan Vecera <ivecera@redhat.com> - 2:5.13-2
+- Module (SFP/QSFP/CMIS) related bugfixes
+
 * Thu Nov 18 2021 Ivan Vecera <ivecera@redhat.com> - 2:5.13-1
 - Updated to upstream v5.13