From a411ce44c9c766c7333febf83f9f67dd28c590b7 Mon Sep 17 00:00:00 2001 From: Wu Jian Gang Date: Fri, 29 Jun 2018 20:51:12 +0800 Subject: [PATCH] feat(esp8266): add phy APIs 1. support save cal data in nvs; 2. support use phy partition to store init data; In old SDK, we use (max_sec - 3) as init data sector, and (max_sec - 4) as cal data sector. This is changed in IDF style SDK, and these 2 sectors can be used for other perpose. --- components/esp8266/Makefile.projbuild | 6 +- .../{util => esp8266}/include/esp_err.h | 0 components/esp8266/include/esp_phy_init.h | 134 +++++++ components/esp8266/source/phy.h | 38 ++ components/esp8266/source/phy_init.c | 331 ++++++++++++++++++ components/esp8266/source/startup.c | 57 +-- 6 files changed, 543 insertions(+), 23 deletions(-) rename components/{util => esp8266}/include/esp_err.h (100%) create mode 100644 components/esp8266/source/phy.h create mode 100644 components/esp8266/source/phy_init.c diff --git a/components/esp8266/Makefile.projbuild b/components/esp8266/Makefile.projbuild index 63ce43e8..f0a39383 100644 --- a/components/esp8266/Makefile.projbuild +++ b/components/esp8266/Makefile.projbuild @@ -1,4 +1,4 @@ -#ifdef CONFIG_ESP8266_LEGACY +ifdef CONFIG_ESP8266_LEGACY BOOTLOADER_FIRMWARE_DIR := $(abspath $(COMPONENT_PATH))/firmware @@ -24,7 +24,7 @@ ESP_INIT_DATA_DEFAULT_BIN := $(BOOTLOADER_FIRMWARE_DIR)/esp_init_data_default.bi ESPTOOL_ALL_FLASH_ARGS += $(ESP_INIT_DATA_DEFAULT_BIN_OFFSET) $(ESP_INIT_DATA_DEFAULT_BIN) -#else +else ifdef CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION @@ -59,7 +59,7 @@ flash: phy_init_data endif # CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION -#endif +endif # global CFLAGS for ESP8266 CFLAGS += -DMEMLEAK_DEBUG -DICACHE_FLASH diff --git a/components/util/include/esp_err.h b/components/esp8266/include/esp_err.h similarity index 100% rename from components/util/include/esp_err.h rename to components/esp8266/include/esp_err.h diff --git a/components/esp8266/include/esp_phy_init.h b/components/esp8266/include/esp_phy_init.h index 35b979f7..fb61519d 100644 --- a/components/esp8266/include/esp_phy_init.h +++ b/components/esp8266/include/esp_phy_init.h @@ -15,6 +15,8 @@ #pragma once #include +#include "esp_err.h" + #ifdef __cplusplus extern "C" { #endif @@ -31,6 +33,138 @@ typedef struct { uint8_t params[128]; /*!< opaque PHY initialization parameters */ } esp_phy_init_data_t; +/** + * @brief Opaque PHY calibration data + */ +typedef struct { + uint8_t rf_cal_data[128]; /*!< calibration data */ + uint32_t rx_gain_dc_table[125]; +} esp_phy_calibration_data_t; + +typedef enum { + PHY_RF_CAL_PARTIAL = 0x00000000, /*!< Do part of RF calibration. This should be used after power-on reset. */ + PHY_RF_CAL_NONE = 0x00000001, /*!< Don't do any RF calibration. This mode is only suggested to be used after deep sleep reset. */ + PHY_RF_CAL_FULL = 0x00000002 /*!< Do full RF calibration. Produces best results, but also consumes a lot of time and current. Suggested to be used once. */ +} esp_phy_calibration_mode_t; + + +/** + * @brief Modules for modem sleep + */ +typedef enum { + MODEM_WIFI_STATION_MODULE, //!< Wi-Fi Station used + MODEM_WIFI_SOFTAP_MODULE, //!< Wi-Fi SoftAP used + MODEM_WIFI_SNIFFER_MODULE, //!< Wi-Fi Sniffer used + MODEM_USER_MODULE, //!< User used + MODEM_MODULE_COUNT //!< Number of items +} modem_sleep_module_t; + +/** + * @brief Module WIFI mask for medem sleep + */ +#define MODEM_WIFI_MASK ((1< +#include +#include + +#include "rom/ets_sys.h" + +#include "esp_err.h" +#include "esp_phy_init.h" +#include "esp_system.h" +#include "esp_log.h" + +#include "nvs.h" +#include "nvs_flash.h" +#include "sdkconfig.h" + +#include "phy_init_data.h" +#include "phy.h" + +static const char *TAG = "phy_init"; + +static uint8_t phy_check_calibration_data(uint8_t *rf_cal_data) +{ +#define CHECK_NUM 26 +#define CHIP_ID_L 24 +#define CHIP_ID_H 25 + + uint8_t i; + uint32_t *cal_data_word = (uint32_t *)rf_cal_data; + uint32_t check_sum = 0; + + /* L: flag_1[79:76], version[59:56], mac_map[55:48], mac_l[47:24] */ + uint32_t chip_id_l = ((REG_READ(0x3FF00058) & 0xF000) << 16) | + (REG_READ(0x3ff00054) & 0xFFFFFFF); + /* H: mac_l[31:24], mac_h[119:96] */ + uint32_t chip_id_h = (REG_READ(0x3FF00050) & 0xFF000000) | + (REG_READ(0x3ff0005C) & 0xFFFFFF); + + cal_data_word[CHIP_ID_L] = chip_id_l; + cal_data_word[CHIP_ID_H] = chip_id_h; + + for (i = 0; i < CHECK_NUM; i++) { + check_sum += cal_data_word[i]; + } + + return (cal_data_word[CHECK_NUM] != ~check_sum); +} + +/* temporary put rx_gain_dc_table in memory */ +/* ToDo: use rx_gain_dc_table in nvs, need to modify internal libraries */ +uint32_t rx_gain_dc_table[125]; + +esp_err_t esp_phy_rf_init(const esp_phy_init_data_t *init_data, esp_phy_calibration_mode_t mode, + esp_phy_calibration_data_t *calibration_data, phy_rf_module_t module) +{ + esp_err_t status = ESP_OK; + uint8_t sta_mac[6]; + uint8_t *local_init_data = calloc(1, 256); + + memcpy(local_init_data, init_data->params, 128); + + extern uint32_t *phy_rx_gain_dc_table; + phy_rx_gain_dc_table = calibration_data->rx_gain_dc_table; + uint8_t cal_data_check = phy_check_calibration_data(calibration_data->rf_cal_data) || + phy_check_data_table(phy_rx_gain_dc_table, 125, 1); + + phy_afterwake_set_rfoption(1); + + if (!cal_data_check) { + write_data_to_rtc(calibration_data->rf_cal_data); + } + + esp_efuse_mac_get_default(sta_mac); + chip_init(local_init_data, sta_mac); + get_data_from_rtc((uint8_t *)calibration_data); + + memcpy(rx_gain_dc_table, calibration_data->rx_gain_dc_table, 4 * 125); + phy_rx_gain_dc_table = rx_gain_dc_table; + + free(local_init_data); + + if (cal_data_check == ESP_CAL_DATA_CHECK_FAIL) { +#ifdef CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE + ESP_LOGW(TAG, "saving new calibration data because of checksum failure, mode(%d)", mode); + if (mode != PHY_RF_CAL_FULL) { + esp_phy_store_cal_data_to_nvs(calibration_data); + } +#endif + } + + return status; +} + +esp_err_t esp_phy_rf_deinit(phy_rf_module_t module) +{ + esp_err_t status = ESP_OK; + + return status; +} + +// PHY init data handling functions +#if CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION +#include "esp_partition.h" + +const esp_phy_init_data_t *esp_phy_get_init_data() +{ + const esp_partition_t *partition = esp_partition_find_first( + ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_PHY, NULL); + + if (partition == NULL) { + ESP_LOGE(TAG, "PHY data partition not found"); + return NULL; + } + + ESP_LOGD(TAG, "loading PHY init data from partition at offset 0x%x", partition->address); + size_t init_data_store_length = sizeof(phy_init_magic_pre) + + sizeof(esp_phy_init_data_t) + sizeof(phy_init_magic_post); + uint8_t *init_data_store = (uint8_t *) malloc(init_data_store_length); + + if (init_data_store == NULL) { + ESP_LOGE(TAG, "failed to allocate memory for PHY init data"); + return NULL; + } + + esp_err_t err = esp_partition_read(partition, 0, init_data_store, init_data_store_length); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "failed to read PHY data partition (0x%x)", err); + return NULL; + } + + if (memcmp(init_data_store, PHY_INIT_MAGIC, sizeof(phy_init_magic_pre)) != 0 || + memcmp(init_data_store + init_data_store_length - sizeof(phy_init_magic_post), + PHY_INIT_MAGIC, sizeof(phy_init_magic_post)) != 0) { + ESP_LOGE(TAG, "failed to validate PHY data partition"); + return NULL; + } + + ESP_LOGD(TAG, "PHY data partition validated"); + return (const esp_phy_init_data_t *)(init_data_store + sizeof(phy_init_magic_pre)); +} + +void esp_phy_release_init_data(const esp_phy_init_data_t *init_data) +{ + free((uint8_t *) init_data - sizeof(phy_init_magic_pre)); +} + +#else // CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION + +// phy_init_data.h will declare static 'phy_init_data' variable initialized with default init data + +const esp_phy_init_data_t *esp_phy_get_init_data() +{ + ESP_LOGD(TAG, "loading PHY init data from application binary"); + return &phy_init_data; +} + +void esp_phy_release_init_data(const esp_phy_init_data_t *init_data) +{ + // no-op +} +#endif // CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION + + +// PHY calibration data handling functions +static const char *PHY_NAMESPACE = "phy"; +static const char *PHY_CAL_DATA_KEY = "cal_data"; +static const char *PHY_RX_GAIN_DC_TABLE_KEY = "dc_table"; + +static esp_err_t load_cal_data_from_nvs_handle(nvs_handle handle, + esp_phy_calibration_data_t *out_cal_data); + +static esp_err_t store_cal_data_to_nvs_handle(nvs_handle handle, + const esp_phy_calibration_data_t *cal_data); + +esp_err_t esp_phy_load_cal_data_from_nvs(esp_phy_calibration_data_t *out_cal_data) +{ + nvs_handle handle; + esp_err_t err = nvs_open(PHY_NAMESPACE, NVS_READONLY, &handle); + + if (err == ESP_ERR_NVS_NOT_INITIALIZED) { + ESP_LOGE(TAG, "%s: NVS has not been initialized. " + "Call nvs_flash_init before starting WiFi/BT.", __func__); + } else if (err != ESP_OK) { + ESP_LOGD(TAG, "%s: failed to open NVS namespace (0x%x)", __func__, err); + return err; + } + + err = load_cal_data_from_nvs_handle(handle, out_cal_data); + nvs_close(handle); + return err; +} + +esp_err_t esp_phy_store_cal_data_to_nvs(const esp_phy_calibration_data_t *cal_data) +{ + nvs_handle handle; + esp_err_t err = nvs_open(PHY_NAMESPACE, NVS_READWRITE, &handle); + + if (err != ESP_OK) { + ESP_LOGD(TAG, "%s: failed to open NVS namespace (0x%x)", __func__, err); + return err; + } else { + err = store_cal_data_to_nvs_handle(handle, cal_data); + nvs_close(handle); + return err; + } +} + +static esp_err_t load_cal_data_from_nvs_handle(nvs_handle handle, + esp_phy_calibration_data_t *out_cal_data) +{ + esp_err_t err; + + size_t length = sizeof(out_cal_data->rf_cal_data); + + err = nvs_get_blob(handle, PHY_CAL_DATA_KEY, out_cal_data->rf_cal_data, &length); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: failed to get cal_data(0x%x)", __func__, err); + return err; + } + + if (length != sizeof(out_cal_data->rf_cal_data)) { + ESP_LOGD(TAG, "%s: invalid length of cal_data (%d)", __func__, length); + return ESP_ERR_INVALID_SIZE; + } + + length = sizeof(out_cal_data->rx_gain_dc_table); + err = nvs_get_blob(handle, PHY_RX_GAIN_DC_TABLE_KEY, out_cal_data->rx_gain_dc_table, &length); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: failed to get rx_gain_dc_table(0x%x)", __func__, err); + return err; + } + + if (length != sizeof(out_cal_data->rx_gain_dc_table)) { + ESP_LOGD(TAG, "%s: invalid length of rx_gain_dc_table (%d)", __func__, length); + return ESP_ERR_INVALID_SIZE; + } + + return ESP_OK; +} + +static esp_err_t store_cal_data_to_nvs_handle(nvs_handle handle, + const esp_phy_calibration_data_t *cal_data) +{ + esp_err_t err; + + err = nvs_set_blob(handle, PHY_CAL_DATA_KEY, cal_data->rf_cal_data, sizeof(cal_data->rf_cal_data)); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: store calibration data failed(0x%x)\n", __func__, err); + return err; + } + + err = nvs_set_blob(handle, PHY_RX_GAIN_DC_TABLE_KEY, cal_data->rx_gain_dc_table, sizeof(cal_data->rx_gain_dc_table)); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: store rx gain dc table failed(0x%x)\n", __func__, err); + return err; + } + + err = nvs_commit(handle); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "%s: store calibration nvs commit failed(0x%x)\n", __func__, err); + } + + return err; +} + +void esp_phy_load_cal_and_init(phy_rf_module_t module) +{ + esp_phy_calibration_data_t *cal_data = + (esp_phy_calibration_data_t *) calloc(sizeof(esp_phy_calibration_data_t), 1); + + if (cal_data == NULL) { + ESP_LOGE(TAG, "failed to allocate memory for RF calibration data"); + abort(); + } + + const esp_phy_init_data_t *init_data = esp_phy_get_init_data(); + + if (init_data == NULL) { + ESP_LOGE(TAG, "failed to obtain PHY init data"); + abort(); + } + +#ifdef CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE + esp_phy_calibration_mode_t calibration_mode = PHY_RF_CAL_PARTIAL; + +// if (rtc_get_reset_reason(0) == DEEPSLEEP_RESET) { +// calibration_mode = PHY_RF_CAL_NONE; +// } + + esp_err_t err = esp_phy_load_cal_data_from_nvs(cal_data); + + if (err != ESP_OK) { + ESP_LOGW(TAG, "failed to load RF calibration data (0x%x), falling back to full calibration", err); + calibration_mode = PHY_RF_CAL_FULL; + } + + esp_phy_rf_init(init_data, calibration_mode, cal_data, module); + + if (calibration_mode != PHY_RF_CAL_NONE && err != ESP_OK) { + err = esp_phy_store_cal_data_to_nvs(cal_data); + } else { + err = ESP_OK; + } + +#else + esp_phy_rf_init(init_data, PHY_RF_CAL_FULL, cal_data, module); +#endif + + esp_phy_release_init_data(init_data); + + free(cal_data); // PHY maintains a copy of calibration data, so we can free this +} diff --git a/components/esp8266/source/startup.c b/components/esp8266/source/startup.c index b902eeb9..e3d3376e 100644 --- a/components/esp8266/source/startup.c +++ b/components/esp8266/source/startup.c @@ -1,17 +1,42 @@ +// Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. -#include "sdkconfig.h" #include #include #include #include -#include "esp_log.h" + +#include "sdkconfig.h" + #include "nvs_flash.h" #include "tcpip_adapter.h" -#include "esp_wifi_osi.h" + +#include "esp_log.h" #include "esp_image_format.h" +#include "esp_phy_init.h" +#include "esp_wifi_osi.h" #define FLASH_MAP_ADDR 0x40200000 +extern void chip_boot(void); +extern int rtc_init(void); +extern int mac_init(void); +extern int base_gpio_init(void); +extern int watchdog_init(void); +extern int wifi_timer_init(void); +extern int wifi_nvs_init(void); + static void user_init_entry(void *param) { void (**func)(void); @@ -25,6 +50,15 @@ static void user_init_entry(void *param) for (func = &__init_array_start; func < &__init_array_end; func++) func[0](); + assert(nvs_flash_init() == 0); + assert(wifi_nvs_init() == 0); + assert(rtc_init() == 0); + assert(mac_init() == 0); + assert(base_gpio_init() == 0); + esp_phy_load_cal_and_init(0); + assert(watchdog_init() == 0); + assert(wifi_timer_init() == 0); + tcpip_adapter_init(); app_main(); @@ -39,15 +73,6 @@ void call_user_start(void) extern int _bss_start, _bss_end; - extern void chip_boot(void); - extern int rtc_init(void); - extern int mac_init(void); - extern int base_gpio_init(void); - extern int phy_calibrate(void); - extern int watchdog_init(void); - extern int wifi_timer_init(void); - extern int wifi_nvs_init(void); - esp_image_header_t *head = (esp_image_header_t *)(FLASH_MAP_ADDR + CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET); esp_image_segment_header_t *segment = (esp_image_segment_header_t *)((uintptr_t)head + sizeof(esp_image_header_t)); @@ -78,14 +103,6 @@ void call_user_start(void) wifi_os_init(); - assert(nvs_flash_init() == 0); - assert(wifi_nvs_init() == 0); - assert(rtc_init() == 0); - assert(mac_init() == 0); - assert(base_gpio_init() == 0); - assert(phy_calibrate() == 0); - assert(watchdog_init() == 0); - assert(wifi_timer_init() == 0); assert(wifi_task_create(user_init_entry, "uiT", 512, NULL, wifi_task_get_max_priority()) != NULL); wifi_os_start();