| /* | 
 |  * 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. | 
 |  */ | 
 |  | 
 | #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"}, | 
 |  | 
 | 	{0}, | 
 | }; | 
 |  | 
 | 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); /* Actually 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; | 
 | } |