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
16 /* Driver for the NVIDIA MCP6x/MCP7x MCP6X_SPI controller.
17 * Based on clean room reverse engineered docs from
18 * https://flashrom.org/pipermail/flashrom/2009-December/001180.html
19 * created by Michael Karcher.
20 */
21
22 #if defined(__i386__) || defined(__x86_64__)
23
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include "flash.h"
27 #include "programmer.h"
28 #include "hwaccess.h"
29
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
39 static void *mcp6x_spibar = NULL;
40
41 /* Cached value of last GPIO state. */
42 static uint8_t mcp_gpiostate;
43
mcp6x_request_spibus(void)44 static void mcp6x_request_spibus(void)
45 {
46 mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
47 mcp_gpiostate |= 1 << MCP6X_SPI_REQUEST;
48 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
49
50 /* Wait until we are allowed to use the SPI bus. */
51 while (!(mmio_readw(mcp6x_spibar + 0x530) & (1 << MCP6X_SPI_GRANT))) ;
52
53 /* Update the cache. */
54 mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
55 }
56
mcp6x_release_spibus(void)57 static void mcp6x_release_spibus(void)
58 {
59 mcp_gpiostate &= ~(1 << MCP6X_SPI_REQUEST);
60 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
61 }
62
mcp6x_bitbang_set_cs(int val)63 static void mcp6x_bitbang_set_cs(int val)
64 {
65 mcp_gpiostate &= ~(1 << MCP6X_SPI_CS);
66 mcp_gpiostate |= (val << MCP6X_SPI_CS);
67 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
68 }
69
mcp6x_bitbang_set_sck(int val)70 static void mcp6x_bitbang_set_sck(int val)
71 {
72 mcp_gpiostate &= ~(1 << MCP6X_SPI_SCK);
73 mcp_gpiostate |= (val << MCP6X_SPI_SCK);
74 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
75 }
76
mcp6x_bitbang_set_mosi(int val)77 static void mcp6x_bitbang_set_mosi(int val)
78 {
79 mcp_gpiostate &= ~(1 << MCP6X_SPI_MOSI);
80 mcp_gpiostate |= (val << MCP6X_SPI_MOSI);
81 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
82 }
83
mcp6x_bitbang_get_miso(void)84 static int mcp6x_bitbang_get_miso(void)
85 {
86 mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
87 return (mcp_gpiostate >> MCP6X_SPI_MISO) & 0x1;
88 }
89
90 static const struct bitbang_spi_master bitbang_spi_master_mcp6x = {
91 .set_cs = mcp6x_bitbang_set_cs,
92 .set_sck = mcp6x_bitbang_set_sck,
93 .set_mosi = mcp6x_bitbang_set_mosi,
94 .get_miso = mcp6x_bitbang_get_miso,
95 .request_bus = mcp6x_request_spibus,
96 .release_bus = mcp6x_release_spibus,
97 .half_period = 0,
98 };
99
mcp6x_spi_init(int want_spi)100 int mcp6x_spi_init(int want_spi)
101 {
102 uint16_t status;
103 uint32_t mcp6x_spibaraddr;
104 struct pci_dev *smbusdev;
105
106 /* Look for the SMBus device (SMBus PCI class) */
107 smbusdev = pci_dev_find_vendorclass(0x10de, 0x0c05);
108 if (!smbusdev) {
109 if (want_spi) {
110 msg_perr("ERROR: SMBus device not found. Not enabling "
111 "SPI.\n");
112 return 1;
113 } else {
114 msg_pinfo("Odd. SMBus device not found.\n");
115 return 0;
116 }
117 }
118 msg_pdbg("Found SMBus device %04x:%04x at %02x:%02x:%01x\n",
119 smbusdev->vendor_id, smbusdev->device_id,
120 smbusdev->bus, smbusdev->dev, smbusdev->func);
121
122
123 /* Locate the BAR where the SPI interface lives. */
124 mcp6x_spibaraddr = pci_read_long(smbusdev, 0x74);
125 /* BAR size is 64k, bits 15..4 are zero, bit 3..0 declare a
126 * 32-bit non-prefetchable memory BAR.
127 */
128 mcp6x_spibaraddr &= ~0xffff;
129 msg_pdbg("MCP SPI BAR is at 0x%08x\n", mcp6x_spibaraddr);
130
131 /* Accessing a NULL pointer BAR is evil. Don't do it. */
132 if (!mcp6x_spibaraddr && want_spi) {
133 msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR is invalid.\n");
134 return 1;
135 } else if (!mcp6x_spibaraddr && !want_spi) {
136 msg_pdbg("MCP SPI is not used.\n");
137 return 0;
138 } else if (mcp6x_spibaraddr && !want_spi) {
139 msg_pdbg("Strange. MCP SPI BAR is valid, but chipset apparently doesn't have SPI enabled.\n");
140 /* FIXME: Should we enable SPI anyway? */
141 return 0;
142 }
143 /* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */
144 mcp6x_spibar = rphysmap("NVIDIA MCP6x SPI", mcp6x_spibaraddr, 0x544);
145 if (mcp6x_spibar == ERROR_PTR)
146 return 1;
147
148 status = mmio_readw(mcp6x_spibar + 0x530);
149 msg_pdbg("SPI control is 0x%04x, req=%i, gnt=%i\n",
150 status, (status >> MCP6X_SPI_REQUEST) & 0x1,
151 (status >> MCP6X_SPI_GRANT) & 0x1);
152 mcp_gpiostate = status & 0xff;
153
154 if (register_spi_bitbang_master(&bitbang_spi_master_mcp6x)) {
155 /* This should never happen. */
156 msg_perr("MCP6X bitbang SPI master init failed!\n");
157 return 1;
158 }
159
160 return 0;
161 }
162
163 #endif
164