[flashrom] [PATCH] Add VIA VT6421A LPC programmer driver.

Stefan Tauner stefan.tauner at student.tuwien.ac.at
Thu Aug 15 03:34:11 CEST 2013


Due to the mysterious address handling of this chip the user can specify
a base address with the offset parameter, e.g.:
flashrom -p atavia:offset=0xFFF00000

Thanks to Idwer Vollering for his iterative testing of this code.

TODO: remove or document the offset parameter.

Signed-off-by: Jonathan Kollasch <jakllsch at kollasch.net>
Signed-off-by: Stefan Tauner <stefan.tauner at student.tuwien.ac.at>
---
 Makefile     |   9 +++
 atavia.c     | 200 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 flash.h      |   2 +-
 flashrom.c   |  17 ++++-
 programmer.h |  12 ++++
 5 files changed, 236 insertions(+), 4 deletions(-)
 create mode 100644 atavia.c

diff --git a/Makefile b/Makefile
index 023c41d..2cd5b43 100644
--- a/Makefile
+++ b/Makefile
@@ -359,6 +359,9 @@ CONFIG_SATASII ?= yes
 # IMPORTANT: This code is not yet working!
 CONFIG_ATAHPT ?= no
 
+# VIA VT6421A LPC memory support
+CONFIG_ATAVIA ?= yes
+
 # Always enable FT2232 SPI dongles for now.
 CONFIG_FT2232_SPI ?= yes
 
@@ -489,6 +492,12 @@ PROGRAMMER_OBJS += atahpt.o
 NEED_PCI := yes
 endif
 
+ifeq ($(CONFIG_ATAVIA), yes)
+FEATURE_CFLAGS += -D'CONFIG_ATAVIA=1'
+PROGRAMMER_OBJS += atavia.o
+NEED_PCI := yes
+endif
+
 ifeq ($(CONFIG_FT2232_SPI), yes)
 # This is a totally ugly hack.
 FEATURE_CFLAGS += $(shell LC_ALL=C grep -q "FTDISUPPORT := yes" .features && printf "%s" "-D'CONFIG_FT2232_SPI=1'")
