
Support for the Winbond W39V080FA series of chips.
Support for flashing on the Kontron 986LCD-M board.

Signed-off-by: Stefan Reinauer <stepan@coresystems.de>

Index: util/flashrom/flash.h
===================================================================
--- util/flashrom/flash.h	(revision 3164)
+++ util/flashrom/flash.h	(working copy)
@@ -276,6 +276,8 @@
 #define W_39V040A		0x3D
 #define W_39V040B		0x54
 #define W_39V080A		0xD0
+#define W_39V080FA		0xD3
+#define W_39V080FA_DM		0x93
 #define W_49F002U		0x0B
 #define W_49V002A		0xB0
 #define W_49V002FA		0x32
@@ -414,6 +416,11 @@
 int erase_sst_fwhub(struct flashchip *flash);
 int write_sst_fwhub(struct flashchip *flash, uint8_t *buf);
 
+/* w39V080fa.c */
+int probe_winbond_fwhub(struct flashchip *flash);
+int erase_winbond_fwhub(struct flashchip *flash);
+int write_winbond_fwhub(struct flashchip *flash, uint8_t *buf);
+
 /* w29ee011.c */
 int probe_w29ee011(struct flashchip *flash);
 
Index: util/flashrom/flashchips.c
===================================================================
--- util/flashrom/flashchips.c	(revision 3164)
+++ util/flashrom/flashchips.c	(working copy)
@@ -129,6 +129,8 @@
 	{"Winbond",	"W49F002U",		WINBOND_ID,	W_49F002U,		256,	128,		probe_jedec,		erase_chip_jedec,		write_49f002},
 	{"Winbond",	"W49V002A",		WINBOND_ID,	W_49V002A,		256,	128,		probe_jedec,		erase_chip_jedec,		write_49f002},
 	{"Winbond",	"W49V002FA",		WINBOND_ID,	W_49V002FA,		256,	128,		probe_jedec,		erase_chip_jedec,		write_49f002},
+	{"Winbond",	"W39V080FA",		WINBOND_ID,	W_39V080FA,		1024,	64*1024,	probe_winbond_fwhub,	erase_winbond_fwhub,		write_winbond_fwhub},
+	{"Winbond",	"W39V080FA (dual mode)",WINBOND_ID,	W_39V080FA_DM,		512,	64*1024,	probe_winbond_fwhub,	erase_winbond_fwhub,		write_winbond_fwhub},
 
 	{"EON",		"unknown SPI chip",	EON_ID_NOPREFIX,GENERIC_DEVICE_ID,	0,	0,		probe_spi,		NULL,				NULL},
 	{"Macronix",	"unknown SPI chip",	MX_ID,		GENERIC_DEVICE_ID,	0,	0,		probe_spi,		NULL,				NULL},
Index: util/flashrom/Makefile
===================================================================
--- util/flashrom/Makefile	(revision 3164)
+++ util/flashrom/Makefile	(working copy)
@@ -24,7 +24,7 @@
 	am29f040b.o mx29f002.o sst39sf020.o m29f400bt.o w49f002u.o \
 	82802ab.o msys_doc.o pm49fl004.o sst49lf040.o sst49lfxxxc.o \
 	sst_fwhub.o layout.o cbtable.o flashchips.o flashrom.o \
-	sharplhf00l04.o w29ee011.o spi.o
+	w39v080fa.o sharplhf00l04.o w29ee011.o spi.o
 
 all: pciutils dep $(PROGRAM)
 
