blob: 71fddc629d6eb9283e395f6bf7d520cb32fb217f [file] [log] [blame]
zbao246e84b2012-07-13 18:47:03 +08001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2012 Advanced Micro Devices, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
zbao246e84b2012-07-13 18:47:03 +080014 */
Zheng Bao7bcffa52012-11-28 11:36:52 +080015#include <stdint.h>
16#include <stdlib.h>
17#include <string.h>
zbao246e84b2012-07-13 18:47:03 +080018#include <arch/io.h>
Kyösti Mälkki9f0a2be2014-06-30 07:34:36 +030019#include <console/console.h>
Furquan Shaikhc28984d2016-11-20 21:04:00 -080020#include <spi_flash.h>
Zheng Bao600784e2013-02-07 17:30:23 +080021#include <spi-generic.h>
zbao246e84b2012-07-13 18:47:03 +080022#include <device/device.h>
Zheng Bao7bcffa52012-11-28 11:36:52 +080023#include <device/pci.h>
24#include <device/pci_ops.h>
zbao246e84b2012-07-13 18:47:03 +080025
Alexandru Gagniuc01e0adf2014-03-29 17:07:26 -050026#include <Proc/Fch/FchPlatform.h>
Martin Roth3316cf22012-12-05 16:22:54 -070027
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050028#define SPI_REG_OPCODE 0x0
29#define SPI_REG_CNTRL01 0x1
30#define SPI_REG_CNTRL02 0x2
31 #define CNTRL02_FIFO_RESET (1 << 4)
32 #define CNTRL02_EXEC_OPCODE (1 << 0)
33#define SPI_REG_CNTRL03 0x3
34 #define CNTRL03_SPIBUSY (1 << 7)
35#define SPI_REG_FIFO 0xc
36#define SPI_REG_CNTRL11 0xd
37 #define CNTRL11_FIFOPTR_MASK 0x07
38
Kyösti Mälkki2fa8cc32014-07-15 02:30:49 +030039#if IS_ENABLED(CONFIG_SOUTHBRIDGE_AMD_AGESA_YANGTZE)
Kyösti Mälkki11104952014-06-29 16:17:33 +030040#define AMD_SB_SPI_TX_LEN 64
Kyösti Mälkki2fa8cc32014-07-15 02:30:49 +030041#else
42#define AMD_SB_SPI_TX_LEN 8
43#endif
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050044
Stefan Reinauerfce128c2015-07-21 12:52:34 -070045static uintptr_t spibar;
Zheng Bao7bcffa52012-11-28 11:36:52 +080046
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050047static inline uint8_t spi_read(uint8_t reg)
48{
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080049 return read8((void *)(spibar + reg));
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050050}
51
52static inline void spi_write(uint8_t reg, uint8_t val)
53{
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080054 write8((void *)(spibar + reg), val);
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050055}
56
Zheng Bao7bcffa52012-11-28 11:36:52 +080057static void reset_internal_fifo_pointer(void)
zbao246e84b2012-07-13 18:47:03 +080058{
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050059 uint8_t reg8;
60
zbao246e84b2012-07-13 18:47:03 +080061 do {
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050062 reg8 = spi_read(SPI_REG_CNTRL02);
63 reg8 |= CNTRL02_FIFO_RESET;
64 spi_write(SPI_REG_CNTRL02, reg8);
65 } while (spi_read(SPI_REG_CNTRL11) & CNTRL11_FIFOPTR_MASK);
zbao246e84b2012-07-13 18:47:03 +080066}
67
Zheng Bao7bcffa52012-11-28 11:36:52 +080068static void execute_command(void)
zbao246e84b2012-07-13 18:47:03 +080069{
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050070 uint8_t reg8;
Zheng Bao7bcffa52012-11-28 11:36:52 +080071
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050072 reg8 = spi_read(SPI_REG_CNTRL02);
73 reg8 |= CNTRL02_EXEC_OPCODE;
74 spi_write(SPI_REG_CNTRL02, reg8);
75
76 while ((spi_read(SPI_REG_CNTRL02) & CNTRL02_EXEC_OPCODE) &&
77 (spi_read(SPI_REG_CNTRL03) & CNTRL03_SPIBUSY));
zbao246e84b2012-07-13 18:47:03 +080078}
79
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050080void spi_init(void)
zbao246e84b2012-07-13 18:47:03 +080081{
Zheng Bao7bcffa52012-11-28 11:36:52 +080082 device_t dev;
83
84 dev = dev_find_slot(0, PCI_DEVFN(0x14, 3));
85 spibar = pci_read_config32(dev, 0xA0) & ~0x1F;
zbao246e84b2012-07-13 18:47:03 +080086}
87
Furquan Shaikh94f86992016-12-01 07:12:32 -080088static int spi_ctrlr_xfer(const struct spi_slave *slave, const void *dout,
Furquan Shaikh0dba0252016-11-30 04:34:22 -080089 size_t bytesout, void *din, size_t bytesin)
zbao246e84b2012-07-13 18:47:03 +080090{
Zheng Bao7bcffa52012-11-28 11:36:52 +080091 /* First byte is cmd which can not being sent through FIFO. */
Gabe Black93d9f922014-03-27 21:52:43 -070092 u8 cmd = *(u8 *)dout++;
93 u8 readoffby1;
Furquan Shaikh0dba0252016-11-30 04:34:22 -080094 size_t count;
zbao246e84b2012-07-13 18:47:03 +080095
Gabe Black93d9f922014-03-27 21:52:43 -070096 bytesout--;
Kyösti Mälkki9f0a2be2014-06-30 07:34:36 +030097
98 /*
99 * Check if this is a write command attempting to transfer more bytes
100 * than the controller can handle. Iterations for writes are not
101 * supported here because each SPI write command needs to be preceded
102 * and followed by other SPI commands, and this sequence is controlled
103 * by the SPI chip driver.
104 */
105 if (bytesout > AMD_SB_SPI_TX_LEN) {
106 printk(BIOS_DEBUG, "FCH SPI: Too much to write. Does your SPI chip driver use"
107 " spi_crop_chunk()?\n");
108 return -1;
109 }
110
Zheng Bao7bcffa52012-11-28 11:36:52 +0800111 readoffby1 = bytesout ? 0 : 1;
zbao246e84b2012-07-13 18:47:03 +0800112
Siyuan Wang91571452013-07-09 17:32:42 +0800113#if CONFIG_SOUTHBRIDGE_AMD_AGESA_YANGTZE
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500114 spi_write(0x1E, 5);
115 spi_write(0x1F, bytesout); /* SpiExtRegIndx [5] - TxByteCount */
116 spi_write(0x1E, 6);
117 spi_write(0x1F, bytesin); /* SpiExtRegIndx [6] - RxByteCount */
Siyuan Wang91571452013-07-09 17:32:42 +0800118#else
Gabe Black93d9f922014-03-27 21:52:43 -0700119 u8 readwrite = (bytesin + readoffby1) << 4 | bytesout;
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500120 spi_write(SPI_REG_CNTRL01, readwrite);
Siyuan Wang91571452013-07-09 17:32:42 +0800121#endif
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500122 spi_write(SPI_REG_OPCODE, cmd);
zbao246e84b2012-07-13 18:47:03 +0800123
Zheng Bao7bcffa52012-11-28 11:36:52 +0800124 reset_internal_fifo_pointer();
125 for (count = 0; count < bytesout; count++, dout++) {
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500126 spi_write(SPI_REG_FIFO, *(uint8_t *)dout);
zbao246e84b2012-07-13 18:47:03 +0800127 }
Zheng Bao7bcffa52012-11-28 11:36:52 +0800128
129 reset_internal_fifo_pointer();
130 execute_command();
131
132 reset_internal_fifo_pointer();
133 /* Skip the bytes we sent. */
134 for (count = 0; count < bytesout; count++) {
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500135 cmd = spi_read(SPI_REG_FIFO);
Zheng Bao7bcffa52012-11-28 11:36:52 +0800136 }
137
Zheng Bao7bcffa52012-11-28 11:36:52 +0800138 for (count = 0; count < bytesin; count++, din++) {
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500139 *(uint8_t *)din = spi_read(SPI_REG_FIFO);
Zheng Bao7bcffa52012-11-28 11:36:52 +0800140 }
141
142 return 0;
143}
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800144
Furquan Shaikhc28984d2016-11-20 21:04:00 -0800145int chipset_volatile_group_begin(const struct spi_flash *flash)
146{
147 if (!IS_ENABLED (CONFIG_HUDSON_IMC_FWM))
148 return 0;
149
150 ImcSleep(NULL);
151 return 0;
152}
153
154int chipset_volatile_group_end(const struct spi_flash *flash)
155{
156 if (!IS_ENABLED (CONFIG_HUDSON_IMC_FWM))
157 return 0;
158
159 ImcWakeup(NULL);
160 return 0;
zbao246e84b2012-07-13 18:47:03 +0800161}
162
Furquan Shaikh94f86992016-12-01 07:12:32 -0800163static const struct spi_ctrlr spi_ctrlr = {
164 .xfer = spi_ctrlr_xfer,
Furquan Shaikhc2973d12016-11-29 22:07:42 -0800165 .xfer_vector = spi_xfer_two_vectors,
Furquan Shaikhde705fa2017-04-19 19:27:28 -0700166 .max_xfer_size = AMD_SB_SPI_TX_LEN,
167 .deduct_cmd_len = true,
Furquan Shaikh94f86992016-12-01 07:12:32 -0800168};
169
Furquan Shaikh12eca762017-05-18 14:58:49 -0700170const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = {
171 {
172 .ctrlr = &spi_ctrlr,
173 .bus_start = 0,
174 .bus_end = 0,
175 },
176};
177
178const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map);