Software APIs
spi_flash_emulator.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_emulator.h"
6 
7 #include <stdalign.h>
8 #include <stdint.h>
9 
13 #include "sw/device/lib/testing/spi_device_testutils.h"
14 #include "sw/device/lib/testing/spi_flash_testutils.h"
15 
16 #define MODULE_ID MAKE_MODULE_ID('s', 'f', 'e')
17 
18 enum {
19  // JEDEC standard continuation code.
20  kJedecContCode = 0x7f,
21  // JEDEC ID of lowRISC.
22  kJedecManufacturer = 0xEF,
23  // lowRISC is on page 12 of JEDEC IDs.
24  kJedecContCodeCount = 12,
25  // A density of 24 corresponds to 16MiB (1<<24 bytes).
26  kJedecDensity = 24,
27 };
28 
29 typedef struct bfpt {
30  uint32_t data[9];
31 } bfpt_t;
32 
33 typedef struct sfdp {
36  bfpt_t bfpt;
37 } sfdp_t;
38 
39 // This function prepares the downstream-visible SFDP table.
40 // Out of convenience, it also checks the quad-enable mechanism from
41 // the backend EEPROM part and returns the mechanism value
42 //
43 // TODO: Restructure this into something more general for dealing
44 // with backend-eeprom properties that the emulator loop might
45 // care about.
46 static status_t read_and_prepare_sfdp(dif_spi_host_t *spih,
48  alignas(uint32_t) uint8_t data[256];
49  TRY(spi_flash_testutils_read_sfdp(spih, 0, data, sizeof(data)));
50 
51  // TODO: present a better SFDP table. This table is as simple
52  // as possible and copies only a few bits of relevant data
53  // from the backend EEPROM's table.
54  sfdp_t sfdp = {
55  .header = {.signature = kSfdpSignature,
56  .minor = 0,
57  .major = 1,
58  .nph = 0,
59  .reserved = 0xFF},
60  .param =
61  {
62  .param_id = 0x00,
63  .minor = 0,
64  .major = 1,
65  .length = sizeof(bfpt_t) / sizeof(uint32_t),
66  .table_pointer = {(uint8_t)offsetof(sfdp_t, bfpt)},
67  .pad = 0xFF,
68  },
69  .bfpt = {0},
70  };
71 
72  uint32_t offset =
73  read_32(data + offsetof(sfdp_t, param.table_pointer)) & 0x00FFFFFF;
74  if (offset >= sizeof(data)) {
75  return INVALID_ARGUMENT();
76  }
77  uint8_t length = data[offsetof(sfdp_t, param.length)];
78 
79  // We want to copy SFDP word 0 from the eeprom and preserve the
80  // block_erase_size, write_granularity, write_en_required, write_en_opcode,
81  // erase_opcode, support_fast_read_112, address_modes and
82  // support_fast_read_114 fields.
83  sfdp.bfpt.data[0] = read_32(data + offset);
84  sfdp.bfpt.data[0] &= 0x0047FFFF;
85  // Preserve the entire density field.
86  sfdp.bfpt.data[1] = read_32(data + offset + 1 * sizeof(uint32_t));
87  // Preserve the 1-1-4 parameters, discard the 1-4-4 parameters.
88  sfdp.bfpt.data[2] = read_32(data + offset + 2 * sizeof(uint32_t));
89  sfdp.bfpt.data[2] &= 0xFFFF0000;
90  // Preserve the sector erase information.
91  sfdp.bfpt.data[7] = read_32(data + offset + 7 * sizeof(uint32_t));
92  sfdp.bfpt.data[8] = read_32(data + offset + 8 * sizeof(uint32_t));
93 
95  0, sizeof(sfdp), (uint8_t *)&sfdp));
96 
97  uint32_t quad_enable = 0;
98  if (length >= 14) {
99  // JESD216F, section 6.4.18:
100  // The Quad Enable mechanism is bits 20:23 of the 15th dword.
101  quad_enable = read_32(data + offset + 14 * sizeof(uint32_t));
102  quad_enable = bitfield_field32_read(quad_enable, SPI_FLASH_QUAD_ENABLE);
103  }
104  return OK_STATUS((int32_t)quad_enable);
105 }
106 
107 static status_t prepare_jedec_id(dif_spi_device_handle_t *spid) {
109  // TODO: configure a density that reflects the backend eeprom.
110  .device_id = kJedecDensity << 8,
111  .manufacturer_id = kJedecManufacturer,
112  .continuation_code = kJedecContCode,
113  .num_continuation_code = kJedecContCodeCount,
114  };
115  TRY(dif_spi_device_set_flash_id(spid, id));
116  return OK_STATUS();
117 }
118 
119 status_t spi_flash_emulator(dif_spi_host_t *spih,
120  dif_spi_device_handle_t *spid) {
121  // TODO: add a mode that uses spi_device address translation.
122  LOG_INFO("Configuring spi_flash_emulator.");
124  TRY(prepare_jedec_id(spid));
125  uint8_t quad_enable = (uint8_t)TRY(read_and_prepare_sfdp(spih, spid));
126  LOG_INFO("Setting the EEPROM's QE bit via mechanism %d", quad_enable);
127  TRY(spi_flash_testutils_quad_enable(spih, quad_enable, /*enabled=*/true));
129  LOG_INFO("Starting spi_flash_emulator.");
130 
131  bool running = true;
132  while (running) {
133  upload_info_t info = {0};
134  TRY(spi_device_testutils_wait_for_upload(spid, &info));
135 
137  switch (info.opcode) {
138  case kSpiDeviceFlashOpChipErase:
139  TRY(spi_flash_testutils_erase_chip(spih));
140  break;
141  case kSpiDeviceFlashOpSectorErase:
142  TRY(spi_flash_testutils_erase_sector(spih, info.address, info.addr_4b));
143  break;
144  case kSpiDeviceFlashOpBlockErase32k:
145  TRY(spi_flash_testutils_erase_op(spih, kSpiDeviceFlashOpBlockErase32k,
146  info.address, info.addr_4b));
147  break;
148  case kSpiDeviceFlashOpBlockErase64k:
149  TRY(spi_flash_testutils_erase_op(spih, kSpiDeviceFlashOpBlockErase64k,
150  info.address, info.addr_4b));
151  break;
152  case kSpiDeviceFlashOpPageProgram:
153  TRY(spi_flash_testutils_program_page(spih, info.data, info.data_len,
154  info.address, info.addr_4b));
155  break;
156  case kSpiDeviceFlashOpSectorErase4b:
157  TRY(spi_flash_testutils_erase_op(spih, kSpiDeviceFlashOpSectorErase4b,
158  info.address, /*addr_is_4b=*/true));
159  break;
160  case kSpiDeviceFlashOpBlockErase32k4b:
161  TRY(spi_flash_testutils_erase_op(spih, kSpiDeviceFlashOpBlockErase32k4b,
162  info.address, /*addr_is_4b=*/true));
163  break;
164  case kSpiDeviceFlashOpBlockErase64k4b:
165  TRY(spi_flash_testutils_erase_op(spih, kSpiDeviceFlashOpBlockErase64k4b,
166  info.address, /*addr_is_4b=*/true));
167  break;
168  case kSpiDeviceFlashOpPageProgram4b:
169  TRY(spi_flash_testutils_program_op(
170  spih, kSpiDeviceFlashOpPageProgram4b, info.data, info.data_len,
171  info.address,
172  /*addr_is_4b=*/true, kTransactionWidthMode111));
173  break;
174  case kSpiDeviceFlashOpReset:
175  running = false;
176  break;
177  default:
178  LOG_ERROR("Unknown SPI op: %02x", info.opcode);
179  }
182  }
183  LOG_INFO("Exiting spi_flash_emulator.");
184  return OK_STATUS();
185 }