soc: Remove copyright notices
[coreboot.git] / src / soc / sifive / fu540 / spi.c
blob0a736315ea0b39de28fa7889cc1607f74b6e8355
1 /*
2 * This file is part of the coreboot project.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 2 of the License.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
15 #include <device/mmio.h>
16 #include <soc/spi.h>
17 #include <soc/clock.h>
18 #include <soc/addressmap.h>
20 #include "spi_internal.h"
22 static struct spi_ctrl *spictrls[] = {
23 (struct spi_ctrl *)FU540_QSPI0,
24 (struct spi_ctrl *)FU540_QSPI1,
25 (struct spi_ctrl *)FU540_QSPI2
29 /**
30 * Wait until SPI is ready for transmission and transmit byte.
32 static void spi_tx(volatile struct spi_ctrl *spictrl, uint8_t in)
34 #if __riscv_atomic
35 int32_t r;
36 do {
37 asm volatile (
38 "amoor.w %0, %2, %1\n"
39 : "=r" (r), "+A" (spictrl->txdata.raw_bits)
40 : "r" (in)
42 } while (r < 0);
43 #else
44 while ((int32_t) spictrl->txdata.raw_bits < 0)
46 spictrl->txdata.data = in;
47 #endif
51 /**
52 * Wait until SPI receive queue has data and read byte.
54 static uint8_t spi_rx(volatile struct spi_ctrl *spictrl)
56 int32_t out;
57 while ((out = (int32_t) spictrl->rxdata.raw_bits) < 0)
59 return (uint8_t) out;
62 static int spi_claim_bus_(const struct spi_slave *slave)
64 struct spi_ctrl *spictrl = spictrls[slave->bus];
65 spi_reg_csmode csmode;
66 csmode.raw_bits = 0;
67 csmode.mode = FU540_SPI_CSMODE_HOLD;
68 write32(&spictrl->csmode.raw_bits, csmode.raw_bits);
69 return 0;
72 static void spi_release_bus_(const struct spi_slave *slave)
74 struct spi_ctrl *spictrl = spictrls[slave->bus];
75 spi_reg_csmode csmode;
76 csmode.raw_bits = 0;
77 csmode.mode = FU540_SPI_CSMODE_OFF;
78 write32(&spictrl->csmode.raw_bits, csmode.raw_bits);
81 static int spi_xfer_(const struct spi_slave *slave,
82 const void *dout, size_t bytesout,
83 void *din, size_t bytesin)
85 struct spi_ctrl *spictrl = spictrls[slave->bus];
86 spi_reg_fmt fmt;
87 fmt.raw_bits = read32(&spictrl->fmt.raw_bits);
88 if (fmt.proto == FU540_SPI_PROTO_S) {
89 /* working in full-duplex mode
90 * receiving data needs to be triggered by sending data */
91 while (bytesout || bytesin) {
92 uint8_t in, out = 0;
93 if (bytesout) {
94 out = *(uint8_t *)dout++;
95 bytesout--;
97 spi_tx(spictrl, out);
98 in = spi_rx(spictrl);
99 if (bytesin) {
100 *(uint8_t *)din++ = in;
101 bytesin--;
104 } else {
105 /* Working in half duplex
106 * send and receive can be done separately */
107 if (dout && din)
108 return -1;
110 if (dout) {
111 while (bytesout) {
112 spi_tx(spictrl, *(uint8_t *)dout++);
113 bytesout--;
117 if (din) {
118 while (bytesin) {
119 *(uint8_t *)din++ = spi_rx(spictrl);
120 bytesin--;
124 return 0;
127 static int spi_setup_(const struct spi_slave *slave)
129 spi_reg_sckmode sckmode;
130 spi_reg_csmode csmode;
131 spi_reg_fmt fmt;
133 if ((slave->bus > 2) || (slave->cs != 0))
134 return -1;
136 struct spi_ctrl *spictrl = spictrls[slave->bus];
138 write32(&spictrl->sckdiv, spi_min_clk_divisor(clock_get_tlclk_khz(),
139 10000));
141 sckmode.raw_bits = 0;
142 sckmode.pha = FU540_SPI_PHA_LOW;
143 sckmode.pol = FU540_SPI_POL_LEADING;
144 write32(&spictrl->sckmode.raw_bits, sckmode.raw_bits);
146 write32(&spictrl->csdef, 0xffffffff);
148 csmode.raw_bits = 0;
149 csmode.mode = FU540_SPI_CSMODE_AUTO;
150 write32(&spictrl->csmode.raw_bits, csmode.raw_bits);
152 fmt.raw_bits = 0;
153 fmt.proto = FU540_SPI_PROTO_S;
154 fmt.endian = FU540_SPI_ENDIAN_BIG;
155 fmt.dir = 0;
156 fmt.len = 8;
157 write32(&spictrl->fmt.raw_bits, fmt.raw_bits);
159 return 0;
162 struct spi_ctrlr fu540_spi_ctrlr = {
163 .xfer = spi_xfer_,
164 .setup = spi_setup_,
165 .claim_bus = spi_claim_bus_,
166 .release_bus = spi_release_bus_,
169 const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = {
171 .bus_start = 0,
172 .bus_end = 2,
173 .ctrlr = &fu540_spi_ctrlr,
177 const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map);
179 int fu540_spi_setup(unsigned int bus, unsigned int cs,
180 struct spi_slave *slave,
181 struct fu540_spi_config *config)
183 spi_reg_sckmode sckmode;
184 spi_reg_csmode csmode;
185 spi_reg_fmt fmt;
187 if ((bus > 2) || (cs != 0))
188 return -1;
190 if ((config->pha > 1)
191 || (config->pol > 1)
192 || (config->protocol > 2)
193 || (config->endianness > 1)
194 || (config->bits_per_frame > 8))
195 return -1;
197 slave->bus = bus;
198 slave->cs = cs;
199 slave->ctrlr = &fu540_spi_ctrlr;
201 struct spi_ctrl *spictrl = spictrls[slave->bus];
203 write32(&spictrl->sckdiv, spi_min_clk_divisor(clock_get_tlclk_khz(),
204 config->freq / 1000));
206 sckmode.raw_bits = 0;
207 sckmode.pha = config->pha;
208 sckmode.pol = config->pol;
209 write32(&spictrl->sckmode.raw_bits, sckmode.raw_bits);
211 write32(&spictrl->csdef, 0xffffffff);
213 csmode.raw_bits = 0;
214 csmode.mode = FU540_SPI_CSMODE_AUTO;
215 write32(&spictrl->csmode.raw_bits, csmode.raw_bits);
217 fmt.raw_bits = 0;
218 fmt.proto = config->protocol;
219 fmt.endian = config->endianness;
220 fmt.dir = 0;
221 fmt.len = config->bits_per_frame;
222 write32(&spictrl->fmt.raw_bits, fmt.raw_bits);
224 return 0;
227 int fu540_spi_mmap(
228 const struct spi_slave *slave,
229 const struct fu540_spi_mmap_config *config)
231 spi_reg_fctrl fctrl;
232 spi_reg_ffmt ffmt;
234 if (slave->bus > 2)
235 return -1;
237 if ((config->cmd_en > 1)
238 || (config->addr_len > 4)
239 || (config->pad_cnt > 15)
240 || (config->cmd_proto > 2)
241 || (config->addr_proto > 2)
242 || (config->data_proto > 2)
243 || (config->cmd_code > 255)
244 || (config->pad_code > 255))
245 return -1;
247 struct spi_ctrl *spictrl = spictrls[slave->bus];
249 /* disable direct memory-mapped spi flash mode */
250 fctrl.raw_bits = 0;
251 fctrl.en = 0;
252 write32(&spictrl->fctrl.raw_bits, fctrl.raw_bits);
254 /* reset spi flash chip */
255 spi_tx(spictrl, 0x66);
256 spi_tx(spictrl, 0x99);
258 /* Pass the information of the flash read operation to the spi
259 * controller */
260 ffmt.raw_bits = 0;
261 ffmt.cmd_en = config->cmd_en;
262 ffmt.addr_len = config->addr_len;
263 ffmt.pad_cnt = config->pad_cnt;
264 ffmt.command_proto = config->cmd_proto;
265 ffmt.addr_proto = config->addr_proto;
266 ffmt.data_proto = config->data_proto;
267 ffmt.command_code = config->cmd_code;
268 ffmt.pad_code = config->pad_code;
269 write32(&spictrl->ffmt.raw_bits, ffmt.raw_bits);
271 /* enable direct memory-mapped spi flash mode */
272 fctrl.raw_bits = 0;
273 fctrl.en = 1;
274 write32(&spictrl->fctrl.raw_bits, fctrl.raw_bits);
276 return 0;