blob: 8827fb926c793eec494bf59776ce8307226bdfd4 [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,
Stefan Taunerd5b2aef2014-05-16 21:39:28 +000071};
72
Carl-Daniel Hailfingera5bcbce2014-07-19 22:03:29 +000073static struct spi_master spi_master_yangtze = {
Thomas Heijligen43040f22022-06-23 14:38:35 +020074 .max_data_read = FIFO_SIZE_YANGTZE - 3, /* Apparently the big SPI 100 buffer is not a ring buffer. */
75 .max_data_write = FIFO_SIZE_YANGTZE - 3,
76 .command = spi100_spi_send_command,
77 .multicommand = default_spi_send_multicommand,
78 .read = default_spi_read,
79 .write_256 = default_spi_write_256,
Wei Hu31402ee2014-05-16 21:39:33 +000080};
81
Edward O'Callaghanc0a27e12019-10-29 17:05:39 +110082static int find_smbus_dev_rev(uint16_t vendor, uint16_t device)
83{
Edward O'Callaghan19ce50d2021-11-13 17:59:18 +110084 struct pci_dev *smbus_dev = pcidev_find(vendor, device);
Edward O'Callaghanc0a27e12019-10-29 17:05:39 +110085 if (!smbus_dev) {
86 msg_pdbg("No SMBus device with ID %04X:%04X found.\n", vendor, device);
87 msg_perr("ERROR: SMBus device not found. Not enabling SPI.\n");
88 return -1;
89 }
90 return pci_read_byte(smbus_dev, PCI_REVISION_ID);
91}
92
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +110093/* Determine the chipset's version and identify the respective SMBUS device. */
94static int determine_generation(struct pci_dev *dev)
Stefan Tauner463dd692013-08-08 12:00:19 +000095{
96 amd_gen = CHIPSET_AMD_UNKNOWN;
Stefan Tauner4442b812013-09-12 15:48:35 +000097 msg_pdbg2("Trying to determine the generation of the SPI interface... ");
98 if (dev->device_id == 0x438d) {
99 amd_gen = CHIPSET_SB6XX;
100 msg_pdbg("SB6xx detected.\n");
101 } else if (dev->device_id == 0x439d) {
Edward O'Callaghanc0a27e12019-10-29 17:05:39 +1100102 int rev = find_smbus_dev_rev(0x1002, 0x4385);
103 if (rev < 0)
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +1100104 return -1;
Stefan Tauner4442b812013-09-12 15:48:35 +0000105 if (rev >= 0x39 && rev <= 0x3D) {
106 amd_gen = CHIPSET_SB7XX;
107 msg_pdbg("SB7xx/SP5100 detected.\n");
108 } else if (rev >= 0x40 && rev <= 0x42) {
109 amd_gen = CHIPSET_SB89XX;
110 msg_pdbg("SB8xx/SB9xx/Hudson-1 detected.\n");
111 } else {
112 msg_pwarn("SB device found but SMBus revision 0x%02x does not match known values.\n"
Nico Huberac90af62022-12-18 00:22:47 +0000113 "Assuming SB8xx/SB9xx/Hudson-1.\n"
114 "Please send a log to flashrom-stable@flashrom.org\n",
115 rev);
Stefan Tauner4442b812013-09-12 15:48:35 +0000116 amd_gen = CHIPSET_SB89XX;
117 }
118 } else if (dev->device_id == 0x780e) {
Stefan Tauner463dd692013-08-08 12:00:19 +0000119 /* The PCI ID of the LPC bridge doesn't change between Hudson-2/3/4 and Yangtze (Kabini/Temash)
120 * although they use different SPI interfaces. */
Edward O'Callaghanc0a27e12019-10-29 17:05:39 +1100121 int rev = find_smbus_dev_rev(0x1022, 0x780B);
122 if (rev < 0)
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +1100123 return -1;
Stefan Tauner463dd692013-08-08 12:00:19 +0000124 if (rev >= 0x11 && rev <= 0x15) {
125 amd_gen = CHIPSET_HUDSON234;
126 msg_pdbg("Hudson-2/3/4 detected.\n");
Martin Roth82b6ec12014-07-15 13:50:58 +0000127 } else if (rev == 0x16) {
128 amd_gen = CHIPSET_BOLTON;
129 msg_pdbg("Bolton detected.\n");
Stefan Tauner5c316f92015-02-08 21:57:52 +0000130 } else if ((rev >= 0x39 && rev <= 0x3A) || rev == 0x42) {
Stefan Tauner463dd692013-08-08 12:00:19 +0000131 amd_gen = CHIPSET_YANGTZE;
132 msg_pdbg("Yangtze detected.\n");
133 } else {
134 msg_pwarn("FCH device found but SMBus revision 0x%02x does not match known values.\n"
Nico Huberac90af62022-12-18 00:22:47 +0000135 "Please report this to flashrom-stable@flashrom.org and include this\n"
136 "log and the output of lspci -nnvx, thanks!.\n", rev);
Stefan Tauner463dd692013-08-08 12:00:19 +0000137 }
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100138 } else if (dev->device_id == 0x790e) {
Edward O'Callaghanc0a27e12019-10-29 17:05:39 +1100139 int rev = find_smbus_dev_rev(0x1022, 0x790B);
140 if (rev < 0)
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +1100141 return -1;
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100142 if (rev == 0x4a) {
143 amd_gen = CHIPSET_YANGTZE;
144 msg_pdbg("Yangtze detected.\n");
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100145 } else if (rev == 0x4b) {
146 amd_gen = CHIPSET_PROMONTORY;
147 msg_pdbg("Promontory detected.\n");
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100148 } else {
149 msg_pwarn("FCH device found but SMBus revision 0x%02x does not match known values.\n"
Nico Huberac90af62022-12-18 00:22:47 +0000150 "Please report this to flashrom-stable@flashrom.org and include this\n"
151 "log and the output of lspci -nnvx, thanks!.\n", rev);
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100152 }
153
154
Stefan Tauner4442b812013-09-12 15:48:35 +0000155 } else
156 msg_pwarn("%s: Unknown LPC device %" PRIx16 ":%" PRIx16 ".\n"
Nico Huberac90af62022-12-18 00:22:47 +0000157 "Please report this to flashrom-stable@flashrom.org and include this\n"
158 "log and the output of lspci -nnvx, thanks!\n",
Stefan Tauner4442b812013-09-12 15:48:35 +0000159 __func__, dev->vendor_id, dev->device_id);
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +1100160 if (amd_gen == CHIPSET_AMD_UNKNOWN) {
161 msg_perr("Could not determine chipset generation.");
162 return -1;
163 }
164 return 0;
Stefan Tauner463dd692013-08-08 12:00:19 +0000165}
Jason Wanga3f04be2008-11-28 21:36:51 +0000166
Carl-Daniel Hailfinger2c7ba8c2009-06-23 00:47:26 +0000167static void reset_internal_fifo_pointer(void)
Jason Wanga3f04be2008-11-28 21:36:51 +0000168{
Carl-Daniel Hailfinger78185dc2009-05-17 15:49:24 +0000169 mmio_writeb(mmio_readb(sb600_spibar + 2) | 0x10, sb600_spibar + 2);
Jason Wanga3f04be2008-11-28 21:36:51 +0000170
Rudolf Marek70e14592013-07-25 22:58:56 +0000171 /* FIXME: This loop needs a timeout and a clearer message. */
Carl-Daniel Hailfinger78185dc2009-05-17 15:49:24 +0000172 while (mmio_readb(sb600_spibar + 0xD) & 0x7)
Carl-Daniel Hailfinger643415b2010-01-10 01:59:50 +0000173 msg_pspew("reset\n");
Jason Wanga3f04be2008-11-28 21:36:51 +0000174}
175
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000176static int compare_internal_fifo_pointer(uint8_t want)
177{
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000178 uint8_t have = mmio_readb(sb600_spibar + 0xd) & 0x07;
179 want %= FIFO_SIZE_OLD;
180 if (have != want) {
181 msg_perr("AMD SPI FIFO pointer corruption! Pointer is %d, wanted %d\n", have, want);
182 msg_perr("Something else is accessing the flash chip and causes random corruption.\n"
183 "Please stop all applications and drivers and IPMI which access the flash chip.\n");
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000184 return 1;
185 } else {
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000186 msg_pspew("AMD SPI FIFO pointer is %d, wanted %d\n", have, want);
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000187 return 0;
188 }
189}
190
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000191/* Check the number of bytes to be transmitted and extract opcode. */
Edward O'Callaghan5eca4272020-04-12 17:27:53 +1000192static int check_readwritecnt(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt)
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000193{
Carl-Daniel Hailfingera5bcbce2014-07-19 22:03:29 +0000194 unsigned int maxwritecnt = flash->mst->spi.max_data_write + 3;
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000195 if (writecnt > maxwritecnt) {
196 msg_pinfo("%s: SPI controller can not send %d bytes, it is limited to %d bytes\n",
197 __func__, writecnt, maxwritecnt);
198 return SPI_INVALID_LENGTH;
199 }
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000200
Stefan Tauner6697f712014-08-06 15:09:15 +0000201 unsigned int maxreadcnt = flash->mst->spi.max_data_read;
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000202 if (readcnt > maxreadcnt) {
203 msg_pinfo("%s: SPI controller can not receive %d bytes, it is limited to %d bytes\n",
204 __func__, readcnt, maxreadcnt);
205 return SPI_INVALID_LENGTH;
206 }
207 return 0;
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000208}
209
Carl-Daniel Hailfinger2c7ba8c2009-06-23 00:47:26 +0000210static void execute_command(void)
Jason Wanga3f04be2008-11-28 21:36:51 +0000211{
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000212 msg_pspew("Executing... ");
Carl-Daniel Hailfinger78185dc2009-05-17 15:49:24 +0000213 mmio_writeb(mmio_readb(sb600_spibar + 2) | 1, sb600_spibar + 2);
Carl-Daniel Hailfinger78185dc2009-05-17 15:49:24 +0000214 while (mmio_readb(sb600_spibar + 2) & 1)
Jason Wanga3f04be2008-11-28 21:36:51 +0000215 ;
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000216 msg_pspew("done\n");
Jason Wanga3f04be2008-11-28 21:36:51 +0000217}
218
Edward O'Callaghan5eca4272020-04-12 17:27:53 +1000219static int sb600_spi_send_command(const struct flashctx *flash, unsigned int writecnt,
Carl-Daniel Hailfinger8a3c60c2011-12-18 15:01:24 +0000220 unsigned int readcnt,
221 const unsigned char *writearr,
222 unsigned char *readarr)
Jason Wanga3f04be2008-11-28 21:36:51 +0000223{
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000224 /* First byte is cmd which can not be sent through the FIFO. */
Jason Wanga3f04be2008-11-28 21:36:51 +0000225 unsigned char cmd = *writearr++;
Jason Wanga3f04be2008-11-28 21:36:51 +0000226 writecnt--;
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000227 msg_pspew("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, cmd, writecnt, readcnt);
228 mmio_writeb(cmd, sb600_spibar + 0);
Jason Wanga3f04be2008-11-28 21:36:51 +0000229
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000230 int ret = check_readwritecnt(flash, writecnt, readcnt);
231 if (ret != 0)
232 return ret;
Jason Wanga3f04be2008-11-28 21:36:51 +0000233
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000234 /* This is a workaround for a bug in SPI controller. If we only send
Carl-Daniel Hailfingerf8555e22009-07-23 01:36:08 +0000235 * an opcode and no additional data/address, the SPI controller will
236 * read one byte too few from the chip. Basically, the last byte of
237 * the chip response is discarded and will not end up in the FIFO.
238 * It is unclear if the CS# line is set high too early as well.
239 */
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000240 unsigned int readoffby1 = (writecnt > 0) ? 0 : 1;
241 uint8_t readwrite = (readcnt + readoffby1) << 4 | (writecnt);
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000242 mmio_writeb(readwrite, sb600_spibar + 1);
Jason Wanga3f04be2008-11-28 21:36:51 +0000243
Jason Wanga3f04be2008-11-28 21:36:51 +0000244 reset_internal_fifo_pointer();
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000245 msg_pspew("Filling FIFO: ");
Nico Huber519be662018-12-23 20:03:35 +0100246 unsigned int count;
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000247 for (count = 0; count < writecnt; count++) {
248 msg_pspew("[%02x]", writearr[count]);
249 mmio_writeb(writearr[count], sb600_spibar + 0xC);
Jason Wanga3f04be2008-11-28 21:36:51 +0000250 }
Carl-Daniel Hailfinger643415b2010-01-10 01:59:50 +0000251 msg_pspew("\n");
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000252 if (compare_internal_fifo_pointer(writecnt))
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000253 return SPI_PROGRAMMER_ERROR;
Jason Wanga3f04be2008-11-28 21:36:51 +0000254
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000255 /*
256 * We should send the data in sequence, which means we need to reset
257 * the FIFO pointer to the first byte we want to send.
258 */
259 reset_internal_fifo_pointer();
Jason Wanga3f04be2008-11-28 21:36:51 +0000260 execute_command();
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000261 if (compare_internal_fifo_pointer(writecnt + readcnt))
262 return SPI_PROGRAMMER_ERROR;
Jason Wanga3f04be2008-11-28 21:36:51 +0000263
264 /*
265 * After the command executed, we should find out the index of the
Carl-Daniel Hailfingerf8555e22009-07-23 01:36:08 +0000266 * received byte. Here we just reset the FIFO pointer and skip the
267 * writecnt.
268 * It would be possible to increase the FIFO pointer by one instead
269 * of reading and discarding one byte from the FIFO.
270 * The FIFO is implemented on top of an 8 byte ring buffer and the
271 * buffer is never cleared. For every byte that is shifted out after
272 * the opcode, the FIFO already stores the response from the chip.
273 * Usually, the chip will respond with 0x00 or 0xff.
Jason Wanga3f04be2008-11-28 21:36:51 +0000274 */
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000275 reset_internal_fifo_pointer();
Jason Wanga3f04be2008-11-28 21:36:51 +0000276
Carl-Daniel Hailfingerf8555e22009-07-23 01:36:08 +0000277 /* Skip the bytes we sent. */
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000278 msg_pspew("Skipping: ");
Jason Wanga3f04be2008-11-28 21:36:51 +0000279 for (count = 0; count < writecnt; count++) {
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000280 msg_pspew("[%02x]", mmio_readb(sb600_spibar + 0xC));
Jason Wanga3f04be2008-11-28 21:36:51 +0000281 }
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000282 msg_pspew("\n");
283 if (compare_internal_fifo_pointer(writecnt))
284 return SPI_PROGRAMMER_ERROR;
Jason Wanga3f04be2008-11-28 21:36:51 +0000285
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000286 msg_pspew("Reading FIFO: ");
287 for (count = 0; count < readcnt; count++) {
288 readarr[count] = mmio_readb(sb600_spibar + 0xC);
289 msg_pspew("[%02x]", readarr[count]);
Jason Wanga3f04be2008-11-28 21:36:51 +0000290 }
Carl-Daniel Hailfinger643415b2010-01-10 01:59:50 +0000291 msg_pspew("\n");
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000292 if (compare_internal_fifo_pointer(writecnt+readcnt))
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000293 return SPI_PROGRAMMER_ERROR;
294
295 if (mmio_readb(sb600_spibar + 1) != readwrite) {
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000296 msg_perr("Unexpected change in AMD SPI read/write count!\n");
297 msg_perr("Something else is accessing the flash chip and causes random corruption.\n"
298 "Please stop all applications and drivers and IPMI which access the flash chip.\n");
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000299 return SPI_PROGRAMMER_ERROR;
300 }
Jason Wanga3f04be2008-11-28 21:36:51 +0000301
302 return 0;
303}
Carl-Daniel Hailfingercceafa22010-05-26 01:45:41 +0000304
Edward O'Callaghan5eca4272020-04-12 17:27:53 +1000305static int spi100_spi_send_command(const struct flashctx *flash, unsigned int writecnt,
Wei Hu31402ee2014-05-16 21:39:33 +0000306 unsigned int readcnt,
307 const unsigned char *writearr,
308 unsigned char *readarr)
309{
310 /* First byte is cmd which can not be sent through the buffer. */
311 unsigned char cmd = *writearr++;
312 writecnt--;
313 msg_pspew("%s, cmd=0x%02x, writecnt=%d, readcnt=%d\n", __func__, cmd, writecnt, readcnt);
314 mmio_writeb(cmd, sb600_spibar + 0);
315
316 int ret = check_readwritecnt(flash, writecnt, readcnt);
317 if (ret != 0)
318 return ret;
319
320 /* Use the extended TxByteCount and RxByteCount registers. */
321 mmio_writeb(writecnt, sb600_spibar + 0x48);
322 mmio_writeb(readcnt, sb600_spibar + 0x4b);
323
324 msg_pspew("Filling buffer: ");
Nico Huber519be662018-12-23 20:03:35 +0100325 unsigned int count;
Wei Hu31402ee2014-05-16 21:39:33 +0000326 for (count = 0; count < writecnt; count++) {
327 msg_pspew("[%02x]", writearr[count]);
328 mmio_writeb(writearr[count], sb600_spibar + 0x80 + count);
329 }
330 msg_pspew("\n");
331
332 execute_command();
333
334 msg_pspew("Reading buffer: ");
335 for (count = 0; count < readcnt; count++) {
336 readarr[count] = mmio_readb(sb600_spibar + 0x80 + (writecnt + count) % FIFO_SIZE_YANGTZE);
337 msg_pspew("[%02x]", readarr[count]);
338 }
339 msg_pspew("\n");
340
341 return 0;
342}
343
Stefan Taunera6a0d202013-09-15 14:17:39 +0000344struct spispeed {
345 const char *const name;
346 const uint8_t speed;
347};
348
349static const struct spispeed spispeeds[] = {
350 { "66 MHz", 0x00 },
351 { "33 MHz", 0x01 },
352 { "22 MHz", 0x02 },
353 { "16.5 MHz", 0x03 },
Wei Hu31402ee2014-05-16 21:39:33 +0000354 { "100 MHz", 0x04 },
355 { "Reserved", 0x05 },
356 { "Reserved", 0x06 },
357 { "800 kHz", 0x07 },
Stefan Taunera6a0d202013-09-15 14:17:39 +0000358};
359
360static int set_speed(struct pci_dev *dev, const struct spispeed *spispeed)
361{
362 bool success = false;
363 uint8_t speed = spispeed->speed;
364
365 msg_pdbg("Setting SPI clock to %s (0x%x).\n", spispeed->name, speed);
Wei Hu31402ee2014-05-16 21:39:33 +0000366 if (amd_gen >= CHIPSET_YANGTZE) {
367 rmmio_writew((speed << 12) | (speed << 8) | (speed << 4) | speed, sb600_spibar + 0x22);
368 uint16_t tmp = mmio_readw(sb600_spibar + 0x22);
369 success = (((tmp >> 12) & 0xf) == speed && ((tmp >> 8) & 0xf) == speed &&
370 ((tmp >> 4) & 0xf) == speed && ((tmp >> 0) & 0xf) == speed);
371 } else {
Stefan Taunera6a0d202013-09-15 14:17:39 +0000372 rmmio_writeb((mmio_readb(sb600_spibar + 0xd) & ~(0x3 << 4)) | (speed << 4), sb600_spibar + 0xd);
373 success = (speed == ((mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3));
374 }
375
376 if (!success) {
377 msg_perr("Setting SPI clock failed.\n");
378 return 1;
379 }
380 return 0;
381}
382
Wei Hu31402ee2014-05-16 21:39:33 +0000383static int set_mode(struct pci_dev *dev, uint8_t read_mode)
384{
385 uint32_t tmp = mmio_readl(sb600_spibar + 0x00);
386 tmp &= ~(0x6 << 28 | 0x1 << 18); /* Clear mode bits */
387 tmp |= ((read_mode & 0x6) << 28) | ((read_mode & 0x1) << 18);
388 rmmio_writel(tmp, sb600_spibar + 0x00);
389 if (tmp != mmio_readl(sb600_spibar + 0x00))
390 return 1;
391 return 0;
392}
393
Stefan Taunera6a0d202013-09-15 14:17:39 +0000394static int handle_speed(struct pci_dev *dev)
395{
396 uint32_t tmp;
Carl-Daniel Hailfinger57cdd6b2016-03-12 19:49:14 +0000397 uint8_t spispeed_idx = 3; /* Default to 16.5 MHz */
Stefan Taunera6a0d202013-09-15 14:17:39 +0000398
Stefan Tauner21071b02014-05-16 21:39:48 +0000399 char *spispeed = extract_programmer_param("spispeed");
400 if (spispeed != NULL) {
Carl-Daniel Hailfinger57cdd6b2016-03-12 19:49:14 +0000401 unsigned int i;
402 for (i = 0; i < ARRAY_SIZE(spispeeds); i++) {
403 if (strcasecmp(spispeeds[i].name, spispeed) == 0) {
404 spispeed_idx = i;
405 break;
Stefan Tauner21071b02014-05-16 21:39:48 +0000406 }
Stefan Tauner21071b02014-05-16 21:39:48 +0000407 }
Carl-Daniel Hailfinger57cdd6b2016-03-12 19:49:14 +0000408 /* "reserved" is not a valid speed.
409 * Error out on speeds not present in the spispeeds array.
410 * Only Yangtze supports the second half of indices.
411 * No 66 MHz before SB8xx. */
412 if ((strcasecmp(spispeed, "reserved") == 0) ||
413 (i == ARRAY_SIZE(spispeeds)) ||
414 (amd_gen < CHIPSET_YANGTZE && spispeed_idx > 3) ||
415 (amd_gen < CHIPSET_SB89XX && spispeed_idx == 0)) {
Stefan Tauner21071b02014-05-16 21:39:48 +0000416 msg_perr("Error: Invalid spispeed value: '%s'.\n", spispeed);
417 free(spispeed);
418 return 1;
419 }
420 free(spispeed);
421 }
422
Stefan Taunera6a0d202013-09-15 14:17:39 +0000423 /* See the chipset support matrix for SPI Base_Addr below for an explanation of the symbols used.
Martin Roth82b6ec12014-07-15 13:50:58 +0000424 * bit 6xx 7xx/SP5100 8xx 9xx hudson1 hudson234 bolton/yangtze
Stefan Taunera6a0d202013-09-15 14:17:39 +0000425 * 18 rsvd <- fastReadEnable ? <- ? SpiReadMode[0]
426 * 29:30 rsvd <- <- ? <- ? SpiReadMode[2:1]
427 */
Martin Roth82b6ec12014-07-15 13:50:58 +0000428 if (amd_gen >= CHIPSET_BOLTON) {
Wei Hu31402ee2014-05-16 21:39:33 +0000429 static const char *spireadmodes[] = {
430 "Normal (up to 33 MHz)", /* 0 */
431 "Reserved", /* 1 */
432 "Dual IO (1-1-2)", /* 2 */
433 "Quad IO (1-1-4)", /* 3 */
434 "Dual IO (1-2-2)", /* 4 */
435 "Quad IO (1-4-4)", /* 5 */
436 "Normal (up to 66 MHz)", /* 6 */
Martin Roth82b6ec12014-07-15 13:50:58 +0000437 "Fast Read", /* 7 (Not defined in the Bolton datasheet.) */
Wei Hu31402ee2014-05-16 21:39:33 +0000438 };
439 tmp = mmio_readl(sb600_spibar + 0x00);
440 uint8_t read_mode = ((tmp >> 28) & 0x6) | ((tmp >> 18) & 0x1);
441 msg_pdbg("SpiReadMode=%s (%i)\n", spireadmodes[read_mode], read_mode);
442 if (read_mode != 6) {
443 read_mode = 6; /* Default to "Normal (up to 66 MHz)" */
444 if (set_mode(dev, read_mode) != 0) {
445 msg_perr("Setting read mode to \"%s\" failed.\n", spireadmodes[read_mode]);
446 return 1;
447 }
448 msg_pdbg("Setting read mode to \"%s\" succeeded.\n", spireadmodes[read_mode]);
449 }
450
Martin Roth82b6ec12014-07-15 13:50:58 +0000451 if (amd_gen >= CHIPSET_YANGTZE) {
452 tmp = mmio_readb(sb600_spibar + 0x20);
453 msg_pdbg("UseSpi100 is %sabled\n", (tmp & 0x1) ? "en" : "dis");
454 if ((tmp & 0x1) == 0) {
455 rmmio_writeb(tmp | 0x1, sb600_spibar + 0x20);
456 tmp = mmio_readb(sb600_spibar + 0x20) & 0x1;
457 if (tmp == 0) {
458 msg_perr("Enabling Spi100 failed.\n");
459 return 1;
460 }
461 msg_pdbg("Enabling Spi100 succeeded.\n");
462 }
463
464 tmp = mmio_readw(sb600_spibar + 0x22); /* SPI 100 Speed Config */
465 msg_pdbg("NormSpeedNew is %s\n", spispeeds[(tmp >> 12) & 0xf].name);
466 msg_pdbg("FastSpeedNew is %s\n", spispeeds[(tmp >> 8) & 0xf].name);
467 msg_pdbg("AltSpeedNew is %s\n", spispeeds[(tmp >> 4) & 0xf].name);
468 msg_pdbg("TpmSpeedNew is %s\n", spispeeds[(tmp >> 0) & 0xf].name);
469 }
Wei Hu31402ee2014-05-16 21:39:33 +0000470 } else {
Stefan Taunera6a0d202013-09-15 14:17:39 +0000471 if (amd_gen >= CHIPSET_SB89XX && amd_gen <= CHIPSET_HUDSON234) {
472 bool fast_read = (mmio_readl(sb600_spibar + 0x00) >> 18) & 0x1;
473 msg_pdbg("Fast Reads are %sabled\n", fast_read ? "en" : "dis");
474 if (fast_read) {
475 msg_pdbg("Disabling them temporarily.\n");
476 rmmio_writel(mmio_readl(sb600_spibar + 0x00) & ~(0x1 << 18),
477 sb600_spibar + 0x00);
478 }
479 }
480 tmp = (mmio_readb(sb600_spibar + 0xd) >> 4) & 0x3;
481 msg_pdbg("NormSpeed is %s\n", spispeeds[tmp].name);
482 }
483 return set_speed(dev, &spispeeds[spispeed_idx]);
484}
485
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000486static int handle_imc(struct pci_dev *dev)
Rudolf Marek70e14592013-07-25 22:58:56 +0000487{
488 /* Handle IMC everywhere but sb600 which does not have one. */
Stefan Tauner4442b812013-09-12 15:48:35 +0000489 if (amd_gen == CHIPSET_SB6XX)
Rudolf Marek70e14592013-07-25 22:58:56 +0000490 return 0;
491
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000492 bool amd_imc_force = false;
493 char *arg = extract_programmer_param("amd_imc_force");
494 if (arg && !strcmp(arg, "yes")) {
495 amd_imc_force = true;
496 msg_pspew("amd_imc_force enabled.\n");
497 } else if (arg && !strlen(arg)) {
498 msg_perr("Missing argument for amd_imc_force.\n");
499 free(arg);
500 return 1;
501 } else if (arg) {
502 msg_perr("Unknown argument for amd_imc_force: \"%s\" (not \"yes\").\n", arg);
503 free(arg);
504 return 1;
505 }
506 free(arg);
507
Rudolf Marek70e14592013-07-25 22:58:56 +0000508 /* TODO: we should not only look at IntegratedImcPresent (LPC Dev 20, Func 3, 40h) but also at
Stefan Tauner5c316f92015-02-08 21:57:52 +0000509 * IMCEnable(Strap) and Override EcEnable(Strap) (sb8xx, sb9xx?, a50, Bolton: Misc_Reg: 80h-87h;
Rudolf Marek70e14592013-07-25 22:58:56 +0000510 * sb7xx, sp5100: PM_Reg: B0h-B1h) etc. */
511 uint8_t reg = pci_read_byte(dev, 0x40);
512 if ((reg & (1 << 7)) == 0) {
513 msg_pdbg("IMC is not active.\n");
514 return 0;
515 }
516
517 if (!amd_imc_force)
Felix Singer980d6b82022-08-19 02:48:15 +0200518 programmer_may_write = false;
Stefan Tauner463dd692013-08-08 12:00:19 +0000519 msg_pinfo("Writes have been disabled for safety reasons because the presence of the IMC\n"
520 "was detected and it could interfere with accessing flash memory. Flashrom will\n"
521 "try to disable it temporarily but even then this might not be safe:\n"
Stefan Tauner0be072c2016-03-13 15:16:30 +0000522 "when it is re-enabled and after a reboot it expects to find working code\n"
Rudolf Marek70e14592013-07-25 22:58:56 +0000523 "in the flash and it is unpredictable what happens if there is none.\n"
524 "\n"
525 "To be safe make sure that there is a working IMC firmware at the right\n"
526 "location in the image you intend to write and do not attempt to erase.\n"
527 "\n"
528 "You can enforce write support with the amd_imc_force programmer option.\n");
529 if (amd_imc_force)
530 msg_pinfo("Continuing with write support because the user forced us to!\n");
531
532 return amd_imc_shutdown(dev);
533}
534
Michael Karcherb05b9e12010-07-22 18:04:19 +0000535int sb600_probe_spi(struct pci_dev *dev)
536{
537 struct pci_dev *smbus_dev;
538 uint32_t tmp;
539 uint8_t reg;
Rudolf Marek70e14592013-07-25 22:58:56 +0000540
Michael Karcherb05b9e12010-07-22 18:04:19 +0000541 /* Read SPI_BaseAddr */
542 tmp = pci_read_long(dev, 0xa0);
543 tmp &= 0xffffffe0; /* remove bits 4-0 (reserved) */
544 msg_pdbg("SPI base address is at 0x%x\n", tmp);
545
546 /* If the BAR has address 0, it is unlikely SPI is used. */
547 if (!tmp)
548 return 0;
549
550 /* Physical memory has to be mapped at page (4k) boundaries. */
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000551 sb600_spibar = rphysmap("SB600 SPI registers", tmp & 0xfffff000, 0x1000);
552 if (sb600_spibar == ERROR_PTR)
Niklas Söderlund5d307202013-09-14 09:02:27 +0000553 return ERROR_FATAL;
Stefan Tauner7fb5aa02013-08-14 15:48:44 +0000554
Michael Karcherb05b9e12010-07-22 18:04:19 +0000555 /* The low bits of the SPI base address are used as offset into
556 * the mapped page.
557 */
558 sb600_spibar += tmp & 0xfff;
559
Edward O'Callaghan9355e6f2019-10-29 18:18:18 +1100560 if (determine_generation(dev) < 0)
Stefan Tauner4442b812013-09-12 15:48:35 +0000561 return ERROR_NONFATAL;
Stefan Tauner463dd692013-08-08 12:00:19 +0000562
Stefan Tauner4442b812013-09-12 15:48:35 +0000563 /* How to read the following table and similar ones in this file:
564 * "?" means we have no datasheet for this chipset generation or it doesn't have any relevant info.
565 * "<-" means the bit/register meaning is identical to the next non-"?" chipset to the left. "<-" thus
566 * never refers to another "?".
567 * If a "?" chipset is between two chipsets with identical meaning, we assume the meaning didn't change
568 * twice in between, i.e. the meaning is unchanged for the "?" chipset. Usually we assume that
569 * succeeding hardware supports the same functionality as its predecessor unless proven different by
570 * tests or documentation, hence "?" will often be implemented equally to "<-".
571 *
572 * Chipset support matrix for SPI Base_Addr (LPC PCI reg 0xa0)
573 * bit 6xx 7xx/SP5100 8xx 9xx hudson1 hudson2+ yangtze
574 * 3 rsvd <- <- ? <- ? RouteTpm2Spi
575 * 2 rsvd AbortEnable rsvd ? <- ? <-
576 * 1 rsvd SpiRomEnable <- ? <- ? <-
577 * 0 rsvd AltSpiCSEnable rsvd ? <- ? <-
578 */
579 if (amd_gen >= CHIPSET_SB7XX) {
580 tmp = pci_read_long(dev, 0xa0);
581 msg_pdbg("SpiRomEnable=%i", (tmp >> 1) & 0x1);
582 if (amd_gen == CHIPSET_SB7XX)
583 msg_pdbg(", AltSpiCSEnable=%i, AbortEnable=%i", tmp & 0x1, (tmp >> 2) & 0x1);
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100584 else if (amd_gen >= CHIPSET_YANGTZE)
Wei Hu31402ee2014-05-16 21:39:33 +0000585 msg_pdbg(", RouteTpm2Sp=%i", (tmp >> 3) & 0x1);
Michael Karcherb05b9e12010-07-22 18:04:19 +0000586
Stefan Tauner4442b812013-09-12 15:48:35 +0000587 tmp = pci_read_byte(dev, 0xba);
588 msg_pdbg(", PrefetchEnSPIFromIMC=%i", (tmp & 0x4) >> 2);
589
590 tmp = pci_read_byte(dev, 0xbb);
591 /* FIXME: Set bit 3,6,7 if not already set.
592 * Set bit 5, otherwise SPI accesses are pointless in LPC mode.
593 * See doc 42413 AMD SB700/710/750 RPR.
594 */
595 if (amd_gen == CHIPSET_SB7XX)
596 msg_pdbg(", SpiOpEnInLpcMode=%i", (tmp >> 5) & 0x1);
597 msg_pdbg(", PrefetchEnSPIFromHost=%i\n", tmp & 0x1);
598 }
599
600 /* Chipset support matrix for SPI_Cntrl0 (spibar + 0x0)
601 * See the chipset support matrix for SPI Base_Addr above for an explanation of the symbols used.
602 * bit 6xx 7xx/SP5100 8xx 9xx hudson1 hudson2+ yangtze
603 * 17 rsvd <- <- ? <- ? <-
Stefan Taunera6a0d202013-09-15 14:17:39 +0000604 * 18 rsvd <- fastReadEnable<1> ? <- ? SpiReadMode[0]<1>
Stefan Tauner4442b812013-09-12 15:48:35 +0000605 * 19 SpiArbEnable <- <- ? <- ? <-
606 * 20 (FifoPtrClr) <- <- ? <- ? <-
607 * 21 (FifoPtrInc) <- <- ? <- ? IllegalAccess
608 * 22 SpiAccessMacRomEn <- <- ? <- ? <-
609 * 23 SpiHostAccessRomEn <- <- ? <- ? <-
610 * 24:26 ArbWaitCount <- <- ? <- ? <-
611 * 27 SpiBridgeDisable <- <- ? <- ? rsvd
612 * 28 rsvd DropOneClkOnRd = SPIClkGate ? <- ? <-
Stefan Taunera6a0d202013-09-15 14:17:39 +0000613 * 29:30 rsvd <- <- ? <- ? SpiReadMode[2:1]<1>
Stefan Tauner4442b812013-09-12 15:48:35 +0000614 * 31 rsvd <- SpiBusy ? <- ? <-
Stefan Taunera6a0d202013-09-15 14:17:39 +0000615 *
616 * <1> see handle_speed
Carl-Daniel Hailfingereb0e7fc2010-08-18 15:12:43 +0000617 */
Stefan Tauner4442b812013-09-12 15:48:35 +0000618 tmp = mmio_readl(sb600_spibar + 0x00);
619 msg_pdbg("(0x%08" PRIx32 ") SpiArbEnable=%i", tmp, (tmp >> 19) & 0x1);
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100620 if (amd_gen >= CHIPSET_YANGTZE)
Wei Hu31402ee2014-05-16 21:39:33 +0000621 msg_pdbg(", IllegalAccess=%i", (tmp >> 21) & 0x1);
Stefan Tauner4442b812013-09-12 15:48:35 +0000622
623 msg_pdbg(", SpiAccessMacRomEn=%i, SpiHostAccessRomEn=%i, ArbWaitCount=%i",
624 (tmp >> 22) & 0x1, (tmp >> 23) & 0x1, (tmp >> 24) & 0x7);
625
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100626 if (amd_gen < CHIPSET_YANGTZE)
Stefan Tauner4442b812013-09-12 15:48:35 +0000627 msg_pdbg(", SpiBridgeDisable=%i", (tmp >> 27) & 0x1);
628
629 switch (amd_gen) {
630 case CHIPSET_SB7XX:
631 msg_pdbg(", DropOneClkOnRd/SpiClkGate=%i", (tmp >> 28) & 0x1);
Richard Hughesdb7482b2018-12-19 12:04:30 +0000632 /* Fall through. */
Stefan Tauner4442b812013-09-12 15:48:35 +0000633 case CHIPSET_SB89XX:
634 case CHIPSET_HUDSON234:
Wei Hu31402ee2014-05-16 21:39:33 +0000635 case CHIPSET_YANGTZE:
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100636 case CHIPSET_PROMONTORY:
Stefan Tauner4442b812013-09-12 15:48:35 +0000637 msg_pdbg(", SpiBusy=%i", (tmp >> 31) & 0x1);
638 default: break;
639 }
640 msg_pdbg("\n");
641
642 if (((tmp >> 22) & 0x1) == 0 || ((tmp >> 23) & 0x1) == 0) {
643 msg_perr("ERROR: State of SpiAccessMacRomEn or SpiHostAccessRomEn prohibits full access.\n");
644 return ERROR_NONFATAL;
645 }
646
Stefan Tauner4442b812013-09-12 15:48:35 +0000647 if (amd_gen >= CHIPSET_SB89XX) {
648 tmp = mmio_readb(sb600_spibar + 0x1D);
649 msg_pdbg("Using SPI_CS%d\n", tmp & 0x3);
Wei Hu31402ee2014-05-16 21:39:33 +0000650 /* FIXME: Handle SpiProtect* configuration on Yangtze. */
Stefan Tauner4442b812013-09-12 15:48:35 +0000651 }
652
Michael Karcherb05b9e12010-07-22 18:04:19 +0000653 /* Look for the SMBus device. */
Edward O'Callaghan19ce50d2021-11-13 17:59:18 +1100654 smbus_dev = pcidev_find(0x1002, 0x4385);
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100655 if (!smbus_dev)
Edward O'Callaghan19ce50d2021-11-13 17:59:18 +1100656 smbus_dev = pcidev_find(0x1022, 0x780b); /* AMD FCH */
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100657 if (!smbus_dev)
Edward O'Callaghan19ce50d2021-11-13 17:59:18 +1100658 smbus_dev = pcidev_find(0x1022, 0x790b); /* AMD FP4 */
Ricardo Ribalda Delgado7b629bc2017-03-22 14:08:31 +0100659 if (!smbus_dev) {
660 msg_perr("ERROR: SMBus device not found. Not enabling SPI.\n");
661 return ERROR_NONFATAL;
Michael Karcherb05b9e12010-07-22 18:04:19 +0000662 }
663
664 /* Note about the bit tests below: If a bit is zero, the GPIO is SPI. */
665 /* GPIO11/SPI_DO and GPIO12/SPI_DI status */
666 reg = pci_read_byte(smbus_dev, 0xAB);
667 reg &= 0xC0;
668 msg_pdbg("GPIO11 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_DO");
669 msg_pdbg("GPIO12 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_DI");
670 if (reg != 0x00) {
671 msg_pdbg("Not enabling SPI");
672 return 0;
673 }
674 /* GPIO31/SPI_HOLD and GPIO32/SPI_CS status */
675 reg = pci_read_byte(smbus_dev, 0x83);
676 reg &= 0xC0;
677 msg_pdbg("GPIO31 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_HOLD");
678 msg_pdbg("GPIO32 used for %s\n", (reg & (1 << 7)) ? "GPIO" : "SPI_CS");
679 /* SPI_HOLD is not used on all boards, filter it out. */
680 if ((reg & 0x80) != 0x00) {
681 msg_pdbg("Not enabling SPI");
682 return 0;
683 }
684 /* GPIO47/SPI_CLK status */
685 reg = pci_read_byte(smbus_dev, 0xA7);
686 reg &= 0x40;
687 msg_pdbg("GPIO47 used for %s\n", (reg & (1 << 6)) ? "GPIO" : "SPI_CLK");
688 if (reg != 0x00) {
689 msg_pdbg("Not enabling SPI");
690 return 0;
691 }
692
Stefan Taunera6a0d202013-09-15 14:17:39 +0000693 if (handle_speed(dev) != 0)
694 return ERROR_FATAL;
695
Stefan Taunerd5b2aef2014-05-16 21:39:28 +0000696 if (handle_imc(dev) != 0)
Rudolf Marek70e14592013-07-25 22:58:56 +0000697 return ERROR_FATAL;
Carl-Daniel Hailfinger39446e32010-09-15 12:02:07 +0000698
Wei Hu31402ee2014-05-16 21:39:33 +0000699 /* Starting with Yangtze the SPI controller got a different interface with a much bigger buffer. */
Edward O'Callaghan93737bc2019-10-29 18:30:01 +1100700 if (amd_gen < CHIPSET_YANGTZE)
Nico Huber5e08e3e2021-05-11 17:38:14 +0200701 register_spi_master(&spi_master_sb600, NULL);
Wei Hu31402ee2014-05-16 21:39:33 +0000702 else
Nico Huber5e08e3e2021-05-11 17:38:14 +0200703 register_spi_master(&spi_master_yangtze, NULL);
Michael Karcherb05b9e12010-07-22 18:04:19 +0000704 return 0;
705}