soc/intel/common/block/chip: Refactor chip_get_common_soc_structure()
[coreboot.git] / src / soc / sifive / fu540 / spi.c
blob87c59a47168470fa88f8c585de87c139f37ec2bc
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <device/mmio.h>
4 #include <soc/spi.h>
5 #include <soc/clock.h>
6 #include <soc/addressmap.h>
8 #include "spi_internal.h"
10 static struct spi_ctrl *spictrls[] = {
11 (struct spi_ctrl *)FU540_QSPI0,
12 (struct spi_ctrl *)FU540_QSPI1,
13 (struct spi_ctrl *)FU540_QSPI2
16 /**
17 * Wait until SPI is ready for transmission and transmit byte.
19 static void spi_tx(volatile struct spi_ctrl *spictrl, uint8_t in)
21 #if __riscv_atomic
22 int32_t r;
23 do {
24 asm volatile (
25 "amoor.w %0, %2, %1\n"
26 : "=r" (r), "+A" (spictrl->txdata.raw_bits)
27 : "r" (in)
29 } while (r < 0);
30 #else
31 while ((int32_t) spictrl->txdata.raw_bits < 0)
33 spictrl->txdata.data = in;
34 #endif
37 /**
38 * Wait until SPI receive queue has data and read byte.
40 static uint8_t spi_rx(volatile struct spi_ctrl *spictrl)
42 int32_t out;
43 while ((out = (int32_t) spictrl->rxdata.raw_bits) < 0)
45 return (uint8_t) out;
48 static int spi_claim_bus_(const struct spi_slave *slave)
50 struct spi_ctrl *spictrl = spictrls[slave->bus];
51 spi_reg_csmode csmode;
52 csmode.raw_bits = 0;
53 csmode.mode = FU540_SPI_CSMODE_HOLD;
54 write32(&spictrl->csmode.raw_bits, csmode.raw_bits);
55 return 0;
58 static void spi_release_bus_(const struct spi_slave *slave)
60 struct spi_ctrl *spictrl = spictrls[slave->bus];
61 spi_reg_csmode csmode;
62 csmode.raw_bits = 0;
63 csmode.mode = FU540_SPI_CSMODE_OFF;
64 write32(&spictrl->csmode.raw_bits, csmode.raw_bits);
67 static int spi_xfer_(const struct spi_slave *slave,
68 const void *dout, size_t bytesout,
69 void *din, size_t bytesin)
71 struct spi_ctrl *spictrl = spictrls[slave->bus];
72 spi_reg_fmt fmt;
73 fmt.raw_bits = read32(&spictrl->fmt.raw_bits);
74 if (fmt.proto == FU540_SPI_PROTO_S) {
75 /* working in full-duplex mode
76 * receiving data needs to be triggered by sending data */
77 while (bytesout || bytesin) {
78 uint8_t in, out = 0;
79 if (bytesout) {
80 out = *(uint8_t *)dout++;
81 bytesout--;
83 spi_tx(spictrl, out);
84 in = spi_rx(spictrl);
85 if (bytesin) {
86 *(uint8_t *)din++ = in;
87 bytesin--;
90 } else {
91 /* Working in half duplex
92 * send and receive can be done separately */
93 if (dout && din)
94 return -1;
96 if (dout) {
97 while (bytesout) {
98 spi_tx(spictrl, *(uint8_t *)dout++);
99 bytesout--;
103 if (din) {
104 while (bytesin) {
105 *(uint8_t *)din++ = spi_rx(spictrl);
106 bytesin--;
110 return 0;
113 static int spi_setup_(const struct spi_slave *slave)
115 spi_reg_sckmode sckmode;
116 spi_reg_csmode csmode;
117 spi_reg_fmt fmt;
119 if ((slave->bus > 2) || (slave->cs != 0))
120 return -1;
122 struct spi_ctrl *spictrl = spictrls[slave->bus];
124 write32(&spictrl->sckdiv, spi_min_clk_divisor(clock_get_tlclk_khz(),
125 10000));
127 sckmode.raw_bits = 0;
128 sckmode.pha = FU540_SPI_PHA_LOW;
129 sckmode.pol = FU540_SPI_POL_LEADING;
130 write32(&spictrl->sckmode.raw_bits, sckmode.raw_bits);
132 write32(&spictrl->csdef, 0xffffffff);
134 csmode.raw_bits = 0;
135 csmode.mode = FU540_SPI_CSMODE_AUTO;
136 write32(&spictrl->csmode.raw_bits, csmode.raw_bits);
138 fmt.raw_bits = 0;
139 fmt.proto = FU540_SPI_PROTO_S;
140 fmt.endian = FU540_SPI_ENDIAN_BIG;
141 fmt.dir = 0;
142 fmt.len = 8;
143 write32(&spictrl->fmt.raw_bits, fmt.raw_bits);
145 return 0;
148 struct spi_ctrlr fu540_spi_ctrlr = {
149 .xfer = spi_xfer_,
150 .setup = spi_setup_,
151 .claim_bus = spi_claim_bus_,
152 .release_bus = spi_release_bus_,
155 const struct spi_ctrlr_buses spi_ctrlr_bus_map[] = {
157 .bus_start = 0,
158 .bus_end = 2,
159 .ctrlr = &fu540_spi_ctrlr,
163 const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map);
165 int fu540_spi_setup(unsigned int bus, unsigned int cs,
166 struct spi_slave *slave,
167 struct fu540_spi_config *config)
169 spi_reg_sckmode sckmode;
170 spi_reg_csmode csmode;
171 spi_reg_fmt fmt;
173 if ((bus > 2) || (cs != 0))
174 return -1;
176 if ((config->pha > 1)
177 || (config->pol > 1)
178 || (config->protocol > 2)
179 || (config->endianness > 1)
180 || (config->bits_per_frame > 8))
181 return -1;
183 slave->bus = bus;
184 slave->cs = cs;
185 slave->ctrlr = &fu540_spi_ctrlr;
187 struct spi_ctrl *spictrl = spictrls[slave->bus];
189 write32(&spictrl->sckdiv, spi_min_clk_divisor(clock_get_tlclk_khz(),
190 config->freq / 1000));
192 sckmode.raw_bits = 0;
193 sckmode.pha = config->pha;
194 sckmode.pol = config->pol;
195 write32(&spictrl->sckmode.raw_bits, sckmode.raw_bits);
197 write32(&spictrl->csdef, 0xffffffff);
199 csmode.raw_bits = 0;
200 csmode.mode = FU540_SPI_CSMODE_AUTO;
201 write32(&spictrl->csmode.raw_bits, csmode.raw_bits);
203 fmt.raw_bits = 0;
204 fmt.proto = config->protocol;
205 fmt.endian = config->endianness;
206 fmt.dir = 0;
207 fmt.len = config->bits_per_frame;
208 write32(&spictrl->fmt.raw_bits, fmt.raw_bits);
210 return 0;
213 int fu540_spi_mmap(
214 const struct spi_slave *slave,
215 const struct fu540_spi_mmap_config *config)
217 spi_reg_fctrl fctrl;
218 spi_reg_ffmt ffmt;
220 if (slave->bus > 2)
221 return -1;
223 if ((config->cmd_en > 1)
224 || (config->addr_len > 4)
225 || (config->pad_cnt > 15)
226 || (config->cmd_proto > 2)
227 || (config->addr_proto > 2)
228 || (config->data_proto > 2)
229 || (config->cmd_code > 255)
230 || (config->pad_code > 255))
231 return -1;
233 struct spi_ctrl *spictrl = spictrls[slave->bus];
235 /* disable direct memory-mapped spi flash mode */
236 fctrl.raw_bits = 0;
237 fctrl.en = 0;
238 write32(&spictrl->fctrl.raw_bits, fctrl.raw_bits);
240 /* reset spi flash chip */
241 spi_tx(spictrl, 0x66);
242 spi_tx(spictrl, 0x99);
244 /* Pass the information of the flash read operation to the spi
245 * controller */
246 ffmt.raw_bits = 0;
247 ffmt.cmd_en = config->cmd_en;
248 ffmt.addr_len = config->addr_len;
249 ffmt.pad_cnt = config->pad_cnt;
250 ffmt.command_proto = config->cmd_proto;
251 ffmt.addr_proto = config->addr_proto;
252 ffmt.data_proto = config->data_proto;
253 ffmt.command_code = config->cmd_code;
254 ffmt.pad_code = config->pad_code;
255 write32(&spictrl->ffmt.raw_bits, ffmt.raw_bits);
257 /* enable direct memory-mapped spi flash mode */
258 fctrl.raw_bits = 0;
259 fctrl.en = 1;
260 write32(&spictrl->fctrl.raw_bits, fctrl.raw_bits);
262 return 0;