xref: /aosp_15_r20/external/flashrom/mcp6x_spi.c (revision 0d6140be3aa665ecc836e8907834fcd3e3b018fc)
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 #include <stdlib.h>
23 #include <ctype.h>
24 #include "flash.h"
25 #include "programmer.h"
26 #include "hwaccess_physmap.h"
27 #include "platform/pci.h"
28 
29 /* Bit positions for each pin. */
30 
31 #define MCP6X_SPI_CS		1
32 #define MCP6X_SPI_SCK		2
33 #define MCP6X_SPI_MOSI		3
34 #define MCP6X_SPI_MISO		4
35 #define MCP6X_SPI_REQUEST	0
36 #define MCP6X_SPI_GRANT		8
37 
38 struct mcp6x_spi_data {
39 	void *spibar;
40 	/* Cached value of last GPIO state. */
41 	uint8_t gpiostate;
42 };
43 
mcp6x_request_spibus(void * spi_data)44 static void mcp6x_request_spibus(void *spi_data)
45 {
46 	struct mcp6x_spi_data *data = spi_data;
47 
48 	data->gpiostate = mmio_readb(data->spibar + 0x530);
49 	data->gpiostate |= 1 << MCP6X_SPI_REQUEST;
50 	mmio_writeb(data->gpiostate, data->spibar + 0x530);
51 
52 	/* Wait until we are allowed to use the SPI bus. */
53 	while (!(mmio_readw(data->spibar + 0x530) & (1 << MCP6X_SPI_GRANT))) ;
54 
55 	/* Update the cache. */
56 	data->gpiostate = mmio_readb(data->spibar + 0x530);
57 }
58 
mcp6x_release_spibus(void * spi_data)59 static void mcp6x_release_spibus(void *spi_data)
60 {
61 	struct mcp6x_spi_data *data = spi_data;
62 
63 	data->gpiostate &= ~(1 << MCP6X_SPI_REQUEST);
64 	mmio_writeb(data->gpiostate, data->spibar + 0x530);
65 }
66 
mcp6x_bitbang_set_cs(int val,void * spi_data)67 static void mcp6x_bitbang_set_cs(int val, void *spi_data)
68 {
69 	struct mcp6x_spi_data *data = spi_data;
70 
71 	data->gpiostate &= ~(1 << MCP6X_SPI_CS);
72 	data->gpiostate |= (val << MCP6X_SPI_CS);
73 	mmio_writeb(data->gpiostate, data->spibar + 0x530);
74 }
75 
mcp6x_bitbang_set_sck(int val,void * spi_data)76 static void mcp6x_bitbang_set_sck(int val, void *spi_data)
77 {
78 	struct mcp6x_spi_data *data = spi_data;
79 
80 	data->gpiostate &= ~(1 << MCP6X_SPI_SCK);
81 	data->gpiostate |= (val << MCP6X_SPI_SCK);
82 	mmio_writeb(data->gpiostate, data->spibar + 0x530);
83 }
84 
mcp6x_bitbang_set_mosi(int val,void * spi_data)85 static void mcp6x_bitbang_set_mosi(int val, void *spi_data)
86 {
87 	struct mcp6x_spi_data *data = spi_data;
88 
89 	data->gpiostate &= ~(1 << MCP6X_SPI_MOSI);
90 	data->gpiostate |= (val << MCP6X_SPI_MOSI);
91 	mmio_writeb(data->gpiostate, data->spibar + 0x530);
92 }
93 
mcp6x_bitbang_get_miso(void * spi_data)94 static int mcp6x_bitbang_get_miso(void *spi_data)
95 {
96 	struct mcp6x_spi_data *data = spi_data;
97 
98 	data->gpiostate = mmio_readb(data->spibar + 0x530);
99 	return (data->gpiostate >> MCP6X_SPI_MISO) & 0x1;
100 }
101 
102 static const struct bitbang_spi_master bitbang_spi_master_mcp6x = {
103 	.set_cs		= mcp6x_bitbang_set_cs,
104 	.set_sck	= mcp6x_bitbang_set_sck,
105 	.set_mosi	= mcp6x_bitbang_set_mosi,
106 	.get_miso	= mcp6x_bitbang_get_miso,
107 	.request_bus	= mcp6x_request_spibus,
108 	.release_bus	= mcp6x_release_spibus,
109 	.half_period	= 0,
110 };
111 
mcp6x_shutdown(void * spi_data)112 static int mcp6x_shutdown(void *spi_data)
113 {
114 	free(spi_data);
115 	return 0;
116 }
117 
mcp6x_spi_init(int want_spi)118 int mcp6x_spi_init(int want_spi)
119 {
120 	uint16_t status;
121 	uint32_t mcp6x_spibaraddr;
122 	struct pci_dev *smbusdev;
123 	void *mcp6x_spibar = NULL;
124 	uint8_t mcp_gpiostate;
125 
126 	/* Look for the SMBus device (SMBus PCI class) */
127 	smbusdev = pcidev_find_vendorclass(0x10de, 0x0c05);
128 	if (!smbusdev) {
129 		if (want_spi) {
130 			msg_perr("ERROR: SMBus device not found. Not enabling "
131 				 "SPI.\n");
132 			return 1;
133 		} else {
134 			msg_pinfo("Odd. SMBus device not found.\n");
135 			return 0;
136 		}
137 	}
138 	msg_pdbg("Found SMBus device %04x:%04x at %02x:%02x:%01x\n",
139 		smbusdev->vendor_id, smbusdev->device_id,
140 		smbusdev->bus, smbusdev->dev, smbusdev->func);
141 
142 
143 	/* Locate the BAR where the SPI interface lives. */
144 	mcp6x_spibaraddr = pci_read_long(smbusdev, 0x74);
145 	/* BAR size is 64k, bits 15..4 are zero, bit 3..0 declare a
146 	 * 32-bit non-prefetchable memory BAR.
147 	 */
148 	mcp6x_spibaraddr &= ~0xffff;
149 	msg_pdbg("MCP SPI BAR is at 0x%08"PRIx32"\n", mcp6x_spibaraddr);
150 
151 	/* Accessing a NULL pointer BAR is evil. Don't do it. */
152 	if (!mcp6x_spibaraddr && want_spi) {
153 		msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR is invalid.\n");
154 		return 1;
155 	} else if (!mcp6x_spibaraddr && !want_spi) {
156 		msg_pdbg("MCP SPI is not used.\n");
157 		return 0;
158 	} else if (mcp6x_spibaraddr && !want_spi) {
159 		msg_pdbg("Strange. MCP SPI BAR is valid, but chipset apparently doesn't have SPI enabled.\n");
160 		/* FIXME: Should we enable SPI anyway? */
161 		return 0;
162 	}
163 	/* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */
164 	mcp6x_spibar = rphysmap("NVIDIA MCP6x SPI", mcp6x_spibaraddr, 0x544);
165 	if (mcp6x_spibar == ERROR_PTR)
166 		return 1;
167 
168 	status = mmio_readw(mcp6x_spibar + 0x530);
169 	msg_pdbg("SPI control is 0x%04x, req=%i, gnt=%i\n",
170 		 status, (status >> MCP6X_SPI_REQUEST) & 0x1,
171 		 (status >> MCP6X_SPI_GRANT) & 0x1);
172 	mcp_gpiostate = status & 0xff;
173 
174 	struct mcp6x_spi_data *data = calloc(1, sizeof(*data));
175 	if (!data) {
176 		msg_perr("Unable to allocate space for SPI master data\n");
177 		return 1;
178 	}
179 	data->spibar = mcp6x_spibar;
180 	data->gpiostate = mcp_gpiostate;
181 
182 	if (register_shutdown(mcp6x_shutdown, data)) {
183 		free(data);
184 		return 1;
185 	}
186 	if (register_spi_bitbang_master(&bitbang_spi_master_mcp6x, data)) {
187 		/* This should never happen. */
188 		msg_perr("MCP6X bitbang SPI master init failed!\n");
189 		return 1;
190 	}
191 
192 	return 0;
193 }
194