Patrick Georgi ([email protected]) just uploaded a new patch set to 
gerrit, which you can find at http://review.coreboot.org/2367

-gerrit

commit 4bd7d40a31a81789c27a4a404d69b25366686faa
Author: Mathias Krause <[email protected]>
Date:   Thu Feb 3 09:28:37 2011 +0100

    Add Flash lockdown for AMD SB600
    
    This directly drives the flash chip since chipset lockdown
    can be worked around using manual SPI access.
    
    Change-Id: I5e37acea4f40e952f9d97784897e3a0d054718aa
    Signed-off-by: Patrick Georgi <[email protected]>
    Signed-off-by: Mathias Krause <[email protected]>
---
 drivers/Makefile.inc |    1 +
 drivers/amd.c        |  110 +++++
 drivers/intel.c      |    5 +-
 drivers/sb600.c      | 1245 ++++++++++++++++++++++++++++++++++++++++++++++++++
 x86/linux_load.c     |   14 +-
 5 files changed, 1370 insertions(+), 5 deletions(-)

diff --git a/drivers/Makefile.inc b/drivers/Makefile.inc
index f93c287..941c151 100644
--- a/drivers/Makefile.inc
+++ b/drivers/Makefile.inc
@@ -23,3 +23,4 @@ TARGETS-$(CONFIG_IDE_NEW_DISK) += drivers/ide_new.o
 TARGETS-$(CONFIG_VIA_SOUND) += drivers/via-sound.o
 TARGETS-$(CONFIG_USB_DISK) += drivers/usb.o
 TARGETS-$(CONFIG_TARGET_I386) += drivers/intel.o
+TARGETS-$(CONFIG_TARGET_I386) += drivers/amd.o drivers/sb600.o
diff --git a/drivers/amd.c b/drivers/amd.c
new file mode 100644
index 0000000..cac7c53
--- /dev/null
+++ b/drivers/amd.c
@@ -0,0 +1,110 @@
+/*
+ * This file is part of FILO.
+ *
+ * Copyright (C) 2010 secunet Security Networks AG
+ *
+ * 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
+ */
+
+#include <libpayload.h>
+#include <config.h>
+#include <pci.h>
+
+#define DEBUG_THIS CONFIG_DEBUG_AMD
+#include <debug.h>
+
+#define ROM_BASE                       0xfff00000      /* FIXME: should be 
read from chipset */
+#define ROM_CHUNK_SIZE         512 /* kB */
+#define SB600_LPC_BRIDGE       PCI_DEV(0, 0x14, 3)
+
+extern int sb600_spi_lockdown(void);
+
+
+int amd_lockdown_flash(void)
+{
+       u32 base = ROM_BASE;
+       u32 reg32, val32;
+       int spi, lpc;
+       int reg;
+
+       /* for now we only suppport the SB600 chipset */
+       reg32 = pci_read_config32(SB600_LPC_BRIDGE, REG_VENDOR_ID);
+       switch (reg32) {
+               case 0x438d1002:
+                       break;
+
+               default:
+                       debug("Not an SB600 chipset\n");
+                       return -1;
+       }
+
+       /* Lock the SPI interface first */
+       debug("Locking SPI interface...\n");
+       spi = sb600_spi_lockdown();
+       if (spi == 0) {
+               debug("Flash chip SPI write protect enabled!\n");
+       } else {
+               debug("Failed to enable flash chip SPI write protect%s!\n",
+                     spi > 0 ? " (already locked)" : "");
+
+               /* if the chip was already locked, this might be no failure */
+               if (spi > 0) {
+                       spi = 0;
+               }
+       }
+
+       /* Enable ROM write protect */
+       debug("Locking BIOS ROM range to read-only...\n");
+
+       /* LPC ROM read/write protect registers (50h, 54h, 58h, 5ch)
+        * 31:11 = base address (2k aligned)
+        *  10:2 = length-1 in kB (0=1kB, 511=512kB)
+        *     1 = read protect bit (1 = read protect)
+        *     0 = write protect bit (1 = write protect)
+        *
+        * => we need 2 of the 4 available registers to protect a chip sized 1MB
+        */
+
+       lpc = -1;
+       for (reg = 0x50; reg <= 0x5c; reg += 4) {
+               val32  = base & ~((1 << 11) - 1);       // base address
+               val32 |= (ROM_CHUNK_SIZE - 1) << 2;     // size = 512kB
+               val32 |= 1;                                                     
// write protect
+
+               pci_write_config32(SB600_LPC_BRIDGE, reg, val32);
+               if (pci_read_config32(SB600_LPC_BRIDGE, reg) != val32) {
+                       debug("Setting LPC ROM protection register 0x%x failed, 
trying to "
+                             "use another\n", reg);
+
+                       continue;
+               }
+
+               if (base + ROM_CHUNK_SIZE * 1024 - 1 == 0xffffffff) {
+                       lpc = 0;
+
+                       break;
+               }
+
+               /* increase to the next chunk to protect */
+               base += ROM_CHUNK_SIZE * 1024;
+       }
+
+       if (lpc == 0) {
+               debug("LPC BIOS ROM range write protectedd until next full 
reset.\n");
+       } else {
+               debug("Failed to protect LPC BIOS ROM range!\n");
+       }
+
+       return spi | lpc;
+}
diff --git a/drivers/intel.c b/drivers/intel.c
index de61c2e..52430f3 100644
--- a/drivers/intel.c
+++ b/drivers/intel.c
@@ -44,7 +44,7 @@
 #define RCBA32(x) *((volatile u32 *)(DEFAULT_RCBA + x))
 
 
-void intel_lockdown_flash(void)
+int intel_lockdown_flash(void)
 {
        u8 reg8;
        u32 reg32;
@@ -58,7 +58,7 @@ void intel_lockdown_flash(void)
                break;
        default:
                debug("Not an ICH7 southbridge\n");
-               return;
+               return -1;
        }
 
        /* Now try this: */
@@ -79,6 +79,7 @@ void intel_lockdown_flash(void)
 
        debug("BIOS hard locked until next full reset.\n");
 
+       return 0;
 }
 
 
