ichspi: Properly add Emmitsburg PCH

The Emmitsburg or C740 series PCH is actually ahead of all the other,
currently supported chipsets. Finally, Intel added new registers that
carry the read and write access permissions for all 16 regions.

The old FRAP register seems to be still around, so we print both new
and old registers. For the detailed report we use the new registers,
though.

We also adapt the descriptor detection slightly: We check for `NM == 6`
just like we did for Lewisburg. This way we won't treat a huge range of
ISL (ICH/PCH strap length) values as Emmitsburg, which should result in
less false positives.

The output of `ich_descriptors_tool' tested on some Supermicro firmware
looks reasonable.  Also tested read/erase/write in `swseq' and  `hwseq'
modes with 7 series PCH, reading with ADL-P. All logs still report FRAP
settings correctly.

Change-Id: Ibf5ebe2e2edfe5e5ae26bf1136648bf6354b0aa9
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.sourcearcade.org/c/flashprog/+/187
diff --git a/chipset_enable.c b/chipset_enable.c
index 5dc651d..2988a60 100644
--- a/chipset_enable.c
+++ b/chipset_enable.c
@@ -699,6 +699,7 @@
 		boot_straps = boot_straps_pch8_lp;
 		break;
 	case CHIPSET_500_SERIES_TIGER_POINT:
+	case CHIPSET_C740_SERIES_EMMITSBURG:
 		boot_straps = boot_straps_pch500;
 		break;
 	case CHIPSET_APOLLO_LAKE:
@@ -1011,6 +1012,12 @@
 	return enable_flash_pch_spidev(spi_dev, name, CHIPSET_500_SERIES_TIGER_POINT);
 }
 
