Software APIs
spi_device_sleep_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 
10 #include "sw/device/lib/dif/dif_rv_core_ibex.h"
12 #include "sw/device/lib/runtime/irq.h"
14 #include "sw/device/lib/testing/aon_timer_testutils.h"
15 #include "sw/device/lib/testing/pwrmgr_testutils.h"
16 #include "sw/device/lib/testing/rv_plic_testutils.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 
24 #include "sw/device/lib/testing/autogen/isr_testutils.h"
25 
26 OTTF_DEFINE_TEST_CONFIG();
27 
28 static dif_pinmux_t pinmux;
29 static dif_gpio_t gpio;
30 static dif_spi_device_handle_t spid;
31 static dif_pwrmgr_t pwrmgr;
32 static dif_aon_timer_t aon_timer;
33 static dif_rv_plic_t plic;
34 static plic_isr_ctx_t plic_ctx = {.rv_plic = &plic,
35  .hart_id = kTopEarlgreyPlicTargetIbex0};
36 static pwrmgr_isr_ctx_t pwrmgr_isr_ctx = {
37  .pwrmgr = &pwrmgr,
38  .plic_pwrmgr_start_irq_id = kTopEarlgreyPlicIrqIdPwrmgrAonWakeup,
39  .expected_irq = kDifPwrmgrIrqWakeup,
40  .is_only_irq = true};
41 
42 static status_t enter_low_power(void) {
43  dif_pwrmgr_domain_config_t pwrmgr_domain_cfg =
45  kDifPwrmgrDomainOptionMainPowerInLowPower;
46  // Wait for the host signal that the device can sleep.
47  bool sleep = false;
48  do {
49  CHECK_DIF_OK(dif_gpio_read(&gpio, 0, &sleep));
50  } while (!sleep);
51 
52  irq_global_ctrl(false);
53  LOG_INFO("SYNC: Sleeping");
54  TRY(pwrmgr_testutils_enable_low_power(
55  &pwrmgr, kDifPwrmgrWakeupRequestSourceThree, pwrmgr_domain_cfg));
57 
58  TRY_CHECK(UNWRAP(pwrmgr_testutils_is_wakeup_reason(
59  &pwrmgr, kDifPwrmgrWakeupRequestSourceThree)));
60  TRY(dif_pwrmgr_wakeup_reason_clear(&pwrmgr));
61 
62  TRY(dif_pinmux_wakeup_cause_clear(&pinmux));
63 
64  // Sometimes execution continues after the wfi while the core is preparing to
65  // enter low power mode. In that case we loop checking if the host requested a
66  // sleep. If not we loop until core enters low power mode.
67  do {
68  CHECK_DIF_OK(dif_gpio_read(&gpio, 0, &sleep));
69  } while (sleep);
70  irq_global_ctrl(true);
71  LOG_INFO("SYNC: Awaked");
72  return OK_STATUS();
73 }
74 
75 static status_t configure_spi_flash_mode(void) {
77  .device_id = 0x2298,
78  .manufacturer_id = 0x74,
79  .continuation_code = 0x17,
80  .num_continuation_code = 2,
81  };
82  TRY(dif_spi_device_set_flash_id(&spid, id));
83 
84  dif_spi_device_config_t spi_device_config = {
87  .device_mode = kDifSpiDeviceModeFlashEmulation,
88  };
89  TRY(dif_spi_device_configure(&spid, spi_device_config));
90 
91  // Configure the READ_JEDEC_ID command (CMD_INFO_3).
93  .opcode = kSpiDeviceFlashOpReadJedec,
94  .address_type = kDifSpiDeviceFlashAddrDisabled,
95  .dummy_cycles = 0,
96  .payload_io_type = kDifSpiDevicePayloadIoSingle,
97  .payload_dir_to_host = false,
98  .upload = false,
99  };
100 
102  &spid, kSpiDeviceReadCommandSlotBase + 3, kDifToggleEnabled, cmd));
103 
104  LOG_INFO("SYNC: Flash mode");
105  return OK_STATUS();
106 }
107 
108 bool test_main(void) {
109  mmio_region_t addr;
111  CHECK_DIF_OK(dif_spi_device_init_handle(addr, &spid));
112 
114  CHECK_DIF_OK(dif_pwrmgr_init(addr, &pwrmgr));
115 
117  CHECK_DIF_OK(dif_aon_timer_init(addr, &aon_timer));
118 
120  CHECK_DIF_OK(dif_pinmux_init(addr, &pinmux));
121 
123  CHECK_DIF_OK(dif_rv_plic_init(addr, &plic));
124 
126  CHECK_DIF_OK(dif_gpio_init(addr, &gpio));
127  CHECK_DIF_OK(dif_gpio_output_set_enabled_all(&gpio, 0x1));
128 
129  // Enable all the AON interrupts used to wake up the core.
130  rv_plic_testutils_irq_range_enable(&plic, kTopEarlgreyPlicTargetIbex0,
133  CHECK_DIF_OK(dif_pwrmgr_irq_set_enabled(&pwrmgr, 0, kDifToggleEnabled));
134 
135  irq_global_ctrl(true);
136  irq_external_ctrl(true);
137 
138  dif_pinmux_index_t detector = 0;
139  dif_pinmux_wakeup_config_t wakeup_cfg = {
141  .signal_filter = false,
142  .pad_type = kDifPinmuxPadKindMio,
143  .pad_select = kTopEarlgreyPinmuxInselIoa8,
144  };
145  CHECK_DIF_OK(
146  dif_pinmux_wakeup_detector_enable(&pinmux, detector, wakeup_cfg));
147 
148  // Phase1: spi sleep test
149  LOG_INFO("Setting SPI_DIO1 to high when sleeping");
150  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
151  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd0, kDifPinmuxPadKindDio,
153  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
154  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd1, kDifPinmuxPadKindDio,
156  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
157  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd2, kDifPinmuxPadKindDio,
159  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
160  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd3, kDifPinmuxPadKindDio,
162  LOG_INFO("Use IOA7 to let host know when sleep is active.");
163  CHECK_DIF_OK(dif_pinmux_input_select(&pinmux,
166  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(&pinmux, kTopEarlgreyMuxedPadsIoa7,
169  enter_low_power();
171  &pinmux, kTopEarlgreyMuxedPadsIoa7, kDifPinmuxPadKindMio));
172 
173  LOG_INFO("Setting SPI_DIO1 to low when sleeping");
174  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
175  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd0, kDifPinmuxPadKindDio,
177  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
178  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd1, kDifPinmuxPadKindDio,
180  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
181  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd2, kDifPinmuxPadKindDio,
183  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
184  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd3, kDifPinmuxPadKindDio,
186 
187  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(&pinmux, kTopEarlgreyMuxedPadsIoa7,
190  enter_low_power();
192  &pinmux, kTopEarlgreyMuxedPadsIoa7, kDifPinmuxPadKindMio));
193 
195  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd0, kDifPinmuxPadKindDio));
196  CHECK_DIF_OK(dif_pinmux_pad_sleep_disable(
197  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd0, kDifPinmuxPadKindDio));
199  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd1, kDifPinmuxPadKindDio));
200  CHECK_DIF_OK(dif_pinmux_pad_sleep_disable(
201  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd1, kDifPinmuxPadKindDio));
203  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd2, kDifPinmuxPadKindDio));
204  CHECK_DIF_OK(dif_pinmux_pad_sleep_disable(
205  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd2, kDifPinmuxPadKindDio));
207  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd3, kDifPinmuxPadKindDio));
208  CHECK_DIF_OK(dif_pinmux_pad_sleep_disable(
209  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd3, kDifPinmuxPadKindDio));
210 
211  // Phase2: spi wake-up test
212  // Configures to wake up when the spi cs goes low.
213  wakeup_cfg = (dif_pinmux_wakeup_config_t){
215  .signal_filter = false,
216  .pad_type = kDifPinmuxPadKindDio,
217  .pad_select = kTopEarlgreyDirectPadsSpiDeviceCsb,
218  };
219  CHECK_DIF_OK(
220  dif_pinmux_wakeup_detector_enable(&pinmux, detector, wakeup_cfg));
221  configure_spi_flash_mode();
222  enter_low_power();
223  CHECK_DIF_OK(dif_pinmux_wakeup_detector_disable(&pinmux, detector));
224 
225  return true;
226 }
227 
228 /**
229  * External interrupt handler.
230  */
231 void ottf_external_isr(uint32_t *exc_info) {
232  dif_pwrmgr_irq_t irq_id;
234 
235  isr_testutils_pwrmgr_isr(plic_ctx, pwrmgr_isr_ctx, &peripheral, &irq_id);
236 
237  // Check that both the peripheral and the irq id is correct
238  CHECK(peripheral == kTopEarlgreyPlicPeripheralPwrmgrAon,
239  "IRQ peripheral: %d is incorrect", peripheral);
240  CHECK(irq_id == kDifPwrmgrIrqWakeup, "IRQ ID: %d is incorrect", irq_id);
241 }