Software APIs
dif_spi_device.c
1 // Copyright lowRISC contributors.
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
6 
10 
11 #include "spi_device_regs.h" // Generated.
12 
13 const uint16_t kDifSpiDeviceBufferLen = SPI_DEVICE_BUFFER_SIZE_BYTES;
14 
15 /**
16  * Computes the required value of the control register from a given
17  * configuration.
18  */
19 static uint32_t build_control_word(const dif_spi_device_config_t *config) {
20  uint32_t val = 0;
21 
22  val =
23  bitfield_bit32_write(val, SPI_DEVICE_CFG_CPOL_BIT,
24  config->clock_polarity == kDifSpiDeviceEdgeNegative);
25  val = bitfield_bit32_write(val, SPI_DEVICE_CFG_CPHA_BIT,
26  config->data_phase == kDifSpiDeviceEdgePositive);
27  val = bitfield_bit32_write(val, SPI_DEVICE_CFG_TX_ORDER_BIT,
28  config->tx_order == kDifSpiDeviceBitOrderLsbToMsb);
29  val = bitfield_bit32_write(val, SPI_DEVICE_CFG_RX_ORDER_BIT,
30  config->rx_order == kDifSpiDeviceBitOrderLsbToMsb);
31  val = bitfield_field32_write(val, SPI_DEVICE_CFG_TIMER_V_FIELD,
32  config->rx_fifo_timeout);
33 
34  return val;
35 }
36 
38  const dif_spi_device_config_t *config) {
39  if (spi == NULL || config == NULL) {
40  return kDifBadArg;
41  }
42 
43  // NOTE: we do not write to any registers until performing all
44  // function argument checks, to avoid a halfway-configured SPI.
45 
46  uint32_t device_config = build_control_word(config);
47 
48  uint16_t rx_fifo_start = 0x0;
49  uint16_t rx_fifo_end = config->rx_fifo_len - 1;
50  uint16_t tx_fifo_start = rx_fifo_end + 1;
51  uint16_t tx_fifo_end = tx_fifo_start + config->tx_fifo_len - 1;
52  if (tx_fifo_end >= kDifSpiDeviceBufferLen) {
53  // We've overflown the SRAM region...
54  return kDifBadArg;
55  }
56 
57  uint32_t rx_fifo_bounds = 0;
58  rx_fifo_bounds = bitfield_field32_write(
59  rx_fifo_bounds, SPI_DEVICE_RXF_ADDR_BASE_FIELD, rx_fifo_start);
60  rx_fifo_bounds = bitfield_field32_write(
61  rx_fifo_bounds, SPI_DEVICE_RXF_ADDR_LIMIT_FIELD, rx_fifo_end);
62 
63  uint32_t tx_fifo_bounds = 0;
64  tx_fifo_bounds = bitfield_field32_write(
65  tx_fifo_bounds, SPI_DEVICE_TXF_ADDR_BASE_FIELD, tx_fifo_start);
66  tx_fifo_bounds = bitfield_field32_write(
67  tx_fifo_bounds, SPI_DEVICE_TXF_ADDR_LIMIT_FIELD, tx_fifo_end);
68 
69  mmio_region_write32(spi->base_addr, SPI_DEVICE_CFG_REG_OFFSET, device_config);
70  mmio_region_write32(spi->base_addr, SPI_DEVICE_RXF_ADDR_REG_OFFSET,
71  rx_fifo_bounds);
72  mmio_region_write32(spi->base_addr, SPI_DEVICE_TXF_ADDR_REG_OFFSET,
73  tx_fifo_bounds);
74 
75  return kDifOk;
76 }
77 
79  if (spi == NULL) {
80  return kDifBadArg;
81  }
82 
83  // Set the `abort` bit, and then spin until `abort_done` is asserted.
84  uint32_t reg =
85  mmio_region_read32(spi->base_addr, SPI_DEVICE_CONTROL_REG_OFFSET);
86  reg = bitfield_bit32_write(reg, SPI_DEVICE_CONTROL_ABORT_BIT, true);
87  mmio_region_write32(spi->base_addr, SPI_DEVICE_CONTROL_REG_OFFSET, reg);
88 
89  while (true) {
90  uint32_t reg =
91  mmio_region_read32(spi->base_addr, SPI_DEVICE_STATUS_REG_OFFSET);
92  if (bitfield_bit32_read(reg, SPI_DEVICE_STATUS_ABORT_DONE_BIT)) {
93  return kDifOk;
94  }
95  }
96 }
97 
99  uint16_t rx_level,
100  uint16_t tx_level) {
101  if (spi == NULL) {
102  return kDifBadArg;
103  }
104 
105  uint32_t compressed_limit = 0;
106  compressed_limit = bitfield_field32_write(
107  compressed_limit, SPI_DEVICE_FIFO_LEVEL_RXLVL_FIELD, rx_level);
108  compressed_limit = bitfield_field32_write(
109  compressed_limit, SPI_DEVICE_FIFO_LEVEL_TXLVL_FIELD, tx_level);
110  mmio_region_write32(spi->base_addr, SPI_DEVICE_FIFO_LEVEL_REG_OFFSET,
111  compressed_limit);
112 
113  return kDifOk;
114 }
115 
116 /**
117  * Parameters for compressing and decompressing a FIFO pointer register.
118  */
119 typedef struct fifo_ptr_params {
120  ptrdiff_t reg_offset;
121  ptrdiff_t write_offset;
122  ptrdiff_t read_offset;
123  uint32_t write_mask;
124  uint32_t read_mask;
126 
127 /**
128  * Parameters for the transmission FIFO.
129  */
130 static const fifo_ptr_params_t kTxFifoParams = {
131  .reg_offset = SPI_DEVICE_TXF_PTR_REG_OFFSET,
132  .write_offset = SPI_DEVICE_TXF_PTR_WPTR_OFFSET,
133  .write_mask = SPI_DEVICE_TXF_PTR_WPTR_MASK,
134  .read_offset = SPI_DEVICE_TXF_PTR_RPTR_OFFSET,
135  .read_mask = SPI_DEVICE_TXF_PTR_RPTR_MASK,
136 };
137 
138 /**
139  * Parameters for the receipt FIFO.
140  */
141 static const fifo_ptr_params_t kRxFifoParams = {
142  .reg_offset = SPI_DEVICE_RXF_PTR_REG_OFFSET,
143  .write_offset = SPI_DEVICE_RXF_PTR_WPTR_OFFSET,
144  .write_mask = SPI_DEVICE_RXF_PTR_WPTR_MASK,
145  .read_offset = SPI_DEVICE_RXF_PTR_RPTR_OFFSET,
146  .read_mask = SPI_DEVICE_RXF_PTR_RPTR_MASK,
147 };
148 
149 /**
150  * An exploded FIFO pointer value, consisting of a `uint11_t`
151  * offset part (an offset into a FIFO), and a `uint1_t` phase
152  * (which indicates whether this pointer has wrapped around or not).
153  *
154  * See also `fifo_ptrs_t`.
155  */
156 typedef struct fifo_ptr {
157  uint16_t offset;
158  bool phase;
159 } fifo_ptr_t;
160 
161 // Masks for extracting the phase and offset parts from a
162 // compressed FIFO pointer.
163 static const uint16_t kFifoPhaseMask = (1 << 12);
164 static const uint16_t kFifoOffsetMask = (1 << 12) - 1;
165 
166 /**
167  * Modifies a `fifo_ptr_t` into a FIFO of length `fifo_len` by
168  * incrementing it by `increment`, making sure to correctly flip the
169  * phase bit on overflow.
170  *
171  * @param ptr the pointer to increment.
172  * @param increment the amount to increment by.
173  * @param fifo_len the length of the FIFO the pointer points into.
174  */
175 static void fifo_ptr_increment(fifo_ptr_t *ptr, uint16_t increment,
176  uint16_t fifo_len) {
177  uint32_t inc_with_overflow = ptr->offset + increment;
178  // If we would overflow, wrap and flip the overflow bit.
179  if (inc_with_overflow >= fifo_len) {
180  inc_with_overflow -= fifo_len;
181  ptr->phase = !ptr->phase;
182  }
183 
184  ptr->offset = inc_with_overflow & kFifoOffsetMask;
185 }
186 
187 /**
188  * A decompressed FIFO pointer register, consisting of a read offset
189  * and a write offset within the FIFO region.
190  *
191  * The offsets themselves are only `uint11_t`, with an additional
192  * 12th "phase" bit used for detecting the wrap around behavior of
193  * the ring buffer FIFOs.
194  */
195 typedef struct fifo_ptrs {
196  fifo_ptr_t write_ptr;
197  fifo_ptr_t read_ptr;
198 } fifo_ptrs_t;
199 
200 /**
201  * Expands read and write FIFO pointers out of `spi`, using the given FIFO
202  * parameters.
203  *
204  * @param spi the SPI device.
205  * @param params bitfield parameters for the FIFO.
206  * @return expanded pointers read out of `spi`.
207  */
208 static fifo_ptrs_t decompress_ptrs(const dif_spi_device_t *spi,
209  fifo_ptr_params_t params) {
210  uint32_t ptr = mmio_region_read32(spi->base_addr, params.reg_offset);
211  uint16_t write_val =
212  (uint16_t)((ptr >> params.write_offset) & params.write_mask);
213  uint16_t read_val =
214  (uint16_t)((ptr >> params.read_offset) & params.read_mask);
215  return (fifo_ptrs_t){
216  .write_ptr =
217  {
218  .offset = write_val & kFifoOffsetMask,
219  .phase = (write_val & kFifoPhaseMask) != 0,
220  },
221  .read_ptr =
222  {
223  .offset = read_val & kFifoOffsetMask,
224  .phase = (read_val & kFifoPhaseMask) != 0,
225  },
226  };
227 }
228 
229 /**
230  * Writes back read and write FIFO pointers into `spi`, using the given FIFO
231  * parameters.
232  *
233  * @param spi the SPI device.
234  * @param params bitfield parameters for the FIFO.
235  * @param ptrs the new pointer values.
236  */
237 static void compress_ptrs(const dif_spi_device_t *spi, fifo_ptr_params_t params,
238  fifo_ptrs_t ptrs) {
239  uint16_t write_val = ptrs.write_ptr.offset;
240  if (ptrs.write_ptr.phase) {
241  write_val |= kFifoPhaseMask;
242  }
243  uint16_t read_val = ptrs.read_ptr.offset;
244  if (ptrs.read_ptr.phase) {
245  read_val |= kFifoPhaseMask;
246  }
247 
248  uint32_t ptr = 0;
249  ptr = bitfield_field32_write(ptr,
251  .mask = params.write_mask,
252  .index = params.write_offset,
253  },
254  write_val);
255  ptr = bitfield_field32_write(ptr,
257  .mask = params.read_mask,
258  .index = params.read_offset,
259  },
260  read_val);
261  mmio_region_write32(spi->base_addr, params.reg_offset, ptr);
262 }
263 
264 /**
265  * Counts the number of bytes from the read pointer to the write pointer in
266  * `ptrs`, in a FIFO of length `fifo_len`.
267  *
268  * @param ptrs a set of FIFO pointers.
269  * @param fifo_len the length of the fifo, in bytes.
270  * @return the number of bytes "in use".
271  */
272 static uint16_t fifo_bytes_in_use(fifo_ptrs_t ptrs, uint16_t fifo_len) {
273  // This represents the case where the valid data of the fifo is "inclusive",
274  // i.e., the buffer looks like (where a / represents valid data):
275  // [ ///// ]
276  // ^ ^
277  // r w
278  //
279  // In particular, when r == w, the fifo is empty.
280  if (ptrs.write_ptr.phase == ptrs.read_ptr.phase) {
281  return ptrs.write_ptr.offset - ptrs.read_ptr.offset;
282  }
283 
284  // This represents the case where the valid data of the fifo is "exclusive",
285  // i.e., the buffer looks like (where a / represents valid data):
286  // [/ //////]
287  // ^ ^
288  // w r
289  //
290  // In particular, when r == w, the fifo is full.
291  return fifo_len - (ptrs.read_ptr.offset - ptrs.write_ptr.offset);
292 }
293 
295  const dif_spi_device_config_t *config,
296  size_t *bytes_pending) {
297  if (spi == NULL || config == NULL || bytes_pending == NULL) {
298  return kDifBadArg;
299  }
300 
301  fifo_ptrs_t ptrs = decompress_ptrs(spi, kRxFifoParams);
302  *bytes_pending = fifo_bytes_in_use(ptrs, config->rx_fifo_len);
303 
304  return kDifOk;
305 }
306 
308  const dif_spi_device_config_t *config,
309  size_t *bytes_pending) {
310  if (spi == NULL || config == NULL || bytes_pending == NULL) {
311  return kDifBadArg;
312  }
313 
314  fifo_ptrs_t ptrs = decompress_ptrs(spi, kTxFifoParams);
315  *bytes_pending = fifo_bytes_in_use(ptrs, config->tx_fifo_len);
316 
317  return kDifOk;
318 }
319 
320 /**
321  * Performs a "memcpy" of sorts between a main memory buffer and SPI SRAM,
322  * which does not support non-word I/O.
323  *
324  * If `is_recv` is set, then the copy direction is `spi -> buf`. If it is
325  * unset, the copy direction is `buf -> spi`.
326  *
327  * @param spi a SPI device.
328  * @param fifo a decompressed FIFO pointer pair.
329  * @param fifo_base the offset from start of SRAM for the FIFO to copy to/from.
330  * @param fifo_len the length of the FIFO, in bytes.
331  * @param byte_buf a main memory buffer for copying from/to.
332  * @param buf_len the length of the main memory buffer.
333  * @param is_recv whether this is a SPI reciept or SPI transmit transaction.
334  * @return the number of bytes copied.
335  */
336 static size_t spi_memcpy(const dif_spi_device_t *spi, fifo_ptrs_t *fifo,
337  uint16_t fifo_base, uint16_t fifo_len,
338  uint8_t *byte_buf, size_t buf_len, bool is_recv) {
339  uint16_t bytes_left = fifo_bytes_in_use(*fifo, fifo_len);
340  // When sending, the bytes left are the empty space still available.
341  if (!is_recv) {
342  bytes_left = fifo_len - bytes_left;
343  }
344 
345  if (bytes_left > buf_len) {
346  bytes_left = buf_len;
347  }
348  if (bytes_left == 0) {
349  return 0;
350  }
351  const uint16_t total_bytes = bytes_left;
352 
353  // For receipt, we advance the read pointer, which indicates how far ahead
354  // we've read so far. For sending, we advance the write pointer, which
355  // indicates how far ahead we've written.
356  fifo_ptr_t *ptr;
357  if (is_recv) {
358  ptr = &fifo->read_ptr;
359  } else {
360  ptr = &fifo->write_ptr;
361  }
362 
363  // `mmio_region_memcpy_*_mmio32` functions assume sequential memory access
364  // while the SPI device uses a circular buffer. Therefore, we split the copy
365  // operation into chunks that access the device buffer sequentially.
366  while (bytes_left > 0) {
367  const uint32_t mmio_offset =
368  SPI_DEVICE_BUFFER_REG_OFFSET + fifo_base + ptr->offset;
369  const uint32_t bytes_until_wrap = fifo_len - ptr->offset;
370  uint16_t bytes_to_copy = bytes_left;
371  if (bytes_to_copy > bytes_until_wrap) {
372  bytes_to_copy = bytes_until_wrap;
373  }
374  if (is_recv) {
375  // SPI device buffer -> `byte_buf`
376  mmio_region_memcpy_from_mmio32(spi->base_addr, mmio_offset, byte_buf,
377  bytes_to_copy);
378  } else {
379  // `byte_buf` -> SPI device buffer
380  mmio_region_memcpy_to_mmio32(spi->base_addr, mmio_offset, byte_buf,
381  bytes_to_copy);
382  }
383  fifo_ptr_increment(ptr, bytes_to_copy, fifo_len);
384  byte_buf += bytes_to_copy;
385  bytes_left -= bytes_to_copy;
386  }
387 
388  return total_bytes;
389 }
390 
392  const dif_spi_device_config_t *config,
393  void *buf, size_t buf_len,
394  size_t *bytes_received) {
395  if (spi == NULL || config == NULL || buf == NULL) {
396  return kDifBadArg;
397  }
398 
399  uint16_t fifo_base = 0;
400  fifo_ptrs_t fifo = decompress_ptrs(spi, kRxFifoParams);
401 
402  size_t bytes = spi_memcpy(spi, &fifo, fifo_base, config->rx_fifo_len,
403  (uint8_t *)buf, buf_len, /*is_recv=*/true);
404  if (bytes_received != NULL) {
405  *bytes_received = bytes;
406  }
407  if (bytes > 0) {
408  // Commit the new RX FIFO pointers.
409  compress_ptrs(spi, kRxFifoParams, fifo);
410  }
411  return kDifOk;
412 }
413 
415  const dif_spi_device_config_t *config,
416  const void *buf, size_t buf_len,
417  size_t *bytes_sent) {
418  if (spi == NULL || config == NULL || buf == NULL) {
419  return kDifBadArg;
420  }
421 
422  // Start of the TX FIFO is the end of the RX FIFO.
423  fifo_ptrs_t fifo = decompress_ptrs(spi, kTxFifoParams);
424 
425  size_t bytes =
426  spi_memcpy(spi, &fifo, config->rx_fifo_len, config->tx_fifo_len,
427  (uint8_t *)buf, buf_len, /*is_recv=*/false);
428  if (bytes_sent != NULL) {
429  *bytes_sent = bytes;
430  }
431  if (bytes > 0) {
432  // Commit the new TX FIFO pointers.
433  compress_ptrs(spi, kTxFifoParams, fifo);
434  }
435  return kDifOk;
436 }