diff --git a/atavia.c b/atavia.c
new file mode 100644
index 0000000..7dc425d
--- /dev/null
+++ b/atavia.c
@@ -0,0 +1,200 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2010 Uwe Hermann <uwe at hermann-uwe.de>
+ * Copyright (C) 2011 Jonathan Kollasch <jakllsch at kollasch.net>
+ * Copyright (C) 2012 Stefan Tauner
+ *
+ * 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 <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include "flash.h"
+#include "programmer.h"
+#include "hwaccess.h"
+
+#define PCI_VENDOR_ID_VIA	0x1106
+
+#define VIA_MAX_RETRIES		300
+
+#define BROM_ADDR		0x60
+
+#define BROM_DATA		0x64
+
+#define BROM_ACCESS		0x68
+#define BROM_TRIGGER		0x80
+#define BROM_WRITE		0x40
+#define BROM_SIZE_MASK		0x30
+#define BROM_SIZE_64K		0x00
+#define BROM_SIZE_32K		0x10
+#define BROM_SIZE_16K		0x20
+#define BROM_SIZE_0K		0x30
+#define BROM_BYTE_ENABLE_MASK	0x0f
+
+#define BROM_STATUS		0x69
+#define BROM_ERROR_STATUS	0x80
+
+/* Select the byte we want to access. This is done by clearing the bit  corresponding to the byte we want to
+ * access, leaving the others set (yes, really). */
+#define ENABLE_BYTE(address)	((~(1 << ((address) & 3))) & BROM_BYTE_ENABLE_MASK)
+#define BYTE_OFFSET(address)	(((addr) & 3) * 8)
+
+const struct dev_entry ata_via[] = {
+	{PCI_VENDOR_ID_VIA, 0x3249, NT, "VIA", "VT6421A"},
+
+	{},
+};
+
+static struct par_programmer lpc_programmer_atavia = {
+		.chip_readb		= atavia_chip_readb,
+		.chip_readw		= fallback_chip_readw,
+		.chip_readl		= fallback_chip_readl,
+		.chip_readn		= fallback_chip_readn,
+		.chip_writeb		= atavia_chip_writeb,
+		.chip_writew		= fallback_chip_writew,
+		.chip_writel		= fallback_chip_writel,
+		.chip_writen		= fallback_chip_writen,
+};
+
+static void *atavia_offset = NULL;
+struct pci_dev *dev = NULL;
+
+static void atavia_prettyprint_access(uint8_t access)
+{
+	uint8_t bmask = access & BROM_BYTE_ENABLE_MASK;
+	uint8_t size = access & BROM_SIZE_MASK;
+
+	msg_pspew("Accessing byte(s):%s%s%s%s\n",
+		  ((bmask & (1<<3)) == 0) ? " 3" : "",
+		  ((bmask & (1<<2)) == 0) ? " 2" : "",
+		  ((bmask & (1<<1)) == 0) ? " 1" : "",
+		  ((bmask & (1<<0)) == 0) ? " 0" : "");
+	if (size == BROM_SIZE_0K) {
+		msg_pspew("No ROM device found.\n");
+	} else
+		msg_pspew("ROM device with %s kB attached.\n",
+			  (size == BROM_SIZE_64K) ? ">=64" :
+			  (size == BROM_SIZE_32K) ? "32" : "16");
+	msg_pspew("Access is a %s.\n", (access & BROM_WRITE) ? "write" : "read");
+	msg_pspew("Device is %s.\n", (access & BROM_TRIGGER) ? "busy" : "ready");
+}
+
+static bool atavia_ready(struct pci_dev *pcidev_dev)
+{
+	int try;
+	uint8_t access, status;
+	bool ready = false;
+
+	for (try = 0; try < VIA_MAX_RETRIES; try++) {
+		access = pci_read_byte(pcidev_dev, BROM_ACCESS);
+		status = pci_read_byte(pcidev_dev, BROM_STATUS);
+		if (((access & BROM_TRIGGER) == 0) && (status & BROM_ERROR_STATUS) == 0) {
+			ready = true;
+			break;
+		} else {
+			programmer_delay(1);
+			continue;
+		}
+	}
+
+	msg_pdbg2("\n%s: %s after %d tries (access=0x%02x, status=0x%02x)\n",
+		  __func__, ready ? "suceeded" : "failed", try, access, status);
+	atavia_prettyprint_access(access);
+	return ready;
+}
+
+void *atavia_map(const char *descr, unsigned long phys_addr, size_t len)
+{
+	return (atavia_offset != 0) ? atavia_offset : (void *)phys_addr;
+}
+
+int atavia_init(void)
+{
+	uint32_t tmp;
+	char *arg;
+
+	arg = extract_programmer_param("offset");
+	if (arg) {
+		if (strlen(arg) == 0) {
+			msg_perr("Missing argument for offset.\n");
+			free(arg);
+			return ERROR_FATAL;
+		}
+		char *endptr;
+		atavia_offset = (void *)strtoul(arg, &endptr, 0);
+		if (*endptr) {
+			msg_perr("Error: Invalid offset specified: \"%s\".\n", arg);
+			free(arg);
+			return ERROR_FATAL;
+		}
+		msg_pinfo("Mapping addresses to base %p.\n", atavia_offset);
+	}
+	free(arg);
+
+	if (rget_io_perms())
+		return 1;
+
+	/* No need to check for errors, pcidev_init() will not return in case of errors. */
+	dev = pcidev_init(ata_via, PCI_ROM_ADDRESS); /* Acutally no BAR setup needed at all. */
+
+	/* Test if a flash chip is attached. */
+	pci_write_long(dev, PCI_ROM_ADDRESS, (uint32_t)PCI_ROM_ADDRESS_MASK);
+	programmer_delay(90);
+	tmp = pci_read_long(dev, PCI_ROM_ADDRESS);
+	msg_pdbg2("BROM base=0x%08x\n", tmp);
+	if ((tmp & PCI_ROM_ADDRESS_MASK) == 0) {
+		msg_pwarn("Controller thinks there is no ROM attached.\n");
+	}
+
+	if (!atavia_ready(dev)) {
+		msg_perr("Controller not ready.\n");
+		return 1;
+	}
+
+	register_par_programmer(&lpc_programmer_atavia, BUS_LPC);
+
+	return 0;
+}
+
+void atavia_chip_writeb(const struct flashctx *flash, uint8_t val, const chipaddr addr)
+{
+	msg_pspew("%s: 0x%02x to 0x%08lx.\n", __func__, val, addr);
+	pci_write_long(dev, BROM_ADDR, (addr & ~3));
+	pci_write_long(dev, BROM_DATA, val << BYTE_OFFSET(addr));
+	pci_write_byte(dev, BROM_ACCESS, BROM_TRIGGER | BROM_WRITE | ENABLE_BYTE(addr));
+
+	if (!atavia_ready(dev)) {
+		msg_perr("not ready after write\n");
+	}
+}
+
+uint8_t atavia_chip_readb(const struct flashctx *flash, const chipaddr addr)
+{
+	uint8_t val;
+
+	pci_write_long(dev, BROM_ADDR, (addr & ~3));
+	pci_write_byte(dev, BROM_ACCESS, BROM_TRIGGER | ENABLE_BYTE(addr));
+
+	if (!atavia_ready(dev)) {
+		msg_perr("not ready after read\n");
+	}
+
+	val = (pci_read_long(dev, BROM_DATA) >> BYTE_OFFSET(addr)) & 0xff;
+
+	msg_pspew("%s: 0x%02x from 0x%08lx.\n", __func__, val, addr);
+	return val;
+}
diff --git a/flash.h b/flash.h
index 63701ed..6c37da4 100644
--- a/flash.h
+++ b/flash.h
@@ -46,7 +46,7 @@ typedef uintptr_t chipaddr;
 #define PRIxPTR_WIDTH ((int)(sizeof(uintptr_t)*2))
 
 int register_shutdown(int (*function) (void *data), void *data);
