[flashrom] [PATCH 4/6] sbxxx: Add support for new AMD SPI controller.

Stefan Tauner stefan.tauner at student.tuwien.ac.at
Sat Aug 10 03:45:55 CEST 2013


This patch adds support for the "SPI 100" SPI engine in Yangtze FCHs
(found in Kabini and Temash).

Tested reading/writing on ASRock IMB-A180.

Signed-off-by: Wei Hu <wei at aristanetworks.com>
Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006 at gmx.net>
Signed-off-by: Stefan Tauner <stefan.tauner at student.tuwien.ac.at>
---
 programmer.h |   1 +
 sb600spi.c   | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 129 insertions(+), 10 deletions(-)

diff --git a/programmer.h b/programmer.h
index f03eac3..0845d61 100644
--- a/programmer.h
+++ b/programmer.h
@@ -494,6 +494,7 @@ enum spi_controller {
 	SPI_CONTROLLER_IT85XX,
 	SPI_CONTROLLER_IT87XX,
 	SPI_CONTROLLER_SB600,
+	SPI_CONTROLLER_YANGTZE,
 	SPI_CONTROLLER_VIA,
 	SPI_CONTROLLER_WBSIO,
 #endif
diff --git a/sb600spi.c b/sb600spi.c
index ff7dbee..eaafb48 100644
--- a/sb600spi.c
+++ b/sb600spi.c
@@ -4,7 +4,8 @@
  * Copyright (C) 2008 Wang Qingpei <Qingpei.Wang at amd.com>
  * Copyright (C) 2008 Joe Bao <Zheng.Bao at amd.com>
  * Copyright (C) 2008 Advanced Micro Devices, Inc.
- * Copyright (C) 2009, 2010 Carl-Daniel Hailfinger
+ * Copyright (C) 2009, 2010, 2013 Carl-Daniel Hailfinger
+ * Copyright (C) 2013 Stefan Tauner
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -55,9 +56,12 @@ enum amd_chipset {
 static enum amd_chipset amd_gen = CHIPSET_AMD_UNKNOWN;
 
 #define FIFO_SIZE_OLD		8
+#define FIFO_SIZE_YANGTZE	71
 
 static int sb600_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
 				  const unsigned char *writearr, unsigned char *readarr);
+static int spi100_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+				  const unsigned char *writearr, unsigned char *readarr);
 
 static struct spi_programmer spi_programmer_sb600 = {
 	.type = SPI_CONTROLLER_SB600,
@@ -70,6 +74,17 @@ static struct spi_programmer spi_programmer_sb600 = {
 	.write_aai = default_spi_write_aai,
 };
 
+static struct spi_programmer spi_programmer_yangtze = {
+	.type = SPI_CONTROLLER_YANGTZE,
+	.max_data_read = FIFO_SIZE_YANGTZE - 3, /* Apparently the big SPI 100 buffer is not a ring buffer. */
+	.max_data_write = FIFO_SIZE_YANGTZE - 3,
+	.command = spi100_spi_send_command,
+	.multicommand = default_spi_send_multicommand,
+	.read = default_spi_read,
+	.write_256 = default_spi_write_256,
+	.write_aai = default_spi_write_aai,
+};
+
 static void determine_generation(struct pci_dev *dev)
 {
 	amd_gen = CHIPSET_AMD_UNKNOWN;
@@ -276,6 +291,45 @@ static int sb600_spi_send_command(struct flashctx *flash, unsigned int writecnt,
 	return 0;
 }
 
+static int spi100_spi_send_command(struct flashctx *flash, unsigned int writecnt,
+				  unsigned int readcnt,
+				  const unsigned char *writearr,
+				  unsigned char *readarr)
+{
+	/* First byte is cmd which can not be sent through the buffer. */
+	unsigned char cmd = *writearr++;
+	writecnt--;
+	msg_pspew("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, cmd, writecnt, readcnt);
+	mmio_writeb(cmd, sb600_spibar + 0);
+
+	int ret = check_readwritecnt(flash, writecnt, readcnt);
+	if (ret != 0)
+		return ret;
+
+	/* Use the extended TxByteCount and RxByteCount registers. */
+	mmio_writeb(writecnt, sb600_spibar + 0x48);
+	mmio_writeb(readcnt, sb600_spibar + 0x4b);
+
+	msg_pspew("Filling buffer: ");
+	int count;
+	for (count = 0; count < writecnt; count++) {
+		msg_pspew("[%02x]", writearr[count]);
+		mmio_writeb(writearr[count], sb600_spibar + 0x80 + count);
+	}
+	msg_pspew("\n");
+
+	execute_command();
+
+	msg_pspew("Reading buffer: ");
+	for (count = 0; count < readcnt; count++) {
+		readarr[count] = mmio_readb(sb600_spibar + 0x80 + (writecnt + count) % FIFO_SIZE_YANGTZE);
+		msg_pspew("[%02x]", readarr[count]);
+	}
+	msg_pspew("\n");
+
+	return 0;
+}
+
 struct spispeed {
 	const char *const name;
 	const int8_t speed;
@@ -286,6 +340,10 @@ static const struct spispeed spispeeds[] = {
 	{ "33 MHz",	0x01 },
 	{ "22 MHz",	0x02 },
 	{ "16.5 MHz",	0x03 },
+	{ "100 MHz",	0x04 },
+	{ "Reserved",	0x05 },
+	{ "Reserved",	0x06 },
+	{ "800 kHz",	0x07 },
 };
 
 static int set_speed(struct pci_dev *dev, const struct spispeed *spispeed)
@@ -294,7 +352,12 @@ static int set_speed(struct pci_dev *dev, const struct spispeed *spispeed)
 	uint8_t speed = spispeed->speed;
 
 	msg_pdbg("Setting SPI clock to %s (0x%x).\n", spispeed->name, speed);
-	if (amd_gen != CHIPSET_YANGTZE) {
+	if (amd_gen >= CHIPSET_YANGTZE) {
+		rmmio_writew((speed << 12) | (speed << 8) | (speed << 4) | speed, sb600_spibar + 0x22);
+		uint16_t tmp = mmio_readw(sb600_spibar + 0x22);
+		success = (((tmp >> 12) & 0xf) == speed && ((tmp >> 8) & 0xf) == speed &&
+			   ((tmp >> 4) & 0xf) == speed && ((tmp >> 0) & 0xf) == speed);
+	} else {
 		rmmio_writeb((mmio_readb(sb600_spibar + 0xd) & ~(0x3 << 4)) | (speed << 4), sb600_spibar + 0xd);
 		success = (speed == ((mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3));
 	}
@@ -306,6 +369,17 @@ static int set_speed(struct pci_dev *dev, const struct spispeed *spispeed)
 	return 0;
 }
 
+static int set_mode(struct pci_dev *dev, uint8_t read_mode)
+{
+	uint32_t tmp = mmio_readl(sb600_spibar + 0x00);
+	tmp &= ~(0x6 << 28 | 0x1 << 18); /* Clear mode bits */
+	tmp |= ((read_mode & 0x6) << 28) | ((read_mode & 0x1) << 18);
+	rmmio_writel(tmp, sb600_spibar + 0x00);
+	if (tmp != mmio_readl(sb600_spibar + 0x00))
+		return 1;
+	return 0;
+}
+
 static int handle_speed(struct pci_dev *dev)
 {
 	uint32_t tmp;
@@ -315,7 +389,47 @@ static int handle_speed(struct pci_dev *dev)
 	 * 18    rsvd  <-          fastReadEnable  ?    <-       ?          SpiReadMode[0]
 	 * 29:30 rsvd  <-          <-              ?    <-       ?          SpiReadMode[2:1]
 	 */
-	if (amd_gen != CHIPSET_YANGTZE) {
+	if (amd_gen >= CHIPSET_YANGTZE) {
+		tmp = mmio_readb(sb600_spibar + 0x20);
+		msg_pdbg("UseSpi100 is %sabled\n", (tmp & 0x1) ? "en" : "dis");
+		if ((tmp & 0x1) == 0) {
+			rmmio_writeb(tmp | 0x1, sb600_spibar + 0x20);
+			tmp = mmio_readb(sb600_spibar + 0x20) & 0x1;
+			if (tmp == 0) {
+				msg_perr("Enabling Spi100 failed.\n");
+				return 1;
+			}
+			msg_pdbg("Enabling Spi100 succeeded.\n");
+		}
+
+		static const char *spireadmodes[] = {
+			"Normal (up to 33 MHz)", /* 0 */
+			"Reserved",		 /* 1 */
+			"Dual IO (1-1-2)",	 /* 2 */
+			"Quad IO (1-1-4)",	 /* 3 */
+			"Dual IO (1-2-2)",	 /* 4 */
+			"Quad IO (1-4-4)",	 /* 5 */
+			"Normal (up to 66 MHz)", /* 6 */
+			"Fast Read",		 /* 7 */
+		};
+		tmp = mmio_readl(sb600_spibar + 0x00);
+		uint8_t read_mode = ((tmp >> 28) & 0x6) | ((tmp >> 18) & 0x1);
+		msg_pdbg("SpiReadMode=%s (%i)\n", spireadmodes[read_mode], read_mode);
+		if (read_mode != 6) {
+			read_mode = 6; /* Default to "Normal (up to 66 MHz)" */
+			if (set_mode(dev, read_mode) != 0) {
+				msg_perr("Setting read mode to \"%s\" failed.\n", spireadmodes[read_mode]);
+				return 1;
+			}
+			msg_pdbg("Setting read mode to \"%s\" succeeded.\n", spireadmodes[read_mode]);
+		}
+
+		tmp = mmio_readw(sb600_spibar + 0x22); /* SPI 100 Speed Config */
+		msg_pdbg("NormSpeedNew is %s\n", spispeeds[(tmp >> 12) & 0xf].name);
+		msg_pdbg("FastSpeedNew is %s\n", spispeeds[(tmp >> 8) & 0xf].name);
+		msg_pdbg("AltSpeedNew is %s\n", spispeeds[(tmp >> 4) & 0xf].name);
+		msg_pdbg("TpmSpeedNew is %s\n", spispeeds[(tmp >> 0) & 0xf].name);
+	} else {
 		if (amd_gen >= CHIPSET_SB89XX && amd_gen <= CHIPSET_HUDSON234) {
 			bool fast_read = (mmio_readl(sb600_spibar + 0x00) >> 18) & 0x1;
 			msg_pdbg("Fast Reads are %sabled\n", fast_read ? "en" : "dis");
@@ -411,12 +525,6 @@ int sb600_probe_spi(struct pci_dev *dev)
 		amd_gen = CHIPSET_SB6XX;
 	}
 
-	if (amd_gen == CHIPSET_YANGTZE) {
-		msg_perr("SPI on Kabini/Temash and newer chipsets are not yet supported.\n"
-			 "Please try a newer version of flashrom.\n");
-		return ERROR_NONFATAL;
-	}
-
 	/* Chipset support matrix for SPI Base_Addr (LPC PCI reg 0xa0)
 	 * bit   6xx         7xx/SP5100         8xx       9xx  hudson1  hudson2+  yangtze
 	 * 3    rsvd         <-                 <-        ?    <-       ?         RouteTpm2Spi
@@ -431,6 +539,8 @@ int sb600_probe_spi(struct pci_dev *dev)
 		msg_pdbg("SpiRomEnable=%i", (tmp >> 1) & 0x1);
 		if (amd_gen == CHIPSET_SB7XX)
 			msg_pdbg(", AltSpiCSEnable=%i, AbortEnable=%i", tmp & 0x1, (tmp >> 2) & 0x1);
+		else if (amd_gen == CHIPSET_YANGTZE)
+			msg_pdbg(", RouteTpm2Sp=%i", (tmp >> 3) & 0x1);
 
 		tmp = pci_read_byte(dev, 0xba);
 		msg_pdbg(", PrefetchEnSPIFromIMC=%i", (tmp & 0x4) >> 2);
@@ -464,6 +574,8 @@ int sb600_probe_spi(struct pci_dev *dev)
 	 */
 	tmp = mmio_readl(sb600_spibar + 0x00);
 	msg_pdbg("(0x%08" PRIx32 ") SpiArbEnable=%i", tmp, (tmp >> 19) & 0x1);
+	if (amd_gen == CHIPSET_YANGTZE)
+		msg_pdbg(", IllegalAccess=%i", (tmp >> 21) & 0x1);
 
 	msg_pdbg(", SpiAccessMacRomEn=%i, SpiHostAccessRomEn=%i, ArbWaitCount=%i",
 		 (tmp >> 22) & 0x1, (tmp >> 23) & 0x1, (tmp >> 24) & 0x7);
@@ -476,6 +588,7 @@ int sb600_probe_spi(struct pci_dev *dev)
 		msg_pdbg(", DropOneClkOnRd/SpiClkGate=%i", (tmp >> 28) & 0x1);
 	case CHIPSET_SB89XX:
 	case CHIPSET_HUDSON234:
+	case CHIPSET_YANGTZE:
 		msg_pdbg(", SpiBusy=%i", (tmp >> 31) & 0x1);
 	default: break;
 	}
@@ -489,6 +602,7 @@ int sb600_probe_spi(struct pci_dev *dev)
 	if (amd_gen >= CHIPSET_SB89XX) {
 		tmp = mmio_readb(sb600_spibar + 0x1D);
 		msg_pdbg("Using SPI_CS%d\n", tmp & 0x3);
+		/* FIXME: Handle SpiProtect* configuration on Yangtze. */
 	}
 
 	/* Look for the SMBus device. */
@@ -536,7 +650,11 @@ int sb600_probe_spi(struct pci_dev *dev)
 	if (handle_imc(dev) != 0)
 		return ERROR_FATAL;
 
-	register_spi_programmer(&spi_programmer_sb600);
+	/* Starting with Yangtze the SPI controller got a bigger FIFO */
+	if (amd_gen != CHIPSET_YANGTZE)
+		register_spi_programmer(&spi_programmer_sb600);
+	else
+		register_spi_programmer(&spi_programmer_yangtze);
 	return 0;
 }
 
-- 
Kind regards, Stefan Tauner





More information about the flashrom mailing list