Software APIs
spi_device_testutils.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 
5 #include "sw/device/lib/testing/spi_device_testutils.h"
6 
7 #include "dt/dt_spi_device.h"
9 #include "sw/device/lib/testing/test_framework/check.h"
10 
11 #define MODULE_ID MAKE_MODULE_ID('s', 'd', 't')
12 
13 /*
14  * The SPI device pins for which `spi_device_testutils_configure_pad_attrs()`
15  * configures the pad attributes.
16  */
17 static const dt_pad_t kSpiDeviceDirectPads[4] = {
18  kDtPadSpiDeviceSd3, // sio[3]
19  kDtPadSpiDeviceSd2, // sio[2]
20  kDtPadSpiDeviceSd1, // sio[1]
21  kDtPadSpiDeviceSd0 // sio[0]
22 };
23 
24 status_t spi_device_testutils_configure_passthrough(
25  dif_spi_device_handle_t *spi_device, uint32_t filters,
26  bool upload_write_commands) {
27  dif_spi_device_config_t spi_device_config = {
30  .device_mode = kDifSpiDeviceModePassthrough,
31  };
32  TRY(dif_spi_device_configure(spi_device, spi_device_config));
33 
35  .status = upload_write_commands,
36  .jedec_id = false,
37  .sfdp = false,
38  .mailbox = false,
39  };
41  intercept_config));
42 
43  // Set up passthrough filter to allow all commands, initially.
46 
47  dif_spi_device_flash_command_t read_commands[] = {
48  {
49  // Slot 0: ReadStatus1
50  .opcode = kSpiDeviceFlashOpReadStatus1,
51  .address_type = kDifSpiDeviceFlashAddrDisabled,
52  .dummy_cycles = 0,
53  .payload_io_type = kDifSpiDevicePayloadIoSingle,
54  .payload_dir_to_host = true,
55  },
56  {
57  // Slot 1: ReadStatus2
58  .opcode = kSpiDeviceFlashOpReadStatus2,
59  .address_type = kDifSpiDeviceFlashAddrDisabled,
60  .dummy_cycles = 0,
61  .payload_io_type = kDifSpiDevicePayloadIoSingle,
62  .payload_dir_to_host = true,
63  },
64  {
65  // Slot 2: ReadStatus3
66  .opcode = kSpiDeviceFlashOpReadStatus3,
67  .address_type = kDifSpiDeviceFlashAddrDisabled,
68  .dummy_cycles = 0,
69  .payload_io_type = kDifSpiDevicePayloadIoSingle,
70  .payload_dir_to_host = true,
71  },
72  {
73  // Slot 3: ReadJedecID
74  .opcode = kSpiDeviceFlashOpReadJedec,
75  .address_type = kDifSpiDeviceFlashAddrDisabled,
76  .dummy_cycles = 0,
77  .payload_io_type = kDifSpiDevicePayloadIoSingle,
78  .payload_dir_to_host = true,
79  },
80  {
81  // Slot 4: ReadSfdp
82  .opcode = kSpiDeviceFlashOpReadSfdp,
83  .address_type = kDifSpiDeviceFlashAddr3Byte,
84  .dummy_cycles = 8,
85  .payload_io_type = kDifSpiDevicePayloadIoSingle,
86  .payload_dir_to_host = true,
87  },
88  {
89  // Slot 5: ReadNormal
90  .opcode = kSpiDeviceFlashOpReadNormal,
91  .address_type = kDifSpiDeviceFlashAddrCfg,
92  .passthrough_swap_address = true,
93  .dummy_cycles = 0,
94  .payload_io_type = kDifSpiDevicePayloadIoSingle,
95  .payload_dir_to_host = true,
96  },
97  {
98  // Slot 6: ReadFast
99  .opcode = kSpiDeviceFlashOpReadFast,
100  .address_type = kDifSpiDeviceFlashAddrCfg,
101  .passthrough_swap_address = true,
102  .dummy_cycles = 8,
103  .payload_io_type = kDifSpiDevicePayloadIoSingle,
104  .payload_dir_to_host = true,
105  },
106  {
107  // Slot 7: ReadDual
108  .opcode = kSpiDeviceFlashOpReadDual,
109  .address_type = kDifSpiDeviceFlashAddrCfg,
110  .passthrough_swap_address = true,
111  .dummy_cycles = 8,
112  .payload_io_type = kDifSpiDevicePayloadIoDual,
113  .payload_dir_to_host = true,
114  },
115  {
116  // Slot 8: ReadQuad
117  .opcode = kSpiDeviceFlashOpReadQuad,
118  .address_type = kDifSpiDeviceFlashAddrCfg,
119  .passthrough_swap_address = true,
120  .dummy_cycles = 8,
121  .payload_io_type = kDifSpiDevicePayloadIoQuad,
122  .payload_dir_to_host = true,
123  },
124  {
125  // Slot 9: Read4b
126  .opcode = kSpiDeviceFlashOpRead4b,
127  .address_type = kDifSpiDeviceFlashAddr4Byte,
128  .passthrough_swap_address = true,
129  .dummy_cycles = 0,
130  .payload_io_type = kDifSpiDevicePayloadIoSingle,
131  .payload_dir_to_host = true,
132  },
133  {
134  // Slot 10: ReadFast4b
135  .opcode = kSpiDeviceFlashOpReadFast4b,
136  .address_type = kDifSpiDeviceFlashAddr4Byte,
137  .passthrough_swap_address = true,
138  .dummy_cycles = 8,
139  .payload_io_type = kDifSpiDevicePayloadIoSingle,
140  .payload_dir_to_host = true,
141  },
142  };
143  static_assert(ARRAYSIZE(read_commands) <= UINT8_MAX,
144  "Length of read_commands must fit in uint8_t or we must change "
145  "the type of i.");
146  for (uint8_t i = 0; i < ARRAYSIZE(read_commands); ++i) {
147  uint8_t slot = i + kSpiDeviceReadCommandSlotBase;
148  if (bitfield_bit32_read(filters, slot)) {
150  spi_device, read_commands[i].opcode, kDifToggleEnabled));
151  }
153  spi_device, slot, kDifToggleEnabled, read_commands[i]));
154  }
155  dif_spi_device_flash_command_t write_commands[] = {
156  {
157  // Slot 11: WriteStatus1
158  .opcode = kSpiDeviceFlashOpWriteStatus1,
159  .address_type = kDifSpiDeviceFlashAddrDisabled,
160  .payload_io_type = kDifSpiDevicePayloadIoSingle,
161  .payload_dir_to_host = false,
162  .upload = upload_write_commands,
163  .set_busy_status = upload_write_commands,
164  },
165  {
166  // Slot 12: WriteStatus2
167  .opcode = kSpiDeviceFlashOpWriteStatus2,
168  .address_type = kDifSpiDeviceFlashAddrDisabled,
169  .payload_io_type = kDifSpiDevicePayloadIoSingle,
170  .payload_dir_to_host = false,
171  .upload = upload_write_commands,
172  .set_busy_status = upload_write_commands,
173  },
174  {
175  // Slot 13: WriteStatus3
176  .opcode = kSpiDeviceFlashOpWriteStatus3,
177  .address_type = kDifSpiDeviceFlashAddrDisabled,
178  .payload_io_type = kDifSpiDevicePayloadIoSingle,
179  .payload_dir_to_host = false,
180  .upload = upload_write_commands,
181  .set_busy_status = upload_write_commands,
182  },
183  {
184  // Slot 14: ChipErase
185  .opcode = kSpiDeviceFlashOpChipErase,
186  .address_type = kDifSpiDeviceFlashAddrDisabled,
187  .payload_io_type = kDifSpiDevicePayloadIoNone,
188  .upload = upload_write_commands,
189  .set_busy_status = upload_write_commands,
190  },
191  {
192  // Slot 15: SectorErase
193  .opcode = kSpiDeviceFlashOpSectorErase,
194  .address_type = kDifSpiDeviceFlashAddrCfg,
195  .payload_io_type = kDifSpiDevicePayloadIoNone,
196  .upload = upload_write_commands,
197  .set_busy_status = upload_write_commands,
198  },
199  {
200  // Slot 16: BlockErase32k
201  .opcode = kSpiDeviceFlashOpBlockErase32k,
202  .address_type = kDifSpiDeviceFlashAddrCfg,
203  .payload_io_type = kDifSpiDevicePayloadIoNone,
204  .upload = upload_write_commands,
205  .set_busy_status = upload_write_commands,
206  },
207  {
208  // Slot 17: BlockErase64k
209  .opcode = kSpiDeviceFlashOpBlockErase64k,
210  .address_type = kDifSpiDeviceFlashAddrCfg,
211  .payload_io_type = kDifSpiDevicePayloadIoNone,
212  .upload = upload_write_commands,
213  .set_busy_status = upload_write_commands,
214  },
215  {
216  // Slot 18: PageProgram
217  .opcode = kSpiDeviceFlashOpPageProgram,
218  .address_type = kDifSpiDeviceFlashAddrCfg,
219  .payload_io_type = kDifSpiDevicePayloadIoSingle,
220  .payload_dir_to_host = false,
221  .upload = upload_write_commands,
222  .set_busy_status = upload_write_commands,
223  },
224  {
225  // Slot 19: SectorErase4b
226  .opcode = kSpiDeviceFlashOpSectorErase4b,
227  .address_type = kDifSpiDeviceFlashAddr4Byte,
228  .payload_io_type = kDifSpiDevicePayloadIoNone,
229  .upload = upload_write_commands,
230  .set_busy_status = upload_write_commands,
231  },
232  {
233  // Slot 20: BlockErase32k4b
234  .opcode = kSpiDeviceFlashOpBlockErase32k4b,
235  .address_type = kDifSpiDeviceFlashAddr4Byte,
236  .payload_io_type = kDifSpiDevicePayloadIoNone,
237  .upload = upload_write_commands,
238  .set_busy_status = upload_write_commands,
239  },
240  {
241  // Slot 21: BlockErase64k4b
242  .opcode = kSpiDeviceFlashOpBlockErase64k4b,
243  .address_type = kDifSpiDeviceFlashAddr4Byte,
244  .payload_io_type = kDifSpiDevicePayloadIoNone,
245  .upload = upload_write_commands,
246  .set_busy_status = upload_write_commands,
247  },
248  {
249  // Slot 22: PageProgram4b
250  .opcode = kSpiDeviceFlashOpPageProgram4b,
251  .address_type = kDifSpiDeviceFlashAddr4Byte,
252  .payload_io_type = kDifSpiDevicePayloadIoSingle,
253  .payload_dir_to_host = false,
254  .upload = upload_write_commands,
255  .set_busy_status = upload_write_commands,
256  },
257  {
258  // Slot 23: Reset
259  .opcode = kSpiDeviceFlashOpReset,
260  .address_type = kDifSpiDeviceFlashAddrDisabled,
261  .payload_io_type = kDifSpiDevicePayloadIoNone,
262  .upload = upload_write_commands,
263  .set_busy_status = upload_write_commands,
264  },
265 
266  };
267  static_assert(ARRAYSIZE(write_commands) <= UINT8_MAX,
268  "Length of write_commands must fit into uint8_t");
269  for (uint8_t i = 0; i < ARRAYSIZE(write_commands); ++i) {
270  uint8_t slot = i + (uint8_t)kSpiDeviceWriteCommandSlotBase;
271  if (bitfield_bit32_read(filters, slot) || upload_write_commands) {
273  spi_device, write_commands[i].opcode, kDifToggleEnabled));
274  }
276  spi_device, slot, kDifToggleEnabled, write_commands[i]));
277  }
278  // This configuration for these commands does not guard against misbehaved
279  // hosts. The timing of any of these commands relative to an uploaded command
280  // cannot be determined.
282  spi_device, kDifToggleEnabled, kSpiDeviceFlashOpWriteEnable));
284  spi_device, kDifToggleEnabled, kSpiDeviceFlashOpWriteDisable));
286  spi_device, kDifToggleEnabled, kSpiDeviceFlashOpEnter4bAddr));
288  kSpiDeviceFlashOpExit4bAddr));
289 
290  if (upload_write_commands) {
292  spi_device, kSpiDeviceFlashOpReadStatus1, kDifToggleEnabled));
294  spi_device, kSpiDeviceFlashOpReadStatus2, kDifToggleEnabled));
296  spi_device, kSpiDeviceFlashOpReadStatus3, kDifToggleEnabled));
298  spi_device, kSpiDeviceFlashOpWriteEnable, kDifToggleEnabled));
300  spi_device, kSpiDeviceFlashOpWriteDisable, kDifToggleEnabled));
301  }
302  return OK_STATUS();
303 }
304 
305 status_t spi_device_testutils_configure_read_pipeline(
306  dif_spi_device_handle_t *spi_device,
307  dif_spi_device_read_pipeline_mode_t dual_mode,
308  dif_spi_device_read_pipeline_mode_t quad_mode) {
309  if (spi_device == NULL || dual_mode >= kDifSpiDeviceReadPipelineModeCount ||
310  quad_mode >= kDifSpiDeviceReadPipelineModeCount) {
311  return INVALID_ARGUMENT();
312  }
313 
314  const dif_spi_device_flash_command_t dual_read_cmd = {
315  // Slot 7: ReadDual
316  .opcode = kSpiDeviceFlashOpReadDual,
317  .address_type = kDifSpiDeviceFlashAddrCfg,
318  .passthrough_swap_address = true,
319  .dummy_cycles = 8,
320  .payload_io_type = kDifSpiDevicePayloadIoDual,
321  .payload_dir_to_host = true,
322  .read_pipeline_mode = dual_mode,
323  };
324  const dif_spi_device_flash_command_t quad_read_cmd = {
325  // Slot 8: ReadQuad
326  .opcode = kSpiDeviceFlashOpReadQuad,
327  .address_type = kDifSpiDeviceFlashAddrCfg,
328  .passthrough_swap_address = true,
329  .dummy_cycles = 8,
330  .payload_io_type = kDifSpiDevicePayloadIoQuad,
331  .payload_dir_to_host = true,
332  .read_pipeline_mode = quad_mode,
333  };
334  TRY(dif_spi_device_set_flash_command_slot(spi_device, /*slot=*/7,
335  kDifToggleEnabled, dual_read_cmd));
336  TRY(dif_spi_device_set_flash_command_slot(spi_device, /*slot=*/8,
337  kDifToggleEnabled, quad_read_cmd));
338  return OK_STATUS();
339 }
340 
341 status_t spi_device_testutils_configure_pad_attrs(dif_pinmux_t *pinmux) {
342  dif_pinmux_pad_attr_t out_attr;
343  dif_pinmux_pad_attr_t in_attr = {.slew_rate = 1, .drive_strength = 3};
344  dif_result_t res;
345  for (uint32_t i = 0; i < ARRAYSIZE(kSpiDeviceDirectPads); ++i) {
346  dt_pad_t pad = kSpiDeviceDirectPads[i];
347  res = dif_pinmux_pad_write_attrs_dt(pinmux, pad, in_attr, &out_attr);
348  if (res == kDifError) {
349  // Some target platforms may not support the specified value for slew rate
350  // and drive strength. If that's the case, use the values actually
351  // supported.
352  if (out_attr.slew_rate != in_attr.slew_rate) {
353  LOG_INFO(
354  "Specified slew rate not supported, trying supported slew rate");
355  in_attr.slew_rate = out_attr.slew_rate;
356  }
357  if (out_attr.drive_strength != in_attr.drive_strength) {
358  LOG_INFO(
359  "Specified drive strength not supported, trying supported drive "
360  "strength");
361  in_attr.drive_strength = out_attr.drive_strength;
362  }
363  TRY(dif_pinmux_pad_write_attrs_dt(pinmux, pad, in_attr, &out_attr));
364  // Note: fallthrough with the modified `in_attr` so that the same
365  // attributes are used for all pads.
366  }
367  }
368  return OK_STATUS();
369 }
370 
371 status_t spi_device_testutils_wait_for_upload(dif_spi_device_handle_t *spid,
372  upload_info_t *info) {
373  // Wait for a SPI transaction cause an upload.
374  bool upload_pending;
375  do {
376  // The UploadCmdfifoNotEmpty interrupt status is updated after the SPI
377  // transaction completes.
378  TRY(dif_spi_device_irq_is_pending(
379  &spid->dev, kDifSpiDeviceIrqUploadCmdfifoNotEmpty, &upload_pending));
380  } while (!upload_pending);
381 
382  uint8_t occupancy;
383 
384  // Get the SPI opcode.
386  if (occupancy != 1) {
387  // Cannot have an uploaded command without an opcode.
388  return INTERNAL();
389  }
390  TRY(dif_spi_device_pop_flash_command_fifo(spid, &info->opcode));
391  // Get the flash_status register.
392  TRY(dif_spi_device_get_flash_status_registers(spid, &info->flash_status));
393 
394  // Get the SPI address (if available).
396  if (occupancy) {
397  dif_toggle_t addr_4b;
398  TRY(dif_spi_device_get_4b_address_mode(spid, &addr_4b));
399  info->addr_4b = addr_4b;
400  TRY(dif_spi_device_pop_flash_address_fifo(spid, &info->address));
401  info->has_address = true;
402  }
403 
404  // Get the SPI data payload (if available).
405  uint32_t start;
406  TRY(dif_spi_device_get_flash_payload_fifo_occupancy(spid, &info->data_len,
407  &start));
408  if (info->data_len) {
409  if (info->data_len > sizeof(info->data)) {
410  // We aren't expecting more than 256 bytes of data.
411  return INVALID_ARGUMENT();
412  }
413  TRY(dif_spi_device_read_flash_payload_buffer(spid, start, info->data_len,
414  info->data));
415  }
416 
417  // Finished: ack the IRQ.
418  TRY(dif_spi_device_irq_acknowledge(&spid->dev,
419  kDifSpiDeviceIrqUploadCmdfifoNotEmpty));
420  return OK_STATUS();
421 }