/******************************************************************
 *
 * Copyright 2019 Samsung Electronics All Rights Reserved.
 *
 * 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.
 *
 ******************************************************************/

/****************************************************************************
 *
 *   Copyright (C) 2014-2016 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * The Samsung sample code has a BSD compatible license that requires this
 * copyright notice:
 *
 *   Copyright (c) 2016 Samsung Electronics, Inc.
 *   All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
// Copyright 2015-2016 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 <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <sys/lock.h>
#include <pthread.h>

#include "chip/dport_access.h"

#include <arch/chip/esp_err.h>
#include "rom/rtc.h"
#include "esp_phy_init.h"
#include "esp_system.h"
#include "nvs.h"
#include "nvs_flash.h"

#include "phy.h"
#include "phy_init_data.h"
#include "coexist_internal.h"
#include "periph_ctrl.h"
#include "esp_wifi_os_adapter.h"
#include "esp_log.h"
#define CONFIG_ESP32_PHY_MAX_TX_POWER 20

static const char *TAG = "phy_init";
//static const char* TAG = "phy_init";
static pthread_mutex_t s_phy_rf_init_lock = PTHREAD_MUTEX_INITIALIZER;

/* Bit mask of modules needing to call phy_rf_init */
static uint32_t s_module_phy_rf_init = 0;

/* Whether modern sleep in turned on */
static volatile bool s_is_phy_rf_en = false;

/* Bit mask of modules needing to enter modem sleep mode */
static uint32_t s_modem_sleep_module_enter = 0;

/* Bit mask of modules which might use RF, system can enter modem
 * sleep mode only when all modules registered require to enter
 * modem sleep*/
static uint32_t s_modem_sleep_module_register = 0;

/* Whether modern sleep is turned on */
static volatile bool s_is_modem_sleep_en = false;

static pthread_mutex_t s_modem_sleep_lock = PTHREAD_MUTEX_INITIALIZER;

uint32_t IRAM_ATTR phy_enter_critical(void)
{
	return portENTER_CRITICAL_NESTED();
}

void IRAM_ATTR phy_exit_critical(uint32_t level)
{
	portEXIT_CRITICAL_NESTED(level);
}

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)
{
	/* 3 modules may call phy_init: Wi-Fi, BT, Modem Sleep */
	if (module >= PHY_MODULE_COUNT) {
		ESP_LOGE(TAG, "%s, invalid module parameter(%d), should be smaller than \
                 module count(%d)", __func__, module, PHY_MODULE_COUNT);
		return ESP_ERR_INVALID_ARG;
	}

	pthread_mutex_lock(&s_phy_rf_init_lock);
	uint32_t s_module_phy_rf_init_old = s_module_phy_rf_init;
	bool is_wifi_or_bt_enabled = ! !(s_module_phy_rf_init_old & (BIT(PHY_BT_MODULE) | BIT(PHY_WIFI_MODULE)));
	esp_err_t status = ESP_OK;
	s_module_phy_rf_init |= BIT(module);

	if ((is_wifi_or_bt_enabled == false) && (module == PHY_MODEM_MODULE)) {
		status = ESP_FAIL;
	} else if (s_is_phy_rf_en == true) {
	} else {
		/* If Wi-Fi, BT all disabled, modem sleep should not take effect;
		 * If either Wi-Fi or BT is enabled, should allow modem sleep requires
		 * to enter sleep;
		 * If Wi-Fi, BT co-exist, it is disallowed that only one module
		 * support modem sleep, E,g. BT support modem sleep but Wi-Fi not
		 * support modem sleep;
		 */
		if (is_wifi_or_bt_enabled == false) {
			if ((module == PHY_BT_MODULE) || (module == PHY_WIFI_MODULE)) {
				s_is_phy_rf_en = true;
			}
		} else {
			if (module == PHY_MODEM_MODULE) {
				s_is_phy_rf_en = true;
			} else if ((module == PHY_BT_MODULE) || (module == PHY_WIFI_MODULE)) {
				/* New module (BT or Wi-Fi) can init RF according to modem_sleep_exit */
			}
		}
		if (s_is_phy_rf_en == true) {
			// Enable WiFi/BT common peripheral clock
			periph_module_enable(PERIPH_WIFI_BT_COMMON_MODULE);
			phy_set_wifi_mode_only(0);

			if (ESP_CAL_DATA_CHECK_FAIL == register_chipv7_phy(init_data, calibration_data, mode)) {
				ESP_LOGW(TAG, "saving new calibration data because of checksum failure, mode(%d)", mode);
#ifdef CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE
				if (mode != PHY_RF_CAL_FULL) {
					esp_phy_store_cal_data_to_nvs(calibration_data);
				}
#endif
			}
			extern esp_err_t wifi_osi_funcs_register(wifi_osi_funcs_t * osi_funcs);
			status = wifi_osi_funcs_register(&g_wifi_osi_funcs);
			if (status != ESP_OK) {
				ESP_LOGE(TAG, "failed to register wifi os adapter, ret(%d)", status);
				pthread_mutex_unlock(&s_phy_rf_init_lock);
				return ESP_FAIL;
			}
			coex_bt_high_prio();

		}
	}

#if CONFIG_SW_COEXIST_ENABLE
	if ((module == PHY_BT_MODULE) || (module == PHY_WIFI_MODULE)) {
		uint32_t phy_bt_wifi_mask = BIT(PHY_BT_MODULE) | BIT(PHY_WIFI_MODULE);
		if ((s_module_phy_rf_init & phy_bt_wifi_mask) == phy_bt_wifi_mask) {	//both wifi & bt enabled
			coex_init();
			coex_preference_set(CONFIG_SW_COEXIST_PREFERENCE_VALUE);
			coex_resume();
		}
	}
#endif
	pthread_mutex_unlock(&s_phy_rf_init_lock);
	return status;
}

