Software APIs
spi_device_tpm_tx_rx_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 
12 #include "sw/device/lib/runtime/irq.h"
14 #include "sw/device/lib/testing/spi_device_testutils.h"
15 #include "sw/device/lib/testing/test_framework/check.h"
17 #include "sw/device/lib/testing/test_framework/status.h"
18 
20 #include "sw/device/lib/testing/autogen/isr_testutils.h"
21 
22 OTTF_DEFINE_TEST_CONFIG();
23 
24 static dif_spi_device_handle_t spi_device;
25 static dif_pinmux_t pinmux;
26 static dif_rv_plic_t plic;
27 
28 // Enum for TPM command
29 typedef enum {
30  kTpmWriteCommand = 0x0,
31  kTpmReadCommand = 0x80,
32  kTpmCommandMask = 0xbf
33 } tpm_cmd_t;
34 
35 enum {
36  kIterations = 10,
37  kTpmCommandRwMask = 0x80,
38  kTpmCommandSizeMask = 0x3f,
39 };
40 
41 const static dif_spi_device_tpm_config_t kTpmConfig = {
43  .disable_return_by_hardware = false,
44  .disable_address_prefix_check = false,
45  .disable_locality_check = false};
46 
47 static volatile bool header_interrupt_received = false;
48 
49 static void en_plic_irqs(dif_rv_plic_t *plic) {
50  // Enable functional interrupts as well as error interrupts to make sure
51  // everything is behaving as expected.
52  const top_earlgrey_plic_irq_id_t kIrqs[] = {
59 
60  for (uint32_t i = 0; i < ARRAYSIZE(kIrqs); ++i) {
61  CHECK_DIF_OK(dif_rv_plic_irq_set_enabled(
63 
64  // Assign a default priority
65  CHECK_DIF_OK(
67  }
68 
69  // Enable the external IRQ at Ibex.
70  irq_global_ctrl(true);
71  irq_external_ctrl(true);
72 }
73 
74 static void en_spi_device_irqs(dif_spi_device_t *spi_device) {
75  const dif_spi_device_irq_t kIrqs[] = {kDifSpiDeviceIrqUploadCmdfifoNotEmpty,
76  kDifSpiDeviceIrqUploadPayloadNotEmpty,
77  kDifSpiDeviceIrqUploadPayloadOverflow,
78  kDifSpiDeviceIrqReadbufWatermark,
79  kDifSpiDeviceIrqReadbufFlip,
80  kDifSpiDeviceIrqTpmHeaderNotEmpty};
81 
82  for (uint32_t i = 0; i < ARRAYSIZE(kIrqs); ++i) {
83  CHECK_DIF_OK(dif_spi_device_irq_set_enabled(spi_device, kIrqs[i],
85  }
86 }
87 
88 void ottf_external_isr(uint32_t *exc_info) {
89  plic_isr_ctx_t plic_ctx = {.rv_plic = &plic,
90  .hart_id = kTopEarlgreyPlicTargetIbex0};
91 
92  // We should only be receiving the tpm header interrupt during this test, but
93  // the tpm rdfifo cmd end intr_state will go high for read FIFO commands.
94  spi_device_isr_ctx_t spi_device_ctx = {
95  .spi_device = &spi_device.dev,
96  .plic_spi_device_start_irq_id =
98  .expected_irq = kDifSpiDeviceIrqTpmHeaderNotEmpty,
99  .is_only_irq = false};
100 
102  dif_spi_device_irq_t spi_device_irq;
103  isr_testutils_spi_device_isr(plic_ctx, spi_device_ctx, false, &peripheral,
104  &spi_device_irq);
105 
106  switch (spi_device_irq) {
107  case kDifSpiDeviceIrqTpmHeaderNotEmpty:
108  header_interrupt_received = true;
109  // Disable interrupt until work is handled.
110  CHECK_DIF_OK(dif_spi_device_irq_set_enabled(
111  &spi_device.dev, kDifSpiDeviceIrqTpmHeaderNotEmpty,
113  break;
114  default:
115  LOG_ERROR("Unexpected interrupt: %d", spi_device_irq);
116  break;
117  }
118 }
119 
120 static void ack_spi_tpm_header_irq(dif_spi_device_handle_t *spi_device) {
121  // Clear interrupt state and re-enable interrupt.
122  header_interrupt_received = false;
123  CHECK_DIF_OK(dif_spi_device_irq_acknowledge(
124  &spi_device->dev, kDifSpiDeviceIrqTpmHeaderNotEmpty));
125  CHECK_DIF_OK(dif_spi_device_irq_set_enabled(
126  &spi_device->dev, kDifSpiDeviceIrqTpmHeaderNotEmpty, kDifToggleEnabled));
127 }
128 
129 bool test_main(void) {
130  CHECK_DIF_OK(dif_pinmux_init(
132 
133  CHECK_DIF_OK(dif_spi_device_init_handle(
135 
136  CHECK_DIF_OK(dif_rv_plic_init(
138 
139  // Set IoA7 for tpm csb.
140  // Longer term this needs to migrate to a top specific, platform specific
141  // setting.
142  CHECK_DIF_OK(dif_pinmux_input_select(
145 
146  if (kDeviceType == kDeviceSimDV) {
147  dif_pinmux_pad_attr_t out_attr;
148  dif_pinmux_pad_attr_t in_attr = {
149  .slew_rate = 0,
150  .drive_strength = 0,
151  .flags = kDifPinmuxPadAttrPullResistorEnable |
152  kDifPinmuxPadAttrPullResistorUp};
153 
154  CHECK_DIF_OK(dif_pinmux_pad_write_attrs(&pinmux, kTopEarlgreyMuxedPadsIoa7,
155  kDifPinmuxPadKindMio, in_attr,
156  &out_attr));
157  }
158 
159  // Configure fast slew rate and strong drive strength for SPI device pads.
160  CHECK_STATUS_OK(spi_device_testutils_configure_pad_attrs(&pinmux));
161 
162  CHECK_DIF_OK(
163  dif_spi_device_tpm_configure(&spi_device, kDifToggleEnabled, kTpmConfig));
164 
165  // enable interrupts
166  en_plic_irqs(&plic);
167  en_spi_device_irqs(&spi_device.dev);
168 
169  // Sync message with testbench to begin.
170  LOG_INFO("SYNC: Begin TPM Test");
171 
172  for (uint32_t i = 0; i < kIterations; i++) {
173  LOG_INFO("Iteration %d", i);
174 
175  // Wait for write interrupt.
176  ATOMIC_WAIT_FOR_INTERRUPT(header_interrupt_received);
177 
178  // Check what command we have received. Store it as expected variables
179  // and compare when the read command is issued.
180  uint8_t write_command = 0;
181  uint32_t write_addr = 0;
182  CHECK_DIF_OK(dif_spi_device_tpm_get_command(&spi_device, &write_command,
183  &write_addr));
184  CHECK((write_command & kTpmCommandRwMask) == kTpmWriteCommand,
185  "Expected write command, received read");
186 
187  // Poll for write data to complete.
188  uint32_t num_bytes = (write_command & kTpmCommandSizeMask) + 1;
189  LOG_INFO("Expecting %d bytes from tpm write", num_bytes);
190 
191  uint8_t buf[64] = {0};
193  while (status == kDifOutOfRange) {
194  status = dif_spi_device_tpm_read_data(&spi_device, num_bytes, buf);
195  };
196  CHECK_DIF_OK(status);
197 
198  // Finished processing the write command
199  CHECK_DIF_OK(dif_spi_device_tpm_free_write_fifo(&spi_device));
200  ack_spi_tpm_header_irq(&spi_device);
201 
202  LOG_INFO("SYNC: Waiting Read");
203  // Wait for read interrupt.
204  ATOMIC_WAIT_FOR_INTERRUPT(header_interrupt_received);
205 
206  uint8_t read_command = 0;
207  uint32_t read_addr = 0;
208  CHECK_DIF_OK(
209  dif_spi_device_tpm_get_command(&spi_device, &read_command, &read_addr));
210  // Send the written data right back out for reads.
211  CHECK_DIF_OK(dif_spi_device_tpm_write_data(&spi_device, num_bytes, buf));
212  ack_spi_tpm_header_irq(&spi_device);
213 
214  // Make sure the received command matches expectation
215  read_command &= kTpmCommandMask;
216  LOG_INFO("Expected 0x%x, received 0x%x",
217  (kTpmReadCommand | (num_bytes - 1)), read_command);
218  CHECK((kTpmReadCommand | (num_bytes - 1)) == read_command,
219  "Expected read command, received write");
220  CHECK(write_addr == read_addr, "Received address did not match");
221  }
222 
223  return true;
224 }