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>
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
30 * Wait until SPI is ready for transmission and transmit byte.
32 static void spi_tx(volatile struct spi_ctrl
*spictrl
, uint8_t in
)
38 "amoor.w %0, %2, %1\n"
39 : "=r" (r
), "+A" (spictrl
->txdata
.raw_bits
)
44 while ((int32_t) spictrl
->txdata
.raw_bits
< 0)
46 spictrl
->txdata
.data
= in
;
52 * Wait until SPI receive queue has data and read byte.
54 static uint8_t spi_rx(volatile struct spi_ctrl
*spictrl
)
57 while ((out
= (int32_t) spictrl
->rxdata
.raw_bits
) < 0)
62 static int spi_claim_bus_(const struct spi_slave
*slave
)
64 struct spi_ctrl
*spictrl
= spictrls
[slave
->bus
];
65 spi_reg_csmode csmode
;
67 csmode
.mode
= FU540_SPI_CSMODE_HOLD
;
68 write32(&spictrl
->csmode
.raw_bits
, csmode
.raw_bits
);
72 static void spi_release_bus_(const struct spi_slave
*slave
)
74 struct spi_ctrl
*spictrl
= spictrls
[slave
->bus
];
75 spi_reg_csmode csmode
;
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
];
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
) {
94 out
= *(uint8_t *)dout
++;
100 *(uint8_t *)din
++ = in
;
105 /* Working in half duplex
106 * send and receive can be done separately */
112 spi_tx(spictrl
, *(uint8_t *)dout
++);
119 *(uint8_t *)din
++ = spi_rx(spictrl
);
127 static int spi_setup_(const struct spi_slave
*slave
)
129 spi_reg_sckmode sckmode
;
130 spi_reg_csmode csmode
;
133 if ((slave
->bus
> 2) || (slave
->cs
!= 0))
136 struct spi_ctrl
*spictrl
= spictrls
[slave
->bus
];
138 write32(&spictrl
->sckdiv
, spi_min_clk_divisor(clock_get_tlclk_khz(),
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);
149 csmode
.mode
= FU540_SPI_CSMODE_AUTO
;
150 write32(&spictrl
->csmode
.raw_bits
, csmode
.raw_bits
);
153 fmt
.proto
= FU540_SPI_PROTO_S
;
154 fmt
.endian
= FU540_SPI_ENDIAN_BIG
;
157 write32(&spictrl
->fmt
.raw_bits
, fmt
.raw_bits
);
162 struct spi_ctrlr fu540_spi_ctrlr
= {
165 .claim_bus
= spi_claim_bus_
,
166 .release_bus
= spi_release_bus_
,
169 const struct spi_ctrlr_buses spi_ctrlr_bus_map
[] = {
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
;
187 if ((bus
> 2) || (cs
!= 0))
190 if ((config
->pha
> 1)
192 || (config
->protocol
> 2)
193 || (config
->endianness
> 1)
194 || (config
->bits_per_frame
> 8))
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);
214 csmode
.mode
= FU540_SPI_CSMODE_AUTO
;
215 write32(&spictrl
->csmode
.raw_bits
, csmode
.raw_bits
);
218 fmt
.proto
= config
->protocol
;
219 fmt
.endian
= config
->endianness
;
221 fmt
.len
= config
->bits_per_frame
;
222 write32(&spictrl
->fmt
.raw_bits
, fmt
.raw_bits
);
228 const struct spi_slave
*slave
,
229 const struct fu540_spi_mmap_config
*config
)
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))
247 struct spi_ctrl
*spictrl
= spictrls
[slave
->bus
];
249 /* disable direct memory-mapped spi flash mode */
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
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 */
274 write32(&spictrl
->fctrl
.raw_bits
, fctrl
.raw_bits
);