[flashrom] [PATCH] Merge the EC drivers from the chromium tree

Carl-Daniel Hailfinger c-d.hailfinger.devel.2006 at gmx.net
Mon Nov 22 23:54:31 CET 2010


The following patch was obtained from a locally modified
http://git.chromium.org/git/flashrom with
git diff 20636ae6fc4ac1e61b3a89ddc5f6e059034d8660
and then removing all hunks unrelated to ECs and by cleaning up the
Nuvoton code to use proper flashrom interfaces. On top of this I forward
ported the code to current flashrom svn HEAD.

No signoff because this patch is an amalgamation of ITE code, Nuvoton
code, Google code, and my code. The git history has the individual
signoffs except for my changes. This needs to be split into code
specific to each EC family, and then one patch for each EC needs to be
submitted with proper signoff.

The IT85 code is mostly ready, and might even work. It _will_ break all
boards which use IT87 flash translation. Once that is fixed (a few lines
in internal.c), the IT85 code might even be mergeable.
The WPCE775x code is not ready yet, and it is not even hooked up.

That said, I believe that this patch may be interesting for people who
want EC support in mainline flashrom and who are willing to review the code.

Index: flashrom-ec_it87_wpce775x/wpce775x.c
===================================================================
--- flashrom-ec_it87_wpce775x/wpce775x.c	(Revision 0)
+++ flashrom-ec_it87_wpce775x/wpce775x.c	(Revision 0)
@@ -0,0 +1,689 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Carl-Daniel Hailfinger
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met:
+ * 
+ * Redistributions of source code must retain the above copyright 
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright 
+ * notice, this list of conditions and the following disclaimer in the 
+ * documentation and/or other materials provided with the distribution.
+ * 
+ * Neither the name of Nuvoton Technology Corporation. or the names of 
+ * contributors or licensors may be used to endorse or promote products derived 
+ * from this software without specific prior written permission.
+ * 
+ * This software is provided "AS IS," without a warranty of any kind. 
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, 
+ * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
+ * NUVOTON TECHNOLOGY CORPORATION. ("NUVOTON") AND ITS LICENSORS SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
+ * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
+ * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
+ * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
+ * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
+ * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * This is an UNOFFICIAL patch for the Nuvoton WPCE775x/NPCE781x. It was tested
+ * for a specific hardware and firmware configuration and should be considered
+ * unreliable. Please see the following URL for Nuvoton's authoritative,
+ * officially supported flash update utility:
+ * http://sourceforge.net/projects/nuvflashupdate/
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+#include "flash.h"
+#include "chipdrivers.h"
+#include "flashchips.h"
+#include "programmer.h"
+#include "spi.h"
+
+/**
+ *  Definition of WPCE775X WCB (Write Command Buffer), as known as Shared Access
+ *  Window 2.
+ *
+ *  The document name is "WPCE775X Software User Guide Revision 1.2".
+ *
+ *  Assume the host is little endian.
+ */
+__attribute__((packed))
+struct wpce775x_wcb {
+	/* Byte 0: semaphore byte */
+	unsigned char exe:1;  /* Bit0-RW- set by host. means wcb is ready to execute.
+	                         should be cleared by host after RDY=1. */
+	unsigned char resv0_41:4;
+	unsigned char pcp:1;  /* Bit5-RO- set by EPCE775x. means preparation operations for
+	                         flash update process is complete. */
+	unsigned char err:1;  /* Bit6-RO- set by EPCE775x. means an error occurs. */
+	unsigned char rdy:1;  /* Bit7-RO- set by EPCE775x. means operation is completed. */
+
+	/* Byte 1-2: reserved */
+	unsigned char byte1;
+	unsigned char byte2;
+
+	/* Byte 3: command code */
+	unsigned char code;
+
+	/* Byte 4-15: command field */
+	unsigned char field[12];
+};
+
+/* The physical address of WCB -- Shared Access Window 2. */
+static chipaddr wcb_physical_address;
+
+/* The virtual address of WCB -- Shared Access Window 2. */
+static volatile struct wpce775x_wcb *volatile wcb;
+
+/* count of entering flash update mode */
+static int in_flash_update_mode;
+
+/* Indicate the flash chip attached to the WPCE7xxx chip.
+ * This variable should be set in probe_wpce775x().
+ * 0 means we haven't or cannot detect the chip type. */
+struct flashchip *scan = 0;
+
+/* SuperI/O related definitions and functions. */
+/* Strapping options */
+#define NUVOTON_SIO_PORT1	0x2e	/* No pull-down resistor */
+#define NUVOTON_SIO_PORT2	0x164e	/* Pull-down resistor on BADDR0 */
+/* Note: There's another funky state that we won't worry about right now */
+
+/* SuperI/O Config */
+#define NUVOTON_SIOCFG_LDN	0x07	/* LDN Bank Selector */
+#define NUVOTON_SIOCFG_SID	0x20	/* SuperI/O ID */
+#define NUVOTON_SIOCFG_SRID	0x27	/* SuperI/O Revision ID */
+#define NUVOTON_LDN_SHM         0x0f    /* LDN of SHM module */
+
+/* WPCE775x shared memory config registers (LDN 0x0f) */
+#define WPCE775X_SHM_BASE_MSB	0x60
+#define WPCE775X_SHM_BASE_LSB	0x61
+#define WPCE775X_SHM_CFG	0xf0
+#define WPCE775X_SHM_CFG_BIOS_FWH_EN	(1 << 3)
+#define WPCE775X_SHM_CFG_FLASH_ACC_EN	(1 << 2)
+#define WPCE775X_SHM_CFG_BIOS_EXT_EN	(1 << 1)
+#define WPCE775X_SHM_CFG_BIOS_LPC_EN	(1 << 0)
+#define WPCE775X_WIN_CFG		0xf1	/* window config */
+#define WPCE775X_WIN_CFG_SHWIN_ACC	(1 << 6)
+
+/* Shared access window 2 bar address registers */
+#define WPCE775X_SHAW2BA_0	0xf8
+#define WPCE775X_SHAW2BA_1	0xf9
+#define WPCE775X_SHAW2BA_2	0xfa
+#define WPCE775X_SHAW2BA_3	0xfb
+
+/** probe for super i/o index
+ *  @returns 0 to indicate success, <0 to indicate error
+ */
+static int nuvoton_get_sio_index(uint16_t *port)
+{
+	uint16_t ports[] = { NUVOTON_SIO_PORT2,
+	                     NUVOTON_SIO_PORT1,
+	};
+	int i;
+	static uint16_t port_internal, port_found = 0;
+
+	if (port_found) {
+		*port = port_internal;
+		return 0;
+	}
+
+	get_io_perms();
+
+	for (i = 0; i < ARRAY_SIZE(ports); i++) {
+		uint8_t sid = sio_read(ports[i], NUVOTON_SIOCFG_SID);
+
+		if (sid == 0xfc) {  /* Family ID */
+			port_internal = ports[i];
+			port_found = 1;
+			break;
+		}
+	}
+
+	if (!port_found) {
+		msg_cdbg("\nfailed to obtain super i/o index");
+		return -1;
+	}
+
+	msg_cdbg("\nsuper i/o index = 0x%04x\n", port_internal);
+	*port = port_internal;
+	return 0;
+}
+
+/** Call superio to get pre-configured WCB address.
+ *  Read LDN 0x0f (SHM) idx:f8-fb (little-endian).
+ */
+static int get_shaw2ba(chipaddr *shaw2ba, uint8_t *comm_fwh)
+{
+	uint16_t idx;
+	uint8_t org_ldn;
+	uint8_t win_cfg;
+	uint8_t shm_cfg;
+	int ret = 0;
+
+	if (nuvoton_get_sio_index(&idx) < 0)
+		return -1;
+
+	org_ldn = sio_read(idx, NUVOTON_SIOCFG_LDN);
+	sio_write(idx, NUVOTON_SIOCFG_LDN, NUVOTON_LDN_SHM);
+
+	/*
+	 * To obtain shared access window 2 base address, we must OR the base
+	 * address bytes, where SHAW2BA_0 is least significant and SHAW2BA_3
+	 * most significant.
+	 */
+	*shaw2ba = sio_read(idx, WPCE775X_SHAW2BA_0) |
+	           (sio_read(idx, WPCE775X_SHAW2BA_1) << 8) |
+	           (sio_read(idx, WPCE775X_SHAW2BA_2) << 16) |
+	           (sio_read(idx, WPCE775X_SHAW2BA_3) << 24);
+
+	/*
+	 * If SHWIN_ACC is cleared, then we're using LPC memory access
+	 * and SHAW2BA_3-0 indicate bits 31-0. If SHWIN_ACC is set, then
+	 * bits 7-4 of SHAW2BA_3 are ignored and bits 31-28 are indicated
+	 * by the idsel nibble. (See table 25 "supported host address ranges"
+	 * for more details)
+	 */
+	win_cfg = sio_read(idx, WPCE775X_WIN_CFG);
+	if (win_cfg & WPCE775X_WIN_CFG_SHWIN_ACC) {
+		/* Make sure shared BIOS memory is enabled */
+		shm_cfg = sio_read(idx, WPCE775X_SHM_CFG);
+		if (!(shm_cfg & WPCE775X_SHM_CFG_BIOS_FWH_EN)) {
+			msg_cdbg("Shared BIOS memory is diabled.\n");
+			msg_cdbg("Please check SHM_CFG:BIOS_FWH_EN.\n");
+			ret = -1;
+	                goto out;
+		}
+		/* FIXME: Check that this is a FWH capable chipset. */
+		*comm_fwh = 1;
+		/* Check if bit 24-27 are set. Intel ICH will never issue any
+		 * LPC firmware memory (FWH) cycles with them cleared.
+		 * FWH cycles only have 28 address bits.
+		 */
+		if ((*shaw2ba & (0xf << 24)) != 0xf << 24) {
+			msg_cerr("WPCE775X shadow window 2 is outside the "
+				 "region addressable with FWH!\n");
+			ret = -1;
+			goto out;
+		}
+		/* FWH cycles are only issued for memory mapped to the top
+		 * 16 MB of the 4 GB address space. We could set only the top
+		 * 4 bits since those are ignored by the WPCE775X and we checked
+		 * bits 24-27 to be 0xf above, but setting the top 8 bits makes
+		 * the 16 MB constraint more clear.
+		 */
+		*shaw2ba |= 0xff << 24;
+	} else {
+		*comm_fwh = 0;
+	}
+
+out:	
+	sio_write(idx, NUVOTON_SIOCFG_LDN, org_ldn);
+	return ret;
+}
+
+/* Call superio to get pre-configured fwh_id.
+ * Read LDN 0x0f (SHM) idx:f0.
+ */
+static int get_fwh_id(uint8_t *fwh_id)
+{
+	uint16_t idx;
+	uint8_t org_ldn;
+
+	if (nuvoton_get_sio_index(&idx) < 0)
+		return -1;
+
+	org_ldn = sio_read(idx, NUVOTON_SIOCFG_LDN);
+	sio_write(idx, NUVOTON_SIOCFG_LDN, NUVOTON_LDN_SHM);
+	/* Upper 4 bits of WPCE775X_SHM_CFG store the FWH ID. */
+	*fwh_id = sio_read(idx, WPCE775X_SHM_CFG) >> 4;
+	sio_write(idx, NUVOTON_SIOCFG_LDN, org_ldn);
+
+	return 0;
+}
+
+/** helper function to make sure the exe bit is 0 (no one is using EC).
+ *  @return 1 for error; 0 for success.
+ */
+static int assert_ec_is_free(void)
+{
+	if (wcb->exe)
+		msg_perr("ASSERT(wcb->exe==0), entering busy loop.\n");
+	while(wcb->exe);
+	return 0;
+}
+
+/** Trigger EXE bit, and block until operation completes.
+ *  @return 1 for error; and 0 for success.
+ */
+static int blocked_exec(void)
+{
+	struct timeval begin, now;
+	int timeout;  /* not zero if timeout occurs */
+	int err;
+
+	assert(wcb->rdy==0);
+
+	/* raise EXE bit, and wait for operation complete or error occur. */
+	wcb->exe = 1;
+
+	timeout = 0;
+	gettimeofday(&begin, NULL);
+	while(wcb->rdy==0 && wcb->err==0) {
+		gettimeofday(&now, NULL);
+		/* According to Nuvoton's suggestion, few seconds is enough for
+	         * longest flash operation, which is erase.
+		 * Cutted from W25X16 datasheet, for max operation time
+		 *   Byte program        tBP1  50us
+		 *   Page program        tPP    3ms
+		 *   Sector Erase (4KB)  tSE  200ms
+		 *   Block Erase (64KB)  tBE    1s
+		 *   Chip Erase          tCE   20s
+	         * Since WPCE775x doesn't support chip erase,
+		 * 3 secs is long enough for block erase.
+		 */
+		if ((now.tv_sec - begin.tv_sec) >= 4) {
+			timeout += 1;
+			break;
+		}
+	}
+
+	/* keep ERR bit before clearing EXE bit. */
+	err = wcb->err;
+
+	/* Clear EXE bit, and wait for RDY back to 0. */
+	wcb->exe = 0;
+	gettimeofday(&begin, NULL);
+	while(wcb->rdy) {
+		gettimeofday(&now, NULL);
+		/* 1 sec should be long enough for clearing rdy bit. */
+		if (((now.tv_sec - begin.tv_sec)*1000*1000 +
+	             (now.tv_usec - begin.tv_usec)) >= 1000*1000) {
+			timeout += 1;
+			break;
+		}
+	}
+
+	if (err || timeout) {
+		msg_cdbg("err=%d timeout=%d\n", err, timeout);
+		return 1;
+	}
+	return 0;
+}
+
+/** Initialize the EC parameters.
+ *  @return 1 for error; 0 for success.
+ */
+static int InitFlash(unsigned char srp)
+{
+	assert_ec_is_free();
+
+	/* Byte 3: command code: Init Flash */
+	wcb->code = 0x5A;
+
+	/* Byte 4: opcode for Read Device Id */
+	wcb->field[0] = JEDEC_RDID;
+
+	/* Byte 5: opcode for Write Status Enable
+	           JEDEC_EWSR defines 0x50, but W25Q16
+	           accepts JEDEC_WREN to enable write status */
+	wcb->field[1] = JEDEC_WREN;
+
+	/* Byte 6: opcode for Write Enable */
+	wcb->field[2] = JEDEC_WREN;
+
+	/* Byte 7: opcode for Read Status Register */
+	wcb->field[3] = JEDEC_RDSR;
+
+	/* Byte 8: opcode for Write Status Register */
+	wcb->field[4] = JEDEC_WRSR;
+
+	/* Byte 9: opcode for Flash Program */
+	wcb->field[5] = JEDEC_BYTE_PROGRAM;
+
+	/* Byte A: opcode for Sector or Block Erase. 0xD8: Block Erase (64KB), 0x20: Sector Erase (4KB) */
+	/* TODO: dhendrix: We may need a more sophisticated routine to determine the proper values on a chip-by-chip basis in the future. */
+	wcb->field[6] = JEDEC_SE;
+
+	/* Byte B: status busy mask */
+	wcb->field[7] = 0x01;
+
+	/* Byte C: Status Register Value */
+	wcb->field[8] = srp;  /* SRP (Status Register Protect), {TB, BP2, BP1, BP0} = {1, 0, 1, 1} */
+
+	/* Byte D: Program Unit Size */
+	wcb->field[9] = 0x01;
+
+	/* Byte E: Page Size, 2^X bytes */
+	wcb->field[10] = 0x08;
+
+	/* Byte F: Read Device ID Type. 0x47 -- send 3 dummy addresses before read ID from flash */
+	wcb->field[11] = 0x00;
+
+	if (blocked_exec())
+		return 1;
+	return 0;
+}
+
+/** Read flash vendor/device IDs through EC.
+ *  @param readarr 4-byte buffer for JEDEC_RDID response.
+ *  @return 1 for error; 0 for success.
+ */
+static int ReadId(unsigned char *readarr)
+{
+	assert_ec_is_free();
+
+	wcb->code = 0xC0;       /* Byte 3: command code: Read ID */
+	if (blocked_exec())
+		return 1;
+
+	readarr[0] = wcb->field[0];
+	readarr[1] = wcb->field[1];
+	readarr[2] = wcb->field[2];
+	readarr[3] = wcb->field[3];
+
+	return 0;
+}
+
+/** probe if WPCE775x is present.
+ *  @return 1 for error; 0 for success
+ */
+int wpce775x_init(void)
+{
+	uint16_t sio_port;
+	uint8_t srid;
+	uint8_t fwh_id;
+	uint8_t comm_fwh = 0;
+
+	/* detect if wpce775x exists */
+	if (nuvoton_get_sio_index(&sio_port) < 0) {
+		msg_cdbg("No Nuvoton chip is found.\n");
+		/* No chip found is not an error. */
+		return 0;
+	}
+	srid = sio_read(sio_port, NUVOTON_SIOCFG_SRID);
+	if ((srid & 0xE0) == 0xA0) {
+		msg_pdbg("Found EC: WPCE775x (Vendor:0x%02x,ID:0x%02x,Rev:0x%02x) on sio_port:0x%x.\n",
+		         sio_read(sio_port, NUVOTON_SIOCFG_SID),
+		         srid >> 5, srid & 0x1f, sio_port);
+
+	} else {
+		msg_pdbg("Found EC: Nuvoton (Vendor:0x%02x,ID:0x%02x,Rev:0x%02x) on sio_port:0x%x.\n",
+		         sio_read(sio_port, NUVOTON_SIOCFG_SID),
+		         srid >> 5, srid & 0x1f, sio_port);
+	}
+
+	/* get the address of Shadow Window 2. */
+	if (get_shaw2ba(&wcb_physical_address, &comm_fwh) < 0) {
+		msg_cdbg("Cannot get the address of Shadow Window 2");
+		return 1;
+	}
+	msg_cdbg("Get the address of WCB(SHA WIN2) at 0x%08x\n",
+	         (uint32_t)wcb_physical_address);
+
+	/* If we use FWH for Shadow Window 2, we have to set FWH IDSEL in the
+	 * chipset before we try to access Shadow Window 2.
+	 */
+	if (comm_fwh) {
+		if (get_fwh_id(&fwh_id) < 0) {
+			msg_cdbg("Cannot get fwh_id value.\n");
+			return 1;
+		}
+		msg_cdbg("get fwh_id: 0x%02x\n", fwh_id);
+
+		/* TODO: set fwh_idsel of chipset.
+	         * Currently, we employ "-p internal:fwh_idsel=0x0000223e".
+		 */
+	}
+
+	wcb = (struct wpce775x_wcb *)physmap("WPCE775X WCB",
+	                                  wcb_physical_address,
+	                                  getpagesize() /* min page size */);
+	msg_cdbg("mapped wcb address: %p for physical addr: 0x%08lx\n", wcb, wcb_physical_address);
+	if (!wcb) {
+		msg_perr("FATAL! Cannot map memory area for wcb physical address.\n");
+		return 1;
+	}
+	memset((void*)wcb, 0, sizeof(*wcb));
+
+	/* Initialize the parameters of EC SHM component */
+	if (InitFlash(0x00))
+		return 1;
+
+	if (buses_supported & CHIP_BUSTYPE_SPI)
+		msg_pdbg("Overriding chipset SPI with WPCE775x SPI.\n");
+	spi_controller = SPI_CONTROLLER_WPCE775XX;
+	buses_supported |= CHIP_BUSTYPE_SPI;
+	return 0;
+}
+
+/** Tell EC to "enter flash update" mode. */
+static int EnterFlashUpdate(void)
+{
+	if (in_flash_update_mode) {
+		/* already in update mode */
+		in_flash_update_mode++;
+		return 0;
+	}
+
+	wcb->code = 0x10;  /* Enter Flash Update */
+	wcb->field[0] = 0x55;  /* required pattern by EC */
+	wcb->field[1] = 0xAA;  /* required pattern by EC */
+	wcb->field[2] = 0xCD;  /* required pattern by EC */
+	wcb->field[3] = 0xBE;  /* required pattern by EC */
+	if (blocked_exec()) {
+		return 1;
+	} else {
+		in_flash_update_mode++;
+		return 0;
+	}
+}
+
+/** Tell EC to "exit flash update" mode.
+ *  Without calling this function, the EC stays in busy-loop and will not
+ *  response further request from host, which means system will halt.
+ */
+static int ExitFlashUpdate(unsigned char exit_code)
+{
+	if (in_flash_update_mode <= 0) {
+		msg_cdbg("Not in flash update mode yet.\n");
+		return 1;
+	}
+
+	if (in_flash_update_mode >= 2) {
+		in_flash_update_mode--;
+		return 0;
+	}
+
+	wcb->code = exit_code;  /* Exit Flash Update */
+	if (blocked_exec()) {
+		return 1;
+	} else {
+		in_flash_update_mode--;
+		return 0;
+	}
+}
+
+/*
+ * Note: The EC firmware this patch has been tested with uses the following
+ * codes to indicate flash update status:
+ * 0x20 is used for EC F/W no change, but BIOS changed (in Share mode)
+ * 0x21 is used for EC F/W changed. Goto EC F/W, wait system reboot.
+ * 0x22 is used for EC F/W changed, Goto EC Watchdog reset. */
+static int ExitFlashUpdateFirmwareNoChange(void) {
+	return ExitFlashUpdate(0x20);
+}
+
+static int ExitFlashUpdateFirmwareChanged(void) {
+	return ExitFlashUpdate(0x21);
+}
+
+static int wpce775x_set_write_window(unsigned int base, unsigned int size)
+{
+	assert_ec_is_free();
+
+	/* Set Write Window on flash chip (optional).
+	 * You may limit the window to partial flash for experimental. */
+	wcb->code = 0xC5;  /* Set Write Window */
+	wcb->field[0] = base & 0xff;  /* window base: little-endian */
+	wcb->field[1] = (base >> 8) & 0xff;
+	wcb->field[2] = (base >> 16) & 0xff;
+	wcb->field[3] = (base >> 24) & 0xff;
+	wcb->field[4] = size & 0xff;  /* window length: little-endian */
+	wcb->field[5] = (size >> 8) & 0xff;
+	wcb->field[6] = (size >> 16) & 0xff;
+	wcb->field[7] = (size >> 24) & 0xff;
+	if (blocked_exec())
+		return 1;
+	return 0;
+}
+
+static int handle_erase_wpce775x(unsigned int blockaddr)
+{
+	/* FIXME: A 2 MByte window is too small. */
+	if (wpce775x_set_write_window(0x0, 0x200000))
+		return 1;
+
+	assert_ec_is_free();
+
+	if (EnterFlashUpdate())
+		return 1;
+
+	wcb->code = 0x80;  /* Sector/block erase */
+	/* WARNING: assume the block address for EC is always little-endian. */
+	wcb->field[0] = blockaddr & 0xff;
+	wcb->field[1] = (blockaddr >> 8) & 0xff;
+	wcb->field[2] = (blockaddr >> 16) & 0xff;
+	wcb->field[3] = (blockaddr >> 24) & 0xff;
+	if (blocked_exec())
+		goto error_exit;
+
+	if (ExitFlashUpdateFirmwareChanged())
+		return 1;
+
+	return 0;
+
+error_exit:
+	ExitFlashUpdateFirmwareChanged();
+	return 1;
+}
+
+/**
+ * Page write helper
+ */
+static int write_wpce775x_entry(unsigned long writeaddr, unsigned long writelen, const unsigned char *buf)
+{
+	if (writelen > 8) {
+		msg_perr("WTF did you call %s with writelen=%li\n", __func__, writelen);
+		return 1;
+	}
+
+	/* FIXME: assert_ec_is_free() missing? */
+	if (EnterFlashUpdate())
+		return 1;
+
+	/* wpce775x provides a 8-byte program buffer. */
+	wcb->code = 0xA0;  /* Set Address */
+	wcb->field[0] = writeaddr & 0xff;
+	wcb->field[1] = (writeaddr >> 8) & 0xff;
+	wcb->field[2] = (writeaddr >> 16) & 0xff;
+	wcb->field[3] = (writeaddr >> 24) & 0xff;
+	if (blocked_exec())
+		goto error_exit;
+
+	/* 8 is maximum buffer size */
+	wcb->code = 0xB0 | writelen;
+	int i;
+	for (i = 0; i < writelen; i++) {
+		wcb->field[i] = buf[i];
+	}
+	if (blocked_exec())
+		goto error_exit;
+
+	if (ExitFlashUpdateFirmwareChanged()) return 1;
+	return 0;
+
+error_exit:
+	ExitFlashUpdateFirmwareChanged();
+	return 1;
+}
+
+static int wpce775x_wrsr(const unsigned char status) {
+	/* InitFlash (with particular status value), and EnterFlashUpdate() then
+	 * ExitFlashUpdate() immediately. Thus, the flash status register will
+	 * be updated. */
+	if (InitFlash(status))
+		return -1;
+	if (EnterFlashUpdate()) return 1;
+		ExitFlashUpdateFirmwareNoChange();
+
+	return 0;
+}
+
+int wpce775x_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+			const unsigned char *writearr, unsigned char *readarr)
+{
+	unsigned char ids[4];
+	unsigned int addr = 0;
+
+	/* Ugly hack. Not my fault. -- Carl-Daniel */
+	switch(writearr[0]) {
+	case JEDEC_RDID:
+		/* We always read 4 ID bytes. Return only the wanted parts. */
+		if (ReadId(ids))
+			return 1;
+		memcpy(readarr, ids, min(readcnt, 4));
+		break;
+	case JEDEC_BYTE_PROGRAM:
+		/* FIXME: Precedence! */
+		addr = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+		if (write_wpce775x_entry(addr, writecnt - 4, writearr + 4))
+			return 1;
+		break;
+	case JEDEC_SE:
+		/* FIXME: Precedence! */
+		addr = writearr[1] << 16 | writearr[2] << 8 | writearr[3];
+		if (handle_erase_wpce775x(addr))
+			return 1;
+		break;
+	case JEDEC_WRSR:
+		if (wpce775x_wrsr(writearr[1]))
+			return 1;
+		break;
+	case JEDEC_WREN:
+	case JEDEC_RDSR:
+	default:
+		msg_perr("You are truly screwed. This command is not "
+			 "implemented (yet)\n");
+		return 1;
+	}
+	return 0;	
+}
+
+/* FIXME: This command is not implemented. We have to use read_memmapped, but
+ * the mapping window is too small for reasonably sized chips.
+ */
+int wpce775x_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len)
+{
+	if (flash->total_size * 1024 > 2 * 1024 * 1024) {
+		msg_perr("Chip is too big for reading on wpce775x.\n");
+		return 1;
+	}
+	return read_memmapped(flash, buf, start, len);
+}
+
+int wpce775x_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
+{
+	return spi_write_chunked(flash, buf, start, len, 8);
+}
Index: flashrom-ec_it87_wpce775x/Makefile
===================================================================
--- flashrom-ec_it87_wpce775x/Makefile	(Revision 1234)
+++ flashrom-ec_it87_wpce775x/Makefile	(Arbeitskopie)
@@ -174,7 +174,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 mcp6x_spi.o
+PROGRAMMER_OBJS += it87spi.o it85spi.o ichspi.o sb600spi.o wbsio_spi.o mcp6x_spi.o wpce775x.o
 NEED_PCI := yes
 endif
 
