Просмотр исходного кода

Merge branch 'feature/esp32c6_light_sleep_support_ieee802154' into 'master'

ieee802154: esp32c6 light sleep support

See merge request espressif/esp-idf!23629
Shu Chen 2 лет назад
Родитель
Сommit
f64a6bcb6a
27 измененных файлов с 537 добавлено и 23 удалено
  1. 1 0
      components/esp_hw_support/include/esp_private/esp_regdma.h
  2. 1 2
      components/esp_hw_support/include/esp_private/sleep_retention.h
  3. 3 3
      components/esp_hw_support/sleep_clock.c
  4. 2 2
      components/esp_hw_support/sleep_modem.c
  5. 0 1
      components/esp_hw_support/sleep_retention.c
  6. 2 2
      components/esp_phy/src/btbb_init.c
  7. 8 0
      components/ieee802154/Kconfig
  8. 56 3
      components/ieee802154/driver/esp_ieee802154_dev.c
  9. 13 1
      components/ieee802154/esp_ieee802154.c
  10. 10 0
      components/ieee802154/include/esp_ieee802154.h
  11. 1 0
      components/ieee802154/include/esp_ieee802154_types.h
  12. 13 0
      components/ieee802154/private_include/esp_ieee802154_dev.h
  13. 7 7
      components/ieee802154/test_apps/test_ieee802154/pytest_test_ieee802154.py
  14. 5 0
      components/openthread/CMakeLists.txt
  15. 42 0
      components/openthread/private_include/esp_openthread_sleep.h
  16. 12 1
      components/openthread/src/esp_openthread.cpp
  17. 1 1
      components/openthread/src/port/esp_openthread_radio.c
  18. 52 0
      components/openthread/src/port/esp_openthread_sleep.c
  19. 9 0
      examples/openthread/.build-test-rules.yml
  20. 2 0
      examples/openthread/README.md
  21. 6 0
      examples/openthread/ot_sleepy_device/CMakeLists.txt
  22. 40 0
      examples/openthread/ot_sleepy_device/README.md
  23. 2 0
      examples/openthread/ot_sleepy_device/main/CMakeLists.txt
  24. 140 0
      examples/openthread/ot_sleepy_device/main/esp_ot_sleepy_device.c
  25. 53 0
      examples/openthread/ot_sleepy_device/main/esp_ot_sleepy_device_config.h
  26. 5 0
      examples/openthread/ot_sleepy_device/partitions.csv
  27. 51 0
      examples/openthread/ot_sleepy_device/sdkconfig.defaults

+ 1 - 0
components/esp_hw_support/include/esp_private/esp_regdma.h

