blob: d507d1503143a4a2142afc483752b1c03343755b [file] [log] [blame]
/****************************************************************************
* arch/xtensa/src/esp32s3/esp32s3_wireless.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/kmalloc.h>
#include <nuttx/mqueue.h>
#include <nuttx/spinlock.h>
#include <debug.h>
#include <assert.h>
#include <netinet/in.h>
#include <sys/param.h>
#include "xtensa.h"
#include "hardware/esp32s3_efuse.h"
#include "hardware/esp32s3_rtccntl.h"
#include "hardware/esp32s3_soc.h"
#include "hardware/esp32s3_syscon.h"
#include "hardware/esp32s3_system.h"
#include "esp32s3_irq.h"
#include "esp32s3_periph.h"
#include "esp_phy_init.h"
#include "phy_init_data.h"
#include "esp32s3_wireless.h"
#include "esp32s3_partition.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define MAC_ADDR0_REG EFUSE_RD_MAC_SPI_SYS_0_REG
#define MAC_ADDR1_REG EFUSE_RD_MAC_SPI_SYS_1_REG
/* Software Interrupt */
#define SWI_IRQ ESP32S3_IRQ_INT_FROM_CPU2
#define SWI_PERIPH ESP32S3_PERIPH_INT_FROM_CPU2
/****************************************************************************
* Private Types
****************************************************************************/
/* Wireless Private Data */
struct esp_wireless_priv_s
{
volatile int ref; /* Reference count */
int cpuint; /* CPU interrupt assigned to SWI */
struct list_node sc_list; /* Semaphore cache list */
struct list_node qc_list; /* Queue cache list */
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static inline void phy_digital_regs_store(void);
static inline void phy_digital_regs_load(void);
/****************************************************************************
* Extern Functions declaration
****************************************************************************/
extern void coex_pti_v2(void);
extern uint8_t esp_crc8(const uint8_t *p, uint32_t len);
extern void phy_wakeup_init(void);
extern void phy_close_rf(void);
extern uint8_t phy_dig_reg_backup(bool init, uint32_t *regs);
extern int register_chipv7_phy(const esp_phy_init_data_t *init_data,
esp_phy_calibration_data_t *cal_data,
esp_phy_calibration_mode_t cal_mode);
extern uint32_t crc32_le(uint32_t crc, uint8_t const *buf, uint32_t len);
/****************************************************************************
* Private Data
****************************************************************************/
/* Wi-Fi sleep private data */
static uint32_t g_phy_clk_en_cnt;
/* Reference count of enabling PHY */
static uint8_t g_phy_access_ref;
/* Memory to store PHY digital registers */
static uint32_t *g_phy_digital_regs_mem = NULL;
/* Indicate PHY is calibrated or not */
static bool g_is_phy_calibrated = false;
/* Reference count of power on of wifi and bt power domain */
static uint8_t g_wifi_bt_pd_controller;
/* Private data of the wireless common interface */
static struct esp_wireless_priv_s g_esp_wireless_priv;
#ifdef CONFIG_ESP32S3_PHY_INIT_DATA_IN_PARTITION
static const char *phy_partion_label = "phy_init";
#endif
#ifdef CONFIG_ESP32S3_SUPPORT_MULTIPLE_PHY_INIT_DATA
static phy_init_data_type_t g_phy_init_data_type;
static phy_init_data_type_t g_current_apply_phy_init_data;
static char g_phy_current_country[PHY_COUNTRY_CODE_LEN];
/* Whether it is a new bin */
static bool g_multiple_phy_init_data_bin;
/* PHY init data type array */
static const char *g_phy_type[ESP_PHY_INIT_DATA_TYPE_NUMBER] =
{
"DEFAULT", "SRRC", "FCC", "CE", "NCC", "KCC", "MIC", "IC",
"ACMA", "ANATEL", "ISED", "WPC", "OFCA", "IFETEL", "RCM"
};
/* Country and PHY init data type map */
static phy_country_to_bin_type_t g_country_code_map_type_table[] =
{
{"01", ESP_PHY_INIT_DATA_TYPE_DEFAULT},
{"AT", ESP_PHY_INIT_DATA_TYPE_CE},
{"AU", ESP_PHY_INIT_DATA_TYPE_ACMA},
{"BE", ESP_PHY_INIT_DATA_TYPE_CE},
{"BG", ESP_PHY_INIT_DATA_TYPE_CE},
{"BR", ESP_PHY_INIT_DATA_TYPE_ANATEL},
{"CA", ESP_PHY_INIT_DATA_TYPE_ISED},
{"CH", ESP_PHY_INIT_DATA_TYPE_CE},
{"CN", ESP_PHY_INIT_DATA_TYPE_SRRC},
{"CY", ESP_PHY_INIT_DATA_TYPE_CE},
{"CZ", ESP_PHY_INIT_DATA_TYPE_CE},
{"DE", ESP_PHY_INIT_DATA_TYPE_CE},
{"DK", ESP_PHY_INIT_DATA_TYPE_CE},
{"EE", ESP_PHY_INIT_DATA_TYPE_CE},
{"ES", ESP_PHY_INIT_DATA_TYPE_CE},
{"FI", ESP_PHY_INIT_DATA_TYPE_CE},
{"FR", ESP_PHY_INIT_DATA_TYPE_CE},
{"GB", ESP_PHY_INIT_DATA_TYPE_CE},
{"GR", ESP_PHY_INIT_DATA_TYPE_CE},
{"HK", ESP_PHY_INIT_DATA_TYPE_OFCA},
{"HR", ESP_PHY_INIT_DATA_TYPE_CE},
{"HU", ESP_PHY_INIT_DATA_TYPE_CE},
{"IE", ESP_PHY_INIT_DATA_TYPE_CE},
{"IN", ESP_PHY_INIT_DATA_TYPE_WPC},
{"IS", ESP_PHY_INIT_DATA_TYPE_CE},
{"IT", ESP_PHY_INIT_DATA_TYPE_CE},
{"JP", ESP_PHY_INIT_DATA_TYPE_MIC},
{"KR", ESP_PHY_INIT_DATA_TYPE_KCC},
{"LI", ESP_PHY_INIT_DATA_TYPE_CE},
{"LT", ESP_PHY_INIT_DATA_TYPE_CE},
{"LU", ESP_PHY_INIT_DATA_TYPE_CE},
{"LV", ESP_PHY_INIT_DATA_TYPE_CE},
{"MT", ESP_PHY_INIT_DATA_TYPE_CE},
{"MX", ESP_PHY_INIT_DATA_TYPE_IFETEL},
{"NL", ESP_PHY_INIT_DATA_TYPE_CE},
{"NO", ESP_PHY_INIT_DATA_TYPE_CE},
{"NZ", ESP_PHY_INIT_DATA_TYPE_RCM},
{"PL", ESP_PHY_INIT_DATA_TYPE_CE},
{"PT", ESP_PHY_INIT_DATA_TYPE_CE},
{"RO", ESP_PHY_INIT_DATA_TYPE_CE},
{"SE", ESP_PHY_INIT_DATA_TYPE_CE},
{"SI", ESP_PHY_INIT_DATA_TYPE_CE},
{"SK", ESP_PHY_INIT_DATA_TYPE_CE},
{"TW", ESP_PHY_INIT_DATA_TYPE_NCC},
{"US", ESP_PHY_INIT_DATA_TYPE_FCC},
};
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: phy_digital_regs_store
*
* Description:
* Store PHY digital registers.
*
****************************************************************************/
static inline void phy_digital_regs_store(void)
{
if (g_phy_digital_regs_mem == NULL)
{
g_phy_digital_regs_mem = (uint32_t *)
kmm_malloc(SOC_PHY_DIG_REGS_MEM_SIZE);
}
DEBUGASSERT(g_phy_digital_regs_mem != NULL);
phy_dig_reg_backup(true, g_phy_digital_regs_mem);
}
/****************************************************************************
* Name: phy_digital_regs_load
*
* Description:
* Load PHY digital registers.
*
****************************************************************************/
static inline void phy_digital_regs_load(void)
{
if (g_phy_digital_regs_mem != NULL)
{
phy_dig_reg_backup(false, g_phy_digital_regs_mem);
}
}
/****************************************************************************
* Name: esp_swi_irq
*
* Description:
* Wireless software interrupt callback function.
*
* Parameters:
* cpuint - CPU interrupt index
* context - Context data from the ISR
* arg - NULL
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
static int esp_swi_irq(int irq, void *context, void *arg)
{
int i;
int ret;
struct esp_semcache_s *sc;
struct esp_semcache_s *sc_tmp;
struct esp_queuecache_s *qc;
struct esp_queuecache_s *qc_tmp;
struct esp_wireless_priv_s *priv = &g_esp_wireless_priv;
modifyreg32(SYSTEM_CPU_INTR_FROM_CPU_2_REG, SYSTEM_CPU_INTR_FROM_CPU_2, 0);
list_for_every_entry_safe(&priv->sc_list, sc, sc_tmp,
struct esp_semcache_s, node)
{
for (i = 0; i < sc->count; i++)
{
ret = nxsem_post(sc->sem);
if (ret < 0)
{
wlerr("ERROR: Failed to post sem ret=%d\n", ret);
}
}
sc->count = 0;
list_delete(&sc->node);
}
list_for_every_entry_safe(&priv->qc_list, qc, qc_tmp,
struct esp_queuecache_s, node)
{
ret = file_mq_send(qc->mq_ptr, (const char *)qc->buffer, qc->size, 0);
if (ret < 0)
{
wlerr("ERROR: Failed to send queue ret=%d\n", ret);
}
list_delete(&qc->node);
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Functions needed by libphy.a
****************************************************************************/
/****************************************************************************
* Name: esp_dport_access_reg_read
*
* Description:
* Read register value safely in SMP
*
* Input Parameters:
* reg - Register address
*
* Returned Value:
* Register value
*
****************************************************************************/
uint32_t IRAM_ATTR esp_dport_access_reg_read(uint32_t reg)
{
return getreg32(reg);
}
/****************************************************************************
* Name: phy_printf
*
* Description:
* Output format string and its arguments
*
* Input Parameters:
* format - format string
*
* Returned Value:
* 0
*
****************************************************************************/
int phy_printf(const char *format, ...)
{
#ifdef CONFIG_DEBUG_WIRELESS_INFO
va_list arg;
va_start(arg, format);
vsyslog(LOG_INFO, format, arg);
va_end(arg);
#endif
return 0;
}
/****************************************************************************
* Name: esp32s3_phy_enable_clock
*
* Description:
* Enable PHY hardware clock
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
void IRAM_ATTR esp32s3_phy_enable_clock(void)
{
esp32s3_periph_wifi_bt_common_module_enable();
}
/****************************************************************************
* Name: esp32s3_phy_disable_clock
*
* Description:
* Disable PHY hardware clock
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
void IRAM_ATTR esp32s3_phy_disable_clock(void)
{
esp32s3_periph_wifi_bt_common_module_disable();
}
#ifdef CONFIG_ESP32S3_SUPPORT_MULTIPLE_PHY_INIT_DATA
/****************************************************************************
* Name: phy_crc_check
*
* Description:
* Check the checksum value of data
*
* Input Parameters:
* data - Data buffer pointer
* length - Data length
* checksum - Checksum pointer
*
* Returned Value:
* OK on success; a negated errno on failure
*
****************************************************************************/
static int phy_crc_check(uint8_t *data, const uint8_t *checksum,
size_t length)
{
uint32_t crc_data = crc32_le(0, data, length);
uint32_t crc_size_conversion = HTONL(crc_data);
uint32_t tmp_crc = checksum[0] | (checksum[1] << 8) | (checksum[2] << 16) |
(checksum[3] << 24);
if (crc_size_conversion != tmp_crc)
{
return ERROR;
}
return OK;
}
/****************************************************************************
* Name: phy_find_bin_type_according_country
*
* Description:
* Find the PHY initialization data type according to country code
*
* Input Parameters:
* country - Country code pointer
*
* Returned Value:
* PHY initialization data type
*
****************************************************************************/
static uint8_t phy_find_bin_type_according_country(const char *country)
{
uint8_t i;
uint8_t phy_init_data_type;
uint8_t num = nitems(g_country_code_map_type_table);
for (i = 0; i < num; i++)
{
if (memcmp(country, g_country_code_map_type_table[i].cc,
sizeof(g_phy_current_country)) == 0)
{
phy_init_data_type = g_country_code_map_type_table[i].type;
wlinfo("Current country is %c%c, PHY init data type is %s\n",
g_country_code_map_type_table[i].cc[0],
g_country_code_map_type_table[i].cc[1],
g_phy_type[g_country_code_map_type_table[i].type]);
break;
}
}
if (i == num)
{
phy_init_data_type = ESP_PHY_INIT_DATA_TYPE_DEFAULT;
wlerr("Use the default certification code beacuse %c%c doesn't "
"have a certificate\n", country[0], country[1]);
}
return phy_init_data_type;
}
/****************************************************************************
* Name: phy_find_bin_data_according_type
*
* Description:
* Find the PHY initialization data according to PHY init data type
*
* Input Parameters:
* output_data - Output data buffer pointer
* control_info - PHY init data control infomation
* input_data - Input data buffer pointer
* init_data_type - PHY init data type
*
* Returned Value:
* OK on success; a negated errno on failure
*
****************************************************************************/
static int phy_find_bin_data_according_type(uint8_t *output_data,
const phy_control_info_data_t *control_info,
const esp_phy_init_data_t *input_data,
phy_init_data_type_t init_data_type)
{
int i;
for (i = 0; i < control_info->number; i++)
{
if (init_data_type == *((uint8_t *)(input_data + i)
+ PHY_INIT_DATA_TYPE_OFFSET))
{
memcpy(output_data + sizeof(phy_init_magic_pre),
(input_data + i), sizeof(esp_phy_init_data_t));
break;
}
}
if (i == control_info->number)
{
return ERROR;
}
return OK;
}
/****************************************************************************
* Name: phy_get_multiple_init_data
*
* Description:
* Get multiple PHY init data according to PHY init data type
*
* Input Parameters:
* data - Data buffer pointer
* length - Data length
* init_data_type - PHY init data type
*
* Returned Value:
* OK on success; a negated errno on failure
*
****************************************************************************/
static int phy_get_multiple_init_data(uint8_t *data, size_t length,
phy_init_data_type_t init_data_type)
{
phy_control_info_data_t *control_info = (phy_control_info_data_t *)
kmm_malloc(sizeof(phy_control_info_data_t));
if (control_info == NULL)
{
wlerr("ERROR: Failed to allocate memory for\
PHY init data control info\n");
return -ENOMEM;
}
int ret = esp32s3_partition_read(phy_partion_label, length, control_info,
sizeof(phy_control_info_data_t));
if (ret != OK)
{
kmm_free(control_info);
wlerr("ERROR: Failed to read PHY control info data partition\n");
return ret;
}
if ((control_info->check_algorithm) == PHY_CRC_ALGORITHM)
{
ret = phy_crc_check(control_info->multiple_bin_checksum,
control_info->control_info_checksum,
sizeof(phy_control_info_data_t) -
sizeof(control_info->control_info_checksum));
if (ret != OK)
{
kmm_free(control_info);
wlerr("ERROR: PHY init data control info check error\n");
return ret;
}
}
else
{
kmm_free(control_info);
wlerr("ERROR: Check algorithm not CRC, PHY init data update failed\n");
return ERROR;
}
uint8_t *init_data_multiple = (uint8_t *)
kmm_malloc(sizeof(esp_phy_init_data_t) * control_info->number);
if (init_data_multiple == NULL)
{
kmm_free(control_info);
wlerr("ERROR: Failed to allocate memory for PHY init data\n");
return -ENOMEM;
}
ret = esp32s3_partition_read(phy_partion_label, length +
sizeof(phy_control_info_data_t), init_data_multiple,
sizeof(esp_phy_init_data_t) * control_info->number);
if (ret != OK)
{
kmm_free(init_data_multiple);
kmm_free(control_info);
wlerr("ERROR: Failed to read PHY init data multiple bin partition\n");
return ret;
}
if ((control_info->check_algorithm) == PHY_CRC_ALGORITHM)
{
ret = phy_crc_check(init_data_multiple,
control_info->multiple_bin_checksum,
sizeof(esp_phy_init_data_t) * control_info->number);
if (ret != OK)
{
kmm_free(init_data_multiple);
kmm_free(control_info);
wlerr("ERROR: PHY init data multiple bin check error\n");
return ret;
}
}
else
{
kmm_free(init_data_multiple);
kmm_free(control_info);
wlerr("ERROR: Check algorithm not CRC, PHY init data update failed\n");
return ERROR;
}
ret = phy_find_bin_data_according_type(data, control_info,
(const esp_phy_init_data_t *)init_data_multiple, init_data_type);
if (ret != OK)
{
wlerr("ERROR: %s has not been certified, use DEFAULT PHY init data\n",
g_phy_type[init_data_type]);
g_phy_init_data_type = ESP_PHY_INIT_DATA_TYPE_DEFAULT;
}
else
{
g_phy_init_data_type = init_data_type;
}
kmm_free(init_data_multiple);
kmm_free(control_info);
return OK;
}
/****************************************************************************
* Name: phy_update_init_data
*
* Description:
* Update PHY init data according to PHY init data type
*
* Input Parameters:
* init_data_type - PHY init data type
*
* Returned Value:
* OK on success; a negated errno on failure
*
****************************************************************************/
static int phy_update_init_data(phy_init_data_type_t init_data_type)
{
int ret;
size_t length = sizeof(phy_init_magic_pre) +
sizeof(esp_phy_init_data_t) + sizeof(phy_init_magic_post);
uint8_t *init_data_store = kmm_malloc(length);
if (init_data_store == NULL)
{
wlerr("ERROR: Failed to allocate memory for updated country code "
"PHY init data\n");
return -ENOMEM;
}
ret = esp32s3_partition_read(phy_partion_label, 0, init_data_store,
length);
if (ret != OK)
{
kmm_free(init_data_store);
wlerr("ERROR: Failed to read updated country code PHY data\n");
return ret;
}
if (memcmp(init_data_store, PHY_INIT_MAGIC,
sizeof(phy_init_magic_pre)) != 0 ||
memcmp(init_data_store + length - sizeof(phy_init_magic_post),
PHY_INIT_MAGIC, sizeof(phy_init_magic_post)) != 0)
{
kmm_free(init_data_store);
wlerr("ERROR: Failed to validate updated country code PHY data\n");
return ERROR;
}
/* find init data bin according init data type */
if (init_data_type != ESP_PHY_INIT_DATA_TYPE_DEFAULT)
{
ret = phy_get_multiple_init_data(init_data_store, length,
init_data_type);
if (ret != OK)
{
kmm_free(init_data_store);
#ifdef CONFIG_ESP32S3_PHY_INIT_DATA_ERROR
abort();
#else
return ret;
#endif
}
}
else
{
g_phy_init_data_type = ESP_PHY_INIT_DATA_TYPE_DEFAULT;
}
if (g_current_apply_phy_init_data != g_phy_init_data_type)
{
ret = esp_phy_apply_phy_init_data(init_data_store +
sizeof(phy_init_magic_pre));
if (ret != OK)
{
wlerr("ERROR: PHY init data failed to load\n");
kmm_free(init_data_store);
return ret;
}
wlinfo("PHY init data type updated from %s to %s\n",
g_phy_type[g_current_apply_phy_init_data],
g_phy_type[g_phy_init_data_type]);
g_current_apply_phy_init_data = g_phy_init_data_type;
}
kmm_free(init_data_store);
return OK;
}
#endif
#ifdef CONFIG_ESP32S3_PHY_INIT_DATA_IN_PARTITION
/****************************************************************************
* Name: esp_phy_get_init_data
*
* Description:
* Get PHY init data
*
* Input Parameters:
* None
*
* Returned Value:
* Pointer to PHY init data structure
*
****************************************************************************/
const esp_phy_init_data_t *esp_phy_get_init_data(void)
{
int ret;
size_t length = sizeof(phy_init_magic_pre) +
sizeof(esp_phy_init_data_t) + sizeof(phy_init_magic_post);
uint8_t *init_data_store = kmm_malloc(length);
if (init_data_store == NULL)
{
wlerr("ERROR: Failed to allocate memory for PHY init data\n");
return NULL;
}
ret = esp32s3_partition_read(phy_partion_label, 0, init_data_store,
length);
if (ret != OK)
{
wlerr("ERROR: Failed to get read data from MTD\n");
kmm_free(init_data_store);
return NULL;
}
if (memcmp(init_data_store, PHY_INIT_MAGIC, sizeof(phy_init_magic_pre))
!= 0 || memcmp(init_data_store + length - sizeof(phy_init_magic_post),
PHY_INIT_MAGIC, sizeof(phy_init_magic_post)) != 0)
{
#ifdef CONFIG_ESP32S3_PHY_DEFAULT_INIT_IF_INVALID
wlerr("ERROR: Failed to validate PHY data partition, restoring "
"default data into flash...");
memcpy(init_data_store, PHY_INIT_MAGIC, sizeof(phy_init_magic_pre));
memcpy(init_data_store + sizeof(phy_init_magic_pre),
&phy_init_data, sizeof(phy_init_data));
memcpy(init_data_store + sizeof(phy_init_magic_pre) +
sizeof(phy_init_data), PHY_INIT_MAGIC,
sizeof(phy_init_magic_post));
DEBUGASSERT(memcmp(init_data_store, PHY_INIT_MAGIC,
sizeof(phy_init_magic_pre)) == 0);
DEBUGASSERT(memcmp(init_data_store + length -
sizeof(phy_init_magic_post), PHY_INIT_MAGIC,
sizeof(phy_init_magic_post)) == 0);
/* write default data */
ret = esp32s3_partition_write(phy_partion_label, 0, init_data_store,
length);
if (ret != OK)
{
wlerr("ERROR: Failed to write default PHY data partition\n");
kmm_free(init_data_store);
return NULL;
}
#else /* CONFIG_ESP32S3_PHY_DEFAULT_INIT_IF_INVALID */
wlerr("ERROR: Failed to validate PHY data partition\n");
kmm_free(init_data_store);
return NULL;
#endif
}
#ifdef CONFIG_ESP32S3_SUPPORT_MULTIPLE_PHY_INIT_DATA
if (*(init_data_store + (sizeof(phy_init_magic_pre) +
PHY_SUPPORT_MULTIPLE_BIN_OFFSET)))
{
g_multiple_phy_init_data_bin = true;
wlinfo("Support multiple PHY init data bins\n");
}
else
{
wlinfo("Does not support multiple PHY init data bins\n");
}
#endif
wlinfo("PHY data partition validated\n");
return (const esp_phy_init_data_t *)
(init_data_store + sizeof(phy_init_magic_pre));
}
/****************************************************************************
* Name: esp_phy_release_init_data
*
* Description:
* Release PHY init data
*
* Input Parameters:
* init_data - Pointer to PHY init data structure
*
* Returned Value:
* None
*
****************************************************************************/
void esp_phy_release_init_data(const esp_phy_init_data_t *init_data)
{
kmm_free((uint8_t *)init_data - sizeof(phy_init_magic_pre));
}
#else /* CONFIG_ESP32S3_PHY_INIT_DATA_IN_PARTITION */
/****************************************************************************
* Name: esp_phy_get_init_data
*
* Description:
* Get PHY init data
*
* Input Parameters:
* None
*
* Returned Value:
* Pointer to PHY init data structure
*
****************************************************************************/
const esp_phy_init_data_t *esp_phy_get_init_data(void)
{
wlinfo("Loading PHY init data from application binary\n");
return &phy_init_data;
}
/****************************************************************************
* Name: esp_phy_release_init_data
*
* Description:
* Release PHY init data
*
* Input Parameters:
* init_data - Pointer to PHY init data structure
*
* Returned Value:
* None
*
****************************************************************************/
void esp_phy_release_init_data(const esp_phy_init_data_t *init_data)
{
}
#endif
/****************************************************************************
* Name: esp_read_mac
*
* Description:
* Read MAC address from efuse
*
* Input Parameters:
* mac - MAC address buffer pointer
* type - MAC address type
*
* Returned Value:
* 0 if success or -1 if fail
*
****************************************************************************/
int32_t esp_read_mac(uint8_t *mac, esp_mac_type_t type)
{
uint32_t regval[2];
uint8_t tmp;
uint8_t *data = (uint8_t *)regval;
int i;
if (mac == NULL)
{
wlerr("mac address param is NULL");
return -1;
}
if (type > ESP_MAC_BT)
{
wlerr("Input type is error=%d\n", type);
return -1;
}
regval[0] = getreg32(MAC_ADDR0_REG);
regval[1] = getreg32(MAC_ADDR1_REG);
for (i = 0; i < MAC_LEN; i++)
{
mac[i] = data[5 - i];
}
if (type == ESP_MAC_WIFI_SOFTAP)
{
tmp = mac[0];
for (i = 0; i < 64; i++)
{
mac[0] = tmp | 0x02;
mac[0] ^= i << 2;
if (mac[0] != tmp)
{
break;
}
}
if (i >= 64)
{
wlerr("Failed to generate SoftAP MAC\n");
return -1;
}
}
if (type == ESP_MAC_BT)
{
tmp = mac[0];
for (i = 0; i < 64; i++)
{
mac[0] = tmp | 0x02;
mac[0] ^= i << 2;
if (mac[0] != tmp)
{
break;
}
}
mac[5] += 1;
}
return 0;
}
/****************************************************************************
* Name: esp32s3_phy_update_country_info
*
* Description:
* Update PHY init data according to country code
*
* Input Parameters:
* country - PHY init data type
*
* Returned Value:
* OK on success; a negated errno on failure
*
****************************************************************************/
int esp32s3_phy_update_country_info(const char *country)
{
#ifdef CONFIG_ESP32S3_SUPPORT_MULTIPLE_PHY_INIT_DATA
uint8_t phy_init_data_type_map = 0;
if (memcmp(country, g_phy_current_country, sizeof(g_phy_current_country))
== 0)
{
return OK;
}
memcpy(g_phy_current_country, country, sizeof(g_phy_current_country));
if (!g_multiple_phy_init_data_bin)
{
wlerr("ERROR: Does not support multiple PHY init data bins\n");
return ERROR;
}
phy_init_data_type_map = phy_find_bin_type_according_country(country);
if (phy_init_data_type_map == g_phy_init_data_type)
{
return OK;
}
int ret = phy_update_init_data(phy_init_data_type_map);
if (ret != OK)
{
wlerr("ERROR: Failed to update PHY init data\n");
return ret;
}
#endif
return OK;
}
/****************************************************************************
* Name: esp32s3_phy_disable
*
* Description:
* Deinitialize PHY hardware
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
void esp32s3_phy_disable(void)
{
irqstate_t flags;
flags = enter_critical_section();
g_phy_access_ref--;
if (g_phy_access_ref == 0)
{
phy_digital_regs_store();
/* Disable PHY and RF. */
phy_close_rf();
/* Disable PHY temperature sensor */
phy_xpd_tsens();
/* Disable Wi-Fi/BT common peripheral clock.
* Do not disable clock for hardware RNG.
*/
esp32s3_phy_disable_clock();
}
leave_critical_section(flags);
}
/****************************************************************************
* Name: esp32s3_phy_enable
*
* Description:
* Initialize PHY hardware
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
void esp32s3_phy_enable(void)
{
static bool debug = false;
irqstate_t flags;
esp_phy_calibration_data_t *cal_data;
if (debug == false)
{
char *phy_version = get_phy_version_str();
wlinfo("phy_version %s\n", phy_version);
debug = true;
}
flags = spin_lock_irqsave(NULL);
if (g_phy_access_ref == 0)
{
esp32s3_phy_enable_clock();
if (g_is_phy_calibrated == false)
{
cal_data = kmm_zalloc(sizeof(esp_phy_calibration_data_t));
if (cal_data == NULL)
{
wlerr("ERROR: Failed to allocate PHY"
"calibration data buffer.");
abort();
}
#if CONFIG_ESP_PHY_ENABLE_USB
phy_bbpll_en_usb(true);
#endif
wlinfo("calibrating");
const esp_phy_init_data_t *init_data = esp_phy_get_init_data();
if (init_data == NULL)
{
wlerr("ERROR: Failed to obtain PHY init data");
abort();
}
register_chipv7_phy(init_data, cal_data, PHY_RF_CAL_FULL);
esp_phy_release_init_data(init_data);
g_is_phy_calibrated = true;
kmm_free(cal_data);
}
else
{
phy_wakeup_init();
phy_digital_regs_load();
}
}
g_phy_access_ref++;
spin_unlock_irqrestore(NULL, flags);
}
/****************************************************************************
* Name: esp_wifi_bt_power_domain_on
*
* Description:
* Initialize Bluetooth and Wi-Fi power domain
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
void IRAM_ATTR esp_wifi_bt_power_domain_on(void)
{
irqstate_t flags;
flags = enter_critical_section();
if (g_wifi_bt_pd_controller++ == 0)
{
modifyreg32(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_WIFI_FORCE_PD, 0);
modifyreg32(SYSCON_WIFI_RST_EN_REG, 0, MODEM_RESET_FIELD_WHEN_PU);
modifyreg32(SYSCON_WIFI_RST_EN_REG, MODEM_RESET_FIELD_WHEN_PU, 0);
modifyreg32(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_WIFI_FORCE_ISO, 0);
}
leave_critical_section(flags);
}
/****************************************************************************
* Name: esp_wifi_bt_power_domain_off
*
* Description:
* Deinitialize Bluetooth and Wi-Fi power domain
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
void esp_wifi_bt_power_domain_off(void)
{
irqstate_t flags;
flags = enter_critical_section();
if (--g_wifi_bt_pd_controller == 0)
{
modifyreg32(RTC_CNTL_DIG_ISO_REG, 0, RTC_CNTL_WIFI_FORCE_ISO);
modifyreg32(RTC_CNTL_DIG_PWC_REG, 0, RTC_CNTL_WIFI_FORCE_PD);
}
leave_critical_section(flags);
}
/****************************************************************************
* Name: esp_timer_create
*
* Description:
* Create timer with given arguments
*
* Input Parameters:
* create_args - Timer arguments data pointer
* out_handle - Timer handle pointer
*
* Returned Value:
* 0 if success or -1 if fail
*
****************************************************************************/
int32_t esp_timer_create(const esp_timer_create_args_t *create_args,
esp_timer_handle_t *out_handle)
{
int ret;
struct rt_timer_args_s rt_timer_args;
struct rt_timer_s *rt_timer;
rt_timer_args.arg = create_args->arg;
rt_timer_args.callback = create_args->callback;
ret = esp32s3_rt_timer_create(&rt_timer_args, &rt_timer);
if (ret)
{
wlerr("Failed to create rt_timer error=%d\n", ret);
return ret;
}
*out_handle = (esp_timer_handle_t)rt_timer;
return 0;
}
/****************************************************************************
* Name: esp_timer_start_once
*
* Description:
* Start timer with one shot mode
*
* Input Parameters:
* timer - Timer handle pointer
* timeout_us - Timeout value by micro second
*
* Returned Value:
* 0 if success or -1 if fail
*
****************************************************************************/
int32_t esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us)
{
struct rt_timer_s *rt_timer = (struct rt_timer_s *)timer;
esp32s3_rt_timer_start(rt_timer, timeout_us, false);
return 0;
}
/****************************************************************************
* Name: esp_timer_start_periodic
*
* Description:
* Start timer with periodic mode
*
* Input Parameters:
* timer - Timer handle pointer
* period - Timeout value by micro second
*
* Returned Value:
* 0 if success or -1 if fail
*
****************************************************************************/
int32_t esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period)
{
struct rt_timer_s *rt_timer = (struct rt_timer_s *)timer;
esp32s3_rt_timer_start(rt_timer, period, true);
return 0;
}
/****************************************************************************
* Name: esp_timer_stop
*
* Description:
* Stop timer
*
* Input Parameters:
* timer - Timer handle pointer
*
* Returned Value:
* 0 if success or -1 if fail
*
****************************************************************************/
int32_t esp_timer_stop(esp_timer_handle_t timer)
{
struct rt_timer_s *rt_timer = (struct rt_timer_s *)timer;
esp32s3_rt_timer_stop(rt_timer);
return 0;
}
/****************************************************************************
* Name: esp_timer_delete
*
* Description:
* Delete timer and free resource
*
* Input Parameters:
* timer - Timer handle pointer
*
* Returned Value:
* 0 if success or -1 if fail
*
****************************************************************************/
int32_t esp_timer_delete(esp_timer_handle_t timer)
{
struct rt_timer_s *rt_timer = (struct rt_timer_s *)timer;
esp32s3_rt_timer_delete(rt_timer);
return 0;
}
/****************************************************************************
* Name: esp_init_semcache
*
* Description:
* Initialize semaphore cache.
*
* Parameters:
* sc - Semaphore cache data pointer
* sem - Semaphore data pointer
*
* Returned Value:
* None.
*
****************************************************************************/
void esp_init_semcache(struct esp_semcache_s *sc, sem_t *sem)
{
sc->sem = sem;
sc->count = 0;
list_initialize(&sc->node);
}
/****************************************************************************
* Name: esp_post_semcache
*
* Description:
* Store posting semaphore action into semaphore cache.
*
* Parameters:
* sc - Semaphore cache data pointer
*
* Returned Value:
* None.
*
****************************************************************************/
IRAM_ATTR void esp_post_semcache(struct esp_semcache_s *sc)
{
struct esp_wireless_priv_s *priv = &g_esp_wireless_priv;
if (!sc->count)
{
list_add_tail(&priv->sc_list, &sc->node);
}
sc->count++;
/* Enable CPU 2 interrupt. This will generate an IRQ as soon as non-IRAM
* are (re)enabled.
*/
modifyreg32(SYSTEM_CPU_INTR_FROM_CPU_2_REG, 0, SYSTEM_CPU_INTR_FROM_CPU_2);
}
/****************************************************************************
* Name: esp_init_queuecache
*
* Description:
* Initialize queue cache.
*
* Parameters:
* qc - Queue cache data pointer
* mq_ptr - Queue data pointer
* buffer - Queue cache buffer pointer
* size - Queue cache buffer size
*
* Returned Value:
* None.
*
****************************************************************************/
void esp_init_queuecache(struct esp_queuecache_s *qc,
struct file *mq_ptr,
uint8_t *buffer,
size_t size)
{
qc->mq_ptr = mq_ptr;
qc->size = size;
qc->buffer = buffer;
list_initialize(&qc->node);
}
/****************************************************************************
* Name: esp_send_queuecache
*
* Description:
* Store posting queue action and data into queue cache.
*
* Parameters:
* qc - Queue cache data pointer
* buffer - Data buffer
* size - Buffer size
*
* Returned Value:
* None.
*
****************************************************************************/
IRAM_ATTR void esp_send_queuecache(struct esp_queuecache_s *qc,
uint8_t *buffer,
int size)
{
struct esp_wireless_priv_s *priv = &g_esp_wireless_priv;
DEBUGASSERT(qc->size == size);
list_add_tail(&priv->qc_list, &qc->node);
memcpy(qc->buffer, buffer, size);
/* Enable CPU 0 interrupt. This will generate an IRQ as soon as non-IRAM
* are (re)enabled.
*/
modifyreg32(SYSTEM_CPU_INTR_FROM_CPU_2_REG, 0, SYSTEM_CPU_INTR_FROM_CPU_2);
}
/****************************************************************************
* Name: esp_wireless_init
*
* Description:
* Initialize ESP32 wireless common components for both BT and Wi-Fi.
*
* Parameters:
* None
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int esp_wireless_init(void)
{
int ret;
irqstate_t flags;
struct esp_wireless_priv_s *priv = &g_esp_wireless_priv;
flags = enter_critical_section();
if (priv->ref != 0)
{
priv->ref++;
leave_critical_section(flags);
return OK;
}
priv->cpuint = esp32s3_setup_irq(0, SWI_PERIPH, ESP32S3_INT_PRIO_DEF, 0);
if (priv->cpuint < 0)
{
/* Failed to allocate a CPU interrupt of this type. */
wlerr("ERROR: Failed to attach IRQ ret=%d\n", ret);
ret = priv->cpuint;
leave_critical_section(flags);
return ret;
}
ret = irq_attach(SWI_IRQ, esp_swi_irq, NULL);
if (ret < 0)
{
esp32s3_teardown_irq(0, SWI_PERIPH, priv->cpuint);
leave_critical_section(flags);
wlerr("ERROR: Failed to attach IRQ ret=%d\n", ret);
return ret;
}
list_initialize(&priv->sc_list);
list_initialize(&priv->qc_list);
up_enable_irq(SWI_IRQ);
priv->ref++;
leave_critical_section(flags);
return OK;
}
/****************************************************************************
* Name: esp_wireless_deinit
*
* Description:
* De-initialize ESP32 wireless common components.
*
* Parameters:
* None
*
* Returned Value:
* Zero (OK) is returned on success. A negated errno value is returned on
* failure.
*
****************************************************************************/
int esp_wireless_deinit(void)
{
irqstate_t flags;
struct esp_wireless_priv_s *priv = &g_esp_wireless_priv;
flags = enter_critical_section();
if (priv->ref > 0)
{
priv->ref--;
if (priv->ref == 0)
{
up_disable_irq(SWI_IRQ);
irq_detach(SWI_IRQ);
esp32s3_teardown_irq(0, SWI_PERIPH, priv->cpuint);
}
}
leave_critical_section(flags);
return OK;
}