+static int enable_flash_c740(struct flashprog_programmer *const prog,
+			     struct pci_dev *const spi_dev, const char *const name)
+{
+	return enable_flash_pch_spidev(spi_dev, name, CHIPSET_C740_SERIES_EMMITSBURG);
+}
+
 /* Silvermont architecture: Bay Trail(-T/-I), Avoton/Rangeley.
  * These have a distinctly different behavior compared to other Intel chipsets and hence are handled separately.
  *
@@ -2182,7 +2189,7 @@
 	{0x8086, 0xa247,   ANY_REV, B_S,    NT,  "Intel", "C620 Series Supersku",	enable_flash_c620},
 	{0x8086, 0xa248,   ANY_REV, B_S,    NT,  "Intel", "C620 Series Supersku",	enable_flash_c620},
 	{0x8086, 0xa249,   ANY_REV, B_S,    NT,  "Intel", "C620 Series Supersku",	enable_flash_c620},
-	{0x8086, 0x1bca,   ANY_REV, B_S,    NT,  "Intel", "Emmitsburg SKU",		enable_flash_c620},
+	{0x8086, 0x1bca,   ANY_REV, B_S,    NT,  "Intel", "Emmitsburg SKU",		enable_flash_c740},
 	{0x8086, 0xa2c4,   ANY_REV, B_S,    NT,  "Intel", "H270",			enable_flash_pch100},
 	{0x8086, 0xa2c5,   ANY_REV, B_S,    NT,  "Intel", "Z270",			enable_flash_pch100},
 	{0x8086, 0xa2c6,   ANY_REV, B_S,    NT,  "Intel", "Q270",			enable_flash_pch100},
diff --git a/ich_descriptors.c b/ich_descriptors.c
index dfc1cb8..207adb0 100644
--- a/ich_descriptors.c
+++ b/ich_descriptors.c
@@ -44,6 +44,7 @@
 	case CHIPSET_GEMINI_LAKE:
 		return 6;
 	case CHIPSET_C620_SERIES_LEWISBURG:
+	case CHIPSET_C740_SERIES_EMMITSBURG:
 	case CHIPSET_300_SERIES_CANNON_POINT:
 	case CHIPSET_500_SERIES_TIGER_POINT:
 	case CHIPSET_ELKHART_LAKE:
@@ -71,6 +72,7 @@
 {
 	switch (cs) {
 	case CHIPSET_C620_SERIES_LEWISBURG:
+	case CHIPSET_C740_SERIES_EMMITSBURG:
 		return 6;
 	case CHIPSET_APOLLO_LAKE:
 	case CHIPSET_GEMINI_LAKE:
@@ -91,6 +93,7 @@
 	switch (cs) {
 	case CHIPSET_100_SERIES_SUNRISE_POINT:
 	case CHIPSET_C620_SERIES_LEWISBURG:
+	case CHIPSET_C740_SERIES_EMMITSBURG:
 		return true;
 	default:
 		return cs < SPI_ENGINE_PCH100;
@@ -124,6 +127,7 @@
 		"9 series Wildcat Point", "9 series Wildcat Point LP", "100 series Sunrise Point",
 		"C620 series Lewisburg", "300/400 series Cannon/Comet Point",
 		"500/600 series Tiger/Alder Point", "Apollo Lake", "Gemini Lake", "Elkhart Lake",
+		"C740 series Emmitsburg",
 	};
 	if (cs < CHIPSET_ICH8 || cs - CHIPSET_ICH8 + 1 >= ARRAY_SIZE(chipset_names))
 		cs = 0;
@@ -292,6 +296,7 @@
 	case CHIPSET_GEMINI_LAKE:
 		return freq_str[2][value];
 	case CHIPSET_500_SERIES_TIGER_POINT:
+	case CHIPSET_C740_SERIES_EMMITSBURG:
 		return freq_str[3][value];
 	case CHIPSET_ELKHART_LAKE:
 		return freq_str[4][value];
@@ -510,7 +515,8 @@
 				" FD", "IFWI", " TXE", " n/a", "Pltf.", "DevExp", NULL
 			};
 			prettyprint_pch100_masters(desc, nm, masters, nr, regions);
-		} else if (cs == CHIPSET_C620_SERIES_LEWISBURG) {
+		} else if (cs == CHIPSET_C620_SERIES_LEWISBURG ||
+			   cs == CHIPSET_C740_SERIES_EMMITSBURG) {
 			const char *const masters[] = {
 				"BIOS", "ME", "GbE", "DE", "BMC", "IE", NULL
 			};
@@ -991,8 +997,13 @@
 			warn_peculiar_desc("Gemini Lake");
 			return CHIPSET_GEMINI_LAKE;
 		}
-		if (content->ISL <= 80)
-			return CHIPSET_C620_SERIES_LEWISBURG;
+		if (content->NM == 6) {
+			/* 0x8b is from the SPI Guide, but not yet seen in the wild. */
+			if (0x50 <= content->ISL && content->ISL <= 0x8b)
+				return CHIPSET_C740_SERIES_EMMITSBURG;
+			warn_peculiar_desc("C740 series");
+			return CHIPSET_C740_SERIES_EMMITSBURG;
+		}
 		warn_peculiar_desc("Ibex Peak");
 		return CHIPSET_5_SERIES_IBEX_PEAK;
 	} else if (upper->MDTBA == 0x00) {
diff --git a/ichspi.c b/ichspi.c
index 6bdd0b6..9a5f1b7 100644
--- a/ichspi.c
+++ b/ichspi.c
@@ -29,6 +29,10 @@
 #include "spi.h"
 #include "ich_descriptors.h"
 
+/* New since C740 series Emmitsburg */
+#define BIOS_BM_RAP		0x118
+#define BIOS_BM_WAP		0x11c
+
 /* Apollo Lake */
 #define APL_REG_FREG12		0xe0	/* 32 Bytes Flash Region 12 */
 
@@ -1517,7 +1521,8 @@
 	"locked", "read-only", "write-only", "read-write"
 };
 