Index: flashrom-ec_it87_wpce775x/internal.c
===================================================================
--- flashrom-ec_it87_wpce775x/internal.c	(Revision 1234)
+++ flashrom-ec_it87_wpce775x/internal.c	(Arbeitskopie)
@@ -104,6 +104,7 @@
 void probe_superio(void)
 {
 	superio = probe_superio_ite();
+	superio = probe_superio_ite85xx();
 #if 0
 	/* Winbond Super I/O code is not yet available. */
 	if (superio.vendor == SUPERIO_VENDOR_NONE)
@@ -234,6 +235,7 @@
 	 * IT87* Parallel write enable.
 	 */
 	init_superio_ite();
+	it85xx_probe_spi_flash(NULL);
 #endif
 
 	board_flash_enable(lb_vendor, lb_part);
@@ -268,6 +270,10 @@
 {
 	release_io_perms();
 
+#if defined(__i386__) || defined(__x86_64__)
+	it85xx_shutdown();
+#endif
+
 	return 0;
 }
 #endif
Index: flashrom-ec_it87_wpce775x/it85spi.c
===================================================================
--- flashrom-ec_it87_wpce775x/it85spi.c	(Revision 0)
+++ flashrom-ec_it87_wpce775x/it85spi.c	(Revision 0)
@@ -0,0 +1,403 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2007, 2008, 2009 Carl-Daniel Hailfinger
+ * Copyright (C) 2008 Ronald Hoogenboom <ronald at zonnet.nl>
+ * Copyright (C) 2008 coresystems GmbH
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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
+ */
+
+/*
+ * Contains the ITE IT85* SPI specific routines
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "flash.h"
+#include "chipdrivers.h"
+#include "spi.h"
+#include "programmer.h"
+
+#define MAX_TIMEOUT 100000
+#define MAX_TRY 5
+
+/* Constans for I/O ports */
+#define ITE_SUPERIO_PORT1	0x2e
+#define ITE_SUPERIO_PORT2	0x4e
+
+/* Legacy I/O */
+#define LEGACY_KBC_PORT_DATA	0x60
+#define LEGACY_KBC_PORT_CMD	0x64
+
+/* Constants for Logical Device registers */
+#define LDNSEL			0x07
+#define CHIP_ID_BYTE1_REG	0x20
+#define CHIP_ID_BYTE2_REG	0x21
+#define CHIP_CHIP_VER_REG	0x22
+
+/* These are standard Super I/O 16-bit base address registers */
+#define SHM_IO_BAD0		0x60  /* big-endian, this is high bits */
+#define SHM_IO_BAD1		0x61
+
+/* 8042 keyboard controller uses an input buffer and an output buffer to
+ * communicate with host CPU. Both buffers are 1-byte depth. That means the
+ * IBF is set to 1 when host CPU sends a command to input buffer (standing on
+ * the EC side). IBF is cleared to 0 once the command is read by EC. */
+#define KB_IBF 			(1 << 1)  /* Input Buffer Full */
+#define KB_OBF 			(1 << 0)  /* Output Buffer Full */
+
+/* IT8502 supports two access modes:
+ *   LPC_MEMORY: through the memory window in 0xFFFFFxxx (follow mode)
+ *   LPC_IO: through I/O port (so called indirect memory)
+ */
+#undef LPC_MEMORY
+#define LPC_IO
+
+#ifdef LPC_IO
+/* macro to fill in indirect-access registers. */
+#define INDIRECT_A0(base, value) OUTB(value, (base) + 0)  /* little-endian */
+#define INDIRECT_A1(base, value) OUTB(value, (base) + 1)
+#define INDIRECT_A2(base, value) OUTB(value, (base) + 2)
+#define INDIRECT_A3(base, value) OUTB(value, (base) + 3)
+#define INDIRECT_READ(base) INB((base) + 4)
+#define INDIRECT_WRITE(base, value) OUTB(value, (base) + 4)
+#endif  /* LPC_IO */
+
+#ifdef LPC_IO
+unsigned int shm_io_base;
+#endif
+unsigned char *ce_high, *ce_low;
+static int it85xx_scratch_rom_reenter = 0;
+
+uint16_t probe_id_ite85(uint16_t port)
+{
+	uint16_t id;
+
+	id = sio_read(port, CHIP_ID_BYTE1_REG) << 8 |
+	     sio_read(port, CHIP_ID_BYTE2_REG);
+
+	return id;
+}
+
+struct superio probe_superio_ite85xx(void)
+{
+	struct superio ret = {};
+	uint16_t ite_ports[] = {ITE_SUPERIO_PORT1, ITE_SUPERIO_PORT2, 0};
+	uint16_t *i = ite_ports;
+
+	ret.vendor = SUPERIO_VENDOR_ITE;
+	for (; *i; i++) {
+		ret.port = *i;
+		ret.model = probe_id_ite85(ret.port);
+		switch (ret.model >> 8) {
+		case 0x85:
+			msg_pdbg("Found EC: ITE85xx (Vendor:0x%02x,ID:0x%02x,"
+			         "Rev:0x%02x) on sio_port:0x%x.\n",
+			         ret.model >> 8, ret.model & 0xff,
+			         sio_read(ret.port, CHIP_CHIP_VER_REG),
+			         ret.port);
+			return ret;
+		}
+	}
+
+	/* No good ID found. */
+	ret.vendor = SUPERIO_VENDOR_NONE;
+	ret.port = 0;
+	ret.model = 0;
+	return ret;
+}
+
+/* This function will poll the keyboard status register until either
+ *   an expected value shows up, or
+ *   timeout reaches.
+ *
+ * Returns: 0 -- the expected value has shown.
+ *          1 -- timeout reached.
+ */
+static int wait_for(
+		const unsigned int mask,
+		const unsigned int expected_value,
+		const int timeout,  /* in usec */
+		const char* error_message,
+		const char* function_name,
+		const int lineno
+) {
+	int time_passed;
+
+	for (time_passed = 0;; ++time_passed) {
+		if ((INB(LEGACY_KBC_PORT_CMD) & mask) == expected_value)
+			return 0;
+		if (time_passed >= timeout)
+			break;
+		programmer_delay(1);
+	}
+	if (error_message)
+		msg_perr("%s():%d %s", function_name, lineno, error_message);
+	return 1;
+}
+
+/* IT8502 employs a scratch ram when flash is being updated. Call the following
+ * two functions before/after flash erase/program. */
+void it85xx_enter_scratch_rom()
+{
+	int ret;
+	int tries;
+
+	msg_pdbg("%s():%d was called ...\n", __FUNCTION__, __LINE__);
+	if (it85xx_scratch_rom_reenter > 0) return;
+
+	/* FIXME: this a workaround for the bug that SMBus signal would
+	 *        interfere the EC firmware update. Should be removed if
+	 *        we find out the root cause. */
+	ret = system("stop powerd >&2");
+	if (ret) {
+		msg_perr("Cannot stop powerd.\n");
+	}
+
+	for (tries = 0; tries < MAX_TRY; ++tries) {
+		/* Wait until IBF (input buffer) is not full. */
+		if (wait_for(KB_IBF, 0, MAX_TIMEOUT,
+		             "* timeout at waiting for IBF==0.\n",
+		             __FUNCTION__, __LINE__))
+			continue;
+
+		/* Copy EC firmware to SRAM. */
+		OUTB(0xb4, LEGACY_KBC_PORT_CMD);
+
+		/* Confirm EC has taken away the command. */
+		if (wait_for(KB_IBF, 0, MAX_TIMEOUT,
+		             "* timeout at taking command.\n",
+		             __FUNCTION__, __LINE__))
+			continue;
+
+		/* Waiting for OBF (output buffer) has data.
+		 * Note sometimes the replied command might be stolen by kernel
+		 * ISR so that it is okay as long as the command is 0xFA. */
+		if (wait_for(KB_OBF, KB_OBF, MAX_TIMEOUT, NULL, NULL, 0))
+			msg_pdbg("%s():%d * timeout at waiting for OBF.\n",
+			         __FUNCTION__, __LINE__);
+		if ((ret = INB(LEGACY_KBC_PORT_DATA)) == 0xFA) {
+			break;
+		} else {
+			msg_perr("%s():%d * not run on SRAM ret=%d\n",
+			         __FUNCTION__, __LINE__, ret);
+			continue;
+		}
+	}
+
+	if (tries < MAX_TRY) {
+		/* EC already runs on SRAM */
+		it85xx_scratch_rom_reenter++;
+		msg_pdbg("%s():%d * SUCCESS.\n", __FUNCTION__, __LINE__);
+	} else {
+		msg_perr("%s():%d * Max try reached.\n",
+		         __FUNCTION__, __LINE__);
+	}
+}
+
+void it85xx_exit_scratch_rom()
+{
+	int ret;
+	int tries;
+
+	msg_pdbg("%s():%d was called ...\n", __FUNCTION__, __LINE__);
+	if (it85xx_scratch_rom_reenter <= 0) return;
+
+	for (tries = 0; tries < MAX_TRY; ++tries) {
+		/* Wait until IBF (input buffer) is not full. */
+		if (wait_for(KB_IBF, 0, MAX_TIMEOUT,
+		             "* timeout at waiting for IBF==0.\n",
+		             __FUNCTION__, __LINE__))
+			continue;
+
+		/* Exit SRAM. Run on flash. */
+		OUTB(0xFE, LEGACY_KBC_PORT_CMD);
+
+		/* Confirm EC has taken away the command. */
+		if (wait_for(KB_IBF, 0, MAX_TIMEOUT,
+		             "* timeout at taking command.\n",
+		             __FUNCTION__, __LINE__)) {
+			/* We cannot ensure if EC has exited update mode.
+			 * If EC is in normal mode already, a further 0xFE
+			 * command will reboot system. So, exit loop here. */
+			tries = MAX_TRY;
+			break;
+		}
+
+		break;
+	}
+
+	if (tries < MAX_TRY) {
+		it85xx_scratch_rom_reenter = 0;
+		msg_pdbg("%s():%d * SUCCESS.\n", __FUNCTION__, __LINE__);
+	} else {
+		msg_perr("%s():%d * Max try reached.\n",
+		         __FUNCTION__, __LINE__);
+	}
+
+	/* FIXME: this a workaround for the bug that SMBus signal would
+	 *        interfere the EC firmware update. Should be removed if
+	 *        we find out the root cause. */
+	ret = system("start powerd >&2");
+	if (ret) {
+		msg_perr("Cannot start powerd again.\n");
+	}
+}
+
+int it85xx_spi_common_init(void)
+{
+	chipaddr base;
+
+	msg_pdbg("%s():%d superio.vendor=0x%02x\n", __func__, __LINE__,
+	         superio.vendor);
+	if (superio.vendor != SUPERIO_VENDOR_ITE)
+		return 1;
+
+#ifdef LPC_IO
+	/* Get LPCPNP of SHM. That's big-endian */
+	sio_write(superio.port, LDNSEL, 0x0F); /* Set LDN to SHM (0x0F) */
+	shm_io_base = (sio_read(superio.port, SHM_IO_BAD0) << 8) +
+	              sio_read(superio.port, SHM_IO_BAD1);
+	msg_pdbg("%s():%d shm_io_base=0x%04x\n", __func__, __LINE__,
+	         shm_io_base);
+
+	/* These pointers are not used directly. They will be send to EC's
+	 * register for indirect access. */
+	base = 0xFFFFF000;
+	ce_high = ((unsigned char*)base) + 0xE00;  /* 0xFFFFFE00 */
+	ce_low = ((unsigned char*)base) + 0xD00;  /* 0xFFFFFD00 */
+
+	/* pre-set indirect-access registers since in most of cases they are
+	 * 0xFFFFxx00. */
+	INDIRECT_A0(shm_io_base, base & 0xFF);
+	INDIRECT_A2(shm_io_base, (base >> 16) & 0xFF);
+	INDIRECT_A3(shm_io_base, (base >> 24));
+#endif
+#ifdef LPC_MEMORY
+	base = (chipaddr)programmer_map_flash_region("flash base", 0xFFFFF000,
+	                                             0x1000);
+	msg_pdbg("%s():%d base=0x%08x\n", __func__, __LINE__,
+	         (unsigned int)base);
+	ce_high = (unsigned char*)(base + 0xE00);  /* 0xFFFFFE00 */
+	ce_low = (unsigned char*)(base + 0xD00);  /* 0xFFFFFD00 */
+#endif
+
+	/* Set this as spi controller. */
+	spi_controller = SPI_CONTROLLER_IT85XX;
+
+	return 0;
+}
+
+/* Called by programmer_entry .init */
+int it85xx_spi_init(void)
+{
+	int ret;
+
+	get_io_perms();
+	/* Probe for the Super I/O chip and fill global struct superio. */
+	probe_superio();
+	ret = it85xx_spi_common_init();
+	if (!ret) {
+		buses_supported = CHIP_BUSTYPE_SPI;
+	} else {
+		buses_supported = CHIP_BUSTYPE_NONE;
+	}
+	return ret;
+}
+
+/* Called by internal_init() */
+int it85xx_probe_spi_flash(const char *name)
+{
+	int ret;
+
+	if (!(buses_supported & CHIP_BUSTYPE_FWH)) {
+		msg_pdbg("%s():%d buses not support FWH\n", __func__, __LINE__);
+		return 1;
+	}
+	ret = it85xx_spi_common_init();
+	msg_pdbg("FWH: %s():%d ret=%d\n", __func__, __LINE__, ret);
+	if (!ret) {
+		msg_pdbg("%s():%d buses_supported=0x%x\n", __func__, __LINE__,
+		          buses_supported);
+		if (buses_supported & CHIP_BUSTYPE_FWH)
+			msg_pdbg("Overriding chipset SPI with IT85 FWH|SPI.\n");
+		buses_supported |= CHIP_BUSTYPE_FWH | CHIP_BUSTYPE_SPI;
+	}
+	return ret;
+}
+
+int it85xx_shutdown(void)
+{
+	msg_pdbg("%s():%d\n", __func__, __LINE__);
+	it85xx_exit_scratch_rom();
+	return 0;
+}
+
+/* According to ITE 8502 document, the procedure to follow mode is following:
+ *   1. write 0x00 to LPC/FWH address 0xffff_fexxh (drive CE# high)
+ *   2. write data to LPC/FWH address 0xffff_fdxxh (drive CE# low and MOSI
+ *      with data)
+ *   3. read date from LPC/FWH address 0xffff_fdxxh (drive CE# low and get
+ *      data from MISO)
+ */
+int it85xx_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+			const unsigned char *writearr, unsigned char *readarr)
+{
+	int i;
+
+	it85xx_enter_scratch_rom();
+	/* exit scratch rom ONLY when programmer shuts down. Otherwise, the
+	 * temporary flash state may halt EC. */
+
+#ifdef LPC_IO
+	INDIRECT_A1(shm_io_base, (((unsigned long int)ce_high) >> 8) & 0xff);
+	INDIRECT_WRITE(shm_io_base, 0xFF);  /* Write anything to this address.*/
+	INDIRECT_A1(shm_io_base, (((unsigned long int)ce_low) >> 8) & 0xff);
+#endif
+#ifdef LPC_MEMORY
+	*ce_high = 0;
+#endif
+	for (i = 0; i < writecnt; ++i) {
+#ifdef LPC_IO
+		INDIRECT_WRITE(shm_io_base, writearr[i]);
+#endif
+#ifdef LPC_MEMORY
+		*ce_low = writearr[i];
+#endif
+	}
+	for (i = 0; i < readcnt; ++i) {
+#ifdef LPC_IO
+		readarr[i] = INDIRECT_READ(shm_io_base);
+#endif
+#ifdef LPC_MEMORY
+		readarr[i] = *ce_low;
+#endif
+	}
+#ifdef LPC_IO
+	INDIRECT_A1(shm_io_base, (((unsigned long int)ce_high) >> 8) & 0xff);
+	INDIRECT_WRITE(shm_io_base, 0xFF);  /* Write anything to this address.*/
+#endif
+#ifdef LPC_MEMORY
+	*ce_high = 0;
+#endif
+
+	return 0;
+}
+
+#endif
Index: flashrom-ec_it87_wpce775x/spi.c
===================================================================
--- flashrom-ec_it87_wpce775x/spi.c	(Revision 1234)
+++ flashrom-ec_it87_wpce775x/spi.c	(Arbeitskopie)
@@ -55,6 +55,13 @@
 		.write_256 = ich_spi_write_256,
 	},
 
