Software APIs
spi_passthru_test.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/json/spi_passthru.h"
6 
7 #include <stdbool.h>
8 #include <stdint.h>
9 
11 #include "sw/device/lib/base/status.h"
16 #include "sw/device/lib/testing/json/command.h"
17 #include "sw/device/lib/testing/spi_device_testutils.h"
18 #include "sw/device/lib/testing/spi_flash_emulator.h"
19 #include "sw/device/lib/testing/spi_flash_testutils.h"
20 #include "sw/device/lib/testing/test_framework/check.h"
22 #include "sw/device/lib/testing/test_framework/ujson_ottf.h"
23 #include "sw/device/lib/testing/test_framework/ujson_ottf_commands.h"
24 #include "sw/device/lib/ujson/ujson.h"
25 
27 
28 OTTF_DEFINE_TEST_CONFIG(.enable_uart_flow_control = true);
29 
30 volatile uint32_t kReadPipelineMode = 0;
31 
32 static uint32_t current_read_pipeline_mode;
33 static dif_spi_device_handle_t spid;
34 static dif_spi_host_t spih;
35 
36 static status_t configure_jedec_id(ujson_t *uj, dif_spi_device_handle_t *spid) {
37  config_jedec_id_t config;
38  TRY(UJSON_WITH_CRC(ujson_deserialize_config_jedec_id_t, uj, &config));
40  .device_id = config.device_id,
41  .manufacturer_id = config.manufacturer_id,
42  .continuation_code = config.continuation_code,
43  .num_continuation_code = config.continuation_len,
44  };
45  TRY(dif_spi_device_set_flash_id(spid, id));
46  return RESP_OK_STATUS(uj);
47 }
48 
49 static status_t write_status_register(ujson_t *uj,
51  status_register_t sr;
52  TRY(UJSON_WITH_CRC(ujson_deserialize_status_register_t, uj, &sr));
53  TRY(dif_spi_device_set_flash_status_registers(spid, sr.status));
54  return RESP_OK_STATUS(uj);
55 }
56 
57 static status_t read_status_register(ujson_t *uj,
59  status_register_t sr;
60  dif_toggle_t addr_4b;
61  TRY(dif_spi_device_get_flash_status_registers(spid, &sr.status));
62  TRY(dif_spi_device_get_4b_address_mode(spid, &addr_4b));
63  sr.addr_4b = addr_4b;
64  RESP_OK(ujson_serialize_status_register_t, uj, &sr);
65  return OK_STATUS();
66 }
67 
68 static status_t write_sfdp_data(ujson_t *uj, dif_spi_device_handle_t *spid) {
69  sfdp_data_t sfdp;
70  TRY(UJSON_WITH_CRC(ujson_deserialize_sfdp_data_t, uj, &sfdp));
72  0, sizeof(sfdp.data), sfdp.data));
73  return RESP_OK_STATUS(uj);
74 }
75 
76 static status_t wait_for_upload(ujson_t *uj, dif_spi_device_handle_t *spid) {
77  upload_info_t info = {0};
78  TRY(spi_device_testutils_wait_for_upload(spid, &info));
79  // Clear the bits in flash_status to signal the end of processing of an
80  // uploaded command.
82  RESP_OK(ujson_serialize_upload_info_t, uj, &info);
83  return OK_STATUS();
84 }
85 
86 status_t spi_flash_read_id(ujson_t *uj, dif_spi_host_t *spih,
90  TRY(spi_flash_testutils_read_id(spih, &jedec_id));
92 
93  spi_flash_read_id_t uj_id = {
94  .device_id = jedec_id.device_id,
95  .manufacturer_id = jedec_id.manufacturer_id,
96  .continuation_len = jedec_id.continuation_len,
97  };
98  return RESP_OK(ujson_serialize_spi_flash_read_id_t, uj, &uj_id);
99 }
100 
101 status_t spi_flash_read_sfdp(ujson_t *uj, dif_spi_host_t *spih,
102  dif_spi_device_handle_t *spid) {
104  spi_flash_read_sfdp_t op;
105  TRY(UJSON_WITH_CRC(ujson_deserialize_spi_flash_read_sfdp_t, uj, &op));
106 
107  sfdp_data_t sfdp;
108  CHECK(op.length <= sizeof(sfdp.data));
109  TRY(spi_flash_testutils_read_sfdp(spih, op.address, sfdp.data, op.length));
111 
112  return RESP_OK(ujson_serialize_sfdp_data_t, uj, &sfdp);
113 }
114 
115 status_t spi_flash_erase_sector(ujson_t *uj, dif_spi_host_t *spih,
116  dif_spi_device_handle_t *spid) {
117  spi_flash_erase_sector_t op;
119  TRY(UJSON_WITH_CRC(ujson_deserialize_spi_flash_erase_sector_t, uj, &op));
120  TRY(spi_flash_testutils_erase_sector(spih, op.address, op.addr4b));
122  return RESP_OK_STATUS(uj);
123 }
124 
125 status_t spi_flash_write(ujson_t *uj, dif_spi_host_t *spih,
126  dif_spi_device_handle_t *spid) {
127  spi_flash_write_t op;
129  TRY(UJSON_WITH_CRC(ujson_deserialize_spi_flash_write_t, uj, &op));
130  if (op.length > sizeof(op.data)) {
131  LOG_ERROR("Flash write length larger than buffer: %u", op.length);
132  return INVALID_ARGUMENT();
133  }
134 
135  TRY(spi_flash_testutils_program_page(spih, op.data, op.length, op.address,
136  op.addr4b));
138  return RESP_OK_STATUS(uj);
139 }
140 
141 status_t spi_mailbox_map(ujson_t *uj, dif_spi_device_handle_t *spid) {
142  spi_mailbox_map_t map;
143  TRY(UJSON_WITH_CRC(ujson_deserialize_spi_mailbox_map_t, uj, &map));
144  TRY(dif_spi_device_enable_mailbox(spid, map.address));
145  return RESP_OK_STATUS(uj);
146 }
147 
148 status_t spi_mailbox_unmap(ujson_t *uj, dif_spi_device_handle_t *spid) {
150  return RESP_OK_STATUS(uj);
151 }
152 
153 status_t spi_mailbox_write(ujson_t *uj, dif_spi_device_handle_t *spid) {
154  spi_mailbox_write_t op;
155  TRY(UJSON_WITH_CRC(ujson_deserialize_spi_mailbox_write_t, uj, &op));
156  if (op.length > sizeof(op.data)) {
157  LOG_ERROR("Mailbox write length larger than buffer: %u", op.length);
158  return INVALID_ARGUMENT();
159  }
162  op.offset, op.length, op.data));
163  return RESP_OK_STATUS(uj);
164 }
165 
166 status_t spi_passthru_set_address_map(ujson_t *uj,
167  dif_spi_device_handle_t *spid) {
168  spi_passthru_swap_map_t swap;
169  TRY(UJSON_WITH_CRC(ujson_deserialize_spi_passthru_swap_map_t, uj, &swap));
170  TRY(dif_spi_device_set_flash_address_swap(spid, swap.mask, swap.value));
171  return RESP_OK_STATUS(uj);
172 }
173 
174 status_t command_processor(ujson_t *uj) {
175  while (true) {
176  test_command_t command;
177  TRY(UJSON_WITH_CRC(ujson_deserialize_test_command_t, uj, &command));
178  status_t result = ujson_ottf_dispatch(uj, command);
179  if (status_ok(result)) {
180  // Check for changes to pipeline mode.
181  if (current_read_pipeline_mode != kReadPipelineMode) {
182  current_read_pipeline_mode = kReadPipelineMode;
183  CHECK_STATUS_OK(spi_device_testutils_configure_read_pipeline(
184  &spid, current_read_pipeline_mode, current_read_pipeline_mode));
185  }
186  continue;
187  } else if (status_err(result) != kUnimplemented) {
188  return result;
189  }
190  switch (command) {
191  case kTestCommandSpiConfigureJedecId:
192  RESP_ERR(uj, configure_jedec_id(uj, &spid));
193  break;
194  case kTestCommandSpiReadStatus:
195  RESP_ERR(uj, read_status_register(uj, &spid));
196  break;
197  case kTestCommandSpiWriteStatus:
198  RESP_ERR(uj, write_status_register(uj, &spid));
199  break;
200  case kTestCommandSpiWriteSfdp:
201  RESP_ERR(uj, write_sfdp_data(uj, &spid));
202  break;
203  case kTestCommandSpiWaitForUpload:
204  RESP_ERR(uj, wait_for_upload(uj, &spid));
205  break;
206  case kTestCommandSpiFlashReadId:
207  RESP_ERR(uj, spi_flash_read_id(uj, &spih, &spid));
208  break;
209  case kTestCommandSpiFlashReadSfdp:
210  RESP_ERR(uj, spi_flash_read_sfdp(uj, &spih, &spid));
211  break;
212  case kTestCommandSpiFlashEraseSector:
213  RESP_ERR(uj, spi_flash_erase_sector(uj, &spih, &spid));
214  break;
215  case kTestCommandSpiFlashWrite:
216  RESP_ERR(uj, spi_flash_write(uj, &spih, &spid));
217  break;
218  case kTestCommandSpiFlashEmulator:
219  RESP_ERR(uj, spi_flash_emulator(&spih, &spid));
220  break;
221  case kTestCommandSpiMailboxMap:
222  RESP_ERR(uj, spi_mailbox_map(uj, &spid));
223  break;
224  case kTestCommandSpiMailboxUnmap:
225  RESP_ERR(uj, spi_mailbox_unmap(uj, &spid));
226  break;
227  case kTestCommandSpiMailboxWrite:
228  RESP_ERR(uj, spi_mailbox_write(uj, &spid));
229  break;
230  case kTestCommandSpiPassthruSetAddressMap:
231  RESP_ERR(uj, spi_passthru_set_address_map(uj, &spid));
232  break;
233 
234  default:
235  LOG_ERROR("Unrecognized command: %d", command);
236  RESP_ERR(uj, INVALID_ARGUMENT());
237  }
238  }
239  // We should never reach here.
240  return INTERNAL();
241 }
242 
243 bool test_main(void) {
244  if (kDeviceType == kDeviceSilicon) {
245  // On teacup board, we need to enable pull-ups on the pins connected to `WP`
246  // and `HOLD`.
247  dif_pinmux_t pinmux;
248  mmio_region_t base_addr =
250  CHECK_DIF_OK(dif_pinmux_init(base_addr, &pinmux));
251 
252  dif_pinmux_pad_attr_t out_attr;
253  dif_pinmux_pad_attr_t in_attr = {
254  .slew_rate = 1,
255  .drive_strength = 3,
256  .flags = kDifPinmuxPadAttrPullResistorEnable |
257  kDifPinmuxPadAttrPullResistorUp};
258 
259  CHECK_DIF_OK(
260  dif_pinmux_pad_write_attrs(&pinmux, kTopEarlgreyDirectPadsSpiHost0Sck,
261  kDifPinmuxPadKindDio, in_attr, &out_attr));
262  CHECK_DIF_OK(
263  dif_pinmux_pad_write_attrs(&pinmux, kTopEarlgreyDirectPadsSpiHost0Sd0,
264  kDifPinmuxPadKindDio, in_attr, &out_attr));
265  CHECK_DIF_OK(
266  dif_pinmux_pad_write_attrs(&pinmux, kTopEarlgreyDirectPadsSpiHost0Sd1,
267  kDifPinmuxPadKindDio, in_attr, &out_attr));
268 
269  CHECK_DIF_OK(
270  dif_pinmux_pad_write_attrs(&pinmux, kTopEarlgreyDirectPadsSpiHost0Sd2,
271  kDifPinmuxPadKindDio, in_attr, &out_attr));
272 
273  CHECK_DIF_OK(
274  dif_pinmux_pad_write_attrs(&pinmux, kTopEarlgreyDirectPadsSpiHost0Sd3,
275  kDifPinmuxPadKindDio, in_attr, &out_attr));
276  }
277 
278  const uint32_t spi_host_clock_freq_hz =
280  CHECK_DIF_OK(dif_spi_host_init(
282  dif_spi_host_config_t config = {
283  .spi_clock = spi_host_clock_freq_hz / 4,
284  .peripheral_clock_freq_hz = spi_host_clock_freq_hz,
285  .chip_select =
286  {
287  .idle = 2,
288  .trail = 2,
289  .lead = 2,
290  },
291  };
292  CHECK_DIF_OK(dif_spi_host_configure(&spih, config));
293  CHECK_DIF_OK(dif_spi_host_output_set_enabled(&spih, /*enabled=*/true));
294 
295  CHECK_DIF_OK(dif_spi_device_init_handle(
297 
298  // We want to block passthru of the first 5 read commands, corresponding to
299  // ReadStatus{1,2,3}, ReadJedecID and ReadSfdp.
300  // We also block all write commands.
301  CHECK_STATUS_OK(spi_device_testutils_configure_passthrough(
302  &spid,
303  /*filters=*/0x1F,
304  /*upload_write_commands=*/true));
305 
307  .status = true,
308  .jedec_id = true,
309  .sfdp = true,
310  .mailbox = true,
311  };
312  CHECK_DIF_OK(
314 
315  current_read_pipeline_mode = kReadPipelineMode;
316  CHECK_STATUS_OK(spi_device_testutils_configure_read_pipeline(
317  &spid, current_read_pipeline_mode, current_read_pipeline_mode));
318 
319  ujson_t uj = ujson_ottf_console();
320  status_t s = command_processor(&uj);
321  LOG_INFO("status = %r", s);
322  return status_ok(s);
323 }