Patchwork Add Nvidia nForce MCP6x/MCP7x series SPI flashing support

login
register
about
Submitter Carl-Daniel Hailfinger
Date 2010-07-28 01:13:42
Message ID <4C4F8446.4060801@gmx.net>
Download mbox | patch
Permalink /patch/1692/
State Accepted
Commit r1113
Headers show

Comments

Carl-Daniel Hailfinger - 2010-07-28 01:13:42
Latest version.

Add Nvidia nForce MCP61/MCP65/MCP67/MCP78S/MCP73/MCP79 SPI flashing support.

Huge thanks go to Michael Karcher for reverse engineering the interface
and to Johannes Sjölund for testing the first iterations of my patch on
his hardware until it worked.

Thanks to the following testers of the patch:
* MCP61, 10de:03e0, LPC OK (valid SPIBAR), ECS Geforce6100SM-M, Andrew
Cleveland
* MCP61, 10de:03e0, LPC OK (valid SPIBAR), Biostar NF520-A2 NF61D-A2,
Vitaliy Buchynskyy
* MCP65, 10de:0441, SPI OK, MSI MS-7369 K9N Neo-F v2, Kjell Braden
* MCP65, 10de:0441, SPI OK, MSI MS-7369, Wolfgang Schnitker
* MCP65, 10de:0441, SPI OK, MSI MS-7369, Johannes Sjölund
* MCP65, 10de:0441, SPI OK, MSI MS-7369, Melchior Franz
* MCP78S, 10de:075c, SPI OK, Asus M3N78 PRO, Brad Rogers
* MCP78S, 10de:075c, SPI OK, Asus M3N78-VM, Marcel Partap
* MCP78S, 10de:075c, SPI OK, Asus M4N78 PRO, Kimmo Vuorinen
* MCP78S, 10de:075c, SPI OK, Asus M4N78 PRO, Vikram Ambrose
* MCP79, 10de:0aad, SPI OK, Acer Aspire R3600, Andrew Morgan
* MCP79, 10de:0aae, LPC ??, Lenovo Ideapad S12 laptop, Christian Schmitt
* MCP79, 10de:0aae, SPI OK, Apple iMac9,1 Mac-F2218EA9, David "dledson"

flashrom will refuse to write/erase for safety reasons if MCP6x/MCP7x
SPI is detected.

Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>
Uwe Hermann - 2010-07-28 10:36:41
On Wed, Jul 28, 2010 at 03:13:42AM +0200, Carl-Daniel Hailfinger wrote:
> Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>

Acked-by: Uwe Hermann <uwe@hermann-uwe.de>