+	{ /* SPI_CONTROLLER_IT85XX */
+		.command = it85xx_spi_send_command,
+		.multicommand = default_spi_send_multicommand,
+		.read = ich_spi_read,
+		.write_256 = ich_spi_write_256,
+	},
+
 	{ /* SPI_CONTROLLER_IT87XX */
 		.command = it8716f_spi_send_command,
 		.multicommand = default_spi_send_multicommand,
@@ -89,6 +96,13 @@
 		.read = bitbang_spi_read,
 		.write_256 = bitbang_spi_write_256,
 	},
+
+	{ /* SPI_CONTROLLER_WPCE775XX */
+		.command = wpce775x_spi_send_command,
+		.multicommand = default_spi_send_multicommand,
+		.read = wpce775x_spi_read,
+		.write_256 = wpce775x_spi_write_256,
+	},
 #endif
 #endif
 
Index: flashrom-ec_it87_wpce775x/programmer.h
===================================================================
--- flashrom-ec_it87_wpce775x/programmer.h	(Revision 1234)
+++ flashrom-ec_it87_wpce775x/programmer.h	(Arbeitskopie)
@@ -497,11 +497,13 @@
 #if defined(__i386__) || defined(__x86_64__)
 	SPI_CONTROLLER_ICH7,
 	SPI_CONTROLLER_ICH9,