diff --git a/drivers/sb600.c b/drivers/sb600.c
new file mode 100644
index 0000000..cce0220
--- /dev/null
+++ b/drivers/sb600.c
@@ -0,0 +1,1245 @@
+/*
+ * This file is part of FILO, based on code from the flashrom project.
+ *
+ * Copyright (C) 2000 Silicon Integrated System Corporation
+ * Copyright (C) 2004 Tyan Corp <[email protected]>
+ * Copyright (C) 2005-2008 coresystems GmbH
+ * Copyright (C) 2008,2009 Carl-Daniel Hailfinger
+ * Copyright (C) 2010 secunet Security Networks AG
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+ */
+
+#include <pci/pci.h>
+#include <stdint.h>
+#include <stddef.h>
+
+/*
+ * libpayload quirks
+ */
+#include <libpayload.h>
+
+#define DEBUG_THIS CONFIG_DEBUG_AMD
+#include <debug.h>
+
+#define print(x,...)   debug(__VA_ARGS__)
+
+
+/*
+ * spi.h
+ */
+
+/* Read Electronic ID */
+#define JEDEC_RDID             0x9f
+#define JEDEC_RDID_OUTSIZE     0x01
+/* INSIZE may be 0x04 for some chips*/
+#define JEDEC_RDID_INSIZE      0x03
+
+/* Read Status Register */
+#define JEDEC_RDSR             0x05
+#define JEDEC_RDSR_OUTSIZE     0x01
+#define JEDEC_RDSR_INSIZE      0x01
+#define JEDEC_RDSR_BIT_WIP     (0x01 << 0)
+
+/* Write Status Enable */
+#define JEDEC_EWSR             0x50
+#define JEDEC_EWSR_OUTSIZE     0x01
+#define JEDEC_EWSR_INSIZE      0x00
+
+/* Write Status Register */
+#define JEDEC_WRSR             0x01
+#define JEDEC_WRSR_OUTSIZE     0x02
+#define JEDEC_WRSR_INSIZE      0x00
+
+/* Write Enable */
+#define JEDEC_WREN             0x06
+#define JEDEC_WREN_OUTSIZE     0x01
+#define JEDEC_WREN_INSIZE      0x00
+
+/* Error codes */
+#define SPI_GENERIC_ERROR      -1
+#define SPI_INVALID_OPCODE     -2
+#define SPI_INVALID_ADDRESS    -3
+#define SPI_INVALID_LENGTH     -4
+#define SPI_FLASHROM_BUG       -5
+#define SPI_PROGRAMMER_ERROR   -6
+
+
+/*
+ * flashchips.h
+ */
+
+#define GENERIC_MANUF_ID       0xffff  /* Check if there is a vendor ID */
+#define GENERIC_DEVICE_ID      0xffff  /* Only match the vendor ID */
+
+#define ATMEL_ID               0x1F    /* Atmel */
+#define ATMEL_AT26DF081A               0x4501
+
+/*
+ * flash.h
+ */
+
+/* Error codes */
+#define TIMEOUT_ERROR  -101
+
+typedef unsigned long chipaddr;
+
+static inline void programmer_delay(int usecs);
+
+enum chipbustype {
+       CHIP_BUSTYPE_NONE       = 0,
+       CHIP_BUSTYPE_PARALLEL   = 1 << 0,
+       CHIP_BUSTYPE_LPC        = 1 << 1,
+       CHIP_BUSTYPE_FWH        = 1 << 2,
+       CHIP_BUSTYPE_SPI        = 1 << 3,
+       CHIP_BUSTYPE_NONSPI     = CHIP_BUSTYPE_PARALLEL | CHIP_BUSTYPE_LPC | 
CHIP_BUSTYPE_FWH,
+       CHIP_BUSTYPE_UNKNOWN    = CHIP_BUSTYPE_PARALLEL | CHIP_BUSTYPE_LPC | 
CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_SPI,
+};
+
+/*
+ * How many different contiguous runs of erase blocks with one size each do
+ * we have for a given erase function?
+ */
+#define NUM_ERASEREGIONS 5
+
+/*
+ * How many different erase functions do we have per chip?
+ * Atmel AT25FS010 has 6 different functions.
+ */
+#define NUM_ERASEFUNCTIONS 6
+
+#define FEATURE_REGISTERMAP    (1 << 0)
+#define FEATURE_BYTEWRITES     (1 << 1)
+#define FEATURE_LONG_RESET     (0 << 4)
+#define FEATURE_SHORT_RESET    (1 << 4)
+#define FEATURE_EITHER_RESET   FEATURE_LONG_RESET
+#define FEATURE_RESET_MASK     (FEATURE_LONG_RESET | FEATURE_SHORT_RESET)
+#define FEATURE_ADDR_FULL      (0 << 2)
+#define FEATURE_ADDR_MASK      (3 << 2)
+#define FEATURE_ADDR_2AA       (1 << 2)
+#define FEATURE_ADDR_AAA       (2 << 2)
+#define FEATURE_ADDR_SHIFTED   (1 << 5)
+#define FEATURE_WRSR_EWSR      (1 << 6)
+#define FEATURE_WRSR_WREN      (1 << 7)
+#define FEATURE_WRSR_EITHER    (FEATURE_WRSR_EWSR | FEATURE_WRSR_WREN)
+
+struct flashchip {
+       const char *vendor;
+       const char *name;
+
+       enum chipbustype bustype;
+
+       /*
+        * With 32bit manufacture_id and model_id we can cover IDs up to
+        * (including) the 4th bank of JEDEC JEP106W Standard Manufacturer's
+        * Identification code.
+        */
+       uint32_t manufacture_id;
+       uint32_t model_id;
+
+       int total_size;
+       int page_size;
+       int feature_bits;
+
+       /*
+        * Indicate if flashrom has been tested with this flash chip and if
+        * everything worked correctly.
+        */
+       uint32_t tested;
+
+       int (*probe) (struct flashchip *flash);
+
+       /* Delay after "enter/exit ID mode" commands in microseconds. */
+       int probe_timing;
+
+       /*
+        * Erase blocks and associated erase function. Any chip erase function
+        * is stored as chip-sized virtual block together with said function.
+        */
+       struct block_eraser {
+               struct eraseblock{
+                       unsigned int size; /* Eraseblock size */
+                       unsigned int count; /* Number of contiguous blocks with 
that size */
+               } eraseblocks[NUM_ERASEREGIONS];
+               int (*block_erase) (struct flashchip *flash, unsigned int 
blockaddr, unsigned int blocklen);
+       } block_erasers[NUM_ERASEFUNCTIONS];
+
+       int (*printlock) (struct flashchip *flash);
+       int (*unlock) (struct flashchip *flash);
+       int (*lock) (struct flashchip *flash);
+       int (*write) (struct flashchip *flash, uint8_t *buf, int start, int 
len);
+       int (*read) (struct flashchip *flash, uint8_t *buf, int start, int len);
+
+       /* Some flash devices have an additional register space. */
+       chipaddr virtual_memory;
+       chipaddr virtual_registers;
+};
+
+#define TEST_UNTESTED  0
+
+#define TEST_OK_PROBE  (1 << 0)
+#define TEST_OK_READ   (1 << 1)
+#define TEST_OK_ERASE  (1 << 2)
+#define TEST_OK_WRITE  (1 << 3)
+#define TEST_OK_PR     (TEST_OK_PROBE | TEST_OK_READ)
+#define TEST_OK_PRE    (TEST_OK_PROBE | TEST_OK_READ | TEST_OK_ERASE)
+#define TEST_OK_PRW    (TEST_OK_PROBE | TEST_OK_READ | TEST_OK_WRITE)
+#define TEST_OK_PREW   (TEST_OK_PROBE | TEST_OK_READ | TEST_OK_ERASE | 
TEST_OK_WRITE)
+#define TEST_OK_MASK   0x0f
+
+#define TEST_BAD_PROBE (1 << 4)
+#define TEST_BAD_READ  (1 << 5)
+#define TEST_BAD_ERASE (1 << 6)
+#define TEST_BAD_WRITE (1 << 7)
+#define TEST_BAD_PREW  (TEST_BAD_PROBE | TEST_BAD_READ | TEST_BAD_ERASE | 
TEST_BAD_WRITE)
+#define TEST_BAD_MASK  0xf0
+
+/* Timing used in probe routines. ZERO is -2 to differentiate between an unset
+ * field and zero delay.
+ * 
+ * SPI devices will always have zero delay and ignore this field.
+ */
+#define TIMING_FIXME   -1
+/* this is intentionally same value as fixme */
+#define TIMING_IGNORED -1
+#define TIMING_ZERO    -2
+
+/* Something happened that shouldn't happen, but we can go on */
+#define ERROR_NONFATAL 0x100
+
+#define MSG_ERROR      0
+#define MSG_INFO       1
+#define MSG_DEBUG      2
+#define MSG_BARF       3
+#define msg_gerr(...)  print(MSG_ERROR, __VA_ARGS__)   /* general errors */
+#define msg_perr(...)  print(MSG_ERROR, __VA_ARGS__)   /* programmer errors */
+#define msg_cerr(...)  print(MSG_ERROR, __VA_ARGS__)   /* chip errors */
+#define msg_ginfo(...) print(MSG_INFO, __VA_ARGS__)    /* general info */
+#define msg_pinfo(...) print(MSG_INFO, __VA_ARGS__)    /* programmer info */
+#define msg_cinfo(...) print(MSG_INFO, __VA_ARGS__)    /* chip info */
+#define msg_gdbg(...)  print(MSG_DEBUG, __VA_ARGS__)   /* general debug */
+#define msg_pdbg(...)  print(MSG_DEBUG, __VA_ARGS__)   /* programmer debug */
+#define msg_cdbg(...)  print(MSG_DEBUG, __VA_ARGS__)   /* chip debug */
+#define msg_gspew(...) print(MSG_BARF, __VA_ARGS__)    /* general debug barf  
*/
+#define msg_pspew(...) print(MSG_BARF, __VA_ARGS__)    /* programmer debug 
barf  */
+#define msg_cspew(...) print(MSG_BARF, __VA_ARGS__)    /* chip debug barf  */
+
+struct spi_command {
+       unsigned int writecnt;
+       unsigned int readcnt;
+       const unsigned char *writearr;
+       unsigned char *readarr;
+};
+
+
+/*
+ * programmer.h
+ */
+
+enum spi_controller {
+       SPI_CONTROLLER_NONE,
+       SPI_CONTROLLER_SB600,
+};
+
+struct spi_programmer {
+       int (*command)(unsigned int writecnt, unsigned int readcnt,
+                  const unsigned char *writearr, unsigned char *readarr);
+       int (*multicommand)(struct spi_command *cmds);
+
+       /* Optimized functions for this programmer */
+       int (*read)(struct flashchip *flash, uint8_t *buf, int start, int len);
+       int (*write_256)(struct flashchip *flash, uint8_t *buf, int start, int 
len);
+};
+
+static int default_spi_send_multicommand(struct spi_command *cmds);
+
+#define rpci_write_byte        pci_write_byte
+
+static int sb600_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+                     const unsigned char *writearr, unsigned char *readarr);
+static int sb600_spi_read(struct flashchip *flash, uint8_t *buf, int start, 
int len);
+static int sb600_spi_write_256(struct flashchip *flash, uint8_t *buf, int 
start, int len);
+
+
+/*
+ * chipdrivers.h
+ */
+
+
+static uint8_t spi_read_status_register(void);
+
+
+/*
+ * hwaccess.h
+ */
+
+
+#define OUTB outb
+#define OUTW outw
+#define OUTL outl
+#define INB  inb
+#define INW  inw
+#define INL  inl
+
+
+/*
+ * jedec.c
+ */
+
+/* Check one byte for odd parity */
+static uint8_t oddparity(uint8_t val)
+{
+       val = (val ^ (val >> 4)) & 0xf;
+       val = (val ^ (val >> 2)) & 0x3;
+       return (val ^ (val >> 1)) & 0x1;
+}
+
+
+/*
+ * spi.c
+ */
+
+static enum spi_controller spi_controller = SPI_CONTROLLER_NONE;
+
+static const struct spi_programmer spi_programmer[] = {
+       { /* SPI_CONTROLLER_NONE */
+               .command = NULL,
+               .multicommand = NULL,
+               .read = NULL,
+               .write_256 = NULL,
+       },
+
+       { /* SPI_CONTROLLER_SB600 */
+               .command = sb600_spi_send_command,
+               .multicommand = default_spi_send_multicommand,
+               .read = sb600_spi_read,
+               .write_256 = sb600_spi_write_256,
+       },
+};
+
+static int spi_send_command(unsigned int writecnt, unsigned int readcnt,
+               const unsigned char *writearr, unsigned char *readarr)
+{
+       if (!spi_programmer[spi_controller].command) {
+               msg_perr("%s called, but SPI is unsupported on this "
+                        "hardware. Please report a bug at "
+                        "[email protected]\n", __func__);
+               return 1;
+       }
+
+       return spi_programmer[spi_controller].command(writecnt, readcnt,
+                                                     writearr, readarr);
+}
+
+static int spi_send_multicommand(struct spi_command *cmds)
+{
+       if (!spi_programmer[spi_controller].multicommand) {
+               msg_perr("%s called, but SPI is unsupported on this "
+                        "hardware. Please report a bug at "
+                        "[email protected]\n", __func__);
+               return 1;
+       }
+
+       return spi_programmer[spi_controller].multicommand(cmds);
+}
+
+static int default_spi_send_multicommand(struct spi_command *cmds)
+{
+       int result = 0;
+       for (; (cmds->writecnt || cmds->readcnt) && !result; cmds++) {
+               result = spi_send_command(cmds->writecnt, cmds->readcnt,
+                                         cmds->writearr, cmds->readarr);
+       }
+       return result;
+}
+
+
+/*
+ * spi25.c
+ */
+
+static int spi_rdid(unsigned char *readarr, int bytes)
+{
+       static const unsigned char cmd[JEDEC_RDID_OUTSIZE] = { JEDEC_RDID };
+       int ret;
+       int i;
+
+       ret = spi_send_command(sizeof(cmd), bytes, cmd, readarr);
+       if (ret)
+               return ret;
+       msg_cspew("RDID returned");
+       for (i = 0; i < bytes; i++)
+               msg_cspew(" 0x%02x", readarr[i]);
+       msg_cspew(". ");
+       return 0;
+}
+
+static void spi_prettyprint_status_register(struct flashchip *flash)
+{
+       uint8_t status;
+
+       status = spi_read_status_register();
+       msg_cdbg("Chip status register is %02x\n", status);
+}
+
+
+static int probe_spi_rdid_generic(struct flashchip *flash, int bytes)
+{
+       unsigned char readarr[4];
+       uint32_t id1;
+       uint32_t id2;
+
+       if (spi_rdid(readarr, bytes))
+               return 0;
+
+       if (!oddparity(readarr[0]))
+               msg_cdbg("RDID byte 0 parity violation. ");
+
+       /* Check if this is a continuation vendor ID.
+        * FIXME: Handle continuation device IDs.
+        */
+       if (readarr[0] == 0x7f) {
+               if (!oddparity(readarr[1]))
+                       msg_cdbg("RDID byte 1 parity violation. ");
+               id1 = (readarr[0] << 8) | readarr[1];
+               id2 = readarr[2];
+               if (bytes > 3) {
+                       id2 <<= 8;
+                       id2 |= readarr[3];
+               }
+       } else {
+               id1 = readarr[0];
+               id2 = (readarr[1] << 8) | readarr[2];
+       }
+
+       msg_cdbg("%s: id1 0x%02x, id2 0x%02x\n", __func__, id1, id2);
+
+       if (id1 == flash->manufacture_id && id2 == flash->model_id) {
+               /* Print the status register to tell the
+                * user about possible write protection.
+                */
+               spi_prettyprint_status_register(flash);
+
+               return 1;
+       }
+
+       /* Test if this is a pure vendor match. */
+       if (id1 == flash->manufacture_id &&
+           GENERIC_DEVICE_ID == flash->model_id)
+               return 1;
+
+       /* Test if there is any vendor ID. */
+       if (GENERIC_MANUF_ID == flash->manufacture_id &&
+           id1 != 0xff)
+               return 1;
+
+       return 0;
+}
+
+static int probe_spi_rdid(struct flashchip *flash)
+{
+       return probe_spi_rdid_generic(flash, 3);
+}
+
+static uint8_t spi_read_status_register(void)
+{
+       static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { JEDEC_RDSR };
+       /* FIXME: No workarounds for driver/hardware bugs in generic code. */
+       unsigned char readarr[2]; /* JEDEC_RDSR_INSIZE=1 but wbsio needs 2 */
+       int ret;
+
+       /* Read Status Register */
+       ret = spi_send_command(sizeof(cmd), sizeof(readarr), cmd, readarr);
+       if (ret)
+               msg_cerr("RDSR failed!\n");
+
+       return readarr[0];
+}
+
+/*
+ * This is according the SST25VF016 datasheet, who knows it is more
+ * generic that this...
+ */
+static int spi_write_status_register_ewsr(struct flashchip *flash, int status)
+{
+       int result;
+       int i = 0;
+       struct spi_command cmds[] = {
+       {
+       /* WRSR requires either EWSR or WREN depending on chip type. */
+               .writecnt       = JEDEC_EWSR_OUTSIZE,
+               .writearr       = (const unsigned char[]){ JEDEC_EWSR },
+               .readcnt        = 0,
+               .readarr        = NULL,
+       }, {
+               .writecnt       = JEDEC_WRSR_OUTSIZE,
+               .writearr       = (const unsigned char[]){ JEDEC_WRSR, 
(unsigned char) status },
+               .readcnt        = 0,
+               .readarr        = NULL,
+       }, {
+               .writecnt       = 0,
+               .writearr       = NULL,
+               .readcnt        = 0,
+               .readarr        = NULL,
+       }};
+
+       result = spi_send_multicommand(cmds);
+       if (result) {
+               msg_cerr("%s failed during command execution\n",
+                       __func__);
+               /* No point in waiting for the command to complete if execution
+                * failed.
+                */
+               return result;
+       }
+       /* WRSR performs a self-timed erase before the changes take effect.
+        * This may take 50-85 ms in most cases, and some chips apparently
+        * allow running RDSR only once. Therefore pick an initial delay of
+        * 100 ms, then wait in 10 ms steps until a total of 5 s have elapsed.
+        */
+       programmer_delay(100 * 1000);
+       while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP) {
+               if (++i > 490) {
+                       msg_cerr("Error: WIP bit after WRSR never cleared\n");
+                       return TIMEOUT_ERROR;
+               }
+               programmer_delay(10 * 1000);
+       }
+       return 0;
+}
+
+static int spi_write_status_register_wren(struct flashchip *flash, int status)
+{
+       int result;
+       int i = 0;
+       struct spi_command cmds[] = {
+       {
+       /* WRSR requires either EWSR or WREN depending on chip type. */
+               .writecnt       = JEDEC_WREN_OUTSIZE,
+               .writearr       = (const unsigned char[]){ JEDEC_WREN },
+               .readcnt        = 0,
+               .readarr        = NULL,
+       }, {
+               .writecnt       = JEDEC_WRSR_OUTSIZE,
+               .writearr       = (const unsigned char[]){ JEDEC_WRSR, 
(unsigned char) status },
+               .readcnt        = 0,
+               .readarr        = NULL,
+       }, {
+               .writecnt       = 0,
+               .writearr       = NULL,
+               .readcnt        = 0,
+               .readarr        = NULL,
+       }};
+
+       result = spi_send_multicommand(cmds);
+       if (result) {
+               msg_cerr("%s failed during command execution\n",
+                       __func__);
+               /* No point in waiting for the command to complete if execution
+                * failed.
+                */
+               return result;
+       }
+       /* WRSR performs a self-timed erase before the changes take effect.
+        * This may take 50-85 ms in most cases, and some chips apparently
+        * allow running RDSR only once. Therefore pick an initial delay of
+        * 100 ms, then wait in 10 ms steps until a total of 5 s have elapsed.
+        */
+       programmer_delay(100 * 1000);
+       while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP) {
+               if (++i > 490) {
+                       msg_cerr("Error: WIP bit after WRSR never cleared\n");
+                       return TIMEOUT_ERROR;
+               }
+               programmer_delay(10 * 1000);
+       }
+       return 0;
+}
+
+static int spi_write_status_register(struct flashchip *flash, int status)
+{
+       int ret = 1;
+
+       if (!(flash->feature_bits & (FEATURE_WRSR_WREN | FEATURE_WRSR_EWSR))) {
+               msg_cdbg("Missing status register write definition, assuming "
+                        "EWSR is needed\n");
+               flash->feature_bits |= FEATURE_WRSR_EWSR;
+       }
+       if (flash->feature_bits & FEATURE_WRSR_WREN)
+               ret = spi_write_status_register_wren(flash, status);
+       if (ret && (flash->feature_bits & FEATURE_WRSR_EWSR))
+               ret = spi_write_status_register_ewsr(flash, status);
+       return ret;
+}
+
+static int spi_enable_blockprotect_at25df(struct flashchip *flash)
+{
+       uint8_t status;
+       int result;
+
+       /* Reset SPRL when needed to be able to modify status register */
+       status = spi_read_status_register();
+       if (status & (1 << 7)) {
+               msg_cdbg("Need to disable Sector Protection Register Lock\n");
+               if ((status & (1 << 4)) == 0) {
+                       msg_cerr("WP# pin is active, disabling "
+                                "write protection is impossible.\n");
+                       return 1;
+               }
+               /* All bits except bit 7 (SPRL) are readonly. */
+               result = spi_write_status_register(flash, status & ~(1 << 7));
+               if (result) {
+                       msg_cerr("spi_write_status_register failed\n");
+                       return result;
+               }
+
+       }
+       /* Global protect. Make sure to set SPRL as well. */
+       result = spi_write_status_register(flash, status | 0xbc);
+       if (result) {
+               msg_cerr("spi_write_status_register failed\n");
+               return result;
+       }
+       status = spi_read_status_register();
+       if ((status & (3 << 2)) != (3 << 2)) {
+               msg_cerr("Block protection could not be enabled!\n");
+               return 1;
+       }
+       msg_cinfo("Block protection enabled, %s locked!\n",
+                 (status & (1 << 4)) ? "software" : "hardware");
+       return 0;
+}
+
+
+/*
+ * hwaccess.c
+ */
+
+static inline void mmio_writeb(uint8_t val, void *addr)
+{
+       *(volatile uint8_t *) addr = val;
+}
+
+static inline void mmio_writew(uint16_t val, void *addr)
+{
+       *(volatile uint16_t *) addr = val;
+}
+
+static inline void mmio_writel(uint32_t val, void *addr)
+{
+       *(volatile uint32_t *) addr = val;
+}
+
+static inline uint8_t mmio_readb(void *addr)
+{
+       return *(volatile uint8_t *) addr;
+}
+
+static inline uint16_t mmio_readw(void *addr)
+{
+       return *(volatile uint16_t *) addr;
+}
+
+static inline uint32_t mmio_readl(void *addr)
+{
+       return *(volatile uint32_t *) addr;
+}
+
+
+/*
+ * physmap.c
+ */
+
+static inline void *physmap(const char *descr, unsigned long phys_addr,
+                            size_t len)
+{
+       return phys_to_virt(phys_addr);
+}
+
+static inline void physunmap(void *virt_addr, size_t len)
+{
+}
+
+
+/*
+ * internal.c
+ */
+
+static struct pci_dev *pci_dev_find(uint16_t vendor, uint16_t device)
+{
+       static struct pci_access *pacc;
+       struct pci_dev *temp;
+       struct pci_filter filter;
+
+       if (pacc == NULL) {
+               pacc = pci_alloc();     /* Get the pci_access structure */
+               pci_init(pacc);         /* Initialize the PCI library */
+               pci_scan_bus(pacc);     /* We want to get the list of devices */
+       }
+
+       pci_filter_init(NULL, &filter);
+       filter.vendor = vendor;
+       filter.device = device;
+
+       for (temp = pacc->devices; temp; temp = temp->next)
+               if (pci_filter_match(&filter, temp))
+                       return temp;
+
+       return NULL;
+}
+
+
+/*
+ * flashchips.c
+ */
+
+
+/**
+ * List of supported flash chips.
+ *
+ * Please keep the list sorted by vendor name and chip name, so that
+ * the output of 'flashrom -L' is alphabetically sorted.
+ */
+static struct flashchip flashchips[] = {
+       {
+               .vendor         = "Atmel",
+               .name           = "AT26DF081A",
+               .bustype        = CHIP_BUSTYPE_SPI,
+               .manufacture_id = ATMEL_ID,
+               .model_id       = ATMEL_AT26DF081A,
+               .total_size     = 1024,
+               .page_size      = 256,
+               .feature_bits   = FEATURE_WRSR_WREN,
+               .tested         = TEST_OK_PREW,
+               .probe          = probe_spi_rdid,
+               .probe_timing   = TIMING_ZERO,
+#if 0                  /* not needed */
+               .block_erasers  =
+               {
+                       {
+                               .eraseblocks = { {4 * 1024, 256} },
+                               .block_erase = spi_block_erase_20,
+                       }, {
+                               .eraseblocks = { {32 * 1024, 32} },
+                               .block_erase = spi_block_erase_52,
+                       }, {
+                               .eraseblocks = { {64 * 1024, 16} },
+                               .block_erase = spi_block_erase_d8,
+                       }, {
+                               .eraseblocks = { {1024 * 1024, 1} },
+                               .block_erase = spi_block_erase_60,
+                       }, {
+                               .eraseblocks = { {1024 * 1024, 1} },
+                               .block_erase = spi_block_erase_c7,
+                       }
+               },
+               .unlock         = spi_disable_blockprotect_at25df,
+#endif
+               .lock           = spi_enable_blockprotect_at25df,
+#if 0                  /* not needed */
+               .write          = spi_chip_write_256,
+               .read           = spi_chip_read,
+#endif
+       },
+
+       { NULL  }
+};
+
+
+/*
+ * flashrom.c
+ */
+
+/* Supported buses for the current programmer. */
+static enum chipbustype buses_supported;
+
+/* Is writing allowed with this programmer? */
+static int programmer_may_write;
+
+/* If nonzero, used as the start address of bottom-aligned flash. */
+static unsigned long flashbase;
+
+static inline void programmer_delay(int usecs)
+{
+       udelay(usecs);
+}
+
+static struct flashchip *probe_flash(struct flashchip *first_flash, int force)
+{
+       struct flashchip *flash;
+       unsigned long base = 0;
+       char location[64];
+       uint32_t size;
+       enum chipbustype buses_common;
+
+       for (flash = first_flash; flash && flash->name; flash++) {
+               msg_gdbg("Probing for %s %s, %d KB: ",
+                            flash->vendor, flash->name, flash->total_size);
+               if (!flash->probe && !force) {
+                       msg_gdbg("failed! flashrom has no probe function for "
+                                "this flash chip.\n");
+                       continue;
+               }
+               buses_common = buses_supported & flash->bustype;
+               if (!buses_common) {
+                       msg_gdbg("skipped.");
+                       msg_gspew(" Host bus type and chip bus type are 
incompatible.");
+                       msg_gdbg("\n");
+                       continue;
+               }
+
+               size = flash->total_size * 1024;
+
+               base = flashbase ? flashbase : (0xffffffff - size + 1);
+               flash->virtual_memory = (chipaddr)physmap("flash chip", base, 
size);
+
+               if (force)
+                       break;
+
+               if (flash->probe(flash) != 1)
+                       goto notfound;
+
+               if (first_flash == flashchips
+                   || flash->model_id != GENERIC_DEVICE_ID)
+                       break;
+
+notfound:
+               physunmap((void *)flash->virtual_memory, size);
+       }
+
+       if (!flash || !flash->name)
+               return NULL;
+
+       snprintf(location, sizeof(location), "at physical address 0x%lx", base);
+
+       msg_cinfo("%s chip \"%s %s\" (%d KB) %s.\n",
+              force ? "Assuming" : "Found",
+              flash->vendor, flash->name, flash->total_size, location);
+
+       /* Flash registers will not be mapped if the chip was forced. Lock info
+        * may be stored in registers, so avoid lock info printing.
+        */
+       if (!force)
+               if (flash->printlock)
+                       flash->printlock(flash);
+
+       return flash;
+}
+
+
+/*
+ * sb600spi.c
+ */
+
+static uint8_t *sb600_spibar = NULL;
+
+static int sb600_spi_read(struct flashchip *flash, uint8_t *buf, int start, 
int len)
+{
+       /* not needed */
+       msg_perr("%s: unsupported!\n", __func__);
+       return SPI_PROGRAMMER_ERROR;
+}
+
+static int sb600_spi_write_256(struct flashchip *flash, uint8_t *buf, int 
start, int len)
+{
+       /* not needed */
+       msg_perr("%s: unsupported!\n", __func__);
+       return SPI_PROGRAMMER_ERROR;
+}
+
+static void reset_internal_fifo_pointer(void)
+{
+       mmio_writeb(mmio_readb(sb600_spibar + 2) | 0x10, sb600_spibar + 2);
+
+       /* FIXME: This loop makes no sense at all. */
+       while (mmio_readb(sb600_spibar + 0xD) & 0x7)
+               msg_pspew("reset\n");
+}
+
+static int compare_internal_fifo_pointer(uint8_t want)
+{
+       uint8_t tmp;
+
+       tmp = mmio_readb(sb600_spibar + 0xd) & 0x07;
+       want &= 0x7;
+       if (want != tmp) {
+               msg_perr("SB600 FIFO pointer corruption! Pointer is %d, wanted "
+                        "%d\n", tmp, want);
+               msg_perr("Something else is accessing the flash chip and "
+                        "causes random corruption.\nPlease stop all "
+                        "applications and drivers and IPMI which access the "
+                        "flash chip.\n");
+               return 1;
+       } else {
+               msg_pspew("SB600 FIFO pointer is %d, wanted %d\n", tmp, want);
+               return 0;
+       }
+}
+
+static int reset_compare_internal_fifo_pointer(uint8_t want)
+{
+       int ret;
+
+       ret = compare_internal_fifo_pointer(want);
+       reset_internal_fifo_pointer();
+       return ret;
+}
+
+static void execute_command(void)
+{
+       mmio_writeb(mmio_readb(sb600_spibar + 2) | 1, sb600_spibar + 2);
+
+       while (mmio_readb(sb600_spibar + 2) & 1)
+               ;
+}
+
+static int sb600_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+                     const unsigned char *writearr, unsigned char *readarr)
+{
+       int count;
+       /* First byte is cmd which can not being sent through FIFO. */
+       unsigned char cmd = *writearr++;
+       unsigned int readoffby1;
+       unsigned char readwrite;
+
+       writecnt--;
+
+       msg_pspew("%s, cmd=%x, writecnt=%x, readcnt=%x\n",
+                 __func__, cmd, writecnt, readcnt);
+
+       if (readcnt > 8) {
+               msg_pinfo("%s, SB600 SPI controller can not receive %d bytes, "
+                      "it is limited to 8 bytes\n", __func__, readcnt);
+               return SPI_INVALID_LENGTH;
+       }
+
+       if (writecnt > 8) {
+               msg_pinfo("%s, SB600 SPI controller can not send %d bytes, "
+                      "it is limited to 8 bytes\n", __func__, writecnt);
+               return SPI_INVALID_LENGTH;
+       }
+
+       /* This is a workaround for a bug in SB600 and SB700. If we only send
+        * an opcode and no additional data/address, the SPI controller will
+        * read one byte too few from the chip. Basically, the last byte of
+        * the chip response is discarded and will not end up in the FIFO.
+        * It is unclear if the CS# line is set high too early as well.
+        */
+       readoffby1 = (writecnt) ? 0 : 1;
+       readwrite = (readcnt + readoffby1) << 4 | (writecnt);
+       mmio_writeb(readwrite, sb600_spibar + 1);
+       mmio_writeb(cmd, sb600_spibar + 0);
+
+       /* Before we use the FIFO, reset it first. */
+       reset_internal_fifo_pointer();
+
+       /* Send the write byte to FIFO. */
+       msg_pspew("Writing: ");
+       for (count = 0; count < writecnt; count++, writearr++) {
+               msg_pspew("[%02x]", *writearr);
+               mmio_writeb(*writearr, sb600_spibar + 0xC);
+       }
+       msg_pspew("\n");
+
+       /*
+        * We should send the data by sequence, which means we need to reset
+        * the FIFO pointer to the first byte we want to send.
+        */
+       if (reset_compare_internal_fifo_pointer(writecnt))
+               return SPI_PROGRAMMER_ERROR;
+
+       msg_pspew("Executing: \n");
+       execute_command();
+
+       /*
+        * After the command executed, we should find out the index of the
+        * received byte. Here we just reset the FIFO pointer and skip the
+        * writecnt.
+        * It would be possible to increase the FIFO pointer by one instead
+        * of reading and discarding one byte from the FIFO.
+        * The FIFO is implemented on top of an 8 byte ring buffer and the
+        * buffer is never cleared. For every byte that is shifted out after
+        * the opcode, the FIFO already stores the response from the chip.
+        * Usually, the chip will respond with 0x00 or 0xff.
+        */
+       if (reset_compare_internal_fifo_pointer(writecnt + readcnt))
+               return SPI_PROGRAMMER_ERROR;
+
+       /* Skip the bytes we sent. */
+       msg_pspew("Skipping: ");
+       for (count = 0; count < writecnt; count++) {
+               cmd = mmio_readb(sb600_spibar + 0xC);
+               msg_pspew("[%02x]", cmd);
+       }
+       msg_pspew("\n");
+       if (compare_internal_fifo_pointer(writecnt))
+               return SPI_PROGRAMMER_ERROR;
+
+       msg_pspew("Reading: ");
+       for (count = 0; count < readcnt; count++, readarr++) {
+               *readarr = mmio_readb(sb600_spibar + 0xC);
+               msg_pspew("[%02x]", *readarr);
+       }
+       msg_pspew("\n");
+       if (reset_compare_internal_fifo_pointer(readcnt + writecnt))
+               return SPI_PROGRAMMER_ERROR;
+
+       if (mmio_readb(sb600_spibar + 1) != readwrite) {
+               msg_perr("Unexpected change in SB600 read/write count!\n");
+               msg_perr("Something else is accessing the flash chip and "
+                        "causes random corruption.\nPlease stop all "
+                        "applications and drivers and IPMI which access the "
+                        "flash chip.\n");
+               return SPI_PROGRAMMER_ERROR;
+       }
+
+       return 0;
+}
+
+static int sb600_probe_spi(struct pci_dev *dev)
+{
+       struct pci_dev *smbus_dev;
+       uint32_t tmp;
+       uint8_t reg;
+       static const char *const speed_names[4] = {
+               "Reserved", "33", "22", "16.5"
+       };
+
+       /* Read SPI_BaseAddr */
+       tmp = pci_read_long(dev, 0xa0);
+       tmp &= 0xffffffe0;      /* remove bits 4-0 (reserved) */
+       msg_pdbg("SPI base address is at 0x%x\n", tmp);
+
+       /* If the BAR has address 0, it is unlikely SPI is used. */
+       if (!tmp)
+               return 0;
+
+       /* Physical memory has to be mapped at page (4k) boundaries. */
+       sb600_spibar = physmap("SB600 SPI registers", tmp & 0xfffff000,
+                              0x1000);
+       /* The low bits of the SPI base address are used as offset into
+        * the mapped page.
+        */
+       sb600_spibar += tmp & 0xfff;
+
+       tmp = pci_read_long(dev, 0xa0);
+       msg_pdbg("AltSpiCSEnable=%i, SpiRomEnable=%i, "
+                    "AbortEnable=%i\n", tmp & 0x1, (tmp & 0x2) >> 1,
+                    (tmp & 0x4) >> 2);
+       tmp = (pci_read_byte(dev, 0xba) & 0x4) >> 2;
+       msg_pdbg("PrefetchEnSPIFromIMC=%i, ", tmp);
+
+       tmp = pci_read_byte(dev, 0xbb);
+       /* FIXME: Set bit 3,6,7 if not already set.
+        * Set bit 5, otherwise SPI accesses are pointless in LPC mode.
+        * See doc 42413 AMD SB700/710/750 RPR.
+        */
+       msg_pdbg("PrefetchEnSPIFromHost=%i, SpiOpEnInLpcMode=%i\n",
+                    tmp & 0x1, (tmp & 0x20) >> 5);
+       tmp = mmio_readl(sb600_spibar);
+       /* FIXME: If SpiAccessMacRomEn or SpiHostAccessRomEn are zero on
+        * SB700 or later, reads and writes will be corrupted. Abort in this
+        * case. Make sure to avoid this check on SB600.
+        */
+       msg_pdbg("SpiArbEnable=%i, SpiAccessMacRomEn=%i, "
+                    "SpiHostAccessRomEn=%i, ArbWaitCount=%i, "
+                    "SpiBridgeDisable=%i, DropOneClkOnRd=%i\n",
+                    (tmp >> 19) & 0x1, (tmp >> 22) & 0x1,
+                    (tmp >> 23) & 0x1, (tmp >> 24) & 0x7,
+                    (tmp >> 27) & 0x1, (tmp >> 28) & 0x1);
+       tmp = (mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3;
+       msg_pdbg("NormSpeed is %s MHz\n", speed_names[tmp]);
+
+       /* Look for the SMBus device. */
+       smbus_dev = pci_dev_find(0x1002, 0x4385);
+
+       if (!smbus_dev) {
+               msg_perr("ERROR: SMBus device not found. Not enabling SPI.\n");
+               return ERROR_NONFATAL;
+       }
+
+       /* Note about the bit tests below: If a bit is zero, the GPIO is SPI. */
+       /* GPIO11/SPI_DO and GPIO12/SPI_DI status */
+       reg = pci_read_byte(smbus_dev, 0xAB);
+       reg &= 0xC0;
+       msg_pdbg("GPIO11 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_DO");
+       msg_pdbg("GPIO12 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_DI");
+       if (reg != 0x00) {
+               msg_pdbg("Not enabling SPI");
+               return 0;
+       }
+       /* GPIO31/SPI_HOLD and GPIO32/SPI_CS status */
+       reg = pci_read_byte(smbus_dev, 0x83);
+       reg &= 0xC0;
+       msg_pdbg("GPIO31 used for %s\n", (reg & (1 << 6)) ? "GPIO" : 
"SPI_HOLD");
+       msg_pdbg("GPIO32 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_CS");
+       /* SPI_HOLD is not used on all boards, filter it out. */
+       if ((reg & 0x80) != 0x00) {
+               msg_pdbg("Not enabling SPI");
+               return 0;
+       }
+       /* GPIO47/SPI_CLK status */
+       reg = pci_read_byte(smbus_dev, 0xA7);
+       reg &= 0x40;
+       msg_pdbg("GPIO47 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_CLK");
+       if (reg != 0x00) {
+               msg_pdbg("Not enabling SPI");
+               return 0;
+       }
+
+       reg = pci_read_byte(dev, 0x40);
+       msg_pdbg("SB700 IMC is %sactive.\n", (reg & (1 << 7)) ? "" : "not ");
+       if (reg & (1 << 7)) {
+               /* If we touch any region used by the IMC, the IMC and the SPI
+                * interface will lock up, and the only way to recover is a
+                * hard reset, but that is a bad choice for a half-erased or
+                * half-written flash chip.
+                * There appears to be an undocumented register which can freeze
+                * or disable the IMC, but for now we want to play it safe.
+                */
+               msg_perr("The SB700 IMC is active and may interfere with SPI "
+                        "commands. Disabling write.\n");
+               /* FIXME: Should we only disable SPI writes, or will the lockup
+                * affect LPC/FWH chips as well?
+                */
+               programmer_may_write = 0;
+       }
+
+       /* Bring the FIFO to a clean state. */
+       reset_internal_fifo_pointer();
+
+       buses_supported |= CHIP_BUSTYPE_SPI;
+       spi_controller = SPI_CONTROLLER_SB600;
+       return 0;
+}
+
+
+/*
+ * chipset_enable.c 
+ */
+
+static int enable_flash_sb600(struct pci_dev *dev)
+{
+       uint32_t prot;
+       uint8_t reg;
+       int ret;
+
+       /* Clear ROM protect 0-3. */
+       for (reg = 0x50; reg < 0x60; reg += 4) {
+               prot = pci_read_long(dev, reg);
+               /* No protection flags for this region?*/
+               if ((prot & 0x3) == 0)
+                       continue;
+               msg_pinfo("SB600 %s%sprotected from 0x%08x to 0x%08x\n",
+                       (prot & 0x1) ? "write " : "",
+                       (prot & 0x2) ? "read " : "",
+                       (prot & 0xfffff800),
+                       (prot & 0xfffff800) + (((prot & 0x7fc) << 8) | 0x3ff));
+               prot &= 0xfffffffc;
+               rpci_write_byte(dev, reg, prot);
+               prot = pci_read_long(dev, reg);
+               if (prot & 0x3)
+                       msg_perr("SB600 %s%sunprotect failed from 0x%08x to 
0x%08x\n",
+                               (prot & 0x1) ? "write " : "",
+                               (prot & 0x2) ? "read " : "",
+                               (prot & 0xfffff800),
+                               (prot & 0xfffff800) + (((prot & 0x7fc) << 8) | 
0x3ff));
+       }
+
+       buses_supported = CHIP_BUSTYPE_LPC | CHIP_BUSTYPE_FWH;
+
+       ret = sb600_probe_spi(dev);
+
+       /* Read ROM strap override register. */
+       OUTB(0x8f, 0xcd6);
+       reg = INB(0xcd7);
+       reg &= 0x0e;
+       msg_pdbg("ROM strap override is %sactive", (reg & 0x02) ? "" : "not ");
+       if (reg & 0x02) {
+               switch ((reg & 0x0c) >> 2) {
+               case 0x00:
+                       msg_pdbg(": LPC");
+                       break;
+               case 0x01:
+                       msg_pdbg(": PCI");
+                       break;
+               case 0x02:
+                       msg_pdbg(": FWH");
+                       break;
+               case 0x03:
+                       msg_pdbg(": SPI");
+                       break;
+               }
+       }
+       msg_pdbg("\n");
+
+       /* Force enable SPI ROM in SB600 PM register.
+        * If we enable SPI ROM here, we have to disable it after we leave.
+        * But how can we know which ROM we are going to handle? So we have
+        * to trade off. We only access LPC ROM if we boot via LPC ROM. And
+        * only SPI ROM if we boot via SPI ROM. If you want to access SPI on
+        * boards with LPC straps, you have to use the code below.
+        */
+       /*
+       OUTB(0x8f, 0xcd6);
+       OUTB(0x0e, 0xcd7);
+       */
+
+       return ret;
+}
+
+
+int sb600_spi_lockdown(void) {
+       struct flashchip *flash;
+       struct pci_dev *dev;
+       int res;
+
+       dev = pci_dev_find(0x1002, 0x438d);
+       if (!dev) {
+               debug("Failed to find SB600 LPC bridge!\n");
+
+               return -1;
+       }
+
+       enable_flash_sb600(dev);
+
+       if (spi_controller == SPI_CONTROLLER_NONE) {
+               debug("Failed to enable SPI controller!\n");
+
+               return -1;
+       }
+
+       flash = probe_flash(flashchips, 0);
+       if (!flash) {
+               debug("Failed to find flash chip!\n");
+
+               return -1;
+       }
+
+       if (!flash->lock) {
+               debug("Flash chip cannot be locked!\n");
+
+               return -1;
+       }
+
+       if (flash->lock(flash)) {
+               debug("Failed to lock flash chip (already locked?)!\n");
+
+               /* signal this case differently, the chip may already be locked 
*/
+               return 1;
+       }
+
+       return 0;
+}
diff --git a/x86/linux_load.c b/x86/linux_load.c
index 01e185e..a4eba5b 100644
--- a/x86/linux_load.c
+++ b/x86/linux_load.c
@@ -680,11 +680,19 @@ static void hardware_setup(void)
 #ifdef CONFIG_FLASHROM_LOCKDOWN
        /* lockdown flashROM */
        extern int flashrom_lockdown;
-       extern void intel_lockdown_flash();
+       extern int intel_lockdown_flash(void);
+       extern int amd_lockdown_flash(void);
 
        if (flashrom_lockdown) {
-               printf("Locking FlashROM...\n");
-               intel_lockdown_flash();
+               printf("Locking FlashROM...");
+               if (intel_lockdown_flash() == 0) {
+                       printf("done (Intel)\n");
+               } else if (amd_lockdown_flash() == 0) {
+                       printf("done (AMD)\n");
+               } else {
+                       printf("FAILED!\n");
+                       delay(5);
+               }
        } else {
                printf("Leaving FlashROM unlocked...\n");
        }

-- 
coreboot mailing list: [email protected]
http://www.coreboot.org/mailman/listinfo/coreboot

Reply via email to