Software APIs
dif_spi_host.c
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
6 
7 #include <assert.h>
8 #include <stdalign.h>
9 #include <stddef.h>
10 
14 
15 #include "spi_host_regs.h" // Generated.
16 
17 // We create weak symbol aliases for the FIFO write and read functions so the
18 // unit tests can provide mocks. The mocks provide for separate testing of
19 // the FIFO functions and the overall transaction management functions.
20 OT_WEAK
21 OT_ALIAS("dif_spi_host_fifo_write")
22 dif_result_t spi_host_fifo_write_alias(const dif_spi_host_t *spi_host,
23  const void *src, uint16_t len);
24 
25 OT_WEAK
27 dif_result_t spi_host_fifo_read_alias(const dif_spi_host_t *spi_host, void *dst,
28  uint16_t len);
29 
30 static void spi_host_reset(const dif_spi_host_t *spi_host) {
31  // Set the software reset request bit.
32  uint32_t reg =
33  mmio_region_read32(spi_host->base_addr, SPI_HOST_CONTROL_REG_OFFSET);
34  mmio_region_write32(
35  spi_host->base_addr, SPI_HOST_CONTROL_REG_OFFSET,
36  bitfield_bit32_write(reg, SPI_HOST_CONTROL_SW_RST_BIT, true));
37 
38  // Wait for the spi host to go inactive.
39  bool active;
40  do {
41  uint32_t reg =
42  mmio_region_read32(spi_host->base_addr, SPI_HOST_STATUS_REG_OFFSET);
43  active = bitfield_bit32_read(reg, SPI_HOST_STATUS_ACTIVE_BIT);
44  } while (active);
45 
46  // Wait for the spi host fifos to drain.
47  uint32_t txqd, rxqd;
48  do {
49  uint32_t reg =
50  mmio_region_read32(spi_host->base_addr, SPI_HOST_STATUS_REG_OFFSET);
51  txqd = bitfield_field32_read(reg, SPI_HOST_STATUS_TXQD_FIELD);
52  rxqd = bitfield_field32_read(reg, SPI_HOST_STATUS_RXQD_FIELD);
53  } while (txqd != 0 || rxqd != 0);
54 
55  // Clear the software reset request bit.
56  mmio_region_write32(
57  spi_host->base_addr, SPI_HOST_CONTROL_REG_OFFSET,
58  bitfield_bit32_write(0, SPI_HOST_CONTROL_SW_RST_BIT, false));
59 }
60 
61 static void spi_host_enable(const dif_spi_host_t *spi_host, bool enable) {
62  uint32_t reg =
63  mmio_region_read32(spi_host->base_addr, SPI_HOST_CONTROL_REG_OFFSET);
64  mmio_region_write32(
65  spi_host->base_addr, SPI_HOST_CONTROL_REG_OFFSET,
66  bitfield_bit32_write(reg, SPI_HOST_CONTROL_SPIEN_BIT, enable));
67 }
68 
69 dif_result_t dif_spi_host_configure(const dif_spi_host_t *spi_host,
70  dif_spi_host_config_t config) {
71  if (spi_host == NULL) {
72  return kDifBadArg;
73  }
74  if (config.peripheral_clock_freq_hz == 0 || config.spi_clock == 0) {
75  return kDifBadArg;
76  }
77 
78  uint32_t divider =
79  ((config.peripheral_clock_freq_hz / config.spi_clock) / 2) - 1;
80  if (divider & ~(uint32_t)SPI_HOST_CONFIGOPTS_CLKDIV_MASK) {
81  return kDifBadArg;
82  }
83 
84  spi_host_reset(spi_host);
85  uint32_t reg = 0;
86  reg = bitfield_field32_write(reg, SPI_HOST_CONFIGOPTS_CLKDIV_FIELD, divider);
87  reg = bitfield_field32_write(reg, SPI_HOST_CONFIGOPTS_CSNIDLE_FIELD,
88  config.chip_select.idle);
89  reg = bitfield_field32_write(reg, SPI_HOST_CONFIGOPTS_CSNTRAIL_FIELD,
90  config.chip_select.trail);
91  reg = bitfield_field32_write(reg, SPI_HOST_CONFIGOPTS_CSNLEAD_FIELD,
92  config.chip_select.lead);
93  reg = bitfield_bit32_write(reg, SPI_HOST_CONFIGOPTS_FULLCYC_BIT,
94  config.full_cycle);
95  reg = bitfield_bit32_write(reg, SPI_HOST_CONFIGOPTS_CPHA_BIT, config.cpha);
96  reg = bitfield_bit32_write(reg, SPI_HOST_CONFIGOPTS_CPOL_BIT, config.cpol);
97  mmio_region_write32(spi_host->base_addr, SPI_HOST_CONFIGOPTS_REG_OFFSET, reg);
98 
99  reg = mmio_region_read32(spi_host->base_addr, SPI_HOST_CONTROL_REG_OFFSET);
100  reg = bitfield_field32_write(reg, SPI_HOST_CONTROL_TX_WATERMARK_FIELD,
101  config.tx_watermark);
102  reg = bitfield_field32_write(reg, SPI_HOST_CONTROL_RX_WATERMARK_FIELD,
103  config.rx_watermark);
104  mmio_region_write32(spi_host->base_addr, SPI_HOST_CONTROL_REG_OFFSET, reg);
105 
106  spi_host_enable(spi_host, true);
107  return kDifOk;
108 }
109 
110 dif_result_t dif_spi_host_output_set_enabled(const dif_spi_host_t *spi_host,
111  bool enabled) {
112  if (spi_host == NULL) {
113  return kDifBadArg;
114  }
115 
116  uint32_t reg =
117  mmio_region_read32(spi_host->base_addr, SPI_HOST_CONTROL_REG_OFFSET);
118  mmio_region_write32(
119  spi_host->base_addr, SPI_HOST_CONTROL_REG_OFFSET,
120  bitfield_bit32_write(reg, SPI_HOST_CONTROL_OUTPUT_EN_BIT, enabled));
121 
122  return kDifOk;
123 }
124 
125 static void wait_ready(const dif_spi_host_t *spi_host) {
126  bool ready;
127  do {
128  uint32_t reg =
129  mmio_region_read32(spi_host->base_addr, SPI_HOST_STATUS_REG_OFFSET);
130  ready = bitfield_bit32_read(reg, SPI_HOST_STATUS_READY_BIT);
131  } while (!ready);
132 }
133 
134 static void wait_tx_fifo(const dif_spi_host_t *spi_host) {
135  uint32_t txqd;
136  do {
137  uint32_t reg =
138  mmio_region_read32(spi_host->base_addr, SPI_HOST_STATUS_REG_OFFSET);
139  txqd = bitfield_field32_read(reg, SPI_HOST_STATUS_TXQD_FIELD);
140  } while (txqd == SPI_HOST_PARAM_TX_DEPTH);
141 }
142 
143 static void wait_rx_fifo(const dif_spi_host_t *spi_host) {
144  uint32_t rxqd;
145  do {
146  uint32_t reg =
147  mmio_region_read32(spi_host->base_addr, SPI_HOST_STATUS_REG_OFFSET);
148  rxqd = bitfield_field32_read(reg, SPI_HOST_STATUS_RXQD_FIELD);
149  } while (rxqd == 0);
150 }
151 
152 static inline void tx_fifo_write8(const dif_spi_host_t *spi_host,
153  uintptr_t srcaddr) {
154  uint8_t *src = (uint8_t *)srcaddr;
155  wait_tx_fifo(spi_host);
156  mmio_region_write8(spi_host->base_addr, SPI_HOST_TXDATA_REG_OFFSET, *src);
157 }
158 
159 static inline void tx_fifo_write32(const dif_spi_host_t *spi_host,
160  uintptr_t srcaddr) {
161  wait_tx_fifo(spi_host);
162  uint32_t val = read_32((const void *)srcaddr);
163  mmio_region_write32(spi_host->base_addr, SPI_HOST_TXDATA_REG_OFFSET, val);
164 }
165 
166 dif_result_t dif_spi_host_fifo_write(const dif_spi_host_t *spi_host,
167  const void *src, uint16_t len) {
168  uintptr_t ptr = (uintptr_t)src;
169  if (spi_host == NULL || (src == NULL && len > 0)) {
170  return kDifBadArg;
171  }
172 
173  // If the pointer starts mis-aligned, write until we are aligned.
174  while (misalignment32_of(ptr) && len > 0) {
175  tx_fifo_write8(spi_host, ptr);
176  ptr += 1;
177  len -= 1;
178  }
179 
180  // Write complete 32-bit words to the fifo.
181  while (len > 3) {
182  tx_fifo_write32(spi_host, ptr);
183  ptr += 4;
184  len -= 4;
185  }
186 
187  // Clean up any leftover bytes.
188  while (len > 0) {
189  tx_fifo_write8(spi_host, ptr);
190  ptr += 1;
191  len -= 1;
192  }
193 
194  return kDifOk;
195 }
196 
197 typedef struct queue {
198  int32_t length;
199  uint8_t alignas(uint64_t) data[8];
200 } queue_t;
201 
202 static void enqueue_byte(queue_t *queue, uint8_t data) {
203  queue->data[queue->length++] = data;
204 }
205 
206 static void enqueue_word(queue_t *queue, uint32_t data) {
207  if (queue->length % (int32_t)sizeof(uint32_t) == 0) {
208  write_32(data, queue->data + queue->length);
209  queue->length += 4;
210  } else {
211  for (size_t i = 0; i < sizeof(uint32_t); ++i) {
212  enqueue_byte(queue, (uint8_t)data);
213  data >>= 8;
214  }
215  }
216 }
217 
218 static uint8_t dequeue_byte(queue_t *queue) {
219  uint8_t val = queue->data[0];
220  uint64_t qword = read_64(queue->data);
221  write_64(qword >> 8, queue->data);
222  queue->length -= 1;
223  return val;
224 }
225 
226 static uint32_t dequeue_word(queue_t *queue) {
227  uint32_t val = read_32(queue->data);
228  write_32(read_32(queue->data + sizeof(uint32_t)), queue->data);
229  queue->length -= 4;
230  return val;
231 }
232 
233 dif_result_t dif_spi_host_fifo_read(const dif_spi_host_t *spi_host, void *dst,
234  uint16_t len) {
235  if (spi_host == NULL || (dst == NULL && len > 0)) {
236  return kDifBadArg;
237  }
238 
239  uintptr_t ptr = (uintptr_t)dst;
240  // We always have to read from the RXFIFO as a 32-bit word. We use a
241  // two-word queue to handle destination and length mis-alignments.
242  queue_t queue = {0};
243 
244  // If the buffer is misaligned, write a byte at a time until we reach
245  // alignment.
246  while (misalignment32_of(ptr) && len > 0) {
247  if (queue.length < 1) {
248  wait_rx_fifo(spi_host);
249  enqueue_word(&queue, mmio_region_read32(spi_host->base_addr,
250  SPI_HOST_RXDATA_REG_OFFSET));
251  }
252  uint8_t *p = (uint8_t *)ptr;
253  *p = dequeue_byte(&queue);
254  ptr += 1;
255  len -= 1;
256  }
257 
258  // While we can write complete words to memory, operate on 4 bytes at a time.
259  while (len > 3) {
260  if (queue.length < 4) {
261  wait_rx_fifo(spi_host);
262  enqueue_word(&queue, mmio_region_read32(spi_host->base_addr,
263  SPI_HOST_RXDATA_REG_OFFSET));
264  }
265  write_32(dequeue_word(&queue), (void *)ptr);
266  ptr += 4;
267  len -= 4;
268  }
269 
270  // Finish up any left over buffer a byte at a time.
271  while (len > 0) {
272  if (queue.length < 1) {
273  wait_rx_fifo(spi_host);
274  enqueue_word(&queue, mmio_region_read32(spi_host->base_addr,
275  SPI_HOST_RXDATA_REG_OFFSET));
276  }
277  uint8_t *p = (uint8_t *)ptr;
278  *p = dequeue_byte(&queue);
279  ptr += 1;
280  len -= 1;
281  }
282 
283  return kDifOk;
284 }
285 
286 static void write_command_reg(const dif_spi_host_t *spi_host, uint16_t length,
287  dif_spi_host_width_t speed,
288  dif_spi_host_direction_t direction,
289  bool last_segment) {
290  uint32_t reg = 0;
291  reg = bitfield_field32_write(reg, SPI_HOST_COMMAND_LEN_FIELD, length - 1);
292  reg = bitfield_field32_write(reg, SPI_HOST_COMMAND_SPEED_FIELD, speed);
293  reg =
294  bitfield_field32_write(reg, SPI_HOST_COMMAND_DIRECTION_FIELD, direction);
295  reg = bitfield_bit32_write(reg, SPI_HOST_COMMAND_CSAAT_BIT, !last_segment);
296  mmio_region_write32(spi_host->base_addr, SPI_HOST_COMMAND_REG_OFFSET, reg);
297 }
298 
299 static void issue_opcode(const dif_spi_host_t *spi_host,
300  dif_spi_host_segment_t *segment, bool last_segment) {
301  wait_tx_fifo(spi_host);
302  mmio_region_write8(spi_host->base_addr, SPI_HOST_TXDATA_REG_OFFSET,
303  segment->opcode.opcode);
304  write_command_reg(spi_host, 1, segment->opcode.width, kDifSpiHostDirectionTx,
305  last_segment);
306 }
307 
308 static void issue_address(const dif_spi_host_t *spi_host,
309  dif_spi_host_segment_t *segment, bool last_segment) {
310  wait_tx_fifo(spi_host);
311  // The address appears on the wire in big-endian order.
312  uint32_t address = bitfield_byteswap32(segment->address.address);
313  uint16_t length;
314  if (segment->address.mode == kDifSpiHostAddrMode4b) {
315  length = 4;
316  mmio_region_write32(spi_host->base_addr, SPI_HOST_TXDATA_REG_OFFSET,
317  address);
318  } else {
319  length = 3;
320  address >>= 8;
321  mmio_region_write32(spi_host->base_addr, SPI_HOST_TXDATA_REG_OFFSET,
322  address);
323  }
324  write_command_reg(spi_host, length, segment->address.width,
325  kDifSpiHostDirectionTx, last_segment);
326 }
327 
328 static void issue_dummy(const dif_spi_host_t *spi_host,
329  dif_spi_host_segment_t *segment, bool last_segment) {
330  if (segment->dummy.length > 0) {
331  // We only want to program a dummy segment if the number of cycles is
332  // greater than zero. Programming a zero to the hardware results in a
333  // dummy segment of 512 bits.
334  write_command_reg(spi_host, (uint16_t)segment->dummy.length,
335  segment->dummy.width, kDifSpiHostDirectionDummy,
336  last_segment);
337  }
338 }
339 
340 static dif_result_t issue_data_phase(const dif_spi_host_t *spi_host,
341  dif_spi_host_segment_t *segment,
342  bool last_segment) {
343  switch (segment->type) {
345  write_command_reg(spi_host, (uint16_t)segment->tx.length,
346  segment->tx.width, kDifSpiHostDirectionTx,
347  last_segment);
348  spi_host_fifo_write_alias(spi_host, segment->tx.buf,
349  (uint16_t)segment->tx.length);
350  break;
352  write_command_reg(spi_host, (uint16_t)segment->bidir.length,
353  segment->bidir.width, kDifSpiHostDirectionBidirectional,
354  last_segment);
355  spi_host_fifo_write_alias(spi_host, segment->bidir.txbuf,
356  (uint16_t)segment->bidir.length);
357  break;
359  write_command_reg(spi_host, (uint16_t)segment->rx.length,
360  segment->rx.width, kDifSpiHostDirectionRx,
361  last_segment);
362  break;
363  default:
364  // Programming error (within this file). We should never get here.
365  // `issue_data_phase` should only get called for segment types which
366  // represent a data transfer.
367  return kDifBadArg;
368  }
369  return kDifOk;
370 }
371 
372 dif_result_t dif_spi_host_start_transaction(const dif_spi_host_t *spi_host,
373  uint32_t csid,
374  dif_spi_host_segment_t *segments,
375  size_t length) {
376  if (spi_host == NULL || segments == NULL) {
377  return kDifBadArg;
378  }
379 
380  // Write to chip select ID.
381  mmio_region_write32(spi_host->base_addr, SPI_HOST_CSID_REG_OFFSET, csid);
382 
383  // For each segment, write the segment information to the
384  // COMMAND register and transmit FIFO.
385  for (size_t i = 0; i < length; ++i) {
386  bool last_segment = i == length - 1;
387  wait_ready(spi_host);
388  dif_spi_host_segment_t *segment = &segments[i];
389  switch (segment->type) {
391  issue_opcode(spi_host, segment, last_segment);
392  break;
394  issue_address(spi_host, segment, last_segment);
395  break;
397  issue_dummy(spi_host, segment, last_segment);
398  break;
402  dif_result_t error = issue_data_phase(spi_host, segment, last_segment);
403  if (error != kDifOk) {
404  return error;
405  }
406  break;
407  }
408  default:
409  return kDifBadArg;
410  }
411  }
412  return kDifOk;
413 }
414 
415 dif_result_t dif_spi_host_transaction(const dif_spi_host_t *spi_host,
416  uint32_t csid,
417  dif_spi_host_segment_t *segments,
418  size_t length) {
420  dif_spi_host_start_transaction(spi_host, csid, segments, length));
421 
422  // For each segment which receives data, read from the receive FIFO.
423  for (size_t i = 0; i < length; ++i) {
424  dif_spi_host_segment_t *segment = &segments[i];
425  switch (segment->type) {
427  spi_host_fifo_read_alias(spi_host, segment->rx.buf,
428  (uint16_t)segment->rx.length);
429  break;
431  spi_host_fifo_read_alias(spi_host, segment->bidir.rxbuf,
432  (uint16_t)segment->bidir.length);
433  break;
434  default:
435  /* do nothing */;
436  }
437  }
438  return kDifOk;
439 }
440 
441 dif_result_t dif_spi_host_event_set_enabled(const dif_spi_host_t *spi_host,
442  dif_spi_host_events_t event,
443  bool enable) {
444  if (spi_host == NULL || (event & ~(uint32_t)kDifSpiHostEvtAll) != 0) {
445  return kDifBadArg;
446  }
447 
448  uint32_t reg =
449  mmio_region_read32(spi_host->base_addr, SPI_HOST_EVENT_ENABLE_REG_OFFSET);
450  if (enable) {
451  reg |= event;
452  } else {
453  reg &= ~event;
454  }
455  mmio_region_write32(spi_host->base_addr, SPI_HOST_EVENT_ENABLE_REG_OFFSET,
456  reg);
457  return kDifOk;
458 }
459 
460 dif_result_t dif_spi_host_event_get_enabled(const dif_spi_host_t *spi_host,
461  dif_spi_host_events_t *events) {
462  if (spi_host == NULL || events == NULL) {
463  return kDifBadArg;
464  }
465 
466  *events =
467  mmio_region_read32(spi_host->base_addr, SPI_HOST_EVENT_ENABLE_REG_OFFSET);
468  return kDifOk;
469 }
470 
471 dif_result_t dif_spi_host_get_status(const dif_spi_host_t *spi_host,
473  if (spi_host == NULL || status == NULL) {
474  return kDifBadArg;
475  }
476 
477  uint32_t reg =
478  mmio_region_read32(spi_host->base_addr, SPI_HOST_STATUS_REG_OFFSET);
479 
480  status->ready = bitfield_bit32_read(reg, SPI_HOST_STATUS_READY_BIT);
481  status->active = bitfield_bit32_read(reg, SPI_HOST_STATUS_ACTIVE_BIT);
482  status->tx_empty = bitfield_bit32_read(reg, SPI_HOST_STATUS_TXEMPTY_BIT);
483  status->rx_empty = bitfield_bit32_read(reg, SPI_HOST_STATUS_RXEMPTY_BIT);
484  status->tx_full = bitfield_bit32_read(reg, SPI_HOST_STATUS_TXFULL_BIT);
485  status->rx_full = bitfield_bit32_read(reg, SPI_HOST_STATUS_RXFULL_BIT);
486  status->tx_water_mark = bitfield_bit32_read(reg, SPI_HOST_STATUS_TXWM_BIT);
487  status->rx_water_mark = bitfield_bit32_read(reg, SPI_HOST_STATUS_RXWM_BIT);
488  status->tx_stall = bitfield_bit32_read(reg, SPI_HOST_STATUS_TXSTALL_BIT);
489  status->rx_stall = bitfield_bit32_read(reg, SPI_HOST_STATUS_RXSTALL_BIT);
490  status->least_significant_first =
491  bitfield_bit32_read(reg, SPI_HOST_STATUS_BYTEORDER_BIT);
492  status->tx_queue_depth =
493  bitfield_field32_read(reg, SPI_HOST_STATUS_TXQD_FIELD);
494  status->rx_queue_depth =
495  bitfield_field32_read(reg, SPI_HOST_STATUS_RXQD_FIELD);
496  status->cmd_queue_depth =
497  bitfield_field32_read(reg, SPI_HOST_STATUS_CMDQD_FIELD);
498 
499  return kDifOk;
500 }
501 
502 dif_result_t dif_spi_host_write_command(const dif_spi_host_t *spi_host,
503  uint16_t length,
504  dif_spi_host_width_t speed,
505  dif_spi_host_direction_t direction,
506  bool last_segment) {
507  if (spi_host == NULL) {
508  return kDifBadArg;
509  }
510  write_command_reg(spi_host, length, speed, direction, last_segment);
511  return kDifOk;
512 }
513 
514 dif_result_t dif_spi_host_error_set_enabled(const dif_spi_host_t *spi_host,
515  dif_spi_host_errors_t error,
516  bool enable) {
517  if (spi_host == NULL || (error & ~(uint32_t)kDifSpiHostIrqErrorAll) != 0) {
518  return kDifBadArg;
519  }
520 
521  uint32_t reg =
522  mmio_region_read32(spi_host->base_addr, SPI_HOST_ERROR_ENABLE_REG_OFFSET);
523  if (enable) {
524  reg |= error;
525  } else {
526  reg &= ~error;
527  }
528  mmio_region_write32(spi_host->base_addr, SPI_HOST_ERROR_ENABLE_REG_OFFSET,
529  reg);
530  return kDifOk;
531 }
532 
533 dif_result_t dif_spi_host_error_get_enabled(const dif_spi_host_t *spi_host,
534  dif_spi_host_errors_t *errors) {
535  if (spi_host == NULL || errors == NULL) {
536  return kDifBadArg;
537  }
538 
539  *errors =
540  mmio_region_read32(spi_host->base_addr, SPI_HOST_ERROR_ENABLE_REG_OFFSET);
541  return kDifOk;
542 }
543 
544 dif_result_t dif_spi_host_get_error(const dif_spi_host_t *spi_host,
545  dif_spi_host_errors_t *error) {
546  if (spi_host == NULL || error == NULL) {
547  return kDifBadArg;
548  }
549 
550  *error =
551  mmio_region_read32(spi_host->base_addr, SPI_HOST_ERROR_STATUS_REG_OFFSET);
552 
553  return kDifOk;
554 }
555 
556 dif_result_t dif_spi_host_wait_until_idle(const dif_spi_host_t *spi_host) {
557  if (spi_host == NULL) {
558  return kDifBadArg;
559  }
560 
561  bool active;
562  do {
563  uint32_t reg =
564  mmio_region_read32(spi_host->base_addr, SPI_HOST_STATUS_REG_OFFSET);
565  active = bitfield_bit32_read(reg, SPI_HOST_STATUS_ACTIVE_BIT);
566  } while (active);
567 
568  return kDifOk;
569 }