+	SPI_CONTROLLER_IT85XX,
 	SPI_CONTROLLER_IT87XX,
 	SPI_CONTROLLER_SB600,
 	SPI_CONTROLLER_VIA,
 	SPI_CONTROLLER_WBSIO,
 	SPI_CONTROLLER_MCP6X_BITBANG,
+	SPI_CONTROLLER_WPCE775XX,
 #endif
 #endif
 #if CONFIG_FT2232_SPI == 1
@@ -554,6 +556,14 @@
 int ich_spi_send_multicommand(struct spi_command *cmds);
 #endif
 
+/* it85spi.c */
+struct superio probe_superio_ite85xx(void);
+int it85xx_spi_init(void);
+int it85xx_shutdown(void);
+int it85xx_probe_spi_flash(const char *name);
+int it85xx_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+			const unsigned char *writearr, unsigned char *readarr);
+
 /* it87spi.c */
 void enter_conf_mode_ite(uint16_t port);
 void exit_conf_mode_ite(uint16_t port);
@@ -582,6 +592,14 @@
 int wbsio_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len);
 #endif
 
+/* wpce775x.c */
+#if CONFIG_INTERNAL == 1
+int wpce775x_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len);
+int wpce775x_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len);
+int wpce775x_spi_send_command(unsigned int writecnt, unsigned int readcnt,
+			const unsigned char *writearr, unsigned char *readarr);
+#endif
+
 /* serprog.c */
 int serprog_init(void);
 int serprog_shutdown(void);


-- 
http://www.hailfinger.org/





More information about the flashrom mailing list