mirror of
https://review.coreboot.org/flashrom.git
synced 2025-04-26 22:52:34 +02:00

Tested mainboards: OK: - ASUS F2A85-M Reported by various corebooters - ASUS M2N-MX SE Plus Reported by Antonio - ASUS P5LD2 Reported by François Revol - Lenovo ThinkPad T530 Reported and partially authored by Edward O'Callaghan - MSI MS-7502 (Medion MD8833) Reported by naq on IRC - Shuttle AB61 Reported by olofolleola4 - ZOTAC IONITX-F-E Reported by Bernardo Kuri Flash chips: - Atmel AT45DB021D to PREW (+PREW) Reported by The Raven - Atmel AT25F4096 to PREW (+PREW) Reported by 공준혁 - GigaDevice GD25Q16(B) to PREW (+PREW) Reported by luxflow@live.com using a GD25Q16BSIG - Catalyst CAT28F512 Mark erase and write as known bad (not implemented) Miscellaneous: - Various spelling corrections by Daniele Forsi. - Added and refined a bunch of chips originally investigated by Carl-Daniel. - Marked the ARM-USB-OCD-H programmer as tested (reported by Ruud Schramp). - Tiny other stuff. Corresponding to flashrom svn r1839. Signed-off-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at> Acked-by: Stefan Tauner <stefan.tauner@alumni.tuwien.ac.at>
197 lines
5.8 KiB
C
197 lines
5.8 KiB
C
/*
|
|
* 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_master lpc_master_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;
|
|
static 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 ? "succeeded" : "failed", try, access, status);
|
|
atavia_prettyprint_access(access);
|
|
return ready;
|
|
}
|
|
|
|
void *atavia_map(const char *descr, uintptr_t 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;
|
|
|
|
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_master(&lpc_master_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%*" PRIxPTR ".\n", __func__, val, PRIxPTR_WIDTH, 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%*" PRIxPTR ".\n", __func__, val, PRIxPTR_WIDTH, addr);
|
|
return val;
|
|
}
|