-static enum ich_access_protection ich9_handle_frap(uint32_t frap, unsigned int i)
+static enum ich_access_protection ich9_handle_access_perm(
+		uint32_t bm_rap, uint32_t bm_wap, unsigned int max, unsigned int i)
 {
 	const int rwperms_unknown = ARRAY_SIZE(access_names);
 	static const char *const region_names[] = {
@@ -1534,9 +1539,9 @@
 		: APL_REG_FREG12 + (i - 12) * 4;
 	uint32_t freg = mmio_readl(ich_spibar + offset);
 
-	if (i < 8) {
-		rwperms = (((ICH_BRWA(frap) >> i) & 1) << 1) |
-			  (((ICH_BRRA(frap) >> i) & 1) << 0);
+	if (i < max) {
+		rwperms = (((bm_wap >> i) & 1) << 1) |
+			  (((bm_rap >> i) & 1) << 0);
 	} else {
 		/* Datasheets don't define any access bits for regions > 7. We
 		   can't rely on the actual descriptor settings either as there
@@ -1803,9 +1808,20 @@
 		msg_pdbg("BRWA 0x%02x, ", ICH_BRWA(tmp));
 		msg_pdbg("BRRA 0x%02x\n", ICH_BRRA(tmp));
 
+		unsigned int max = 8; /* old, FRAP max. */
+		uint32_t bm_wap = ICH_BRWA(tmp), bm_rap = ICH_BRRA(tmp);
+
+		if (ich_gen >= CHIPSET_HAS_NEW_ACCESS_PERM) {
+			max = 32;
+			bm_wap = mmio_readl(ich_spibar + BIOS_BM_WAP);
+			bm_rap = mmio_readl(ich_spibar + BIOS_BM_RAP);
+			msg_pdbg("0x%x: 0x%08x (BIOS_BM_WAP)\n", BIOS_BM_WAP, bm_wap);
+			msg_pdbg("0x%x: 0x%08x (BIOS_BM_RAP)\n", BIOS_BM_RAP, bm_rap);
+		}
+
 		/* Handle FREGx and FRAP registers */
 		for (i = 0; i < num_freg; i++)
-			ich_spi_rw_restricted |= ich9_handle_frap(tmp, i);
+			ich_spi_rw_restricted |= ich9_handle_access_perm(bm_rap, bm_wap, max, i);
 		if (ich_spi_rw_restricted)
 			msg_pinfo("Not all flash regions are freely accessible by flashprog. This is "
 				  "most likely\ndue to an active ME. Please see "
diff --git a/include/programmer.h b/include/programmer.h
index 873dc37..f82f11f 100644
--- a/include/programmer.h
+++ b/include/programmer.h
@@ -363,6 +363,10 @@
 	CHIPSET_APOLLO_LAKE,
 	CHIPSET_GEMINI_LAKE,
 	CHIPSET_ELKHART_LAKE,
+
+	CHIPSET_HAS_NEW_ACCESS_PERM, /****** BM_RAP/WAP regs from here on *****/
+
+	CHIPSET_C740_SERIES_EMMITSBURG = CHIPSET_HAS_NEW_ACCESS_PERM,
 };
 
 /* ichspi.c */
diff --git a/util/ich_descriptors_tool/ich_descriptors_tool.c b/util/ich_descriptors_tool/ich_descriptors_tool.c
index e6fa83a..36fd3e4 100644
--- a/util/ich_descriptors_tool/ich_descriptors_tool.c
+++ b/util/ich_descriptors_tool/ich_descriptors_tool.c
@@ -140,6 +140,7 @@
 "\t- \"500\" or \"tiger\" for Intel's 500 series chipsets.\n"
 "\t- \"600\" or \"alder\" for Intel's 600 series chipsets.\n"
 "\t- \"c620\" or \"lewis\" for Intel's C620 series aka. Lewisburg chipsets.\n"
+"\t- \"c740\" or \"emmits\" for Intel's C740 series chipsets.\n"
 "If '-d' is specified some regions such as the BIOS image as seen by the CPU or\n"
 "the GbE blob that is required to initialize the GbE are also dumped to files.\n",
 	argv[0], argv[0]);
@@ -248,6 +249,9 @@
 		else if ((strcmp(csn, "c620") == 0) ||
 			 (strcmp(csn, "lewis") == 0))
 			cs = CHIPSET_C620_SERIES_LEWISBURG;
+		else if ((strcmp(csn, "c740") == 0) ||
+			 (strcmp(csn, "emmits") == 0))
+			cs = CHIPSET_C740_SERIES_EMMITSBURG;
 	}
 
 	ret = read_ich_descriptors_from_dump(buf, len, &cs, &desc);