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.
14 * Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
17 #include <device/mmio.h>
19 #include <console/console.h>
21 #include <soc/addressmap.h>
23 #include <soc/clock.h>
24 #include <spi-generic.h>
25 #include <spi_flash.h>
28 union cavium_spi_cfg
{
41 u64 csena
: 4; /* Must be one */
47 union cavium_spi_sts
{
73 union cavium_spi_cfg cfg
;
74 union cavium_spi_sts sts
;
75 union cavium_spi_tx tx
;
86 check_member(cavium_spi
, cfg
, 0);
87 check_member(cavium_spi
, sts
, 0x8);
88 check_member(cavium_spi
, tx
, 0x10);
89 check_member(cavium_spi
, dat
[7], 0xb8);
91 struct cavium_spi_slave
{
92 struct cavium_spi
*regs
;
96 #define SPI_TIMEOUT_US 5000
98 static struct cavium_spi_slave cavium_spi_slaves
[] = {
100 .regs
= (struct cavium_spi
*)MPI_PF_BAR0
,
105 static struct cavium_spi_slave
*to_cavium_spi(const struct spi_slave
*slave
)
107 assert(slave
->bus
< ARRAY_SIZE(cavium_spi_slaves
));
108 return &cavium_spi_slaves
[slave
->bus
];
112 * Enable the SPI controller. Pins are driven.
114 * @param bus The SPI bus to operate on
116 void spi_enable(const size_t bus
)
118 union cavium_spi_cfg cfg
;
120 assert(bus
< ARRAY_SIZE(cavium_spi_slaves
));
121 if (bus
>= ARRAY_SIZE(cavium_spi_slaves
))
124 struct cavium_spi
*regs
= cavium_spi_slaves
[bus
].regs
;
126 cfg
.u
= read64(®s
->cfg
);
129 write64(®s
->cfg
, cfg
.u
);
133 * Disable the SPI controller. Pins are tristated.
135 * @param bus The SPI bus to operate on
137 void spi_disable(const size_t bus
)
139 union cavium_spi_cfg cfg
;
141 assert(bus
< ARRAY_SIZE(cavium_spi_slaves
));
142 if (bus
>= ARRAY_SIZE(cavium_spi_slaves
))
145 struct cavium_spi
*regs
= cavium_spi_slaves
[bus
].regs
;
147 cfg
.u
= read64(®s
->cfg
);
150 write64(®s
->cfg
, cfg
.u
);
154 * Set SPI Chip select line and level if asserted.
156 * @param bus The SPI bus to operate on
157 * @param chip_select The chip select pin to use (0 - 3)
158 * @param assert_is_low CS pin state is low when asserted
160 void spi_set_cs(const size_t bus
,
161 const size_t chip_select
,
162 const size_t assert_is_low
)
164 union cavium_spi_cfg cfg
;
166 assert(bus
< ARRAY_SIZE(cavium_spi_slaves
));
167 if (bus
>= ARRAY_SIZE(cavium_spi_slaves
))
170 cavium_spi_slaves
[bus
].cs
= chip_select
& 0x3;
171 struct cavium_spi
*regs
= cavium_spi_slaves
[bus
].regs
;
173 cfg
.u
= read64(®s
->cfg
);
175 cfg
.s
.cshi
= !assert_is_low
;
176 write64(®s
->cfg
, cfg
.u
);
178 //FIXME: CS2/3: Change pin mux here
182 * Set SPI clock frequency.
184 * @param bus The SPI bus to operate on
185 * @param speed_hz The SPI frequency in Hz
186 * @param idle_low The SPI clock idles low
187 * @param idle_cycles Number of CLK cycles between two commands (0 - 3)
190 void spi_set_clock(const size_t bus
,
191 const size_t speed_hz
,
192 const size_t idle_low
,
193 const size_t idle_cycles
)
195 union cavium_spi_cfg cfg
;
197 assert(bus
< ARRAY_SIZE(cavium_spi_slaves
));
198 if (bus
>= ARRAY_SIZE(cavium_spi_slaves
))
201 struct cavium_spi
*regs
= cavium_spi_slaves
[bus
].regs
;
202 const uint64_t sclk
= thunderx_get_io_clock();
204 cfg
.u
= read64(®s
->cfg
);
207 cfg
.s
.idlelow
= !!idle_low
;
208 cfg
.s
.idleclks
= idle_cycles
& 0x3;
209 cfg
.s
.clkdiv
= MIN(sclk
/ (2ULL * speed_hz
), 0x1fff);
210 write64(®s
->cfg
, cfg
.u
);
212 printk(BIOS_DEBUG
, "SPI: set clock to %lld kHz\n",
213 (sclk
/ (2ULL * cfg
.s
.clkdiv
)) >> 10);
217 * Get current SPI clock frequency in Hz.
219 * @param bus The SPI bus to operate on
221 uint64_t spi_get_clock(const size_t bus
)
223 union cavium_spi_cfg cfg
;
225 assert(bus
< ARRAY_SIZE(cavium_spi_slaves
));
226 if (bus
>= ARRAY_SIZE(cavium_spi_slaves
))
229 struct cavium_spi
*regs
= cavium_spi_slaves
[bus
].regs
;
230 const uint64_t sclk
= thunderx_get_io_clock();
232 cfg
.u
= read64(®s
->cfg
);
234 return (sclk
/ (2ULL * cfg
.s
.clkdiv
));
238 * Set SPI LSB/MSB first.
240 * @param bus The SPI bus to operate on
241 * @param lsb_first The SPI operates LSB first
244 void spi_set_lsbmsb(const size_t bus
, const size_t lsb_first
)
246 union cavium_spi_cfg cfg
;
248 assert(bus
< ARRAY_SIZE(cavium_spi_slaves
));
249 if (bus
>= ARRAY_SIZE(cavium_spi_slaves
))
252 struct cavium_spi
*regs
= cavium_spi_slaves
[bus
].regs
;
254 cfg
.u
= read64(®s
->cfg
);
256 cfg
.s
.lsbfirst
= !!lsb_first
;
257 write64(®s
->cfg
, cfg
.u
);
261 * Init SPI with custom parameters and enable SPI controller.
263 * @param bus The SPI bus to operate on
264 * @param speed_hz The SPI frequency in Hz
265 * @param idle_low The SPI clock idles low
266 * @param idle_cycles Number of CLK cycles between two commands (0 - 3)
267 * @param lsb_first The SPI operates LSB first
268 * @param chip_select The chip select pin to use (0 - 3)
269 * @param assert_is_low CS pin state is low when asserted
271 void spi_init_custom(const size_t bus
,
272 const size_t speed_hz
,
273 const size_t idle_low
,
274 const size_t idle_cycles
,
275 const size_t lsb_first
,
276 const size_t chip_select
,
277 const size_t assert_is_low
)
280 spi_set_clock(bus
, speed_hz
, idle_low
, idle_cycles
);
281 spi_set_lsbmsb(bus
, lsb_first
);
282 spi_set_cs(bus
, chip_select
, assert_is_low
);
287 * Init all SPI controllers with default values and enable all SPI controller.
292 for (size_t i
= 0; i
< ARRAY_SIZE(cavium_spi_slaves
); i
++) {
294 spi_set_clock(i
, 12500000, 0, 0);
295 spi_set_lsbmsb(i
, 0);
301 static int cavium_spi_wait(struct cavium_spi
*regs
)
304 union cavium_spi_sts sts
;
306 stopwatch_init_usecs_expire(&sw
, SPI_TIMEOUT_US
);
308 sts
.u
= read64(®s
->sts
);
311 } while (!stopwatch_expired(&sw
));
312 printk(BIOS_DEBUG
, "SPI: Timed out after %uus\n", SPI_TIMEOUT_US
);
316 static int do_xfer(const struct spi_slave
*slave
, struct spi_op
*vector
,
319 struct cavium_spi
*regs
= to_cavium_spi(slave
)->regs
;
320 uint8_t *out_buf
= (uint8_t *)vector
->dout
;
321 size_t bytesout
= vector
->bytesout
;
322 uint8_t *in_buf
= (uint8_t *)vector
->din
;
323 size_t bytesin
= vector
->bytesin
;
324 union cavium_spi_sts sts
;
325 union cavium_spi_tx tx
;
328 * The CN81xx SPI controller is half-duplex and has 8 data registers.
329 * If >8 bytes remain in the transfer then we must set LEAVECS = 1 so
330 * that the /CS remains asserted. Once <=8 bytes remain we must set
331 * LEAVECS = 0 so that /CS is de-asserted, thus completing the transfer.
334 size_t out_now
= MIN(bytesout
, 8);
337 for (i
= 0; i
< out_now
; i
++)
338 write64(®s
->dat
[i
], out_buf
[i
] & 0xff);
341 tx
.s
.csid
= to_cavium_spi(slave
)->cs
;
342 if (leavecs
|| ((bytesout
> 8) || bytesin
))
344 /* number of bytes to transmit goes in both TXNUM and TOTNUM */
345 tx
.s
.totnum
= out_now
;
346 tx
.s
.txnum
= out_now
;
347 write64(®s
->tx
, tx
.u
);
350 if (cavium_spi_wait(regs
) < 0)
358 size_t in_now
= MIN(bytesin
, 8);
362 tx
.s
.csid
= to_cavium_spi(slave
)->cs
;
363 if (leavecs
|| (bytesin
> 8))
365 tx
.s
.totnum
= in_now
;
366 write64(®s
->tx
, tx
.u
);
369 if (cavium_spi_wait(regs
) < 0)
372 sts
.u
= read64(®s
->sts
);
373 if (sts
.s
.rxnum
!= in_now
) {
375 "SPI: Incorrect number of bytes received: %u.\n",
380 for (i
= 0; i
< in_now
; i
++) {
381 *in_buf
= (uint8_t)((read64(®s
->dat
[i
]) & 0xff));
390 static int spi_ctrlr_xfer_vector(const struct spi_slave
*slave
,
391 struct spi_op vectors
[], size_t count
)
395 for (i
= 0; i
< count
; i
++) {
396 if (do_xfer(slave
, &vectors
[i
], count
- 1 == i
? 0 : 1)) {
398 "SPI: Failed to transfer %zu vectors.\n", count
);
405 static const struct spi_ctrlr spi_ctrlr
= {
407 .xfer_vector
= spi_ctrlr_xfer_vector
,
408 .max_xfer_size
= SPI_CTRLR_DEFAULT_MAX_XFER_SIZE
,
411 const struct spi_ctrlr_buses spi_ctrlr_bus_map
[] = {
415 .bus_end
= ARRAY_SIZE(cavium_spi_slaves
) - 1,
418 const size_t spi_ctrlr_bus_map_count
= ARRAY_SIZE(spi_ctrlr_bus_map
);