esp_err_t esp_phy_rf_deinit(phy_rf_module_t module)
{
	/* 3 modules may call phy_init: Wi-Fi, BT, Modem Sleep */
	if (module >= PHY_MODULE_COUNT) {
		ESP_LOGE(TAG, "%s, invalid module parameter(%d), should be smaller than \
                 module count(%d)", __func__, module, PHY_MODULE_COUNT);
		return ESP_ERR_INVALID_ARG;
	}

	pthread_mutex_lock(&s_phy_rf_init_lock);
	uint32_t s_module_phy_rf_init_old = s_module_phy_rf_init;
	uint32_t phy_bt_wifi_mask = BIT(PHY_BT_MODULE) | BIT(PHY_WIFI_MODULE);
	bool is_wifi_or_bt_enabled = ! !(s_module_phy_rf_init_old & phy_bt_wifi_mask);
	bool is_both_wifi_bt_enabled = ((s_module_phy_rf_init_old & phy_bt_wifi_mask) == phy_bt_wifi_mask);
	s_module_phy_rf_init &= ~BIT(module);
	esp_err_t status = ESP_OK;

#if CONFIG_SW_COEXIST_ENABLE
	if ((module == PHY_BT_MODULE) || (module == PHY_WIFI_MODULE)) {
		if (is_both_wifi_bt_enabled == true) {
			coex_deinit();
		}
	}
#endif

	if ((is_wifi_or_bt_enabled == false) && (module == PHY_MODEM_MODULE)) {
		/* Modem sleep should not take effect in this case */
		status = ESP_FAIL;
	} else if (s_is_phy_rf_en == false) {
		//do nothing
	} else {
		if (is_wifi_or_bt_enabled == false) {
			if ((module == PHY_BT_MODULE) || (module == PHY_WIFI_MODULE)) {
				s_is_phy_rf_en = false;
				ESP_LOGE(TAG, "%s, RF should not be in enabled state if both Wi-Fi and BT are disabled", __func__);
			}
		} else {
			if (module == PHY_MODEM_MODULE) {
				s_is_phy_rf_en = false;
			} else if ((module == PHY_BT_MODULE) || (module == PHY_WIFI_MODULE)) {
				s_is_phy_rf_en = is_both_wifi_bt_enabled ? true : false;
			}
		}

		if (s_is_phy_rf_en == false) {
			// Disable PHY and RF.
			phy_close_rf();
			// Disable WiFi/BT common peripheral clock. Do not disable clock for hardware RNG
			periph_module_disable(PERIPH_WIFI_BT_COMMON_MODULE);
		}
	}

	pthread_mutex_unlock(&s_phy_rf_init_lock);
	return status;
}

esp_err_t esp_modem_sleep_enter(modem_sleep_module_t module)
{
#if CONFIG_SW_COEXIST_ENABLE
	uint32_t phy_bt_wifi_mask = BIT(PHY_BT_MODULE) | BIT(PHY_WIFI_MODULE);
#endif

	if (module >= MODEM_MODULE_COUNT) {
		ESP_LOGE(TAG, "%s, invalid module parameter(%d), should be smaller than \
                 module count(%d)", __func__, module, MODEM_MODULE_COUNT);
		return ESP_ERR_INVALID_ARG;
	} else if (!(s_modem_sleep_module_register & BIT(module))) {
		ESP_LOGW(TAG, "%s, module (%d) has not been registered", __func__, module);
		return ESP_ERR_INVALID_ARG;
	} else {
		pthread_mutex_lock(&s_modem_sleep_lock);
		s_modem_sleep_module_enter |= BIT(module);
#if CONFIG_SW_COEXIST_ENABLE
		pthread_mutex_lock(&s_phy_rf_init_lock);
		if (((s_module_phy_rf_init & phy_bt_wifi_mask) == phy_bt_wifi_mask)	//both wifi & bt enabled
			&& (s_modem_sleep_module_enter & (MODEM_BT_MASK | MODEM_WIFI_MASK)) != 0) {
			coex_pause();
		}
		pthread_mutex_unlock(&s_phy_rf_init_lock);
#endif
		if (!s_is_modem_sleep_en && (s_modem_sleep_module_enter == s_modem_sleep_module_register)) {
			esp_err_t status = esp_phy_rf_deinit(PHY_MODEM_MODULE);
			if (status == ESP_OK) {
				s_is_modem_sleep_en = true;
			}
		}
		pthread_mutex_unlock(&s_modem_sleep_lock);
		return ESP_OK;
	}
}

esp_err_t esp_modem_sleep_exit(modem_sleep_module_t module)
{
#if CONFIG_SW_COEXIST_ENABLE
	uint32_t phy_bt_wifi_mask = BIT(PHY_BT_MODULE) | BIT(PHY_WIFI_MODULE);
#endif

	if (module >= MODEM_MODULE_COUNT) {
		ESP_LOGE(TAG, "%s, invalid module parameter(%d), should be smaller than \
                 module count(%d)", __func__, module, MODEM_MODULE_COUNT);
		return ESP_ERR_INVALID_ARG;
	} else if (!(s_modem_sleep_module_register & BIT(module))) {
		ESP_LOGW(TAG, "%s, module (%d) has not been registered", __func__, module);
		return ESP_ERR_INVALID_ARG;
	} else {
		pthread_mutex_lock(&s_modem_sleep_lock);
		s_modem_sleep_module_enter &= ~BIT(module);
		if (s_is_modem_sleep_en) {
			esp_err_t status = esp_phy_rf_init(NULL, PHY_RF_CAL_NONE, NULL, PHY_MODEM_MODULE);
			if (status == ESP_OK) {
				s_is_modem_sleep_en = false;
			}
		}
#if CONFIG_SW_COEXIST_ENABLE
		pthread_mutex_lock(&s_phy_rf_init_lock);
		if (((s_module_phy_rf_init & phy_bt_wifi_mask) == phy_bt_wifi_mask)	//both wifi & bt enabled
			&& (s_modem_sleep_module_enter & (MODEM_BT_MASK | MODEM_WIFI_MASK)) == 0) {
			coex_resume();
		}
		pthread_mutex_unlock(&s_phy_rf_init_lock);
#endif
		pthread_mutex_unlock(&s_modem_sleep_lock);
		return ESP_OK;
	}
	return ESP_OK;
}

esp_err_t esp_modem_sleep_register(modem_sleep_module_t module)
{
	if (module >= MODEM_MODULE_COUNT) {
		ESP_LOGE(TAG, "%s, invalid module parameter(%d), should be smaller than \
                 module count(%d)", __func__, module, MODEM_MODULE_COUNT);
		return ESP_ERR_INVALID_ARG;
	} else if (s_modem_sleep_module_register & BIT(module)) {
		ESP_LOGI(TAG, "%s, multiple registration of module (%d)", __func__, module);
		return ESP_OK;
	} else {
		pthread_mutex_lock(&s_modem_sleep_lock);
		s_modem_sleep_module_register |= BIT(module);
		/* The module is set to enter modem sleep by default, otherwise will prevent
		 * other modules from entering sleep mode if this module never call enter sleep function
		 * in the future */
		s_modem_sleep_module_enter |= BIT(module);
		pthread_mutex_unlock(&s_modem_sleep_lock);
		return ESP_OK;
	}
}

