diff --git a/Makefile b/Makefile index 290b9e63a..782ecacbe 100644 --- a/Makefile +++ b/Makefile @@ -118,8 +118,12 @@ CONFIG_RAYER_SPI ?= yes 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 @@ ifeq ($(CONFIG_INTERNAL), yes) 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 diff --git a/chipset_enable.c b/chipset_enable.c index 17318c29a..d75fc82c4 100644 --- a/chipset_enable.c +++ b/chipset_enable.c @@ -868,18 +868,16 @@ static int enable_flash_mcp55(struct pci_dev *dev, const char *name) 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 @@ static int enable_flash_mcp6x_7x_common(struct pci_dev *dev, const char *name) 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 @@ static int enable_flash_mcp6x_7x_common(struct pci_dev *dev, const char *name) 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 @@ out_msg: 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 @@ const struct penable chipset_enables[] = { {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}, diff --git a/mcp6x_spi.c b/mcp6x_spi.c new file mode 100644 index 000000000..f3890be63 --- /dev/null +++ b/mcp6x_spi.c @@ -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 +#include +#include +#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 tmp; + + tmp = mmio_readb(mcp6x_spibar + 0x530); + tmp |= 1 << MCP6X_SPI_REQUEST; + mmio_writeb(tmp, 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 tmp; + + tmp = mmio_readb(mcp6x_spibar + 0x530); + tmp &= ~(1 << MCP6X_SPI_REQUEST); + mmio_writeb(tmp, mcp6x_spibar + 0x530); +} + +static void mcp6x_bitbang_set_cs(int val) +{ + uint8_t tmp; + + /* 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(); + + tmp = mmio_readb(mcp6x_spibar + 0x530); + tmp &= ~(1 << MCP6X_SPI_CS); + tmp |= (val << MCP6X_SPI_CS); + mmio_writeb(tmp, mcp6x_spibar + 0x530); + + if (val == 1) + mcp6x_release_spibus(); +} + +static void mcp6x_bitbang_set_sck(int val) +{ + uint8_t tmp; + + tmp = mmio_readb(mcp6x_spibar + 0x530); + tmp &= ~(1 << MCP6X_SPI_SCK); + tmp |= (val << MCP6X_SPI_SCK); + mmio_writeb(tmp, mcp6x_spibar + 0x530); +} + +static void mcp6x_bitbang_set_mosi(int val) +{ + uint8_t tmp; + + tmp = mmio_readb(mcp6x_spibar + 0x530); + tmp &= ~(1 << MCP6X_SPI_MOSI); + tmp |= (val << MCP6X_SPI_MOSI); + mmio_writeb(tmp, mcp6x_spibar + 0x530); +} + +static int mcp6x_bitbang_get_miso(void) +{ + uint8_t tmp; + + tmp = mmio_readb(mcp6x_spibar + 0x530); + tmp = (tmp >> MCP6X_SPI_MISO) & 0x1; + return tmp; +} + +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 diff --git a/programmer.h b/programmer.h index 68ad500b6..226db0445 100644 --- a/programmer.h +++ b/programmer.h @@ -110,6 +110,11 @@ enum bitbang_spi_master_type { #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 ft2232_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int l 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 @@ enum spi_controller { SPI_CONTROLLER_SB600, SPI_CONTROLLER_VIA, SPI_CONTROLLER_WBSIO, + SPI_CONTROLLER_MCP6X_BITBANG, #endif #endif #if CONFIG_FT2232_SPI == 1 diff --git a/spi.c b/spi.c index 65d43be66..b005cdc40 100644 --- a/spi.c +++ b/spi.c @@ -83,6 +83,13 @@ const struct spi_programmer spi_programmer[] = { .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