|  | // Copyright lowRISC contributors. | 
|  | // Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  | // | 
|  | // This is a DV specific pwrmgr usbdev smoketest that fakes usb activity. | 
|  | // Using the phy drive function of usbdev, we present the usbdev is in suspend | 
|  | // and thus trigger low power entry. | 
|  | // Once the system enters low power entry, the incoming USBP/USBN lines are | 
|  | // immediately different from what the wake module expects and thus causes | 
|  | // a wakeup. | 
|  | // This test just smoke checks the pwrmgr's ability to enter / exit low power | 
|  | // and the usbdev's aon wake function that has been de-coupled from the main | 
|  | // usbdev. | 
|  |  | 
|  | #include "sw/device/lib/base/mmio.h" | 
|  | #include "sw/device/lib/dif/dif_pwrmgr.h" | 
|  | #include "sw/device/lib/dif/dif_usbdev.h" | 
|  | #include "sw/device/lib/runtime/hart.h" | 
|  | #include "sw/device/lib/runtime/log.h" | 
|  | #include "sw/device/lib/testing/pwrmgr_testutils.h" | 
|  | #include "sw/device/lib/testing/test_framework/check.h" | 
|  | #include "sw/device/lib/testing/test_framework/ottf_main.h" | 
|  |  | 
|  | #include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" | 
|  |  | 
|  | static dif_pwrmgr_t pwrmgr; | 
|  | static dif_usbdev_t usbdev; | 
|  |  | 
|  | OTTF_DEFINE_TEST_CONFIG(); | 
|  |  | 
|  | static bool compare_wakeup_reasons(dif_pwrmgr_wakeup_reason_t lhs, | 
|  | dif_pwrmgr_wakeup_reason_t rhs) { | 
|  | return lhs.types == rhs.types && lhs.request_sources == rhs.request_sources; | 
|  | } | 
|  |  | 
|  | bool test_main(void) { | 
|  | CHECK_DIF_OK(dif_pwrmgr_init( | 
|  | mmio_region_from_addr(TOP_EARLGREY_PWRMGR_AON_BASE_ADDR), &pwrmgr)); | 
|  | CHECK_DIF_OK(dif_usbdev_init( | 
|  | mmio_region_from_addr(TOP_EARLGREY_USBDEV_BASE_ADDR), &usbdev)); | 
|  |  | 
|  | // Assuming the chip hasn't slept yet, wakeup reason should be empty. | 
|  | dif_pwrmgr_wakeup_reason_t wakeup_reason; | 
|  | CHECK_DIF_OK(dif_pwrmgr_wakeup_reason_get(&pwrmgr, &wakeup_reason)); | 
|  |  | 
|  | const dif_pwrmgr_wakeup_reason_t exp_por_wakeup_reason = { | 
|  | .types = 0, | 
|  | .request_sources = 0, | 
|  | }; | 
|  |  | 
|  | const dif_pwrmgr_wakeup_reason_t exp_test_wakeup_reason = { | 
|  | .types = kDifPwrmgrWakeupTypeRequest, | 
|  | .request_sources = kDifPwrmgrWakeupRequestSourceFour, | 
|  | }; | 
|  |  | 
|  | bool low_power_exit = false; | 
|  | if (compare_wakeup_reasons(wakeup_reason, exp_por_wakeup_reason)) { | 
|  | LOG_INFO("Powered up for the first time, begin test"); | 
|  | } else if (compare_wakeup_reasons(wakeup_reason, exp_test_wakeup_reason)) { | 
|  | low_power_exit = true; | 
|  | LOG_INFO("USB wakeup detected"); | 
|  | } else { | 
|  | LOG_ERROR("Unexpected wakeup reason (types: %02x, sources: %08x)", | 
|  | wakeup_reason.types, wakeup_reason.request_sources); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Fake low power entry through usb | 
|  | // Force wake detection module active | 
|  | if (!low_power_exit) { | 
|  | CHECK_DIF_OK(dif_usbdev_set_wake_enable(&usbdev, kDifToggleDisabled)); | 
|  | dif_usbdev_phy_pins_drive_t pins = { | 
|  | .dp_pullup_en = true, | 
|  | .dn_pullup_en = false, | 
|  | }; | 
|  | CHECK_DIF_OK( | 
|  | dif_usbdev_set_phy_pins_state(&usbdev, kDifToggleEnabled, pins)); | 
|  | CHECK_DIF_OK(dif_usbdev_set_wake_enable(&usbdev, kDifToggleEnabled)); | 
|  |  | 
|  | // give the hardware a chance to recognize the wakeup values are the same | 
|  | busy_spin_micros(20);  // 20us | 
|  |  | 
|  | // Enable low power on the next WFI with default settings. | 
|  | pwrmgr_testutils_enable_low_power( | 
|  | &pwrmgr, kDifPwrmgrWakeupRequestSourceFour, | 
|  | kDifPwrmgrDomainOptionUsbClockInActivePower); | 
|  |  | 
|  | // Enter low power mode. | 
|  | wait_for_interrupt(); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } |