Flashrom

Flashrom Svn Source Tree

Root/trunk/mcp6x_spi.c

1/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2010 Carl-Daniel Hailfinger
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20/* Driver for the NVIDIA MCP6x/MCP7x MCP6X_SPI controller.
21 * Based on clean room reverse engineered docs from
22 * http://www.flashrom.org/pipermail/flashrom/2009-December/001180.html
23 * created by Michael Karcher.
24 */
25
26#if defined(__i386__) || defined(__x86_64__)
27
28#include <stdlib.h>
29#include <ctype.h>
30#include "flash.h"
31#include "programmer.h"
32
33/* Bit positions for each pin. */
34
35#define MCP6X_SPI_CS1
36#define MCP6X_SPI_SCK2
37#define MCP6X_SPI_MOSI3
38#define MCP6X_SPI_MISO4
39#define MCP6X_SPI_REQUEST0
40#define MCP6X_SPI_GRANT8
41
42void *mcp6x_spibar = NULL;
43
44/* Cached value of last GPIO state. */
45static uint8_t mcp_gpiostate;
46
47static void mcp6x_request_spibus(void)
48{
49mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
50mcp_gpiostate |= 1 << MCP6X_SPI_REQUEST;
51mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
52
53/* Wait until we are allowed to use the SPI bus. */
54while (!(mmio_readw(mcp6x_spibar + 0x530) & (1 << MCP6X_SPI_GRANT))) ;
55
56/* Update the cache. */
57mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
58}
59
60static void mcp6x_release_spibus(void)
61{
62mcp_gpiostate &= ~(1 << MCP6X_SPI_REQUEST);
63mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
64}
65
66static void mcp6x_bitbang_set_cs(int val)
67{
68mcp_gpiostate &= ~(1 << MCP6X_SPI_CS);
69mcp_gpiostate |= (val << MCP6X_SPI_CS);
70mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
71}
72
73static void mcp6x_bitbang_set_sck(int val)
74{
75mcp_gpiostate &= ~(1 << MCP6X_SPI_SCK);
76mcp_gpiostate |= (val << MCP6X_SPI_SCK);
77mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
78}
79
80static void mcp6x_bitbang_set_mosi(int val)
81{
82mcp_gpiostate &= ~(1 << MCP6X_SPI_MOSI);
83mcp_gpiostate |= (val << MCP6X_SPI_MOSI);
84mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
85}
86
87static int mcp6x_bitbang_get_miso(void)
88{
89mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
90return (mcp_gpiostate >> MCP6X_SPI_MISO) & 0x1;
91}
92
93static const struct bitbang_spi_master bitbang_spi_master_mcp6x = {
94.type = BITBANG_SPI_MASTER_MCP,
95.set_cs = mcp6x_bitbang_set_cs,
96.set_sck = mcp6x_bitbang_set_sck,
97.set_mosi = mcp6x_bitbang_set_mosi,
98.get_miso = mcp6x_bitbang_get_miso,
99.request_bus = mcp6x_request_spibus,
100.release_bus = mcp6x_release_spibus,
101.half_period = 0,
102};
103
104int mcp6x_spi_init(int want_spi)
105{
106uint16_t status;
107uint32_t mcp6x_spibaraddr;
108struct pci_dev *smbusdev;
109
110/* Look for the SMBus device (SMBus PCI class) */
111smbusdev = pci_dev_find_vendorclass(0x10de, 0x0c05);
112if (!smbusdev) {
113if (want_spi) {
114msg_perr("ERROR: SMBus device not found. Not enabling "
115 "SPI.\n");
116return 1;
117} else {
118msg_pinfo("Odd. SMBus device not found.\n");
119return 0;
120}
121}
122msg_pdbg("Found SMBus device %04x:%04x at %02x:%02x:%01x\n",
123smbusdev->vendor_id, smbusdev->device_id,
124smbusdev->bus, smbusdev->dev, smbusdev->func);
125
126
127/* Locate the BAR where the SPI interface lives. */
128mcp6x_spibaraddr = pci_read_long(smbusdev, 0x74);
129/* BAR size is 64k, bits 15..4 are zero, bit 3..0 declare a
130 * 32-bit non-prefetchable memory BAR.
131 */
132mcp6x_spibaraddr &= ~0xffff;
133msg_pdbg("MCP SPI BAR is at 0x%08x\n", mcp6x_spibaraddr);
134
135/* Accessing a NULL pointer BAR is evil. Don't do it. */
136if (!mcp6x_spibaraddr && want_spi) {
137msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR "
138 "is invalid.\n");
139return 1;
140} else if (!mcp6x_spibaraddr && !want_spi) {
141msg_pdbg("MCP SPI is not used.\n");
142return 0;
143} else if (mcp6x_spibaraddr && !want_spi) {
144msg_pdbg("Strange. MCP SPI BAR is valid, but chipset apparently"
145 " doesn't have SPI enabled.\n");
146/* FIXME: Should we enable SPI anyway? */
147return 0;
148}
149/* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */
150mcp6x_spibar = physmap("NVIDIA MCP6x SPI", mcp6x_spibaraddr, 0x544);
151
152#if 0
153/* FIXME: Run the physunmap in a shutdown function. */
154physunmap(mcp6x_spibar, 0x544);
155#endif
156
157status = mmio_readw(mcp6x_spibar + 0x530);
158msg_pdbg("SPI control is 0x%04x, req=%i, gnt=%i\n",
159 status, (status >> MCP6X_SPI_REQUEST) & 0x1,
160 (status >> MCP6X_SPI_GRANT) & 0x1);
161mcp_gpiostate = status & 0xff;
162
163if (bitbang_spi_init(&bitbang_spi_master_mcp6x)) {
164/* This should never happen. */
165msg_perr("MCP6X bitbang SPI master init failed!\n");
166return 1;
167}
168
169return 0;
170}
171
172#endif
173

Archive Download this file

Revision: HEAD