[flashrom] [PATCH 11/11] add support for Intel Hardware Sequencing

Stefan Tauner stefan.tauner at student.tuwien.ac.at
Sat May 28 05:38:48 CEST 2011


Signed-off-by: Stefan Tauner <stefan.tauner at student.tuwien.ac.at>
---
 chipdrivers.h |    6 ++
 flashchips.c  |   40 ++++++++-
 ichspi.c      |  280 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 programmer.h  |    1 +
 4 files changed, 316 insertions(+), 11 deletions(-)

diff --git a/chipdrivers.h b/chipdrivers.h
index 92ddbea..f97e2ad 100644
--- a/chipdrivers.h
+++ b/chipdrivers.h
@@ -84,6 +84,12 @@ void print_status_82802ab(uint8_t status);
 int unlock_82802ab(struct flashchip *flash);
 int unlock_28f004s5(struct flashchip *flash);
 
+/* ichspi.c */
+int ich_hwseq_probe(struct flashchip *flash);
+int ich_hwseq_read(struct flashchip *flash, uint8_t *buf, int start, int len);
+int ich_hwseq_block_erase(struct flashchip *flash, unsigned int addr, unsigned int blocklen);
+int ich_hwseq_write_256(struct flashchip *flash, uint8_t *buf, int start, int len);
+
 /* jedec.c */
 uint8_t oddparity(uint8_t val);
 void toggle_ready_jedec(chipaddr dst);
diff --git a/flashchips.c b/flashchips.c
index a1e3441..42391cd 100644
--- a/flashchips.c
+++ b/flashchips.c
@@ -8273,7 +8273,44 @@ const struct flashchip flashchips[] = {
 		.write		= write_jedec_1,
 		.read		= read_memmapped,
 	},
