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