blob: b392f382964bed359cdb5ec5c44fb6bc7d785a8d [file] [log] [blame]
hailfinger52384c92010-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 *
hailfinger52384c92010-07-28 15:08:35 +000015 */
16
uwee15beb92010-08-08 17:01:18 +000017/* Driver for the NVIDIA MCP6x/MCP7x MCP6X_SPI controller.
hailfinger52384c92010-07-28 15:08:35 +000018 * Based on clean room reverse engineered docs from
Patrick Georgie39d6442017-03-22 21:23:35 +010019 * https://flashrom.org/pipermail/flashrom/2009-December/001180.html
hailfinger52384c92010-07-28 15:08:35 +000020 * created by Michael Karcher.
21 */
22
23#if defined(__i386__) || defined(__x86_64__)
24
hailfinger52384c92010-07-28 15:08:35 +000025#include <stdlib.h>
26#include <ctype.h>
27#include "flash.h"
28#include "programmer.h"
Patrick Georgib6e26e62017-04-11 20:24:22 +020029#include "hwaccess.h"
hailfinger52384c92010-07-28 15:08:35 +000030
31/* Bit positions for each pin. */
32
33#define MCP6X_SPI_CS 1
34#define MCP6X_SPI_SCK 2
35#define MCP6X_SPI_MOSI 3
36#define MCP6X_SPI_MISO 4
37#define MCP6X_SPI_REQUEST 0
38#define MCP6X_SPI_GRANT 8
39
Edward O'Callaghanef4e28b2019-06-28 13:18:41 +100040static void *mcp6x_spibar = NULL;
hailfinger52384c92010-07-28 15:08:35 +000041
hailfinger935a20c2010-09-14 01:29:49 +000042/* Cached value of last GPIO state. */
43static uint8_t mcp_gpiostate;
44
hailfinger52384c92010-07-28 15:08:35 +000045static void mcp6x_request_spibus(void)
46{
hailfinger935a20c2010-09-14 01:29:49 +000047 mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
48 mcp_gpiostate |= 1 << MCP6X_SPI_REQUEST;
49 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
hailfinger52384c92010-07-28 15:08:35 +000050
51 /* Wait until we are allowed to use the SPI bus. */
52 while (!(mmio_readw(mcp6x_spibar + 0x530) & (1 << MCP6X_SPI_GRANT))) ;
hailfinger935a20c2010-09-14 01:29:49 +000053
54 /* Update the cache. */
55 mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
hailfinger52384c92010-07-28 15:08:35 +000056}
57
58static void mcp6x_release_spibus(void)
59{
hailfinger935a20c2010-09-14 01:29:49 +000060 mcp_gpiostate &= ~(1 << MCP6X_SPI_REQUEST);
61 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
hailfinger52384c92010-07-28 15:08:35 +000062}
63
64static void mcp6x_bitbang_set_cs(int val)
65{
hailfinger935a20c2010-09-14 01:29:49 +000066 mcp_gpiostate &= ~(1 << MCP6X_SPI_CS);
67 mcp_gpiostate |= (val << MCP6X_SPI_CS);
68 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
hailfinger52384c92010-07-28 15:08:35 +000069}
70
71static void mcp6x_bitbang_set_sck(int val)
72{
hailfinger935a20c2010-09-14 01:29:49 +000073 mcp_gpiostate &= ~(1 << MCP6X_SPI_SCK);
74 mcp_gpiostate |= (val << MCP6X_SPI_SCK);
75 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
hailfinger52384c92010-07-28 15:08:35 +000076}
77
78static void mcp6x_bitbang_set_mosi(int val)
79{
hailfinger935a20c2010-09-14 01:29:49 +000080 mcp_gpiostate &= ~(1 << MCP6X_SPI_MOSI);
81 mcp_gpiostate |= (val << MCP6X_SPI_MOSI);
82 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
hailfinger52384c92010-07-28 15:08:35 +000083}
84
85static int mcp6x_bitbang_get_miso(void)
86{
hailfinger935a20c2010-09-14 01:29:49 +000087 mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
88 return (mcp_gpiostate >> MCP6X_SPI_MISO) & 0x1;
hailfinger52384c92010-07-28 15:08:35 +000089}
90
91static const struct bitbang_spi_master bitbang_spi_master_mcp6x = {
92 .type = BITBANG_SPI_MASTER_MCP,
93 .set_cs = mcp6x_bitbang_set_cs,
94 .set_sck = mcp6x_bitbang_set_sck,
95 .set_mosi = mcp6x_bitbang_set_mosi,
96 .get_miso = mcp6x_bitbang_get_miso,
hailfinger12cba9a2010-09-15 00:17:37 +000097 .request_bus = mcp6x_request_spibus,
98 .release_bus = mcp6x_release_spibus,
Patrick Georgie081d5d2017-03-22 21:18:18 +010099 .half_period = 0,
hailfinger52384c92010-07-28 15:08:35 +0000100};
101
102int mcp6x_spi_init(int want_spi)
103{
104 uint16_t status;
105 uint32_t mcp6x_spibaraddr;
106 struct pci_dev *smbusdev;
107
108 /* Look for the SMBus device (SMBus PCI class) */
109 smbusdev = pci_dev_find_vendorclass(0x10de, 0x0c05);
110 if (!smbusdev) {
111 if (want_spi) {
112 msg_perr("ERROR: SMBus device not found. Not enabling "
113 "SPI.\n");
114 return 1;
115 } else {
116 msg_pinfo("Odd. SMBus device not found.\n");
117 return 0;
118 }
119 }
120 msg_pdbg("Found SMBus device %04x:%04x at %02x:%02x:%01x\n",
121 smbusdev->vendor_id, smbusdev->device_id,
122 smbusdev->bus, smbusdev->dev, smbusdev->func);
123
124
125 /* Locate the BAR where the SPI interface lives. */
126 mcp6x_spibaraddr = pci_read_long(smbusdev, 0x74);
127 /* BAR size is 64k, bits 15..4 are zero, bit 3..0 declare a
128 * 32-bit non-prefetchable memory BAR.
129 */
130 mcp6x_spibaraddr &= ~0xffff;
131 msg_pdbg("MCP SPI BAR is at 0x%08x\n", mcp6x_spibaraddr);
132
133 /* Accessing a NULL pointer BAR is evil. Don't do it. */
134 if (!mcp6x_spibaraddr && want_spi) {
Patrick Georgie39d6442017-03-22 21:23:35 +0100135 msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR is invalid.\n");
hailfinger52384c92010-07-28 15:08:35 +0000136 return 1;
137 } else if (!mcp6x_spibaraddr && !want_spi) {
138 msg_pdbg("MCP SPI is not used.\n");
139 return 0;
140 } else if (mcp6x_spibaraddr && !want_spi) {
Patrick Georgie39d6442017-03-22 21:23:35 +0100141 msg_pdbg("Strange. MCP SPI BAR is valid, but chipset apparently doesn't have SPI enabled.\n");
hailfinger52384c92010-07-28 15:08:35 +0000142 /* FIXME: Should we enable SPI anyway? */
143 return 0;
144 }
145 /* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */
Patrick Georgi124bd002017-03-21 17:25:59 +0100146 mcp6x_spibar = rphysmap("NVIDIA MCP6x SPI", mcp6x_spibaraddr, 0x544);
147 if (mcp6x_spibar == ERROR_PTR)
148 return 1;
hailfinger52384c92010-07-28 15:08:35 +0000149
150 status = mmio_readw(mcp6x_spibar + 0x530);
151 msg_pdbg("SPI control is 0x%04x, req=%i, gnt=%i\n",
152 status, (status >> MCP6X_SPI_REQUEST) & 0x1,
153 (status >> MCP6X_SPI_GRANT) & 0x1);
hailfinger935a20c2010-09-14 01:29:49 +0000154 mcp_gpiostate = status & 0xff;
hailfinger52384c92010-07-28 15:08:35 +0000155
Patrick Georgie081d5d2017-03-22 21:18:18 +0100156 if (register_spi_bitbang_master(&bitbang_spi_master_mcp6x)) {
hailfinger52384c92010-07-28 15:08:35 +0000157 /* This should never happen. */
158 msg_perr("MCP6X bitbang SPI master init failed!\n");
159 return 1;
160 }
161
hailfinger52384c92010-07-28 15:08:35 +0000162 return 0;
163}
164
165#endif