blob: 543142bba1a5ea0d8db01d0567a48bbd2fe4e58e [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.
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000014 */
15
Uwe Hermann48ec1b12010-08-08 17:01:18 +000016/* Driver for the NVIDIA MCP6x/MCP7x MCP6X_SPI controller.
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000017 * Based on clean room reverse engineered docs from
Stefan Tauner4c723152016-01-14 22:47:55 +000018 * https://flashrom.org/pipermail/flashrom/2009-December/001180.html
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000019 * created by Michael Karcher.
20 */
21
22#if defined(__i386__) || defined(__x86_64__)
23
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000024#include <stdlib.h>
25#include <ctype.h>
26#include "flash.h"
27#include "programmer.h"
Patrick Georgi32508eb2012-07-20 20:35:14 +000028#include "hwaccess.h"
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000029
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
Jacob Garberafc3ad62019-06-24 16:05:28 -060039static void *mcp6x_spibar = NULL;
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000040
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000041/* Cached value of last GPIO state. */
42static uint8_t mcp_gpiostate;
43
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000044static void mcp6x_request_spibus(void)
45{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000046 mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
47 mcp_gpiostate |= 1 << MCP6X_SPI_REQUEST;
48 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000049
50 /* Wait until we are allowed to use the SPI bus. */
51 while (!(mmio_readw(mcp6x_spibar + 0x530) & (1 << MCP6X_SPI_GRANT))) ;
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000052
53 /* Update the cache. */
54 mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000055}
56
57static void mcp6x_release_spibus(void)
58{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000059 mcp_gpiostate &= ~(1 << MCP6X_SPI_REQUEST);
60 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000061}
62
63static void mcp6x_bitbang_set_cs(int val)
64{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000065 mcp_gpiostate &= ~(1 << MCP6X_SPI_CS);
66 mcp_gpiostate |= (val << MCP6X_SPI_CS);
67 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000068}
69
70static void mcp6x_bitbang_set_sck(int val)
71{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000072 mcp_gpiostate &= ~(1 << MCP6X_SPI_SCK);
73 mcp_gpiostate |= (val << MCP6X_SPI_SCK);
74 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000075}
76
77static void mcp6x_bitbang_set_mosi(int val)
78{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000079 mcp_gpiostate &= ~(1 << MCP6X_SPI_MOSI);
80 mcp_gpiostate |= (val << MCP6X_SPI_MOSI);
81 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000082}
83
84static int mcp6x_bitbang_get_miso(void)
85{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000086 mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
87 return (mcp_gpiostate >> MCP6X_SPI_MISO) & 0x1;
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000088}
89
90static const struct bitbang_spi_master bitbang_spi_master_mcp6x = {
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000091 .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,
Carl-Daniel Hailfinger28228882010-09-15 00:17:37 +000095 .request_bus = mcp6x_request_spibus,
96 .release_bus = mcp6x_release_spibus,
Carl-Daniel Hailfingerc40cff72011-12-20 00:19:29 +000097 .half_period = 0,
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000098};
99
100int 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) {
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000133 msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR is invalid.\n");
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000134 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) {
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000139 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 +0000140 /* FIXME: Should we enable SPI anyway? */
141 return 0;
142 }
143 /* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000144 mcp6x_spibar = rphysmap("NVIDIA MCP6x SPI", mcp6x_spibaraddr, 0x544);
145 if (mcp6x_spibar == ERROR_PTR)
146 return 1;
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000147
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);
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +0000152 mcp_gpiostate = status & 0xff;
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000153
Anastasia Klimchuk30815fc2021-05-31 11:20:01 +1000154 if (register_spi_bitbang_master(&bitbang_spi_master_mcp6x, NULL)) {
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000155 /* This should never happen. */
156 msg_perr("MCP6X bitbang SPI master init failed!\n");
157 return 1;
158 }
159
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000160 return 0;
161}
162
163#endif