blob: f6625b944d4e766cc4393423c5e24c96f734048b [file] [log] [blame]
Angel Pons182dbde2020-04-02 23:49:05 +02001/* SPDX-License-Identifier: GPL-2.0-only */
2/* This file is part of the coreboot project. */
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>
zbao246e84b2012-07-13 18:47:03 +080011
Alexandru Gagniuc01e0adf2014-03-29 17:07:26 -050012#include <Proc/Fch/FchPlatform.h>
Martin Roth3316cf22012-12-05 16:22:54 -070013
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050014#define SPI_REG_OPCODE 0x0
15#define SPI_REG_CNTRL01 0x1
16#define SPI_REG_CNTRL02 0x2
17 #define CNTRL02_FIFO_RESET (1 << 4)
18 #define CNTRL02_EXEC_OPCODE (1 << 0)
19#define SPI_REG_CNTRL03 0x3
20 #define CNTRL03_SPIBUSY (1 << 7)
21#define SPI_REG_FIFO 0xc
22#define SPI_REG_CNTRL11 0xd
23 #define CNTRL11_FIFOPTR_MASK 0x07
24
Julius Wernercd49cce2019-03-05 16:53:33 -080025#if CONFIG(SOUTHBRIDGE_AMD_AGESA_YANGTZE)
Kyösti Mälkki11104952014-06-29 16:17:33 +030026#define AMD_SB_SPI_TX_LEN 64
Kyösti Mälkki2fa8cc32014-07-15 02:30:49 +030027#else
28#define AMD_SB_SPI_TX_LEN 8
29#endif
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050030
Stefan Reinauerfce128c2015-07-21 12:52:34 -070031static uintptr_t spibar;
Zheng Bao7bcffa52012-11-28 11:36:52 +080032
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050033static inline uint8_t spi_read(uint8_t reg)
34{
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080035 return read8((void *)(spibar + reg));
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050036}
37
38static inline void spi_write(uint8_t reg, uint8_t val)
39{
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080040 write8((void *)(spibar + reg), val);
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050041}
42
Zheng Bao7bcffa52012-11-28 11:36:52 +080043static void reset_internal_fifo_pointer(void)
zbao246e84b2012-07-13 18:47:03 +080044{
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050045 uint8_t reg8;
46
zbao246e84b2012-07-13 18:47:03 +080047 do {
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050048 reg8 = spi_read(SPI_REG_CNTRL02);
49 reg8 |= CNTRL02_FIFO_RESET;
50 spi_write(SPI_REG_CNTRL02, reg8);
51 } while (spi_read(SPI_REG_CNTRL11) & CNTRL11_FIFOPTR_MASK);
zbao246e84b2012-07-13 18:47:03 +080052}
53
Zheng Bao7bcffa52012-11-28 11:36:52 +080054static void execute_command(void)
zbao246e84b2012-07-13 18:47:03 +080055{
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050056 uint8_t reg8;
Zheng Bao7bcffa52012-11-28 11:36:52 +080057
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050058 reg8 = spi_read(SPI_REG_CNTRL02);
59 reg8 |= CNTRL02_EXEC_OPCODE;
60 spi_write(SPI_REG_CNTRL02, reg8);
61
62 while ((spi_read(SPI_REG_CNTRL02) & CNTRL02_EXEC_OPCODE) &&
63 (spi_read(SPI_REG_CNTRL03) & CNTRL03_SPIBUSY));
zbao246e84b2012-07-13 18:47:03 +080064}
65
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050066void spi_init(void)
zbao246e84b2012-07-13 18:47:03 +080067{
Elyes HAOUASa93e7542018-05-19 14:30:47 +020068 struct device *dev;
Zheng Bao7bcffa52012-11-28 11:36:52 +080069
Kyösti Mälkki4ad7f5b2018-05-22 01:15:17 +030070 dev = pcidev_on_root(0x14, 3);
Zheng Bao7bcffa52012-11-28 11:36:52 +080071 spibar = pci_read_config32(dev, 0xA0) & ~0x1F;
zbao246e84b2012-07-13 18:47:03 +080072}
73
Furquan Shaikh94f86992016-12-01 07:12:32 -080074static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout,
Furquan Shaikh0dba0252016-11-30 04:34:22 -080075 size_t bytesout, void *din, size_t bytesin)
zbao246e84b2012-07-13 18:47:03 +080076{
Paul Menzeldee0f882018-11-10 11:24:38 +010077 /* First byte is cmd which can not be sent through FIFO. */
Gabe Black93d9f922014-03-27 21:52:43 -070078 u8 cmd = *(u8 *)dout++;
79 u8 readoffby1;
Furquan Shaikh0dba0252016-11-30 04:34:22 -080080 size_t count;
zbao246e84b2012-07-13 18:47:03 +080081
Gabe Black93d9f922014-03-27 21:52:43 -070082 bytesout--;
Kyösti Mälkki9f0a2be2014-06-30 07:34:36 +030083
84 /*
85 * Check if this is a write command attempting to transfer more bytes
86 * than the controller can handle. Iterations for writes are not
87 * supported here because each SPI write command needs to be preceded
88 * and followed by other SPI commands, and this sequence is controlled
89 * by the SPI chip driver.
90 */
91 if (bytesout > AMD_SB_SPI_TX_LEN) {
92 printk(BIOS_DEBUG, "FCH SPI: Too much to write. Does your SPI chip driver use"
93 " spi_crop_chunk()?\n");
94 return -1;
95 }
96
Zheng Bao7bcffa52012-11-28 11:36:52 +080097 readoffby1 = bytesout ? 0 : 1;
zbao246e84b2012-07-13 18:47:03 +080098
Julius Wernercd49cce2019-03-05 16:53:33 -080099#if CONFIG(SOUTHBRIDGE_AMD_AGESA_YANGTZE)
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500100 spi_write(0x1E, 5);
101 spi_write(0x1F, bytesout); /* SpiExtRegIndx [5] - TxByteCount */
102 spi_write(0x1E, 6);
103 spi_write(0x1F, bytesin); /* SpiExtRegIndx [6] - RxByteCount */
Siyuan Wang91571452013-07-09 17:32:42 +0800104#else
Gabe Black93d9f922014-03-27 21:52:43 -0700105 u8 readwrite = (bytesin + readoffby1) << 4 | bytesout;
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500106 spi_write(SPI_REG_CNTRL01, readwrite);
Siyuan Wang91571452013-07-09 17:32:42 +0800107#endif
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500108 spi_write(SPI_REG_OPCODE, cmd);
zbao246e84b2012-07-13 18:47:03 +0800109
Zheng Bao7bcffa52012-11-28 11:36:52 +0800110 reset_internal_fifo_pointer();
111 for (count = 0; count < bytesout; count++, dout++) {
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500112 spi_write(SPI_REG_FIFO, *(uint8_t *)dout);
zbao246e84b2012-07-13 18:47:03 +0800113 }
Zheng Bao7bcffa52012-11-28 11:36:52 +0800114
115 reset_internal_fifo_pointer();
116 execute_command();
117
118 reset_internal_fifo_pointer();
119 /* Skip the bytes we sent. */
120 for (count = 0; count < bytesout; count++) {
Paul Menzel9eb4d0a2018-11-10 11:27:02 +0100121 spi_read(SPI_REG_FIFO);
Zheng Bao7bcffa52012-11-28 11:36:52 +0800122 }
123
Zheng Bao7bcffa52012-11-28 11:36:52 +0800124 for (count = 0; count < bytesin; count++, din++) {
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500125 *(uint8_t *)din = spi_read(SPI_REG_FIFO);
Zheng Bao7bcffa52012-11-28 11:36:52 +0800126 }
127
128 return 0;
129}
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800130
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800131int chipset_volatile_group_begin(const struct spi_flash *flash)
132{
Julius Wernercd49cce2019-03-05 16:53:33 -0800133 if (!CONFIG(HUDSON_IMC_FWM))
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800134 return 0;
135
136 ImcSleep(NULL);
137 return 0;
138}
139
140int chipset_volatile_group_end(const struct spi_flash *flash)
141{
Julius Wernercd49cce2019-03-05 16:53:33 -0800142 if (!CONFIG(HUDSON_IMC_FWM))
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800143 return 0;
144
145 ImcWakeup(NULL);
146 return 0;
zbao246e84b2012-07-13 18:47:03 +0800147}
148
Aaron Durbin851dde82018-04-19 21:15:25 -0600149static int xfer_vectors(const struct spi_slave *slave,
150 struct spi_op vectors[], size_t count)
151{
152 return spi_flash_vector_helper(slave, vectors, count, spi_ctrlr_xfer);
153}
154
Furquan Shaikh94f86992016-12-01 07:12:32 -0800155static const struct spi_ctrlr spi_ctrlr = {
Aaron Durbin851dde82018-04-19 21:15:25 -0600156 .xfer_vector = xfer_vectors,
Furquan Shaikhde705fa2017-04-19 19:27:28 -0700157 .max_xfer_size = AMD_SB_SPI_TX_LEN,
Aaron Durbin1fcc9f32018-01-29 11:30:17 -0700158 .flags = SPI_CNTRLR_DEDUCT_CMD_LEN,
Furquan Shaikh94f86992016-12-01 07:12:32 -0800159};
160
Furquan Shaikh12eca762017-05-18 14:58:49 -0700161const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = {
162 {
163 .ctrlr = &spi_ctrlr,
164 .bus_start = 0,
165 .bus_end = 0,
166 },
167};
168
169const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map);