Thanks to Idwer Vollering for his iterative testing of this code.
Signed-off-by: Jonathan Kollasch jakllsch@kollasch.net Signed-off-by: Stefan Tauner stefan.tauner@student.tuwien.ac.at --- Now with less WTF... :)
Makefile | 9 +++ atavia.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ flashrom.c | 10 ++++ print.c | 5 ++ programmer.h | 13 ++++- 5 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 atavia.c
diff --git a/Makefile b/Makefile index 9309ce5..e4b364d 100644 --- a/Makefile +++ b/Makefile @@ -334,6 +334,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
@@ -459,6 +462,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) FTDILIBS := $(shell pkg-config --libs libftdi 2>/dev/null || printf "%s" "-lftdi -lusb") # This is a totally ugly hack. diff --git a/atavia.c b/atavia.c new file mode 100644 index 0000000..6045e2d --- /dev/null +++ b/atavia.c @@ -0,0 +1,181 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2010 Uwe Hermann uwe@hermann-uwe.de + * Copyright (C) 2011 Jonathan Kollasch jakllsch@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 "flash.h" +#include "programmer.h" +#include "hwaccess.h" + +#define PCI_VENDOR_ID_VIA 0x1106 + +#define VIA_MAX_RETRIES 300 +#define VT6421A_OFF 0 + +#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 pcidev_status ata_via[] = { + {PCI_VENDOR_ID_VIA, 0x3249, NT, "VIA", "VT6421A"}, + + {}, +}; + +static const 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_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)) ? " 3" : "", + (bmask & (1<<2)) ? " 2" : "", + (bmask & (1<<1)) ? " 1" : "", + (bmask & (1<<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(void) +{ + 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("%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; +} + +static int atavia_shutdown(void *data) +{ + pci_cleanup(pacc); + return 0; +} + +int atavia_init(void) +{ + uint32_t tmp; + if (rget_io_perms()) + return 1; + + pcidev_init(PCI_ROM_ADDRESS, ata_via); /* Acutally no BAR setup needed at all. */ + + if (register_shutdown(atavia_shutdown, NULL)) + return 1; + + /* Test if a flash chip is attached. */ + pci_write_long(pcidev_dev, PCI_ROM_ADDRESS, (uint32_t)PCI_ROM_ADDRESS_MASK); + programmer_delay(90); + tmp = pci_read_long(pcidev_dev, PCI_ROM_ADDRESS); + msg_pdbg2("BROM base=0x%08x\n", tmp); + if ((tmp & PCI_ROM_ADDRESS_MASK) == 0) { + msg_perr("Controller thinks there is no ROM attached.\n"); + //return 1; + } + + if (!atavia_ready()) { + msg_perr("Controller not ready.\n"); + //return 1; + } + + atavia_prettyprint_access(pci_read_byte(pcidev_dev, BROM_ACCESS)); + register_par_programmer(&lpc_programmer_atavia, BUS_LPC); + + return 0; +} + +void atavia_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr) +{ + addr += VT6421A_OFF; + pci_write_long(pcidev_dev, BROM_ADDR, (addr & ~3)); + pci_write_long(pcidev_dev, BROM_DATA, val << BYTE_OFFSET(addr)); + pci_write_byte(pcidev_dev, BROM_ACCESS, BROM_TRIGGER | BROM_WRITE | ENABLE_BYTE(addr)); + + if (!atavia_ready()) { + msg_perr("not ready after write\n"); + } +} + +uint8_t atavia_chip_readb(const struct flashctx *flash, chipaddr addr) +{ + uint8_t val; + + addr += VT6421A_OFF; + pci_write_long(pcidev_dev, BROM_ADDR, (addr & ~3)); + pci_write_byte(pcidev_dev, BROM_ACCESS, BROM_TRIGGER | ENABLE_BYTE(addr)); + + if (!atavia_ready()) { + msg_perr("not ready after read\n"); + } + + val = (pci_read_long(pcidev_dev, BROM_DATA) >> BYTE_OFFSET(addr)) & 0xff; + + return val; +} diff --git a/flashrom.c b/flashrom.c index fdc5412..f5bf45f 100644 --- a/flashrom.c +++ b/flashrom.c @@ -152,6 +152,16 @@ const struct programmer_entry programmer_table[] = { }, #endif
+#if CONFIG_ATAVIA == 1 + { + .name = "atavia", + .init = atavia_init, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .delay = internal_delay, + }, +#endif + #if CONFIG_FT2232_SPI == 1 { .name = "ft2232_spi", diff --git a/print.c b/print.c index 582a49e..724f91c 100644 --- a/print.c +++ b/print.c @@ -487,6 +487,11 @@ void print_supported(void) programmer_table[PROGRAMMER_ATAHPT].name); print_supported_pcidevs(ata_hpt); #endif +#if CONFIG_ATAVIA == 1 + printf("\nSupported devices for the %s programmer:\n", + programmer_table[PROGRAMMER_ATAVIA].name); + print_supported_pcidevs(ata_via); +#endif #if CONFIG_FT2232_SPI == 1 msg_ginfo("\nSupported devices for the %s programmer:\n", programmer_table[PROGRAMMER_FT2232_SPI].name); diff --git a/programmer.h b/programmer.h index 51b9c40..105613c 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 @@ -239,7 +242,7 @@ int rpci_write_long(struct pci_dev *dev, int reg, uint32_t data); #endif
/* print.c */ -#if CONFIG_NIC3COM+CONFIG_NICREALTEK+CONFIG_NICNATSEMI+CONFIG_GFXNVIDIA+CONFIG_DRKAISER+CONFIG_SATASII+CONFIG_ATAHPT+CONFIG_NICINTEL+CONFIG_NICINTEL_SPI+CONFIG_OGP_SPI+CONFIG_SATAMV >= 1 +#if CONFIG_NIC3COM+CONFIG_NICREALTEK+CONFIG_NICNATSEMI+CONFIG_GFXNVIDIA+CONFIG_DRKAISER+CONFIG_SATASII+CONFIG_ATAHPT+CONFIG_ATAVIA+CONFIG_NICINTEL+CONFIG_NICINTEL_SPI+CONFIG_OGP_SPI+CONFIG_SATAMV >= 1 /* Not needed for CONFIG_INTERNAL, but for all other PCI-based programmers. */ void print_supported_pcidevs(const struct pcidev_status *devs); #endif @@ -420,6 +423,14 @@ int atahpt_init(void); extern const struct pcidev_status 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); +extern const struct pcidev_status ata_via[]; +#endif + /* ft2232_spi.c */ #if CONFIG_FT2232_SPI == 1 struct usbdev_status {