blob: 705a72b98d8f95a705c36edb4cea4b1d2f190500 [file] [log] [blame]
Jason Wanga3f04be2008-11-28 21:36:51 +00001/*
2 * This file is part of the flashrom project.
3 *
Jason Wang13f98ce2008-11-29 15:07:15 +00004 * Copyright (C) 2008 Wang Qingpei <Qingpei.Wang@amd.com>
5 * Copyright (C) 2008 Joe Bao <Zheng.Bao@amd.com>
Uwe Hermann97e8f222009-04-13 21:35:49 +00006 * Copyright (C) 2008 Advanced Micro Devices, Inc.
Wei Hu31402ee2014-05-16 21:39:33 +00007 * Copyright (C) 2009, 2010, 2013 Carl-Daniel Hailfinger
8 * Copyright (C) 2013 Stefan Tauner
Jason Wanga3f04be2008-11-28 21:36:51 +00009 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
Jason Wanga3f04be2008-11-28 21:36:51 +000019 */
20
Felix Singer980d6b82022-08-19 02:48:15 +020021#include <stdbool.h>
Rudolf Marek70e14592013-07-25 22:58:56 +000022#include <string.h>
23#include <stdlib.h>
Jason Wanga3f04be2008-11-28 21:36:51 +000024#include "flash.h"
Carl-Daniel Hailfinger5b997c32010-07-27 22:41:39 +000025#include "programmer.h"
Thomas Heijligen74b4aa02021-12-14 17:52:30 +010026#include "hwaccess_physmap.h"
Jason Wanga3f04be2008-11-28 21:36:51 +000027#include "spi.h"
Thomas Heijligend96c97c2021-11-02 21:03:00 +010028#include "platform/pci.h"
Jason Wanga3f04be2008-11-28 21:36:51 +000029
Carl-Daniel Hailfinger2c7ba8c2009-06-23 00:47:26 +000030/* This struct is unused, but helps visualize the SB600 SPI BAR layout.
31 *struct sb600_spi_controller {
32 * unsigned int spi_cntrl0; / * 00h * /
33 * unsigned int restrictedcmd1; / * 04h * /
34 * unsigned int restrictedcmd2; / * 08h * /
35 * unsigned int spi_cntrl1; / * 0ch * /
36 * unsigned int spi_cmdvalue0; / * 10h * /
37 * unsigned int spi_cmdvalue1; / * 14h * /
38 * unsigned int spi_cmdvalue2; / * 18h * /
39 * unsigned int spi_fakeid; / * 1Ch * /
40 *};
41 */
Jason Wanga3f04be2008-11-28 21:36:51 +000042
Michael Karcherb05b9e12010-07-22 18:04:19 +000043static uint8_t *sb600_spibar = NULL;
Stefan Tauner463dd692013-08-08 12:00:19 +000044enum amd_chipset {
45 CHIPSET_AMD_UNKNOWN,
46 CHIPSET_SB6XX,
47 CHIPSET_SB7XX, /* SP5100 too */
48 CHIPSET_SB89XX, /* Hudson-1 too */
49 CHIPSET_HUDSON234,
Martin Roth82b6ec12014-07-15 13:50:58 +000050 CHIPSET_BOLTON,
Stefan Tauner463dd692013-08-08 12:00:19 +000051 CHIPSET_YANGTZE,
Edward O'Callaghan93737bc2019-10-29 18:30:01 +110052 CHIPSET_PROMONTORY,
Stefan Tauner463dd692013-08-08 12:00:19 +000053};
54static enum amd_chipset amd_gen = CHIPSET_AMD_UNKNOWN;
55
Stefan Taunerd5b2aef2014-05-16 21:39:28 +000056#define FIFO_SIZE_OLD 8
Wei Hu31402ee2014-05-16 21:39:33 +000057#define FIFO_SIZE_YANGTZE 71
Stefan Taunerd5b2aef2014-05-16 21:39:28 +000058
Edward O'Callaghan5eca4272020-04-12 17:27:53 +100059static int sb600_spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
Stefan Taunerd5b2aef2014-05-16 21:39:28 +000060 const unsigned char *writearr, unsigned char *readarr);
Edward O'Callaghan5eca4272020-04-12 17:27:53 +100061static int spi100_spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
Wei Hu31402ee2014-05-16 21:39:33 +000062 const unsigned char *writearr, unsigned char *readarr);
Stefan Taunerd5b2aef2014-05-16 21:39:28 +000063
Carl-Daniel Hailfingera5bcbce2014-07-19 22:03:29 +000064static struct spi_master spi_master_sb600 = {
Thomas Heijligen43040f22022-06-23 14:38:35 +020065 .max_data_read = FIFO_SIZE_OLD,
66 .max_data_write = FIFO_SIZE_OLD - 3,
67 .command = sb600_spi_send_command,
68 .multicommand = default_spi_send_multicommand,
69 .read = default_spi_read,
70 .write_256 = default_spi_write_256,
71 .write_aai = default_spi_write_aai,
Stefan Taunerd5b2aef2014-05-16 21:39:28 +000072};
73
Carl-Daniel Hailfingera5bcbce2014-07-19 22:03:29 +000074static struct spi_master spi_master_yangtze = {
Thomas Heijligen43040f22022-06-23 14:38:35 +020075 .max_data_read = FIFO_SIZE_YANGTZE - 3, /* Apparently the big SPI 100 buffer is not a ring buffer. */
76 .max_data_write = FIFO_SIZE_YANGTZE - 3,
77 .command = spi100_spi_send_command,
78 .multicommand = default_spi_send_multicommand,
79 .read = default_spi_read,
80 .write_256 = default_spi_write_256,
81 .write_aai = default_spi_write_aai,
Wei Hu31402ee2014-05-16 21:39:33 +000082};
83
Edward O'Callaghanc0a27e12019-10-29 17:05:39 +110084static int find_smbus_dev_rev(uint16_t vendor, uint16_t device)
85{
86 struct pci_dev *smbus_dev = pci_dev_find(vendor, device);
87 if (!smbus_dev) {
88 msg_pdbg("No SMBus device with ID %04X:%04X found.\n", vendor, device);
89 msg_perr("ERROR: SMBus device not found. Not enabling SPI.\n");
90 return -1;
91 }
92 return pci_read_byte(smbus_dev, PCI_REVISION_ID);
93}
94
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +110095/* Determine the chipset's version and identify the respective SMBUS device. */
96static int determine_generation(struct pci_dev *dev)
Stefan Tauner463dd692013-08-08 12:00:19 +000097{
98 amd_gen = CHIPSET_AMD_UNKNOWN;
Stefan Tauner4442b812013-09-12 15:48:35 +000099 msg_pdbg2("Trying to determine the generation of the SPI interface... ");
100 if (dev->device_id == 0x438d) {
101 amd_gen = CHIPSET_SB6XX;
102 msg_pdbg("SB6xx detected.\n");
103 } else if (dev->device_id == 0x439d) {
Edward O'Callaghanc0a27e12019-10-29 17:05:39 +1100104 int rev = find_smbus_dev_rev(0x1002, 0x4385);
105 if (rev < 0)
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +1100106 return -1;
Stefan Tauner4442b812013-09-12 15:48:35 +0000107 if (rev >= 0x39 && rev <= 0x3D) {
108 amd_gen = CHIPSET_SB7XX;
109 msg_pdbg("SB7xx/SP5100 detected.\n");
110 } else if (rev >= 0x40 && rev <= 0x42) {
111 amd_gen = CHIPSET_SB89XX;
112 msg_pdbg("SB8xx/SB9xx/Hudson-1 detected.\n");
113 } else {
114 msg_pwarn("SB device found but SMBus revision 0x%02x does not match known values.\n"
Nico Huberac90af62022-12-18 00:22:47 +0000115 "Assuming SB8xx/SB9xx/Hudson-1.\n"
116 "Please send a log to flashrom-stable@flashrom.org\n",
117 rev);
Stefan Tauner4442b812013-09-12 15:48:35 +0000118 amd_gen = CHIPSET_SB89XX;
119 }
120 } else if (dev->device_id == 0x780e) {
Stefan Tauner463dd692013-08-08 12:00:19 +0000121 /* The PCI ID of the LPC bridge doesn't change between Hudson-2/3/4 and Yangtze (Kabini/Temash)
122 * although they use different SPI interfaces. */
Edward O'Callaghanc0a27e12019-10-29 17:05:39 +1100123 int rev = find_smbus_dev_rev(0x1022, 0x780B);
124 if (rev < 0)
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +1100125 return -1;
Stefan Tauner463dd692013-08-08 12:00:19 +0000126 if (rev >= 0x11 && rev <= 0x15) {
127 amd_gen = CHIPSET_HUDSON234;
128 msg_pdbg("Hudson-2/3/4 detected.\n");
Martin Roth82b6ec12014-07-15 13:50:58 +0000129 } else if (rev == 0x16) {
130 amd_gen = CHIPSET_BOLTON;
131 msg_pdbg("Bolton detected.\n");
Stefan Tauner5c316f92015-02-08 21:57:52 +0000132 } else if ((rev >= 0x39 && rev <= 0x3A) || rev == 0x42) {
Stefan Tauner463dd692013-08-08 12:00:19 +0000133 amd_gen = CHIPSET_YANGTZE;
134 msg_pdbg("Yangtze detected.\n");
135 } else {
136 msg_pwarn("FCH device found but SMBus revision 0x%02x does not match known values.\n"
Nico Huberac90af62022-12-18 00:22:47 +0000137 "Please report this to flashrom-stable@flashrom.org and include this\n"
138 "log and the output of lspci -nnvx, thanks!.\n", rev);
Stefan Tauner463dd692013-08-08 12:00:19 +0000139 }
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100140 } else if (dev->device_id == 0x790e) {
Edward O'Callaghanc0a27e12019-10-29 17:05:39 +1100141 int rev = find_smbus_dev_rev(0x1022, 0x790B);
142 if (rev < 0)
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +1100143 return -1;
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100144 if (rev == 0x4a) {
145 amd_gen = CHIPSET_YANGTZE;
146 msg_pdbg("Yangtze detected.\n");
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100147 } else if (rev == 0x4b) {
148 amd_gen = CHIPSET_PROMONTORY;
149 msg_pdbg("Promontory detected.\n");
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100150 } else {
151 msg_pwarn("FCH device found but SMBus revision 0x%02x does not match known values.\n"
Nico Huberac90af62022-12-18 00:22:47 +0000152 "Please report this to flashrom-stable@flashrom.org and include this\n"
153 "log and the output of lspci -nnvx, thanks!.\n", rev);
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100154 }
155
156
Stefan Tauner4442b812013-09-12 15:48:35 +0000157 } else
158 msg_pwarn("%s: Unknown LPC device %" PRIx16 ":%" PRIx16 ".\n"
Nico Huberac90af62022-12-18 00:22:47 +0000159 "Please report this to flashrom-stable@flashrom.org and include this\n"
160 "log and the output of lspci -nnvx, thanks!\n",
Stefan Tauner4442b812013-09-12 15:48:35 +0000161 __func__, dev->vendor_id, dev->device_id);
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +1100162 if (amd_gen == CHIPSET_AMD_UNKNOWN) {
163 msg_perr("Could not determine chipset generation.");
164 return -1;
165 }
166 return 0;
Stefan Tauner463dd692013-08-08 12:00:19 +0000167}
Jason Wanga3f04be2008-11-28 21:36:51 +0000168
Carl-Daniel Hailfinger2c7ba8c2009-06-23 00:47:26 +0000169static void reset_internal_fifo_pointer(void)
Jason Wanga3f04be2008-11-28 21:36:51 +0000170{
Carl-Daniel Hailfinger78185dc2009-05-17 15:49:24 +0000171 mmio_writeb(mmio_readb(sb600_spibar + 2) | 0x10, sb600_spibar + 2);
Jason Wanga3f04be2008-11-28 21:36:51 +0000172
Rudolf Marek70e14592013-07-25 22:58:56 +0000173 /* FIXME: This loop needs a timeout and a clearer message. */
Carl-Daniel Hailfinger78185dc2009-05-17 15:49:24 +0000174 while (mmio_readb(sb600_spibar + 0xD) & 0x7)
Carl-Daniel Hailfinger643415b2010-01-10 01:59:50 +0000175 msg_pspew("reset\n");
Jason Wanga3f04be2008-11-28 21:36:51 +0000176}
177
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000178static int compare_internal_fifo_pointer(uint8_t want)
179{
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000180 uint8_t have = mmio_readb(sb600_spibar + 0xd) & 0x07;
181 want %= FIFO_SIZE_OLD;
182 if (have != want) {
183 msg_perr("AMD SPI FIFO pointer corruption! Pointer is %d, wanted %d\n", have, want);
184 msg_perr("Something else is accessing the flash chip and causes random corruption.\n"
185 "Please stop all applications and drivers and IPMI which access the flash chip.\n");
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000186 return 1;
187 } else {
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000188 msg_pspew("AMD SPI FIFO pointer is %d, wanted %d\n", have, want);
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000189 return 0;
190 }
191}
192
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000193/* Check the number of bytes to be transmitted and extract opcode. */
Edward O'Callaghan5eca4272020-04-12 17:27:53 +1000194static int check_readwritecnt(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt)
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000195{
Carl-Daniel Hailfingera5bcbce2014-07-19 22:03:29 +0000196 unsigned int maxwritecnt = flash->mst->spi.max_data_write + 3;
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000197 if (writecnt > maxwritecnt) {
198 msg_pinfo("%s: SPI controller can not send %d bytes, it is limited to %d bytes\n",
199 __func__, writecnt, maxwritecnt);
200 return SPI_INVALID_LENGTH;
201 }
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000202
Stefan Tauner6697f712014-08-06 15:09:15 +0000203 unsigned int maxreadcnt = flash->mst->spi.max_data_read;
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000204 if (readcnt > maxreadcnt) {
205 msg_pinfo("%s: SPI controller can not receive %d bytes, it is limited to %d bytes\n",
206 __func__, readcnt, maxreadcnt);
207 return SPI_INVALID_LENGTH;
208 }
209 return 0;
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000210}
211
Carl-Daniel Hailfinger2c7ba8c2009-06-23 00:47:26 +0000212static void execute_command(void)
Jason Wanga3f04be2008-11-28 21:36:51 +0000213{
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000214 msg_pspew("Executing... ");
Carl-Daniel Hailfinger78185dc2009-05-17 15:49:24 +0000215 mmio_writeb(mmio_readb(sb600_spibar + 2) | 1, sb600_spibar + 2);
Carl-Daniel Hailfinger78185dc2009-05-17 15:49:24 +0000216 while (mmio_readb(sb600_spibar + 2) & 1)
Jason Wanga3f04be2008-11-28 21:36:51 +0000217 ;
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000218 msg_pspew("done\n");
Jason Wanga3f04be2008-11-28 21:36:51 +0000219}
220
Edward O'Callaghan5eca4272020-04-12 17:27:53 +1000221static int sb600_spi_send_command(const struct flashctx *flash, unsigned int writecnt,
Carl-Daniel Hailfinger8a3c60c2011-12-18 15:01:24 +0000222 unsigned int readcnt,
223 const unsigned char *writearr,
224 unsigned char *readarr)
Jason Wanga3f04be2008-11-28 21:36:51 +0000225{
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000226 /* First byte is cmd which can not be sent through the FIFO. */
Jason Wanga3f04be2008-11-28 21:36:51 +0000227 unsigned char cmd = *writearr++;
Jason Wanga3f04be2008-11-28 21:36:51 +0000228 writecnt--;
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000229 msg_pspew("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, cmd, writecnt, readcnt);
230 mmio_writeb(cmd, sb600_spibar + 0);
Jason Wanga3f04be2008-11-28 21:36:51 +0000231
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000232 int ret = check_readwritecnt(flash, writecnt, readcnt);
233 if (ret != 0)
234 return ret;
Jason Wanga3f04be2008-11-28 21:36:51 +0000235
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000236 /* This is a workaround for a bug in SPI controller. If we only send
Carl-Daniel Hailfingerf8555e22009-07-23 01:36:08 +0000237 * an opcode and no additional data/address, the SPI controller will
238 * read one byte too few from the chip. Basically, the last byte of
239 * the chip response is discarded and will not end up in the FIFO.
240 * It is unclear if the CS# line is set high too early as well.
241 */
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000242 unsigned int readoffby1 = (writecnt > 0) ? 0 : 1;
243 uint8_t readwrite = (readcnt + readoffby1) << 4 | (writecnt);
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000244 mmio_writeb(readwrite, sb600_spibar + 1);
Jason Wanga3f04be2008-11-28 21:36:51 +0000245
Jason Wanga3f04be2008-11-28 21:36:51 +0000246 reset_internal_fifo_pointer();
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000247 msg_pspew("Filling FIFO: ");
Nico Huber519be662018-12-23 20:03:35 +0100248 unsigned int count;
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000249 for (count = 0; count < writecnt; count++) {
250 msg_pspew("[%02x]", writearr[count]);
251 mmio_writeb(writearr[count], sb600_spibar + 0xC);
Jason Wanga3f04be2008-11-28 21:36:51 +0000252 }
Carl-Daniel Hailfinger643415b2010-01-10 01:59:50 +0000253 msg_pspew("\n");
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000254 if (compare_internal_fifo_pointer(writecnt))
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000255 return SPI_PROGRAMMER_ERROR;
Jason Wanga3f04be2008-11-28 21:36:51 +0000256
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000257 /*
258 * We should send the data in sequence, which means we need to reset
259 * the FIFO pointer to the first byte we want to send.
260 */
261 reset_internal_fifo_pointer();
Jason Wanga3f04be2008-11-28 21:36:51 +0000262 execute_command();
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000263 if (compare_internal_fifo_pointer(writecnt + readcnt))
264 return SPI_PROGRAMMER_ERROR;
Jason Wanga3f04be2008-11-28 21:36:51 +0000265
266 /*
267 * After the command executed, we should find out the index of the
Carl-Daniel Hailfingerf8555e22009-07-23 01:36:08 +0000268 * received byte. Here we just reset the FIFO pointer and skip the
269 * writecnt.
270 * It would be possible to increase the FIFO pointer by one instead
271 * of reading and discarding one byte from the FIFO.
272 * The FIFO is implemented on top of an 8 byte ring buffer and the
273 * buffer is never cleared. For every byte that is shifted out after
274 * the opcode, the FIFO already stores the response from the chip.
275 * Usually, the chip will respond with 0x00 or 0xff.
Jason Wanga3f04be2008-11-28 21:36:51 +0000276 */
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000277 reset_internal_fifo_pointer();
Jason Wanga3f04be2008-11-28 21:36:51 +0000278
Carl-Daniel Hailfingerf8555e22009-07-23 01:36:08 +0000279 /* Skip the bytes we sent. */
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000280 msg_pspew("Skipping: ");
Jason Wanga3f04be2008-11-28 21:36:51 +0000281 for (count = 0; count < writecnt; count++) {
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000282 msg_pspew("[%02x]", mmio_readb(sb600_spibar + 0xC));
Jason Wanga3f04be2008-11-28 21:36:51 +0000283 }
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000284 msg_pspew("\n");
285 if (compare_internal_fifo_pointer(writecnt))
286 return SPI_PROGRAMMER_ERROR;
Jason Wanga3f04be2008-11-28 21:36:51 +0000287
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000288 msg_pspew("Reading FIFO: ");
289 for (count = 0; count < readcnt; count++) {
290 readarr[count] = mmio_readb(sb600_spibar + 0xC);
291 msg_pspew("[%02x]", readarr[count]);
Jason Wanga3f04be2008-11-28 21:36:51 +0000292 }
Carl-Daniel Hailfinger643415b2010-01-10 01:59:50 +0000293 msg_pspew("\n");
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000294 if (compare_internal_fifo_pointer(writecnt+readcnt))
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000295 return SPI_PROGRAMMER_ERROR;
296
297 if (mmio_readb(sb600_spibar + 1) != readwrite) {
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000298 msg_perr("Unexpected change in AMD SPI read/write count!\n");
299 msg_perr("Something else is accessing the flash chip and causes random corruption.\n"
300 "Please stop all applications and drivers and IPMI which access the flash chip.\n");
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000301 return SPI_PROGRAMMER_ERROR;
302 }
Jason Wanga3f04be2008-11-28 21:36:51 +0000303
304 return 0;
305}
Carl-Daniel Hailfingercceafa22010-05-26 01:45:41 +0000306
Edward O'Callaghan5eca4272020-04-12 17:27:53 +1000307static int spi100_spi_send_command(const struct flashctx *flash, unsigned int writecnt,
Wei Hu31402ee2014-05-16 21:39:33 +0000308 unsigned int readcnt,
309 const unsigned char *writearr,
310 unsigned char *readarr)
311{
312 /* First byte is cmd which can not be sent through the buffer. */
313 unsigned char cmd = *writearr++;
314 writecnt--;
315 msg_pspew("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, cmd, writecnt, readcnt);
316 mmio_writeb(cmd, sb600_spibar + 0);
317
318 int ret = check_readwritecnt(flash, writecnt, readcnt);
319 if (ret != 0)
320 return ret;
321
322 /* Use the extended TxByteCount and RxByteCount registers. */
323 mmio_writeb(writecnt, sb600_spibar + 0x48);
324 mmio_writeb(readcnt, sb600_spibar + 0x4b);
325
326 msg_pspew("Filling buffer: ");
Nico Huber519be662018-12-23 20:03:35 +0100327 unsigned int count;
Wei Hu31402ee2014-05-16 21:39:33 +0000328 for (count = 0; count < writecnt; count++) {
329 msg_pspew("[%02x]", writearr[count]);
330 mmio_writeb(writearr[count], sb600_spibar + 0x80 + count);
331 }
332 msg_pspew("\n");
333
334 execute_command();
335
336 msg_pspew("Reading buffer: ");
337 for (count = 0; count < readcnt; count++) {
338 readarr[count] = mmio_readb(sb600_spibar + 0x80 + (writecnt + count) % FIFO_SIZE_YANGTZE);
339 msg_pspew("[%02x]", readarr[count]);
340 }
341 msg_pspew("\n");
342
343 return 0;
344}
345
Stefan Taunera6a0d202013-09-15 14:17:39 +0000346struct spispeed {
347 const char *const name;
348 const uint8_t speed;
349};
350
351static const struct spispeed spispeeds[] = {
352 { "66 MHz", 0x00 },
353 { "33 MHz", 0x01 },
354 { "22 MHz", 0x02 },
355 { "16.5 MHz", 0x03 },
Wei Hu31402ee2014-05-16 21:39:33 +0000356 { "100 MHz", 0x04 },
357 { "Reserved", 0x05 },
358 { "Reserved", 0x06 },
359 { "800 kHz", 0x07 },
Stefan Taunera6a0d202013-09-15 14:17:39 +0000360};
361
362static int set_speed(struct pci_dev *dev, const struct spispeed *spispeed)
363{
364 bool success = false;
365 uint8_t speed = spispeed->speed;
366
367 msg_pdbg("Setting SPI clock to %s (0x%x).\n", spispeed->name, speed);
Wei Hu31402ee2014-05-16 21:39:33 +0000368 if (amd_gen >= CHIPSET_YANGTZE) {
369 rmmio_writew((speed << 12) | (speed << 8) | (speed << 4) | speed, sb600_spibar + 0x22);
370 uint16_t tmp = mmio_readw(sb600_spibar + 0x22);
371 success = (((tmp >> 12) & 0xf) == speed && ((tmp >> 8) & 0xf) == speed &&
372 ((tmp >> 4) & 0xf) == speed && ((tmp >> 0) & 0xf) == speed);
373 } else {
Stefan Taunera6a0d202013-09-15 14:17:39 +0000374 rmmio_writeb((mmio_readb(sb600_spibar + 0xd) & ~(0x3 << 4)) | (speed << 4), sb600_spibar + 0xd);
375 success = (speed == ((mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3));
376 }
377
378 if (!success) {
379 msg_perr("Setting SPI clock failed.\n");
380 return 1;
381 }
382 return 0;
383}
384
Wei Hu31402ee2014-05-16 21:39:33 +0000385static int set_mode(struct pci_dev *dev, uint8_t read_mode)
386{
387 uint32_t tmp = mmio_readl(sb600_spibar + 0x00);
388 tmp &= ~(0x6 << 28 | 0x1 << 18); /* Clear mode bits */
389 tmp |= ((read_mode & 0x6) << 28) | ((read_mode & 0x1) << 18);
390 rmmio_writel(tmp, sb600_spibar + 0x00);
391 if (tmp != mmio_readl(sb600_spibar + 0x00))
392 return 1;
393 return 0;
394}
395
Stefan Taunera6a0d202013-09-15 14:17:39 +0000396static int handle_speed(struct pci_dev *dev)
397{
398 uint32_t tmp;
Carl-Daniel Hailfinger57cdd6b2016-03-12 19:49:14 +0000399 uint8_t spispeed_idx = 3; /* Default to 16.5 MHz */
Stefan Taunera6a0d202013-09-15 14:17:39 +0000400
Stefan Tauner21071b02014-05-16 21:39:48 +0000401 char *spispeed = extract_programmer_param("spispeed");
402 if (spispeed != NULL) {
Carl-Daniel Hailfinger57cdd6b2016-03-12 19:49:14 +0000403 unsigned int i;
404 for (i = 0; i < ARRAY_SIZE(spispeeds); i++) {
405 if (strcasecmp(spispeeds[i].name, spispeed) == 0) {
406 spispeed_idx = i;
407 break;
Stefan Tauner21071b02014-05-16 21:39:48 +0000408 }
Stefan Tauner21071b02014-05-16 21:39:48 +0000409 }
Carl-Daniel Hailfinger57cdd6b2016-03-12 19:49:14 +0000410 /* "reserved" is not a valid speed.
411 * Error out on speeds not present in the spispeeds array.
412 * Only Yangtze supports the second half of indices.
413 * No 66 MHz before SB8xx. */
414 if ((strcasecmp(spispeed, "reserved") == 0) ||
415 (i == ARRAY_SIZE(spispeeds)) ||
416 (amd_gen < CHIPSET_YANGTZE && spispeed_idx > 3) ||
417 (amd_gen < CHIPSET_SB89XX && spispeed_idx == 0)) {
Stefan Tauner21071b02014-05-16 21:39:48 +0000418 msg_perr("Error: Invalid spispeed value: '%s'.\n", spispeed);
419 free(spispeed);
420 return 1;
421 }
422 free(spispeed);
423 }
424
Stefan Taunera6a0d202013-09-15 14:17:39 +0000425 /* See the chipset support matrix for SPI Base_Addr below for an explanation of the symbols used.
Martin Roth82b6ec12014-07-15 13:50:58 +0000426 * bit 6xx 7xx/SP5100 8xx 9xx hudson1 hudson234 bolton/yangtze
Stefan Taunera6a0d202013-09-15 14:17:39 +0000427 * 18 rsvd <- fastReadEnable ? <- ? SpiReadMode[0]
428 * 29:30 rsvd <- <- ? <- ? SpiReadMode[2:1]
429 */
Martin Roth82b6ec12014-07-15 13:50:58 +0000430 if (amd_gen >= CHIPSET_BOLTON) {
Wei Hu31402ee2014-05-16 21:39:33 +0000431 static const char *spireadmodes[] = {
432 "Normal (up to 33 MHz)", /* 0 */
433 "Reserved", /* 1 */
434 "Dual IO (1-1-2)", /* 2 */
435 "Quad IO (1-1-4)", /* 3 */
436 "Dual IO (1-2-2)", /* 4 */
437 "Quad IO (1-4-4)", /* 5 */
438 "Normal (up to 66 MHz)", /* 6 */
Martin Roth82b6ec12014-07-15 13:50:58 +0000439 "Fast Read", /* 7 (Not defined in the Bolton datasheet.) */
Wei Hu31402ee2014-05-16 21:39:33 +0000440 };
441 tmp = mmio_readl(sb600_spibar + 0x00);
442 uint8_t read_mode = ((tmp >> 28) & 0x6) | ((tmp >> 18) & 0x1);
443 msg_pdbg("SpiReadMode=%s (%i)\n", spireadmodes[read_mode], read_mode);
444 if (read_mode != 6) {
445 read_mode = 6; /* Default to "Normal (up to 66 MHz)" */
446 if (set_mode(dev, read_mode) != 0) {
447 msg_perr("Setting read mode to \"%s\" failed.\n", spireadmodes[read_mode]);
448 return 1;
449 }
450 msg_pdbg("Setting read mode to \"%s\" succeeded.\n", spireadmodes[read_mode]);
451 }
452
Martin Roth82b6ec12014-07-15 13:50:58 +0000453 if (amd_gen >= CHIPSET_YANGTZE) {
454 tmp = mmio_readb(sb600_spibar + 0x20);
455 msg_pdbg("UseSpi100 is %sabled\n", (tmp & 0x1) ? "en" : "dis");
456 if ((tmp & 0x1) == 0) {
457 rmmio_writeb(tmp | 0x1, sb600_spibar + 0x20);
458 tmp = mmio_readb(sb600_spibar + 0x20) & 0x1;
459 if (tmp == 0) {
460 msg_perr("Enabling Spi100 failed.\n");
461 return 1;
462 }
463 msg_pdbg("Enabling Spi100 succeeded.\n");
464 }
465
466 tmp = mmio_readw(sb600_spibar + 0x22); /* SPI 100 Speed Config */
467 msg_pdbg("NormSpeedNew is %s\n", spispeeds[(tmp >> 12) & 0xf].name);
468 msg_pdbg("FastSpeedNew is %s\n", spispeeds[(tmp >> 8) & 0xf].name);
469 msg_pdbg("AltSpeedNew is %s\n", spispeeds[(tmp >> 4) & 0xf].name);
470 msg_pdbg("TpmSpeedNew is %s\n", spispeeds[(tmp >> 0) & 0xf].name);
471 }
Wei Hu31402ee2014-05-16 21:39:33 +0000472 } else {
Stefan Taunera6a0d202013-09-15 14:17:39 +0000473 if (amd_gen >= CHIPSET_SB89XX && amd_gen <= CHIPSET_HUDSON234) {
474 bool fast_read = (mmio_readl(sb600_spibar + 0x00) >> 18) & 0x1;
475 msg_pdbg("Fast Reads are %sabled\n", fast_read ? "en" : "dis");
476 if (fast_read) {
477 msg_pdbg("Disabling them temporarily.\n");
478 rmmio_writel(mmio_readl(sb600_spibar + 0x00) & ~(0x1 << 18),
479 sb600_spibar + 0x00);
480 }
481 }
482 tmp = (mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3;
483 msg_pdbg("NormSpeed is %s\n", spispeeds[tmp].name);
484 }
485 return set_speed(dev, &spispeeds[spispeed_idx]);
486}
487
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000488static int handle_imc(struct pci_dev *dev)
Rudolf Marek70e14592013-07-25 22:58:56 +0000489{
490 /* Handle IMC everywhere but sb600 which does not have one. */
Stefan Tauner4442b812013-09-12 15:48:35 +0000491 if (amd_gen == CHIPSET_SB6XX)
Rudolf Marek70e14592013-07-25 22:58:56 +0000492 return 0;
493
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000494 bool amd_imc_force = false;
495 char *arg = extract_programmer_param("amd_imc_force");
496 if (arg && !strcmp(arg, "yes")) {
497 amd_imc_force = true;
498 msg_pspew("amd_imc_force enabled.\n");
499 } else if (arg && !strlen(arg)) {
500 msg_perr("Missing argument for amd_imc_force.\n");
501 free(arg);
502 return 1;
503 } else if (arg) {
504 msg_perr("Unknown argument for amd_imc_force: \"%s\" (not \"yes\").\n", arg);
505 free(arg);
506 return 1;
507 }
508 free(arg);
509
Rudolf Marek70e14592013-07-25 22:58:56 +0000510 /* TODO: we should not only look at IntegratedImcPresent (LPC Dev 20, Func 3, 40h) but also at
Stefan Tauner5c316f92015-02-08 21:57:52 +0000511 * IMCEnable(Strap) and Override EcEnable(Strap) (sb8xx, sb9xx?, a50, Bolton: Misc_Reg: 80h-87h;
Rudolf Marek70e14592013-07-25 22:58:56 +0000512 * sb7xx, sp5100: PM_Reg: B0h-B1h) etc. */
513 uint8_t reg = pci_read_byte(dev, 0x40);
514 if ((reg & (1 << 7)) == 0) {
515 msg_pdbg("IMC is not active.\n");
516 return 0;
517 }
518
519 if (!amd_imc_force)
Felix Singer980d6b82022-08-19 02:48:15 +0200520 programmer_may_write = false;
Stefan Tauner463dd692013-08-08 12:00:19 +0000521 msg_pinfo("Writes have been disabled for safety reasons because the presence of the IMC\n"
522 "was detected and it could interfere with accessing flash memory. Flashrom will\n"
523 "try to disable it temporarily but even then this might not be safe:\n"
Stefan Tauner0be072c2016-03-13 15:16:30 +0000524 "when it is re-enabled and after a reboot it expects to find working code\n"
Rudolf Marek70e14592013-07-25 22:58:56 +0000525 "in the flash and it is unpredictable what happens if there is none.\n"
526 "\n"
527 "To be safe make sure that there is a working IMC firmware at the right\n"
528 "location in the image you intend to write and do not attempt to erase.\n"
529 "\n"
530 "You can enforce write support with the amd_imc_force programmer option.\n");
531 if (amd_imc_force)
532 msg_pinfo("Continuing with write support because the user forced us to!\n");
533
534 return amd_imc_shutdown(dev);
535}
536
Michael Karcherb05b9e12010-07-22 18:04:19 +0000537int sb600_probe_spi(struct pci_dev *dev)
538{
539 struct pci_dev *smbus_dev;
540 uint32_t tmp;
541 uint8_t reg;
Rudolf Marek70e14592013-07-25 22:58:56 +0000542
Michael Karcherb05b9e12010-07-22 18:04:19 +0000543 /* Read SPI_BaseAddr */
544 tmp = pci_read_long(dev, 0xa0);
545 tmp &= 0xffffffe0; /* remove bits 4-0 (reserved) */
546 msg_pdbg("SPI base address is at 0x%x\n", tmp);
547
548 /* If the BAR has address 0, it is unlikely SPI is used. */
549 if (!tmp)
550 return 0;
551
552 /* Physical memory has to be mapped at page (4k) boundaries. */
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000553 sb600_spibar = rphysmap("SB600 SPI registers", tmp & 0xfffff000, 0x1000);
554 if (sb600_spibar == ERROR_PTR)
Niklas Söderlund5d307202013-09-14 09:02:27 +0000555 return ERROR_FATAL;
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000556
Michael Karcherb05b9e12010-07-22 18:04:19 +0000557 /* The low bits of the SPI base address are used as offset into
558 * the mapped page.
559 */
560 sb600_spibar += tmp & 0xfff;
561
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +1100562 if (determine_generation(dev) < 0)
Stefan Tauner4442b812013-09-12 15:48:35 +0000563 return ERROR_NONFATAL;
Stefan Tauner463dd692013-08-08 12:00:19 +0000564
Stefan Tauner4442b812013-09-12 15:48:35 +0000565 /* How to read the following table and similar ones in this file:
566 * "?" means we have no datasheet for this chipset generation or it doesn't have any relevant info.
567 * "<-" means the bit/register meaning is identical to the next non-"?" chipset to the left. "<-" thus
568 * never refers to another "?".
569 * If a "?" chipset is between two chipsets with identical meaning, we assume the meaning didn't change
570 * twice in between, i.e. the meaning is unchanged for the "?" chipset. Usually we assume that
571 * succeeding hardware supports the same functionality as its predecessor unless proven different by
572 * tests or documentation, hence "?" will often be implemented equally to "<-".
573 *
574 * Chipset support matrix for SPI Base_Addr (LPC PCI reg 0xa0)
575 * bit 6xx 7xx/SP5100 8xx 9xx hudson1 hudson2+ yangtze
576 * 3 rsvd <- <- ? <- ? RouteTpm2Spi
577 * 2 rsvd AbortEnable rsvd ? <- ? <-
578 * 1 rsvd SpiRomEnable <- ? <- ? <-
579 * 0 rsvd AltSpiCSEnable rsvd ? <- ? <-
580 */
581 if (amd_gen >= CHIPSET_SB7XX) {
582 tmp = pci_read_long(dev, 0xa0);
583 msg_pdbg("SpiRomEnable=%i", (tmp >> 1) & 0x1);
584 if (amd_gen == CHIPSET_SB7XX)
585 msg_pdbg(", AltSpiCSEnable=%i, AbortEnable=%i", tmp & 0x1, (tmp >> 2) & 0x1);
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100586 else if (amd_gen >= CHIPSET_YANGTZE)
Wei Hu31402ee2014-05-16 21:39:33 +0000587 msg_pdbg(", RouteTpm2Sp=%i", (tmp >> 3) & 0x1);
Michael Karcherb05b9e12010-07-22 18:04:19 +0000588
Stefan Tauner4442b812013-09-12 15:48:35 +0000589 tmp = pci_read_byte(dev, 0xba);
590 msg_pdbg(", PrefetchEnSPIFromIMC=%i", (tmp & 0x4) >> 2);
591
592 tmp = pci_read_byte(dev, 0xbb);
593 /* FIXME: Set bit 3,6,7 if not already set.
594 * Set bit 5, otherwise SPI accesses are pointless in LPC mode.
595 * See doc 42413 AMD SB700/710/750 RPR.
596 */
597 if (amd_gen == CHIPSET_SB7XX)
598 msg_pdbg(", SpiOpEnInLpcMode=%i", (tmp >> 5) & 0x1);
599 msg_pdbg(", PrefetchEnSPIFromHost=%i\n", tmp & 0x1);
600 }
601
602 /* Chipset support matrix for SPI_Cntrl0 (spibar + 0x0)
603 * See the chipset support matrix for SPI Base_Addr above for an explanation of the symbols used.
604 * bit 6xx 7xx/SP5100 8xx 9xx hudson1 hudson2+ yangtze
605 * 17 rsvd <- <- ? <- ? <-
Stefan Taunera6a0d202013-09-15 14:17:39 +0000606 * 18 rsvd <- fastReadEnable<1> ? <- ? SpiReadMode[0]<1>
Stefan Tauner4442b812013-09-12 15:48:35 +0000607 * 19 SpiArbEnable <- <- ? <- ? <-
608 * 20 (FifoPtrClr) <- <- ? <- ? <-
609 * 21 (FifoPtrInc) <- <- ? <- ? IllegalAccess
610 * 22 SpiAccessMacRomEn <- <- ? <- ? <-
611 * 23 SpiHostAccessRomEn <- <- ? <- ? <-
612 * 24:26 ArbWaitCount <- <- ? <- ? <-
613 * 27 SpiBridgeDisable <- <- ? <- ? rsvd
614 * 28 rsvd DropOneClkOnRd = SPIClkGate ? <- ? <-
Stefan Taunera6a0d202013-09-15 14:17:39 +0000615 * 29:30 rsvd <- <- ? <- ? SpiReadMode[2:1]<1>
Stefan Tauner4442b812013-09-12 15:48:35 +0000616 * 31 rsvd <- SpiBusy ? <- ? <-
Stefan Taunera6a0d202013-09-15 14:17:39 +0000617 *
618 * <1> see handle_speed
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000619 */
Stefan Tauner4442b812013-09-12 15:48:35 +0000620 tmp = mmio_readl(sb600_spibar + 0x00);
621 msg_pdbg("(0x%08" PRIx32 ") SpiArbEnable=%i", tmp, (tmp >> 19) & 0x1);
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100622 if (amd_gen >= CHIPSET_YANGTZE)
Wei Hu31402ee2014-05-16 21:39:33 +0000623 msg_pdbg(", IllegalAccess=%i", (tmp >> 21) & 0x1);
Stefan Tauner4442b812013-09-12 15:48:35 +0000624
625 msg_pdbg(", SpiAccessMacRomEn=%i, SpiHostAccessRomEn=%i, ArbWaitCount=%i",
626 (tmp >> 22) & 0x1, (tmp >> 23) & 0x1, (tmp >> 24) & 0x7);
627
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100628 if (amd_gen < CHIPSET_YANGTZE)
Stefan Tauner4442b812013-09-12 15:48:35 +0000629 msg_pdbg(", SpiBridgeDisable=%i", (tmp >> 27) & 0x1);
630
631 switch (amd_gen) {
632 case CHIPSET_SB7XX:
633 msg_pdbg(", DropOneClkOnRd/SpiClkGate=%i", (tmp >> 28) & 0x1);
Richard Hughesdb7482b2018-12-19 12:04:30 +0000634 /* Fall through. */
Stefan Tauner4442b812013-09-12 15:48:35 +0000635 case CHIPSET_SB89XX:
636 case CHIPSET_HUDSON234:
Wei Hu31402ee2014-05-16 21:39:33 +0000637 case CHIPSET_YANGTZE:
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100638 case CHIPSET_PROMONTORY:
Stefan Tauner4442b812013-09-12 15:48:35 +0000639 msg_pdbg(", SpiBusy=%i", (tmp >> 31) & 0x1);
640 default: break;
641 }
642 msg_pdbg("\n");
643
644 if (((tmp >> 22) & 0x1) == 0 || ((tmp >> 23) & 0x1) == 0) {
645 msg_perr("ERROR: State of SpiAccessMacRomEn or SpiHostAccessRomEn prohibits full access.\n");
646 return ERROR_NONFATAL;
647 }
648
Stefan Tauner4442b812013-09-12 15:48:35 +0000649 if (amd_gen >= CHIPSET_SB89XX) {
650 tmp = mmio_readb(sb600_spibar + 0x1D);
651 msg_pdbg("Using SPI_CS%d\n", tmp & 0x3);
Wei Hu31402ee2014-05-16 21:39:33 +0000652 /* FIXME: Handle SpiProtect* configuration on Yangtze. */
Stefan Tauner4442b812013-09-12 15:48:35 +0000653 }
654
Michael Karcherb05b9e12010-07-22 18:04:19 +0000655 /* Look for the SMBus device. */
656 smbus_dev = pci_dev_find(0x1002, 0x4385);
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100657 if (!smbus_dev)
Stefan Tauner463dd692013-08-08 12:00:19 +0000658 smbus_dev = pci_dev_find(0x1022, 0x780b); /* AMD FCH */
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100659 if (!smbus_dev)
660 smbus_dev = pci_dev_find(0x1022, 0x790b); /* AMD FP4 */
661 if (!smbus_dev) {
662 msg_perr("ERROR: SMBus device not found. Not enabling SPI.\n");
663 return ERROR_NONFATAL;
Michael Karcherb05b9e12010-07-22 18:04:19 +0000664 }
665
666 /* Note about the bit tests below: If a bit is zero, the GPIO is SPI. */
667 /* GPIO11/SPI_DO and GPIO12/SPI_DI status */
668 reg = pci_read_byte(smbus_dev, 0xAB);
669 reg &= 0xC0;
670 msg_pdbg("GPIO11 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_DO");
671 msg_pdbg("GPIO12 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_DI");
672 if (reg != 0x00) {
673 msg_pdbg("Not enabling SPI");
674 return 0;
675 }
676 /* GPIO31/SPI_HOLD and GPIO32/SPI_CS status */
677 reg = pci_read_byte(smbus_dev, 0x83);
678 reg &= 0xC0;
679 msg_pdbg("GPIO31 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_HOLD");
680 msg_pdbg("GPIO32 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_CS");
681 /* SPI_HOLD is not used on all boards, filter it out. */
682 if ((reg & 0x80) != 0x00) {
683 msg_pdbg("Not enabling SPI");
684 return 0;
685 }
686 /* GPIO47/SPI_CLK status */
687 reg = pci_read_byte(smbus_dev, 0xA7);
688 reg &= 0x40;
689 msg_pdbg("GPIO47 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_CLK");
690 if (reg != 0x00) {
691 msg_pdbg("Not enabling SPI");
692 return 0;
693 }
694
Stefan Taunera6a0d202013-09-15 14:17:39 +0000695 if (handle_speed(dev) != 0)
696 return ERROR_FATAL;
697
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000698 if (handle_imc(dev) != 0)
Rudolf Marek70e14592013-07-25 22:58:56 +0000699 return ERROR_FATAL;
Carl-Daniel Hailfinger39446e32010-09-15 12:02:07 +0000700
Wei Hu31402ee2014-05-16 21:39:33 +0000701 /* Starting with Yangtze the SPI controller got a different interface with a much bigger buffer. */
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100702 if (amd_gen < CHIPSET_YANGTZE)
Nico Huber5e08e3e2021-05-11 17:38:14 +0200703 register_spi_master(&spi_master_sb600, NULL);
Wei Hu31402ee2014-05-16 21:39:33 +0000704 else
Nico Huber5e08e3e2021-05-11 17:38:14 +0200705 register_spi_master(&spi_master_yangtze, NULL);
Michael Karcherb05b9e12010-07-22 18:04:19 +0000706 return 0;
707}