Software APIs
spi_flash_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_flash_testutils.h"
6 
10 #include "sw/device/lib/testing/spi_device_testutils.h"
11 #include "sw/device/lib/testing/test_framework/check.h"
12 
13 #define MODULE_ID MAKE_MODULE_ID('s', 'f', 't')
14 
15 status_t spi_flash_testutils_read_id(dif_spi_host_t *spih,
17  TRY_CHECK(spih != NULL);
18  TRY_CHECK(id != NULL);
19 
20  uint8_t buffer[32];
21  dif_spi_host_segment_t transaction[] = {
23  .opcode = {.opcode = kSpiDeviceFlashOpReadJedec,
24  .width = kDifSpiHostWidthStandard}},
25  {
27  .rx =
28  {
29  .width = kDifSpiHostWidthStandard,
30  .buf = buffer,
31  .length = sizeof(buffer),
32  },
33  },
34  };
35  TRY(dif_spi_host_transaction(spih, /*csid=*/0, transaction,
36  ARRAYSIZE(transaction)));
37 
38  size_t page = 0;
39  while ((page < sizeof(buffer)) && (buffer[page] == 0x7fu)) {
40  ++page;
41  }
42  TRY_CHECK(page + 3 <= sizeof(buffer));
43  TRY_CHECK(page <= UINT8_MAX);
44  id->continuation_len = (uint8_t)page;
45  id->manufacturer_id = buffer[page];
46  id->device_id = buffer[page + 1];
47  id->device_id |= (uint16_t)buffer[page + 2] << 8;
48  return OK_STATUS();
49 }
50 
51 status_t spi_flash_testutils_read_sfdp(dif_spi_host_t *spih, uint32_t address,
52  void *buffer, size_t length) {
53  TRY_CHECK(spih != NULL);
54  TRY_CHECK(buffer != NULL);
55 
56  dif_spi_host_segment_t transaction[] = {
58  .opcode = {.opcode = kSpiDeviceFlashOpReadSfdp,
59  .width = kDifSpiHostWidthStandard}},
60  {
62  .address =
63  {
64  .width = kDifSpiHostWidthStandard,
65  .mode = kDifSpiHostAddrMode3b,
66  .address = address,
67  },
68  },
69  {
71  .dummy =
72  {
73  .width = kDifSpiHostWidthStandard,
74  .length = 8,
75  },
76  },
77  {
79  .rx =
80  {
81  .width = kDifSpiHostWidthStandard,
82  .buf = buffer,
83  .length = length,
84  },
85  },
86  };
87  TRY(dif_spi_host_transaction(spih, /*csid=*/0, transaction,
88  ARRAYSIZE(transaction)));
89  return OK_STATUS();
90 }
91 
92 status_t spi_flash_testutils_read_status(dif_spi_host_t *spih, uint8_t opcode,
93  size_t length) {
94  TRY_CHECK(spih != NULL);
95  TRY_CHECK(length <= 3);
96  uint32_t status = 0;
97  dif_spi_host_segment_t transaction[] = {
99  .opcode = {.opcode = opcode, .width = kDifSpiHostWidthStandard}},
100  {
101  .type = kDifSpiHostSegmentTypeRx,
102  .rx =
103  {
104  .width = kDifSpiHostWidthStandard,
105  .buf = &status,
106  .length = length,
107  },
108  },
109  };
110  TRY(dif_spi_host_transaction(spih, /*csid=*/0, transaction,
111  ARRAYSIZE(transaction)));
112  return OK_STATUS((int32_t)status);
113 }
114 
115 status_t spi_flash_testutils_write_status(dif_spi_host_t *spih, uint8_t opcode,
116  uint32_t status, size_t length) {
117  TRY_CHECK(spih != NULL);
118  TRY_CHECK(length <= 3);
119  TRY(spi_flash_testutils_issue_write_enable(spih));
120  dif_spi_host_segment_t transaction[] = {
122  .opcode = {.opcode = opcode, .width = kDifSpiHostWidthStandard}},
123  {
124  .type = kDifSpiHostSegmentTypeTx,
125  .tx =
126  {
127  .width = kDifSpiHostWidthStandard,
128  .buf = &status,
129  .length = length,
130  },
131  },
132  };
133  TRY(dif_spi_host_transaction(spih, /*csid=*/0, transaction,
134  ARRAYSIZE(transaction)));
135  return OK_STATUS();
136 }
137 
138 status_t spi_flash_testutils_wait_until_not_busy(dif_spi_host_t *spih) {
139  TRY_CHECK(spih != NULL);
140  int32_t status = 0;
141 
142  do {
143  status = TRY(
144  spi_flash_testutils_read_status(spih, kSpiDeviceFlashOpReadStatus1, 1));
145  } while (status & kSpiFlashStatusBitWip);
146  return OK_STATUS();
147 }
148 
149 status_t spi_flash_testutils_issue_write_enable(dif_spi_host_t *spih) {
150  TRY_CHECK(spih != NULL);
151  dif_spi_host_segment_t transaction[] = {
153  .opcode = {.opcode = kSpiDeviceFlashOpWriteEnable,
154  .width = kDifSpiHostWidthStandard}},
155  };
156  TRY(dif_spi_host_transaction(spih, /*csid=*/0, transaction,
157  ARRAYSIZE(transaction)));
158  return OK_STATUS();
159 }
160 
161 status_t spi_flash_testutils_erase_chip(dif_spi_host_t *spih) {
162  TRY_CHECK(spih != NULL);
163  TRY(spi_flash_testutils_issue_write_enable(spih));
164 
165  dif_spi_host_segment_t transaction[] = {
167  .opcode = {.opcode = kSpiDeviceFlashOpChipErase,
168  .width = kDifSpiHostWidthStandard}},
169  };
170  TRY(dif_spi_host_transaction(spih, /*csid=*/0, transaction,
171  ARRAYSIZE(transaction)));
172  return spi_flash_testutils_wait_until_not_busy(spih);
173 }
174 
175 status_t spi_flash_testutils_erase_op(dif_spi_host_t *spih, uint8_t opcode,
176  uint32_t address, bool addr_is_4b) {
177  TRY_CHECK(spih != NULL);
178  TRY(spi_flash_testutils_issue_write_enable(spih));
179 
180  dif_spi_host_addr_mode_t addr_mode =
182  dif_spi_host_segment_t transaction[] = {
184  .opcode = {.opcode = opcode, .width = kDifSpiHostWidthStandard}},
185  {
187  .address =
188  {
189  .width = kDifSpiHostWidthStandard,
190  .mode = addr_mode,
191  .address = address,
192  },
193  },
194  };
195  TRY(dif_spi_host_transaction(spih, /*csid=*/0, transaction,
196  ARRAYSIZE(transaction)));
197 
198  return spi_flash_testutils_wait_until_not_busy(spih);
199 }
200 
201 status_t spi_flash_testutils_erase_sector(dif_spi_host_t *spih,
202  uint32_t address, bool addr_is_4b) {
203  return spi_flash_testutils_erase_op(spih, kSpiDeviceFlashOpSectorErase,
204  address, addr_is_4b);
205 }
206 
207 status_t spi_flash_testutils_erase_block32k(dif_spi_host_t *spih,
208  uint32_t address, bool addr_is_4b) {
209  return spi_flash_testutils_erase_op(spih, kSpiDeviceFlashOpBlockErase32k,
210  address, addr_is_4b);
211 }
212 
213 status_t spi_flash_testutils_erase_block64k(dif_spi_host_t *spih,
214  uint32_t address, bool addr_is_4b) {
215  return spi_flash_testutils_erase_op(spih, kSpiDeviceFlashOpBlockErase64k,
216  address, addr_is_4b);
217 }
218 
219 status_t spi_flash_testutils_program_op(
220  dif_spi_host_t *spih, uint8_t opcode, const void *payload, size_t length,
221  uint32_t address, bool addr_is_4b,
222  spi_flash_testutils_transaction_width_mode_t page_program_mode) {
223  TRY_CHECK(spih != NULL);
224  TRY_CHECK(payload != NULL);
225  TRY_CHECK(length <= 256); // Length must be less than a page size.
226 
227  TRY(spi_flash_testutils_issue_write_enable(spih));
228 
229  dif_spi_host_addr_mode_t addr_mode =
231  dif_spi_host_segment_t transaction[] = {
233  .opcode = {.opcode = opcode,
234  .width =
235  spi_flash_testutils_get_opcode_width(page_program_mode)}},
236  {
238  .address =
239  {
240  .width =
241  spi_flash_testutils_get_address_width(page_program_mode),
242  .mode = addr_mode,
243  .address = address,
244  },
245  },
246  {
247  .type = kDifSpiHostSegmentTypeTx,
248  .tx =
249  {
250  .width =
251  spi_flash_testutils_get_data_width(page_program_mode),
252  .buf = payload,
253  .length = length,
254  },
255  },
256  };
257  TRY(dif_spi_host_transaction(spih, /*csid=*/0, transaction,
258  ARRAYSIZE(transaction)));
259 
260  return spi_flash_testutils_wait_until_not_busy(spih);
261 }
262 
263 status_t spi_flash_testutils_program_page(dif_spi_host_t *spih,
264  const void *payload, size_t length,
265  uint32_t address, bool addr_is_4b) {
266  return spi_flash_testutils_program_op(spih, kSpiDeviceFlashOpPageProgram,
267  payload, length, address, addr_is_4b,
268  kTransactionWidthMode111);
269 }
270 
271 status_t spi_flash_testutils_read_op(dif_spi_host_t *spih, uint8_t opcode,
272  void *payload, size_t length,
273  uint32_t address, bool addr_is_4b,
274  uint8_t width, uint8_t dummy) {
275  TRY_CHECK(spih != NULL);
276  TRY_CHECK(payload != NULL);
277  TRY_CHECK(length <= 256); // Length must be less than a page size.
278  TRY_CHECK(width == 1 || width == 2 || width == 4);
279 
280  dif_spi_host_addr_mode_t addr_mode =
282  dif_spi_host_width_t data_width = width == 1 ? kDifSpiHostWidthStandard
283  : width == 2 ? kDifSpiHostWidthDual
285 
286  dif_spi_host_segment_t transaction[4] = {
288  .opcode = {.opcode = opcode, .width = kDifSpiHostWidthStandard}},
289  {
291  .address =
292  {
293  .width = kDifSpiHostWidthStandard,
294  .mode = addr_mode,
295  .address = address,
296  },
297  },
298  {
300  .dummy =
301  {
302  .width = kDifSpiHostWidthStandard,
303  .length = dummy,
304  },
305  },
306  {
307  .type = kDifSpiHostSegmentTypeRx,
308  .rx =
309  {
310  .width = data_width,
311  .buf = payload,
312  .length = length,
313  },
314  },
315  };
316  TRY(dif_spi_host_transaction(spih, /*csid=*/0, transaction,
317  ARRAYSIZE(transaction)));
318  return OK_STATUS();
319 }
320 
321 status_t spi_flash_testutils_quad_enable(dif_spi_host_t *spih, uint8_t method,
322  bool enabled) {
323  uint32_t status = 0;
324  switch (method) {
325  case 0:
326  // Device does not have a QE bit.
327  break;
328  case 1:
329  // QE is bit1 of status reg 2.
330  // Set/clear via two-byte reads/writes via SR1 opcodes.
331  // Writing only one byte to SR1 clears SR2.
332  status = (uint32_t)TRY(spi_flash_testutils_read_status(
333  spih, kSpiDeviceFlashOpReadStatus1, 2));
334  status = bitfield_bit32_write(status, 9, enabled);
335  TRY(spi_flash_testutils_write_status(spih, kSpiDeviceFlashOpWriteStatus1,
336  status, enabled ? 2 : 1));
337  break;
338  case 2:
339  // QE is bit6 of status reg 1.
340  status = (uint32_t)TRY(spi_flash_testutils_read_status(
341  spih, kSpiDeviceFlashOpReadStatus1, 1));
342  status = bitfield_bit32_write(status, 6, enabled);
343  TRY(spi_flash_testutils_write_status(spih, kSpiDeviceFlashOpWriteStatus1,
344  status, 1));
345  break;
346  case 3:
347  // QE is bit7 of status reg 2.
348  // Use "status register 2" opcodes.
349  status = (uint32_t)TRY(spi_flash_testutils_read_status(
350  spih, kSpiDeviceFlashOpReadStatus2, 1));
351  status = bitfield_bit32_write(status, 7, enabled);
352  TRY(spi_flash_testutils_write_status(spih, kSpiDeviceFlashOpWriteStatus2,
353  status, 1));
354  break;
355  case 4:
356  // QE is bit1 of status reg 2.
357  // Set/clear via two-byte reads/writes via SR1 opcodes.
358  // Writing only one byte to SR1 does not affcet SR2.
359  status = (uint32_t)TRY(spi_flash_testutils_read_status(
360  spih, kSpiDeviceFlashOpReadStatus1, 2));
361  status = bitfield_bit32_write(status, 9, enabled);
362  TRY(spi_flash_testutils_write_status(spih, kSpiDeviceFlashOpWriteStatus1,
363  status, 2));
364  break;
365  case 5:
366  // QE is bit1 of status reg 2.
367  // Requires reading status reg via SR1/SR2 opcodes.
368  // Set/clear via two-byte reads/writes via SR1 opcodes.
369  status = (uint32_t)TRY(spi_flash_testutils_read_status(
370  spih, kSpiDeviceFlashOpReadStatus1, 1));
371  status |= (uint32_t)(TRY(spi_flash_testutils_read_status(
372  spih, kSpiDeviceFlashOpReadStatus2, 1))
373  << 8);
374  status = bitfield_bit32_write(status, 9, enabled);
375  TRY(spi_flash_testutils_write_status(spih, kSpiDeviceFlashOpWriteStatus1,
376  status, 2));
377  break;
378  case 6:
379  // QE is bit1 of status reg 2.
380  // Requires reading status reg via SR1/SR2/SR3 opcodes.
381  // Set/clear via one-byte writes via SR2 opcode.
382  status = (uint32_t)TRY(spi_flash_testutils_read_status(
383  spih, kSpiDeviceFlashOpReadStatus2, 1));
384  status = bitfield_bit32_write(status, 1, enabled);
385  TRY(spi_flash_testutils_write_status(spih, kSpiDeviceFlashOpWriteStatus2,
386  status, 1));
387  break;
388  case 7:
389  // Reserved.
390  return INVALID_ARGUMENT();
391  break;
392  }
393  return OK_STATUS();
394 }
395 
396 status_t spi_flash_testutils_enter_4byte_address_mode(dif_spi_host_t *spih) {
399  .opcode = {.opcode = kSpiDeviceFlashOpEnter4bAddr,
400  .width = kDifSpiHostWidthStandard}};
401  TRY(dif_spi_host_transaction(spih, /*cs_id=*/0, &op, 1));
402  return OK_STATUS();
403 }
404 
405 status_t spi_flash_testutils_exit_4byte_address_mode(dif_spi_host_t *spih) {
407  .opcode = {.opcode = kSpiDeviceFlashOpExit4bAddr,
408  .width = kDifSpiHostWidthStandard}};
409  TRY(dif_spi_host_transaction(spih, /*cs_id=*/0, &op, 1));
410  return OK_STATUS();
411 }