blob: fa309ca3cfc036aa0f4ec00b8447317be8adf6a0 [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.
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000014 */
15
Uwe Hermann48ec1b12010-08-08 17:01:18 +000016/* Driver for the NVIDIA MCP6x/MCP7x MCP6X_SPI controller.
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000017 * Based on clean room reverse engineered docs from
Stefan Tauner4c723152016-01-14 22:47:55 +000018 * https://flashrom.org/pipermail/flashrom/2009-December/001180.html
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000019 * created by Michael Karcher.
20 */
21
22#if defined(__i386__) || defined(__x86_64__)
23
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000024#include <stdlib.h>
25#include <ctype.h>
26#include "flash.h"
27#include "programmer.h"
Patrick Georgi32508eb2012-07-20 20:35:14 +000028#include "hwaccess.h"
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000029
30/* Bit positions for each pin. */
31
32#define MCP6X_SPI_CS 1
33#define MCP6X_SPI_SCK 2
34#define MCP6X_SPI_MOSI 3
35#define MCP6X_SPI_MISO 4
36#define MCP6X_SPI_REQUEST 0
37#define MCP6X_SPI_GRANT 8
38
Anastasia Klimchuk4a7d2592021-05-26 13:23:26 +100039struct mcp6x_spi_data {
40 void *spibar;
41 /* Cached value of last GPIO state. */
42 uint8_t gpiostate;
43};
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000044
Anastasia Klimchuk5f5eaeb2021-05-26 09:54:08 +100045static void mcp6x_request_spibus(void *spi_data)
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000046{
Anastasia Klimchuk4a7d2592021-05-26 13:23:26 +100047 struct mcp6x_spi_data *data = spi_data;
48
49 data->gpiostate = mmio_readb(data->spibar + 0x530);
50 data->gpiostate |= 1 << MCP6X_SPI_REQUEST;
51 mmio_writeb(data->gpiostate, data->spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000052
53 /* Wait until we are allowed to use the SPI bus. */
Anastasia Klimchuk4a7d2592021-05-26 13:23:26 +100054 while (!(mmio_readw(data->spibar + 0x530) & (1 << MCP6X_SPI_GRANT))) ;
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000055
56 /* Update the cache. */
Anastasia Klimchuk4a7d2592021-05-26 13:23:26 +100057 data->gpiostate = mmio_readb(data->spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000058}
59
Anastasia Klimchuk5f5eaeb2021-05-26 09:54:08 +100060static void mcp6x_release_spibus(void *spi_data)
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000061{
Anastasia Klimchuk4a7d2592021-05-26 13:23:26 +100062 struct mcp6x_spi_data *data = spi_data;
63
64 data->gpiostate &= ~(1 << MCP6X_SPI_REQUEST);
65 mmio_writeb(data->gpiostate, data->spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000066}
67
Anastasia Klimchuk5f5eaeb2021-05-26 09:54:08 +100068static void mcp6x_bitbang_set_cs(int val, void *spi_data)
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000069{
Anastasia Klimchuk4a7d2592021-05-26 13:23:26 +100070 struct mcp6x_spi_data *data = spi_data;
71
72 data->gpiostate &= ~(1 << MCP6X_SPI_CS);
73 data->gpiostate |= (val << MCP6X_SPI_CS);
74 mmio_writeb(data->gpiostate, data->spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000075}
76
Anastasia Klimchuk5f5eaeb2021-05-26 09:54:08 +100077static void mcp6x_bitbang_set_sck(int val, void *spi_data)
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000078{
Anastasia Klimchuk4a7d2592021-05-26 13:23:26 +100079 struct mcp6x_spi_data *data = spi_data;
80
81 data->gpiostate &= ~(1 << MCP6X_SPI_SCK);
82 data->gpiostate |= (val << MCP6X_SPI_SCK);
83 mmio_writeb(data->gpiostate, data->spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000084}
85
Anastasia Klimchuk5f5eaeb2021-05-26 09:54:08 +100086static void mcp6x_bitbang_set_mosi(int val, void *spi_data)
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000087{
Anastasia Klimchuk4a7d2592021-05-26 13:23:26 +100088 struct mcp6x_spi_data *data = spi_data;
89
90 data->gpiostate &= ~(1 << MCP6X_SPI_MOSI);
91 data->gpiostate |= (val << MCP6X_SPI_MOSI);
92 mmio_writeb(data->gpiostate, data->spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000093}
94
Anastasia Klimchuk5f5eaeb2021-05-26 09:54:08 +100095static int mcp6x_bitbang_get_miso(void *spi_data)
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000096{
Anastasia Klimchuk4a7d2592021-05-26 13:23:26 +100097 struct mcp6x_spi_data *data = spi_data;
98
99 data->gpiostate = mmio_readb(data->spibar + 0x530);
100 return (data->gpiostate >> MCP6X_SPI_MISO) & 0x1;
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000101}
102
103static const struct bitbang_spi_master bitbang_spi_master_mcp6x = {
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000104 .set_cs = mcp6x_bitbang_set_cs,
105 .set_sck = mcp6x_bitbang_set_sck,
106 .set_mosi = mcp6x_bitbang_set_mosi,
107 .get_miso = mcp6x_bitbang_get_miso,
Carl-Daniel Hailfinger28228882010-09-15 00:17:37 +0000108 .request_bus = mcp6x_request_spibus,
109 .release_bus = mcp6x_release_spibus,
Carl-Daniel Hailfingerc40cff72011-12-20 00:19:29 +0000110 .half_period = 0,
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000111};
112
Anastasia Klimchuk4a7d2592021-05-26 13:23:26 +1000113static int mcp6x_shutdown(void *spi_data)
114{
115 free(spi_data);
116 return 0;
117}
118
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000119int mcp6x_spi_init(int want_spi)
120{
121 uint16_t status;
122 uint32_t mcp6x_spibaraddr;
123 struct pci_dev *smbusdev;
Anastasia Klimchuk4a7d2592021-05-26 13:23:26 +1000124 void *mcp6x_spibar = NULL;
125 uint8_t mcp_gpiostate;
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000126
127 /* Look for the SMBus device (SMBus PCI class) */
128 smbusdev = pci_dev_find_vendorclass(0x10de, 0x0c05);
129 if (!smbusdev) {
130 if (want_spi) {
131 msg_perr("ERROR: SMBus device not found. Not enabling "
132 "SPI.\n");
133 return 1;
134 } else {
135 msg_pinfo("Odd. SMBus device not found.\n");
136 return 0;
137 }
138 }
139 msg_pdbg("Found SMBus device %04x:%04x at %02x:%02x:%01x\n",
140 smbusdev->vendor_id, smbusdev->device_id,
141 smbusdev->bus, smbusdev->dev, smbusdev->func);
142
143
144 /* Locate the BAR where the SPI interface lives. */
145 mcp6x_spibaraddr = pci_read_long(smbusdev, 0x74);
146 /* BAR size is 64k, bits 15..4 are zero, bit 3..0 declare a
147 * 32-bit non-prefetchable memory BAR.
148 */
149 mcp6x_spibaraddr &= ~0xffff;
150 msg_pdbg("MCP SPI BAR is at 0x%08x\n", mcp6x_spibaraddr);
151
152 /* Accessing a NULL pointer BAR is evil. Don't do it. */
153 if (!mcp6x_spibaraddr && want_spi) {
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000154 msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR is invalid.\n");
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000155 return 1;
156 } else if (!mcp6x_spibaraddr && !want_spi) {
157 msg_pdbg("MCP SPI is not used.\n");
158 return 0;
159 } else if (mcp6x_spibaraddr && !want_spi) {
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000160 msg_pdbg("Strange. MCP SPI BAR is valid, but chipset apparently doesn't have SPI enabled.\n");
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000161 /* FIXME: Should we enable SPI anyway? */
162 return 0;
163 }
164 /* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000165 mcp6x_spibar = rphysmap("NVIDIA MCP6x SPI", mcp6x_spibaraddr, 0x544);
166 if (mcp6x_spibar == ERROR_PTR)
167 return 1;
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000168
169 status = mmio_readw(mcp6x_spibar + 0x530);
170 msg_pdbg("SPI control is 0x%04x, req=%i, gnt=%i\n",
171 status, (status >> MCP6X_SPI_REQUEST) & 0x1,
172 (status >> MCP6X_SPI_GRANT) & 0x1);
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +0000173 mcp_gpiostate = status & 0xff;
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000174
Anastasia Klimchuk4a7d2592021-05-26 13:23:26 +1000175 struct mcp6x_spi_data *data = calloc(1, sizeof(*data));
176 if (!data) {
177 msg_perr("Unable to allocate space for SPI master data\n");
178 return 1;
179 }
180 data->spibar = mcp6x_spibar;
181 data->gpiostate = mcp_gpiostate;
182
183 if (register_shutdown(mcp6x_shutdown, data)) {
184 free(data);
185 return 1;
186 }
187 if (register_spi_bitbang_master(&bitbang_spi_master_mcp6x, data)) {
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000188 /* This should never happen. */
189 msg_perr("MCP6X bitbang SPI master init failed!\n");
190 return 1;
191 }
192
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000193 return 0;
194}
195
196#endif