Index: util/flashrom/w39v080fa.c
===================================================================
--- util/flashrom/w39v080fa.c	(revision 0)
+++ util/flashrom/w39v080fa.c	(revision 0)
@@ -0,0 +1,206 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2008 coresystems GmbH
+ *
+ * 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 <stdio.h>
+#include "flash.h"
+
+int probe_winbond_fwhub(struct flashchip *flash)
+{
+	volatile uint8_t *bios = flash->virtual_memory;
+	uint8_t vid, did;
+
+	/* Product Identification Entry */
+	*(volatile uint8_t *)(bios + 0x5555) = 0xAA;
+	*(volatile uint8_t *)(bios + 0x2AAA) = 0x55;
+	*(volatile uint8_t *)(bios + 0x5555) = 0x90;
+	myusec_delay(10);
+
+	/* Read product ID */
+	vid = *(volatile uint8_t *)bios;
+	did = *(volatile uint8_t *)(bios + 0x01);
+
+	/* Product Identifixation Exit */
+	*(volatile uint8_t *)(bios + 0x5555) = 0xAA;
+	*(volatile uint8_t *)(bios + 0x2AAA) = 0x55;
+	*(volatile uint8_t *)(bios + 0x5555) = 0xF0;
+	myusec_delay(10);
+
+	printf_debug("%s: vid 0x%x, did 0x%x\n", __FUNCTION__, vid, did);
+
+	if (vid != flash->manufacture_id || did != flash->model_id)
+		return 0;
+
+	map_flash_registers(flash);
+
+	return 1;
+}
+
+static int unlock_block_winbond_fwhub(struct flashchip *flash, int offset)
+{
+	volatile uint8_t *wrprotect = flash->virtual_registers + offset + 2;
+	uint8_t locking;
+
+	printf_debug("Trying to unlock block @0x%08x = 0x%02x\n", offset, *wrprotect);
+
+	locking = *wrprotect;
+	switch (locking & 0x7 ) {
+	case 0:
+		printf_debug("Full Access.\n");
+		return 0;
+	case 1:
+		printf_debug("Write Lock (Default State).\n");
+		*wrprotect = 0;
+		return 0;
+	case 2:
+		printf_debug("Locked Open (Full Access, Lock Down).\n");
+		return 0;
+	case 3:
+		fprintf(stderr, "Error: Write Lock, Locked Down.\n");
+		return -1;
+	case 4:
+		printf_debug("Read Lock.\n");
+		*wrprotect = 0;
+		return 0;
+	case 5:
+		printf_debug("Read/Write Lock.\n");
+		*wrprotect = 0;
+		return 0;
+	case 6:
+		fprintf(stderr, "Error: Read Lock, Locked Down.\n");
+		return -1;
+	case 7:
+		fprintf(stderr, "Error: Read/Write Lock, Locked Down.\n");
+		return -1;
+	}
+
+	/* We will never reach this point, but GCC doesn't know */
+	return -1;
+}
+
+int unlock_winbond_fwhub(struct flashchip *flash)
+{
+	int i, total_size = flash->total_size * 1024;
+	volatile uint8_t *bios = flash->virtual_memory;
+	uint8_t locking;
+	
+	/* Are there any hardware restrictions that we can't overcome? 
+	 * If flashrom fail here, someone's got to check all those GPIOs.
+	 */
+
+	/* Product Identification Entry */
+	*(volatile uint8_t *)(bios + 0x5555) = 0xAA;
+	*(volatile uint8_t *)(bios + 0x2AAA) = 0x55;
+	*(volatile uint8_t *)(bios + 0x5555) = 0x90;
+	myusec_delay(10);
+
+	/* Read Hardware Lock Bits */
+	locking = *(volatile uint8_t *)(bios + 0xffff2);
+
+	/* Product Identification Exit */
+	*(volatile uint8_t *)(bios + 0x5555) = 0xAA;
+	*(volatile uint8_t *)(bios + 0x2AAA) = 0x55;
+	*(volatile uint8_t *)(bios + 0x5555) = 0xF0;
+	myusec_delay(10);
+
+	printf_debug("Lockout bits:\n");
+
+	if (locking & (1<<2))
+		fprintf(stderr, "Error: hardware bootblock locking (#TBL).\n");
+	else
+		printf_debug("No hardware bootblock locking (good!)\n");
+
+	if (locking & (1<<3))
+		fprintf(stderr, "Error: hardware block locking (#WP).\n");
+	else
+		printf_debug("No hardware block locking (good!)\n");
+
+	if (locking & ((1<<2) | (1<<3)))
+		return -1;
+
+	/* Unlock the complete chip */
+	for (i = 0; i < total_size; i += flash->page_size)
+		if (unlock_block_winbond_fwhub(flash, i))
+			return -1;
+
+	return 0;
+}
+
+static int erase_sector_winbond_fwhub(volatile uint8_t *bios, unsigned int sector)
+{
+	/* Remember: too much sleep can waste your day. */
+
+	printf("0x%08x\b\b\b\b\b\b\b\b\b\b", sector);
+
+	/* Sector Erase */
+	*(volatile uint8_t *)(bios + 0x5555) = 0xAA;
+	*(volatile uint8_t *)(bios + 0x2AAA) = 0x55;
+	*(volatile uint8_t *)(bios + 0x5555) = 0x80;
+
+	*(volatile uint8_t *)(bios + 0x5555) = 0xAA;
+	*(volatile uint8_t *)(bios + 0x2AAA) = 0x55;
+	*(volatile uint8_t *)(bios + sector) = 0x30;
+
+	/* wait for Toggle bit ready */
+	toggle_ready_jedec(bios);
+
+	return 0;
+}
+
+int erase_winbond_fwhub(struct flashchip *flash)
+{
+	int i, total_size = flash->total_size * 1024;
+	volatile uint8_t *bios = flash->virtual_memory;
+	
+	printf("Erasing:     ");
+
+	for (i = 0; i < total_size; i += flash->page_size)
+		erase_sector_winbond_fwhub(bios, i);
+
+	printf("\n");
+
+	for (i = 0; i < total_size; i++) {
+		if (bios[i] != 0xff) {
+			fprintf(stderr, "Error: Flash chip erase failed at 0x%08x(0x%02x)\n", i, bios[i]);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+int write_winbond_fwhub(struct flashchip *flash, uint8_t *buf)
+{
+	int i;
+	int total_size = flash->total_size * 1024;
+	volatile uint8_t *bios = flash->virtual_memory;
+
+	if (erase_winbond_fwhub(flash))
+		return -1;
+
+	printf("Programming: ");
+	for (i = 0; i < total_size; i+=flash->page_size) {
+		printf("0x%08x\b\b\b\b\b\b\b\b\b\b", i);
+		write_sector_jedec(bios, buf + i, bios + i, flash->page_size);
+	}
+	printf("\n");
+
+	return 0;
+}
+
Index: util/flashrom/flashrom.c
===================================================================
--- util/flashrom/flashrom.c	(revision 3164)
+++ util/flashrom/flashrom.c	(working copy)
@@ -109,8 +109,8 @@
 			flash++;
 			continue;
 		}
-		printf_debug("Probing for %s, %d KB\n",
-			     flash->name, flash->total_size);
+		printf_debug("Probing for %s %s, %d KB: ",
+			     flash->vendor, flash->name, flash->total_size);
 
 		size = flash->total_size * 1024;
 
Index: util/flashrom/board_enable.c
===================================================================
--- util/flashrom/board_enable.c	(revision 3164)
+++ util/flashrom/board_enable.c	(working copy)
@@ -431,6 +431,42 @@
 	return 0;
 }
 
+static int board_kontron_986lcd_m(const char *name)
+{
+	struct pci_dev *dev;
+	uint16_t gpiobar;
+	uint32_t val;
+
+#define ICH7_GPIO_LVL2 0x38
+
+	dev = pci_dev_find(0x8086, 0x27b8);     /* Intel ICH7 LPC */
+	if (!dev) {
+		// This will never happen on this board
+		fprintf(stderr, "\nERROR: ICH7 LPC bridge not found.\n");
+		return -1;
+	}
+
+	/* Use GPIOBASE register to find where the GPIO is mapped. */
+	gpiobar = pci_read_word(dev, 0x48) & 0xfffc;
+
+	val = inl(gpiobar + ICH7_GPIO_LVL2);	/* GP_LVL2 */
+	printf_debug("\nGPIOBAR=0x%04x GP_LVL: 0x%08x\n", gpiobar, val);
+
+	/* bit 2 (0x04) = 0 #TBL --> bootblock locking = 1
+	 * bit 2 (0x04) = 1 #TBL --> bootblock locking = 0
+	 * bit 3 (0x08) = 0 #WP --> block locking = 1
+	 * bit 3 (0x08) = 1 #WP --> block locking = 0
+	 *
+	 * To enable full block locking, you would do:
+	 *     val &= ~ ((1 << 2) | (1 << 3));
+	 */
+	val |= (1 << 2) | (1 << 3);
+
+	outl(val, gpiobar + ICH7_GPIO_LVL2);
+
+	return 0;
+}
+
 /**
  * We use 2 sets of IDs here, you're free to choose which is which. This
  * is to provide a very high degree of certainty when matching a board on
@@ -497,6 +533,8 @@
 	 "artecgroup", "dbe61", "Artec Group DBE61", board_artecgroup_dbe6x},
 	{0x1022, 0x2090, 0x0000, 0x0000, 0x1022, 0x2080, 0x0000, 0x0000,
 	 "artecgroup", "dbe62", "Artec Group DBE62", board_artecgroup_dbe6x},
+ 	{0x8086, 0x27b8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 	 "kontron", "986lcd-m", "Kontron 986LCD-M", board_kontron_986lcd_m},
 	{0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL}	/* Keep this */
 };
 
