summaryrefslogtreecommitdiffstats
path: root/mcp6x_spi.c
diff options
context:
space:
mode:
authorCarl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>2010-07-28 15:08:35 +0000
committerCarl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>2010-07-28 15:08:35 +0000
commit2f43616873dd88cd417017dc5bc218b3e10deb0d (patch)
treeaeb262cf0a555f42e00890c5907e8ac6f537a4b4 /mcp6x_spi.c
parent5b997c3ed66ddbbb9470f27d4e27ab4c263bc9cf (diff)
downloadast2050-flashrom-2f43616873dd88cd417017dc5bc218b3e10deb0d.zip
ast2050-flashrom-2f43616873dd88cd417017dc5bc218b3e10deb0d.tar.gz
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, ECS Geforce6100SM-M, Andrew Cleveland * MCP61, 10de:03e0, LPC OK, 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. Corresponding to flashrom svn r1113. Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net> Acked-by: Uwe Hermann <uwe@hermann-uwe.de>
Diffstat (limited to 'mcp6x_spi.c')
-rw-r--r--mcp6x_spi.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/mcp6x_spi.c b/mcp6x_spi.c
new file mode 100644
index 0000000..f3890be
--- /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 <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 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
OpenPOWER on IntegriCloud