blob: 4298b7bd23d1695b8212377b85fad1a735cba466 [file] [log] [blame]
Timothy Pearson85c39a42015-09-05 18:14:25 -05001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
5 * Copyright (C) 2012 Advanced Micro Devices, Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Timothy Pearson85c39a42015-09-05 18:14:25 -050015 */
16
17#include <stdint.h>
18#include <stdlib.h>
19#include <string.h>
20#include <arch/io.h>
21#include <console/console.h>
22#include <spi-generic.h>
23#include <spi_flash.h>
24#include <device/device.h>
25#include <device/pci.h>
26#include <device/pci_ops.h>
27
28#define AMD_SB_SPI_TX_LEN 8
29
30static uint32_t get_spi_bar(void)
31{
32 device_t dev;
33
34 dev = dev_find_slot(0, PCI_DEVFN(0x14, 3));
35 return pci_read_config32(dev, 0xa0) & ~0x1f;
36}
37
38void spi_init(void)
39{
40 /* Not needed */
41}
42
43unsigned int spi_crop_chunk(unsigned int cmd_len, unsigned int buf_len)
44{
45 return min(AMD_SB_SPI_TX_LEN - cmd_len, buf_len);
46}
47
48static void reset_internal_fifo_pointer(void)
49{
50 uint32_t spibar = get_spi_bar();
51
52 do {
53 write8((void *)(spibar + 2),
54 read8((void *)(spibar + 2)) | 0x10);
55 } while (read8((void *)(spibar + 0xd)) & 0x7);
56}
57
58static void execute_command(void)
59{
60 uint32_t spibar = get_spi_bar();
61
62 write8((void *)(spibar + 2), read8((void *)(spibar + 2)) | 1);
63
64 while ((read8((void *)(spibar + 2)) & 1) &&
65 (read8((void *)(spibar+3)) & 0x80));
66}
67
68int spi_claim_bus(struct spi_slave *slave)
69{
70 /* Handled internally by the SB700 */
71 return 0;
72}
73
74void spi_release_bus(struct spi_slave *slave)
75{
76 /* Handled internally by the SB700 */
77}
78
79struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs)
80{
81 struct spi_slave *slave = malloc(sizeof(*slave));
82
83 if (!slave) {
84 return NULL;
85 }
86
87 memset(slave, 0, sizeof(*slave));
88
89 return slave;
90}
91
92int spi_xfer(struct spi_slave *slave, const void *dout,
93 unsigned int bytesout, void *din, unsigned int bytesin)
94{
95 /* First byte is cmd which cannot be sent through the FIFO. */
96 u8 cmd = *(u8 *)dout++;
97 u8 readoffby1;
98 u8 readwrite;
99 u8 count;
100
101 uint32_t spibar = get_spi_bar();
102
103 bytesout--;
104
105 /*
106 * Check if this is a write command attempting to transfer more bytes
107 * than the controller can handle. Iterations for writes are not
108 * supported here because each SPI write command needs to be preceded
109 * and followed by other SPI commands, and this sequence is controlled
110 * by the SPI chip driver.
111 */
112 if (bytesout > AMD_SB_SPI_TX_LEN) {
113 printk(BIOS_DEBUG, "FCH SPI: Too much to write. Does your SPI chip driver use"
114 " spi_crop_chunk()?\n");
115 return -1;
116 }
117
118 readoffby1 = bytesout ? 0 : 1;
119
120 readwrite = (bytesin + readoffby1) << 4 | bytesout;
121 write8((void *)(spibar + 1), readwrite);
122 write8((void *)(spibar + 0), cmd);
123
124 reset_internal_fifo_pointer();
125 for (count = 0; count < bytesout; count++, dout++) {
126 write8((void *)(spibar + 0x0C), *(u8 *)dout);
127 }
128
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++) {
135 cmd = read8((void *)(spibar + 0x0C));
136 }
137
138 reset_internal_fifo_pointer();
139 for (count = 0; count < bytesin; count++, din++) {
140 *(u8 *)din = read8((void *)(spibar + 0x0C));
141 }
142
143 return 0;
144}