blob: dd76e609ad185236648c00b7f2474e5450cf4537 [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
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000022#include <stdlib.h>
23#include <ctype.h>
24#include "flash.h"
25#include "programmer.h"
Thomas Heijligen74b4aa02021-12-14 17:52:30 +010026#include "hwaccess_physmap.h"
Thomas Heijligend96c97c2021-11-02 21:03:00 +010027#include "platform/pci.h"
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000028
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
Jacob Garberafc3ad62019-06-24 16:05:28 -060038static void *mcp6x_spibar = NULL;
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000039
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000040/* Cached value of last GPIO state. */
41static uint8_t mcp_gpiostate;
42
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000043static void mcp6x_request_spibus(void)
44{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000045 mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
46 mcp_gpiostate |= 1 << MCP6X_SPI_REQUEST;
47 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000048
49 /* Wait until we are allowed to use the SPI bus. */
50 while (!(mmio_readw(mcp6x_spibar + 0x530) & (1 << MCP6X_SPI_GRANT))) ;
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000051
52 /* Update the cache. */
53 mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000054}
55
56static void mcp6x_release_spibus(void)
57{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000058 mcp_gpiostate &= ~(1 << MCP6X_SPI_REQUEST);
59 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000060}
61
62static void mcp6x_bitbang_set_cs(int val)
63{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000064 mcp_gpiostate &= ~(1 << MCP6X_SPI_CS);
65 mcp_gpiostate |= (val << MCP6X_SPI_CS);
66 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000067}
68
69static void mcp6x_bitbang_set_sck(int val)
70{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000071 mcp_gpiostate &= ~(1 << MCP6X_SPI_SCK);
72 mcp_gpiostate |= (val << MCP6X_SPI_SCK);
73 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000074}
75
76static void mcp6x_bitbang_set_mosi(int val)
77{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000078 mcp_gpiostate &= ~(1 << MCP6X_SPI_MOSI);
79 mcp_gpiostate |= (val << MCP6X_SPI_MOSI);
80 mmio_writeb(mcp_gpiostate, mcp6x_spibar + 0x530);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000081}
82
83static int mcp6x_bitbang_get_miso(void)
84{
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +000085 mcp_gpiostate = mmio_readb(mcp6x_spibar + 0x530);
86 return (mcp_gpiostate >> MCP6X_SPI_MISO) & 0x1;
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000087}
88
89static const struct bitbang_spi_master bitbang_spi_master_mcp6x = {
Thomas Heijligen43040f22022-06-23 14:38:35 +020090 .set_cs = mcp6x_bitbang_set_cs,
91 .set_sck = mcp6x_bitbang_set_sck,
92 .set_mosi = mcp6x_bitbang_set_mosi,
93 .get_miso = mcp6x_bitbang_get_miso,
94 .request_bus = mcp6x_request_spibus,
95 .release_bus = mcp6x_release_spibus,
96 .half_period = 0,
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +000097};
98
99int mcp6x_spi_init(int want_spi)
100{
101 uint16_t status;
102 uint32_t mcp6x_spibaraddr;
103 struct pci_dev *smbusdev;
104
105 /* Look for the SMBus device (SMBus PCI class) */
Edward O'Callaghan48a94662022-02-26 11:36:17 +1100106 smbusdev = pcidev_find_vendorclass(0x10de, 0x0c05);
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000107 if (!smbusdev) {
108 if (want_spi) {
109 msg_perr("ERROR: SMBus device not found. Not enabling "
110 "SPI.\n");
111 return 1;
112 } else {
113 msg_pinfo("Odd. SMBus device not found.\n");
114 return 0;
115 }
116 }
117 msg_pdbg("Found SMBus device %04x:%04x at %02x:%02x:%01x\n",
118 smbusdev->vendor_id, smbusdev->device_id,
119 smbusdev->bus, smbusdev->dev, smbusdev->func);
120
121
122 /* Locate the BAR where the SPI interface lives. */
123 mcp6x_spibaraddr = pci_read_long(smbusdev, 0x74);
124 /* BAR size is 64k, bits 15..4 are zero, bit 3..0 declare a
125 * 32-bit non-prefetchable memory BAR.
126 */
127 mcp6x_spibaraddr &= ~0xffff;
128 msg_pdbg("MCP SPI BAR is at 0x%08x\n", mcp6x_spibaraddr);
129
130 /* Accessing a NULL pointer BAR is evil. Don't do it. */
131 if (!mcp6x_spibaraddr && want_spi) {
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000132 msg_perr("Error: Chipset is strapped for SPI, but MCP SPI BAR is invalid.\n");
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000133 return 1;
134 } else if (!mcp6x_spibaraddr && !want_spi) {
135 msg_pdbg("MCP SPI is not used.\n");
136 return 0;
137 } else if (mcp6x_spibaraddr && !want_spi) {
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000138 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 +0000139 /* FIXME: Should we enable SPI anyway? */
140 return 0;
141 }
142 /* Map the BAR. Bytewise/wordwise access at 0x530 and 0x540. */
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000143 mcp6x_spibar = rphysmap("NVIDIA MCP6x SPI", mcp6x_spibaraddr, 0x544);
144 if (mcp6x_spibar == ERROR_PTR)
145 return 1;
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000146
147 status = mmio_readw(mcp6x_spibar + 0x530);
148 msg_pdbg("SPI control is 0x%04x, req=%i, gnt=%i\n",
149 status, (status >> MCP6X_SPI_REQUEST) & 0x1,
150 (status >> MCP6X_SPI_GRANT) & 0x1);
Carl-Daniel Hailfinger7b61df82010-09-14 01:29:49 +0000151 mcp_gpiostate = status & 0xff;
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000152
Carl-Daniel Hailfingera5bcbce2014-07-19 22:03:29 +0000153 if (register_spi_bitbang_master(&bitbang_spi_master_mcp6x)) {
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000154 /* This should never happen. */
155 msg_perr("MCP6X bitbang SPI master init failed!\n");
156 return 1;
157 }
158
Carl-Daniel Hailfinger2f436162010-07-28 15:08:35 +0000159 return 0;
160}