blob: 3a1445acd265d7526680c341c34bc22029614df3 [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
Carl-Daniel Hailfingercceafa22010-05-26 01:45:41 +000021#if defined(__i386__) || defined(__x86_64__)
22
Felix Singer980d6b82022-08-19 02:48:15 +020023#include <stdbool.h>
Rudolf Marek70e14592013-07-25 22:58:56 +000024#include <string.h>
25#include <stdlib.h>
Jason Wanga3f04be2008-11-28 21:36:51 +000026#include "flash.h"
Carl-Daniel Hailfinger5b997c32010-07-27 22:41:39 +000027#include "programmer.h"
Patrick Georgi32508eb2012-07-20 20:35:14 +000028#include "hwaccess.h"
Jason Wanga3f04be2008-11-28 21:36:51 +000029#include "spi.h"
30
Carl-Daniel Hailfinger2c7ba8c2009-06-23 00:47:26 +000031/* This struct is unused, but helps visualize the SB600 SPI BAR layout.
32 *struct sb600_spi_controller {
33 * unsigned int spi_cntrl0; / * 00h * /
34 * unsigned int restrictedcmd1; / * 04h * /
35 * unsigned int restrictedcmd2; / * 08h * /
36 * unsigned int spi_cntrl1; / * 0ch * /
37 * unsigned int spi_cmdvalue0; / * 10h * /
38 * unsigned int spi_cmdvalue1; / * 14h * /
39 * unsigned int spi_cmdvalue2; / * 18h * /
40 * unsigned int spi_fakeid; / * 1Ch * /
41 *};
42 */
Jason Wanga3f04be2008-11-28 21:36:51 +000043
Michael Karcherb05b9e12010-07-22 18:04:19 +000044static uint8_t *sb600_spibar = NULL;
Stefan Tauner463dd692013-08-08 12:00:19 +000045enum amd_chipset {
46 CHIPSET_AMD_UNKNOWN,
47 CHIPSET_SB6XX,
48 CHIPSET_SB7XX, /* SP5100 too */
49 CHIPSET_SB89XX, /* Hudson-1 too */
50 CHIPSET_HUDSON234,
Martin Roth82b6ec12014-07-15 13:50:58 +000051 CHIPSET_BOLTON,
Stefan Tauner463dd692013-08-08 12:00:19 +000052 CHIPSET_YANGTZE,
Edward O'Callaghan93737bc2019-10-29 18:30:01 +110053 CHIPSET_PROMONTORY,
Stefan Tauner463dd692013-08-08 12:00:19 +000054};
55static enum amd_chipset amd_gen = CHIPSET_AMD_UNKNOWN;
56
Stefan Taunerd5b2aef2014-05-16 21:39:28 +000057#define FIFO_SIZE_OLD 8
Wei Hu31402ee2014-05-16 21:39:33 +000058#define FIFO_SIZE_YANGTZE 71
Stefan Taunerd5b2aef2014-05-16 21:39:28 +000059
Edward O'Callaghan5eca4272020-04-12 17:27:53 +100060static int sb600_spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
Stefan Taunerd5b2aef2014-05-16 21:39:28 +000061 const unsigned char *writearr, unsigned char *readarr);
Edward O'Callaghan5eca4272020-04-12 17:27:53 +100062static int spi100_spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
Wei Hu31402ee2014-05-16 21:39:33 +000063 const unsigned char *writearr, unsigned char *readarr);
Stefan Taunerd5b2aef2014-05-16 21:39:28 +000064
Carl-Daniel Hailfingera5bcbce2014-07-19 22:03:29 +000065static struct spi_master spi_master_sb600 = {
Thomas Heijligen43040f22022-06-23 14:38:35 +020066 .max_data_read = FIFO_SIZE_OLD,
67 .max_data_write = FIFO_SIZE_OLD - 3,
68 .command = sb600_spi_send_command,
69 .multicommand = default_spi_send_multicommand,
70 .read = default_spi_read,
71 .write_256 = default_spi_write_256,
72 .write_aai = default_spi_write_aai,
Stefan Taunerd5b2aef2014-05-16 21:39:28 +000073};
74
Carl-Daniel Hailfingera5bcbce2014-07-19 22:03:29 +000075static struct spi_master spi_master_yangtze = {
Thomas Heijligen43040f22022-06-23 14:38:35 +020076 .max_data_read = FIFO_SIZE_YANGTZE - 3, /* Apparently the big SPI 100 buffer is not a ring buffer. */
77 .max_data_write = FIFO_SIZE_YANGTZE - 3,
78 .command = spi100_spi_send_command,
79 .multicommand = default_spi_send_multicommand,
80 .read = default_spi_read,
81 .write_256 = default_spi_write_256,
82 .write_aai = default_spi_write_aai,
Wei Hu31402ee2014-05-16 21:39:33 +000083};
84
Edward O'Callaghanc0a27e12019-10-29 17:05:39 +110085static int find_smbus_dev_rev(uint16_t vendor, uint16_t device)
86{
87 struct pci_dev *smbus_dev = pci_dev_find(vendor, device);
88 if (!smbus_dev) {
89 msg_pdbg("No SMBus device with ID %04X:%04X found.\n", vendor, device);
90 msg_perr("ERROR: SMBus device not found. Not enabling SPI.\n");
91 return -1;
92 }
93 return pci_read_byte(smbus_dev, PCI_REVISION_ID);
94}
95
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +110096/* Determine the chipset's version and identify the respective SMBUS device. */
97static int determine_generation(struct pci_dev *dev)
Stefan Tauner463dd692013-08-08 12:00:19 +000098{
99 amd_gen = CHIPSET_AMD_UNKNOWN;
Stefan Tauner4442b812013-09-12 15:48:35 +0000100 msg_pdbg2("Trying to determine the generation of the SPI interface... ");
101 if (dev->device_id == 0x438d) {
102 amd_gen = CHIPSET_SB6XX;
103 msg_pdbg("SB6xx detected.\n");
104 } else if (dev->device_id == 0x439d) {
Edward O'Callaghanc0a27e12019-10-29 17:05:39 +1100105 int rev = find_smbus_dev_rev(0x1002, 0x4385);
106 if (rev < 0)
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +1100107 return -1;
Stefan Tauner4442b812013-09-12 15:48:35 +0000108 if (rev >= 0x39 && rev <= 0x3D) {
109 amd_gen = CHIPSET_SB7XX;
110 msg_pdbg("SB7xx/SP5100 detected.\n");
111 } else if (rev >= 0x40 && rev <= 0x42) {
112 amd_gen = CHIPSET_SB89XX;
113 msg_pdbg("SB8xx/SB9xx/Hudson-1 detected.\n");
114 } else {
115 msg_pwarn("SB device found but SMBus revision 0x%02x does not match known values.\n"
Nico Huberac90af62022-12-18 00:22:47 +0000116 "Assuming SB8xx/SB9xx/Hudson-1.\n"
117 "Please send a log to flashrom-stable@flashrom.org\n",
118 rev);
Stefan Tauner4442b812013-09-12 15:48:35 +0000119 amd_gen = CHIPSET_SB89XX;
120 }
121 } else if (dev->device_id == 0x780e) {
Stefan Tauner463dd692013-08-08 12:00:19 +0000122 /* The PCI ID of the LPC bridge doesn't change between Hudson-2/3/4 and Yangtze (Kabini/Temash)
123 * although they use different SPI interfaces. */
Edward O'Callaghanc0a27e12019-10-29 17:05:39 +1100124 int rev = find_smbus_dev_rev(0x1022, 0x780B);
125 if (rev < 0)
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +1100126 return -1;
Stefan Tauner463dd692013-08-08 12:00:19 +0000127 if (rev >= 0x11 && rev <= 0x15) {
128 amd_gen = CHIPSET_HUDSON234;
129 msg_pdbg("Hudson-2/3/4 detected.\n");
Martin Roth82b6ec12014-07-15 13:50:58 +0000130 } else if (rev == 0x16) {
131 amd_gen = CHIPSET_BOLTON;
132 msg_pdbg("Bolton detected.\n");
Stefan Tauner5c316f92015-02-08 21:57:52 +0000133 } else if ((rev >= 0x39 && rev <= 0x3A) || rev == 0x42) {
Stefan Tauner463dd692013-08-08 12:00:19 +0000134 amd_gen = CHIPSET_YANGTZE;
135 msg_pdbg("Yangtze detected.\n");
136 } else {
137 msg_pwarn("FCH device found but SMBus revision 0x%02x does not match known values.\n"
Nico Huberac90af62022-12-18 00:22:47 +0000138 "Please report this to flashrom-stable@flashrom.org and include this\n"
139 "log and the output of lspci -nnvx, thanks!.\n", rev);
Stefan Tauner463dd692013-08-08 12:00:19 +0000140 }
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100141 } else if (dev->device_id == 0x790e) {
Edward O'Callaghanc0a27e12019-10-29 17:05:39 +1100142 int rev = find_smbus_dev_rev(0x1022, 0x790B);
143 if (rev < 0)
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +1100144 return -1;
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100145 if (rev == 0x4a) {
146 amd_gen = CHIPSET_YANGTZE;
147 msg_pdbg("Yangtze detected.\n");
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100148 } else if (rev == 0x4b) {
149 amd_gen = CHIPSET_PROMONTORY;
150 msg_pdbg("Promontory detected.\n");
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100151 } else {
152 msg_pwarn("FCH device found but SMBus revision 0x%02x does not match known values.\n"
Nico Huberac90af62022-12-18 00:22:47 +0000153 "Please report this to flashrom-stable@flashrom.org and include this\n"
154 "log and the output of lspci -nnvx, thanks!.\n", rev);
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100155 }
156
157
Stefan Tauner4442b812013-09-12 15:48:35 +0000158 } else
159 msg_pwarn("%s: Unknown LPC device %" PRIx16 ":%" PRIx16 ".\n"
Nico Huberac90af62022-12-18 00:22:47 +0000160 "Please report this to flashrom-stable@flashrom.org and include this\n"
161 "log and the output of lspci -nnvx, thanks!\n",
Stefan Tauner4442b812013-09-12 15:48:35 +0000162 __func__, dev->vendor_id, dev->device_id);
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +1100163 if (amd_gen == CHIPSET_AMD_UNKNOWN) {
164 msg_perr("Could not determine chipset generation.");
165 return -1;
166 }
167 return 0;
Stefan Tauner463dd692013-08-08 12:00:19 +0000168}
Jason Wanga3f04be2008-11-28 21:36:51 +0000169
Carl-Daniel Hailfinger2c7ba8c2009-06-23 00:47:26 +0000170static void reset_internal_fifo_pointer(void)
Jason Wanga3f04be2008-11-28 21:36:51 +0000171{
Carl-Daniel Hailfinger78185dc2009-05-17 15:49:24 +0000172 mmio_writeb(mmio_readb(sb600_spibar + 2) | 0x10, sb600_spibar + 2);
Jason Wanga3f04be2008-11-28 21:36:51 +0000173
Rudolf Marek70e14592013-07-25 22:58:56 +0000174 /* FIXME: This loop needs a timeout and a clearer message. */
Carl-Daniel Hailfinger78185dc2009-05-17 15:49:24 +0000175 while (mmio_readb(sb600_spibar + 0xD) & 0x7)
Carl-Daniel Hailfinger643415b2010-01-10 01:59:50 +0000176 msg_pspew("reset\n");
Jason Wanga3f04be2008-11-28 21:36:51 +0000177}
178
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000179static int compare_internal_fifo_pointer(uint8_t want)
180{
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000181 uint8_t have = mmio_readb(sb600_spibar + 0xd) & 0x07;
182 want %= FIFO_SIZE_OLD;
183 if (have != want) {
184 msg_perr("AMD SPI FIFO pointer corruption! Pointer is %d, wanted %d\n", have, want);
185 msg_perr("Something else is accessing the flash chip and causes random corruption.\n"
186 "Please stop all applications and drivers and IPMI which access the flash chip.\n");
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000187 return 1;
188 } else {
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000189 msg_pspew("AMD SPI FIFO pointer is %d, wanted %d\n", have, want);
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000190 return 0;
191 }
192}
193
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000194/* Check the number of bytes to be transmitted and extract opcode. */
Edward O'Callaghan5eca4272020-04-12 17:27:53 +1000195static int check_readwritecnt(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt)
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000196{
Carl-Daniel Hailfingera5bcbce2014-07-19 22:03:29 +0000197 unsigned int maxwritecnt = flash->mst->spi.max_data_write + 3;
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000198 if (writecnt > maxwritecnt) {
199 msg_pinfo("%s: SPI controller can not send %d bytes, it is limited to %d bytes\n",
200 __func__, writecnt, maxwritecnt);
201 return SPI_INVALID_LENGTH;
202 }
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000203
Stefan Tauner6697f712014-08-06 15:09:15 +0000204 unsigned int maxreadcnt = flash->mst->spi.max_data_read;
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000205 if (readcnt > maxreadcnt) {
206 msg_pinfo("%s: SPI controller can not receive %d bytes, it is limited to %d bytes\n",
207 __func__, readcnt, maxreadcnt);
208 return SPI_INVALID_LENGTH;
209 }
210 return 0;
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000211}
212
Carl-Daniel Hailfinger2c7ba8c2009-06-23 00:47:26 +0000213static void execute_command(void)
Jason Wanga3f04be2008-11-28 21:36:51 +0000214{
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000215 msg_pspew("Executing... ");
Carl-Daniel Hailfinger78185dc2009-05-17 15:49:24 +0000216 mmio_writeb(mmio_readb(sb600_spibar + 2) | 1, sb600_spibar + 2);
Carl-Daniel Hailfinger78185dc2009-05-17 15:49:24 +0000217 while (mmio_readb(sb600_spibar + 2) & 1)
Jason Wanga3f04be2008-11-28 21:36:51 +0000218 ;
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000219 msg_pspew("done\n");
Jason Wanga3f04be2008-11-28 21:36:51 +0000220}
221
Edward O'Callaghan5eca4272020-04-12 17:27:53 +1000222static int sb600_spi_send_command(const struct flashctx *flash, unsigned int writecnt,
Carl-Daniel Hailfinger8a3c60c2011-12-18 15:01:24 +0000223 unsigned int readcnt,
224 const unsigned char *writearr,
225 unsigned char *readarr)
Jason Wanga3f04be2008-11-28 21:36:51 +0000226{
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000227 /* First byte is cmd which can not be sent through the FIFO. */
Jason Wanga3f04be2008-11-28 21:36:51 +0000228 unsigned char cmd = *writearr++;
Jason Wanga3f04be2008-11-28 21:36:51 +0000229 writecnt--;
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000230 msg_pspew("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, cmd, writecnt, readcnt);
231 mmio_writeb(cmd, sb600_spibar + 0);
Jason Wanga3f04be2008-11-28 21:36:51 +0000232
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000233 int ret = check_readwritecnt(flash, writecnt, readcnt);
234 if (ret != 0)
235 return ret;
Jason Wanga3f04be2008-11-28 21:36:51 +0000236
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000237 /* This is a workaround for a bug in SPI controller. If we only send
Carl-Daniel Hailfingerf8555e22009-07-23 01:36:08 +0000238 * an opcode and no additional data/address, the SPI controller will
239 * read one byte too few from the chip. Basically, the last byte of
240 * the chip response is discarded and will not end up in the FIFO.
241 * It is unclear if the CS# line is set high too early as well.
242 */
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000243 unsigned int readoffby1 = (writecnt > 0) ? 0 : 1;
244 uint8_t readwrite = (readcnt + readoffby1) << 4 | (writecnt);
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000245 mmio_writeb(readwrite, sb600_spibar + 1);
Jason Wanga3f04be2008-11-28 21:36:51 +0000246
Jason Wanga3f04be2008-11-28 21:36:51 +0000247 reset_internal_fifo_pointer();
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000248 msg_pspew("Filling FIFO: ");
Nico Huber519be662018-12-23 20:03:35 +0100249 unsigned int count;
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000250 for (count = 0; count < writecnt; count++) {
251 msg_pspew("[%02x]", writearr[count]);
252 mmio_writeb(writearr[count], sb600_spibar + 0xC);
Jason Wanga3f04be2008-11-28 21:36:51 +0000253 }
Carl-Daniel Hailfinger643415b2010-01-10 01:59:50 +0000254 msg_pspew("\n");
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000255 if (compare_internal_fifo_pointer(writecnt))
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000256 return SPI_PROGRAMMER_ERROR;
Jason Wanga3f04be2008-11-28 21:36:51 +0000257
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000258 /*
259 * We should send the data in sequence, which means we need to reset
260 * the FIFO pointer to the first byte we want to send.
261 */
262 reset_internal_fifo_pointer();
Jason Wanga3f04be2008-11-28 21:36:51 +0000263 execute_command();
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000264 if (compare_internal_fifo_pointer(writecnt + readcnt))
265 return SPI_PROGRAMMER_ERROR;
Jason Wanga3f04be2008-11-28 21:36:51 +0000266
267 /*
268 * After the command executed, we should find out the index of the
Carl-Daniel Hailfingerf8555e22009-07-23 01:36:08 +0000269 * received byte. Here we just reset the FIFO pointer and skip the
270 * writecnt.
271 * It would be possible to increase the FIFO pointer by one instead
272 * of reading and discarding one byte from the FIFO.
273 * The FIFO is implemented on top of an 8 byte ring buffer and the
274 * buffer is never cleared. For every byte that is shifted out after
275 * the opcode, the FIFO already stores the response from the chip.
276 * Usually, the chip will respond with 0x00 or 0xff.
Jason Wanga3f04be2008-11-28 21:36:51 +0000277 */
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000278 reset_internal_fifo_pointer();
Jason Wanga3f04be2008-11-28 21:36:51 +0000279
Carl-Daniel Hailfingerf8555e22009-07-23 01:36:08 +0000280 /* Skip the bytes we sent. */
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000281 msg_pspew("Skipping: ");
Jason Wanga3f04be2008-11-28 21:36:51 +0000282 for (count = 0; count < writecnt; count++) {
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000283 msg_pspew("[%02x]", mmio_readb(sb600_spibar + 0xC));
Jason Wanga3f04be2008-11-28 21:36:51 +0000284 }
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000285 msg_pspew("\n");
286 if (compare_internal_fifo_pointer(writecnt))
287 return SPI_PROGRAMMER_ERROR;
Jason Wanga3f04be2008-11-28 21:36:51 +0000288
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000289 msg_pspew("Reading FIFO: ");
290 for (count = 0; count < readcnt; count++) {
291 readarr[count] = mmio_readb(sb600_spibar + 0xC);
292 msg_pspew("[%02x]", readarr[count]);
Jason Wanga3f04be2008-11-28 21:36:51 +0000293 }
Carl-Daniel Hailfinger643415b2010-01-10 01:59:50 +0000294 msg_pspew("\n");
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000295 if (compare_internal_fifo_pointer(writecnt+readcnt))
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000296 return SPI_PROGRAMMER_ERROR;
297
298 if (mmio_readb(sb600_spibar + 1) != readwrite) {
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000299 msg_perr("Unexpected change in AMD SPI read/write count!\n");
300 msg_perr("Something else is accessing the flash chip and causes random corruption.\n"
301 "Please stop all applications and drivers and IPMI which access the flash chip.\n");
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000302 return SPI_PROGRAMMER_ERROR;
303 }
Jason Wanga3f04be2008-11-28 21:36:51 +0000304
305 return 0;
306}
Carl-Daniel Hailfingercceafa22010-05-26 01:45:41 +0000307
Edward O'Callaghan5eca4272020-04-12 17:27:53 +1000308static int spi100_spi_send_command(const struct flashctx *flash, unsigned int writecnt,
Wei Hu31402ee2014-05-16 21:39:33 +0000309 unsigned int readcnt,
310 const unsigned char *writearr,
311 unsigned char *readarr)
312{
313 /* First byte is cmd which can not be sent through the buffer. */
314 unsigned char cmd = *writearr++;
315 writecnt--;
316 msg_pspew("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, cmd, writecnt, readcnt);
317 mmio_writeb(cmd, sb600_spibar + 0);
318
319 int ret = check_readwritecnt(flash, writecnt, readcnt);
320 if (ret != 0)
321 return ret;
322
323 /* Use the extended TxByteCount and RxByteCount registers. */
324 mmio_writeb(writecnt, sb600_spibar + 0x48);
325 mmio_writeb(readcnt, sb600_spibar + 0x4b);
326
327 msg_pspew("Filling buffer: ");
Nico Huber519be662018-12-23 20:03:35 +0100328 unsigned int count;
Wei Hu31402ee2014-05-16 21:39:33 +0000329 for (count = 0; count < writecnt; count++) {
330 msg_pspew("[%02x]", writearr[count]);
331 mmio_writeb(writearr[count], sb600_spibar + 0x80 + count);
332 }
333 msg_pspew("\n");
334
335 execute_command();
336
337 msg_pspew("Reading buffer: ");
338 for (count = 0; count < readcnt; count++) {
339 readarr[count] = mmio_readb(sb600_spibar + 0x80 + (writecnt + count) % FIFO_SIZE_YANGTZE);
340 msg_pspew("[%02x]", readarr[count]);
341 }
342 msg_pspew("\n");
343
344 return 0;
345}
346
Stefan Taunera6a0d202013-09-15 14:17:39 +0000347struct spispeed {
348 const char *const name;
349 const uint8_t speed;
350};
351
352static const struct spispeed spispeeds[] = {
353 { "66 MHz", 0x00 },
354 { "33 MHz", 0x01 },
355 { "22 MHz", 0x02 },
356 { "16.5 MHz", 0x03 },
Wei Hu31402ee2014-05-16 21:39:33 +0000357 { "100 MHz", 0x04 },
358 { "Reserved", 0x05 },
359 { "Reserved", 0x06 },
360 { "800 kHz", 0x07 },
Stefan Taunera6a0d202013-09-15 14:17:39 +0000361};
362
363static int set_speed(struct pci_dev *dev, const struct spispeed *spispeed)
364{
365 bool success = false;
366 uint8_t speed = spispeed->speed;
367
368 msg_pdbg("Setting SPI clock to %s (0x%x).\n", spispeed->name, speed);
Wei Hu31402ee2014-05-16 21:39:33 +0000369 if (amd_gen >= CHIPSET_YANGTZE) {
370 rmmio_writew((speed << 12) | (speed << 8) | (speed << 4) | speed, sb600_spibar + 0x22);
371 uint16_t tmp = mmio_readw(sb600_spibar + 0x22);
372 success = (((tmp >> 12) & 0xf) == speed && ((tmp >> 8) & 0xf) == speed &&
373 ((tmp >> 4) & 0xf) == speed && ((tmp >> 0) & 0xf) == speed);
374 } else {
Stefan Taunera6a0d202013-09-15 14:17:39 +0000375 rmmio_writeb((mmio_readb(sb600_spibar + 0xd) & ~(0x3 << 4)) | (speed << 4), sb600_spibar + 0xd);
376 success = (speed == ((mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3));
377 }
378
379 if (!success) {
380 msg_perr("Setting SPI clock failed.\n");
381 return 1;
382 }
383 return 0;
384}
385
Wei Hu31402ee2014-05-16 21:39:33 +0000386static int set_mode(struct pci_dev *dev, uint8_t read_mode)
387{
388 uint32_t tmp = mmio_readl(sb600_spibar + 0x00);
389 tmp &= ~(0x6 << 28 | 0x1 << 18); /* Clear mode bits */
390 tmp |= ((read_mode & 0x6) << 28) | ((read_mode & 0x1) << 18);
391 rmmio_writel(tmp, sb600_spibar + 0x00);
392 if (tmp != mmio_readl(sb600_spibar + 0x00))
393 return 1;
394 return 0;
395}
396
Stefan Taunera6a0d202013-09-15 14:17:39 +0000397static int handle_speed(struct pci_dev *dev)
398{
399 uint32_t tmp;
Carl-Daniel Hailfinger57cdd6b2016-03-12 19:49:14 +0000400 uint8_t spispeed_idx = 3; /* Default to 16.5 MHz */
Stefan Taunera6a0d202013-09-15 14:17:39 +0000401
Stefan Tauner21071b02014-05-16 21:39:48 +0000402 char *spispeed = extract_programmer_param("spispeed");
403 if (spispeed != NULL) {
Carl-Daniel Hailfinger57cdd6b2016-03-12 19:49:14 +0000404 unsigned int i;
405 for (i = 0; i < ARRAY_SIZE(spispeeds); i++) {
406 if (strcasecmp(spispeeds[i].name, spispeed) == 0) {
407 spispeed_idx = i;
408 break;
Stefan Tauner21071b02014-05-16 21:39:48 +0000409 }
Stefan Tauner21071b02014-05-16 21:39:48 +0000410 }
Carl-Daniel Hailfinger57cdd6b2016-03-12 19:49:14 +0000411 /* "reserved" is not a valid speed.
412 * Error out on speeds not present in the spispeeds array.
413 * Only Yangtze supports the second half of indices.
414 * No 66 MHz before SB8xx. */
415 if ((strcasecmp(spispeed, "reserved") == 0) ||
416 (i == ARRAY_SIZE(spispeeds)) ||
417 (amd_gen < CHIPSET_YANGTZE && spispeed_idx > 3) ||
418 (amd_gen < CHIPSET_SB89XX && spispeed_idx == 0)) {
Stefan Tauner21071b02014-05-16 21:39:48 +0000419 msg_perr("Error: Invalid spispeed value: '%s'.\n", spispeed);
420 free(spispeed);
421 return 1;
422 }
423 free(spispeed);
424 }
425
Stefan Taunera6a0d202013-09-15 14:17:39 +0000426 /* See the chipset support matrix for SPI Base_Addr below for an explanation of the symbols used.
Martin Roth82b6ec12014-07-15 13:50:58 +0000427 * bit 6xx 7xx/SP5100 8xx 9xx hudson1 hudson234 bolton/yangtze
Stefan Taunera6a0d202013-09-15 14:17:39 +0000428 * 18 rsvd <- fastReadEnable ? <- ? SpiReadMode[0]
429 * 29:30 rsvd <- <- ? <- ? SpiReadMode[2:1]
430 */
Martin Roth82b6ec12014-07-15 13:50:58 +0000431 if (amd_gen >= CHIPSET_BOLTON) {
Wei Hu31402ee2014-05-16 21:39:33 +0000432 static const char *spireadmodes[] = {
433 "Normal (up to 33 MHz)", /* 0 */
434 "Reserved", /* 1 */
435 "Dual IO (1-1-2)", /* 2 */
436 "Quad IO (1-1-4)", /* 3 */
437 "Dual IO (1-2-2)", /* 4 */
438 "Quad IO (1-4-4)", /* 5 */
439 "Normal (up to 66 MHz)", /* 6 */
Martin Roth82b6ec12014-07-15 13:50:58 +0000440 "Fast Read", /* 7 (Not defined in the Bolton datasheet.) */
Wei Hu31402ee2014-05-16 21:39:33 +0000441 };
442 tmp = mmio_readl(sb600_spibar + 0x00);
443 uint8_t read_mode = ((tmp >> 28) & 0x6) | ((tmp >> 18) & 0x1);
444 msg_pdbg("SpiReadMode=%s (%i)\n", spireadmodes[read_mode], read_mode);
445 if (read_mode != 6) {
446 read_mode = 6; /* Default to "Normal (up to 66 MHz)" */
447 if (set_mode(dev, read_mode) != 0) {
448 msg_perr("Setting read mode to \"%s\" failed.\n", spireadmodes[read_mode]);
449 return 1;
450 }
451 msg_pdbg("Setting read mode to \"%s\" succeeded.\n", spireadmodes[read_mode]);
452 }
453
Martin Roth82b6ec12014-07-15 13:50:58 +0000454 if (amd_gen >= CHIPSET_YANGTZE) {
455 tmp = mmio_readb(sb600_spibar + 0x20);
456 msg_pdbg("UseSpi100 is %sabled\n", (tmp & 0x1) ? "en" : "dis");
457 if ((tmp & 0x1) == 0) {
458 rmmio_writeb(tmp | 0x1, sb600_spibar + 0x20);
459 tmp = mmio_readb(sb600_spibar + 0x20) & 0x1;
460 if (tmp == 0) {
461 msg_perr("Enabling Spi100 failed.\n");
462 return 1;
463 }
464 msg_pdbg("Enabling Spi100 succeeded.\n");
465 }
466
467 tmp = mmio_readw(sb600_spibar + 0x22); /* SPI 100 Speed Config */
468 msg_pdbg("NormSpeedNew is %s\n", spispeeds[(tmp >> 12) & 0xf].name);
469 msg_pdbg("FastSpeedNew is %s\n", spispeeds[(tmp >> 8) & 0xf].name);
470 msg_pdbg("AltSpeedNew is %s\n", spispeeds[(tmp >> 4) & 0xf].name);
471 msg_pdbg("TpmSpeedNew is %s\n", spispeeds[(tmp >> 0) & 0xf].name);
472 }
Wei Hu31402ee2014-05-16 21:39:33 +0000473 } else {
Stefan Taunera6a0d202013-09-15 14:17:39 +0000474 if (amd_gen >= CHIPSET_SB89XX && amd_gen <= CHIPSET_HUDSON234) {
475 bool fast_read = (mmio_readl(sb600_spibar + 0x00) >> 18) & 0x1;
476 msg_pdbg("Fast Reads are %sabled\n", fast_read ? "en" : "dis");
477 if (fast_read) {
478 msg_pdbg("Disabling them temporarily.\n");
479 rmmio_writel(mmio_readl(sb600_spibar + 0x00) & ~(0x1 << 18),
480 sb600_spibar + 0x00);
481 }
482 }
483 tmp = (mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3;
484 msg_pdbg("NormSpeed is %s\n", spispeeds[tmp].name);
485 }
486 return set_speed(dev, &spispeeds[spispeed_idx]);
487}
488
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000489static int handle_imc(struct pci_dev *dev)
Rudolf Marek70e14592013-07-25 22:58:56 +0000490{
491 /* Handle IMC everywhere but sb600 which does not have one. */
Stefan Tauner4442b812013-09-12 15:48:35 +0000492 if (amd_gen == CHIPSET_SB6XX)
Rudolf Marek70e14592013-07-25 22:58:56 +0000493 return 0;
494
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000495 bool amd_imc_force = false;
496 char *arg = extract_programmer_param("amd_imc_force");
497 if (arg && !strcmp(arg, "yes")) {
498 amd_imc_force = true;
499 msg_pspew("amd_imc_force enabled.\n");
500 } else if (arg && !strlen(arg)) {
501 msg_perr("Missing argument for amd_imc_force.\n");
502 free(arg);
503 return 1;
504 } else if (arg) {
505 msg_perr("Unknown argument for amd_imc_force: \"%s\" (not \"yes\").\n", arg);
506 free(arg);
507 return 1;
508 }
509 free(arg);
510
Rudolf Marek70e14592013-07-25 22:58:56 +0000511 /* TODO: we should not only look at IntegratedImcPresent (LPC Dev 20, Func 3, 40h) but also at
Stefan Tauner5c316f92015-02-08 21:57:52 +0000512 * IMCEnable(Strap) and Override EcEnable(Strap) (sb8xx, sb9xx?, a50, Bolton: Misc_Reg: 80h-87h;
Rudolf Marek70e14592013-07-25 22:58:56 +0000513 * sb7xx, sp5100: PM_Reg: B0h-B1h) etc. */
514 uint8_t reg = pci_read_byte(dev, 0x40);
515 if ((reg & (1 << 7)) == 0) {
516 msg_pdbg("IMC is not active.\n");
517 return 0;
518 }
519
520 if (!amd_imc_force)
Felix Singer980d6b82022-08-19 02:48:15 +0200521 programmer_may_write = false;
Stefan Tauner463dd692013-08-08 12:00:19 +0000522 msg_pinfo("Writes have been disabled for safety reasons because the presence of the IMC\n"
523 "was detected and it could interfere with accessing flash memory. Flashrom will\n"
524 "try to disable it temporarily but even then this might not be safe:\n"
Stefan Tauner0be072c2016-03-13 15:16:30 +0000525 "when it is re-enabled and after a reboot it expects to find working code\n"
Rudolf Marek70e14592013-07-25 22:58:56 +0000526 "in the flash and it is unpredictable what happens if there is none.\n"
527 "\n"
528 "To be safe make sure that there is a working IMC firmware at the right\n"
529 "location in the image you intend to write and do not attempt to erase.\n"
530 "\n"
531 "You can enforce write support with the amd_imc_force programmer option.\n");
532 if (amd_imc_force)
533 msg_pinfo("Continuing with write support because the user forced us to!\n");
534
535 return amd_imc_shutdown(dev);
536}
537
Michael Karcherb05b9e12010-07-22 18:04:19 +0000538int sb600_probe_spi(struct pci_dev *dev)
539{
540 struct pci_dev *smbus_dev;
541 uint32_t tmp;
542 uint8_t reg;
Rudolf Marek70e14592013-07-25 22:58:56 +0000543
Michael Karcherb05b9e12010-07-22 18:04:19 +0000544 /* Read SPI_BaseAddr */
545 tmp = pci_read_long(dev, 0xa0);
546 tmp &= 0xffffffe0; /* remove bits 4-0 (reserved) */
547 msg_pdbg("SPI base address is at 0x%x\n", tmp);
548
549 /* If the BAR has address 0, it is unlikely SPI is used. */
550 if (!tmp)
551 return 0;
552
553 /* Physical memory has to be mapped at page (4k) boundaries. */
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000554 sb600_spibar = rphysmap("SB600 SPI registers", tmp & 0xfffff000, 0x1000);
555 if (sb600_spibar == ERROR_PTR)
Niklas Söderlund5d307202013-09-14 09:02:27 +0000556 return ERROR_FATAL;
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000557
Michael Karcherb05b9e12010-07-22 18:04:19 +0000558 /* The low bits of the SPI base address are used as offset into
559 * the mapped page.
560 */
561 sb600_spibar += tmp & 0xfff;
562
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +1100563 if (determine_generation(dev) < 0)
Stefan Tauner4442b812013-09-12 15:48:35 +0000564 return ERROR_NONFATAL;
Stefan Tauner463dd692013-08-08 12:00:19 +0000565
Stefan Tauner4442b812013-09-12 15:48:35 +0000566 /* How to read the following table and similar ones in this file:
567 * "?" means we have no datasheet for this chipset generation or it doesn't have any relevant info.
568 * "<-" means the bit/register meaning is identical to the next non-"?" chipset to the left. "<-" thus
569 * never refers to another "?".
570 * If a "?" chipset is between two chipsets with identical meaning, we assume the meaning didn't change
571 * twice in between, i.e. the meaning is unchanged for the "?" chipset. Usually we assume that
572 * succeeding hardware supports the same functionality as its predecessor unless proven different by
573 * tests or documentation, hence "?" will often be implemented equally to "<-".
574 *
575 * Chipset support matrix for SPI Base_Addr (LPC PCI reg 0xa0)
576 * bit 6xx 7xx/SP5100 8xx 9xx hudson1 hudson2+ yangtze
577 * 3 rsvd <- <- ? <- ? RouteTpm2Spi
578 * 2 rsvd AbortEnable rsvd ? <- ? <-
579 * 1 rsvd SpiRomEnable <- ? <- ? <-
580 * 0 rsvd AltSpiCSEnable rsvd ? <- ? <-
581 */
582 if (amd_gen >= CHIPSET_SB7XX) {
583 tmp = pci_read_long(dev, 0xa0);
584 msg_pdbg("SpiRomEnable=%i", (tmp >> 1) & 0x1);
585 if (amd_gen == CHIPSET_SB7XX)
586 msg_pdbg(", AltSpiCSEnable=%i, AbortEnable=%i", tmp & 0x1, (tmp >> 2) & 0x1);
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100587 else if (amd_gen >= CHIPSET_YANGTZE)
Wei Hu31402ee2014-05-16 21:39:33 +0000588 msg_pdbg(", RouteTpm2Sp=%i", (tmp >> 3) & 0x1);
Michael Karcherb05b9e12010-07-22 18:04:19 +0000589
Stefan Tauner4442b812013-09-12 15:48:35 +0000590 tmp = pci_read_byte(dev, 0xba);
591 msg_pdbg(", PrefetchEnSPIFromIMC=%i", (tmp & 0x4) >> 2);
592
593 tmp = pci_read_byte(dev, 0xbb);
594 /* FIXME: Set bit 3,6,7 if not already set.
595 * Set bit 5, otherwise SPI accesses are pointless in LPC mode.
596 * See doc 42413 AMD SB700/710/750 RPR.
597 */
598 if (amd_gen == CHIPSET_SB7XX)
599 msg_pdbg(", SpiOpEnInLpcMode=%i", (tmp >> 5) & 0x1);
600 msg_pdbg(", PrefetchEnSPIFromHost=%i\n", tmp & 0x1);
601 }
602
603 /* Chipset support matrix for SPI_Cntrl0 (spibar + 0x0)
604 * See the chipset support matrix for SPI Base_Addr above for an explanation of the symbols used.
605 * bit 6xx 7xx/SP5100 8xx 9xx hudson1 hudson2+ yangtze
606 * 17 rsvd <- <- ? <- ? <-
Stefan Taunera6a0d202013-09-15 14:17:39 +0000607 * 18 rsvd <- fastReadEnable<1> ? <- ? SpiReadMode[0]<1>
Stefan Tauner4442b812013-09-12 15:48:35 +0000608 * 19 SpiArbEnable <- <- ? <- ? <-
609 * 20 (FifoPtrClr) <- <- ? <- ? <-
610 * 21 (FifoPtrInc) <- <- ? <- ? IllegalAccess
611 * 22 SpiAccessMacRomEn <- <- ? <- ? <-
612 * 23 SpiHostAccessRomEn <- <- ? <- ? <-
613 * 24:26 ArbWaitCount <- <- ? <- ? <-
614 * 27 SpiBridgeDisable <- <- ? <- ? rsvd
615 * 28 rsvd DropOneClkOnRd = SPIClkGate ? <- ? <-
Stefan Taunera6a0d202013-09-15 14:17:39 +0000616 * 29:30 rsvd <- <- ? <- ? SpiReadMode[2:1]<1>
Stefan Tauner4442b812013-09-12 15:48:35 +0000617 * 31 rsvd <- SpiBusy ? <- ? <-
Stefan Taunera6a0d202013-09-15 14:17:39 +0000618 *
619 * <1> see handle_speed
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000620 */
Stefan Tauner4442b812013-09-12 15:48:35 +0000621 tmp = mmio_readl(sb600_spibar + 0x00);
622 msg_pdbg("(0x%08" PRIx32 ") SpiArbEnable=%i", tmp, (tmp >> 19) & 0x1);
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100623 if (amd_gen >= CHIPSET_YANGTZE)
Wei Hu31402ee2014-05-16 21:39:33 +0000624 msg_pdbg(", IllegalAccess=%i", (tmp >> 21) & 0x1);
Stefan Tauner4442b812013-09-12 15:48:35 +0000625
626 msg_pdbg(", SpiAccessMacRomEn=%i, SpiHostAccessRomEn=%i, ArbWaitCount=%i",
627 (tmp >> 22) & 0x1, (tmp >> 23) & 0x1, (tmp >> 24) & 0x7);
628
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100629 if (amd_gen < CHIPSET_YANGTZE)
Stefan Tauner4442b812013-09-12 15:48:35 +0000630 msg_pdbg(", SpiBridgeDisable=%i", (tmp >> 27) & 0x1);
631
632 switch (amd_gen) {
633 case CHIPSET_SB7XX:
634 msg_pdbg(", DropOneClkOnRd/SpiClkGate=%i", (tmp >> 28) & 0x1);
Richard Hughesdb7482b2018-12-19 12:04:30 +0000635 /* Fall through. */
Stefan Tauner4442b812013-09-12 15:48:35 +0000636 case CHIPSET_SB89XX:
637 case CHIPSET_HUDSON234:
Wei Hu31402ee2014-05-16 21:39:33 +0000638 case CHIPSET_YANGTZE:
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100639 case CHIPSET_PROMONTORY:
Stefan Tauner4442b812013-09-12 15:48:35 +0000640 msg_pdbg(", SpiBusy=%i", (tmp >> 31) & 0x1);
641 default: break;
642 }
643 msg_pdbg("\n");
644
645 if (((tmp >> 22) & 0x1) == 0 || ((tmp >> 23) & 0x1) == 0) {
646 msg_perr("ERROR: State of SpiAccessMacRomEn or SpiHostAccessRomEn prohibits full access.\n");
647 return ERROR_NONFATAL;
648 }
649
Stefan Tauner4442b812013-09-12 15:48:35 +0000650 if (amd_gen >= CHIPSET_SB89XX) {
651 tmp = mmio_readb(sb600_spibar + 0x1D);
652 msg_pdbg("Using SPI_CS%d\n", tmp & 0x3);
Wei Hu31402ee2014-05-16 21:39:33 +0000653 /* FIXME: Handle SpiProtect* configuration on Yangtze. */
Stefan Tauner4442b812013-09-12 15:48:35 +0000654 }
655
Michael Karcherb05b9e12010-07-22 18:04:19 +0000656 /* Look for the SMBus device. */
657 smbus_dev = pci_dev_find(0x1002, 0x4385);
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100658 if (!smbus_dev)
Stefan Tauner463dd692013-08-08 12:00:19 +0000659 smbus_dev = pci_dev_find(0x1022, 0x780b); /* AMD FCH */
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100660 if (!smbus_dev)
661 smbus_dev = pci_dev_find(0x1022, 0x790b); /* AMD FP4 */
662 if (!smbus_dev) {
663 msg_perr("ERROR: SMBus device not found. Not enabling SPI.\n");
664 return ERROR_NONFATAL;
Michael Karcherb05b9e12010-07-22 18:04:19 +0000665 }
666
667 /* Note about the bit tests below: If a bit is zero, the GPIO is SPI. */
668 /* GPIO11/SPI_DO and GPIO12/SPI_DI status */
669 reg = pci_read_byte(smbus_dev, 0xAB);
670 reg &= 0xC0;
671 msg_pdbg("GPIO11 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_DO");
672 msg_pdbg("GPIO12 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_DI");
673 if (reg != 0x00) {
674 msg_pdbg("Not enabling SPI");
675 return 0;
676 }
677 /* GPIO31/SPI_HOLD and GPIO32/SPI_CS status */
678 reg = pci_read_byte(smbus_dev, 0x83);
679 reg &= 0xC0;
680 msg_pdbg("GPIO31 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_HOLD");
681 msg_pdbg("GPIO32 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_CS");
682 /* SPI_HOLD is not used on all boards, filter it out. */
683 if ((reg & 0x80) != 0x00) {
684 msg_pdbg("Not enabling SPI");
685 return 0;
686 }
687 /* GPIO47/SPI_CLK status */
688 reg = pci_read_byte(smbus_dev, 0xA7);
689 reg &= 0x40;
690 msg_pdbg("GPIO47 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_CLK");
691 if (reg != 0x00) {
692 msg_pdbg("Not enabling SPI");
693 return 0;
694 }
695
Stefan Taunera6a0d202013-09-15 14:17:39 +0000696 if (handle_speed(dev) != 0)
697 return ERROR_FATAL;
698
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000699 if (handle_imc(dev) != 0)
Rudolf Marek70e14592013-07-25 22:58:56 +0000700 return ERROR_FATAL;
Carl-Daniel Hailfinger39446e32010-09-15 12:02:07 +0000701
Wei Hu31402ee2014-05-16 21:39:33 +0000702 /* Starting with Yangtze the SPI controller got a different interface with a much bigger buffer. */
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100703 if (amd_gen < CHIPSET_YANGTZE)
Nico Huber5e08e3e2021-05-11 17:38:14 +0200704 register_spi_master(&spi_master_sb600, NULL);
Wei Hu31402ee2014-05-16 21:39:33 +0000705 else
Nico Huber5e08e3e2021-05-11 17:38:14 +0200706 register_spi_master(&spi_master_yangtze, NULL);
Michael Karcherb05b9e12010-07-22 18:04:19 +0000707 return 0;
708}
709
Carl-Daniel Hailfingercceafa22010-05-26 01:45:41 +0000710#endif