blob: 38e9a8b20e86faa8df9efcecf4f399b111cfdab8 [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
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000028#include <stdlib.h>
29#include <ctype.h>
30#include "flash.h"
31#include "programmer.h"
Patrick Georgi32508eb2012-07-20 20:35:14 +000032#include "hwaccess.h"
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000033
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
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000045/* Cached value of last GPIO state. */
46static uint8_t mcp_gpiostate;
47
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000048static void mcp6x_request_spibus(void)
49{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000050 mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
51 mcp_gpiostate |= 1 << MCP6X_SPI_REQUEST;
52 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000053
54 /* Wait until we are allowed to use the SPI bus. */
55 while (!(mmio_readw(mcp6x_spibar + 0x530) & (1 << MCP6X_SPI_GRANT))) ;
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000056
57 /* Update the cache. */
58 mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000059}
60
61static void mcp6x_release_spibus(void)
62{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000063 mcp_gpiostate &= ~(1 << MCP6X_SPI_REQUEST);
64 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000065}
66
67static void mcp6x_bitbang_set_cs(int val)
68{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000069 mcp_gpiostate &= ~(1 << MCP6X_SPI_CS);
70 mcp_gpiostate |= (val << MCP6X_SPI_CS);
71 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000072}
73
74static void mcp6x_bitbang_set_sck(int val)
75{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000076 mcp_gpiostate &= ~(1 << MCP6X_SPI_SCK);
77 mcp_gpiostate |= (val << MCP6X_SPI_SCK);
78 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000079}
80
81static void mcp6x_bitbang_set_mosi(int val)
82{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000083 mcp_gpiostate &= ~(1 << MCP6X_SPI_MOSI);
84 mcp_gpiostate |= (val << MCP6X_SPI_MOSI);
85 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000086}
87
88static int mcp6x_bitbang_get_miso(void)
89{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000090 mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
91 return (mcp_gpiostate >> MCP6X_SPI_MISO) & 0x1;
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000092}
93
94static const struct bitbang_spi_master bitbang_spi_master_mcp6x = {
95 .type = BITBANG_SPI_MASTER_MCP,
96 .set_cs = mcp6x_bitbang_set_cs,
97 .set_sck = mcp6x_bitbang_set_sck,
98 .set_mosi = mcp6x_bitbang_set_mosi,
99 .get_miso = mcp6x_bitbang_get_miso,
Carl-Daniel Hailfinger28228882010-09-15 00:17:37 +0000100 .request_bus = mcp6x_request_spibus,
101 .release_bus = mcp6x_release_spibus,
Carl-Daniel Hailfingerc40cff72011-12-20 00:19:29 +0000102 .half_period = 0,
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000103};
104
105int mcp6x_spi_init(int want_spi)
106{
107 uint16_t status;
108 uint32_t mcp6x_spibaraddr;
109 struct pci_dev *smbusdev;
110
111 /* Look for the SMBus device (SMBus PCI class) */
112 smbusdev = pci_dev_find_vendorclass(0x10de, 0x0c05);
113 if (!smbusdev) {
114 if (want_spi) {
115 msg_perr("ERROR: SMBus device not found. Not enabling "
116 "SPI.\n");
117 return 1;
118 } else {
119 msg_pinfo("Odd. SMBus device not found.\n");
120 return 0;
121 }
122 }
123 msg_pdbg("Found SMBus device %04x:%04x at %02x:%02x:%01x\n",
124 smbusdev->vendor_id, smbusdev->device_id,
125 smbusdev->bus, smbusdev->dev, smbusdev->func);
126
127
128 /* Locate the BAR where the SPI interface lives. */
129 mcp6x_spibaraddr = pci_read_long(smbusdev, 0x74);
130 /* BAR size is 64k, bits 15..4 are zero, bit 3..0 declare a
131 * 32-bit non-prefetchable memory BAR.
132 */
133 mcp6x_spibaraddr &= ~0xffff;
134 msg_pdbg("MCP SPI BAR is at 0x%08x\n", mcp6x_spibaraddr);
135
136 /* Accessing a NULL pointer BAR is evil. Don't do it. */
137 if (!mcp6x_spibaraddr && want_spi) {
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000138 msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR is invalid.\n");
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000139 return 1;
140 } else if (!mcp6x_spibaraddr && !want_spi) {
141 msg_pdbg("MCP SPI is not used.\n");
142 return 0;
143 } else if (mcp6x_spibaraddr && !want_spi) {
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000144 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 +0000145 /* FIXME: Should we enable SPI anyway? */
146 return 0;
147 }
148 /* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000149 mcp6x_spibar = rphysmap("NVIDIA MCP6x SPI", mcp6x_spibaraddr, 0x544);
150 if (mcp6x_spibar == ERROR_PTR)
151 return 1;
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000152
153 status = mmio_readw(mcp6x_spibar + 0x530);
154 msg_pdbg("SPI control is 0x%04x, req=%i, gnt=%i\n",
155 status, (status >> MCP6X_SPI_REQUEST) & 0x1,
156 (status >> MCP6X_SPI_GRANT) & 0x1);
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +0000157 mcp_gpiostate = status & 0xff;
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000158
Carl-Daniel Hailfingera5bcbce2014-07-19 22:03:29 +0000159 if (register_spi_bitbang_master(&bitbang_spi_master_mcp6x)) {
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000160 /* This should never happen. */
161 msg_perr("MCP6X bitbang SPI master init failed!\n");
162 return 1;
163 }
164
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000165 return 0;
166}
167
168#endif