diff options
author | stefanct <stefanct@2b7e53f0-3cfb-0310-b3e9-8179ed1497e1> | 2014-06-01 10:26:23 +0000 |
---|---|---|
committer | stefanct <stefanct@2b7e53f0-3cfb-0310-b3e9-8179ed1497e1> | 2014-06-01 10:26:23 +0000 |
commit | 982a7389b61b02444e352757b1b1c13d502bb35d (patch) | |
tree | 1d2beb0dcc9fa121e7cfffc7e84d5c367cce9203 /atavia.c | |
parent | bf002f61950d07ee9353205c83b3451d63ad3cba (diff) | |
download | flashrom-982a7389b61b02444e352757b1b1c13d502bb35d.tar.gz |
Add VIA VT6421A LPC programmer driver.
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, as well as to
Martijn Bastiaan who did the last tests before merging.
Signed-off-by: Jonathan Kollasch <jakllsch@kollasch.net>
Signed-off-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
Acked-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
git-svn-id: https://code.coreboot.org/svn/flashrom/trunk@1809 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'atavia.c')
-rw-r--r-- | atavia.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/atavia.c b/atavia.c new file mode 100644 index 0000000..952a860 --- /dev/null +++ b/atavia.c @@ -0,0 +1,197 @@ +/* + * 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-2013 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 <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, DEP, "VIA", "VT6421A"}, + + {}, +}; + +static void atavia_chip_writeb(const struct flashctx *flash, uint8_t val, chipaddr addr); +static uint8_t atavia_chip_readb(const struct flashctx *flash, const chipaddr addr); +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_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) +{ + char *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. */ + if (!dev) + return 1; + + /* Test if a flash chip is attached. */ + pci_write_long(dev, PCI_ROM_ADDRESS, (uint32_t)PCI_ROM_ADDRESS_MASK); + programmer_delay(90); + uint32_t base = pci_read_long(dev, PCI_ROM_ADDRESS); + msg_pdbg2("BROM base=0x%08x\n", base); + if ((base & 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; +} + +static 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"); + } +} + +static uint8_t atavia_chip_readb(const struct flashctx *flash, const chipaddr addr) +{ + 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"); + } + + uint8_t 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; +} |