blob: 986f094a77f0fe474b6daf71d9b7e453d6d64111 [file] [log] [blame]
Angel Pons182dbde2020-04-02 23:49:05 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Elyes HAOUAS24230f62020-07-27 07:56:14 +02002
Zheng Bao7bcffa52012-11-28 11:36:52 +08003#include <stdint.h>
Kyösti Mälkki13f66502019-03-03 08:01:05 +02004#include <device/mmio.h>
Kyösti Mälkki9f0a2be2014-06-30 07:34:36 +03005#include <console/console.h>
Furquan Shaikhc28984d2016-11-20 21:04:00 -08006#include <spi_flash.h>
Zheng Bao600784e2013-02-07 17:30:23 +08007#include <spi-generic.h>
zbao246e84b2012-07-13 18:47:03 +08008#include <device/device.h>
Zheng Bao7bcffa52012-11-28 11:36:52 +08009#include <device/pci.h>
10#include <device/pci_ops.h>
Elyes HAOUAS24230f62020-07-27 07:56:14 +020011#include <stddef.h>
zbao246e84b2012-07-13 18:47:03 +080012
Alexandru Gagniuc01e0adf2014-03-29 17:07:26 -050013#include <Proc/Fch/FchPlatform.h>
Martin Roth3316cf22012-12-05 16:22:54 -070014
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050015#define SPI_REG_OPCODE 0x0
16#define SPI_REG_CNTRL01 0x1
17#define SPI_REG_CNTRL02 0x2
18 #define CNTRL02_FIFO_RESET (1 << 4)
19 #define CNTRL02_EXEC_OPCODE (1 << 0)
20#define SPI_REG_CNTRL03 0x3
21 #define CNTRL03_SPIBUSY (1 << 7)
22#define SPI_REG_FIFO 0xc
23#define SPI_REG_CNTRL11 0xd
24 #define CNTRL11_FIFOPTR_MASK 0x07
25
Julius Wernercd49cce2019-03-05 16:53:33 -080026#if CONFIG(SOUTHBRIDGE_AMD_AGESA_YANGTZE)
Kyösti Mälkki11104952014-06-29 16:17:33 +030027#define AMD_SB_SPI_TX_LEN 64
Kyösti Mälkki2fa8cc32014-07-15 02:30:49 +030028#else
29#define AMD_SB_SPI_TX_LEN 8
30#endif
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050031
Stefan Reinauerfce128c2015-07-21 12:52:34 -070032static uintptr_t spibar;
Zheng Bao7bcffa52012-11-28 11:36:52 +080033
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050034static inline uint8_t spi_read(uint8_t reg)
35{
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080036 return read8((void *)(spibar + reg));
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050037}
38
39static inline void spi_write(uint8_t reg, uint8_t val)
40{
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080041 write8((void *)(spibar + reg), val);
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050042}
43
Zheng Bao7bcffa52012-11-28 11:36:52 +080044static void reset_internal_fifo_pointer(void)
zbao246e84b2012-07-13 18:47:03 +080045{
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050046 uint8_t reg8;
47
zbao246e84b2012-07-13 18:47:03 +080048 do {
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050049 reg8 = spi_read(SPI_REG_CNTRL02);
50 reg8 |= CNTRL02_FIFO_RESET;
51 spi_write(SPI_REG_CNTRL02, reg8);
52 } while (spi_read(SPI_REG_CNTRL11) & CNTRL11_FIFOPTR_MASK);
zbao246e84b2012-07-13 18:47:03 +080053}
54
Zheng Bao7bcffa52012-11-28 11:36:52 +080055static void execute_command(void)
zbao246e84b2012-07-13 18:47:03 +080056{
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050057 uint8_t reg8;
Zheng Bao7bcffa52012-11-28 11:36:52 +080058
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050059 reg8 = spi_read(SPI_REG_CNTRL02);
60 reg8 |= CNTRL02_EXEC_OPCODE;
61 spi_write(SPI_REG_CNTRL02, reg8);
62
63 while ((spi_read(SPI_REG_CNTRL02) & CNTRL02_EXEC_OPCODE) &&
64 (spi_read(SPI_REG_CNTRL03) & CNTRL03_SPIBUSY));
zbao246e84b2012-07-13 18:47:03 +080065}
66
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050067void spi_init(void)
zbao246e84b2012-07-13 18:47:03 +080068{
Elyes HAOUASa93e7542018-05-19 14:30:47 +020069 struct device *dev;
Zheng Bao7bcffa52012-11-28 11:36:52 +080070
Kyösti Mälkki4ad7f5b2018-05-22 01:15:17 +030071 dev = pcidev_on_root(0x14, 3);
Zheng Bao7bcffa52012-11-28 11:36:52 +080072 spibar = pci_read_config32(dev, 0xA0) & ~0x1F;
zbao246e84b2012-07-13 18:47:03 +080073}
74
Furquan Shaikh94f86992016-12-01 07:12:32 -080075static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout,
Furquan Shaikh0dba0252016-11-30 04:34:22 -080076 size_t bytesout, void *din, size_t bytesin)
zbao246e84b2012-07-13 18:47:03 +080077{
Paul Menzeldee0f882018-11-10 11:24:38 +010078 /* First byte is cmd which can not be sent through FIFO. */
Gabe Black93d9f922014-03-27 21:52:43 -070079 u8 cmd = *(u8 *)dout++;
80 u8 readoffby1;
Furquan Shaikh0dba0252016-11-30 04:34:22 -080081 size_t count;
zbao246e84b2012-07-13 18:47:03 +080082
Gabe Black93d9f922014-03-27 21:52:43 -070083 bytesout--;
Kyösti Mälkki9f0a2be2014-06-30 07:34:36 +030084
85 /*
86 * Check if this is a write command attempting to transfer more bytes
87 * than the controller can handle. Iterations for writes are not
88 * supported here because each SPI write command needs to be preceded
89 * and followed by other SPI commands, and this sequence is controlled
90 * by the SPI chip driver.
91 */
92 if (bytesout > AMD_SB_SPI_TX_LEN) {
93 printk(BIOS_DEBUG, "FCH SPI: Too much to write. Does your SPI chip driver use"
94 " spi_crop_chunk()?\n");
95 return -1;
96 }
97
Zheng Bao7bcffa52012-11-28 11:36:52 +080098 readoffby1 = bytesout ? 0 : 1;
zbao246e84b2012-07-13 18:47:03 +080099
Arthur Heymansbbf5de52022-03-23 22:41:05 +0100100 if (CONFIG(SOUTHBRIDGE_AMD_AGESA_YANGTZE)) {
101 spi_write(0x1E, 5);
102 spi_write(0x1F, bytesout); /* SpiExtRegIndx [5] - TxByteCount */
103 spi_write(0x1E, 6);
104 spi_write(0x1F, bytesin); /* SpiExtRegIndx [6] - RxByteCount */
105 } else {
106 u8 readwrite = (bytesin + readoffby1) << 4 | bytesout;
107 spi_write(SPI_REG_CNTRL01, readwrite);
108 }
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500109 spi_write(SPI_REG_OPCODE, cmd);
zbao246e84b2012-07-13 18:47:03 +0800110
Zheng Bao7bcffa52012-11-28 11:36:52 +0800111 reset_internal_fifo_pointer();
112 for (count = 0; count < bytesout; count++, dout++) {
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500113 spi_write(SPI_REG_FIFO, *(uint8_t *)dout);
zbao246e84b2012-07-13 18:47:03 +0800114 }
Zheng Bao7bcffa52012-11-28 11:36:52 +0800115
116 reset_internal_fifo_pointer();
117 execute_command();
118
119 reset_internal_fifo_pointer();
120 /* Skip the bytes we sent. */
121 for (count = 0; count < bytesout; count++) {
Paul Menzel9eb4d0a2018-11-10 11:27:02 +0100122 spi_read(SPI_REG_FIFO);
Zheng Bao7bcffa52012-11-28 11:36:52 +0800123 }
124
Zheng Bao7bcffa52012-11-28 11:36:52 +0800125 for (count = 0; count < bytesin; count++, din++) {
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500126 *(uint8_t *)din = spi_read(SPI_REG_FIFO);
Zheng Bao7bcffa52012-11-28 11:36:52 +0800127 }
128
129 return 0;
130}
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800131
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800132int chipset_volatile_group_begin(const struct spi_flash *flash)
133{
Julius Wernercd49cce2019-03-05 16:53:33 -0800134 if (!CONFIG(HUDSON_IMC_FWM))
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800135 return 0;
136
137 ImcSleep(NULL);
138 return 0;
139}
140
141int chipset_volatile_group_end(const struct spi_flash *flash)
142{
Julius Wernercd49cce2019-03-05 16:53:33 -0800143 if (!CONFIG(HUDSON_IMC_FWM))
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800144 return 0;
145
146 ImcWakeup(NULL);
147 return 0;
zbao246e84b2012-07-13 18:47:03 +0800148}
149
Aaron Durbin851dde82018-04-19 21:15:25 -0600150static int xfer_vectors(const struct spi_slave *slave,
151 struct spi_op vectors[], size_t count)
152{
153 return spi_flash_vector_helper(slave, vectors, count, spi_ctrlr_xfer);
154}
155
Furquan Shaikh94f86992016-12-01 07:12:32 -0800156static const struct spi_ctrlr spi_ctrlr = {
Aaron Durbin851dde82018-04-19 21:15:25 -0600157 .xfer_vector = xfer_vectors,
Furquan Shaikhde705fa2017-04-19 19:27:28 -0700158 .max_xfer_size = AMD_SB_SPI_TX_LEN,
Aaron Durbin1fcc9f32018-01-29 11:30:17 -0700159 .flags = SPI_CNTRLR_DEDUCT_CMD_LEN,
Furquan Shaikh94f86992016-12-01 07:12:32 -0800160};
161
Furquan Shaikh12eca762017-05-18 14:58:49 -0700162const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = {
163 {
164 .ctrlr = &spi_ctrlr,
165 .bus_start = 0,
166 .bus_end = 0,
167 },
168};
169
170const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map);