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_transaction(const dif_spi_host_t *spi_host,
373  uint32_t csid,
374  dif_spi_host_segment_t *segments,
375  size_t length) {
376  // Write to chip select ID.
377  mmio_region_write32(spi_host->base_addr, SPI_HOST_CSID_REG_OFFSET, csid);
378 
379  // For each segment, write the segment information to the
380  // COMMAND register and transmit FIFO.
381  for (size_t i = 0; i < length; ++i) {
382  bool last_segment = i == length - 1;
383  wait_ready(spi_host);
384  dif_spi_host_segment_t *segment = &segments[i];
385  switch (segment->type) {
387  issue_opcode(spi_host, segment, last_segment);
388  break;
390  issue_address(spi_host, segment, last_segment);
391  break;
393  issue_dummy(spi_host, segment, last_segment);
394  break;
398  dif_result_t error = issue_data_phase(spi_host, segment, last_segment);
399  if (error != kDifOk) {
400  return error;
401  }
402  break;
403  }
404  default:
405  return kDifBadArg;
406  }
407  }
408 
409  // For each segment which receives data, read from the receive FIFO.
410  for (size_t i = 0; i < length; ++i) {
411  dif_spi_host_segment_t *segment = &segments[i];
412  switch (segment->type) {
414  spi_host_fifo_read_alias(spi_host, segment->rx.buf,
415  (uint16_t)segment->rx.length);
416  break;
418  spi_host_fifo_read_alias(spi_host, segment->bidir.rxbuf,
419  (uint16_t)segment->bidir.length);
420  break;
421  default:
422  /* do nothing */;
423  }
424  }
425  return kDifOk;
426 }
427 
428 dif_result_t dif_spi_host_event_set_enabled(const dif_spi_host_t *spi_host,
429  dif_spi_host_events_t event,
430  bool enable) {
431  if (spi_host == NULL || (event & ~(uint32_t)kDifSpiHostEvtAll) != 0) {
432  return kDifBadArg;
433  }
434 
435  uint32_t reg =
436  mmio_region_read32(spi_host->base_addr, SPI_HOST_EVENT_ENABLE_REG_OFFSET);
437  if (enable) {
438  reg |= event;
439  } else {
440  reg &= ~event;
441  }
442  mmio_region_write32(spi_host->base_addr, SPI_HOST_EVENT_ENABLE_REG_OFFSET,
443  reg);
444  return kDifOk;
445 }
446 
447 dif_result_t dif_spi_host_event_get_enabled(const dif_spi_host_t *spi_host,
448  dif_spi_host_events_t *events) {
449  if (spi_host == NULL || events == NULL) {
450  return kDifBadArg;
451  }
452 
453  *events =
454  mmio_region_read32(spi_host->base_addr, SPI_HOST_EVENT_ENABLE_REG_OFFSET);
455  return kDifOk;
456 }
457 
458 dif_result_t dif_spi_host_get_status(const dif_spi_host_t *spi_host,
460  if (spi_host == NULL || status == NULL) {
461  return kDifBadArg;
462  }
463 
464  uint32_t reg =
465  mmio_region_read32(spi_host->base_addr, SPI_HOST_STATUS_REG_OFFSET);
466 
467  status->ready = bitfield_bit32_read(reg, SPI_HOST_STATUS_READY_BIT);
468  status->active = bitfield_bit32_read(reg, SPI_HOST_STATUS_ACTIVE_BIT);
469  status->tx_empty = bitfield_bit32_read(reg, SPI_HOST_STATUS_TXEMPTY_BIT);
470  status->rx_empty = bitfield_bit32_read(reg, SPI_HOST_STATUS_RXEMPTY_BIT);
471  status->tx_full = bitfield_bit32_read(reg, SPI_HOST_STATUS_TXFULL_BIT);
472  status->rx_full = bitfield_bit32_read(reg, SPI_HOST_STATUS_RXFULL_BIT);
473  status->tx_water_mark = bitfield_bit32_read(reg, SPI_HOST_STATUS_TXWM_BIT);
474  status->rx_water_mark = bitfield_bit32_read(reg, SPI_HOST_STATUS_RXWM_BIT);
475  status->tx_stall = bitfield_bit32_read(reg, SPI_HOST_STATUS_TXSTALL_BIT);
476  status->rx_stall = bitfield_bit32_read(reg, SPI_HOST_STATUS_RXSTALL_BIT);
477  status->least_significant_first =
478  bitfield_bit32_read(reg, SPI_HOST_STATUS_BYTEORDER_BIT);
479  status->tx_queue_depth =
480  bitfield_field32_read(reg, SPI_HOST_STATUS_TXQD_FIELD);
481  status->rx_queue_depth =
482  bitfield_field32_read(reg, SPI_HOST_STATUS_RXQD_FIELD);
483  status->cmd_queue_depth =
484  bitfield_field32_read(reg, SPI_HOST_STATUS_CMDQD_FIELD);
485 
486  return kDifOk;
487 }
488 
489 dif_result_t dif_spi_host_write_command(const dif_spi_host_t *spi_host,
490  uint16_t length,
491  dif_spi_host_width_t speed,
492  dif_spi_host_direction_t direction,
493  bool last_segment) {
494  if (spi_host == NULL) {
495  return kDifBadArg;
496  }
497  write_command_reg(spi_host, length, speed, direction, last_segment);
498  return kDifOk;
499 }
500 
501 dif_result_t dif_spi_host_error_set_enabled(const dif_spi_host_t *spi_host,
502  dif_spi_host_errors_t error,
503  bool enable) {
504  if (spi_host == NULL || (error & ~(uint32_t)kDifSpiHostIrqErrorAll) != 0) {
505  return kDifBadArg;
506  }
507 
508  uint32_t reg =
509  mmio_region_read32(spi_host->base_addr, SPI_HOST_ERROR_ENABLE_REG_OFFSET);
510  if (enable) {
511  reg |= error;
512  } else {
513  reg &= ~error;
514  }
515  mmio_region_write32(spi_host->base_addr, SPI_HOST_ERROR_ENABLE_REG_OFFSET,
516  reg);
517  return kDifOk;
518 }
519 
520 dif_result_t dif_spi_host_error_get_enabled(const dif_spi_host_t *spi_host,
521  dif_spi_host_errors_t *errors) {
522  if (spi_host == NULL || errors == NULL) {
523  return kDifBadArg;
524  }
525 
526  *errors =
527  mmio_region_read32(spi_host->base_addr, SPI_HOST_ERROR_ENABLE_REG_OFFSET);
528  return kDifOk;
529 }
530 
531 dif_result_t dif_spi_host_get_error(const dif_spi_host_t *spi_host,
532  dif_spi_host_errors_t *error) {
533  if (spi_host == NULL || error == NULL) {
534  return kDifBadArg;
535  }
536 
537  *error =
538  mmio_region_read32(spi_host->base_addr, SPI_HOST_ERROR_STATUS_REG_OFFSET);
539 
540  return kDifOk;
541 }
542 
543 dif_result_t dif_spi_host_wait_until_idle(const dif_spi_host_t *spi_host) {
544  if (spi_host == NULL) {
545  return kDifBadArg;
546  }
547 
548  bool active;
549  do {
550  uint32_t reg =
551  mmio_region_read32(spi_host->base_addr, SPI_HOST_STATUS_REG_OFFSET);
552  active = bitfield_bit32_read(reg, SPI_HOST_STATUS_ACTIVE_BIT);
553  } while (active);
554 
555  return kDifOk;
556 }