soc: Remove copyright notices
[coreboot.git] / src / soc / cavium / cn81xx / spi.c
blob09f27a488f061d7ce7034198728fc6fdb6972644
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.
14 * Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
17 #include <device/mmio.h>
18 #include <assert.h>
19 #include <console/console.h>
20 #include <endian.h>
21 #include <soc/addressmap.h>
22 #include <soc/spi.h>
23 #include <soc/clock.h>
24 #include <spi-generic.h>
25 #include <spi_flash.h>
26 #include <timer.h>
28 union cavium_spi_cfg {
29 u64 u;
30 struct {
31 u64 enable : 1;
32 u64 idlelow : 1;
33 u64 clk_cont : 1;
34 u64 wireor : 1;
35 u64 lsbfirst : 1;
36 u64 : 2;
37 u64 cshi : 1;
38 u64 idleclks : 2;
39 u64 tristate : 1;
40 u64 cslate : 1;
41 u64 csena : 4; /* Must be one */
42 u64 clkdiv : 13;
43 u64 : 35;
44 } s;
47 union cavium_spi_sts {
48 u64 u;
49 struct {
50 u64 busy : 1;
51 u64 mpi_intr : 1;
52 u64 : 6;
53 u64 rxnum : 5;
54 u64 : 51;
55 } s;
58 union cavium_spi_tx {
59 u64 u;
60 struct {
61 u64 totnum : 5;
62 u64 : 3;
63 u64 txnum : 5;
64 u64 : 3;
65 u64 leavecs : 1;
66 u64 : 3;
67 u64 csid : 2;
68 u64 : 42;
69 } s;
72 struct cavium_spi {
73 union cavium_spi_cfg cfg;
74 union cavium_spi_sts sts;
75 union cavium_spi_tx tx;
76 u64 rsvd1;
77 u64 sts_w1s;
78 u64 rsvd2;
79 u64 int_ena_w1c;
80 u64 int_ena_w1s;
81 u64 wide_dat;
82 u8 rsvd4[0x38];
83 u64 dat[8];
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;
93 int cs;
96 #define SPI_TIMEOUT_US 5000
98 static struct cavium_spi_slave cavium_spi_slaves[] = {
100 .regs = (struct cavium_spi *)MPI_PF_BAR0,
101 .cs = 0,
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))
122 return;
124 struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
126 cfg.u = read64(&regs->cfg);
127 cfg.s.csena = 0xf;
128 cfg.s.enable = 1;
129 write64(&regs->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))
143 return;
145 struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
147 cfg.u = read64(&regs->cfg);
148 cfg.s.csena = 0xf;
149 cfg.s.enable = 0;
150 write64(&regs->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))
168 return;
170 cavium_spi_slaves[bus].cs = chip_select & 0x3;
171 struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
173 cfg.u = read64(&regs->cfg);
174 cfg.s.csena = 0xf;
175 cfg.s.cshi = !assert_is_low;
176 write64(&regs->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))
199 return;
201 struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
202 const uint64_t sclk = thunderx_get_io_clock();
204 cfg.u = read64(&regs->cfg);
205 cfg.s.csena = 0xf;
206 cfg.s.clk_cont = 0;
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(&regs->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))
227 return 0;
229 struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
230 const uint64_t sclk = thunderx_get_io_clock();
232 cfg.u = read64(&regs->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))
250 return;
252 struct cavium_spi *regs = cavium_spi_slaves[bus].regs;
254 cfg.u = read64(&regs->cfg);
255 cfg.s.csena = 0xf;
256 cfg.s.lsbfirst = !!lsb_first;
257 write64(&regs->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)
279 spi_disable(bus);
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);
283 spi_enable(bus);
287 * Init all SPI controllers with default values and enable all SPI controller.
290 void spi_init(void)
292 for (size_t i = 0; i < ARRAY_SIZE(cavium_spi_slaves); i++) {
293 spi_disable(i);
294 spi_set_clock(i, 12500000, 0, 0);
295 spi_set_lsbmsb(i, 0);
296 spi_set_cs(i, 0, 1);
297 spi_enable(i);
301 static int cavium_spi_wait(struct cavium_spi *regs)
303 struct stopwatch sw;
304 union cavium_spi_sts sts;
306 stopwatch_init_usecs_expire(&sw, SPI_TIMEOUT_US);
307 do {
308 sts.u = read64(&regs->sts);
309 if (!sts.s.busy)
310 return 0;
311 } while (!stopwatch_expired(&sw));
312 printk(BIOS_DEBUG, "SPI: Timed out after %uus\n", SPI_TIMEOUT_US);
313 return -1;
316 static int do_xfer(const struct spi_slave *slave, struct spi_op *vector,
317 int leavecs)
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.
333 while (bytesout) {
334 size_t out_now = MIN(bytesout, 8);
335 unsigned int i;
337 for (i = 0; i < out_now; i++)
338 write64(&regs->dat[i], out_buf[i] & 0xff);
340 tx.u = 0;
341 tx.s.csid = to_cavium_spi(slave)->cs;
342 if (leavecs || ((bytesout > 8) || bytesin))
343 tx.s.leavecs = 1;
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(&regs->tx, tx.u);
349 /* check status */
350 if (cavium_spi_wait(regs) < 0)
351 return -1;
353 bytesout -= out_now;
354 out_buf += out_now;
357 while (bytesin) {
358 size_t in_now = MIN(bytesin, 8);
359 unsigned int i;
361 tx.u = 0;
362 tx.s.csid = to_cavium_spi(slave)->cs;
363 if (leavecs || (bytesin > 8))
364 tx.s.leavecs = 1;
365 tx.s.totnum = in_now;
366 write64(&regs->tx, tx.u);
368 /* check status */
369 if (cavium_spi_wait(regs) < 0)
370 return -1;
372 sts.u = read64(&regs->sts);
373 if (sts.s.rxnum != in_now) {
374 printk(BIOS_ERR,
375 "SPI: Incorrect number of bytes received: %u.\n",
376 sts.s.rxnum);
377 return -1;
380 for (i = 0; i < in_now; i++) {
381 *in_buf = (uint8_t)((read64(&regs->dat[i]) & 0xff));
382 in_buf++;
384 bytesin -= in_now;
387 return 0;
390 static int spi_ctrlr_xfer_vector(const struct spi_slave *slave,
391 struct spi_op vectors[], size_t count)
393 int i;
395 for (i = 0; i < count; i++) {
396 if (do_xfer(slave, &vectors[i], count - 1 == i ? 0 : 1)) {
397 printk(BIOS_ERR,
398 "SPI: Failed to transfer %zu vectors.\n", count);
399 return -1;
403 return 0;
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[] = {
413 .ctrlr = &spi_ctrlr,
414 .bus_start = 0,
415 .bus_end = ARRAY_SIZE(cavium_spi_slaves) - 1,
418 const size_t spi_ctrlr_bus_map_count = ARRAY_SIZE(spi_ctrlr_bus_map);