Looks good, compile-tested and reviewed, also tested on hardware by
various people.

 
> +static void mcp6x_request_spibus(void)
> +{
> +	uint8_t byte;

You use "byte" in most functions as variable name, but IIRC there were
issues on some systems/compilers with this name. I recommend renaming to
reg8 or tmp8 or the like.


Uwe.
Carl-Daniel Hailfinger - 2010-07-28 15:10:37
On 28.07.2010 12:36, Uwe Hermann wrote:
> On Wed, Jul 28, 2010 at 03:13:42AM +0200, Carl-Daniel Hailfinger wrote:
>   
>> Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>
>>     
>
> Acked-by: Uwe Hermann <uwe@hermann-uwe.de>
>
> Looks good, compile-tested and reviewed, also tested on hardware by
> various people.
>
>  
>   
>> +static void mcp6x_request_spibus(void)
>> +{
>> +	uint8_t byte;
>>     
>
> You use "byte" in most functions as variable name, but IIRC there were
> issues on some systems/compilers with this name. I recommend renaming to
> reg8 or tmp8 or the like.
>   

Thanks for the review.
Renamed the identifier and committed in r1113.

Regards,
Carl-Daniel
Melchior FRANZ - 2010-09-27 18:43:20
* Carl-Daniel Hailfinger -- Wednesday 28 July 2010:
> Add Nvidia nForce MCP61/MCP65/MCP67/MCP78S/MCP73/MCP79 SPI flashing support.

JFTR: On the motherboard which I originally reported ("NVIDIA MCP65,
MSI MS-7369  "K9N Neo V2"  v1.0  (2007)"), reading and verifying the
BIOS was successful using r1182, which is HEAD as of today.

m.




# flashrom -Vr /tmp/bios
flashrom v0.9.2-r1182 on Linux 2.6.35.5 (x86_64), built with libpci 3.1.7, GCC 4.5.0 20100604 [gcc-4_5-branch revision 160292], little endian
flashrom is free software, get the source code at http://www.flashrom.org

Calibrating delay loop... OS timer resolution is 1 usecs, 1378M loops per second, 10 myus = 11 us, 100 myus = 100 us, 1000 myus = 996 us, 10000 myus = 10214 us, 4 myus = 4 us, OK.
Initializing internal programmer
No coreboot table found.
DMI string system-manufacturer: "MSI"
DMI string system-product-name: "MS-7369"
DMI string system-version: "1.0"
DMI string baseboard-manufacturer: "MSI"
DMI string baseboard-product-name: "MS-7369"
DMI string baseboard-version: "1.0"
DMI string chassis-type: "Desktop"
Found chipset "NVIDIA MCP65", enabling flash write... chipset PCI ID is 10de:0441, This chipset is not really supported yet. Guesswork...
ISA/LPC bridge reg 0x8a contents: 0x40, bit 6 is 1, bit 5 is 0
Flash bus type is SPI
SPI on this chipset is WIP. Write is unsupported!
Found SMBus device 10de:0446 at 00:01:1
MCP SPI BAR is at 0xfec80000
Mapping NVIDIA MCP6x SPI at 0xfec80000, unaligned size 0x544.
SPI control is 0x0002, req=0, gnt=0
Please send the output of "flashrom -V" to flashrom@flashrom.org to help us finish support for your chipset. Thanks.
OK.
This chipset supports the following protocols: SPI.
[...]
Probing for Winbond W25x80, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3014
Chip status register is 00
Found chip "Winbond W25x80" (1024 KB, SPI) at physical address 0xfff00000.
[...]



# flashrom -Vv /tmp/bios
[...]
Flash image seems to be a legacy BIOS. Disabling checks.
Verifying flash... VERIFIED.

Patch

Index: flashrom-bitbang_spi_nvidia_mcp/Makefile
===================================================================
--- flashrom-bitbang_spi_nvidia_mcp/Makefile	(Revision 1112)
+++ flashrom-bitbang_spi_nvidia_mcp/Makefile	(Arbeitskopie)
@@ -118,8 +118,12 @@ 
 ifeq ($(CONFIG_RAYER_SPI), yes)
 override CONFIG_BITBANG_SPI = yes
 else
+ifeq ($(CONFIG_INTERNAL), yes)
+override CONFIG_BITBANG_SPI = yes
+else
 CONFIG_BITBANG_SPI ?= no
 endif
+endif
 
 # Always enable 3Com NICs for now.
 CONFIG_NIC3COM ?= yes
@@ -162,7 +166,7 @@ 
 FEATURE_CFLAGS += -D'CONFIG_INTERNAL=1'
 PROGRAMMER_OBJS += processor_enable.o chipset_enable.o board_enable.o cbtable.o dmi.o internal.o
 # FIXME: The PROGRAMMER_OBJS below should only be included on x86.
-PROGRAMMER_OBJS += it87spi.o ichspi.o sb600spi.o wbsio_spi.o
+PROGRAMMER_OBJS += it87spi.o ichspi.o sb600spi.o wbsio_spi.o mcp6x_spi.o
 NEED_PCI := yes
 endif
 
Index: flashrom-bitbang_spi_nvidia_mcp/spi.c
===================================================================
--- flashrom-bitbang_spi_nvidia_mcp/spi.c	(Revision 1112)
+++ flashrom-bitbang_spi_nvidia_mcp/spi.c	(Arbeitskopie)
@@ -83,6 +83,13 @@ 
 		.read = wbsio_spi_read,
 		.write_256 = spi_chip_write_1_new,
 	},
+
+	{ /* SPI_CONTROLLER_MCP6X_BITBANG */
+		.command = bitbang_spi_send_command,
+		.multicommand = default_spi_send_multicommand,
+		.read = bitbang_spi_read,
+		.write_256 = bitbang_spi_write_256,
+	},
 #endif
 #endif
 
Index: flashrom-bitbang_spi_nvidia_mcp/chipset_enable.c
===================================================================
--- flashrom-bitbang_spi_nvidia_mcp/chipset_enable.c	(Revision 1112)
+++ flashrom-bitbang_spi_nvidia_mcp/chipset_enable.c	(Arbeitskopie)
@@ -868,18 +868,16 @@ 
 	return 0;
 }
 
-/* This is a shot in the dark. Even if the code is totally bogus for some
- * chipsets, users will at least start to send in reports.
+/**
+ * The MCP6x/MCP7x code is based on cleanroom reverse engineering.
+ * It is assumed that LPC chips need the MCP55 code and SPI chips need the
+ * code provided in enable_flash_mcp6x_7x_common.
  */
-static int enable_flash_mcp6x_7x_common(struct pci_dev *dev, const char *name)
+static int enable_flash_mcp6x_7x(struct pci_dev *dev, const char *name)
 {
 	int ret = 0;
+	int want_spi = 0;
 	uint8_t val;
-	uint16_t status;
-	char *busname;
-	uint32_t mcp_spibaraddr;
-	void *mcp_spibar;
-	struct pci_dev *smbusdev;
 
 	msg_pinfo("This chipset is not really supported yet. Guesswork...\n");
 
@@ -887,20 +885,31 @@ 
 	val = pci_read_byte(dev, 0x8a);
 	msg_pdbg("ISA/LPC bridge reg 0x8a contents: 0x%02x, bit 6 is %i, bit 5 "
 		 "is %i\n", val, (val >> 6) & 0x1, (val >> 5) & 0x1);
+
 	switch ((val >> 5) & 0x3) {
 	case 0x0:
+		ret = enable_flash_mcp55(dev, name);
 		buses_supported = CHIP_BUSTYPE_LPC;
+		msg_pdbg("Flash bus type is LPC\n");
 		break;
 	case 0x2:
-		buses_supported = CHIP_BUSTYPE_SPI;
+		want_spi = 1;
+		/* SPI is added in mcp6x_spi_init if it works.
+		 * Do we really want to disable LPC in this case?
+		 */
+		buses_supported = CHIP_BUSTYPE_NONE;
+		msg_pdbg("Flash bus type is SPI\n");
+		msg_perr("SPI on this chipset is WIP. Write is unsupported!\n");
+		programmer_may_write = 0;
 		break;
 	default:
-		buses_supported = CHIP_BUSTYPE_UNKNOWN;
+		/* Should not happen. */
+		buses_supported = CHIP_BUSTYPE_NONE;
+		msg_pdbg("Flash bus type is unknown (none)\n");
+		msg_pinfo("Something went wrong with bus type detection.\n");
+		goto out_msg;
 		break;
 	}
-	busname = flashbuses_to_text(buses_supported);
-	msg_pdbg("Guessed flash bus type is %s\n", busname);
-	free(busname);
 
 	/* Force enable SPI and disable LPC? Not a good idea. */
 #if 0
@@ -909,62 +918,8 @@ 
 	pci_write_byte(dev, 0x8a, val);
 #endif
 
-	/* Look for the SMBus device (SMBus PCI class) */
-	smbusdev = pci_dev_find_vendorclass(0x10de, 0x0c05);
-	if (!smbusdev) {
-		if (buses_supported & CHIP_BUSTYPE_SPI) {
-			msg_perr("ERROR: SMBus device not found. Not enabling "
-				 "SPI.\n");
-			buses_supported &= ~CHIP_BUSTYPE_SPI;
-			ret = 1;
-		} else {
-			msg_pinfo("Odd. SMBus device not found.\n");
-		}
-		goto out_msg;
-	}
-	msg_pdbg("Found SMBus device %04x:%04x at %02x:%02x:%01x\n",
-		smbusdev->vendor_id, smbusdev->device_id,
-		smbusdev->bus, smbusdev->dev, smbusdev->func);
-
-	/* Locate the BAR where the SPI interface lives. */
-	mcp_spibaraddr = pci_read_long(smbusdev, 0x74);
-	msg_pdbg("SPI BAR is at 0x%08x, ", mcp_spibaraddr);
-	/* We hope this has native alignment. We know the SPI interface (well,
-	 * a set of GPIOs that is connected to SPI flash) is at offset 0x530,
-	 * so we expect a size of at least 0x800. Clear the lower bits.
-	 * It is entirely possible that the BAR is 64k big and the low bits are
-	 * reserved for an entirely different purpose.
-	 */
-	mcp_spibaraddr &= ~0x7ff;
-	msg_pdbg("after clearing low bits BAR is at 0x%08x\n", mcp_spibaraddr);
-
-	/* Accessing a NULL pointer BAR is evil. Don't do it. */
-	if (mcp_spibaraddr && (buses_supported == CHIP_BUSTYPE_SPI)) {
-		/* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */
-		mcp_spibar = physmap("MCP67 SPI", mcp_spibaraddr, 0x544);
-
-/* Guessed. If this is correct, migrate to a separate MCP67 SPI driver. */
-#define MCP67_SPI_CS		(1 << 1)
-#define MCP67_SPI_SCK		(1 << 2)
-#define MCP67_SPI_MOSI		(1 << 3)
-#define MCP67_SPI_MISO		(1 << 4)
-#define MCP67_SPI_ENABLE	(1 << 0)
-#define MCP67_SPI_IDLE		(1 << 8)
-
-		status = mmio_readw(mcp_spibar + 0x530);
-		msg_pdbg("SPI control is 0x%04x, enable=%i, idle=%i\n",
-			 status, status & 0x1, (status >> 8) & 0x1);
-		/* FIXME: Remove the physunmap once the SPI driver exists. */
-		physunmap(mcp_spibar, 0x544);
-	} else if (!mcp_spibaraddr && (buses_supported & CHIP_BUSTYPE_SPI)) {
-		msg_pdbg("Strange. MCP SPI BAR is invalid.\n");
-		buses_supported &= ~CHIP_BUSTYPE_SPI;
+	if (mcp6x_spi_init(want_spi)) {
 		ret = 1;
-	} else if (mcp_spibaraddr && !(buses_supported & CHIP_BUSTYPE_SPI)) {
-		msg_pdbg("Strange. MCP SPI BAR is valid, but chipset apparently"
-			 " doesn't have SPI enabled.\n");
-	} else {
-		msg_pdbg("MCP SPI is not used.\n");
 	}
 out_msg:
 	msg_pinfo("Please send the output of \"flashrom -V\" to "
@@ -974,68 +929,6 @@ 
 	return ret;
 }
 
-/**
- * The MCP61/MCP67 code is guesswork based on cleanroom reverse engineering.
- * Due to that, it only reads info and doesn't change any settings.
- * It is assumed that LPC chips need the MCP55 code and SPI chips need the
- * code provided in enable_flash_mcp6x_7x_common. Until we know for sure, call
- * enable_flash_mcp55 from this function only if enable_flash_mcp6x_7x_common
- * indicates the flash chip is LPC. Warning: enable_flash_mcp55
- * might make SPI flash inaccessible. The same caveat applies to SPI init
- * for LPC flash.
- */
-static int enable_flash_mcp67(struct pci_dev *dev, const char *name)
-{
-	int result = 0;
-
-	result = enable_flash_mcp6x_7x_common(dev, name);
-	if (result)
-		return result;
-
-	/* Not sure if this is correct. No docs as usual. */
-	switch (buses_supported) {
-	case CHIP_BUSTYPE_LPC:
-		result = enable_flash_mcp55(dev, name);
-		break;
-	case CHIP_BUSTYPE_SPI:
-		msg_pinfo("SPI on this chipset is not supported yet.\n");
-		buses_supported = CHIP_BUSTYPE_NONE;
-		break;
-	default:
-		msg_pinfo("Something went wrong with bus type detection.\n");
-		buses_supported = CHIP_BUSTYPE_NONE;
-		break;
-	}
-
-	return result;
-}
-
-static int enable_flash_mcp7x(struct pci_dev *dev, const char *name)
-{
-	int result = 0;
-
-	result = enable_flash_mcp6x_7x_common(dev, name);
-	if (result)
-		return result;
-
-	/* Not sure if this is correct. No docs as usual. */
-	switch (buses_supported) {
-	case CHIP_BUSTYPE_LPC:
-		msg_pinfo("LPC on this chipset is not supported yet.\n");
-		break;
-	case CHIP_BUSTYPE_SPI:
-		msg_pinfo("SPI on this chipset is not supported yet.\n");
-		buses_supported = CHIP_BUSTYPE_NONE;
-		break;
-	default:
-		msg_pinfo("Something went wrong with bus type detection.\n");
-		buses_supported = CHIP_BUSTYPE_NONE;
-		break;
-	}
-
-	return result;
-}
-
 static int enable_flash_ht1000(struct pci_dev *dev, const char *name)
 {
 	uint8_t val;
@@ -1187,22 +1080,22 @@ 
 	{0x10de, 0x0365, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* LPC */
 	{0x10de, 0x0366, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* LPC */
 	{0x10de, 0x0367, OK, "NVIDIA", "MCP55",		enable_flash_mcp55}, /* Pro */
-	{0x10de, 0x03e0, NT, "NVIDIA", "MCP61",		enable_flash_mcp67},
-	{0x10de, 0x03e1, NT, "NVIDIA", "MCP61",		enable_flash_mcp67},
-	{0x10de, 0x03e2, NT, "NVIDIA", "MCP61",		enable_flash_mcp67},
-	{0x10de, 0x03e3, NT, "NVIDIA", "MCP61",		enable_flash_mcp67},
-	{0x10de, 0x0440, NT, "NVIDIA", "MCP65",		enable_flash_mcp7x},
-	{0x10de, 0x0441, NT, "NVIDIA", "MCP65",		enable_flash_mcp7x},
-	{0x10de, 0x0442, NT, "NVIDIA", "MCP65",		enable_flash_mcp7x},
-	{0x10de, 0x0443, NT, "NVIDIA", "MCP65",		enable_flash_mcp7x},
-	{0x10de, 0x0548, OK, "NVIDIA", "MCP67",		enable_flash_mcp67},
-	{0x10de, 0x075c, NT, "NVIDIA", "MCP78S",	enable_flash_mcp7x},
-	{0x10de, 0x075d, NT, "NVIDIA", "MCP78S",	enable_flash_mcp7x},
-	{0x10de, 0x07d7, NT, "NVIDIA", "MCP73",		enable_flash_mcp7x},
-	{0x10de, 0x0aac, NT, "NVIDIA", "MCP79",		enable_flash_mcp7x},
-	{0x10de, 0x0aad, NT, "NVIDIA", "MCP79",		enable_flash_mcp7x},
-	{0x10de, 0x0aae, NT, "NVIDIA", "MCP79",		enable_flash_mcp7x},
-	{0x10de, 0x0aaf, NT, "NVIDIA", "MCP79",		enable_flash_mcp7x},
+	{0x10de, 0x03e0, NT, "NVIDIA", "MCP61",		enable_flash_mcp6x_7x},
+	{0x10de, 0x03e1, NT, "NVIDIA", "MCP61",		enable_flash_mcp6x_7x},
+	{0x10de, 0x03e2, NT, "NVIDIA", "MCP61",		enable_flash_mcp6x_7x},
+	{0x10de, 0x03e3, NT, "NVIDIA", "MCP61",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0440, NT, "NVIDIA", "MCP65",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0441, NT, "NVIDIA", "MCP65",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0442, NT, "NVIDIA", "MCP65",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0443, NT, "NVIDIA", "MCP65",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0548, OK, "NVIDIA", "MCP67",		enable_flash_mcp6x_7x},
+	{0x10de, 0x075c, NT, "NVIDIA", "MCP78S",	enable_flash_mcp6x_7x},
+	{0x10de, 0x075d, NT, "NVIDIA", "MCP78S",	enable_flash_mcp6x_7x},
+	{0x10de, 0x07d7, NT, "NVIDIA", "MCP73",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0aac, NT, "NVIDIA", "MCP79",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0aad, NT, "NVIDIA", "MCP79",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0aae, NT, "NVIDIA", "MCP79",		enable_flash_mcp6x_7x},
+	{0x10de, 0x0aaf, NT, "NVIDIA", "MCP79",		enable_flash_mcp6x_7x},
 	{0x1039, 0x0496, NT, "SiS", "85C496+497",	enable_flash_sis85c496},
 	{0x1039, 0x0406, NT, "SiS", "501/5101/5501",	enable_flash_sis501},
 	{0x1039, 0x5511, NT, "SiS", "5511",		enable_flash_sis5511},
Index: flashrom-bitbang_spi_nvidia_mcp/mcp6x_spi.c
===================================================================
--- flashrom-bitbang_spi_nvidia_mcp/mcp6x_spi.c	(Revision 0)
+++ flashrom-bitbang_spi_nvidia_mcp/mcp6x_spi.c	(Revision 0)
@@ -0,0 +1,193 @@ 
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Carl-Daniel Hailfinger
+ *
+ * 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
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+/* Driver for the Nvidia MCP6x/MCP7x MCP6X_SPI controller.
+ * Based on clean room reverse engineered docs from
+ * http://www.flashrom.org/pipermail/flashrom/2009-December/001180.html
+ * created by Michael Karcher.
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "flash.h"
+#include "programmer.h"
+
+/* Bit positions for each pin. */
+
+#define MCP6X_SPI_CS		1
+#define MCP6X_SPI_SCK		2
+#define MCP6X_SPI_MOSI		3
+#define MCP6X_SPI_MISO		4
+#define MCP6X_SPI_REQUEST	0
+#define MCP6X_SPI_GRANT		8
+
+void *mcp6x_spibar = NULL;
+
+static void mcp6x_request_spibus(void)
+{
+	uint8_t byte;
+
+	byte = mmio_readb(mcp6x_spibar + 0x530);
+	byte |= 1 << MCP6X_SPI_REQUEST;
+	mmio_writeb(byte, mcp6x_spibar + 0x530);
+
+	/* Wait until we are allowed to use the SPI bus. */
+	while (!(mmio_readw(mcp6x_spibar + 0x530) & (1 << MCP6X_SPI_GRANT))) ;
+}
+
+static void mcp6x_release_spibus(void)
+{
+	uint8_t byte;
+
+	byte = mmio_readb(mcp6x_spibar + 0x530);
+	byte &= ~(1 << MCP6X_SPI_REQUEST);
+	mmio_writeb(byte, mcp6x_spibar + 0x530);
+}
+
+static void mcp6x_bitbang_set_cs(int val)
+{
+	uint8_t byte;
+
+	/* Requesting and releasing the SPI bus is handled in here to allow the
+	 * chipset to use its own SPI engine for native reads.
+	 */
+	if (val == 0)
+		mcp6x_request_spibus();
+
+	byte = mmio_readb(mcp6x_spibar + 0x530);
+	byte &= ~(1 << MCP6X_SPI_CS);
+	byte |= (val << MCP6X_SPI_CS);
+	mmio_writeb(byte, mcp6x_spibar + 0x530);
+
+	if (val == 1)
+		mcp6x_release_spibus();
+}
+
+static void mcp6x_bitbang_set_sck(int val)
+{
+	uint8_t byte;
+
+	byte = mmio_readb(mcp6x_spibar + 0x530);
+	byte &= ~(1 << MCP6X_SPI_SCK);
+	byte |= (val << MCP6X_SPI_SCK);
+	mmio_writeb(byte, mcp6x_spibar + 0x530);
+}
+
+static void mcp6x_bitbang_set_mosi(int val)
+{
+	uint8_t byte;
+
+	byte = mmio_readb(mcp6x_spibar + 0x530);
+	byte &= ~(1 << MCP6X_SPI_MOSI);
+	byte |= (val << MCP6X_SPI_MOSI);
+	mmio_writeb(byte, mcp6x_spibar + 0x530);
+}
+
+static int mcp6x_bitbang_get_miso(void)
+{
+	uint8_t byte;
+
+	byte = mmio_readb(mcp6x_spibar + 0x530);
+	byte = (byte >> MCP6X_SPI_MISO) & 0x1;
+	return byte;
+}
+
+static const struct bitbang_spi_master bitbang_spi_master_mcp6x = {
+	.type = BITBANG_SPI_MASTER_MCP,
+	.set_cs = mcp6x_bitbang_set_cs,
+	.set_sck = mcp6x_bitbang_set_sck,
+	.set_mosi = mcp6x_bitbang_set_mosi,
+	.get_miso = mcp6x_bitbang_get_miso,
+};
+
+int mcp6x_spi_init(int want_spi)
+{
+	uint16_t status;
+	uint32_t mcp6x_spibaraddr;
+	struct pci_dev *smbusdev;
+
+	/* Look for the SMBus device (SMBus PCI class) */
+	smbusdev = pci_dev_find_vendorclass(0x10de, 0x0c05);
+	if (!smbusdev) {
+		if (want_spi) {
+			msg_perr("ERROR: SMBus device not found. Not enabling "
+				 "SPI.\n");
+			return 1;
+		} else {
+			msg_pinfo("Odd. SMBus device not found.\n");
+			return 0;
+		}
+	}
+	msg_pdbg("Found SMBus device %04x:%04x at %02x:%02x:%01x\n",
+		smbusdev->vendor_id, smbusdev->device_id,
+		smbusdev->bus, smbusdev->dev, smbusdev->func);
+
+
+	/* Locate the BAR where the SPI interface lives. */
+	mcp6x_spibaraddr = pci_read_long(smbusdev, 0x74);
+	/* BAR size is 64k, bits 15..4 are zero, bit 3..0 declare a
+	 * 32-bit non-prefetchable memory BAR.
+	 */
+	mcp6x_spibaraddr &= ~0xffff;
+	msg_pdbg("MCP SPI BAR is at 0x%08x\n", mcp6x_spibaraddr);
+
+	/* Accessing a NULL pointer BAR is evil. Don't do it. */
+	if (!mcp6x_spibaraddr && want_spi) {
+		msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR "
+			 "is invalid.\n");
+		return 1;
+	} else if (!mcp6x_spibaraddr && !want_spi) {
+		msg_pdbg("MCP SPI is not used.\n");
+		return 0;
+	} else if (mcp6x_spibaraddr && !want_spi) {
+		msg_pdbg("Strange. MCP SPI BAR is valid, but chipset apparently"
+			 " doesn't have SPI enabled.\n");
+		/* FIXME: Should we enable SPI anyway? */
+		return 0;
+	}
+	/* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */
+	mcp6x_spibar = physmap("Nvidia MCP6x SPI", mcp6x_spibaraddr, 0x544);
+
+#if 0
+	/* FIXME: Run the physunmap in a shutdown function. */
+	physunmap(mcp6x_spibar, 0x544);
+#endif
+
+	status = mmio_readw(mcp6x_spibar + 0x530);
+	msg_pdbg("SPI control is 0x%04x, req=%i, gnt=%i\n",
+		 status, (status >> MCP6X_SPI_REQUEST) & 0x1,
+		 (status >> MCP6X_SPI_GRANT) & 0x1);
+
+	/* 1 usec halfperiod delay for now. */
+	if (bitbang_spi_init(&bitbang_spi_master_mcp6x, 1)) {
+		/* This should never happen. */
+		msg_perr("MCP6X bitbang SPI master init failed!\n");
+		return 1;
+	}
+
+	buses_supported |= CHIP_BUSTYPE_SPI;
+	spi_controller = SPI_CONTROLLER_MCP6X_BITBANG;
+
+	return 0;
+}
+
+#endif
Index: flashrom-bitbang_spi_nvidia_mcp/programmer.h
===================================================================
--- flashrom-bitbang_spi_nvidia_mcp/programmer.h	(Revision 1112)
+++ flashrom-bitbang_spi_nvidia_mcp/programmer.h	(Arbeitskopie)
@@ -110,6 +110,11 @@ 
 #if CONFIG_RAYER_SPI == 1
 	BITBANG_SPI_MASTER_RAYER,
 #endif
+#if CONFIG_INTERNAL == 1
+#if defined(__i386__) || defined(__x86_64__)
+	BITBANG_SPI_MASTER_MCP,
+#endif
+#endif
 };
 
 struct bitbang_spi_master {
@@ -404,6 +409,13 @@ 
 int rayer_spi_init(void);
 #endif
 
+/* mcp6x_spi.c */
+#if CONFIG_INTERNAL == 1
+#if defined(__i386__) || defined(__x86_64__)
+int mcp6x_spi_init(int want_spi);
+#endif
+#endif
+
 /* bitbang_spi.c */
 int bitbang_spi_init(const struct bitbang_spi_master *master, int halfperiod);
 int bitbang_spi_send_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr);
@@ -455,6 +467,7 @@ 
 	SPI_CONTROLLER_SB600,
 	SPI_CONTROLLER_VIA,
 	SPI_CONTROLLER_WBSIO,
+	SPI_CONTROLLER_MCP6X_BITBANG,
 #endif
 #endif
 #if CONFIG_FT2232_SPI == 1