/* * This file is part of the flashrom project. * * Copyright (C) 2010 Carl-Daniel Hailfinger * * 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; version 2 of the License. * * 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. */ /* Driver for the NVIDIA MCP6x/MCP7x MCP6X_SPI controller. * Based on clean room reverse engineered docs from * https://flashrom.org/pipermail/flashrom/2009-December/001180.html * created by Michael Karcher. */ #if defined(__i386__) || defined(__x86_64__) #include #include #include "flash.h" #include "programmer.h" #include "hwaccess.h" /* Bit positions for each pin. */ #define MCP6X_SPI_CS 1 #define MCP6X_SPI_SCK 2 #define MCP6X_SPI_MOSI 3 #define MCP6X_SPI_MISO 4 #define MCP6X_SPI_REQUEST 0 #define MCP6X_SPI_GRANT 8 static void *mcp6x_spibar = NULL; /* Cached value of last GPIO state. */ static uint8_t mcp_gpiostate; static void mcp6x_request_spibus(void) { mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530); mcp_gpiostate |= 1 << MCP6X_SPI_REQUEST; mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530); /* Wait until we are allowed to use the SPI bus. */ while (!(mmio_readw(mcp6x_spibar + 0x530) & (1 << MCP6X_SPI_GRANT))) ; /* Update the cache. */ mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530); } static void mcp6x_release_spibus(void) { mcp_gpiostate &= ~(1 << MCP6X_SPI_REQUEST); mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530); } static void mcp6x_bitbang_set_cs(int val) { mcp_gpiostate &= ~(1 << MCP6X_SPI_CS); mcp_gpiostate |= (val << MCP6X_SPI_CS); mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530); } static void mcp6x_bitbang_set_sck(int val) { mcp_gpiostate &= ~(1 << MCP6X_SPI_SCK); mcp_gpiostate |= (val << MCP6X_SPI_SCK); mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530); } static void mcp6x_bitbang_set_mosi(int val) { mcp_gpiostate &= ~(1 << MCP6X_SPI_MOSI); mcp_gpiostate |= (val << MCP6X_SPI_MOSI); mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530); } static int mcp6x_bitbang_get_miso(void) { mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530); return (mcp_gpiostate >> MCP6X_SPI_MISO) & 0x1; } static const struct bitbang_spi_master bitbang_spi_master_mcp6x = { .set_cs = mcp6x_bitbang_set_cs, .set_sck = mcp6x_bitbang_set_sck, .set_mosi = mcp6x_bitbang_set_mosi, .get_miso = mcp6x_bitbang_get_miso, .request_bus = mcp6x_request_spibus, .release_bus = mcp6x_release_spibus, .half_period = 0, }; int mcp6x_spi_init(int want_spi) { uint16_t status; uint32_t mcp6x_spibaraddr; struct pci_dev *smbusdev; /* Look for the SMBus device (SMBus PCI class) */ smbusdev = pci_dev_find_vendorclass(0x10de, 0x0c05); if (!smbusdev) { if (want_spi) { msg_perr("ERROR: SMBus device not found. Not enabling " "SPI.\n"); return 1; } else { msg_pinfo("Odd. SMBus device not found.\n"); return 0; } } msg_pdbg("Found SMBus device %04x:%04x at %02x:%02x:%01x\n", smbusdev->vendor_id, smbusdev->device_id, smbusdev->bus, smbusdev->dev, smbusdev->func); /* Locate the BAR where the SPI interface lives. */ mcp6x_spibaraddr = pci_read_long(smbusdev, 0x74); /* BAR size is 64k, bits 15..4 are zero, bit 3..0 declare a * 32-bit non-prefetchable memory BAR. */ mcp6x_spibaraddr &= ~0xffff; msg_pdbg("MCP SPI BAR is at 0x%08x\n", mcp6x_spibaraddr); /* Accessing a NULL pointer BAR is evil. Don't do it. */ if (!mcp6x_spibaraddr && want_spi) { msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR is invalid.\n"); return 1; } else if (!mcp6x_spibaraddr && !want_spi) { msg_pdbg("MCP SPI is not used.\n"); return 0; } else if (mcp6x_spibaraddr && !want_spi) { msg_pdbg("Strange. MCP SPI BAR is valid, but chipset apparently doesn't have SPI enabled.\n"); /* FIXME: Should we enable SPI anyway? */ return 0; } /* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */ mcp6x_spibar = rphysmap("NVIDIA MCP6x SPI", mcp6x_spibaraddr, 0x544); if (mcp6x_spibar == ERROR_PTR) return 1; status = mmio_readw(mcp6x_spibar + 0x530); msg_pdbg("SPI control is 0x%04x, req=%i, gnt=%i\n", status, (status >> MCP6X_SPI_REQUEST) & 0x1, (status >> MCP6X_SPI_GRANT) & 0x1); mcp_gpiostate = status & 0xff; if (register_spi_bitbang_master(&bitbang_spi_master_mcp6x)) { /* This should never happen. */ msg_perr("MCP6X bitbang SPI master init failed!\n"); return 1; } return 0; } #endif 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
/*
 * This file is part of the flashrom project.
 *
 * Copyright (C) 2009 Sean Nelson <audiohacked@gmail.com>
 * Copyright (C) 2011 Carl-Daniel Hailfinger
 *
 * 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 <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include "flash.h"

enum flashrom_log_level verbose_screen = FLASHROM_MSG_INFO;
enum flashrom_log_level verbose_logfile = FLASHROM_MSG_DEBUG2;

#ifndef STANDALONE
static FILE *logfile = NULL;

int close_logfile(void)
{
	if (!logfile)
		return 0;
	/* No need to call fflush() explicitly, fclose() already does that. */
	if (fclose(logfile)) {
		/* fclose returned an error. Stop writing to be safe. */
		logfile = NULL;
		msg_gerr("Closing the log file returned error %s\n", strerror(errno));
		return 1;
	}
	logfile = NULL;
	return 0;
}

int open_logfile(const char * const filename)
{
	if (!filename) {
		msg_gerr("No logfile name specified.\n");
		return 1;
	}
	if ((logfile = fopen(filename, "w")) == NULL) {
		msg_gerr("Error: opening log file \"%s\" failed: %s\n", filename, strerror(errno));
		return 1;
	}
	return 0;
}

void start_logging(void)
{
	enum flashrom_log_level oldverbose_screen = verbose_screen;

	/* Shut up the console. */
	verbose_screen = FLASHROM_MSG_ERROR;
	print_version();
	verbose_screen = oldverbose_screen;
}
#endif /* !STANDALONE */

/* Please note that level is the verbosity, not the importance of the message. */
int flashrom_print_cb(enum flashrom_log_level level, const char *fmt, va_list ap)
{
	int ret = 0;
	FILE *output_type = stdout;

	va_list logfile_args;
	va_copy(logfile_args, ap);

	if (level < FLASHROM_MSG_INFO)
		output_type = stderr;

	if (level <= verbose_screen) {
		ret = vfprintf(output_type, fmt, ap);
		/* msg_*spew often happens inside chip accessors in possibly
		 * time-critical operations. Don't slow them down by flushing. */
		if (level != FLASHROM_MSG_SPEW)
			fflush(output_type);
	}
#ifndef STANDALONE
	if ((level <= verbose_logfile) && logfile) {
		ret = vfprintf(logfile, fmt, logfile_args);
		if (level != FLASHROM_MSG_SPEW)
			fflush(logfile);
	}
#endif /* !STANDALONE */
	va_end(logfile_args);
	return ret;
}