-void *programmer_map_flash_region(const char *descr, uintptr_t phys_addr, size_t len);
+void *programmer_map_flash_region(const char *const descr, uintptr_t phys_addr, size_t len);
 void programmer_unmap_flash_region(void *virt_addr, size_t len);
 void programmer_delay(int usecs);
 
diff --git a/flashrom.c b/flashrom.c
index 86e64a2..cc7248b 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -172,6 +172,16 @@ const struct programmer_entry programmer_table[] = {
 	},
 #endif
 
+#if CONFIG_ATAVIA == 1
+	{
+		.name			= "atavia",
+		.init			= atavia_init,
+		.map_flash_region	= atavia_map,
+		.unmap_flash_region	= fallback_unmap,
+		.delay			= internal_delay,
+	},
+#endif
+
 #if CONFIG_FT2232_SPI == 1
 	{
 		.name			= "ft2232_spi",
@@ -430,10 +440,11 @@ int programmer_shutdown(void)
 	return ret;
 }
 
-void *programmer_map_flash_region(const char *descr, uintptr_t phys_addr, size_t len)
+void *programmer_map_flash_region(const char *const descr, uintptr_t phys_addr, size_t len)
 {
-	return programmer_table[programmer].map_flash_region(descr,
-							     phys_addr, len);
+	void *ret = programmer_table[programmer].map_flash_region(descr, phys_addr, len);
+	msg_gspew("%s: mapping %s from 0x%08lx to %p\n", __func__, descr, phys_addr, ret);
+	return ret;
 }
 
 void programmer_unmap_flash_region(void *virt_addr, size_t len)
diff --git a/programmer.h b/programmer.h
index f9f5241..8753a5d 100644
--- a/programmer.h
+++ b/programmer.h
@@ -54,6 +54,9 @@ enum programmer {
 #if CONFIG_ATAHPT == 1
 	PROGRAMMER_ATAHPT,
 #endif
+#if CONFIG_ATAVIA == 1
+	PROGRAMMER_ATAVIA,
+#endif
 #if CONFIG_FT2232_SPI == 1
 	PROGRAMMER_FT2232_SPI,
 #endif
@@ -430,6 +433,15 @@ int atahpt_init(void);
 extern const struct dev_entry ata_hpt[];
 #endif
 
+/* atavia.c */
+#if CONFIG_ATAVIA == 1
+int atavia_init(void);
+void atavia_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr);
+uint8_t atavia_chip_readb(const struct flashctx *flash, const chipaddr addr);
+void *atavia_map(const char *descr, uintptr_t phys_addr, size_t len);
+extern const struct dev_entry ata_via[];
+#endif
+
 /* ft2232_spi.c */
 #if CONFIG_FT2232_SPI == 1
 int ft2232_spi_init(void);
-- 
Kind regards, Stefan Tauner





More information about the flashrom mailing list