blob: bfdef2d56588d1069eb8a447a1ca5303467c9bea [file] [log] [blame]
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +00001/*
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
Uwe Hermann48ec1b12010-08-08 17:01:18 +000020/* Driver for the NVIDIA MCP6x/MCP7x MCP6X_SPI controller.
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000021 * 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 <stdint.h>
29#include <stdlib.h>
30#include <ctype.h>
31#include "flash.h"
32#include "programmer.h"
33
34/* Bit positions for each pin. */
35
36#define MCP6X_SPI_CS 1
37#define MCP6X_SPI_SCK 2
38#define MCP6X_SPI_MOSI 3
39#define MCP6X_SPI_MISO 4
40#define MCP6X_SPI_REQUEST 0
41#define MCP6X_SPI_GRANT 8
42
43void *mcp6x_spibar = NULL;
44
45static void mcp6x_request_spibus(void)
46{
47 uint8_t tmp;
48
49 tmp = mmio_readb(mcp6x_spibar + 0x530);
50 tmp |= 1 << MCP6X_SPI_REQUEST;
51 mmio_writeb(tmp, mcp6x_spibar + 0x530);
52
53 /* Wait until we are allowed to use the SPI bus. */
54 while (!(mmio_readw(mcp6x_spibar + 0x530) & (1 << MCP6X_SPI_GRANT))) ;
55}
56
57static void mcp6x_release_spibus(void)
58{
59 uint8_t tmp;
60
61 tmp = mmio_readb(mcp6x_spibar + 0x530);
62 tmp &= ~(1 << MCP6X_SPI_REQUEST);
63 mmio_writeb(tmp, mcp6x_spibar + 0x530);
64}
65
66static void mcp6x_bitbang_set_cs(int val)
67{
68 uint8_t tmp;
69
70 /* Requesting and releasing the SPI bus is handled in here to allow the
71 * chipset to use its own SPI engine for native reads.
72 */
73 if (val == 0)
74 mcp6x_request_spibus();
75
76 tmp = mmio_readb(mcp6x_spibar + 0x530);
77 tmp &= ~(1 << MCP6X_SPI_CS);
78 tmp |= (val << MCP6X_SPI_CS);
79 mmio_writeb(tmp, mcp6x_spibar + 0x530);
80
81 if (val == 1)
82 mcp6x_release_spibus();
83}
84
85static void mcp6x_bitbang_set_sck(int val)
86{
87 uint8_t tmp;
88
89 tmp = mmio_readb(mcp6x_spibar + 0x530);
90 tmp &= ~(1 << MCP6X_SPI_SCK);
91 tmp |= (val << MCP6X_SPI_SCK);
92 mmio_writeb(tmp, mcp6x_spibar + 0x530);
93}
94
95static void mcp6x_bitbang_set_mosi(int val)
96{
97 uint8_t tmp;
98
99 tmp = mmio_readb(mcp6x_spibar + 0x530);
100 tmp &= ~(1 << MCP6X_SPI_MOSI);
101 tmp |= (val << MCP6X_SPI_MOSI);
102 mmio_writeb(tmp, mcp6x_spibar + 0x530);
103}
104
105static int mcp6x_bitbang_get_miso(void)
106{
107 uint8_t tmp;
108
109 tmp = mmio_readb(mcp6x_spibar + 0x530);
110 tmp = (tmp >> MCP6X_SPI_MISO) & 0x1;
111 return tmp;
112}
113
114static const struct bitbang_spi_master bitbang_spi_master_mcp6x = {
115 .type = BITBANG_SPI_MASTER_MCP,
116 .set_cs = mcp6x_bitbang_set_cs,
117 .set_sck = mcp6x_bitbang_set_sck,
118 .set_mosi = mcp6x_bitbang_set_mosi,
119 .get_miso = mcp6x_bitbang_get_miso,
120};
121
122int mcp6x_spi_init(int want_spi)
123{
124 uint16_t status;
125 uint32_t mcp6x_spibaraddr;
126 struct pci_dev *smbusdev;
127
128 /* Look for the SMBus device (SMBus PCI class) */
129 smbusdev = pci_dev_find_vendorclass(0x10de, 0x0c05);
130 if (!smbusdev) {
131 if (want_spi) {
132 msg_perr("ERROR: SMBus device not found. Not enabling "
133 "SPI.\n");
134 return 1;
135 } else {
136 msg_pinfo("Odd. SMBus device not found.\n");
137 return 0;
138 }
139 }
140 msg_pdbg("Found SMBus device %04x:%04x at %02x:%02x:%01x\n",
141 smbusdev->vendor_id, smbusdev->device_id,
142 smbusdev->bus, smbusdev->dev, smbusdev->func);
143
144
145 /* Locate the BAR where the SPI interface lives. */
146 mcp6x_spibaraddr = pci_read_long(smbusdev, 0x74);
147 /* BAR size is 64k, bits 15..4 are zero, bit 3..0 declare a
148 * 32-bit non-prefetchable memory BAR.
149 */
150 mcp6x_spibaraddr &= ~0xffff;
151 msg_pdbg("MCP SPI BAR is at 0x%08x\n", mcp6x_spibaraddr);
152
153 /* Accessing a NULL pointer BAR is evil. Don't do it. */
154 if (!mcp6x_spibaraddr && want_spi) {
155 msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR "
156 "is invalid.\n");
157 return 1;
158 } else if (!mcp6x_spibaraddr && !want_spi) {
159 msg_pdbg("MCP SPI is not used.\n");
160 return 0;
161 } else if (mcp6x_spibaraddr && !want_spi) {
162 msg_pdbg("Strange. MCP SPI BAR is valid, but chipset apparently"
163 " doesn't have SPI enabled.\n");
164 /* FIXME: Should we enable SPI anyway? */
165 return 0;
166 }
167 /* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */
Uwe Hermann48ec1b12010-08-08 17:01:18 +0000168 mcp6x_spibar = physmap("NVIDIA MCP6x SPI", mcp6x_spibaraddr, 0x544);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000169
170#if 0
171 /* FIXME: Run the physunmap in a shutdown function. */
172 physunmap(mcp6x_spibar, 0x544);
173#endif
174
175 status = mmio_readw(mcp6x_spibar + 0x530);
176 msg_pdbg("SPI control is 0x%04x, req=%i, gnt=%i\n",
177 status, (status >> MCP6X_SPI_REQUEST) & 0x1,
178 (status >> MCP6X_SPI_GRANT) & 0x1);
179
180 /* 1 usec halfperiod delay for now. */
181 if (bitbang_spi_init(&bitbang_spi_master_mcp6x, 1)) {
182 /* This should never happen. */
183 msg_perr("MCP6X bitbang SPI master init failed!\n");
184 return 1;
185 }
186
187 buses_supported |= CHIP_BUSTYPE_SPI;
188 spi_controller = SPI_CONTROLLER_MCP6X_BITBANG;
189
190 return 0;
191}
192
193#endif