esp_err_t esp_modem_sleep_deregister(modem_sleep_module_t module)
{
	if (module >= MODEM_MODULE_COUNT) {
		ESP_LOGE(TAG, "%s, invalid module parameter(%d), should be smaller than \
                 module count(%d)", __func__, module, MODEM_MODULE_COUNT);
		return ESP_ERR_INVALID_ARG;
	} else if (!(s_modem_sleep_module_register & BIT(module))) {
		ESP_LOGI(TAG, "%s, module (%d) has not been registered", __func__, module);
		return ESP_OK;
	} else {
		pthread_mutex_lock(&s_modem_sleep_lock);
		s_modem_sleep_module_enter &= ~BIT(module);
		s_modem_sleep_module_register &= ~BIT(module);
		if (s_modem_sleep_module_register == 0) {
			s_modem_sleep_module_enter = 0;
			/* Once all module are de-registered and current state
			 * is modem sleep mode, we need to turn off modem sleep
			 */
			if (s_is_modem_sleep_en == true) {
				s_is_modem_sleep_en = false;
				esp_phy_rf_init(NULL, PHY_RF_CAL_NONE, NULL, PHY_MODEM_MODULE);
			}
		}
		pthread_mutex_unlock(&s_modem_sleep_lock);
		return ESP_OK;
	}
}

// PHY init data handling functions
#if CONFIG_ESP32_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_ESP32_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_ESP32_PHY_INIT_DATA_IN_PARTITION

// PHY calibration data handling functions
static const char *PHY_NAMESPACE = "phy";
static const char *PHY_CAL_VERSION_KEY = "cal_version";
static const char *PHY_CAL_MAC_KEY = "cal_mac";
static const char *PHY_CAL_DATA_KEY = "cal_data";

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;
	uint32_t cal_data_version;
	err = nvs_get_u32(handle, PHY_CAL_VERSION_KEY, &cal_data_version);
	if (err != ESP_OK) {
		ESP_LOGD(TAG, "%s: failed to get cal_version (0x%x)", __func__, err);
		return err;
	}
	uint32_t cal_format_version = phy_get_rf_cal_version() & (~BIT(16));
	ESP_LOGV(TAG, "phy_get_rf_cal_version: %d\n", cal_format_version);
	if (cal_data_version != cal_format_version) {
		ESP_LOGD(TAG, "%s: expected calibration data format %d, found %d", __func__, cal_format_version, cal_data_version);
		return ESP_FAIL;
	}
	uint8_t cal_data_mac[6];
	size_t length = sizeof(cal_data_mac);
	err = nvs_get_blob(handle, PHY_CAL_MAC_KEY, cal_data_mac, &length);
	if (err != ESP_OK) {
		ESP_LOGD(TAG, "%s: failed to get cal_mac (0x%x)", __func__, err);
		return err;
	}
	if (length != sizeof(cal_data_mac)) {
		ESP_LOGD(TAG, "%s: invalid length of cal_mac (%d)", __func__, length);
		return ESP_ERR_INVALID_SIZE;
	}
	uint8_t sta_mac[6];
	esp_efuse_mac_get_default(sta_mac);
	if (memcmp(sta_mac, cal_data_mac, sizeof(sta_mac)) != 0) {
		ESP_LOGE(TAG, "%s: calibration data MAC check failed: expected " MACSTR ", found " MACSTR, __func__, MAC2STR(sta_mac), MAC2STR(cal_data_mac));
		return ESP_FAIL;
	}
	length = sizeof(*out_cal_data);
	err = nvs_get_blob(handle, PHY_CAL_DATA_KEY, out_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)) {
		ESP_LOGD(TAG, "%s: invalid length of cal_data (%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, sizeof(*cal_data));
	if (err != ESP_OK) {
		ESP_LOGE(TAG, "%s: store calibration data failed(0x%x)\n", __func__, err);
		return err;
	}

	uint8_t sta_mac[6];
	esp_efuse_mac_get_default(sta_mac);
	err = nvs_set_blob(handle, PHY_CAL_MAC_KEY, sta_mac, sizeof(sta_mac));
	if (err != ESP_OK) {
		ESP_LOGE(TAG, "%s: store calibration mac failed(0x%x)\n", __func__, err);
		return err;
	}
	uint32_t cal_format_version = phy_get_rf_cal_version() & (~BIT(16));
	ESP_LOGV(TAG, "phy_get_rf_cal_version: %d\n", cal_format_version);
	err = nvs_set_u32(handle, PHY_CAL_VERSION_KEY, cal_format_version);
	if (err != ESP_OK) {
		ESP_LOGE(TAG, "%s: store calibration version 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_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE
	esp_phy_calibration_mode_t calibration_mode = PHY_RF_CAL_PARTIAL;
	uint8_t sta_mac[6];
	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_efuse_mac_get_default(sta_mac);
	memcpy(cal_data->mac, sta_mac, 6);
	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
}