-
+#if defined(__i386__) || defined(__x86_64__)
+	{
+		.vendor		= "Intel",
+		.name		= "Hardware Sequencing",
+		.bustype	= CHIP_BUSTYPE_SPI,
+		.manufacture_id	= INTEL_ID,
+		.model_id	= GENERIC_DEVICE_ID,
+		.total_size	= 4,
+		.page_size	= 256,
+		.tested		= TEST_OK_PR,
+		.probe		= ich_hwseq_probe,
+		.block_erasers	=
+		{
+			{
+				.eraseblocks = { {256, 4 * 4} },
+				.block_erase = ich_hwseq_block_erase,
+			//}, {
+			//{
+				//.eraseblocks = { {256, CHIPSIZE * 4} },
+				//.block_erase = ich_hwseq_block_erase,
+			//}, {
+			//{
+				//.eraseblocks = { {4 * 1024, CHIPSIZE / 4} },
+				//.block_erase = ich_hwseq_block_erase,
+			//}, {
+			//{
+				//.eraseblocks = { {8 * 1024, CHIPSIZE / 8} },
+				//.block_erase = ich_hwseq_block_erase,
+			//}, {
+			//{
+				//.eraseblocks = { {64 * 1024, CHIPSIZE / 64} },
+				//.block_erase = ich_hwseq_block_erase,
+			}
+		},
+		.write		= ich_hwseq_write_256,
+		.read		= ich_hwseq_read,
+	},
+#endif // defined(__i386__) || defined(__x86_64__)
 	{
 		.vendor		= "AMIC",
 		.name		= "unknown AMIC SPI chip",
@@ -8406,6 +8443,7 @@ const struct flashchip flashchips[] = {
 		.probe		= probe_spi_rdid,
 		.write		= NULL,
 	},
+
 	{
 		.vendor		= "Generic",
 		.name		= "unknown SPI chip (REMS)",
diff --git a/ichspi.c b/ichspi.c
index 5bba0ab..8cdcb5c 100644
--- a/ichspi.c
+++ b/ichspi.c
@@ -1099,6 +1099,240 @@ static int ich_spi_send_command(unsigned int writecnt, unsigned int readcnt,
 	return result;
 }
 
+uint32_t ich_hwseq_get_erase_block_size(void)
+{
+	uint8_t enc_berase = (REGREAD16(ICH9_REG_HSFS) & HSFS_BERASE) >>
+				   HSFS_BERASE_OFF;
+	const uint32_t dec_berase[4] = {
+		256,
+		4 * 1024,
+		8 * 1024,
+		64 * 1024
+	};
+	return dec_berase[enc_berase];
+}
+
+int ich_hwseq_probe(struct flashchip *flash)
+{
+	extern struct flash_descriptor fdbar;
+	msg_cdbg("Prerequisites for Intel Hardware Sequencing are ");
+	if (spi_programmer->type == SPI_CONTROLLER_ICH_HWSEQ) {
+		msg_cdbg("met.\n");
+		msg_cdbg("Found %d attached SPI flash chips with a "
+			 "combined density of %d kB.\n", fdbar.NC + 1,
+			 (getFCBA_component_density(0) +
+			  getFCBA_component_density(1)) / 1024);
+		return 1;
+	} else {
+		msg_cdbg("not met.\n");
+		return 0;
+	}
+}
+
+static int ich_hwseq_send_command(unsigned int writecnt,
+				      unsigned int readcnt,
+				      const unsigned char *writearr,
+				      unsigned char *readarr)
+{
+	msg_pdbg("skipped. Intel Hardware Sequencing does not support sending "
+		 "arbitrary commands.");
+	return -1;
+}
+
+int ich_hwseq_block_erase(struct flashchip *flash,
+			  unsigned int addr,
+			  unsigned int len)
+{
+	uint32_t erase_block;
+	uint32_t hsfc;
+	uint32_t hsfs;
+	uint32_t timeout = 5000 * 1000; /* 5 s for max 64 kB */
+
+	REGWRITE32(ICH9_REG_FADDR, (addr & 0x00FFFFFF));
+	erase_block = ich_hwseq_get_erase_block_size();
+	if (strcmp(flash->name, "Hardware Sequencing") != 0) {
+		msg_perr("This chip (%s) is not supported in hardware"
+			 "sequencing mode\n", flash->name);
+		return -1;
+	}
+
+	if (len != erase_block) {
+		msg_cerr("Erase block size for address 0x%06x is %d B, "
+			 "but requested erase block size is %d B. "
+			 "Not erasing anything.\n",
+			 addr,
+			 erase_block,
+			 len);
+		return -1;
+	}
+
+	if (addr % erase_block != 0) {
+		msg_cerr("Erase address 0x%06x is not aligned to the erase "
+			 "block boundary (any multiple of %d). "
+			 "Not erasing anything.\n",
+			 addr,
+			 erase_block);
+		return -1;
+	}
+
+	msg_pdbg("erasing %d bytes starting at 0x%06x\n", len, addr);
+
+	hsfc = REGREAD16(ICH9_REG_HSFC);
+	/* clear FDONE, FCERR, AEL by writing 1 to them (if they are set) */
+	REGWRITE16(ICH9_REG_HSFC, hsfc);
+	hsfc &= ~HSFC_FCYCLE; /* clear operation */
+	hsfc = (0x11 << HSFC_FCYCLE_OFF); /* set erase operation */
+	hsfc |= HSFC_FGO; /* start */
+	REGWRITE16(ICH9_REG_HSFC, hsfc);
+
+	/* Wait for Cycle Done Status or Flash Cycle Error. */
+	while (((REGREAD16(ICH9_REG_HSFS) &
+		(HSFS_FDONE | HSFS_FCERR)) == 0) &&
+	       --timeout) {
+		programmer_delay(10);
+	}
+	if (!timeout) {
+		msg_perr("timeout, ICH9_REG_HSFS=0x%04x\n",
+			 REGREAD16(ICH9_REG_HSFS));
+		return 1;
+	}
+
+	hsfs = REGREAD16(ICH9_REG_HSFS);
+	REGWRITE16(ICH9_REG_HSFS, hsfs); /* clear all error bits */
+	if (hsfs & HSFS_FCERR) {
+		msg_perr("Transaction error between offset 0x%06x and 0x%06x + "
+			 "%d (= 0x%06x)!\n",
+			 addr, addr, len, addr+len);
+		prettyprint_ich9_reg_hsfs(hsfs);
+		prettyprint_ich9_reg_hsfc(hsfs);
+		return 1;
+	}
+	return 0;
+}
+
+int ich_hwseq_read(struct flashchip *flash, uint8_t *buf, int addr, int len)
+{
+	uint32_t *buf32 = (uint32_t *)buf;
+	uint16_t hsfc = REGREAD16(ICH9_REG_HSFC);
+	uint16_t hsfs;
+	uint16_t timeout = 100 * 60;
+	int i;
+
+	if (strcmp(flash->name, "Hardware Sequencing") != 0) {
+		msg_perr("This chip (%s) is not supported in hardware"
+			 "sequencing mode\n", flash->name);
+		return -1;
+	}
+
+	if (len % 4 != 0) {
+		msg_perr("Read size has to be a multiple of 4 in hardware"
+			 "sequencing mode\n");
+		return -1;
+	}
+
+	msg_pdbg("reading %d bytes starting at 0x%06x\n", len, addr);
+	/* clear FDONE, FCERR, AEL by writing 1 to them (if they are set) */
+	REGWRITE16(ICH9_REG_HSFC, hsfc);
+
+	for (i = 0; i < len; i += 4) {
+		REGWRITE32(ICH9_REG_FADDR, (addr & 0x00FFFFFF));
+		hsfc = REGREAD16(ICH9_REG_HSFC);
+		hsfc &= ~HSFC_FCYCLE; /* set read operation */
+		hsfc &= ~HSFC_FDBC; /* clear byte count */
+		hsfc |= (3 << HSFC_FDBC_OFF); /* set byte count to 3+1 */
+		hsfc |= HSFC_FGO; /* start */
+		REGWRITE16(ICH9_REG_HSFC, hsfc);
+
+		/* Wait for Cycle Done Status or Flash Cycle Error. */
+		while (((REGREAD16(ICH9_REG_HSFS) &
+		        (HSFS_FDONE | HSFS_FCERR)) == 0) &&
+		       --timeout) {
+			programmer_delay(10);
+		}
+		if (!timeout) {
+			msg_perr("timeout, ICH9_REG_HSFS=0x%04x\n",
+				 REGREAD16(ICH9_REG_HSFS));
+			return 1;
+		}
+
+		hsfs = REGREAD16(ICH9_REG_HSFS);
+		REGWRITE16(ICH9_REG_HSFS, hsfs); /* clear all error bits */
+		if (hsfs & HSFS_FCERR) {
+			msg_perr("Transaction error between offset 0x%06x and "
+				 "0x%06x + %d (= 0x%06x)!\n",
+				 addr, addr, 4, addr + 4);
+			prettyprint_ich9_reg_hsfs(hsfs);
+			prettyprint_ich9_reg_hsfc(hsfs);
+			return 1;
+		}
+
+		*buf32++ = REGREAD32(ICH9_REG_FDATA0);
+		addr += 4;
+	}
+	return 0;
+}
+
+int ich_hwseq_write_256(struct flashchip *flash, uint8_t *buf, int addr, int len)
+{
+	uint32_t *buf32 = (uint32_t *)buf;
+	uint16_t hsfc = REGREAD16(ICH9_REG_HSFC);
+	uint16_t hsfs;
+	uint16_t timeout = 100 * 60;
+	int i;
+	if (strcmp(flash->name, "Hardware Sequencing") != 0) {
+		msg_perr("This chip (%s) is not supported in hardware"
+			 "sequencing mode\n", flash->name);
+		return -1;
+	}
+
+	if ((len % 4 != 0)) {
+		msg_perr("Write size has to be a multiple of 4 in hardware "
+			 "sequencing mode\n");
+		return -1;
+	}
+
+	msg_pdbg("writing %d bytes starting at 0x%06x\n", len, addr);
+	/* clear FDONE, FCERR, AEL by writing 1 to them (if they are set) */
+	REGWRITE16(ICH9_REG_HSFC, hsfc);
+
+	for (i = 0; i < len; i += 4) {
+		REGWRITE32(ICH9_REG_FADDR, (addr & 0x00FFFFFF));
+		REGWRITE32(ICH9_REG_FDATA0, *buf32++);
+		hsfc = REGREAD16(ICH9_REG_HSFC);
+		hsfc &= ~HSFC_FCYCLE; /* clear operation */
+		hsfc = (0x10 << HSFC_FCYCLE_OFF); /* set write operation */
+		hsfc &= ~HSFC_FDBC; /* clear byte count */
+		hsfc |= (3 << HSFC_FDBC_OFF); /* set byte count to 3+1 */
+		hsfc |= HSFC_FGO; /* start */
+		REGWRITE16(ICH9_REG_HSFC, hsfc);
+
+		/* Wait for Cycle Done Status or Flash Cycle Error. */
+		while (((REGREAD16(ICH9_REG_HSFS) &
+		        (HSFS_FDONE | HSFS_FCERR)) == 0) &&
+		       --timeout) {
+			programmer_delay(10);
+		}
+		if (!timeout) {
+			msg_perr("timeout, ICH9_REG_HSFS=0x%04x\n",
+				 REGREAD16(ICH9_REG_HSFS));
+			return 1;
+		}
+
+		hsfs = REGREAD16(ICH9_REG_HSFS);
+		REGWRITE16(ICH9_REG_HSFS, hsfs); /* clear all error bits */
+		if (hsfs & HSFS_FCERR) {
+			msg_perr("Transaction error between offset 0x%06x and "
+				 "0x%06x + %d (= 0x%06x)!\n",
+				 addr, addr, 4, addr + 4);
+			prettyprint_ich9_reg_hsfs(hsfs);
+			prettyprint_ich9_reg_hsfc(hsfs);
+			return 1;
+		}
+		addr += 4;
+	}
+	return 0;
+}
+
 static int ich_spi_send_multicommand(struct spi_command *cmds)
 {
 	int ret = 0;
@@ -1217,6 +1451,16 @@ static const struct spi_programmer spi_programmer_ich9 = {
 	.write_256 = default_spi_write_256,
 };
 
+static const struct spi_programmer spi_programmer_ich_hwseq = {
+	.type = SPI_CONTROLLER_ICH_HWSEQ,
+	.max_data_read = 64,
+	.max_data_write = 64,
+	.command = ich_hwseq_send_command,
+	.multicommand = default_spi_send_multicommand,
+	.read = ich_hwseq_read,
+	.write_256 = ich_hwseq_write_256,
+};
+
 int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb,
 			int ich_generation)
 {
@@ -1225,20 +1469,21 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb,
 	uint16_t spibar_offset, tmp2;
 	uint32_t tmp;
 	int ichspi_desc = 0;
+	/* used for hw sequencing detection */
+	extern struct flash_descriptor fdbar;
+	uint32_t size_low;
+	uint32_t size_high;
 
 	switch (ich_generation) {
 	case 7:
-		register_spi_programmer(&spi_programmer_ich7);
 		spibar_offset = 0x3020;
 		break;
 	case 8:
-		register_spi_programmer(&spi_programmer_ich9);
 		spibar_offset = 0x3020;
 		break;
 	case 9:
 	case 10:
 	default:		/* Future version might behave the same */
-		register_spi_programmer(&spi_programmer_ich9);
 		spibar_offset = 0x3800;
 		break;
 	}
@@ -1249,8 +1494,8 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb,
 	/* Assign Virtual Address */
 	ich_spibar = rcrb + spibar_offset;
 
-	switch (spi_programmer->type) {
-	case SPI_CONTROLLER_ICH7:
+	switch (ich_generation) {
+	case 7:
 		msg_pdbg("0x00: 0x%04x     (SPIS)\n",
 			     mmio_readw(ich_spibar + 0));
 		msg_pdbg("0x02: 0x%04x     (SPIC)\n",
@@ -1286,9 +1531,13 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb,
 			msg_pinfo("WARNING: SPI Configuration Lockdown activated.\n");
 			ichspi_lock = 1;
 		}
+		register_spi_programmer(&spi_programmer_ich7);
 		ich_init_opcodes();
 		break;
-	case SPI_CONTROLLER_ICH9:
+	case 8:
+	case 9:
+	case 10:
+	default:		/* Future version might behave the same */
 		tmp2 = mmio_readw(ich_spibar + ICH9_REG_HSFS);
 		msg_pdbg("0x04: 0x%04x (HSFS)\n", tmp2);
 		prettyprint_ich9_reg_hsfs(tmp2);
@@ -1365,10 +1614,21 @@ int ich_init_spi(struct pci_dev *dev, uint32_t base, void *rcrb,
 			read_ich_descriptors_from_fdo(ich_spibar);
 			pretty_print_ich_descriptors();
 		}
-		ich_init_opcodes();
-		break;
-	default:
-		/* Nothing */
+
+		/* Fetch erase block sizes for addresses below and above FPBA
+		 * and check if erase blocks are equal for all addresses. */
+		REGWRITE32(ICH9_REG_FADDR, (0 & 0x00FFFFFF));
+		size_low = ich_hwseq_get_erase_block_size();
+		REGWRITE32(ICH9_REG_FADDR,
+			   (REGREAD32(ICH9_REG_FPB) & FPB_FPBA) & 0x00FFFFFF);
+		size_high = ich_hwseq_get_erase_block_size();
+
+		if ((ichspi_lock || fdbar.NC != 0) && (size_low == size_high))
+			register_spi_programmer(&spi_programmer_ich_hwseq);
+		else {
+			register_spi_programmer(&spi_programmer_ich9);
+			ich_init_opcodes();
+		}
 		break;
 	}
 
diff --git a/programmer.h b/programmer.h
index e1147f1..8ccaf0b 100644
--- a/programmer.h
+++ b/programmer.h
@@ -538,6 +538,7 @@ enum spi_controller {
 #if defined(__i386__) || defined(__x86_64__)
 	SPI_CONTROLLER_ICH7,
 	SPI_CONTROLLER_ICH9,
+	SPI_CONTROLLER_ICH_HWSEQ,
 	SPI_CONTROLLER_IT85XX,
 	SPI_CONTROLLER_IT87XX,
 	SPI_CONTROLLER_SB600,
-- 
1.7.1





More information about the flashrom mailing list