blob: bbf6dd3ee2186ea91187f83e2e85330a64075e01 [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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
Paul Menzela46a7122013-02-23 18:37:27 +010017 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
zbao246e84b2012-07-13 18:47:03 +080018 */
Zheng Bao7bcffa52012-11-28 11:36:52 +080019#include <stdint.h>
20#include <stdlib.h>
21#include <string.h>
zbao246e84b2012-07-13 18:47:03 +080022#include <arch/io.h>
Kyösti Mälkki9f0a2be2014-06-30 07:34:36 +030023#include <console/console.h>
Zheng Bao600784e2013-02-07 17:30:23 +080024#include <spi-generic.h>
zbao246e84b2012-07-13 18:47:03 +080025#include <device/device.h>
Zheng Bao7bcffa52012-11-28 11:36:52 +080026#include <device/pci.h>
27#include <device/pci_ops.h>
zbao246e84b2012-07-13 18:47:03 +080028
Dave Frodin9b800ae2014-06-11 13:15:56 -060029#if IS_ENABLED (CONFIG_HUDSON_IMC_FWM)
Alexandru Gagniuc01e0adf2014-03-29 17:07:26 -050030#include <Proc/Fch/FchPlatform.h>
Martin Roth3316cf22012-12-05 16:22:54 -070031
32static int bus_claimed = 0;
33#endif
34
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050035#define SPI_REG_OPCODE 0x0
36#define SPI_REG_CNTRL01 0x1
37#define SPI_REG_CNTRL02 0x2
38 #define CNTRL02_FIFO_RESET (1 << 4)
39 #define CNTRL02_EXEC_OPCODE (1 << 0)
40#define SPI_REG_CNTRL03 0x3
41 #define CNTRL03_SPIBUSY (1 << 7)
42#define SPI_REG_FIFO 0xc
43#define SPI_REG_CNTRL11 0xd
44 #define CNTRL11_FIFOPTR_MASK 0x07
45
Kyösti Mälkki2fa8cc32014-07-15 02:30:49 +030046#if IS_ENABLED(CONFIG_SOUTHBRIDGE_AMD_AGESA_YANGTZE)
Kyösti Mälkki11104952014-06-29 16:17:33 +030047#define AMD_SB_SPI_TX_LEN 64
Kyösti Mälkki2fa8cc32014-07-15 02:30:49 +030048#else
49#define AMD_SB_SPI_TX_LEN 8
50#endif
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050051
Zheng Bao7bcffa52012-11-28 11:36:52 +080052static u32 spibar;
53
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050054static inline uint8_t spi_read(uint8_t reg)
55{
56 return read8(spibar + reg);
57}
58
59static inline void spi_write(uint8_t reg, uint8_t val)
60{
61 write8(spibar + reg, val);
62}
63
Zheng Bao7bcffa52012-11-28 11:36:52 +080064static void reset_internal_fifo_pointer(void)
zbao246e84b2012-07-13 18:47:03 +080065{
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050066 uint8_t reg8;
67
zbao246e84b2012-07-13 18:47:03 +080068 do {
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050069 reg8 = spi_read(SPI_REG_CNTRL02);
70 reg8 |= CNTRL02_FIFO_RESET;
71 spi_write(SPI_REG_CNTRL02, reg8);
72 } while (spi_read(SPI_REG_CNTRL11) & CNTRL11_FIFOPTR_MASK);
zbao246e84b2012-07-13 18:47:03 +080073}
74
Zheng Bao7bcffa52012-11-28 11:36:52 +080075static void execute_command(void)
zbao246e84b2012-07-13 18:47:03 +080076{
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050077 uint8_t reg8;
Zheng Bao7bcffa52012-11-28 11:36:52 +080078
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050079 reg8 = spi_read(SPI_REG_CNTRL02);
80 reg8 |= CNTRL02_EXEC_OPCODE;
81 spi_write(SPI_REG_CNTRL02, reg8);
82
83 while ((spi_read(SPI_REG_CNTRL02) & CNTRL02_EXEC_OPCODE) &&
84 (spi_read(SPI_REG_CNTRL03) & CNTRL03_SPIBUSY));
zbao246e84b2012-07-13 18:47:03 +080085}
86
Alexandru Gagniuc991e9512014-04-19 22:21:27 -050087void spi_init(void)
zbao246e84b2012-07-13 18:47:03 +080088{
Zheng Bao7bcffa52012-11-28 11:36:52 +080089 device_t dev;
90
91 dev = dev_find_slot(0, PCI_DEVFN(0x14, 3));
92 spibar = pci_read_config32(dev, 0xA0) & ~0x1F;
zbao246e84b2012-07-13 18:47:03 +080093}
94
Kyösti Mälkki11104952014-06-29 16:17:33 +030095unsigned int spi_crop_chunk(unsigned int cmd_len, unsigned int buf_len)
96{
97 return min(AMD_SB_SPI_TX_LEN - cmd_len, buf_len);
98}
99
Zheng Bao7bcffa52012-11-28 11:36:52 +0800100int spi_xfer(struct spi_slave *slave, const void *dout,
Gabe Black93d9f922014-03-27 21:52:43 -0700101 unsigned int bytesout, void *din, unsigned int bytesin)
zbao246e84b2012-07-13 18:47:03 +0800102{
Zheng Bao7bcffa52012-11-28 11:36:52 +0800103 /* First byte is cmd which can not being sent through FIFO. */
Gabe Black93d9f922014-03-27 21:52:43 -0700104 u8 cmd = *(u8 *)dout++;
105 u8 readoffby1;
106 u8 count;
zbao246e84b2012-07-13 18:47:03 +0800107
Gabe Black93d9f922014-03-27 21:52:43 -0700108 bytesout--;
Kyösti Mälkki9f0a2be2014-06-30 07:34:36 +0300109
110 /*
111 * Check if this is a write command attempting to transfer more bytes
112 * than the controller can handle. Iterations for writes are not
113 * supported here because each SPI write command needs to be preceded
114 * and followed by other SPI commands, and this sequence is controlled
115 * by the SPI chip driver.
116 */
117 if (bytesout > AMD_SB_SPI_TX_LEN) {
118 printk(BIOS_DEBUG, "FCH SPI: Too much to write. Does your SPI chip driver use"
119 " spi_crop_chunk()?\n");
120 return -1;
121 }
122
Zheng Bao7bcffa52012-11-28 11:36:52 +0800123 readoffby1 = bytesout ? 0 : 1;
zbao246e84b2012-07-13 18:47:03 +0800124
Siyuan Wang91571452013-07-09 17:32:42 +0800125#if CONFIG_SOUTHBRIDGE_AMD_AGESA_YANGTZE
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500126 spi_write(0x1E, 5);
127 spi_write(0x1F, bytesout); /* SpiExtRegIndx [5] - TxByteCount */
128 spi_write(0x1E, 6);
129 spi_write(0x1F, bytesin); /* SpiExtRegIndx [6] - RxByteCount */
Siyuan Wang91571452013-07-09 17:32:42 +0800130#else
Gabe Black93d9f922014-03-27 21:52:43 -0700131 u8 readwrite = (bytesin + readoffby1) << 4 | bytesout;
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500132 spi_write(SPI_REG_CNTRL01, readwrite);
Siyuan Wang91571452013-07-09 17:32:42 +0800133#endif
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500134 spi_write(SPI_REG_OPCODE, cmd);
zbao246e84b2012-07-13 18:47:03 +0800135
Zheng Bao7bcffa52012-11-28 11:36:52 +0800136 reset_internal_fifo_pointer();
137 for (count = 0; count < bytesout; count++, dout++) {
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500138 spi_write(SPI_REG_FIFO, *(uint8_t *)dout);
zbao246e84b2012-07-13 18:47:03 +0800139 }
Zheng Bao7bcffa52012-11-28 11:36:52 +0800140
141 reset_internal_fifo_pointer();
142 execute_command();
143
144 reset_internal_fifo_pointer();
145 /* Skip the bytes we sent. */
146 for (count = 0; count < bytesout; count++) {
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500147 cmd = spi_read(SPI_REG_FIFO);
Zheng Bao7bcffa52012-11-28 11:36:52 +0800148 }
149
150 reset_internal_fifo_pointer();
151 for (count = 0; count < bytesin; count++, din++) {
Alexandru Gagniuc991e9512014-04-19 22:21:27 -0500152 *(uint8_t *)din = spi_read(SPI_REG_FIFO);
Zheng Bao7bcffa52012-11-28 11:36:52 +0800153 }
154
155 return 0;
156}
157int spi_claim_bus(struct spi_slave *slave)
158{
Dave Frodin9b800ae2014-06-11 13:15:56 -0600159#if IS_ENABLED (CONFIG_HUDSON_IMC_FWM)
Martin Roth3316cf22012-12-05 16:22:54 -0700160
161 if (slave->rw == SPI_WRITE_FLAG) {
162 bus_claimed++;
163 if (bus_claimed == 1)
164 ImcSleep(NULL);
165 }
166#endif
167
Zheng Bao7bcffa52012-11-28 11:36:52 +0800168 return 0;
zbao246e84b2012-07-13 18:47:03 +0800169}
170
Zheng Bao7bcffa52012-11-28 11:36:52 +0800171void spi_release_bus(struct spi_slave *slave)
zbao246e84b2012-07-13 18:47:03 +0800172{
Dave Frodin9b800ae2014-06-11 13:15:56 -0600173#if IS_ENABLED (CONFIG_HUDSON_IMC_FWM)
Martin Roth3316cf22012-12-05 16:22:54 -0700174
175 if (slave->rw == SPI_WRITE_FLAG) {
176 bus_claimed--;
177 if (bus_claimed <= 0) {
178 bus_claimed = 0;
179 ImcWakeup(NULL);
180 }
181 }
182#endif
zbao246e84b2012-07-13 18:47:03 +0800183}
184
Zheng Bao7bcffa52012-11-28 11:36:52 +0800185void spi_cs_activate(struct spi_slave *slave)
zbao246e84b2012-07-13 18:47:03 +0800186{
Zheng Bao7bcffa52012-11-28 11:36:52 +0800187}
188
189void spi_cs_deactivate(struct spi_slave *slave)
190{
191}
192
Gabe Black1e187352014-03-27 20:37:03 -0700193struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs)
Zheng Bao7bcffa52012-11-28 11:36:52 +0800194{
195 struct spi_slave *slave = malloc(sizeof(*slave));
196
197 if (!slave) {
198 return NULL;
199 }
200
201 memset(slave, 0, sizeof(*slave));
202
203 return slave;
zbao246e84b2012-07-13 18:47:03 +0800204}