@@ -41,6 +41,7 @@ extern "C" {
 #define REGDMA_SYSTIMER_LINK(_pri)          ((0x14 << 8) | _pri)
 #define REGDMA_BLE_MAC_LINK(_pri)           ((0x15 << 8) | _pri)
 #define REGDMA_MODEM_BT_BB_LINK(_pri)       ((0x16 << 8) | _pri)
+#define REGDMA_MODEM_IEEE802154_LINK(_pri)  ((0x17 << 8) | _pri)
 #define REGDMA_MODEM_FE_LINK(_pri)          ((0xFF << 8) | _pri)
 
 typedef enum {

+ 1 - 2
components/esp_hw_support/include/esp_private/sleep_retention.h

@@ -32,9 +32,8 @@ typedef enum sleep_retention_module_bitmap {
     SLEEP_RETENTION_MODULE_WIFI_MAC     = BIT(10),
     SLEEP_RETENTION_MODULE_WIFI_BB      = BIT(11),
     SLEEP_RETENTION_MODULE_BLE_MAC      = BIT(12),
-    SLEEP_RETENTION_MODULE_BLE_BB       = BIT(13),
+    SLEEP_RETENTION_MODULE_BT_BB        = BIT(13),
     SLEEP_RETENTION_MODULE_802154_MAC   = BIT(14),
-    SLEEP_RETENTION_MODULE_802154_BB    = BIT(15),
 
     /* digital peripheral module, which includes Interrupt Matrix, HP_SYSTEM,
      * TEE, APM, UART, Timer Group, IOMUX, SPIMEM, SysTimer, etc.. */

+ 3 - 3
components/esp_hw_support/sleep_clock.c

@@ -71,21 +71,21 @@ bool IRAM_ATTR clock_domain_pd_allowed(void)
     const uint32_t modules = sleep_retention_get_modules();
     const uint32_t mask = (const uint32_t) (
             SLEEP_RETENTION_MODULE_CLOCK_SYSTEM
-#if CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE
+#if CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE || CONFIG_IEEE802154_SLEEP_ENABLE
           | SLEEP_RETENTION_MODULE_CLOCK_MODEM
 #endif
           );
     return ((modules & mask) == mask);
 }
 
-#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP || CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE
+#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP || CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE || CONFIG_IEEE802154_SLEEP_ENABLE
 ESP_SYSTEM_INIT_FN(sleep_clock_startup_init, BIT(0), 106)
 {
 #if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
     sleep_clock_system_retention_init();
 #endif
 
-#if CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE
+#if CONFIG_MAC_BB_PD || CONFIG_BT_LE_SLEEP_ENABLE || CONFIG_IEEE802154_SLEEP_ENABLE
     sleep_clock_modem_retention_init();
 #endif
     return ESP_OK;

+ 2 - 2
components/esp_hw_support/sleep_modem.c

@@ -279,9 +279,9 @@ bool IRAM_ATTR modem_domain_pd_allowed(void)
     const uint32_t mask_wifi = (const uint32_t) (SLEEP_RETENTION_MODULE_WIFI_MAC |
                                                  SLEEP_RETENTION_MODULE_WIFI_BB);
     const uint32_t mask_ble = (const uint32_t) (SLEEP_RETENTION_MODULE_BLE_MAC |
-                                                SLEEP_RETENTION_MODULE_BLE_BB);
+                                                SLEEP_RETENTION_MODULE_BT_BB);
     const uint32_t mask_154 = (const uint32_t) (SLEEP_RETENTION_MODULE_802154_MAC |
-                                                SLEEP_RETENTION_MODULE_802154_BB);
+                                                SLEEP_RETENTION_MODULE_BT_BB);
     return (((modules & mask_wifi) == mask_wifi) ||
             ((modules & mask_ble)  == mask_ble) ||
             ((modules & mask_154)  == mask_154));

+ 0 - 1
components/esp_hw_support/sleep_retention.c

@@ -21,7 +21,6 @@
 #include "sdkconfig.h"
 #include "esp_pmu.h"
 
-
 static __attribute__((unused)) const char *TAG = "sleep";
 
 /**

+ 2 - 2
components/esp_phy/src/btbb_init.c

@@ -35,7 +35,7 @@ static esp_err_t btbb_sleep_retention_init(void)
         [1] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_MODEM_BT_BB_LINK(0x01), BB_PART_1_ADDR, BB_PART_1_ADDR, BB_PART_1_SIZE, 0, 0), .owner = BTBB_LINK_OWNER },
         [2] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_MODEM_BT_BB_LINK(0x02), BB_PART_2_ADDR, BB_PART_2_ADDR, BB_PART_2_SIZE, 0, 0), .owner = BTBB_LINK_OWNER }
     };
-    esp_err_t err = sleep_retention_entries_create(btbb_regs_retention, ARRAY_SIZE(btbb_regs_retention), REGDMA_LINK_PRI_5, SLEEP_RETENTION_MODULE_BLE_BB);
+    esp_err_t err = sleep_retention_entries_create(btbb_regs_retention, ARRAY_SIZE(btbb_regs_retention), REGDMA_LINK_PRI_5, SLEEP_RETENTION_MODULE_BT_BB);
     ESP_RETURN_ON_ERROR(err, TAG, "failed to allocate memory for btbb retention");
     ESP_LOGI(TAG, "btbb sleep retention initialization");
     return ESP_OK;
@@ -43,7 +43,7 @@ static esp_err_t btbb_sleep_retention_init(void)
 
 static void btbb_sleep_retention_deinit(void)
 {
-    sleep_retention_entries_destroy(SLEEP_RETENTION_MODULE_BLE_BB);
+    sleep_retention_entries_destroy(SLEEP_RETENTION_MODULE_BT_BB);
 }
 #endif // SOC_PM_MODEM_RETENTION_BY_REGDMA && CONFIG_FREERTOS_USE_TICKLESS_IDLE && !CONFIG_IDF_TARGET_ESP32H2
 

+ 8 - 0
components/ieee802154/Kconfig

@@ -74,4 +74,12 @@ menu "IEEE 802.15.4"
             Enabling this option increases throughput by ~5% at the expense of ~2.1k
             IRAM code size increase.
 
+    config IEEE802154_SLEEP_ENABLE
+        # Todo: Remove when support safe power-down of the power domain (IDF-7317)
+        bool "Enable IEEE802154 light sleep"
+        depends on PM_ENABLE && PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
+        default n
+        help
+            Enabling this option allows the IEEE802.15.4 module to be powered down during automatic light sleep,
+            which reduces current consumption.
 endmenu  # IEEE 802.15.4

+ 56 - 3
components/ieee802154/driver/esp_ieee802154_dev.c

@@ -24,6 +24,13 @@
 #include "esp_ieee802154_timer.h"
 #include "hal/ieee802154_ll.h"
 #include "esp_attr.h"
+#include "esp_phy_init.h"
+
+#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
+#include "esp_pm.h"
+#include "esp_private/esp_clk.h"
+#include "esp_private/sleep_retention.h"
+#endif
 
 #define CCA_DETECTION_TIME 8
 
@@ -38,6 +45,8 @@ static uint8_t s_enh_ack_frame[128];
 static uint8_t s_recent_rx_frame_info_index;
 static portMUX_TYPE s_ieee802154_spinlock = portMUX_INITIALIZER_UNLOCKED;
 
+static esp_err_t ieee802154_sleep_init(void);
+
 static IRAM_ATTR void event_end_process(void)
 {
     ieee802154_etm_channel_clear(IEEE802154_ETM_CHANNEL0);
@@ -193,7 +202,11 @@ static bool stop_current_operation(void)
         break;
 
     case IEEE802154_STATE_IDLE:
-        // do nothing
+        ieee802154_ll_set_cmd(IEEE802154_CMD_STOP);
+        break;
+
+    case IEEE802154_STATE_SLEEP:
+        // Do nothing
         break;
 
     case IEEE802154_STATE_RX:
@@ -555,10 +568,12 @@ static IRAM_ATTR void ieee802154_exit_critical(void)
 void ieee802154_enable(void)
 {
     modem_clock_module_enable(ieee802154_periph.module);
+    s_ieee802154_state = IEEE802154_STATE_IDLE;
 }
 
 void ieee802154_disable(void)
 {
+    modem_clock_module_disable(ieee802154_periph.module);
     s_ieee802154_state = IEEE802154_STATE_DISABLE;
 }
 
@@ -589,12 +604,13 @@ esp_err_t ieee802154_mac_init(void)
 #endif
 
     memset(s_rx_frame, 0, sizeof(s_rx_frame));
-    s_ieee802154_state = IEEE802154_STATE_IDLE;
 
     // TODO: Add flags for IEEE802154 ISR allocating. TZ-102
     ret = esp_intr_alloc(ieee802154_periph.irq_id, 0, ieee802154_isr, NULL, NULL);
     ESP_RETURN_ON_FALSE(ret == ESP_OK, ESP_FAIL, IEEE802154_TAG, "IEEE802154 MAC init failed");
 
+    ESP_RETURN_ON_FALSE(ieee802154_sleep_init() == ESP_OK, ESP_FAIL, IEEE802154_TAG, "IEEE802154 MAC sleep init failed");
+
     return ret;
 }
 
@@ -716,12 +732,49 @@ esp_err_t ieee802154_receive_at(uint32_t time)
     return ESP_OK;
 }
 
+static esp_err_t ieee802154_sleep_init(void)
+{
+    esp_err_t err = ESP_OK;
+#if SOC_PM_MODEM_RETENTION_BY_REGDMA && CONFIG_FREERTOS_USE_TICKLESS_IDLE
+    #define N_REGS_IEEE802154() (((IEEE802154_MAC_DATE_REG - IEEE802154_REG_BASE) / 4) + 1)
+    const static sleep_retention_entries_config_t ieee802154_mac_regs_retention[] = {
+        [0] = { .config = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_MODEM_IEEE802154_LINK(0x00), IEEE802154_REG_BASE, IEEE802154_REG_BASE, N_REGS_IEEE802154(), 0, 0), .owner = ENTRY(3) },
+    };
+    err = sleep_retention_entries_create(ieee802154_mac_regs_retention, ARRAY_SIZE(ieee802154_mac_regs_retention), REGDMA_LINK_PRI_7, SLEEP_RETENTION_MODULE_802154_MAC);
+    ESP_RETURN_ON_ERROR(err, IEEE802154_TAG, "failed to allocate memory for ieee802154 mac retention");
+    ESP_LOGI(IEEE802154_TAG, "ieee802154 mac sleep retention initialization");
+#endif
+    return err;
+}
+
+IRAM_ATTR void ieee802154_enter_sleep(void)
+{
+#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
+    esp_phy_disable();
+#if SOC_PM_RETENTION_HAS_CLOCK_BUG
+    sleep_retention_do_extra_retention(true);// backup
+#endif
+    ieee802154_disable(); // IEEE802154 CLOCK Disable
+#endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE
+}
+
+IRAM_ATTR void ieee802154_wakeup(void)
+{
+#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
+    ieee802154_enable(); // IEEE802154 CLOCK Enable
+#if SOC_PM_RETENTION_HAS_CLOCK_BUG
+    sleep_retention_do_extra_retention(false);// restore
+#endif
+    esp_phy_enable();
+#endif //CONFIG_FREERTOS_USE_TICKLESS_IDLE
+}
+
 esp_err_t ieee802154_sleep(void)
 {
     ieee802154_enter_critical();
 
     stop_current_operation();
-    s_ieee802154_state = IEEE802154_STATE_IDLE;
+    s_ieee802154_state = IEEE802154_STATE_SLEEP;
 
     ieee802154_exit_critical();
     return ESP_OK;

+ 13 - 1
components/ieee802154/esp_ieee802154.c

@@ -21,7 +21,6 @@
 
 esp_err_t esp_ieee802154_enable(void)
 {
-
     ieee802154_enable();
     esp_phy_enable();
     esp_btbb_enable();
@@ -281,6 +280,9 @@ esp_ieee802154_state_t esp_ieee802154_get_state(void)
         return ESP_IEEE802154_RADIO_DISABLE;
 
     case IEEE802154_STATE_IDLE:
+        return ESP_IEEE802154_RADIO_IDLE;
+
+    case IEEE802154_STATE_SLEEP:
         return ESP_IEEE802154_RADIO_SLEEP;
 
     case IEEE802154_STATE_RX:
@@ -332,6 +334,16 @@ uint8_t esp_ieee802154_get_recent_lqi(void)
     return ieee802154_get_recent_lqi();
 }
 
+void esp_ieee802154_enter_sleep(void)
+{
+    ieee802154_enter_sleep();
+}
+
+void esp_ieee802154_wakeup(void)
+{
+    ieee802154_wakeup();
+}
+
 __attribute__((weak)) void esp_ieee802154_receive_done(uint8_t *data, esp_ieee802154_frame_info_t *frame_info)
 {
 

+ 10 - 0
components/ieee802154/include/esp_ieee802154.h

@@ -111,6 +111,16 @@ esp_ieee802154_state_t esp_ieee802154_get_state(void);
  */
 esp_err_t esp_ieee802154_sleep(void);
 
+/**
+ * @brief  The IEEE 802.15.4 enter sleep.
+ */
+void esp_ieee802154_enter_sleep(void);
+
+/**
+ * @brief  The IEEE 802.15.4 wakeup.
+ */
+void esp_ieee802154_wakeup(void);
+
 /**
  * @brief  Set the IEEE 802.15.4 Radio to receive state.
  *

+ 1 - 0
components/ieee802154/include/esp_ieee802154_types.h

@@ -18,6 +18,7 @@ extern "C" {
  */
 typedef enum {
     ESP_IEEE802154_RADIO_DISABLE,   /*!< Radio not up */
+    ESP_IEEE802154_RADIO_IDLE,      /*!< Radio in the idle state */
     ESP_IEEE802154_RADIO_SLEEP,     /*!< Radio in the sleep state */
     ESP_IEEE802154_RADIO_RECEIVE,   /*!< Radio in the receive state */
     ESP_IEEE802154_RADIO_TRANSMIT,  /*!< Radio in the transmit state */

+ 13 - 0
components/ieee802154/private_include/esp_ieee802154_dev.h

@@ -31,6 +31,7 @@ extern "C" {
 typedef enum {
     IEEE802154_STATE_DISABLE,               /*!< IEEE802154 radio state disable */
     IEEE802154_STATE_IDLE,                  /*!< IEEE802154 radio state idle */
+    IEEE802154_STATE_SLEEP,                 /*!< IEEE802154 radio state sleep */
     IEEE802154_STATE_RX,                    /*!< IEEE802154 radio state rx */
     IEEE802154_STATE_TX_ACK,                /*!< IEEE802154 radio state tx ack */
     IEEE802154_STATE_TX_ENH_ACK,            /*!< IEEE802154 radio state tx enh-ack */
@@ -177,6 +178,18 @@ uint8_t ieee802154_get_recent_lqi(void);
  */
 ieee802154_state_t ieee802154_get_state(void);
 
+/**
+ * @brief  The IEEE 802.15.4 enter sleep.
+ *
+ */
+void ieee802154_enter_sleep(void);
+
+/**
+ * @brief  The IEEE 802.15.4 wakeup.
+ *
+ */
+void ieee802154_wakeup(void);
+
 /** The following three functions are only used for internal test. **/
 /**
  * @brief  The clear channel assessment done.

+ 7 - 7
components/ieee802154/test_apps/test_ieee802154/pytest_test_ieee802154.py

@@ -129,17 +129,17 @@ def test_based_txrx(dut: Tuple[IdfDut, IdfDut]) -> None:
     receive.expect('RX Start', timeout=10)
     transmit.expect('ieee802154>', timeout=10)
     transmit.write('tx -l 10')
-    transmit.expect('tx sfd done, Radio state: 3', timeout=10)
+    transmit.expect('tx sfd done, Radio state: 4', timeout=10)
     transmit.expect('Tx Done 10 bytes', timeout=10)
     transmit.expect('00 01 02 03 04 05 06 07', timeout=10)
     transmit.expect('08 09 00 00 00 00 00 00', timeout=10)
-    receive.expect('rx sfd done, Radio state: 2', timeout=10)
+    receive.expect('rx sfd done, Radio state: 3', timeout=10)
     receive.expect('Rx Done 10 bytes', timeout=10)
     receive.expect('00 01 02 03 04 05 06 07', timeout=10)
     receive.write('rx -r 0')
     receive.expect('radio exit receive mode', timeout=10)
     transmit.write('tx -l 10')
-    transmit.expect('tx sfd done, Radio state: 3', timeout=10)
+    transmit.expect('tx sfd done, Radio state: 4', timeout=10)
     transmit.expect('Tx Done 10 bytes', timeout=10)
     transmit.expect('00 01 02 03 04 05 06 07', timeout=10)
     transmit.expect('08 09 00 00 00 00 00 00', timeout=10)
@@ -344,13 +344,13 @@ def test_based_autoack(dut: Tuple[IdfDut, IdfDut]) -> None:
     receive.expect('RX Start', timeout=10)
 
     transmit.write('tx 0x20 0x88 0x00 0x0A 0x28 0xDB 0x6F 0xBC 0x94 0x5A 0x43 0x68 0x02 0xaa 0x15 0x30 0x01 0x02')
-    transmit.expect('tx sfd done, Radio state: 3', timeout=10)
-    transmit.expect('rx sfd done, Radio state: 3', timeout=10)
+    transmit.expect('tx sfd done, Radio state: 4', timeout=10)
+    transmit.expect('rx sfd done, Radio state: 4', timeout=10)
     transmit.expect('Tx Done 18 bytes', timeout=10)
     transmit.expect('20 88 00 0a 28 db 6f bc', timeout=10)
     transmit.expect('94 5a 43 68 02 aa 15 30', timeout=10)
 
-    receive.expect('rx sfd done, Radio state: 2', timeout=10)
+    receive.expect('rx sfd done, Radio state: 3', timeout=10)
     receive.expect('Rx Done 18 bytes', timeout=10)
     receive.expect('20 88 00 0a 28 db 6f bc', timeout=10)
     receive.expect('94 5a 43 68 02 aa 15 30', timeout=10)
@@ -573,7 +573,7 @@ def test_based_transmit_failed(dut: IdfDut) -> None:
     transmit.write('tx -l 10 -C')
     transmit.expect('the Frame Transmission failed, Failure reason: 1', timeout=10)
     transmit.write('tx -l 10')
-    transmit.expect('tx sfd done, Radio state: 3', timeout=10)
+    transmit.expect('tx sfd done, Radio state: 4', timeout=10)
     transmit.expect('Tx Done 10 bytes', timeout=10)
     transmit.expect('00 01 02 03 04 05 06 07', timeout=10)
     transmit.expect('08 09 00 00 00 00 00 00', timeout=10)

+ 5 - 0
components/openthread/CMakeLists.txt

@@ -139,6 +139,11 @@ if(CONFIG_OPENTHREAD_ENABLED)
             "src/esp_openthread_dns64.c")
     endif()
 
+    if(NOT CONFIG_FREERTOS_USE_TICKLESS_IDLE)
+        list(APPEND exclude_srcs
+            "src/port/esp_openthread_sleep.c")
+    endif()
+
     if(CONFIG_OPENTHREAD_FTD)
         set(device_type "OPENTHREAD_FTD=1")
     elseif(CONFIG_OPENTHREAD_MTD)

+ 42 - 0
components/openthread/private_include/esp_openthread_sleep.h

@@ -0,0 +1,42 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include "esp_err.h"
+#include "sdkconfig.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef CONFIG_FREERTOS_USE_TICKLESS_IDLE
+/**
+ * @brief This function initializes the OpenThread sleep.
+ *
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_FAIL on failure
+ *
+ */
+esp_err_t esp_openthread_sleep_init(void);
+
+/**
+ * @brief This function performs the OpenThread sleep process.
+ *
+ */
+void esp_openthread_sleep_process(void);
+
+/**
+ * @brief This function performs the OpenThread wakeup process.
+ *
+ */
+void esp_openthread_wakeup_process(void);
+#endif
+
+#ifdef __cplusplus
+}
+#endif

+ 12 - 1
components/openthread/src/esp_openthread.cpp

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -11,6 +11,7 @@
 #include "esp_openthread_dns64.h"
 #include "esp_openthread_lock.h"
 #include "esp_openthread_platform.h"
+#include "esp_openthread_sleep.h"
 #include "esp_openthread_task_queue.h"
 #include "esp_openthread_types.h"
 #include "freertos/FreeRTOS.h"
@@ -58,6 +59,10 @@ esp_err_t esp_openthread_init(const esp_openthread_platform_config_t *config)
 {
     ESP_RETURN_ON_ERROR(esp_openthread_platform_init(config), OT_PLAT_LOG_TAG,
                         "Failed to initialize OpenThread platform driver");
+#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
+    ESP_RETURN_ON_ERROR(esp_openthread_sleep_init(), OT_PLAT_LOG_TAG,
+                        "Failed to initialize OpenThread esp pm_lock");
+#endif
     esp_openthread_lock_acquire(portMAX_DELAY);
     ESP_RETURN_ON_FALSE(otInstanceInitSingle() != NULL, ESP_FAIL, OT_PLAT_LOG_TAG,
                         "Failed to initialize OpenThread instance");
@@ -154,11 +159,17 @@ esp_err_t esp_openthread_launch_mainloop(void)
             mainloop.timeout.tv_sec = 0;
             mainloop.timeout.tv_usec = 0;
         }
+#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
+        esp_openthread_sleep_process();
+#endif
         esp_openthread_lock_release();
 
         if (select(mainloop.max_fd + 1, &mainloop.read_fds, &mainloop.write_fds, &mainloop.error_fds,
                    &mainloop.timeout) >= 0) {
             esp_openthread_lock_acquire(portMAX_DELAY);
+#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
+            esp_openthread_wakeup_process();
+#endif
             error = esp_openthread_platform_process(instance, &mainloop);
             while (otTaskletsArePending(instance)) {
                 otTaskletsProcess(instance);

+ 1 - 1
components/openthread/src/port/esp_openthread_radio.c

@@ -273,7 +273,7 @@ otError otPlatRadioEnable(otInstance *aInstance)
 
 otError otPlatRadioDisable(otInstance *aInstance)
 {
-    esp_ieee802154_disable();
+    // radio will be disabled in esp_openthread_radio_deinit()
 
     return OT_ERROR_NONE;
 }

+ 52 - 0
components/openthread/src/port/esp_openthread_sleep.c

@@ -0,0 +1,52 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "sdkconfig.h"
+
+#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
+#include "esp_log.h"
+#include "esp_check.h"
+#include "esp_ieee802154.h"
+#include "esp_pm.h"
+static esp_pm_lock_handle_t s_pm_lock = NULL;
+static const char* TAG = "esp openthread sleep";
+static bool s_ot_sleep = false;
+
+esp_err_t esp_openthread_sleep_init(void)
+{
+    esp_err_t err = ESP_OK;
+
+    err = esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "ieee802154", &s_pm_lock);
+    if (err == ESP_OK) {
+        esp_pm_lock_acquire(s_pm_lock);
+        ESP_LOGI(TAG, "Enable ieee802154 light sleep, the wake up source is ESP timer");
+    } else {
+        if (s_pm_lock != NULL) {
+            esp_pm_lock_delete(s_pm_lock);
+            s_pm_lock = NULL;
+        }
+    }
+    return err;
+}
+
+void esp_openthread_sleep_process(void)
+{
+    if (esp_ieee802154_get_state() == ESP_IEEE802154_RADIO_SLEEP) {
+        esp_ieee802154_enter_sleep();
+        esp_pm_lock_release(s_pm_lock);
+        s_ot_sleep = true;
+    }
+}
+
+void esp_openthread_wakeup_process(void)
+{
+    if (s_ot_sleep) {
+        esp_pm_lock_acquire(s_pm_lock);
+        esp_ieee802154_wakeup();
+        s_ot_sleep = false;
+    }
+}
+#endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE

+ 9 - 0
examples/openthread/.build-test-rules.yml

@@ -49,3 +49,12 @@ examples/openthread/ot_rcp:
       temporary: true
       reason: only test on esp32c6
   <<: *openthread_dependencies
+
+examples/openthread/ot_sleepy_device:
+  enable:
+    - if: IDF_TARGET  == "esp32c6"
+  disable_test:
+    - if: IDF_TARGET in ["esp32h2", "esp32c6"]
+      temporary: true
+      reason: No support # TO-DO: TZ-134
+  <<: *openthread_dependencies

+ 2 - 0
examples/openthread/README.md

@@ -11,3 +11,5 @@ In this folder, it contains following OpenThread examples:
 * [ot_rcp](ot_rcp) is an [OpenThread RCP](https://openthread.io/platforms/co-processor) example. It runs on an 802.15.4 SoC like ESP32-H2, to extend 802.15.4 radio.
 
 * [ot_br](ot_br) is an [OpenThread Border Router](https://openthread.io/guides/border-router) example. It runs on a Wi-Fi SoC such as ESP32, ESP32-C3 and ESP32-S3. It needs an 802.15.4 SoC like ESP32-H2 running [ot_rcp](ot_rcp) example to provide 802.15.4 radio.
+
+* [ot_sleepy_device](ot_sleepy_device) is an OpenThread sleepy device example, it supports 802.15.4 radio light sleep. It runs on an 802.15.4 SoC.

+ 6 - 0
examples/openthread/ot_sleepy_device/CMakeLists.txt

@@ -0,0 +1,6 @@
+# The following lines of boilerplate have to be in your project's CMakeLists
+# in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.16)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(ot_sleepy_device)

+ 40 - 0
examples/openthread/ot_sleepy_device/README.md

@@ -0,0 +1,40 @@
+| Supported Targets | ESP32-C6 |
+| ----------------- | -------- |
+
+# OpenThread Sleepy Device Example
+
+The example demonstrates the Thread Sleepy End Device (SED), the device will enter [Light Sleep mode](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c6/api-reference/system/sleep_modes.html#sleep-modes) during idle state.
+## How to use example
+
+### Hardware Required
+
+* Prepare an 802.15.4 SoC development board as an OpenThread Sleepy End Device (SED).
+* Connect the board using a USB cable for power supply and programming.
+* Choose another 802.15.4 SoC as the OpenThread Leader.
+
+## Configure the Openthread Dataset
+
+* Run [ot_cli](../ot_cli/) on another 802.15.4 SoC device to create openthread dataset configuration and start an openthread network as the leader.
+* Configure the Openthread dataset using `idf.py menuconfig` in `Component config ---> Openthread ---> Thread Operation Dataset`, ensuring that the openthread sleepy device's dataset matches the dataset of the leader.
+
+### Build and Flash
+
+Build the project and flash it to the board. Use the following command: `idf.py -p <PORT> erase-flash flash monitor`.
+
+### Example Output
+
+As the example runs, you will see the log output indicating the initialization and operation of OpenThread, including the device joining the OpenThread network as a Sleepy End Device (SED) and periodic polling of the leader.
+
+```
+I (769) btbb_init: btbb sleep retention initialization
+I (769) ieee802154: ieee802154 mac sleep retention initialization
+I(769) OPENTHREAD:[I] ChildSupervsn-: Timeout: 0 -> 190
+I (699) main_task: Returned from app_main()
+I (799) OPENTHREAD: OpenThread attached to netif
+I(799) OPENTHREAD:[N] Mle-----------: Mode 0x0f -> 0x04 [rx-on:no ftd:no full-net:no]
+I(809) OPENTHREAD:[N] Mle-----------: Role disabled -> detached
+I (819) OPENTHREAD: netif up
+I(1519) OPENTHREAD:[N] Mle-----------: Attach attempt 1, AnyPartition reattaching with Active Dataset
+I(2479) OPENTHREAD:[N] Mle-----------: RLOC16 fffe -> 5023
+I(2529) OPENTHREAD:[N] Mle-----------: Role detached -> child
+```  

+ 2 - 0
examples/openthread/ot_sleepy_device/main/CMakeLists.txt

@@ -0,0 +1,2 @@
+idf_component_register(SRCS "esp_ot_sleepy_device.c"
+                       INCLUDE_DIRS ".")

+ 140 - 0
examples/openthread/ot_sleepy_device/main/esp_ot_sleepy_device.c

@@ -0,0 +1,140 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: CC0-1.0
+ *
+ * OpenThread Command Line Example
+ *
+ * This example code is in the Public Domain (or CC0 licensed, at your option.)
+ *
+ * Unless required by applicable law or agreed to in writing, this
+ * software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include "esp_err.h"
+#include "esp_event.h"
+#include "esp_log.h"
+#include "esp_openthread.h"
+#include "esp_openthread_netif_glue.h"
+#include "esp_ot_sleepy_device_config.h"
+#include "esp_vfs_eventfd.h"
+#include "driver/uart.h"
+#include "nvs_flash.h"
+#include "openthread/logging.h"
+#include "openthread/thread.h"
+
+#ifdef CONFIG_PM_ENABLE
+#include "esp_pm.h"
+#endif
+
+#if !SOC_IEEE802154_SUPPORTED
+#error "Openthread sleepy device is only supported for the SoCs which have IEEE 802.15.4 module"
+#endif
+
+#define TAG "ot_esp_power_save"
+
+static void create_config_network(otInstance *instance)
+{
+    otLinkModeConfig linkMode = { 0 };
+
+    linkMode.mRxOnWhenIdle = false;
+    linkMode.mDeviceType = false;
+    linkMode.mNetworkData = false;
+
+    if (otLinkSetPollPeriod(instance, CONFIG_OPENTHREAD_NETWORK_POLLPERIOD_TIME) != OT_ERROR_NONE) {
+        ESP_LOGE(TAG, "Failed to set OpenThread pollperiod.");
+        abort();
+    }
+
+    if (otThreadSetLinkMode(instance, linkMode) != OT_ERROR_NONE) {
+        ESP_LOGE(TAG, "Failed to set OpenThread linkmode.");
+        abort();
+    }
+    ESP_ERROR_CHECK(esp_openthread_auto_start(NULL));
+}
+
+static esp_netif_t *init_openthread_netif(const esp_openthread_platform_config_t *config)
+{
+    esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD();
+    esp_netif_t *netif = esp_netif_new(&cfg);
+    assert(netif != NULL);
+    ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init(config)));
+
+    return netif;
+}
+
+static void ot_task_worker(void *aContext)
+{
+    esp_openthread_platform_config_t config = {
+        .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(),
+        .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(),
+        .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(),
+    };
+
+    // Initialize the OpenThread stack
+    ESP_ERROR_CHECK(esp_openthread_init(&config));
+
+#if CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC
+    // The OpenThread log level directly matches ESP log level
+    (void)otLoggingSetLevel(CONFIG_LOG_DEFAULT_LEVEL);
+#endif
+    esp_netif_t *openthread_netif;
+    // Initialize the esp_netif bindings
+    openthread_netif = init_openthread_netif(&config);
+    esp_netif_set_default_netif(openthread_netif);
+
+    create_config_network(esp_openthread_get_instance());
+
+    // Run the main loop
+    esp_openthread_launch_mainloop();
+
+    // Clean up
+    esp_netif_destroy(openthread_netif);
+    esp_openthread_netif_glue_deinit();
+
+    esp_vfs_eventfd_unregister();
+    vTaskDelete(NULL);
+}
+
+static esp_err_t ot_power_save_init(void)
+{
+    esp_err_t rc = ESP_OK;
+#ifdef CONFIG_PM_ENABLE
+    int cur_cpu_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ;
+
+    esp_pm_config_t pm_config = {
+        .max_freq_mhz = cur_cpu_freq_mhz,
+        .min_freq_mhz = cur_cpu_freq_mhz,
+#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
+        .light_sleep_enable = true
+#endif
+    };
+
+    rc = esp_pm_configure(&pm_config);
+#endif
+    return rc;
+}
+
+void app_main(void)
+{
+    // Used eventfds:
+    // * netif
+    // * ot task queue
+    // * radio driver
+    esp_vfs_eventfd_config_t eventfd_config = {
+        .max_fds = 3,
+    };
+
+    ESP_ERROR_CHECK(nvs_flash_init());
+    ESP_ERROR_CHECK(esp_event_loop_create_default());
+    ESP_ERROR_CHECK(esp_netif_init());
+    ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config));
+    ESP_ERROR_CHECK(ot_power_save_init());
+
+    xTaskCreate(ot_task_worker, "ot_power_save_main", 4096, NULL, 5, NULL);
+}

+ 53 - 0
examples/openthread/ot_sleepy_device/main/esp_ot_sleepy_device_config.h

@@ -0,0 +1,53 @@
+/*
+ * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: CC0-1.0
+ *
+ * OpenThread Command Line Example
+ *
+ * This example code is in the Public Domain (or CC0 licensed, at your option.)
+ *
+ * Unless required by applicable law or agreed to in writing, this
+ * software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, either express or implied.
+ */
+
+#pragma once
+
+#include "esp_openthread_types.h"
+
+# define CONFIG_OPENTHREAD_NETWORK_POLLPERIOD_TIME 3000
+
+#if SOC_IEEE802154_SUPPORTED
+#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG()              \
+    {                                                      \
+        .radio_mode = RADIO_MODE_NATIVE,                   \
+    }
+#endif
+
+#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG()                    \
+    {                                                           \
+        .host_connection_mode = HOST_CONNECTION_MODE_CLI_UART,  \
+        .host_uart_config = {                                   \
+            .port = 0,                                          \
+            .uart_config =                                      \
+                {                                               \
+                    .baud_rate = 115200,                        \
+                    .data_bits = UART_DATA_8_BITS,              \
+                    .parity = UART_PARITY_DISABLE,              \
+                    .stop_bits = UART_STOP_BITS_1,              \
+                    .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,      \
+                    .rx_flow_ctrl_thresh = 0,                   \
+                    .source_clk = UART_SCLK_DEFAULT,            \
+                },                                              \
+            .rx_pin = UART_PIN_NO_CHANGE,                       \
+            .tx_pin = UART_PIN_NO_CHANGE,                       \
+        },                                                      \
+    }
+
+#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG()    \
+    {                                           \
+        .storage_partition_name = "nvs", \
+        .netif_queue_size = 10,                 \
+        .task_queue_size = 10,                  \
+    }

+ 5 - 0
examples/openthread/ot_sleepy_device/partitions.csv

@@ -0,0 +1,5 @@
+# Name,   Type, SubType, Offset,  Size, Flags
+# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
+nvs,        data, nvs,      0x9000,  0x6000,
+phy_init,   data, phy,      0xf000,  0x1000,
+factory,    app,  factory,  0x10000, 0x120000,

+ 51 - 0
examples/openthread/ot_sleepy_device/sdkconfig.defaults

@@ -0,0 +1,51 @@
+#
+# Partition Table
+#
+CONFIG_PARTITION_TABLE_CUSTOM=y
+CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
+CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
+# end of Partition Table
+
+#
+# mbedTLS
+#
+# TODO: Re-enable HW acceleration when HW AES support pm_lock (IDF-7704)
+CONFIG_MBEDTLS_HARDWARE_AES=n
+CONFIG_MBEDTLS_HARDWARE_MPI=n
+CONFIG_MBEDTLS_HARDWARE_SHA=n
+CONFIG_MBEDTLS_CMAC_C=y
+CONFIG_MBEDTLS_SSL_PROTO_DTLS=y
+CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=y
+CONFIG_MBEDTLS_ECJPAKE_C=y
+# end of mbedTLS
+
+#
+# OpenThread
+#
+CONFIG_OPENTHREAD_ENABLED=y
+CONFIG_OPENTHREAD_BORDER_ROUTER=n
+CONFIG_OPENTHREAD_DNS64_CLIENT=y
+# end of OpenThread
+
+#
+# lwIP
+#
+CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=4096
+CONFIG_LWIP_IPV6_NUM_ADDRESSES=8
+CONFIG_LWIP_MULTICAST_PING=y
+# end of lwIP
+
+#
+# IEEE 802.15.4
+#
+CONFIG_IEEE802154_ENABLED=y
+# end of IEEE 802.15.4
+
+#
+# light sleep
+#
+CONFIG_PM_ENABLE=y
+CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
+CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP=y
+CONFIG_IEEE802154_SLEEP_ENABLE=y
+# end of light sleep