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 
25 OTTF_DEFINE_TEST_CONFIG();
26 
27 static dif_pinmux_t pinmux;
28 static dif_gpio_t gpio;
29 static dif_spi_device_handle_t spid;
30 static dif_pwrmgr_t pwrmgr;
31 static dif_aon_timer_t aon_timer;
32 static dif_rv_plic_t plic;
33 
34 enum {
35  kPlicTarget = 0,
36 };
37 
38 static const dt_pwrmgr_t kPwrmgrDt = 0;
39 static_assert(kDtPwrmgrCount == 1, "this test expects a pwrmgr");
40 static const dt_rv_plic_t kRvPlicDt = 0;
41 static_assert(kDtRvPlicCount == 1, "this test expects exactly one rv_plic");
42 static const dt_pinmux_t kPinmuxDt = 0;
43 static_assert(kDtPinmuxCount == 1, "this test expects exactly one pinmux");
44 static const dt_aon_timer_t kAonTimerDt = 0;
45 static_assert(kDtAonTimerCount == 1, "this test expects an aon_timer");
46 static const dt_gpio_t kGpioDt = 0;
47 static_assert(kDtGpioCount == 1, "this test expects a gpio");
48 static const dt_spi_device_t kSpiDeviceDt = 0;
49 static_assert(kDtSpiDeviceCount >= 1,
50  "this test expects at least one spi_device");
51 
52 static status_t enter_low_power(void) {
53  dif_pwrmgr_domain_config_t pwrmgr_domain_cfg =
55  kDifPwrmgrDomainOptionMainPowerInLowPower;
56  // Wait for the host signal that the device can sleep.
57  bool sleep = false;
58  do {
59  CHECK_DIF_OK(dif_gpio_read(&gpio, 0, &sleep));
60  } while (!sleep);
61 
62  dif_pwrmgr_request_sources_t wakeup_sources;
63  CHECK_DIF_OK(dif_pwrmgr_find_request_source(
64  &pwrmgr, kDifPwrmgrReqTypeWakeup, dt_pinmux_instance_id(kPinmuxDt),
65  kDtPinmuxWakeupPinWkupReq, &wakeup_sources));
66 
67  irq_global_ctrl(false);
68  LOG_INFO("SYNC: Sleeping");
69  TRY(pwrmgr_testutils_enable_low_power(&pwrmgr, wakeup_sources,
70  pwrmgr_domain_cfg));
72 
73  TRY_CHECK(UNWRAP(pwrmgr_testutils_is_wakeup_reason(&pwrmgr, wakeup_sources)));
74  TRY(dif_pwrmgr_wakeup_reason_clear(&pwrmgr));
75 
76  TRY(dif_pinmux_wakeup_cause_clear(&pinmux));
77 
78  // Sometimes execution continues after the wfi while the core is preparing to
79  // enter low power mode. In that case we loop checking if the host requested a
80  // sleep. If not we loop until core enters low power mode.
81  do {
82  CHECK_DIF_OK(dif_gpio_read(&gpio, 0, &sleep));
83  } while (sleep);
84  irq_global_ctrl(true);
85  LOG_INFO("SYNC: Awaked");
86  return OK_STATUS();
87 }
88 
89 static status_t configure_spi_flash_mode(void) {
91  .device_id = 0x2298,
92  .manufacturer_id = 0x74,
93  .continuation_code = 0x17,
94  .num_continuation_code = 2,
95  };
96  TRY(dif_spi_device_set_flash_id(&spid, id));
97 
98  dif_spi_device_config_t spi_device_config = {
100  .rx_order = kDifSpiDeviceBitOrderMsbToLsb,
101  .device_mode = kDifSpiDeviceModeFlashEmulation,
102  };
103  TRY(dif_spi_device_configure(&spid, spi_device_config));
104 
105  // Configure the READ_JEDEC_ID command (CMD_INFO_3).
107  .opcode = kSpiDeviceFlashOpReadJedec,
108  .address_type = kDifSpiDeviceFlashAddrDisabled,
109  .dummy_cycles = 0,
110  .payload_io_type = kDifSpiDevicePayloadIoSingle,
111  .payload_dir_to_host = false,
112  .upload = false,
113  };
114 
116  &spid, kSpiDeviceReadCommandSlotBase + 3, kDifToggleEnabled, cmd));
117 
118  LOG_INFO("SYNC: Flash mode");
119  return OK_STATUS();
120 }
121 
122 bool test_main(void) {
123  CHECK_DIF_OK(dif_spi_device_init_handle_from_dt(kSpiDeviceDt, &spid));
124 
125  CHECK_DIF_OK(dif_pwrmgr_init_from_dt(kPwrmgrDt, &pwrmgr));
126 
127  CHECK_DIF_OK(dif_aon_timer_init_from_dt(kAonTimerDt, &aon_timer));
128 
129  CHECK_DIF_OK(dif_pinmux_init_from_dt(kPinmuxDt, &pinmux));
130 
131  CHECK_DIF_OK(dif_rv_plic_init_from_dt(kRvPlicDt, &plic));
132 
133  CHECK_DIF_OK(dif_gpio_init_from_dt(kGpioDt, &gpio));
134  CHECK_DIF_OK(dif_gpio_output_set_enabled_all(&gpio, 0x1));
135 
136  // Enable all the AON interrupts used to wake up the core.
137  dif_rv_plic_irq_id_t plic_id =
138  dt_pwrmgr_irq_to_plic_id(kPwrmgrDt, kDtPwrmgrIrqWakeup);
139  rv_plic_testutils_irq_range_enable(&plic, kPlicTarget, plic_id, plic_id);
140  CHECK_DIF_OK(dif_pwrmgr_irq_set_enabled(&pwrmgr, 0, kDifToggleEnabled));
141 
142  irq_global_ctrl(true);
143  irq_external_ctrl(true);
144 
145  dif_pinmux_index_t detector = 0;
146  dif_pinmux_wakeup_config_t wakeup_cfg = {
148  .signal_filter = false,
149  .pad_type = kDifPinmuxPadKindMio,
150  .pad_select = kTopEarlgreyPinmuxInselIoa8,
151  };
152  CHECK_DIF_OK(
153  dif_pinmux_wakeup_detector_enable(&pinmux, detector, wakeup_cfg));
154 
155  // Phase1: spi sleep test
156  LOG_INFO("Setting SPI_DIO1 to high when sleeping");
157  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
158  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd0, kDifPinmuxPadKindDio,
160  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
161  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd1, kDifPinmuxPadKindDio,
163  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
164  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd2, kDifPinmuxPadKindDio,
166  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
167  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd3, kDifPinmuxPadKindDio,
169  LOG_INFO("Use IOA7 to let host know when sleep is active.");
170  CHECK_DIF_OK(dif_pinmux_input_select(&pinmux,
173  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(&pinmux, kTopEarlgreyMuxedPadsIoa7,
176  enter_low_power();
178  &pinmux, kTopEarlgreyMuxedPadsIoa7, kDifPinmuxPadKindMio));
179 
180  LOG_INFO("Setting SPI_DIO1 to low when sleeping");
181  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
182  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd0, kDifPinmuxPadKindDio,
184  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
185  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd1, kDifPinmuxPadKindDio,
187  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
188  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd2, kDifPinmuxPadKindDio,
190  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(
191  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd3, kDifPinmuxPadKindDio,
193 
194  CHECK_DIF_OK(dif_pinmux_pad_sleep_enable(&pinmux, kTopEarlgreyMuxedPadsIoa7,
197  enter_low_power();
199  &pinmux, kTopEarlgreyMuxedPadsIoa7, kDifPinmuxPadKindMio));
200 
202  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd0, kDifPinmuxPadKindDio));
203  CHECK_DIF_OK(dif_pinmux_pad_sleep_disable(
204  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd0, kDifPinmuxPadKindDio));
206  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd1, kDifPinmuxPadKindDio));
207  CHECK_DIF_OK(dif_pinmux_pad_sleep_disable(
208  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd1, kDifPinmuxPadKindDio));
210  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd2, kDifPinmuxPadKindDio));
211  CHECK_DIF_OK(dif_pinmux_pad_sleep_disable(
212  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd2, kDifPinmuxPadKindDio));
214  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd3, kDifPinmuxPadKindDio));
215  CHECK_DIF_OK(dif_pinmux_pad_sleep_disable(
216  &pinmux, kTopEarlgreyDirectPadsSpiDeviceSd3, kDifPinmuxPadKindDio));
217 
218  // Phase2: spi wake-up test
219  // Configures to wake up when the spi cs goes low.
220  wakeup_cfg = (dif_pinmux_wakeup_config_t){
222  .signal_filter = false,
223  .pad_type = kDifPinmuxPadKindDio,
224  .pad_select = kTopEarlgreyDirectPadsSpiDeviceCsb,
225  };
226  CHECK_DIF_OK(
227  dif_pinmux_wakeup_detector_enable(&pinmux, detector, wakeup_cfg));
228  configure_spi_flash_mode();
229  enter_low_power();
230  CHECK_DIF_OK(dif_pinmux_wakeup_detector_disable(&pinmux, detector));
231 
232  return true;
233 }
234 
235 /**
236  * External interrupt handler.
237  */
238 bool ottf_handle_irq(uint32_t *exc_info, dt_instance_id_t devid,
239  dif_rv_plic_irq_id_t irq_id) {
240  if (devid == dt_pwrmgr_instance_id(kPwrmgrDt) &&
241  irq_id == dt_pwrmgr_irq_to_plic_id(kPwrmgrDt, kDtPwrmgrIrqWakeup)) {
242  CHECK_DIF_OK(dif_pwrmgr_irq_acknowledge(&pwrmgr, kDtPwrmgrIrqWakeup));
243  return true;
244  } else {
245  return false;
246  }
247 }