diff --git a/boards/common/esp32/Makefile b/boards/common/esp32/Makefile new file mode 100644 index 0000000000000..765715e31582b --- /dev/null +++ b/boards/common/esp32/Makefile @@ -0,0 +1,3 @@ +MODULE = boards_common_esp32 + +include $(RIOTBASE)/Makefile.base diff --git a/boards/common/esp32/Makefile.dep b/boards/common/esp32/Makefile.dep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/boards/common/esp32/Makefile.features b/boards/common/esp32/Makefile.features new file mode 100644 index 0000000000000..32ce1ed896345 --- /dev/null +++ b/boards/common/esp32/Makefile.features @@ -0,0 +1,4 @@ +# on ESP32 modules all board features results directly from CPU features +# therefore just include CPU's Makefile.features + +include $(RIOTCPU)/esp32/Makefile.features diff --git a/boards/common/esp32/Makefile.include b/boards/common/esp32/Makefile.include new file mode 100644 index 0000000000000..2ce4a3dbea0bb --- /dev/null +++ b/boards/common/esp32/Makefile.include @@ -0,0 +1,11 @@ +# the cpu to build for +export CPU ?= esp32 +export CPU_MODEL ?= esp32 + +# configure the serial interface +PORT_LINUX ?= /dev/ttyUSB0 +PORT_DARWIN ?= $(firstword $(sort $(wildcard /dev/tty.SLAB_USBtoUART*))) +include $(RIOTMAKE)/tools/serial.inc.mk + +# reset tool configuration +export RESET = esptool.py --before default_reset run diff --git a/boards/common/esp32/board_common.c b/boards/common/esp32/board_common.c new file mode 100644 index 0000000000000..94f07664d4451 --- /dev/null +++ b/boards/common/esp32/board_common.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup boards_common_esp32 ESP32 Board Commons + * @ingroup boards_common + * @brief Common definitions for all ESP32 boards + * @{ + * + * @file + * @brief Common declarations and functions for all ESP32 boards. + * + * This file contains default declarations and functions that are valid + * for all ESP32 boards. + * + * @author Gunar Schorcht + */ + +#include "board_common.h" +#include "common.h" +#include "log.h" +#include "periph/gpio.h" +#include "periph/spi.h" + +#ifdef __cplusplus + extern "C" { +#endif + +void board_init(void) +{ + for (int i = 0; i < SPI_NUMOF; i++) + spi_init (SPI_DEV(i)); +} + +static uint32_t leds1_initialized = 0x0; /* GPIOs 0 ... 31 */ +static uint32_t leds2_initialized = 0x0; /* GPIOs 32 ... 39 */ + +static void _led_init(uint8_t led) +{ + uint32_t led_mask = (led < 32) ? BIT(led) : BIT(led - 32); + uint32_t* led_init = (led < 32) ? &leds1_initialized : &leds2_initialized; + + if (!(*led_init & led_mask)) + { + gpio_init (led, GPIO_OUT); + *led_init |= led_mask; + } +} + +void led_on_off (uint8_t led, uint8_t value) +{ + _led_init(led); + gpio_write(led, value ? 0 : 1); +} + +void led_toggle (uint8_t led) +{ + _led_init(led); + gpio_toggle(led); +} + +extern void adc_print_config(void); +extern void pwm_print_config(void); +extern void i2c_print_config(void); +extern void spi_print_config(void); +extern void uart_print_config(void); + +void print_board_config (void) +{ + adc_print_config(); + pwm_print_config(); + i2c_print_config(); + spi_print_config(); + uart_print_config(); + + LOG_INFO("LED: pins=[ "); + #ifdef LED0_PIN + LOG_INFO("%d ", LED0_PIN); + #endif + #ifdef LED1_PIN + LOG_INFO("%d ", LED1_PIN); + #endif + #ifdef LED2_PIN + LOG_INFO("%d ", LED2_PIN); + #endif + LOG_INFO("]\n"); +} + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +/** @} */ diff --git a/boards/common/esp32/doc.txt b/boards/common/esp32/doc.txt new file mode 100644 index 0000000000000..d19a3daf58a79 --- /dev/null +++ b/boards/common/esp32/doc.txt @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup boards_common_esp32 ESP32 Common + * @ingroup boards_common + * @brief Definitions that are common for all esp32 boards. + */ diff --git a/boards/common/esp32/include/board_common.h b/boards/common/esp32/include/board_common.h new file mode 100644 index 0000000000000..7098a5cae3839 --- /dev/null +++ b/boards/common/esp32/include/board_common.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup boards_common_esp32 + * @brief Common declarations and functions for all ESP32 boards. + * + * This file contains default declarations and functions that are used + * for all ESP32 boards. + * + * @author Gunar Schorcht + * @file + * @{ + */ + +#ifndef BOARD_COMMON_H +#define BOARD_COMMON_H + +#include + +#include "cpu.h" +#include "periph_conf.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * @brief Initialize board specific hardware + * + * Since all features of ESP32 boards are provided by the SOC, almost all + * initializations are done during the CPU initialization that is called from + * boot loader. Therefore, this function only initializes SPI interfaces. + */ +void board_init (void); + +/** + * @brief Swith LED on or off. + * @note LED outputs are supposed to be low active. + */ +void led_on_off (uint8_t led, uint8_t value); + +/** Toggle the LED status. */ +void led_toggle (uint8_t led); + +/** Print board configuration */ +void print_board_config (void); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* BOARD_COMMON_H */ +/** @} */ diff --git a/boards/common/esp32/include/periph_conf.h b/boards/common/esp32/include/periph_conf.h new file mode 100644 index 0000000000000..d64ff694bca6f --- /dev/null +++ b/boards/common/esp32/include/periph_conf.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup boards_common_esp32 + * @brief Common declarations of ESP32 periphery for all ESP32 boards + * + * This file contains default declarations that are valid for all ESP32. These + * default configurations can be overriden in *board.h* with board specific + * declarations. + * + * @author Gunar Schorcht + * @file + * @{ + */ + +#ifndef PERIPH_CONF_H +#define PERIPH_CONF_H + +/* load default periphery configurations provided by the CPU first */ +#include "periph_cpu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DOXYGEN + +#if defined(MODULE_MRF24J40) && !defined(MRF24J40_PARAM_SPI) +/** default configuration for MRF24J40 using VSPI / SPI_DEV(0) and GPIO2 as CS */ +#define MRF24J40_PARAM_SPI SPI_DEV(0) +#define MRF24J40_PARAM_SPI_CLK SPI_CLK_1MHZ +#define MRF24J40_PARAM_CS GPIO2 +#define MRF24J40_PARAM_RESET GPIO32 +#define MRF24J40_PARAM_INT GPIO34 +#endif /* defined(MODULE_MRF24J40) && !defined(MRF24J40_PARAM_SPI) */ + +#if defined(MODULE_ENC28J60) && !defined(ENC28J60_PARAM_SPI) +/** default configuration for ENC28J60 using VSPI / SPI_DEV(0) and GPIO2 as CS */ +#define ENC28J60_PARAM_SPI SPI_DEV(0) +#define ENC28J60_PARAM_CS GPIO4 +#define ENC28J60_PARAM_RESET GPIO33 +#define ENC28J60_PARAM_INT GPIO35 +#endif /* defined(MODULE_ENC28J60) && !defined(ENC28J60_PARAM_SPI) */ + +#if defined(MODULE_SDCARD_SPI) && !defined(SDCARD_SPI_PARAM_SPI) +/** default configuration for SDCARD module using HSPI / SPI_DEV(1) with default CS */ +#define SDCARD_SPI_PARAM_SPI SPI_DEV(1) +#define SDCARD_SPI_PARAM_CS SPI1_CS0 +#define SDCARD_SPI_PARAM_CLK SPI1_SCK +#define SDCARD_SPI_PARAM_MOSI SPI1_MOSI +#define SDCARD_SPI_PARAM_MISO SPI1_MISO +#define SDCARD_SPI_PARAM_POWER GPIO_UNDEF +#endif /* defined(MODULE_SDCARD_SPI) && !defined(SDCARD_SPI_PARAM_SPI) */ + +/** + * @name LED configuration (three predefined LEDs at maximum) + * @{ + */ +#ifdef LED0_PIN +/** if LED0 pin defined, declare according macros for handling */ +#define LED0_MASK (BIT(LED0_PIN)) +#define LED0_ON led_on_off (LED0_PIN, 1) +#define LED0_OFF led_on_off (LED0_PIN, 0) +#define LED0_TOGGLE led_toggle (LED0_PIN) +#endif + +#ifdef LED1_PIN +/** if LED1 pin defined, declare according macros for handling */ +#define LED1_MASK (BIT(LED1_PIN)) +#define LED1_ON led_on_off (LED1_PIN, 1) +#define LED1_OFF led_on_off (LED1_PIN, 0) +#define LED1_TOGGLE led_toggle (LED1_PIN) +#endif + +#ifdef LED2_PIN +/** if LED2 pin defined, declare according macros for handling */ +#define LED2_MASK (BIT(LED2_PIN)) +#define LED2_ON led_on_off (LED2_PIN, 1) +#define LED2_OFF led_on_off (LED2_PIN, 0) +#define LED2_TOGGLE led_toggle (LED2_PIN) +#endif +/** @} */ + +#endif /* !DOXYGEN */ + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* PERIPH_CONF_H */ +/** @} */ diff --git a/boards/esp32-generic/Makefile b/boards/esp32-generic/Makefile new file mode 100644 index 0000000000000..ca00a69d9cecc --- /dev/null +++ b/boards/esp32-generic/Makefile @@ -0,0 +1,5 @@ +MODULE = board + +DIRS = $(RIOTBOARD)/common/esp32 + +include $(RIOTBASE)/Makefile.base diff --git a/boards/esp32-generic/Makefile.features b/boards/esp32-generic/Makefile.features new file mode 100644 index 0000000000000..afe770df76c29 --- /dev/null +++ b/boards/esp32-generic/Makefile.features @@ -0,0 +1 @@ +include $(RIOTBOARD)/common/esp32/Makefile.features diff --git a/boards/esp32-generic/Makefile.include b/boards/esp32-generic/Makefile.include new file mode 100644 index 0000000000000..ccb56a010d71a --- /dev/null +++ b/boards/esp32-generic/Makefile.include @@ -0,0 +1,3 @@ +USEMODULE += boards_common_esp32 + +include $(RIOTBOARD)/common/esp32/Makefile.include diff --git a/boards/esp32-generic/include/board.h b/boards/esp32-generic/include/board.h new file mode 100644 index 0000000000000..b3447a15642ee --- /dev/null +++ b/boards/esp32-generic/include/board.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup boards_esp32_generic ESP32 - Generic Board + * @ingroup boards + * @brief Board defitions for generic ESP32 boards + * @{ + * + * This board definition can be used for all ESP32 boards that simply break + * out all GPIOs to external pads without having any special hardware or + * interfaces on-board. These are usually boards that use the ESP32 + * WROOM module like Espressif's EPS32-DEVKIT or ESP-32 NodeMCU and a large + * number of clones. + * + * Therefore the board definition is simply a wrapper arround the common ESP32 + * board definition. + * + * **PLEASE NOTE:** + * Most of the board defitions can be overriden by an application specific + * board configuration file. For that purpose, a header file located in + * application directory can be specified using the BOARD_APP_CONF make + * variable at command line, for example: + * + * BOARD_APP_CONF=esp32_generic_conf.h + * + * @file + * @author Gunar Schorcht + */ + +#ifndef BOARD_H +#define BOARD_H + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +/* include the application specific board configuration file from application + source directory if specified */ +#ifdef BOARD_APP_CONF +#include BOARD_APP_CONF +#endif + +/* define all board specific definitions here */ + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +/* include common board definitions as last step */ +#include "board_common.h" + +#endif /* BOARD_H */ +/** @} */ diff --git a/boards/esp32-mh-et-live-minikit/Makefile b/boards/esp32-mh-et-live-minikit/Makefile new file mode 100644 index 0000000000000..ca00a69d9cecc --- /dev/null +++ b/boards/esp32-mh-et-live-minikit/Makefile @@ -0,0 +1,5 @@ +MODULE = board + +DIRS = $(RIOTBOARD)/common/esp32 + +include $(RIOTBASE)/Makefile.base diff --git a/boards/esp32-mh-et-live-minikit/Makefile.features b/boards/esp32-mh-et-live-minikit/Makefile.features new file mode 100644 index 0000000000000..afe770df76c29 --- /dev/null +++ b/boards/esp32-mh-et-live-minikit/Makefile.features @@ -0,0 +1 @@ +include $(RIOTBOARD)/common/esp32/Makefile.features diff --git a/boards/esp32-mh-et-live-minikit/Makefile.include b/boards/esp32-mh-et-live-minikit/Makefile.include new file mode 100644 index 0000000000000..ccb56a010d71a --- /dev/null +++ b/boards/esp32-mh-et-live-minikit/Makefile.include @@ -0,0 +1,3 @@ +USEMODULE += boards_common_esp32 + +include $(RIOTBOARD)/common/esp32/Makefile.include diff --git a/boards/esp32-mh-et-live-minikit/include/board.h b/boards/esp32-mh-et-live-minikit/include/board.h new file mode 100644 index 0000000000000..b395155f169f0 --- /dev/null +++ b/boards/esp32-mh-et-live-minikit/include/board.h @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup boards_esp32_mh_et_live ESP32 - MH-ET LIVE MiniKit + * @ingroup boards + * @brief Board defitions for MH-ET LIVE MiniKit for ESP32 + * @{ + * + * + * The MH-ET LIVE MiniKit for ESP32 uses the ESP32-WROOM module. It is a + * very interesting development kit as it is available in the stackable + * Wemos D1 Mini format. Thus, all shields for Wemos D1 mini (ESP8266 + * platform) can also be used with ESP32. All GPIOs are broken so that + * it can be configured very flexibly. + * + * Board configuration defined in this file is: + * + * Pin | Defined Function | Remarks / Prerequisites + * :------|:-------------------------|:---------------------------------- + * GPIO0 | PWM_DEV(0):0 | | + * GPIO2 | PWM_DEV(0):1 / LED blue | | + * GPIO4 | PWM_DEV(0):2 | | + * GPIO22 | I2C_DEV(0):SCL | defined in ```periph_cpu.h``` + * GPIO21 | I2C_DEV(0):SDA | defined in ```periph_cpu.h``` + * GPIO18 | SPI_DEV(0):SCK | defined in ```periph_cpu.h``` + * GPIO19 | SPI_DEV(0):MISO | defined in ```periph_cpu.h``` + * GPIO23 | SPI_DEV(0):MOSI | defined in ```periph_cpu.h``` + * GPIO5 | SPI_DEV(0):CS0 | defined in ```periph_cpu.h``` + * GPIO1 | UART_DEV(0):TxD | Console (cannot be changed) + * GPIO3 | UART_DEV(0):RxD | Console (cannot be changed) + * GPIO9 | UART_DEV(1):TxD | defined in ```periph_cpu.h``` + * GPIO10 | UART_DEV(1):RxD | defined in ```periph_cpu.h``` + * GPIO34 | ADC:0 | | + * GPIO35 | ADC:1 | | + * GPIO36 | ADC:2 | | + * GPIO39 | ADC:3 | | + * GPIO25 | DAC:0 | | + * GPIO26 | DAC:1 | used as CS for MRF24J40 + * GPIO12 | Digital In/Out | used as CS for ENC28J60 + * GPIO13 | Digital In/Out | | + * GPIO14 | Digital In/Out | + * GPIO15 | Digital In/Out | + * GPIO16 | Digital In/Out | used as RESET for MRF24J40 + * GPIO17 | Digital In/Out | used as INT for MRF24J40 + * GPIO19 | Digital In/Out | | + * GPIO27 | Digital In/Out | used as RESET for ENC28J60 + * GPIO32 | Digital In/Out | used as INT for ENC28J60 + * GPIO33 | Digital In/Out | | + * + * **PLEASE NOTE:** + * Most of the board defitions can be overriden by an application specific + * board configuration file. For that purpose, a header file located in + * application directory can be specified using the BOARD_APP_CONF make + * variable at command line, for example: + * + * BOARD_APP_CONF=esp32_wrover_kit_conf.h + * + * If this option is defined, the given file is included from application + * source directory. + * + * @file + * @author Gunar Schorcht + */ + +#ifndef BOARD_H +#define BOARD_H + +#include + +/* include the application specific board configuration file from application + source directory if specified */ +#ifdef BOARD_APP_CONF +#include BOARD_APP_CONF +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * @name Declaration of possible ADC and DAC GPIOs + * @{ + */ +#if !defined(ADC_GPIOS) && !defined(ADC_GPIOS_NOT_AVAILABLE) +#define ADC_GPIOS { GPIO34, GPIO35, GPIO36, GPIO39 } +#endif + +#if !defined(DAC_GPIOS) && !defined(DAC_GPIOS_NOT_AVAILABLE) +#define DAC_GPIOS { GPIO25, GPIO26 } +#endif +/** @} */ + + +/** + * @name Declaration of PWM channels. + */ +#if !defined(PWM0_GPIOS) && !defined(PWM0_GPIOS_NOT_AVAILABLE) +/** GPIOS that can be used with PWM_DEV(0) as PWM channels */ +#define PWM0_GPIOS { GPIO0, GPIO2, GPIO4 } +#endif + +#if !defined(PWM1_GPIOS) && !defined(PWM1_GPIOS_NOT_AVAILABLE) +/** + * By default, PWM_DEV(1) is not used with this board. This can be changed + * by an application-specific board configuration file located in the + * application source directory specified by the BOARD_APP_CONF make + * variable at command line. + */ +#define PWM1_GPIOS_NOT_AVAILABLE +#endif + +/** + * @name Declaration of on-board LEDs + * @{ + */ +#define LED0_PIN GPIO2 +#define LED_BLUE_PIN GPIO2 +/** @} */ + +/** + * @name Configuration of VSPI / SPI_DEV(0) interface + * The default configuration is used. + * @{ + */ +#if !defined(SPI0_SCK) && !defined(SPI0_MISO) && !defined(SPI0_MOSI) && !defined(SPI0_CS0) +#define SPI0_SCK GPIO18 +#define SPI0_MISO GPIO19 +#define SPI0_MOSI GPIO23 +#define SPI0_CS0 GPIO5 +#endif +/** @} */ + +/** + * @name Configuration of HSPI / SPI_DEV(1) interface + * SD card shield uses SPI_DEV(0) interface. Therefore, the interface is + * declared as not available. + * @{ + */ +#if !defined(SPI1_NOT_AVAILABLE) && !defined(SPI1_SCK) +#define SPI1_NOT_AVAILABLE +#endif +/** @} */ + +/** + * @name Configuration of UART interfaces. + * + * UART_DEV(0) and UART_DEV(1) use the default configuration as defined + * in ```periph_cpu.h```. + * @{ + */ +#if !defined(UART2_NOT_AVAILABLE) && !defined(UART2_TXD) && !defined(UART2_RXD) +/** UART_DEV(2) interface is not used with this board. */ +#define UART2_NOT_AVAILABLE +#endif +/** @} */ + +/** + * @name Configuration for SD card interface + * + * SD card interface uses SPI_DEV(0) on this board to be compatible with the + * Wemos D1 mini micro SD card shield. + */ +#if (defined(MODULE_SDCARD_SPI) && !defined(SDCARD_SPI_PARAM_SPI)) || DOXYGEN +#define SDCARD_SPI_PARAM_SPI SPI_DEV(0) +#define SDCARD_SPI_PARAM_CS SPI0_CS0 +#define SDCARD_SPI_PARAM_CLK SPI0_SCK +#define SDCARD_SPI_PARAM_MOSI SPI0_MOSI +#define SDCARD_SPI_PARAM_MISO SPI0_MISO +#define SDCARD_SPI_PARAM_POWER GPIO_UNDEF +#endif + +/** + * @name Configuration for MRF24J40 module using VPSI / SPI_DEV(0) + * @{ + */ +#if (defined(MODULE_MRF24J40) && !defined(MRF24J40_PARAM_SPI)) || DOXYGEN +#define MRF24J40_PARAM_SPI SPI_DEV(0) +#define MRF24J40_PARAM_SPI_CLK SPI_CLK_1MHZ +#define MRF24J40_PARAM_CS GPIO26 +#define MRF24J40_PARAM_RESET GPIO16 +#define MRF24J40_PARAM_INT GPIO17 +#endif +/** @} */ + +/** + * @name Configuration for ENC28J60 module using VSPI / SPI_DEV(0) + * @{ + */ +#if (defined(MODULE_ENC28J60) && !defined(ENC28J60_PARAM_SPI)) || DOXYGEN +#define ENC28J60_PARAM_SPI SPI_DEV(0) +#define ENC28J60_PARAM_CS GPIO12 +#define ENC28J60_PARAM_RESET GPIO27 +#define ENC28J60_PARAM_INT GPIO32 +#endif +/** @} */ + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +/* include common board definitions as last step */ +#include "board_common.h" + +#endif /* BOARD_H */ +/** @} */ diff --git a/boards/esp32-wemos-d32-pro/Makefile b/boards/esp32-wemos-d32-pro/Makefile new file mode 100644 index 0000000000000..ca00a69d9cecc --- /dev/null +++ b/boards/esp32-wemos-d32-pro/Makefile @@ -0,0 +1,5 @@ +MODULE = board + +DIRS = $(RIOTBOARD)/common/esp32 + +include $(RIOTBASE)/Makefile.base diff --git a/boards/esp32-wemos-d32-pro/Makefile.features b/boards/esp32-wemos-d32-pro/Makefile.features new file mode 100644 index 0000000000000..afe770df76c29 --- /dev/null +++ b/boards/esp32-wemos-d32-pro/Makefile.features @@ -0,0 +1 @@ +include $(RIOTBOARD)/common/esp32/Makefile.features diff --git a/boards/esp32-wemos-d32-pro/Makefile.include b/boards/esp32-wemos-d32-pro/Makefile.include new file mode 100644 index 0000000000000..ccb56a010d71a --- /dev/null +++ b/boards/esp32-wemos-d32-pro/Makefile.include @@ -0,0 +1,3 @@ +USEMODULE += boards_common_esp32 + +include $(RIOTBOARD)/common/esp32/Makefile.include diff --git a/boards/esp32-wemos-d32-pro/include/board.h b/boards/esp32-wemos-d32-pro/include/board.h new file mode 100644 index 0000000000000..d6060dda2c0a1 --- /dev/null +++ b/boards/esp32-wemos-d32-pro/include/board.h @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup boards_esp32_wemos_d32_pro ESP32 - Wemos D32 Pro + * @ingroup boards + * @brief Board defitions for Wemos D32 Pro + * @{ + * + * + * The Wemos D32 Pro is a development board that uses the ESP32-WROVER + * module which includes a built-in 4 MByte SPI RAM. Most important + * features of the board are + * + * - Micro-SD card interface + * - LCD interface + * + * Furthermore, many GPIOs are broken out for extension. + * + * Board configuration defined in this file is: + * + * Pin | Defined Function | Remarks / Prerequisites + * :------|:-------------------------|:---------------------------------- + * GPIO5 | PWM_DEV(0):0 / LED | | + * GPIO22 | I2C_DEV(0):SCL | defined in ```periph_cpu.h``` + * GPIO21 | I2C_DEV(0):SDA | defined in ```periph_cpu.h``` + * GPIO18 | SPI_DEV(0):SCK | | + * GPIO19 | SPI_DEV(0):MISO | | + * GPIO23 | SPI_DEV(0):MOSI | | + * GPIO4 | SPI_DEV(0):CS0 | | + * GPIO1 | UART_DEV(0):TxD | Console (cannot be changed) + * GPIO3 | UART_DEV(0):RxD | Console (cannot be changed) + * GPIO35 | ADC:0 | VBat (not broken out) + * GPIO34 | ADC:1 | | + * GPIO36 | ADC:2 | | + * GPIO39 | ADC:3 | | + * GPIO32 | ADC:4 | available if LCD is not connected + * GPIO33 | ADC:5 | available if LCD is not connected + * GPIO25 | DAC:0 | | + * GPIO26 | DAC:1 | | + * GPIO0 | Digital In/Out | used as CS for MRF24J40, ENC28J60 + * GPIO2 | Digital In/Out | used as RESET for MRF24J40, ENC28J60 + * GPIO13 | Digital In/Out | used as INT for MRF24J40, ENC28J60 + * GPIO15 | Digital In/Out | | + * GPIO12 | Digital In/Out | available if LCD is not connected + * GPIO14 | Digital In/Out | available if LCD is not connected + * GPIO27 | Digital In/Out | available if LCD is not connected + * + * **PLEASE NOTE:** + * Most of the board defitions can be overriden by an application specific + * board configuration file. For that purpose, a header file located in + * application directory can be specified using the BOARD_APP_CONF make + * variable at command line, for example: + * + * BOARD_APP_CONF=esp32_wrover_kit_conf.h + * + * If this option is defined, the given file is included from application + * source directory. + * + * @file + * @author Gunar Schorcht + */ + +#ifndef BOARD_H +#define BOARD_H + +#include + +/* include the application specific board configuration file from application + source directory if specified */ +#ifdef BOARD_APP_CONF +#include BOARD_APP_CONF +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/* Board specific definitions */ + +/** + * Set this definition to 1 when LCD is connected. + * It can be changed during make by adding the CONFIGS make variable at + * command line: CONFIGS='-DESP_LCD_PLUGGED_IN=1'. Alternatively, it + * can be defined in an application specific board configuration file located + * in application directory can be specified using the BOARD_APP_CONF make + * variable at command line. + */ +#ifndef ESP_LCD_PLUGGED_IN +#define ESP_LCD_PLUGGED_IN 1 +#endif + +/** + * @name Declaration of possible ADC and DAC GPIOs + * @{ + */ +#if !defined(ADC_GPIOS) && !defined(ADC_GPIOS_NOT_AVAILABLE) +#if !ESP_LCD_PLUGGED_IN +#define ADC_GPIOS { GPIO35, GPIO34, GPIO36, GPIO39, GPIO32, GPIO33 } +#else +#define ADC_GPIOS { GPIO35, GPIO34, GPIO36, GPIO39 } +#endif +#endif + +#if !defined(DAC_GPIOS) && !defined(DAC_GPIOS_NOT_AVAILABLE) +#define DAC_GPIOS { GPIO25, GPIO26 } +#endif +/** @} */ + +/** + * @name Declaration of PWM channels. + */ +#if !defined(PWM0_GPIOS) && !defined(PWM0_GPIOS_NOT_AVAILABLE) +/** GPIOS that can be used with PWM_DEV(0) as PWM channels */ +#define PWM0_GPIOS { GPIO5 } +#endif + +#if !defined(PWM1_GPIOS) && !defined(PWM1_GPIOS_NOT_AVAILABLE) +/** + * By default, PWM_DEV(1) is not used with this board. This can be changed + * by an application-specific board configuration file located in the + * application source directory specified by the BOARD_APP_CONF make + * variable at command line. + */ +#define PWM1_GPIOS_NOT_AVAILABLE +#endif + + +/** + * @name Declaration of on-board LEDs + * @{ + */ +#define LED0_PIN GPIO5 +/** @} */ + +/** + * @name Configuration of VSPI / SPI_DEV(0) interface + * + * SPI_DEV(0) is used for SD card and LCD interface. It can also be used + * by other peripherals with different CS signals. + * @{ + */ +#if !defined(SPI0_SCK) && !defined(SPI0_MISO) && !defined(SPI0_MOSI) && !defined(SPI0_CS0) +#define SPI0_SCK GPIO18 +#define SPI0_MISO GPIO19 +#define SPI0_MOSI GPIO23 +#define SPI0_CS0 GPIO4 +#endif +/** @} */ + +/** + * @name Configuration of HSPI / SPI_DEV(1) interface + * + * SD card shield uses SPI_DEV(0) interface. Therefore, the interface is + * declared as not available. + * @{ + */ +#if !defined(SPI1_NOT_AVAILABLE) && !defined(SPI1_SCK) +#define SPI1_NOT_AVAILABLE +#endif +/** @} */ + + +/** + * @name Configuration of UART interfaces. + * + * UART_DEV(0) uses the default configuration as defined + * in ```periph_cpu.h```. + * @{ + */ +#if !defined(UART1_NOT_AVAILABLE) && !defined(UART1_TXD) && !defined(UART1_RXD) +/** UART_DEV(1) interface is not used with this board. */ +#define UART1_NOT_AVAILABLE +#endif + +#if !defined(UART2_NOT_AVAILABLE) && !defined(UART2_TXD) && !defined(UART2_RXD) +/** UART_DEV(2) interface is not used with this board. */ +#define UART2_NOT_AVAILABLE +#endif +/** @} */ + +/** + * @name Configuration for SD card interface + * + * SD card interface uses SPI_DEV(0) on this board. + */ +#if (defined(MODULE_SDCARD_SPI) && !defined(SDCARD_SPI_PARAM_SPI)) || DOXYGEN +#define SDCARD_SPI_PARAM_SPI SPI_DEV(0) +#define SDCARD_SPI_PARAM_CS SPI0_CS0 +#define SDCARD_SPI_PARAM_CLK SPI0_SCK +#define SDCARD_SPI_PARAM_MOSI SPI0_MOSI +#define SDCARD_SPI_PARAM_MISO SPI0_MISO +#define SDCARD_SPI_PARAM_POWER GPIO_UNDEF +#endif + +/** + * Please note: It uses the same CS, RESET and INT configuration as module + * ENC28J60. If both modules are used simultaneously, the configuration of one + * module has to be changed in an application-specific board configuration + * file located in the application source directory specified by the + * BOARD_APP_CONF make variable at command line. + * @{ + */ +#if (defined(MODULE_MRF24J40) && !defined(MRF24J40_PARAM_SPI)) || DOXYGEN +#define MRF24J40_PARAM_SPI SPI_DEV(0) +#define MRF24J40_PARAM_SPI_CLK SPI_CLK_1MHZ +#define MRF24J40_PARAM_CS GPIO0 +#define MRF24J40_PARAM_RESET GPIO2 +#define MRF24J40_PARAM_INT GPIO13 +#endif +/** @} */ + +/** + * @name Configuration for ENC28J60 module using VSPI / SPI_DEV(0) + * Please note: If both modules are used simultaneously, the configuration of one + * module has to be changed in an application-specific board configuration + * file located in the application source directory specified by the + * BOARD_APP_CONF make variable at command line. + * @{ + */ +#if (defined(MODULE_ENC28J60) && !defined(ENC28J60_PARAM_SPI)) || DOXYGEN +#define ENC28J60_PARAM_SPI SPI_DEV(0) +#define ENC28J60_PARAM_CS GPIO0 +#define ENC28J60_PARAM_RESET GPIO2 +#define ENC28J60_PARAM_INT GPIO13 +#endif +/** @} */ + + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +/* include common board definitions as last step */ +#include "board_common.h" + +#endif /* BOARD_H */ +/** @} */ diff --git a/boards/esp32-wrover-kit/Makefile b/boards/esp32-wrover-kit/Makefile new file mode 100644 index 0000000000000..ca00a69d9cecc --- /dev/null +++ b/boards/esp32-wrover-kit/Makefile @@ -0,0 +1,5 @@ +MODULE = board + +DIRS = $(RIOTBOARD)/common/esp32 + +include $(RIOTBASE)/Makefile.base diff --git a/boards/esp32-wrover-kit/Makefile.features b/boards/esp32-wrover-kit/Makefile.features new file mode 100644 index 0000000000000..afe770df76c29 --- /dev/null +++ b/boards/esp32-wrover-kit/Makefile.features @@ -0,0 +1 @@ +include $(RIOTBOARD)/common/esp32/Makefile.features diff --git a/boards/esp32-wrover-kit/Makefile.include b/boards/esp32-wrover-kit/Makefile.include new file mode 100644 index 0000000000000..ccb56a010d71a --- /dev/null +++ b/boards/esp32-wrover-kit/Makefile.include @@ -0,0 +1,3 @@ +USEMODULE += boards_common_esp32 + +include $(RIOTBOARD)/common/esp32/Makefile.include diff --git a/boards/esp32-wrover-kit/include/board.h b/boards/esp32-wrover-kit/include/board.h new file mode 100644 index 0000000000000..05946ef2fe159 --- /dev/null +++ b/boards/esp32-wrover-kit/include/board.h @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup boards_esp32_wrover_kit ESP32 - ESP-WROVER-KIT V3 + * @ingroup boards + * @brief Board defitions for Espressif ESP-WROVER-KIT V3 + * @{ + * + * + * The Espressif ESP-WROVER-KIT is a development board that uses the + * ESP32-WROVER module which includes a built-in 4 MByte SPI RAM. Most + * important features of the board are + * + * - Micro-SD card interface + * - OV7670 camera interface + * - 3.2" SPI LCD panel + * - RGB LED + * + * Furthermore, many GPIOs are broken out for extension. The USB bridge + * based on FDI FT2232HL provides a JTAG interface for debugging through + * the USB interface. + * + * Board configuration defined in this file is: + * + * Pin | Defined Function | Prerequisites / Remarks + * :------|:-------------------------|:---------------------------------- + * GPIO0 | LED red / PWM_DEV(0):0 | Camera not connected + * GPIO2 | LED green / PWM_DEV(0):1 | SD Card interface not used + * GPIO4 | LED blue / PWM_DEV(0):2 | Camera not connected + * GPIO5 | I2C_DEV(0):SCL | Camera not connected + * GPIO27 | I2C_DEV(0):SDA | Camera not connected + * GPIO19 | SPI_DEV(0):SCK | Camera not connected + * GPIO23 | SPI_DEV(0):MOSI | Camera not connected + * GPIO25 | SPI_DEV(0):MISO | Camera not connected + * GPIO22 | SPI_DEV(0):CS0 | Camera not connected + * GPIO1 | UART_DEV(0):TxD | Console (cannot be changed) + * GPIO3 | UART_DEV(0):RxD | Console (cannot be changed) + * GPIO26 | DAC:0 | Camera not connected + * GPIO12 | ADC:0 | | + * GPIO36 | ADC:1 | Camera not connected + * GPIO39 | ADC:2 | Camera not connected + * GPIO9 | Digital In/Out | SPI RAM not used, used as CS for MRF24J40, ENC28J60 + * GPIO10 | Digital In/Out | SPI RAM not used, used as RESET for MRF24J40, ENC28J60 + * GPIO13 | Digital In/Out | SD Card interface not used + * GPIO14 | Digital In/Out | SD Card interface not used + * GPIO15 | Digital In/Out | SD Card interface not used + * GPIO34 | Digital In | used as INT for MRF24J40, ENC28J60 + * GPIO35 | Digital In | | + * + * **PLEASE NOTE:** + * LCD and camera can not be used at the same time as both use the same + * signals. The availability of some peripherals depends on the use of the + * camera and the SD card interface. + * + * **PLEASE NOTE:** + * Most of the board defitions can be overriden by an application specific + * board configuration file. For that purpose, a header file located in + * application directory can be specified using the BOARD_APP_CONF make + * variable at command line, for example: + * + * BOARD_APP_CONF=esp32_wrover_kit_conf.h + * + * If this option is defined, the given file is included from application + * source directory. + * + * @file + * @author Gunar Schorcht + */ + +#ifndef BOARD_H +#define BOARD_H + +#include + +/* include the application specific board configuration file from application + source directory if specified */ +#ifdef BOARD_APP_CONF +#include BOARD_APP_CONF +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * Set this definition to 1 when LCD is connected. + * It can be changed during make by adding the CONFIGS make variable at + * command line: CONFIGS='-DESP_CAMERA_PLUGGED_IN=1'. Alternatively, it + * can be defined in an application specific board configuration file located + * in application directory can be specified using the BOARD_APP_CONF make + * variable at command line. + */ +#ifndef ESP_CAMERA_PLUGGED_IN +#define ESP_CAMERA_PLUGGED_IN 0 +#endif + +/** + * @name Declaration of possible ADC GPIOs + * @{ + */ +#if !defined(ADC_GPIOS) && !defined(ADC_GPIOS_NOT_AVAILABLE) +#if !ESP_CAMERA_PLUGGED_IN +/** These ADC Channels can only be used if the camera is not plugged in. */ +#define ADC_GPIOS { GPIO12, GPIO36, GPIO39 } +#else +#define ADC_GPIOS_NOT_AVAILABLE +#endif /* ESP_CAMERA_PLUGGED_IN == 0 */ +#endif /* ADC_GPIOS */ +/** @} */ + +/** + * @name Declaration of possible DAC GPIOs + * @{ + */ +#if !defined(DAC_GPIOS) && !defined(DAC_GPIOS_NOT_AVAILABLE) +#if !ESP_CAMERA_PLUGGED_IN +/** These DAC Channels can only be used if the camera is not plugged in. */ +#define DAC_GPIOS { GPIO26 } +#else +#define DAC_GPIOS_NOT_AVAILABLE +#endif /* ESP_CAMERA_PLUGGED_IN == 0 */ +#endif /* DAC_GPIOS */ +/** @} */ + +/** + * @name I2C configuration + * + * If the camera is connected, this interface is not available because the + * camera interface uses some of the signals. + * @{ + */ +#if !defined(I2C0_NOT_AVAILABLE) && !defined(I2C0_SCL) && !defined(I2C0_SDA) +#if !ESP_CAMERA_PLUGGED_IN +#define I2C0_SCL GPIO5 +#define I2C0_SDA GPIO27 +#else +#define I2C0_NOT_AVAILABLE +#endif /* ESP_CAMERA_PLUGGED_IN == 0 */ +#endif /* !defined(I2C0_NOT_AVAILABLE) && !defined(I2C0_SCL) && !defined(I2C0_SDA) */ +/** @} */ + + +/** + * @name VSPI / SPI_DEV(0) interface configuration + * + * The interface is used by the on-board LCD interface. It can also be used + * to connect other peripherals with different CS signals. + * + * If the camera is connected, this interface is not available because the + * camera interface uses some of the signals. + * @{ + */ +#if !defined(SPI0_NOT_AVAILABLE) && !defined(SPI0_SCK) +#if !ESP_CAMERA_PLUGGED_IN +#define SPI0_CS0 GPIO22 /**< LCD CS */ +#define SPI0_SCK GPIO19 /**< LCD SCL, can be used to connect peripherals */ +#define SPI0_MOSI GPIO23 /**< LCD MOSI, can be used to connect peripherals */ +#define SPI0_MISO GPIO25 /**< LCD MISO, can be used to connect peripherals */ +#else +#define SPI0_NOT_AVAILABLE +#endif /* ESP_CAMERA_PLUGGED_IN == 0 */ +#endif /* !defined(SPI0_NOT_AVAILABLE) && !defined(SPI0_SCK) */ +/** @} */ + +/** + * @name HSPI / SPI_DEV(1) interface configuration interface + * + * The interface is used by the on-board SD card interface. In this case the + * green LED cannot be used. + * @{ + */ +#if !defined(SPI1_NOT_AVAILABLE) && !defined(SPI1_SCK) +#if MODULE_SDCARD_SPI || DOXYGEN +#define SPI1_SCK GPIO14 /**< SD card interface CLK */ +#define SPI1_MISO GPIO2 /**< SD card interface MISO */ +#define SPI1_MOSI GPIO15 /**< SD card interface_MOSI */ +#define SPI1_CS0 GPIO13 /**< SD card interface CS */ +#else +#define SPI1_NOT_AVAILABLE +#endif /* MODULE_SDCARD_SPI */ +#endif /* !defined(SPI1_NOT_AVAILABLE) && !defined(SPI1_SCK) */ +/** @} */ + +/** + * @name Declaration of PWM channels. + */ +#if !defined(PWM0_GPIOS) && !defined(PWM0_GPIOS_NOT_AVAILABLE) +#if (!ESP_CAMERA_PLUGGED_IN && defined(SPI1_NOT_AVAILABLE)) || DOXYGEN +/** LED pins are used with PWM_DEV(0) as PWM channels */ +#define PWM0_GPIOS { GPIO0, GPIO2, GPIO4 } +#elif !ESP_CAMERA_PLUGGED_IN && !defined(SPI1_NOT_AVAILABLE) +#define PWM0_GPIOS { GPIO0, GPIO4 } +#elif ESP_CAMERA_PLUGGED_IN && defined(SPI1_NOT_AVAILABLE) +#define PWM0_GPIOS { GPIO2 } +#else +#define PWM0_GPIOS_NOT_AVAILABLE +#endif +#endif /* !defined(PWM0_GPIOS) && !defined(PWM0_GPIOS_NOT_AVAILABLE) */ + +#if !defined(PWM1_GPIOS) && !defined(PWM1_GPIOS_NOT_AVAILABLE) +/** PWM_DEV(1) is not used. */ +#define PWM1_GPIOS_NOT_AVAILABLE +#endif + +/** + * @name Declaration of UART interfaces. + * + * There are no additional UART interfaces defined here. If addtional + * UART interface are neede, they have to be defined in an application + * specific board definition file. + */ +#if !defined(UART1_NOT_AVAILABLE) && !defined(UART1_TXD) && !defined(UART1_RXD) +#define UART1_NOT_AVAILABLE +#endif + +#if !defined(UART2_NOT_AVAILABLE) && !defined(UART2_TXD) && !defined(UART2_RXD) +#define UART2_NOT_AVAILABLE +#endif + +/** + * @name Declaration of on-board LEDs + * @{ + */ +#if !ESP_CAMERA_PLUGGED_IN || DOXYGEN /* camera is not plugged in */ +#define LED0_PIN GPIO0 +#define LED2_PIN GPIO4 +#define LED_RED_PIN GPIO0 +#define LED_BLUE_PIN GPIO4 +#endif + +#if defined(SPI1_NOT_AVAILABLE) || DOXYGEN /* SD card interface is not used */ +#define LED1_PIN GPIO2 +#define LED_GREEN_PIN GPIO2 +#endif + +/** @} */ + +/** + * @name Configuration for MRF24J40 module using VPSI / SPI_DEV(0) + * Please note: It uses the same CS, RESET and INT configuration as module + * ENC28J60. If both modules are used simultaneously, the configuration of one + * module has to be changed in an application-specific board configuration + * file located in the application source directory specified by the + * BOARD_APP_CONF make variable at command line. + * @{ + */ +#if (defined(MODULE_MRF24J40) && !defined(MRF24J40_PARAM_SPI)) || DOXYGEN +#if SPI_RAM_USED +#error GPIO9 and GPIO10 are not availabl when SPI RAM is activated +#else +#define MRF24J40_PARAM_SPI SPI_DEV(0) +#define MRF24J40_PARAM_SPI_CLK SPI_CLK_1MHZ +#define MRF24J40_PARAM_CS GPIO9 +#define MRF24J40_PARAM_RESET GPIO10 +#define MRF24J40_PARAM_INT GPIO34 +#endif /* SPI_RAM_USED */ +#endif /* defined(MODULE_MRF24J40) && !defined(MRF24J40_PARAM_SPI) */ +/** @} */ + +/** + * @name Configuration for ENC28J60 module using VSPI / SPI_DEV(0) + * Please note: If both modules are used simultaneously, the configuration of one + * module has to be changed in an application-specific board configuration + * file located in the application source directory specified by the + * BOARD_APP_CONF make variable at command line. + * @{ + */ +#if (defined(MODULE_ENC28J60) && !defined(ENC28J60_PARAM_SPI)) || DOXYGEN +#if SPI_RAM_USED +#error GPIO9 and GPIO10 are not availabl when SPI RAM is activated +#else +#define ENC28J60_PARAM_SPI SPI_DEV(0) +#define ENC28J60_PARAM_CS GPIO9 +#define ENC28J60_PARAM_RESET GPIO10 +#define ENC28J60_PARAM_INT GPIO34 +#endif /* SPI_RAM_USED */ +#endif /* defined(MODULE_ENC28J60) && !defined(ENC28J60_PARAM_SPI) */ +/** @} */ + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +/* include common board definitions as last step */ +#include "board_common.h" + +#endif /* BOARD_H */ +/** @} */ diff --git a/cpu/esp32/Makefile b/cpu/esp32/Makefile new file mode 100644 index 0000000000000..bb0ae68634809 --- /dev/null +++ b/cpu/esp32/Makefile @@ -0,0 +1,13 @@ +# Define the module that is built: +MODULE = cpu + +EXCLUDE += esp-idf +EXCLUDE += periph +EXCLUDE += xtensa + +# Add a list of subdirectories, that should also be built: +DIRS += esp-idf +DIRS += periph +DIRS += xtensa + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/esp32/Makefile.features b/cpu/esp32/Makefile.features new file mode 100644 index 0000000000000..544e81e10cb0c --- /dev/null +++ b/cpu/esp32/Makefile.features @@ -0,0 +1,11 @@ +FEATURES_PROVIDED += periph_adc +FEATURES_PROVIDED += periph_cpuid +FEATURES_PROVIDED += periph_dac +FEATURES_PROVIDED += periph_gpio +FEATURES_PROVIDED += periph_hwrng +FEATURES_PROVIDED += periph_i2c +FEATURES_PROVIDED += periph_pm +FEATURES_PROVIDED += periph_pwm +FEATURES_PROVIDED += periph_spi +FEATURES_PROVIDED += periph_timer +FEATURES_PROVIDED += periph_uart diff --git a/cpu/esp32/Makefile.include b/cpu/esp32/Makefile.include new file mode 100644 index 0000000000000..a42f30648f9dc --- /dev/null +++ b/cpu/esp32/Makefile.include @@ -0,0 +1,261 @@ +# check some environment variables first +ifndef SDK_DIR + $(info SDK_DIR should be defined as /path/to/esp-idf directory) + $(info SDK_DIR is set by default to /opt/esp/esp-idf) + export SDK_DIR=/opt/esp/esp-idf +endif + +# DEFAULT compile configuration + +# using ESP-IDF newlib is standard since it does not require pthread +export USE_ESP_IDF_NEWLIB ?= 1 + +# not using ESP-IDF heap is standard +export USE_ESP_IDF_HEAP ?= 0 + +# disable I2C software implemenation and use I2C hardware implementation +export ENABLE_SW_I2C ?= 0 + +# disable hardware counter module and use hardware timer module +export ENABLE_HW_COUNTER ?=0 + +# disable SPI RAM by default +export ENABLE_SPI_RAM ?= 0 + +# we use flash mode dout by default to keep GPIO 9 and 10 free for use +export FLASH_MODE ?= dout + +# SPECIAL compile configuration dependent on used make variables + +# if WiFi is enabled, we cannot use ESP-IDF newlib, overwrite settings +ifeq ($(ENABLE_WIFI), 1) + export USE_ESP_IDF_NEWLIB = 0 + export USE_ESP_IDF_HEAP = 1 + CFLAGS += -DWIFI_USED +endif + +ifeq ($(USE_ESP_IDF_NEWLIB), 1) + # when ESP-IDF newlib is used, also ESP-IDF heap has to be used + export USE_ESP_IDF_HEAP = 1 + export NEWLIB_DIR=$(SDK_DIR)/components/newlib + INCLUDES += -I$(RIOTBASE)/sys/posix/pthread/include + INCLUDES += -I$(RIOTCPU)/$(CPU)/esp-idf/include/newlib + INCLUDES += -I$(NEWLIB_DIR)/include +else + USEMODULE += pthread +endif + +ifeq ($(USE_ESP_IDF_HEAP), 1) + CFLAGS += -DESP_IDF_HEAP_USED + USEMODULE += esp_idf_heap +endif + +# if SPI RAM is enabled, ESP-IDF heap and quot flash mode has to be used +ifeq ($(ENABLE_SPI_RAM), 1) + export USE_ESP_IDF_HEAP= 1 + export FLASH_MODE = qout + CFLAGS += -DFLASH_MODE_QOUT -DSPI_RAM_USED +else + ifeq ($(FLASH_MODE), qio) + CFLAGS += -DFLASH_MODE_QIO + endif + ifeq ($(FLASH_MODE), qout) + CFLAGS += -DFLASH_MODE_QOUT + endif + ifeq ($(FLASH_MODE), dio) + CFLAGS += -DFLASH_MODE_DIO + endif + ifeq ($(FLASH_MODE), qout) + CFLAGS += -DFLASH_MODE_DOUT + endif +endif + +export CPU ?= esp32 +export TARGET_ARCH ?= xtensa-esp32-elf +export ESPTOOL ?= $(SDK_DIR)/components/esptool_py/esptool/esptool.py + +INCLUDES += -I$(APPDIR) +INCLUDES += -I$(RIOTCPU)/$(CPU)/esp-idf/include +INCLUDES += -I$(RIOTCPU)/$(CPU)/esp-idf/include/esp32 +INCLUDES += -I$(RIOTCPU)/$(CPU)/esp-idf/include/heap +INCLUDES += -I$(RIOTCPU)/$(CPU)/esp-idf/include/newlib +INCLUDES += -I$(RIOTCPU)/$(CPU)/esp-idf/include/spi_flash +INCLUDES += -I$(SDK_DIR)/components/ +INCLUDES += -I$(SDK_DIR)/components/driver/include +INCLUDES += -I$(SDK_DIR)/components/esp32/include +INCLUDES += -I$(SDK_DIR)/components/heap/include +INCLUDES += -I$(SDK_DIR)/components/soc/esp32/include +INCLUDES += -I$(SDK_DIR)/components/soc/include +INCLUDES += -I$(RIOTBOARD)/common/$(CPU)/include +INCLUDES += -I$(RIOTCPU)/$(CPU) + +CFLAGS += -DSDK_NOT_USED -DCONFIG_FREERTOS_UNICORE=1 -DESP_PLATFORM +CFLAGS += -DRIOT_OS # -DFLASH_MODE=$(FLASH_MODE) +CFLAGS += -Wno-unused-parameter -Wformat=0 +CFLAGS += -mlongcalls -mtext-section-literals -fstrict-volatile-bitfields +ASFLAGS += --longcalls --text-section-literals + +ifneq ($(BOARD_APP_CONF),) + CFLAGS += -DBOARD_APP_CONF=\"$(APPDIR)/$(BOARD_APP_CONF)\" +endif + +ifneq ($(CONFIGS),) + CFLAGS += $(CONFIGS) +endif + +NETIF_NUMOF=0 + +CFLAGS_OPT = -fzero-initialized-in-bss -O2 + +ifeq ($(ENABLE_GDBSTUB), 1) + CFLAGS_OPT = -fzero-initialized-in-bss -Og -ggdb + CFLAGS += -DENABLE_GDBSTUB +endif + +ifeq ($(ENABLE_GDB), 1) + CFLAGS_OPT = -fzero-initialized-in-bss -Og -ggdb +endif + +CFLAGS += $(CFLAGS_OPT) + +ifeq ($(ENABLE_HW_COUNTER), 1) + CFLAGS += -DHW_COUNTER_USED +endif + +ifeq ($(ENABLE_SW_I2C), 1) + CFLAGS += -DI2C_SW_USED +else + CFLAGS += -DI2C_HW_USED +endif + +ifeq ($(ENABLE_SPIFFS), 1) + export SPIFFS_STD_OPTION = -std=c99 + USEMODULE += spiffs + USEMODULE += vfs +endif + +ifeq ($(ENABLE_MRF24J40), 1) + $(eval NETIF_NUMOF=$(shell echo $$(($(NETIF_NUMOF)+1)))) + USEMODULE += mrf24j40 +endif + +ifeq ($(ENABLE_ENC28J60), 1) + $(eval NETIF_NUMOF=$(shell echo $$(($(NETIF_NUMOF)+1)))) + USEMODULE += enc28j60 +endif + +ifdef DEFAULT_NETIF + CFLAGS += -DGNRC_RPL_DEFAULT_NETIF=$(DEFAULT_NETIF) +endif + +CFLAGS += -DGNRC_NETIF_NUMOF=$(NETIF_NUMOF) + +ifeq ($(QEMU), 1) + CFLAGS += -DQEMU +endif + +# LINKFLAGS += -Wl,--verbose + +ifeq ($(USE_ESP_IDF_NEWLIB), 1) + LINKFLAGS += -L$(NEWLIB_DIR)/lib +endif + +LINKFLAGS += -L$(SDK_DIR)/components/esp32 +LINKFLAGS += -L$(SDK_DIR)/components/esp32/lib +LINKFLAGS += -Wl,--start-group + +ifeq ($(ENABLE_WIFI), 1) + LINKFLAGS += $(BINDIR)/esp_idf_nvs_flash.a + LINKFLAGS += $(BINDIR)/esp_idf_esp32.a + LINKFLAGS += $(BINDIR)/esp_idf_spi_flash.a + LINKFLAGS += -lcore -lrtc -lnet80211 -lpp -lwpa -lsmartconfig -lcoexist + LINKFLAGS += -lwps -lwpa2 -lespnow -lphy -lmesh -lstdc++ +endif + +ifneq (,$(filter pthread,$(USEMODULE))) + LINKFLAGS += $(BINDIR)/core.a + LINKFLAGS += $(BINDIR)/pthread.a +endif + +LINKFLAGS += -lhal -lc -lg +LINKFLAGS += -Wl,--end-group +LINKFLAGS += -L$(RIOTCPU)/$(CPU)/ld/ +LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp32.ld +LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp32.common.ld +LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp32.peripherals.ld +LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp32.rom.ld +LINKFLAGS += -T$(RIOTCPU)/$(CPU)/ld/esp32.rom.nanofmt.ld +LINKFLAGS += -nostdlib -lgcc -u putchar -Wl,-gc-sections + +USEMODULE += auto_init +USEMODULE += core_thread_flags +USEMODULE += esp_idf +USEMODULE += esp_idf_driver +USEMODULE += esp_idf_esp32 +USEMODULE += esp_idf_soc +USEMODULE += esp_idf_spi_flash +USEMODULE += mtd +USEMODULE += periph +USEMODULE += periph_common +USEMODULE += ps +USEMODULE += random +USEMODULE += xtensa + +ifeq ($(ENABLE_WIFI), 1) + USEMODULE += esp_idf_nvs_flash +endif + +ifneq (,$(filter shell,$(USEMODULE))) + USEMODULE += newlib_syscalls_default + USEMODULE += xtimer +endif + +ifneq (,$(filter xtimer,$(USEMODULE))) + USEMODULE += newlib_syscalls_default +endif + +ifneq (,$(findstring posix,$(USEMODULE))) + USEMODULE += newlib_syscalls_default +endif + +ifneq (,$(filter newlib_syscalls_default,$(USEMODULE))) + USEMODULE += uart_stdio +endif + +# configure preflasher to convert .elf to .bin before flashing +FLASH_MODE ?= dout # FIX configuration, DO NOT CHANGE +FLASH_FREQ = 40m # FIX configuration, DO NOT CHANGE +FLASH_SIZE ?= 16m +export PREFLASHER = $(ESPTOOL) +export PREFFLAGS = --chip esp32 elf2image +export PREFFLAGS += -fm $(FLASH_MODE) -fs $(FLASH_SIZE) -ff $(FLASH_FREQ) +export PREFFLAGS += -o $(ELFFILE).bin $(ELFFILE); +export PREFFLAGS += echo -n "factory, app, factory, 0x10000, " > $(BINDIR)/partitions.csv; +export PREFFLAGS += ls -l $(ELFFILE).bin | awk '{ print $$5 }' >> $(BINDIR)/partitions.csv; +export PREFFLAGS += python $(RIOTCPU)/$(CPU)/gen_esp32part.py +export PREFFLAGS += --verify $(BINDIR)/partitions.csv $(BINDIR)/partitions.bin +export FLASHDEPS = preflash + +# flasher configuration +ifeq ($(QEMU), 1) + export FLASHER = dd + export FFLAGS += if=/dev/zero bs=1M count=4 | tr "\\000" "\\377" > tmp.bin && cat tmp.bin | + export FFLAGS += head -c $$((0x1000)) | + export FFLAGS += cat - $(RIOTCPU)/$(CPU)/bin/bootloader.bin tmp.bin | + export FFLAGS += head -c $$((0x8000)) | + export FFLAGS += cat - $(BINDIR)/partitions.bin tmp.bin | + export FFLAGS += head -c $$((0x10000)) | + export FFLAGS += cat - $(ELFFILE).bin tmp.bin | + export FFLAGS += head -c $$((0x400000)) > $(BINDIR)/esp32flash.bin && rm tmp.bin && + export FFLAGS += cp $(RIOTCPU)/$(CPU)/bin/rom_0x3ff90000_0x00010000.bin $(BINDIR)/rom1.bin && + export FFLAGS += cp $(RIOTCPU)/$(CPU)/bin/rom_0x40000000_0x000c2000.bin $(BINDIR)/rom.bin +else + export PROGRAMMER_SPEED ?= 460800 + export FLASHER = $(ESPTOOL) + export FFLAGS += --chip esp32 -p $(PORT) -b $(PROGRAMMER_SPEED) + export FFLAGS += --before default_reset --after hard_reset write_flash + export FFLAGS += -z -fm $(FLASH_MODE) -fs detect -ff $(FLASH_FREQ) + export FFLAGS += 0x1000 $(RIOTCPU)/$(CPU)/bin/bootloader.bin + export FFLAGS += 0x8000 $(BINDIR)/partitions.bin + export FFLAGS += 0x10000 $(ELFFILE).bin +endif diff --git a/cpu/esp32/README.md b/cpu/esp32/README.md new file mode 100644 index 0000000000000..24cc4ec6dfa7e --- /dev/null +++ b/cpu/esp32/README.md @@ -0,0 +1,346 @@ +## Introduction + +**RIOT-Xtensa-ESP** is a bare metal implementation of **RIOT-OS** for **ESP32** SOCs which supports most features of RIOT-OS. The peripheral SPI and I2C interfaces allow to connect all external hardware modules supported by RIOT-OS, such as sensors and actuators. SPI interface can also be used to connect external IEEE802.15.4 modules to integrate ESP32 boards into a GNRC network. + +Although the port does not use the official ESP-IDF (Espresso IoT Development Framework) SDK, it must be installed for compilation. The reason is that the port uses most of the ESP32 SOC definitions provided by the ESP-IDF header files. In addition, it needs the hardware abstraction library (libhal), which is part of the ESP-IDF SDK. + +## Limitations + +- The implementation uses **only one core** (the PRO CPU) because RIOT does not support running multiple threads simultaneously. + +- **WLAN and Bluetooth** cannot be used at the moment. (Maybe it will never be possible.) The reason is that these modules are controlled by **proprietary binary libraries** from Espressif which are not documented and require the ESP-IDF, the official SDK from Espressif. ESP-IDF itself is a multicore FreeRTOS port, which of course can not be used together with the RIOT-OS. Furthermore, Espressif's proprietary libraries use FreeRTOS functionality themselves. That is, to get the Wi-Fi or Bluetooth module up and running, many ESP-IDF SDK functions and a FreeRTOS adoption level would have to be (re)implemented. + +- **Flash encryption** is not yet supported. + +## Known Problems + +If a task terminates with return statement, spilling registers in the windowed register ABI may result in illegal retw instructions during next context switch. As long as all tasks are executed as infinite loops and don't terminate, there are no problems. + +## Toolchain + +Following software components are required: + +- Xtensa GCC compiler suite for ESP32 +- ESP-IDF SDK which includes all ESP32 SOC definitions, the hardware abstraction library ```libhal.a```, and the flash programmer tool ```esptool.py``` + +### Installation of Xtensa GCC compiler suite + +Xtensa GCC compiler for ESP32 can be downloaded and installed as precompiled binary archive from Espressif's web page. + +``` +mkdir -p $HOME/esp +cd $HOME/esp +wget https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz +tar -xzf xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz +rm xtensa-esp32-elf-linux64-1.22.0-80-g6c4433a-5.2.0.tar.gz +``` +Once the compiler is installed you can add the binary directory to your ```PATH``` variable. +``` +export PATH=$PATH:$HOME/esp/xtensa-esp32-elf/bin +``` + +### Installation of ESP-IDF (Espressif IoT Development Framework) + +ESP-IDF, the official SDK from Espressif, can be dowloaded and installed as GIT repository. + +``` +cd $HOME/esp +git clone --recursive https://github.com/espressif/esp-idf.git +cd esp-idf +git checkout release/v3.0 +``` +Please take care to checkout branch ```release/v3.0``` which was used for the port. Newer versions might also work but were not tested. + +Since we only use a few header files, ESP-IDF does not need to be compiled in any way. To use the installed ESP-IDF, just set the variable ```SDK_DIR``` accordingly. +``` +export SDK_DIR=$HOME/esp/esp-idf +``` + +### Usage + +To compile an application for an ESP32 board, change to RIOT's root directory and execute the make command, e.g., +``` +make flash BOARD=esp32-generic -C tests/shell [Compile Options] +``` +where the ```BOARD``` variable specifies the generic ESP32 board definition and option ```-C``` the directory of application. + +#### Compile Options + +The make process can be configured by a number of command-line variables for the make command, e.g., ```ENABLE_SPIFFS=1``` to activate a SPIFFS drive in built-in flash memory. + +Following table shows all possible command-line options in alphabetical order. + +Option | Values | Default | Meaning +:------|:-------|:--------|:------- +BOARD_APP_CONF | file name | empty | Specify an application specific board configuration file located in application source directory, see section _Application Specific Board Configuration_. +CONFIGS | string | empty | Specify an application specific board configuration file located in application source directory, see section _Application Specific Board Configuration_. +ENABLE_GDB | 0, 1 | 0 | Enable the compilation with debug information for debugging with QEMU (```QEMU=1```), see section _QEMU Mode and GDB_ below. +ENABLE_HW_COUNTER | 0, 1 | 0 | Enable the timer implementation that uses counters instead of hardware timer modules, see section _Timer Implementations_ below. +ENABLE_MRF24J40 | 0, 1 | 0 | Enable MRF24J40 module to use IEEE 802.15.4 radio modules based on MRF 24J40. +ENABLE_SPIFFS | 0, 1 | 0 | Enable SPIFFS file system, see section _SPIFFS Drive_ below. +ENABLE_SPI_RAM | 0, 1 | 0 | Enable SPI RAM, see section _SPI RAM_ below. +ENABLE_SW_I2C | 0, 1 | 0 | Enable the I2C software implementation, see section _I2C Implementations_ below. +PORT | /dev/ttyUSBx | /dev/ttyUSB0 | Set the port for flashing the firmware. +QEMU | 0, 1 | 0 | Use QEMU mode and generate an image for QEMU, see _QEMU Mode and GDB_ below. +USE_ESP_IDF_NEWLIB | 0, 1 | 1 | Use the ```newlib``` C library from ESP-IDF (```USE_ESP_IDF_NEWLIB=1```), see below. +USE_ESP_IDF_HEAP | 0, 1 | 0 | Use the complex heap implementation from ESP-IDF (```USE_ESP_IDF_HEAP=1```), see below. + +## Application Specific Board Configuration + +The board configuration in ```board.h``` usually defines a default configuration which GPIOs are used for which purpose. However, because GPIOs can be used for different purposes, it is often necessary to change some of the default configurations for individual applications. For example, while many PWM channels are needed in one application, another application does not need PWM channels, but many ADC channels. Board configurations in ```board.h``` are usually only a compromise between these different requirements. There are two ways to give the application the ability to change some of the predefined configurations: + +- ```CONFIGS``` make variable +- application specific board configuration file + +Using the ```CONFIGS``` make variable at command line, single board definitions can be overridden, e.g., the flag which indicates a LCD display is connected that is normally not set: +``` +CONFIGS='-DESP_LCD_PLUGGED_IN=1' +``` + +When a number of board definitions have to be overridden, this approach is impractical. In that case, an application specific board configuration file located in application source directory can be used. For example, if a file ```esp32_wrover_kit_conf.h``` in application source directory would define some board configuration for the ESP32 WROVER Kit board, it could specified as ```BOARD_APP_CONF``` make variable at command line: +``` +BOARD_APP_CONF=esp32_wrover_kit_conf.h +``` + +## ESP-IDF newlib and heap implementation + +ESP-IDF provides two components that can be used with RIOT: + +- ```newlib``` C library +- complex heap implementation + +While the version of the ```newlib``` C library shipped with the precompiled Xtensa GCC compiler suite requires a POSIX thread library pthreads, the ```newlib``` version from the ESP IDF SDK does not need it. By default, the ```newlib``` version from the ESP-IDF SDK is used ```(USE_ESP_IDF_NEWLIB=1)```. To use the ```newlib``` C library from the Xtensa GCC compiler suite, just define ```USE_ESP_IDF_NEWLIB=0``` as make variable at the command line. + +The ESP-IDF SDK provides a complex heap implementation that supports multiple heap segments in different memory areas such as DRAM, IRAM, and PSRAM. Whenever you want to use these memory areas as heap, you have to use the heap implementation from the ESP-IDF SDK (```USE_ESP_IDF_HEAP=1```). + +**Please note:** If the ```newlib``` version from ESP-IDF SDK is used (```USE_ESP_IDF_NEWLIB=1```), also the heap implementation from ESP-IDF SDK has to be used. In that case it is enabled by default (```USE_ESP_IDF_HEAP=1```). Otherwise, it is disabled by default (```USE_ESP_IDF_HEAP=0```) and RIOT's simple heap implementation is used which support only one heap segment in DRAM. + +## SPIFFS Drive + +If SPIFFS module is enabled (```ENABLE_SPIFFS=1```), the implemented MTD device ```mtd0``` is uses the built-in SPI flash memory together with the modules SPIFFS and VFS to realize a persistent file system. + +For this purpose, flash memory is formatted as SPIFFS the first time the system is started. The start address is determined from the created flash partition table and starts at a multiple of 0x10000 (64 kByte) after the application partition. + +Please refer file ```$(RIOTBASE)/tests/unittests/test-spiffs/tests-spiffs.c``` for more information on how to use SPIFFS and VFS together with a MTD device ```mtd0``` alias ```MTD_0```. + +## SPI RAM modules + +Some ESP32 modules, e.g., the WROVER module, integrate external RAM that is connected via the FSPI interface. To activate and use this external RAM, set the make variable ```ENABLE_SPI_RAM=1``` at command line. By default, SPI RAM is not activated and cannot be used. + +**Please note**: +ESP32 uses four data lines to access the external SPI RAM, called quad output flash mode (QOUT). Therefore, GPIOs 9 and 10 are used as SPI data lines and are not available for other purposes. + +**Please note** +Enabling SPI RAM for modules that don't have SPI RAM may lead to boot problems for some modules. For others is simply throws an error message. + +## I2C Implementations + +The ESP32 has two built-in I2C hardware interfaces that support I2C bus speed up to 400 kbps (```I2C_SPEED_FAST```). The default pin configuration of I2C interfaces can be changed by the application, see section _Application Specific Board Configuration_. + +Device |Signal|Pin |Symbol |Remarks +:---------------|:-----|:-------|:--------------|:---------------- +```I2C_DEV(0)```| SCL | GPIO22 |```I2C0_SCL``` | cannot be changed +```I2C_DEV(0)```| SDA | GPIO21 |```I2C0_SDA``` | cannot be changed +```I2C_DEV(1)```| SCL | - |```I2C1_SCL``` | not declared +```I2C_DEV(1)```| SDA | - |```I2C1_SDA``` | not declared + +If the according I2C interfaces are not to be used, ```I2C0_NOT_AVAILABLE``` and/or ```I2C1_NOT_AVAILABLE``` have to be declared instead. Most boards definitions do not declare I2C_DEV(1). + +Beside the I2C hardware implementation, a I2C bit-banging protocol software implementation can be used. This implementation allow I2C bus speeds up to 1 Mbps (```I2C_SPEED_FAST_PLUS```). It can be activated by defining ```ENABLE_SW_I2C=1``` as make variable at the command line. However, since it uses busy waiting, you should always prefer the hardware implementation when possible which is used by default. + +## SPI Interfaces + +ESP32 integrates four SPI controllers: + +- controller SPI0 is reserved for accessing flash memory +- controller SPI1 realizes interface **``````FSPI``````** which shares its signals with SPI0 +- controller SPI2 realizes interface **``````HSPI``````** that can be used for peripherals +- controller SPI3 realizes interface **``````VSPI``````** that can be used for peripherals + +Since SPI0 controller is used to access flash and other external memories, at most three interfaces can be used: + +- **``````VSPI``````**: realized by controller SPI3 and mapped to ```SPI_DEV(0)``` +- **``````HSPI``````**: realized by controller SPI2 and mapped to ```SPI_DEV(1)``` +- **``````FSPI``````**: realized by controller SPI1 and mapped to ```SPI_DEV(2)```. + +In order to make it clear to the interface SPI_DEV (2) that we are talking about the interface that shares its bus signals with the controller for access to the flash memory, we use the identifier ```FSPI``` here. In the technical reference, this interface is misleadingly called simply SPI. + +**Please note**: +Since the ```FSPI``` interface ```SPI_DEV(2)``` shares its bus signals with flash memory interface and other external memories, you can only use ```SPI_DEV(2)``` to attach external memory with same SPI mode and same bus speed but with a different CS. Using ```SPI_DEV(2)``` for anything else can disturb flash memory access which causes a number of problems. If not really necessary, you should not use this interface. + +Even though the following default pin configuration of SPI interfaces is used on most boards, it can vary from board to board. The pin configuration of ```VSPI``` and ```HSPI``` interface can be changed by the application, see section _Application Specific Board Configuration_. + +Interface |Device |Signal|Pin |Symbol |Remarks +:---------|:----------------|:-----|:-------|:--------------|:---------------- +```VSPI```|```SPI_DEV(0)``` | SCK | GPIO18 |```SPI0_SCK``` | optional, can be overridden +```VSPI```|```SPI_DEV(0)``` | MISO | GPIO19 |```SPI0_MISO```| optional, can be overridden +```VSPI```|```SPI_DEV(0)``` | MOSI | GPIO23 |```SPI0_MOSI```| optional, can be overridden +```VSPI```|```SPI_DEV(0)``` | CS0 | GPIO18 |```SPI0_CS0``` | optional, can be overridden +```HSPI```|```SPI_DEV(1)``` | SCK | GPIO14 |```SPI0_SCK``` | optional, can be overridden +```HSPI```|```SPI_DEV(1)``` | MISO | GPIO12 |```SPI0_MISO```| optional, can be overridden +```HSPI```|```SPI_DEV(1)``` | MOSI | GPIO13 |```SPI0_MOSI```| optional, can be overridden +```HSPI```|```SPI_DEV(1)``` | CS0 | GPIO15 |```SPI0_CS0``` | optional, can be overridden +```FSPI```|```SPI_DEV(2)``` | SCK | GPIO14 |```SPI0_SCK``` | mandatory, cannot be overridden +```FSPI```|```SPI_DEV(2)``` | MISO | GPIO12 |```SPI0_MISO```| mandatory, cannot be overridden +```FSPI```|```SPI_DEV(2)``` | MOSI | GPIO13 |```SPI0_MOSI```| mandatory, cannot be overridden +```FSPI```|```SPI_DEV(2)``` | CS0 | GPIO15 |```SPI0_CS0``` | mandatory, cannot be overridden + +## UART Interfaces + +ESP32 supports up to three UART devices UART_DEV(0) pin configuration is fix. All ESP32 boards use it as standard configuration. The pin configuration of ```UART_DEV(1)``` and ```UART_DEV(2)``` can be changed. + +The following default pin configuration of UART interfaces can be overridden by the application, see section _Application Specific Board Configuration_. + +Device |Signal|Pin |Symbol |Remarks +:-----------------|:-----|:-------|:--------------|:---------------- +```UART_DEV(0)``` | TxD | GPIO1 |```UART0_TXD```| cannot be changed +```UART_DEV(0)``` | RxD | GPIO3 |```UART0_RXD```| cannot be changed +```UART_DEV(1)``` | TxD | GPIO10 |```UART1_TXD```| optional, can be overridden +```UART_DEV(1)``` | RxD | GPIO9 |```UART1_RXD```| optional, can be overridden +```UART_DEV(2)``` | TxD | GPIO17 |```UART2_TXD```| optional, can be overridden +```UART_DEV(2)``` | RxD | GPIO16 |```UART2_RXD```| optional, can be overridden + +If the according UART interfaces are not to be used, ```UART1_NOT_AVAILABLE``` and/or ```UART2_NOT_AVAILABLE``` have to be declared instead. Most boards definitions do not declare UART_DEV(1) and UART_DEV(2). + +## PWM Channels + +ESP supports two types of PWM generators + +- one LED PWM controller (LEDPWM) with 16 channels, and +- two high-speed Motor Control PWM controllers (MCPWM) with 6 channels each. + +The PWM implementation uses the ESP32's high-speed MCPWM modules. Reason is that the LED PWM controller only supports resolutions of powers of two. + +Using both Motor Control PWM controllers with 6 channels each, the maximum number of PWM +devices is 2 and the maximum total number of PWM channels is 12. + +Which PWM device can drive which GPIOs is declared by the defines ```PWM0_GPIOS``` for ```PWM_DEV(0)``` and ```PWM1_GPIOS``` for ```PWM_DEV(1)```, respectively. + +**Please note:** +As long as the respective PWM device is not initialized with the ```pwm_init``` function, these GPIOs can be used for other purposes. That is, it would be theoretically possible to declare all 12 PWM channels without using them. + +The definition of ```PWM0_GPIOS``` and ```PWM1_GPIOS``` can be omitted or empty. In the latter case, they must at least contain the curly braces. The corresponding PWM device can not be used in this case. + +Furthermore, ```PWM0_GPIOS_NOT_AVAILABLE``` and ```PWM1_GPIOS_NOT_AVAILABLE``` can be declared to indicate the the according device is not used. + +Board definitions usually declare a number of GPIOs as PWM channels. This configuration can be changed by the application, see section _Application Specific Board Configuration_. + +## ADC Channels + +ESP32 integrates two 12-bit ADCs (ADC1 and ADC2) capable of measuring up to 18 analog signals in total. Most of these ADC channels are either connected to a number of intergrated sensors like a Hall sensors, touch sensors and a temperature sensor or can be connected with certain GPIOs. Integrated sensors are disabled in RIOT's implementation and are not accessible. Thus, up to 18 GPIOs, can be used as ADC inputs: + +- ADC1 supports 8 channels: GPIOs 32-39 +- ADC2 supports 10 channels: GPIOs 0, 2, 4, 12-15, 25-27 + +These GPIOs are realized by the RTC unit and are therefore also called RTC GPIOs or RTCIO GPIOs. + +**Please note**: GPIO37 and GPIO38 are normally not broken out on ESP32 modules and are therefore not usable. + +**```ADC_GPIOS```** declares which GPIOs can be used as ADC channels by an application for a certain board. The order of the listed GPIOs determines the mapping between the RIOT's ADC lines and the GPIOs. The definition of ```ADC_NUMOF``` is derived automatically from ```ADC_GPIOS``` and must not be changed. + +Board definitions usually declare a number of possible GPIOs as ADC channels. This configuration can be changed by the application, see section _Application Specific Board Configuration_. +```ADC_GPIOS``` is usually defined in ```board.h```. + +**Please note:** +Declaration of ```ADC_GPIOS``` must not be empty. If no ADC pins are to be used, just omit the ```ADC_GPIOS``` declaration and declare ```ADC_GPIOS_NOT_AVAILABLE``` instead. + +**Please note:** +As long as the GPIOs listed in ```ADC_GPIOS``` are not initialized as ADC channels with the ```adc_init``` function, they can be used for all other uses of GPIOs. + +## DAC Channels + +ESP32 supports 2 DAC lines at GPIO25 and GPIO26. These DACs have a width of 8 bits and produce voltages in the range from 0 V to 3.3 V (VDD_A). The 16 bits DAC values given as parameter of function *dac_set* are down-scaled to 8 bit. + +**```DAC_GPIOS```** declares which GPIOs can be used as DAC channels by an application for a certain board. The order of the listed GPIOs determines the mapping between the RIOT's DAC lines and the GPIOs. The definition of ```DAC_NUMOF``` is derived automatically from ```DAC_GPIOS``` and must not be changed. + +Board definitions usually declare one or two GPIOs as DAC channels. This configuration can be overridden by the application, see section _Application Specific Board Configuration_. +```DAC_GPIOS``` is usually defined in ```board.h```. + +**Please note:** +Declaration of ```DAC_GPIOS``` must not be empty. If no DAC pins are to be used, just omit the ```DAC_GPIOS``` declaration and declare ```DAC_GPIOS_NOT_AVAILABLE``` instead. + +**Please note:** +As long as the GPIOs listed in ```DAC_GPIOS``` are not initialized as DAC channels with the ```dac_init``` function, they can be used for all other uses of GPIOs. + +## Timer Implementations + +There are two different implementations for hardware timers. + +- timer module implementation +- counter implementation + +The hardware timer module provides 4 high-speed timers, where 1 timer is used for system time. The remaining 3 timers with one channel each can be used as timer devices with a clock rate of 1 MHz. + +The hardware counter implementation uses CCOUNT/CCOMPARE registers to implement 2 timers devices with one channel each and a clock rate of 1 MHz. + +By default, hardware timer module is used. To use the hardware counter implementation define ```ENABLE_HW_COUNTER=1``` for the make command. + +## Flash Modes + +Principally, **ESP32 modules** can be flashed with ```qio```, ```qout```, ```dio``` and ```dout```. Although using ```qio``` or ```qout``` increases the performance of SPI flash data transfers, these modes require the two additional GPIO's 9 and 10. That is, in these flash modes these GPIO's are not available. Therefore, the default flash mode is ```dout``` and cannot be changed. + +For more information about flash mode, refer the documentation of [esptool.py](https://github.com/espressif/esptool/wiki/SPI-Flash-Modes). + +## JTAG Debugging + +For debugging, ESP32 provides a JTAG interface at GPIOs 12 ... 15. + +ESP32 Pin | ESP32 signal name JTAG Signal +:-------------|:----------- +CHIP_PU | TRST_N +GPIO15 (MTDO) | TDO +GPIO12 (MTDI) | TDI +GPIO13 (MTCK) | TCK +GPIO14 (MTMS) | TMS +GND | GND + +This JTAG interface can be used together with OpenOCD and GDB to debug your software on instruction level. When you compile your software with debugging information (```ENABLE_GDB=1```) you can also debug on source code level as well. + +**Please note:** +When debugging, the GPIOs used for the JTAG interface must not be used for anything else. + +Detailed information on how to configure the JTAG interface of the ESP32 and to setup of OpenOCD and GDB can be found in section JTAG Debugging in the [ESP-IDF Programming Guide](https://esp-idf.readthedocs.io/en/latest/api-guides/jtag-debugging/index.html). + +## QEMU Mode and GDB + +When you execute command ```make flash``` with QEMU mode enabled (```QEMU=1```), instead of loading the image to the target hardware, a binary image called ```esp32flash.bin``` is created in the target directory. Furthermore, two ROM binary files ```rom.bin``` and ```rom1.bin``` are copied to the target directory. This files file can then be used together with QEMU to debug the code in GDB. + +The binary image can be compiled with debugging information (```ENABLE_GDB=1```) or optimized without debugging information (```ENABLE_GDB=0```). The latter one is the default. The version with debugging information can be debugged in source code while the optimized version can only be debugged in assembler mode. + +To use QEMU, you have to install QEMU for Xtensa with ESP32 machine implementation as following. + +``` +cd $HOME/src +git clone git://github.com/Ebiroll/qemu_esp32 +cp qemu_esp32/bin/xtensa-esp32-elf-gdb $HOME/esp/xtensa-esp32-elf/bin/xtensa-esp32-elf-gdb.qemu +rm -rf qemu_esp32 + +git clone git://github.com/Ebiroll/qemu-xtensa-esp32 +cd qemu-xtensa-esp32 +./configure --disable-werror --prefix=$HOME/esp/qemu-esp32 --target-list=xtensa-softmmu,xtensaeb-softmmu +make install +cd ..; rm -rf qemu-xtensa-esp32 # optional +``` + +Once the compilation has been finished, QEMU for Xtensa with ESP32 machine implementation should be available in ```$HOME/esp/qemu-esp32``` and you can change to your application target directory to start it in one terminal window , e.g., + +``` +cd $HOME/src/RIOT-Xtensa-ESP/tests/shell/bin/esp32-generic +$HOME/esp/qemu-esp32/bin/qemu-system-xtensa -d guest_errors,unimp -cpu esp32 -M esp32 -m 4M -S -s > io.txt +``` +where ```$HOME/src/RIOT-Xtensa-ESP``` is the root directory of RIOT and ```tests/shell``` is the application. + +**Please note:** +QEMU starts always the files ```esp32flash.bin```, ```rom.bin``` and ```rom1.bin``` in local directory. Therefore, please take care to ensure that you are in right target directory before you start QEMU. + +In second terminal window, you can then start GDB and connect to the emulation for the example. +``` +xtensa-esp32-elf-gdb.qemu $HOME/src/RIOT-Xtensa-ESP/tests/shell/bin/esp32-generic/tests_shell.elf +``` +To start debugging, you have to connect to QEMU with command: +``` +(gdb) target remote :1234 +``` + +**Please note**: +QEMU for Xtensa ESP32 does not support interrupts. That is, once your application uses interrupts, e.g., timers, the application cannot be debugged using QEMU together with GDB.A diff --git a/cpu/esp32/bin/blank.bin b/cpu/esp32/bin/blank.bin new file mode 100644 index 0000000000000..7de9e36a64119 --- /dev/null +++ b/cpu/esp32/bin/blank.bin @@ -0,0 +1 @@ +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/cpu/esp32/bin/bootloader.bin b/cpu/esp32/bin/bootloader.bin new file mode 100644 index 0000000000000..5613094bf682b Binary files /dev/null and b/cpu/esp32/bin/bootloader.bin differ diff --git a/cpu/esp32/bin/bootloader.elf b/cpu/esp32/bin/bootloader.elf new file mode 100755 index 0000000000000..7edfd36320b59 Binary files /dev/null and b/cpu/esp32/bin/bootloader.elf differ diff --git a/cpu/esp32/bin/null.bin b/cpu/esp32/bin/null.bin new file mode 100644 index 0000000000000..08e7df176454f Binary files /dev/null and b/cpu/esp32/bin/null.bin differ diff --git a/cpu/esp32/bin/rom_0x3ff90000_0x00010000.bin b/cpu/esp32/bin/rom_0x3ff90000_0x00010000.bin new file mode 100644 index 0000000000000..283d613208779 Binary files /dev/null and b/cpu/esp32/bin/rom_0x3ff90000_0x00010000.bin differ diff --git a/cpu/esp32/bin/rom_0x40000000_0x000c2000.bin b/cpu/esp32/bin/rom_0x40000000_0x000c2000.bin new file mode 100644 index 0000000000000..e105055160f92 Binary files /dev/null and b/cpu/esp32/bin/rom_0x40000000_0x000c2000.bin differ diff --git a/cpu/esp32/doc.txt b/cpu/esp32/doc.txt new file mode 100644 index 0000000000000..525c897e926ce --- /dev/null +++ b/cpu/esp32/doc.txt @@ -0,0 +1,27 @@ +/** +@defgroup cpu_esp32 ESP32 +@ingroup cpu +@brief Port of RIOT-OS to Espressif's ESP32 MCUs + +## Introduction + +**RIOT-Xtensa-ESP** is a bare metal implementation of **RIOT-OS** for **ESP32** SOCs which supports most features of RIOT-OS. The peripheral SPI and I2C interfaces allow to connect all external hardware modules supported by RIOT-OS, such as sensors and actuators. SPI interface can also be used to connect external IEEE802.15.4 modules to integrate ESP32 boards into a GNRC network. + +Although the port does not use the official ESP-IDF (Espresso IoT Development Framework) SDK, it must be installed for compilation. The reason is that the port uses most of the ESP32 SOC definitions provided by the ESP-IDF header files. In addition, it needs the hardware abstraction library (libhal), which is part of the ESP-IDF SDK. + +## Limitations + +- The implementation uses **only one core** (the PRO CPU) because RIOT does not support running multiple threads simultaneously. + +- **WLAN and Bluetooth** cannot be used at the moment. (Maybe it will never be possible.) The reason is that these modules are controlled by **proprietary binary libraries** from Espressif which are not documented and require the ESP-IDF, the official SDK from Espressif. ESP-IDF itself is a multicore FreeRTOS port, which of course can not be used together with the RIOT-OS. Furthermore, Espressif's proprietary libraries use FreeRTOS functionality themselves. That is, to get the Wi-Fi or Bluetooth module up and running, many ESP-IDF SDK functions and a FreeRTOS adoption level would have to be (re)implemented. + +- **Flash encryption** is not yet supported. + +## Known Problems + +If a task terminates with return statement, spilling registers in the windowed register ABI may result in illegal retw instructions during next context switch. As long as all tasks are executed as infinite loops and don't terminate, there are no problems. + +## Readme + +Please refer file $(RIOTBASE)/cpu/esp32/README.md for more information about the toolchain, supported features and compile options. +*/ diff --git a/cpu/esp32/esp-idf/Makefile b/cpu/esp32/esp-idf/Makefile new file mode 100644 index 0000000000000..a94fdf4ca8aec --- /dev/null +++ b/cpu/esp32/esp-idf/Makefile @@ -0,0 +1,13 @@ +MODULE=esp_idf + +DIRS += driver +DIRS += esp32 +DIRS += heap +DIRS += soc +DIRS += spi_flash + +ifeq ($(ENABLE_WIFI), 1) +DIRS += nvs_flash +endif + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/esp32/esp-idf/README.md b/cpu/esp32/esp-idf/README.md new file mode 100644 index 0000000000000..42806a0d43c1a --- /dev/null +++ b/cpu/esp32/esp-idf/README.md @@ -0,0 +1 @@ +The files in this directory and all subdirectories are from the Espressif IoT Development Framework[ESP-IDF](https://github.com/espressif/esp-idf.git), the official development framework for ESP32. All of these files are copyright of Espressif Systems (Shanghai) PTE LTD or their respective owners and licensed under the Apache License, Version 2.0. Please refer the copyright notice in these files for details. diff --git a/cpu/esp32/esp-idf/driver/Makefile b/cpu/esp32/esp-idf/driver/Makefile new file mode 100644 index 0000000000000..b51a188b3648d --- /dev/null +++ b/cpu/esp32/esp-idf/driver/Makefile @@ -0,0 +1,3 @@ +MODULE=esp_idf_driver + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/esp32/esp-idf/driver/periph_ctrl.c b/cpu/esp32/esp-idf/driver/periph_ctrl.c new file mode 100644 index 0000000000000..364ef8f14fb97 --- /dev/null +++ b/cpu/esp32/esp-idf/driver/periph_ctrl.c @@ -0,0 +1,212 @@ +// 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 +#include "esp_intr.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/xtensa_api.h" +#include "soc/dport_reg.h" +#include "driver/periph_ctrl.h" + +static portMUX_TYPE periph_spinlock = portMUX_INITIALIZER_UNLOCKED; + +/* Static functions to return register address & mask for clk_en / rst of each peripheral */ +static uint32_t get_clk_en_mask(periph_module_t periph); +static uint32_t get_rst_en_mask(periph_module_t periph); +static uint32_t get_clk_en_reg(periph_module_t periph); +static uint32_t get_rst_en_reg(periph_module_t periph); + +void periph_module_enable(periph_module_t periph) +{ + portENTER_CRITICAL(&periph_spinlock); + DPORT_SET_PERI_REG_MASK(get_clk_en_reg(periph), get_clk_en_mask(periph)); + DPORT_CLEAR_PERI_REG_MASK(get_rst_en_reg(periph), get_rst_en_mask(periph)); + portEXIT_CRITICAL(&periph_spinlock); +} + +void periph_module_disable(periph_module_t periph) +{ + portENTER_CRITICAL(&periph_spinlock); + DPORT_CLEAR_PERI_REG_MASK(get_clk_en_reg(periph), get_clk_en_mask(periph)); + DPORT_SET_PERI_REG_MASK(get_rst_en_reg(periph), get_rst_en_mask(periph)); + portEXIT_CRITICAL(&periph_spinlock); +} + +void periph_module_reset(periph_module_t periph) +{ + portENTER_CRITICAL(&periph_spinlock); + DPORT_SET_PERI_REG_MASK(get_rst_en_reg(periph), get_rst_en_mask(periph)); + DPORT_CLEAR_PERI_REG_MASK(get_rst_en_reg(periph), get_rst_en_mask(periph)); + portEXIT_CRITICAL(&periph_spinlock); +} + +static uint32_t get_clk_en_mask(periph_module_t periph) +{ + switch(periph) { + case PERIPH_RMT_MODULE: + return DPORT_RMT_CLK_EN; + case PERIPH_LEDC_MODULE: + return DPORT_LEDC_CLK_EN; + case PERIPH_UART0_MODULE: + return DPORT_UART_CLK_EN; + case PERIPH_UART1_MODULE: + return DPORT_UART1_CLK_EN; + case PERIPH_UART2_MODULE: + return DPORT_UART2_CLK_EN; + case PERIPH_I2C0_MODULE: + return DPORT_I2C_EXT0_CLK_EN; + case PERIPH_I2C1_MODULE: + return DPORT_I2C_EXT1_CLK_EN; + case PERIPH_I2S0_MODULE: + return DPORT_I2S0_CLK_EN; + case PERIPH_I2S1_MODULE: + return DPORT_I2S1_CLK_EN; + case PERIPH_TIMG0_MODULE: + return DPORT_TIMERGROUP_CLK_EN; + case PERIPH_TIMG1_MODULE: + return DPORT_TIMERGROUP1_CLK_EN; + case PERIPH_PWM0_MODULE: + return DPORT_PWM0_CLK_EN; + case PERIPH_PWM1_MODULE: + return DPORT_PWM1_CLK_EN; + case PERIPH_PWM2_MODULE: + return DPORT_PWM2_CLK_EN; + case PERIPH_PWM3_MODULE: + return DPORT_PWM3_CLK_EN; + case PERIPH_UHCI0_MODULE: + return DPORT_UHCI0_CLK_EN; + case PERIPH_UHCI1_MODULE: + return DPORT_UHCI1_CLK_EN; + case PERIPH_PCNT_MODULE: + return DPORT_PCNT_CLK_EN; + case PERIPH_SPI_MODULE: + return DPORT_SPI_CLK_EN_1; + case PERIPH_HSPI_MODULE: + return DPORT_SPI_CLK_EN; + case PERIPH_VSPI_MODULE: + return DPORT_SPI_CLK_EN_2; + case PERIPH_SPI_DMA_MODULE: + return DPORT_SPI_DMA_CLK_EN; + case PERIPH_SDMMC_MODULE: + return DPORT_WIFI_CLK_SDIO_HOST_EN; + case PERIPH_SDIO_SLAVE_MODULE: + return DPORT_WIFI_CLK_SDIOSLAVE_EN; + case PERIPH_CAN_MODULE: + return DPORT_CAN_CLK_EN; + case PERIPH_EMAC_MODULE: + return DPORT_WIFI_CLK_EMAC_EN; + case PERIPH_RNG_MODULE: + return DPORT_WIFI_CLK_RNG_EN; + case PERIPH_WIFI_MODULE: + return DPORT_WIFI_CLK_WIFI_EN_M; + case PERIPH_BT_MODULE: + return DPORT_WIFI_CLK_BT_EN_M; + case PERIPH_WIFI_BT_COMMON_MODULE: + return DPORT_WIFI_CLK_WIFI_BT_COMMON_M; + default: + return 0; + } +} + +static uint32_t get_rst_en_mask(periph_module_t periph) +{ + switch(periph) { + case PERIPH_RMT_MODULE: + return DPORT_RMT_RST; + case PERIPH_LEDC_MODULE: + return DPORT_LEDC_RST; + case PERIPH_UART0_MODULE: + return DPORT_UART_RST; + case PERIPH_UART1_MODULE: + return DPORT_UART1_RST; + case PERIPH_UART2_MODULE: + return DPORT_UART2_RST; + case PERIPH_I2C0_MODULE: + return DPORT_I2C_EXT0_RST; + case PERIPH_I2C1_MODULE: + return DPORT_I2C_EXT1_RST; + case PERIPH_I2S0_MODULE: + return DPORT_I2S0_RST; + case PERIPH_I2S1_MODULE: + return DPORT_I2S1_RST; + case PERIPH_TIMG0_MODULE: + return DPORT_TIMERGROUP_RST; + case PERIPH_TIMG1_MODULE: + return DPORT_TIMERGROUP1_RST; + case PERIPH_PWM0_MODULE: + return DPORT_PWM0_RST; + case PERIPH_PWM1_MODULE: + return DPORT_PWM1_RST; + case PERIPH_PWM2_MODULE: + return DPORT_PWM2_RST; + case PERIPH_PWM3_MODULE: + return DPORT_PWM3_RST; + case PERIPH_UHCI0_MODULE: + return DPORT_UHCI0_RST; + case PERIPH_UHCI1_MODULE: + return DPORT_UHCI1_RST; + case PERIPH_PCNT_MODULE: + return DPORT_PCNT_RST; + case PERIPH_SPI_MODULE: + return DPORT_SPI_RST_1; + case PERIPH_HSPI_MODULE: + return DPORT_SPI_RST; + case PERIPH_VSPI_MODULE: + return DPORT_SPI_RST_2; + case PERIPH_SPI_DMA_MODULE: + return DPORT_SPI_DMA_RST; + case PERIPH_SDMMC_MODULE: + return DPORT_SDIO_HOST_RST; + case PERIPH_SDIO_SLAVE_MODULE: + return DPORT_SDIO_RST; + case PERIPH_CAN_MODULE: + return DPORT_CAN_RST; + case PERIPH_EMAC_MODULE: + return DPORT_EMAC_RST; + case PERIPH_WIFI_MODULE: + case PERIPH_BT_MODULE: + case PERIPH_WIFI_BT_COMMON_MODULE: + return 0; + default: + return 0; + } +} + +static bool is_wifi_clk_peripheral(periph_module_t periph) +{ + /* A small subset of peripherals use WIFI_CLK_EN_REG and + CORE_RST_EN_REG for their clock & reset registers */ + switch(periph) { + case PERIPH_SDMMC_MODULE: + case PERIPH_SDIO_SLAVE_MODULE: + case PERIPH_EMAC_MODULE: + case PERIPH_RNG_MODULE: + case PERIPH_WIFI_MODULE: + case PERIPH_BT_MODULE: + case PERIPH_WIFI_BT_COMMON_MODULE: + return true; + default: + return false; + } +} + +static uint32_t get_clk_en_reg(periph_module_t periph) +{ + return is_wifi_clk_peripheral(periph) ? DPORT_WIFI_CLK_EN_REG : DPORT_PERIP_CLK_EN_REG; +} + +static uint32_t get_rst_en_reg(periph_module_t periph) +{ + return is_wifi_clk_peripheral(periph) ? DPORT_CORE_RST_EN_REG : DPORT_PERIP_RST_EN_REG; +} diff --git a/cpu/esp32/esp-idf/esp32/Makefile b/cpu/esp32/esp-idf/esp32/Makefile new file mode 100644 index 0000000000000..0219b3c76fa6b --- /dev/null +++ b/cpu/esp32/esp-idf/esp32/Makefile @@ -0,0 +1,8 @@ +MODULE=esp_idf_esp32 + +include $(RIOTBASE)/Makefile.base + +INCLUDES += -I$(RIOTCPU)/$(CPU)/esp-idf/include/esp32 +INCLUDES += -I$(RIOTCPU)/$(CPU)/esp-idf/include/log +INCLUDES += -I$(SDK_DIR)/components/esp32 +INCLUDES += -I$(SDK_DIR)/components/nvs_flash/include diff --git a/cpu/esp32/esp-idf/esp32/clk.c b/cpu/esp32/esp-idf/esp32/clk.c new file mode 100644 index 0000000000000..268c71aca94d6 --- /dev/null +++ b/cpu/esp32/esp-idf/esp32/clk.c @@ -0,0 +1,309 @@ +// Copyright 2015-2017 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. + +/* + * PLEASE NOTE: This file is an excerpt of the original ESP-IDF file + * + * /path/to/esp-idf/components/esp32/clk.c + * + * with a few minor changes or adjustments. + */ + +#define ENABLE_DEBUG 0 +#include "debug.h" +#include "common.h" + +#include +#include +#include +#include +#include "sdkconfig.h" +#include "esp_attr.h" +#include "esp_log.h" +#include "esp_clk.h" +#include "esp_clk_internal.h" +#include "rom/ets_sys.h" +#include "rom/uart.h" +#include "rom/rtc.h" +#include "soc/soc.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/i2s_reg.h" +#include "driver/periph_ctrl.h" +#include "xtensa/core-macros.h" + +/* Number of cycles to wait from the 32k XTAL oscillator to consider it running. + * Larger values increase startup delay. Smaller values may cause false positive + * detection (i.e. oscillator runs for a few cycles and then stops). + */ +#define XTAL_32K_DETECT_CYCLES 32 +#define SLOW_CLK_CAL_CYCLES CONFIG_ESP32_RTC_CLK_CAL_CYCLES + +#define MHZ (1000000) + +void select_rtc_slow_clk(rtc_slow_freq_t slow_clk); + +// g_ticks_us defined in ROMs for PRO and APP CPU +extern uint32_t g_ticks_per_us_pro; +extern uint32_t g_ticks_per_us_app; + +static const char* TAG = "clk"; + +void IRAM_ATTR esp_clk_slowclk_cal_set(uint32_t new_cal) +{ +#ifdef WITH_RTC + /* To force monotonic time values even when clock calibration value changes, + * we adjust boot time, given current time and the new calibration value: + * T = boot_time_old + cur_cal * ticks / 2^19 + * T = boot_time_adj + new_cal * ticks / 2^19 + * which results in: + * boot_time_adj = boot_time_old + ticks * (cur_cal - new_cal) / 2^19 + */ + const int64_t ticks = (int64_t) rtc_time_get(); + const uint32_t cur_cal = REG_READ(RTC_SLOW_CLK_CAL_REG); + int32_t cal_diff = (int32_t) (cur_cal - new_cal); + int64_t boot_time_diff = ticks * cal_diff / (1LL << RTC_CLK_CAL_FRACT); + uint64_t boot_time_adj = get_boot_time() + boot_time_diff; + set_boot_time(boot_time_adj); +#endif + REG_WRITE(RTC_SLOW_CLK_CAL_REG, new_cal); +} + +uint32_t esp_clk_slowclk_cal_get(void) +{ + return REG_READ(RTC_SLOW_CLK_CAL_REG); +} + +void esp_clk_init(void) +{ + rtc_config_t cfg = RTC_CONFIG_DEFAULT(); + rtc_init(cfg); + +#ifdef CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS + /* Check the bootloader set the XTAL frequency. + + Bootloaders pre-v2.1 don't do this. + */ + rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); + if (xtal_freq == RTC_XTAL_FREQ_AUTO) { + ESP_EARLY_LOGW(TAG, "RTC domain not initialised by bootloader"); + bootloader_clock_configure(); + } +#else + /* If this assertion fails, either upgrade the bootloader or enable CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS */ + assert(rtc_clk_xtal_freq_get() != RTC_XTAL_FREQ_AUTO); +#endif + + rtc_clk_fast_freq_set(RTC_FAST_FREQ_8M); + +#ifdef CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL + select_rtc_slow_clk(RTC_SLOW_FREQ_32K_XTAL); +#else + select_rtc_slow_clk(RTC_SLOW_FREQ_RTC); +#endif + + uint32_t freq_mhz = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; + rtc_cpu_freq_t freq = RTC_CPU_FREQ_80M; + switch(freq_mhz) { + case 240: + freq = RTC_CPU_FREQ_240M; + break; + case 160: + freq = RTC_CPU_FREQ_160M; + break; + default: + freq_mhz = 80; + /* no break */ + case 80: + freq = RTC_CPU_FREQ_80M; + break; + } + + // Wait for UART TX to finish, otherwise some UART output will be lost + // when switching APB frequency + uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); + + uint32_t freq_before = rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()) / MHZ ; + + rtc_clk_cpu_freq_set(freq); + + // Re calculate the ccount to make time calculation correct. + uint32_t freq_after = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; + XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before ); +} + +int IRAM_ATTR esp_clk_cpu_freq(void) +{ + return g_ticks_per_us_pro * 1000000; +} + +int IRAM_ATTR esp_clk_apb_freq(void) +{ + return MIN(g_ticks_per_us_pro, 80) * 1000000; +} + +void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us) +{ + /* Update scale factors used by ets_delay_us */ + g_ticks_per_us_pro = ticks_per_us; + g_ticks_per_us_app = ticks_per_us; +} + +void select_rtc_slow_clk(rtc_slow_freq_t slow_clk) +{ + if (slow_clk == RTC_SLOW_FREQ_32K_XTAL) { + /* 32k XTAL oscillator needs to be enabled and running before it can + * be used. Hardware doesn't have a direct way of checking if the + * oscillator is running. Here we use rtc_clk_cal function to count + * the number of main XTAL cycles in the given number of 32k XTAL + * oscillator cycles. If the 32k XTAL has not started up, calibration + * will time out, returning 0. + */ + rtc_clk_32k_enable(true); + uint32_t cal_val = 0; + uint32_t wait = 0; + // increment of 'wait' counter equivalent to 3 seconds + const uint32_t warning_timeout = 3 /* sec */ + * 32768 /* Hz */ + / (2 * XTAL_32K_DETECT_CYCLES); + ESP_EARLY_LOGD(TAG, "waiting for 32k oscillator to start up"); + do { + ++wait; + cal_val = rtc_clk_cal(RTC_CAL_32K_XTAL, XTAL_32K_DETECT_CYCLES); + if (wait % warning_timeout == 0) { + ESP_EARLY_LOGW(TAG, "still waiting for 32k oscillator to start up"); + } + } while (cal_val == 0); + ESP_EARLY_LOGD(TAG, "32k oscillator ready, wait=%d", wait); + } + rtc_clk_slow_freq_set(slow_clk); + uint32_t cal_val; + if (SLOW_CLK_CAL_CYCLES > 0) { + /* TODO: 32k XTAL oscillator has some frequency drift at startup. + * Improve calibration routine to wait until the frequency is stable. + */ + cal_val = rtc_clk_cal(RTC_CAL_RTC_MUX, SLOW_CLK_CAL_CYCLES); + } else { + const uint64_t cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL; + cal_val = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz()); + } + ESP_EARLY_LOGD(TAG, "RTC_SLOW_CLK calibration value: %d", cal_val); + esp_clk_slowclk_cal_set(cal_val); +} + +/* This function is not exposed as an API at this point. + * All peripheral clocks are default enabled after chip is powered on. + * This function disables some peripheral clocks when cpu starts. + * These peripheral clocks are enabled when the peripherals are initialized + * and disabled when they are de-initialized. + */ +void esp_perip_clk_init(void) +{ + uint32_t common_perip_clk, hwcrypto_perip_clk, wifi_bt_sdio_clk = 0; + +#if CONFIG_FREERTOS_UNICORE + RESET_REASON rst_reas[1]; +#else + RESET_REASON rst_reas[2]; +#endif + + rst_reas[0] = rtc_get_reset_reason(0); + +#if !CONFIG_FREERTOS_UNICORE + rst_reas[1] = rtc_get_reset_reason(1); +#endif + + /* For reason that only reset CPU, do not disable the clocks + * that have been enabled before reset. + */ + if ((rst_reas[0] >= TGWDT_CPU_RESET && rst_reas[0] <= RTCWDT_CPU_RESET) +#if !CONFIG_FREERTOS_UNICORE + || (rst_reas[1] >= TGWDT_CPU_RESET && rst_reas[1] <= RTCWDT_CPU_RESET) +#endif + ) { + common_perip_clk = ~DPORT_READ_PERI_REG(DPORT_PERIP_CLK_EN_REG); + hwcrypto_perip_clk = ~DPORT_READ_PERI_REG(DPORT_PERI_CLK_EN_REG); + wifi_bt_sdio_clk = ~DPORT_READ_PERI_REG(DPORT_WIFI_CLK_EN_REG); + } + else { + common_perip_clk = DPORT_WDG_CLK_EN | + DPORT_I2S0_CLK_EN | +#if CONFIG_CONSOLE_UART_NUM != 0 + DPORT_UART_CLK_EN | +#endif +#if CONFIG_CONSOLE_UART_NUM != 1 + DPORT_UART1_CLK_EN | +#endif +#if CONFIG_CONSOLE_UART_NUM != 2 + DPORT_UART2_CLK_EN | +#endif + DPORT_SPI_CLK_EN | + DPORT_I2C_EXT0_CLK_EN | + DPORT_UHCI0_CLK_EN | + DPORT_RMT_CLK_EN | + DPORT_PCNT_CLK_EN | + DPORT_LEDC_CLK_EN | + DPORT_UHCI1_CLK_EN | + DPORT_TIMERGROUP1_CLK_EN | + DPORT_SPI_CLK_EN_2 | + DPORT_PWM0_CLK_EN | + DPORT_I2C_EXT1_CLK_EN | + DPORT_CAN_CLK_EN | + DPORT_PWM1_CLK_EN | + DPORT_I2S1_CLK_EN | + DPORT_SPI_DMA_CLK_EN | + DPORT_PWM2_CLK_EN | + DPORT_PWM3_CLK_EN; + hwcrypto_perip_clk = DPORT_PERI_EN_AES | + DPORT_PERI_EN_SHA | + DPORT_PERI_EN_RSA | + DPORT_PERI_EN_SECUREBOOT; + wifi_bt_sdio_clk = DPORT_WIFI_CLK_WIFI_EN | + DPORT_WIFI_CLK_BT_EN_M | + DPORT_WIFI_CLK_UNUSED_BIT5 | + DPORT_WIFI_CLK_UNUSED_BIT12 | + DPORT_WIFI_CLK_SDIOSLAVE_EN | + DPORT_WIFI_CLK_SDIO_HOST_EN | + DPORT_WIFI_CLK_EMAC_EN; + } + + +#if CONFIG_SPIRAM_SPEED_80M +//80MHz SPIRAM uses SPI2 as well; it's initialized before this is called. Because it is used in +//a weird mode where clock to the peripheral is disabled but reset is also disabled, it 'hangs' +//in a state where it outputs a continuous 80MHz signal. Mask its bit here because we should +//not modify that state, regardless of what we calculated earlier. + common_perip_clk &= ~DPORT_SPI_CLK_EN_2; +#endif + + /* Change I2S clock to audio PLL first. Because if I2S uses 160MHz clock, + * the current is not reduced when disable I2S clock. + */ + DPORT_SET_PERI_REG_MASK(I2S_CLKM_CONF_REG(0), I2S_CLKA_ENA); + DPORT_SET_PERI_REG_MASK(I2S_CLKM_CONF_REG(1), I2S_CLKA_ENA); + + /* Disable some peripheral clocks. */ + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, common_perip_clk); + DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, common_perip_clk); + + /* Disable hardware crypto clocks. */ + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERI_CLK_EN_REG, hwcrypto_perip_clk); + DPORT_SET_PERI_REG_MASK(DPORT_PERI_RST_EN_REG, hwcrypto_perip_clk); + + /* Disable WiFi/BT/SDIO clocks. */ + DPORT_CLEAR_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, wifi_bt_sdio_clk); + + /* Enable RNG clock. */ + periph_module_enable(PERIPH_RNG_MODULE); +} diff --git a/cpu/esp32/esp-idf/esp32/dport_access.c b/cpu/esp32/esp-idf/esp32/dport_access.c new file mode 100644 index 0000000000000..fadd13cf5c600 --- /dev/null +++ b/cpu/esp32/esp-idf/esp32/dport_access.c @@ -0,0 +1,224 @@ +// Copyright 2010-2017 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. + +/* + * DPORT access is used for do protection when dual core access DPORT internal register and APB register via DPORT simultaneously + * This function will be initialize after FreeRTOS startup. + * When cpu0 want to access DPORT register, it should notify cpu1 enter in high-priority interrupt for be mute. When cpu1 already in high-priority interrupt, + * cpu0 can access DPORT register. Currently, cpu1 will wait for cpu0 finish access and exit high-priority interrupt. + */ + +#ifdef RIOT_OS +#include "common.h" +#endif /* RIOT_OS */ + +#include +#include + +#include +#include "esp_attr.h" +#include "esp_err.h" +#include "esp_intr.h" +#include "rom/ets_sys.h" +#include "rom/uart.h" + +#include "soc/cpu.h" +#include "soc/dport_reg.h" +#include "soc/spi_reg.h" + +#ifndef CONFIG_FREERTOS_UNICORE +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/portmacro.h" +#endif + +#include "xtensa/core-macros.h" + +#ifndef CONFIG_FREERTOS_UNICORE +static portMUX_TYPE g_dport_mux = portMUX_INITIALIZER_UNLOCKED; + +#define DPORT_CORE_STATE_IDLE 0 +#define DPORT_CORE_STATE_RUNNING 1 +static uint32_t volatile dport_core_state[portNUM_PROCESSORS]; //cpu is already run + +/* these global variables are accessed from interrupt vector, hence not declared as static */ +uint32_t volatile dport_access_start[portNUM_PROCESSORS]; //dport register could be accessed +uint32_t volatile dport_access_end[portNUM_PROCESSORS]; //dport register is accessed over + +static uint32_t volatile dport_access_ref[portNUM_PROCESSORS]; //dport access reference + +#ifdef DPORT_ACCESS_BENCHMARK +#define DPORT_ACCESS_BENCHMARK_STORE_NUM +static uint32_t ccount_start[portNUM_PROCESSORS]; +static uint32_t ccount_end[portNUM_PROCESSORS]; +static uint32_t ccount_margin[portNUM_PROCESSORS][DPORT_ACCESS_BENCHMARK_STORE_NUM]; +static uint32_t ccount_margin_cnt; +#endif + + +static BaseType_t oldInterruptLevel[2]; +#endif // CONFIG_FREERTOS_UNICORE + +/* stall other cpu that this cpu is pending to access dport register start */ +void IRAM_ATTR esp_dport_access_stall_other_cpu_start(void) +{ +#ifndef CONFIG_FREERTOS_UNICORE + if (dport_core_state[0] == DPORT_CORE_STATE_IDLE + || dport_core_state[1] == DPORT_CORE_STATE_IDLE) { + return; + } + + BaseType_t intLvl = portENTER_CRITICAL_NESTED(); + + int cpu_id = xPortGetCoreID(); + +#ifdef DPORT_ACCESS_BENCHMARK + ccount_start[cpu_id] = XTHAL_GET_CCOUNT(); +#endif + + if (dport_access_ref[cpu_id] == 0) { + portENTER_CRITICAL_ISR(&g_dport_mux); + + oldInterruptLevel[cpu_id]=intLvl; + + dport_access_start[cpu_id] = 0; + dport_access_end[cpu_id] = 0; + + if (cpu_id == 0) { + _DPORT_REG_WRITE(DPORT_CPU_INTR_FROM_CPU_3_REG, DPORT_CPU_INTR_FROM_CPU_3); //interrupt on cpu1 + } else { + _DPORT_REG_WRITE(DPORT_CPU_INTR_FROM_CPU_2_REG, DPORT_CPU_INTR_FROM_CPU_2); //interrupt on cpu0 + } + + while (!dport_access_start[cpu_id]) {}; + + REG_READ(SPI_DATE_REG(3)); //just read a APB register sure that the APB-bus is idle + } + + dport_access_ref[cpu_id]++; + + if (dport_access_ref[cpu_id] > 1) { + /* Interrupts are already disabled by the parent, we're nested here. */ + portEXIT_CRITICAL_NESTED(intLvl); + } +#endif /* CONFIG_FREERTOS_UNICORE */ +} + +/* stall other cpu that this cpu is pending to access dport register end */ +void IRAM_ATTR esp_dport_access_stall_other_cpu_end(void) +{ +#ifndef CONFIG_FREERTOS_UNICORE + int cpu_id = xPortGetCoreID(); + + if (dport_core_state[0] == DPORT_CORE_STATE_IDLE + || dport_core_state[1] == DPORT_CORE_STATE_IDLE) { + return; + } + + if (dport_access_ref[cpu_id] == 0) { + assert(0); + } + + dport_access_ref[cpu_id]--; + + if (dport_access_ref[cpu_id] == 0) { + dport_access_end[cpu_id] = 1; + + portEXIT_CRITICAL_ISR(&g_dport_mux); + + portEXIT_CRITICAL_NESTED(oldInterruptLevel[cpu_id]); + } + +#ifdef DPORT_ACCESS_BENCHMARK + ccount_end[cpu_id] = XTHAL_GET_CCOUNT(); + ccount_margin[cpu_id][ccount_margin_cnt] = ccount_end[cpu_id] - ccount_start[cpu_id]; + ccount_margin_cnt = (ccount_margin_cnt + 1)&(DPORT_ACCESS_BENCHMARK_STORE_NUM - 1); +#endif +#endif /* CONFIG_FREERTOS_UNICORE */ +} + +void IRAM_ATTR esp_dport_access_stall_other_cpu_start_wrap(void) +{ + DPORT_STALL_OTHER_CPU_START(); +} + +void IRAM_ATTR esp_dport_access_stall_other_cpu_end_wrap(void) +{ + DPORT_STALL_OTHER_CPU_END(); +} + +#ifndef CONFIG_FREERTOS_UNICORE +static void dport_access_init_core(void *arg) +{ + int core_id = 0; + uint32_t intr_source = ETS_FROM_CPU_INTR2_SOURCE; + + + core_id = xPortGetCoreID(); + if (core_id == 1) { + intr_source = ETS_FROM_CPU_INTR3_SOURCE; + } + + ESP_INTR_DISABLE(ETS_DPORT_INUM); + intr_matrix_set(core_id, intr_source, ETS_DPORT_INUM); + ESP_INTR_ENABLE(ETS_DPORT_INUM); + + dport_access_ref[core_id] = 0; + dport_access_start[core_id] = 0; + dport_access_end[core_id] = 0; + dport_core_state[core_id] = DPORT_CORE_STATE_RUNNING; + + vTaskDelete(NULL); +} +#endif + +/* Defer initialisation until after scheduler is running */ +void esp_dport_access_int_init(void) +{ +#ifndef CONFIG_FREERTOS_UNICORE + portBASE_TYPE res = xTaskCreatePinnedToCore(&dport_access_init_core, "dport", configMINIMAL_STACK_SIZE, NULL, 5, NULL, xPortGetCoreID()); + assert(res == pdTRUE); +#endif +} + +void IRAM_ATTR esp_dport_access_int_pause(void) +{ +#ifndef CONFIG_FREERTOS_UNICORE + portENTER_CRITICAL_ISR(&g_dport_mux); + dport_core_state[0] = DPORT_CORE_STATE_IDLE; + dport_core_state[1] = DPORT_CORE_STATE_IDLE; + portEXIT_CRITICAL_ISR(&g_dport_mux); +#endif +} + +//Used in panic code: the enter_critical stuff may be messed up so we just stop everything without checking the mux. +void IRAM_ATTR esp_dport_access_int_abort(void) +{ +#ifndef CONFIG_FREERTOS_UNICORE + dport_core_state[0] = DPORT_CORE_STATE_IDLE; + dport_core_state[1] = DPORT_CORE_STATE_IDLE; +#endif +} + +void IRAM_ATTR esp_dport_access_int_resume(void) +{ +#ifndef CONFIG_FREERTOS_UNICORE + portENTER_CRITICAL_ISR(&g_dport_mux); + dport_core_state[0] = DPORT_CORE_STATE_RUNNING; + dport_core_state[1] = DPORT_CORE_STATE_RUNNING; + portEXIT_CRITICAL_ISR(&g_dport_mux); +#endif +} diff --git a/cpu/esp32/esp-idf/esp32/gdbstub.c b/cpu/esp32/esp-idf/esp32/gdbstub.c new file mode 100644 index 0000000000000..30566be2c690b --- /dev/null +++ b/cpu/esp32/esp-idf/esp32/gdbstub.c @@ -0,0 +1,370 @@ +// 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. + +/* + * PLEASE NOTE: This file is an excerpt of the original ESP-IDF file + * + * /path/to/esp-idf/components/esp32/gdbstub.c + * + * with a few minor changes to make it compilable with RIOT. + */ + +/****************************************************************************** + * Description: A stub to make the ESP32 debuggable by GDB over the serial + * port, at least enough to do a backtrace on panic. This gdbstub is read-only: + * it allows inspecting the ESP32 state + *******************************************************************************/ + +#ifdef ENABLE_GDBSTUB + +#include "rom/ets_sys.h" +#include "soc/uart_reg.h" +#include "soc/io_mux_reg.h" +#include "esp_gdbstub.h" +#include "driver/gpio.h" + +//Length of buffer used to reserve GDB commands. Has to be at least able to fit the G command, which +//implies a minimum size of about 320 bytes. +#define PBUFLEN 512 + +static unsigned char cmd[PBUFLEN]; //GDB command input buffer +static char chsum; //Running checksum of the output packet + +#define ATTR_GDBFN + +//Receive a char from the uart. Uses polling and feeds the watchdog. +static int ATTR_GDBFN gdbRecvChar(void) { + int i; + while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_RXFIFO_CNT_S)&UART_RXFIFO_CNT)==0) ; + i=READ_PERI_REG(UART_FIFO_REG(0)); + return i; +} + +//Send a char to the uart. +static void ATTR_GDBFN gdbSendChar(char c) { + while (((READ_PERI_REG(UART_STATUS_REG(0))>>UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT)>=126) ; + WRITE_PERI_REG(UART_FIFO_REG(0), c); +} + +//Send the start of a packet; reset checksum calculation. +static void ATTR_GDBFN gdbPacketStart(void) { + chsum=0; + gdbSendChar('$'); +} + +//Send a char as part of a packet +static void ATTR_GDBFN gdbPacketChar(char c) { + if (c=='#' || c=='$' || c=='}' || c=='*') { + gdbSendChar('}'); + gdbSendChar(c^0x20); + chsum+=(c^0x20)+'}'; + } else { + gdbSendChar(c); + chsum+=c; + } +} + +//Send a string as part of a packet +static void ATTR_GDBFN gdbPacketStr(const char *c) { + while (*c!=0) { + gdbPacketChar(*c); + c++; + } +} + +//Send a hex val as part of a packet. 'bits'/4 dictates the number of hex chars sent. +static void ATTR_GDBFN gdbPacketHex(int val, int bits) { + char hexChars[]="0123456789abcdef"; + int i; + for (i=bits; i>0; i-=4) { + gdbPacketChar(hexChars[(val>>(i-4))&0xf]); + } +} + +//Finish sending a packet. +static void ATTR_GDBFN gdbPacketEnd(void) { + gdbSendChar('#'); + gdbPacketHex(chsum, 8); +} + +//Error states used by the routines that grab stuff from the incoming gdb packet +#define ST_ENDPACKET -1 +#define ST_ERR -2 +#define ST_OK -3 +#define ST_CONT -4 + +//Grab a hex value from the gdb packet. Ptr will get positioned on the end +//of the hex string, as far as the routine has read into it. Bits/4 indicates +//the max amount of hex chars it gobbles up. Bits can be -1 to eat up as much +//hex chars as possible. +static long ATTR_GDBFN gdbGetHexVal(unsigned char **ptr, int bits) { + int i; + int no; + unsigned int v=0; + no=bits/4; + if (bits==-1) no=64; + for (i=0; i='0' && c<='9') { + v<<=4; + v|=(c-'0'); + } else if (c>='A' && c<='F') { + v<<=4; + v|=(c-'A')+10; + } else if (c>='a' && c<='f') { + v<<=4; + v|=(c-'a')+10; + } else if (c=='#') { + if (bits==-1) { + (*ptr)--; + return v; + } + return ST_ENDPACKET; + } else { + if (bits==-1) { + (*ptr)--; + return v; + } + return ST_ERR; + } + } + return v; +} + +//Swap an int into the form gdb wants it +static int ATTR_GDBFN iswap(int i) { + int r; + r=((i>>24)&0xff); + r|=((i>>16)&0xff)<<8; + r|=((i>>8)&0xff)<<16; + r|=((i>>0)&0xff)<<24; + return r; +} + +//Read a byte from ESP32 memory. +static unsigned char ATTR_GDBFN readbyte(unsigned int p) { + int *i=(int*)(p&(~3)); + if (p<0x20000000 || p>=0x80000000) return -1; + return *i>>((p&3)*8); +} + + +//Register file in the format exp108 gdb port expects it. +//Inspired by gdb/regformats/reg-xtensa.dat +typedef struct { + uint32_t pc; + uint32_t a[64]; + uint32_t lbeg; + uint32_t lend; + uint32_t lcount; + uint32_t sar; + uint32_t windowbase; + uint32_t windowstart; + uint32_t configid0; + uint32_t configid1; + uint32_t ps; + uint32_t threadptr; + uint32_t br; + uint32_t scompare1; + uint32_t acclo; + uint32_t acchi; + uint32_t m0; + uint32_t m1; + uint32_t m2; + uint32_t m3; + uint32_t expstate; //I'm going to assume this is exccause... + uint32_t f64r_lo; + uint32_t f64r_hi; + uint32_t f64s; + uint32_t f[16]; + uint32_t fcr; + uint32_t fsr; +} GdbRegFile; + + +GdbRegFile gdbRegFile; + +/* +//Register format as the Xtensa HAL has it: +STRUCT_FIELD (long, 4, XT_STK_EXIT, exit) +STRUCT_FIELD (long, 4, XT_STK_PC, pc) +STRUCT_FIELD (long, 4, XT_STK_PS, ps) +STRUCT_FIELD (long, 4, XT_STK_A0, a0) +[..] +STRUCT_FIELD (long, 4, XT_STK_A15, a15) +STRUCT_FIELD (long, 4, XT_STK_SAR, sar) +STRUCT_FIELD (long, 4, XT_STK_EXCCAUSE, exccause) +STRUCT_FIELD (long, 4, XT_STK_EXCVADDR, excvaddr) +STRUCT_FIELD (long, 4, XT_STK_LBEG, lbeg) +STRUCT_FIELD (long, 4, XT_STK_LEND, lend) +STRUCT_FIELD (long, 4, XT_STK_LCOUNT, lcount) +// Temporary space for saving stuff during window spill +STRUCT_FIELD (long, 4, XT_STK_TMP0, tmp0) +STRUCT_FIELD (long, 4, XT_STK_TMP1, tmp1) +STRUCT_FIELD (long, 4, XT_STK_TMP2, tmp2) +STRUCT_FIELD (long, 4, XT_STK_VPRI, vpri) +STRUCT_FIELD (long, 4, XT_STK_OVLY, ovly) +#endif +STRUCT_END(XtExcFrame) +*/ + + +static void dumpHwToRegfile(XtExcFrame *frame) { + int i; + long *frameAregs=&frame->a0; + gdbRegFile.pc=frame->pc; + for (i=0; i<16; i++) gdbRegFile.a[i]=frameAregs[i]; + for (i=16; i<64; i++) gdbRegFile.a[i]=0xDEADBEEF; + gdbRegFile.lbeg=frame->lbeg; + gdbRegFile.lend=frame->lend; + gdbRegFile.lcount=frame->lcount; + gdbRegFile.sar=frame->sar; + //All windows have been spilled to the stack by the ISR routines. The following values should indicate that. + gdbRegFile.sar=frame->sar; + gdbRegFile.windowbase=0; //0 + gdbRegFile.windowstart=0x1; //1 + gdbRegFile.configid0=0xdeadbeef; //ToDo + gdbRegFile.configid1=0xdeadbeef; //ToDo + gdbRegFile.ps=frame->ps-PS_EXCM_MASK; + gdbRegFile.threadptr=0xdeadbeef; //ToDo + gdbRegFile.br=0xdeadbeef; //ToDo + gdbRegFile.scompare1=0xdeadbeef; //ToDo + gdbRegFile.acclo=0xdeadbeef; //ToDo + gdbRegFile.acchi=0xdeadbeef; //ToDo + gdbRegFile.m0=0xdeadbeef; //ToDo + gdbRegFile.m1=0xdeadbeef; //ToDo + gdbRegFile.m2=0xdeadbeef; //ToDo + gdbRegFile.m3=0xdeadbeef; //ToDo + gdbRegFile.expstate=frame->exccause; //ToDo +} + + +//Send the reason execution is stopped to GDB. +static void sendReason(void) { + //exception-to-signal mapping + char exceptionSignal[]={4,31,11,11,2,6,8,0,6,7,0,0,7,7,7,7}; + unsigned int i=0; + gdbPacketStart(); + gdbPacketChar('T'); + i=gdbRegFile.expstate&0x7f; + if (i=PBUFLEN) return ST_ERR; + } + //A # has been received. Get and check the received chsum. + sentchs[0]=gdbRecvChar(); + sentchs[1]=gdbRecvChar(); + ptr=&sentchs[0]; + rchsum=gdbGetHexVal(&ptr, 8); + if (rchsum!=chsum) { + gdbSendChar('-'); + return ST_ERR; + } else { + gdbSendChar('+'); + return gdbHandleCommand(cmd, p); + } +} + + + +void esp_gdbstub_panic_handler(XtExcFrame *frame) { + dumpHwToRegfile(frame); + //Make sure txd/rxd are enabled + gpio_pullup_dis(1); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0RXD_U, FUNC_U0RXD_U0RXD); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_U0TXD_U, FUNC_U0TXD_U0TXD); + + sendReason(); + while(gdbReadCommand()!=ST_CONT); + while(1); +} + +#endif /* ENABLE_GDBSTUB */ diff --git a/cpu/esp32/esp-idf/esp32/lib_printf.c b/cpu/esp32/esp-idf/esp32/lib_printf.c new file mode 100644 index 0000000000000..30608e93314a0 --- /dev/null +++ b/cpu/esp32/esp-idf/esp32/lib_printf.c @@ -0,0 +1,131 @@ +// Copyright 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. + +/** + * @file lib_printf.c + * + * This file contains library-specific printf functions + * used by WiFi libraries in the `lib` directory. + * These function are used to catch any output which gets printed + * by libraries, and redirect it to ESP_LOG macros. + * + * Eventually WiFi libraries will use ESP_LOG functions internally + * and these definitions will be removed. + */ + +#include +#include +#include +#include "esp_log.h" +#include "esp_attr.h" + +#define VPRINTF_STACK_BUFFER_SIZE 80 + +static int lib_printf(const char* tag, const char* format, va_list arg) +{ + char temp[VPRINTF_STACK_BUFFER_SIZE]; + int len = vsnprintf(temp, sizeof(temp) - 1, format, arg); + temp[sizeof(temp) - 1] = 0; + int i; + for (i = len - 1; i >= 0; --i) { + if (temp[i] != '\n' && temp[i] != '\r' && temp[i] != ' ') { + break; + } + temp[i] = 0; + } + if (i > 0) { + ESP_EARLY_LOGI(tag, "%s", temp); + } + va_end(arg); + return len; +} + +int phy_printf(const char* format, ...) +{ + va_list arg; + va_start(arg, format); + int res = lib_printf("phy", format, arg); + va_end(arg); + return res; +} + + +int rtc_printf(const char* format, ...) +{ + // librtc.a printf temporary disabled due to UART baud rate switching bug. + return 0; +} + +int wpa_printf(const char* format, ...) +{ + va_list arg; + va_start(arg, format); + int res = lib_printf("wpa", format, arg); + va_end(arg); + return res; +} + +int wpa2_printf(const char* format, ...) +{ + va_list arg; + va_start(arg, format); + int res = lib_printf("wpa2", format, arg); + va_end(arg); + return res; +} + +int wps_printf(const char* format, ...) +{ + va_list arg; + va_start(arg, format); + int res = lib_printf("wps", format, arg); + va_end(arg); + return res; +} + +int pp_printf(const char* format, ...) +{ + va_list arg; + va_start(arg, format); + int res = lib_printf("pp", format, arg); + va_end(arg); + return res; +} + +int sc_printf(const char* format, ...) +{ + va_list arg; + va_start(arg, format); + int res = lib_printf("smartconfig", format, arg); + va_end(arg); + return res; +} + +int core_printf(const char* format, ...) +{ + va_list arg; + va_start(arg, format); + int res = lib_printf("core", format, arg); + va_end(arg); + return res; +} + +int net80211_printf(const char* format, ...) +{ + va_list arg; + va_start(arg, format); + int res = lib_printf("net80211", format, arg); + va_end(arg); + return res; +} diff --git a/cpu/esp32/esp-idf/esp32/spiram.c b/cpu/esp32/esp-idf/esp32/spiram.c new file mode 100644 index 0000000000000..cb3f4d6a33232 --- /dev/null +++ b/cpu/esp32/esp-idf/esp32/spiram.c @@ -0,0 +1,207 @@ +/* +Abstraction layer for spi-ram. For now, it's no more than a stub for the spiram_psram functions, but if +we add more types of external RAM memory, this can be made into a more intelligent dispatcher. +*/ + +// Copyright 2015-2017 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 +#include + +#include "sdkconfig.h" +#include "esp_attr.h" +#include "esp_err.h" +#include "spiram_psram.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/xtensa_api.h" +#include "soc/soc.h" +#include "esp_heap_caps_init.h" +#include "soc/soc_memory_layout.h" +#include "soc/dport_reg.h" +#include "rom/cache.h" + +#if CONFIG_FREERTOS_UNICORE +#define PSRAM_MODE PSRAM_VADDR_MODE_NORMAL +#else +#if CONFIG_MEMMAP_SPIRAM_CACHE_EVENODD +#define PSRAM_MODE PSRAM_VADDR_MODE_EVENODD +#else +#define PSRAM_MODE PSRAM_VADDR_MODE_LOWHIGH +#endif +#endif + +#if CONFIG_SPIRAM_SUPPORT + +static const char* TAG = "spiram"; + +#if CONFIG_SPIRAM_SPEED_40M && CONFIG_ESPTOOLPY_FLASHFREQ_40M +#define PSRAM_SPEED PSRAM_CACHE_F40M_S40M +#elif CONFIG_SPIRAM_SPEED_40M && CONFIG_ESPTOOLPY_FLASHFREQ_80M +#define PSRAM_SPEED PSRAM_CACHE_F80M_S40M +#elif CONFIG_SPIRAM_SPEED_80M && CONFIG_ESPTOOLPY_FLASHFREQ_80M +#define PSRAM_SPEED PSRAM_CACHE_F80M_S80M +#else +#error "FLASH speed can only be equal to or higher than SRAM speed while SRAM is enabled!" +#endif + + +static bool spiram_inited=false; + + +/* + Simple RAM test. Writes a word every 32 bytes. Takes about a second to complete for 4MiB. Returns + true when RAM seems OK, false when test fails. WARNING: Do not run this before the 2nd cpu has been + initialized (in a two-core system) or after the heap allocator has taken ownership of the memory. +*/ +bool esp_spiram_test(void) +{ + volatile int *spiram=(volatile int*)SOC_EXTRAM_DATA_LOW; + size_t p; + size_t s=CONFIG_SPIRAM_SIZE; + int errct=0; + int initial_err=-1; + for (p=0; p<(s/sizeof(int)); p+=8) { + spiram[p]=p^0xAAAAAAAA; + } + for (p=0; p<(s/sizeof(int)); p+=8) { + if ((unsigned)(spiram[p])!=(p^0xAAAAAAAA)) { + errct++; + if (errct==1) initial_err=p*4; + } + } + if (errct) { + ESP_EARLY_LOGE(TAG, "SPI SRAM memory test fail. %d/%d writes failed, first @ %X\n", errct, s/32, initial_err+SOC_EXTRAM_DATA_LOW); + return false; + } else { + ESP_EARLY_LOGI(TAG, "SPI SRAM memory test OK"); + return true; + } +} + +void IRAM_ATTR esp_spiram_init_cache(void) +{ + //Enable external RAM in MMU + cache_sram_mmu_set( 0, 0, SOC_EXTRAM_DATA_LOW, 0, 32, 128 ); + //Flush and enable icache for APP CPU +#if !CONFIG_FREERTOS_UNICORE + DPORT_CLEAR_PERI_REG_MASK(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MASK_DRAM1); + cache_sram_mmu_set( 1, 0, SOC_EXTRAM_DATA_LOW, 0, 32, 128 ); +#endif +} + + +esp_err_t esp_spiram_init(void) +{ + esp_err_t r; + r = psram_enable(PSRAM_SPEED, PSRAM_MODE); + if (r != ESP_OK) { + ESP_EARLY_LOGE(TAG, "SPI RAM enabled but initialization failed. Bailing out."); + return r; + } + + ESP_EARLY_LOGI(TAG, "SPI RAM mode: %s", PSRAM_SPEED == PSRAM_CACHE_F40M_S40M ? "flash 40m sram 40m" : \ + PSRAM_SPEED == PSRAM_CACHE_F80M_S40M ? "flash 80m sram 40m" : \ + PSRAM_SPEED == PSRAM_CACHE_F80M_S80M ? "flash 80m sram 80m" : "ERROR"); + ESP_EARLY_LOGI(TAG, "PSRAM initialized, cache is in %s mode.", \ + (PSRAM_MODE==PSRAM_VADDR_MODE_EVENODD)?"even/odd (2-core)": \ + (PSRAM_MODE==PSRAM_VADDR_MODE_LOWHIGH)?"low/high (2-core)": \ + (PSRAM_MODE==PSRAM_VADDR_MODE_NORMAL)?"normal (1-core)":"ERROR"); + spiram_inited=true; + return ESP_OK; +} + + +esp_err_t esp_spiram_add_to_heapalloc(void) +{ + ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", CONFIG_SPIRAM_SIZE/1024); + //Add entire external RAM region to heap allocator. Heap allocator knows the capabilities of this type of memory, so there's + //no need to explicitly specify them. + return heap_caps_add_region((intptr_t)SOC_EXTRAM_DATA_LOW, (intptr_t)SOC_EXTRAM_DATA_LOW + CONFIG_SPIRAM_SIZE-1); +} + + +static uint8_t *dma_heap; + +esp_err_t esp_spiram_reserve_dma_pool(size_t size) { + if (size==0) return ESP_OK; //no-op + ESP_EARLY_LOGI(TAG, "Reserving pool of %dK of internal memory for DMA/internal allocations", size/1024); + dma_heap=heap_caps_malloc(size, MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL); + if (!dma_heap) return ESP_ERR_NO_MEM; + uint32_t caps[]={MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_8BIT|MALLOC_CAP_32BIT}; + return heap_caps_add_region_with_caps(caps, (intptr_t) dma_heap, (intptr_t) dma_heap+size-1); +} + +size_t esp_spiram_get_size(void) +{ + return CONFIG_SPIRAM_SIZE; +} + +/* + Before flushing the cache, if psram is enabled as a memory-mapped thing, we need to write back the data in the cache to the psram first, + otherwise it will get lost. For now, we just read 64/128K of random PSRAM memory to do this. +*/ +void IRAM_ATTR esp_spiram_writeback_cache(void) +{ + int x; + volatile int i=0; + volatile uint8_t *psram=(volatile uint8_t*)SOC_EXTRAM_DATA_LOW; + int cache_was_disabled=0; + + if (!spiram_inited) return; + + //We need cache enabled for this to work. Re-enable it if needed; make sure we + //disable it again on exit as well. + if (DPORT_REG_GET_BIT(DPORT_PRO_CACHE_CTRL_REG, DPORT_PRO_CACHE_ENABLE)==0) { + cache_was_disabled|=(1<<0); + DPORT_SET_PERI_REG_BITS(DPORT_PRO_CACHE_CTRL_REG, 1, 1, DPORT_PRO_CACHE_ENABLE_S); + } +#ifndef CONFIG_FREERTOS_UNICORE + if (DPORT_REG_GET_BIT(DPORT_APP_CACHE_CTRL_REG, DPORT_APP_CACHE_ENABLE)==0) { + cache_was_disabled|=(1<<1); + DPORT_SET_PERI_REG_BITS(DPORT_APP_CACHE_CTRL_REG, 1, 1, DPORT_APP_CACHE_ENABLE_S); + } +#endif + +#if CONFIG_FREERTOS_UNICORE + for (x=0; x<1024*64; x+=32) { + i+=psram[x]; + } +#else + /* + Note: this assumes the amount of external RAM is >2M. If it is 2M or less, what this code does is undefined. If + we ever support external RAM chips of 2M or smaller, this may need adjusting. + */ + for (x=0; x<1024*64; x+=32) { + i+=psram[x]; + i+=psram[x+(1024*1024*2)+(1024*64)]; //address picked to also clear cache of app cpu in low/high mode + } +#endif + + if (cache_was_disabled&(1<<0)) { + while (DPORT_GET_PERI_REG_BITS2(DPORT_PRO_DCACHE_DBUG0_REG, DPORT_PRO_CACHE_STATE, DPORT_PRO_CACHE_STATE_S) != 1) ; + DPORT_SET_PERI_REG_BITS(DPORT_PRO_CACHE_CTRL_REG, 1, 0, DPORT_PRO_CACHE_ENABLE_S); + } +#ifndef CONFIG_FREERTOS_UNICORE + if (cache_was_disabled&(1<<1)) { + while (DPORT_GET_PERI_REG_BITS2(DPORT_APP_DCACHE_DBUG0_REG, DPORT_APP_CACHE_STATE, DPORT_APP_CACHE_STATE_S) != 1); + DPORT_SET_PERI_REG_BITS(DPORT_APP_CACHE_CTRL_REG, 1, 0, DPORT_APP_CACHE_ENABLE_S); + } +#endif +} + + + +#endif diff --git a/cpu/esp32/esp-idf/esp32/spiram_psram.c b/cpu/esp32/esp-idf/esp32/spiram_psram.c new file mode 100644 index 0000000000000..5c89b1104fcce --- /dev/null +++ b/cpu/esp32/esp-idf/esp32/spiram_psram.c @@ -0,0 +1,659 @@ +/* + Driver bits for PSRAM chips (at the moment only the ESP-PSRAM32 chip). +*/ + +// Copyright 2013-2017 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 "string.h" +#include "esp_attr.h" +#include "esp_err.h" +#include "esp_types.h" +#include "esp_log.h" +#include "spiram_psram.h" +#include "rom/ets_sys.h" +#include "rom/spi_flash.h" +#include "rom/cache.h" +#include "soc/io_mux_reg.h" +#include "soc/dport_reg.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/efuse_reg.h" +#include "driver/gpio.h" +#include "driver/periph_ctrl.h" + +#if CONFIG_SPIRAM_SUPPORT + +//Commands for PSRAM chip +#define PSRAM_READ 0x03 +#define PSRAM_FAST_READ 0x0B +#define PSRAM_FAST_READ_DUMMY 0x3 +#define PSRAM_FAST_READ_QUAD 0xEB +#define PSRAM_WRITE 0x02 +#define PSRAM_QUAD_WRITE 0x38 +#define PSRAM_ENTER_QMODE 0x35 +#define PSRAM_EXIT_QMODE 0xF5 +#define PSRAM_RESET_EN 0x66 +#define PSRAM_RESET 0x99 +#define PSRAM_SET_BURST_LEN 0xC0 +#define PSRAM_DEVICE_ID 0x9F + +#if CONFIG_SPIRAM_TYPE_ESPPSRAM32 + +#define PSRAM_MFG_ID_M 0xff +#define PSRAM_MFG_ID_S 8 +#define PSRAM_MFG_ID_V 0x5d + +#endif + +// IO-pins for PSRAM. These need to be in the VDD_SIO power domain because all chips we +// currently support are 1.8V parts. +// WARNING: PSRAM shares all but the CS and CLK pins with the flash, so these defines +// hardcode the flash pins as well, making this code incompatible with either a setup +// that has the flash on non-standard pins or ESP32s with built-in flash. +#define FLASH_CLK_IO 6 //Psram clock is a delayed version of this in 40MHz mode +#define FLASH_CS_IO 11 +#define PSRAM_CLK_IO 17 +#define PSRAM_CS_IO 16 +#define PSRAM_SPIQ_IO 7 +#define PSRAM_SPID_IO 8 +#define PSRAM_SPIWP_IO 10 +#define PSRAM_SPIHD_IO 9 + +#define PSRAM_INTERNAL_IO_28 28 +#define PSRAM_INTERNAL_IO_29 29 +#define PSRAM_IO_MATRIX_DUMMY_40M 1 +#define PSRAM_IO_MATRIX_DUMMY_80M 2 + +#if CONFIG_FLASHMODE_QIO +#define SPI_CACHE_DUMMY SPI0_R_QIO_DUMMY_CYCLELEN //qio 3 +#elif CONFIG_FLASHMODE_QOUT +#define SPI_CACHE_DUMMY SPI0_R_FAST_DUMMY_CYCLELEN //qout 7 +#elif CONFIG_FLASHMODE_DIO +#define SPI_CACHE_DUMMY SPI0_R_DIO_DUMMY_CYCLELEN //dio 3 +#elif CONFIG_FLASHMODE_DOUT +#define SPI_CACHE_DUMMY SPI0_R_FAST_DUMMY_CYCLELEN //dout 7 +#endif + +static const char* TAG = "psram"; +typedef enum { + PSRAM_SPI_1 = 0x1, + PSRAM_SPI_2, + PSRAM_SPI_3, + PSRAM_SPI_MAX , +} psram_spi_num_t; + +static psram_cache_mode_t s_psram_mode = PSRAM_CACHE_MAX; + +/* dummy_len_plus values defined in ROM for SPI flash configuration */ +extern uint8_t g_rom_spiflash_dummy_len_plus[]; + +static int extra_dummy = 0; + +typedef enum { + PSRAM_CMD_QPI, + PSRAM_CMD_SPI, +} psram_cmd_mode_t; + +typedef struct { + uint16_t cmd; /*!< Command value */ + uint16_t cmdBitLen; /*!< Command byte length*/ + uint32_t *addr; /*!< Point to address value*/ + uint16_t addrBitLen; /*!< Address byte length*/ + uint32_t *txData; /*!< Point to send data buffer*/ + uint16_t txDataBitLen; /*!< Send data byte length.*/ + uint32_t *rxData; /*!< Point to recevie data buffer*/ + uint16_t rxDataBitLen; /*!< Recevie Data byte length.*/ + uint32_t dummyBitLen; +} psram_cmd_t; + +static void IRAM_ATTR psram_cache_init(psram_cache_mode_t psram_cache_mode, psram_vaddr_mode_t vaddrmode); + +static void psram_clear_spi_fifo(psram_spi_num_t spi_num) +{ + int i; + for (i = 0; i < 16; i++) { + WRITE_PERI_REG(SPI_W0_REG(spi_num)+i*4, 0); + } +} + +//set basic SPI write mode +static void psram_set_basic_write_mode(psram_spi_num_t spi_num) +{ + CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_QIO); + CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_DIO); + CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_QUAD); + CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_DUAL); +} +//set QPI write mode +static void psram_set_qio_write_mode(psram_spi_num_t spi_num) +{ + SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_QIO); + CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_DIO); + CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_QUAD); + CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_FWRITE_DUAL); +} +//set QPI read mode +static void psram_set_qio_read_mode(psram_spi_num_t spi_num) +{ + SET_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_QIO); + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_QUAD); + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_DUAL); + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_DIO); +} +//set SPI read mode +static void psram_set_basic_read_mode(psram_spi_num_t spi_num) +{ + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_QIO); + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_QUAD); + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_DUAL); + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_FREAD_DIO); +} + + +//start sending cmd/addr and optionally, receiving data +static void IRAM_ATTR psram_cmd_recv_start(psram_spi_num_t spi_num, uint32_t* pRxData, uint16_t rxByteLen, + psram_cmd_mode_t cmd_mode) +{ + //get cs1 + CLEAR_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1), SPI_CS1_DIS_M); + SET_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1), SPI_CS0_DIS_M); + + uint32_t mode_backup = (READ_PERI_REG(SPI_USER_REG(spi_num)) >> SPI_FWRITE_DUAL_S) & 0xf; + uint32_t rd_mode_backup = READ_PERI_REG(SPI_CTRL_REG(spi_num)) & (SPI_FREAD_DIO_M | SPI_FREAD_DUAL_M | SPI_FREAD_QUAD_M | SPI_FREAD_QIO_M); + if (cmd_mode == PSRAM_CMD_SPI) { + psram_set_basic_write_mode(spi_num); + psram_set_basic_read_mode(spi_num); + } else if (cmd_mode == PSRAM_CMD_QPI) { + psram_set_qio_write_mode(spi_num); + psram_set_qio_read_mode(spi_num); + } + + //Wait for SPI0 to idle + while ( READ_PERI_REG(SPI_EXT2_REG(0)) != 0); + DPORT_SET_PERI_REG_MASK(DPORT_HOST_INF_SEL_REG, 1 << 14); + + // Start send data + SET_PERI_REG_MASK(SPI_CMD_REG(spi_num), SPI_USR); + while ((READ_PERI_REG(SPI_CMD_REG(spi_num)) & SPI_USR)); + DPORT_CLEAR_PERI_REG_MASK(DPORT_HOST_INF_SEL_REG, 1 << 14); + + //recover spi mode + SET_PERI_REG_BITS(SPI_USER_REG(spi_num), (pRxData?SPI_FWRITE_DUAL_M:0xf), mode_backup, SPI_FWRITE_DUAL_S); + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), (SPI_FREAD_DIO_M|SPI_FREAD_DUAL_M|SPI_FREAD_QUAD_M|SPI_FREAD_QIO_M)); + SET_PERI_REG_MASK(SPI_CTRL_REG(spi_num), rd_mode_backup); + + //return cs to cs0 + SET_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1), SPI_CS1_DIS_M); + CLEAR_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1), SPI_CS0_DIS_M); + + if (pRxData) { + int idx = 0; + // Read data out + do { + *pRxData++ = READ_PERI_REG(SPI_W0_REG(spi_num) + (idx << 2)); + } while (++idx < ((rxByteLen / 4) + ((rxByteLen % 4) ? 1 : 0))); + } +} + +static uint32_t backup_usr[3]; +static uint32_t backup_usr1[3]; +static uint32_t backup_usr2[3]; + + + +//setup spi command/addr/data/dummy in user mode +static int psram_cmd_config(psram_spi_num_t spi_num, psram_cmd_t* pInData) +{ + while (READ_PERI_REG(SPI_CMD_REG(spi_num)) & SPI_USR); + backup_usr[spi_num]=READ_PERI_REG(SPI_USER_REG(spi_num)); + backup_usr1[spi_num]=READ_PERI_REG(SPI_USER1_REG(spi_num)); + backup_usr2[spi_num]=READ_PERI_REG(SPI_USER2_REG(spi_num)); + // Set command by user. + if (pInData->cmdBitLen != 0) { + // Max command length 16 bits. + SET_PERI_REG_BITS(SPI_USER2_REG(spi_num), SPI_USR_COMMAND_BITLEN, pInData->cmdBitLen - 1, + SPI_USR_COMMAND_BITLEN_S); + // Enable command + SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_COMMAND); + // Load command,bit15-0 is cmd value. + SET_PERI_REG_BITS(SPI_USER2_REG(spi_num), SPI_USR_COMMAND_VALUE, pInData->cmd, SPI_USR_COMMAND_VALUE_S); + } else { + CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_COMMAND); + SET_PERI_REG_BITS(SPI_USER2_REG(spi_num), SPI_USR_COMMAND_BITLEN, 0, SPI_USR_COMMAND_BITLEN_S); + } + // Set Address by user. + if (pInData->addrBitLen != 0) { + SET_PERI_REG_BITS(SPI_USER1_REG(spi_num), SPI_USR_ADDR_BITLEN, (pInData->addrBitLen - 1), SPI_USR_ADDR_BITLEN_S); + // Enable address + SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_ADDR); + // Set address + WRITE_PERI_REG(SPI_ADDR_REG(spi_num), *pInData->addr); + } else { + CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_ADDR); + SET_PERI_REG_BITS(SPI_USER1_REG(spi_num), SPI_USR_ADDR_BITLEN, 0, SPI_USR_ADDR_BITLEN_S); + } + // Set data by user. + uint32_t* p_tx_val = pInData->txData; + if (pInData->txDataBitLen != 0) { + // Enable MOSI + SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_MOSI); + // Load send buffer + int len = (pInData->txDataBitLen + 31) / 32; + if (p_tx_val != NULL) { + memcpy((void*)SPI_W0_REG(spi_num), p_tx_val, len * 4); + } + // Set data send buffer length.Max data length 64 bytes. + SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(spi_num), SPI_USR_MOSI_DBITLEN, (pInData->txDataBitLen - 1), + SPI_USR_MOSI_DBITLEN_S); + } else { + CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_MOSI); + SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(spi_num), SPI_USR_MOSI_DBITLEN, 0, SPI_USR_MOSI_DBITLEN_S); + } + // Set rx data by user. + if (pInData->rxDataBitLen != 0) { + // Enable MOSI + SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_MISO); + // Set data send buffer length.Max data length 64 bytes. + SET_PERI_REG_BITS(SPI_MISO_DLEN_REG(spi_num), SPI_USR_MISO_DBITLEN, (pInData->rxDataBitLen - 1), + SPI_USR_MISO_DBITLEN_S); + } else { + CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_MISO); + SET_PERI_REG_BITS(SPI_MISO_DLEN_REG(spi_num), SPI_USR_MISO_DBITLEN, 0, SPI_USR_MISO_DBITLEN_S); + } + if (pInData->dummyBitLen != 0) { + SET_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_USR_DUMMY); // dummy en + SET_PERI_REG_BITS(SPI_USER1_REG(PSRAM_SPI_1), SPI_USR_DUMMY_CYCLELEN_V, pInData->dummyBitLen - 1, + SPI_USR_DUMMY_CYCLELEN_S); //DUMMY + } else { + CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_USR_DUMMY); // dummy en + SET_PERI_REG_BITS(SPI_USER1_REG(PSRAM_SPI_1), SPI_USR_DUMMY_CYCLELEN_V, 0, SPI_USR_DUMMY_CYCLELEN_S); //DUMMY + } + return 0; +} + +void psram_cmd_end(int spi_num) { + while (READ_PERI_REG(SPI_CMD_REG(spi_num)) & SPI_USR); + WRITE_PERI_REG(SPI_USER_REG(spi_num), backup_usr[spi_num]); + WRITE_PERI_REG(SPI_USER1_REG(spi_num), backup_usr1[spi_num]); + WRITE_PERI_REG(SPI_USER2_REG(spi_num), backup_usr2[spi_num]); +} + +//exit QPI mode(set back to SPI mode) +static void psram_disable_qio_mode(psram_spi_num_t spi_num) +{ + psram_cmd_t ps_cmd; + uint32_t cmd_exit_qpi; + switch (s_psram_mode) { + case PSRAM_CACHE_F80M_S80M: + cmd_exit_qpi = PSRAM_EXIT_QMODE; + ps_cmd.txDataBitLen = 8; + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: + cmd_exit_qpi = PSRAM_EXIT_QMODE << 8; + ps_cmd.txDataBitLen = 16; + break; + } + ps_cmd.txData = &cmd_exit_qpi; + ps_cmd.cmd = 0; + ps_cmd.cmdBitLen = 0; + ps_cmd.addr = 0; + ps_cmd.addrBitLen = 0; + ps_cmd.rxData = NULL; + ps_cmd.rxDataBitLen = 0; + ps_cmd.dummyBitLen = 0; + psram_cmd_config(spi_num, &ps_cmd); + psram_cmd_recv_start(spi_num, NULL, 0, PSRAM_CMD_QPI); + psram_cmd_end(spi_num); +} + +//read psram id +static void psram_read_id(uint32_t* dev_id) +{ + psram_spi_num_t spi_num = PSRAM_SPI_1; + psram_disable_qio_mode(spi_num); + uint32_t addr = (PSRAM_DEVICE_ID << 24) | 0; + uint32_t dummy_bits = 0; + psram_cmd_t ps_cmd; + switch (s_psram_mode) { + case PSRAM_CACHE_F80M_S80M: + dummy_bits = 0 + extra_dummy; + ps_cmd.cmdBitLen = 0; + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: + dummy_bits = 0 + extra_dummy; + ps_cmd.cmdBitLen = 2; //this two bits is used to delay 2 clock cycle + break; + } + ps_cmd.cmd = 0; + ps_cmd.addr = &addr; + ps_cmd.addrBitLen = 4 * 8; + ps_cmd.txDataBitLen = 0; + ps_cmd.txData = NULL; + ps_cmd.rxDataBitLen = 4 * 8; + ps_cmd.rxData = dev_id; + ps_cmd.dummyBitLen = dummy_bits; + psram_cmd_config(spi_num, &ps_cmd); + psram_clear_spi_fifo(spi_num); + psram_cmd_recv_start(spi_num, ps_cmd.rxData, ps_cmd.rxDataBitLen / 8, PSRAM_CMD_SPI); + psram_cmd_end(spi_num); +} + +//enter QPI mode +static esp_err_t IRAM_ATTR psram_enable_qio_mode(psram_spi_num_t spi_num) +{ + psram_cmd_t ps_cmd; + uint32_t addr = (PSRAM_ENTER_QMODE << 24) | 0; + switch (s_psram_mode) { + case PSRAM_CACHE_F80M_S80M: + ps_cmd.cmdBitLen = 0; + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: + ps_cmd.cmdBitLen = 2; + break; + } + ps_cmd.cmd = 0; + ps_cmd.addr = &addr; + ps_cmd.addrBitLen = 8; + ps_cmd.txData = NULL; + ps_cmd.txDataBitLen = 0; + ps_cmd.rxData = NULL; + ps_cmd.rxDataBitLen = 0; + ps_cmd.dummyBitLen = 0; + psram_cmd_config(spi_num, &ps_cmd); + psram_cmd_recv_start(spi_num, NULL, 0, PSRAM_CMD_SPI); + psram_cmd_end(spi_num); + return ESP_OK; +} + +//spi param init for psram +void IRAM_ATTR psram_spi_init(psram_spi_num_t spi_num, psram_cache_mode_t mode) +{ + uint8_t i, k; + CLEAR_PERI_REG_MASK(SPI_SLAVE_REG(spi_num), SPI_TRANS_DONE << 5); + SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_CS_SETUP); + // SPI_CPOL & SPI_CPHA + CLEAR_PERI_REG_MASK(SPI_PIN_REG(spi_num), SPI_CK_IDLE_EDGE); + CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_CK_OUT_EDGE); + // SPI bit order + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_WR_BIT_ORDER); + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spi_num), SPI_RD_BIT_ORDER); + // SPI bit order + CLEAR_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_DOUTDIN); + // May be not must to do. + WRITE_PERI_REG(SPI_USER1_REG(spi_num), 0); + // SPI mode type + CLEAR_PERI_REG_MASK(SPI_SLAVE_REG(spi_num), SPI_SLAVE_MODE); + // Set SPI speed for non-80M mode. (80M mode uses APB clock directly.) + if (mode!=PSRAM_CACHE_F80M_S80M) { + i = 1; //Pre-divider + k = 2; //Main divider. Divide by 2 so we get 40MHz + //clear bit 31, set SPI clock div + CLEAR_PERI_REG_MASK(SPI_CLOCK_REG(spi_num), SPI_CLK_EQU_SYSCLK); + WRITE_PERI_REG(SPI_CLOCK_REG(spi_num), + (((i - 1) & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) | + (((k - 1) & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) | + ((((k + 1) / 2 - 1) & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) | //50% duty cycle + (((k - 1) & SPI_CLKCNT_L) << SPI_CLKCNT_L_S)); + } + // Enable MOSI + SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_CS_SETUP | SPI_CS_HOLD | SPI_USR_MOSI); + memset((void*)SPI_W0_REG(spi_num), 0, 16 * 4); +} + +static void IRAM_ATTR psram_gpio_config(psram_cache_mode_t mode) +{ + gpio_matrix_out(FLASH_CS_IO, SPICS0_OUT_IDX, 0, 0); + gpio_matrix_out(PSRAM_SPIQ_IO, SPIQ_OUT_IDX, 0, 0); + gpio_matrix_in(PSRAM_SPIQ_IO, SPIQ_IN_IDX, 0); + gpio_matrix_out(PSRAM_SPID_IO, SPID_OUT_IDX, 0, 0); + gpio_matrix_in(PSRAM_SPID_IO, SPID_IN_IDX, 0); + gpio_matrix_out(PSRAM_SPIWP_IO, SPIWP_OUT_IDX, 0, 0); + gpio_matrix_in(PSRAM_SPIWP_IO, SPIWP_IN_IDX, 0); + gpio_matrix_out(PSRAM_SPIHD_IO, SPIHD_OUT_IDX, 0, 0); + gpio_matrix_in(PSRAM_SPIHD_IO, SPIHD_IN_IDX, 0); + + switch (mode) { + case PSRAM_CACHE_F80M_S40M: + extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M; + g_rom_spiflash_dummy_len_plus[1] = PSRAM_IO_MATRIX_DUMMY_40M; + SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, SPI_CACHE_DUMMY + PSRAM_IO_MATRIX_DUMMY_80M, SPI_USR_DUMMY_CYCLELEN_S); //DUMMY + //set drive ability for clock + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, 3, FUN_DRV_S); + SET_PERI_REG_BITS(GPIO_PIN_MUX_REG[PSRAM_CLK_IO], FUN_DRV, 2, FUN_DRV_S); + break; + case PSRAM_CACHE_F80M_S80M: + extra_dummy = PSRAM_IO_MATRIX_DUMMY_80M; + g_rom_spiflash_dummy_len_plus[1] = PSRAM_IO_MATRIX_DUMMY_80M; + SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, SPI_CACHE_DUMMY + PSRAM_IO_MATRIX_DUMMY_80M, SPI_USR_DUMMY_CYCLELEN_S); //DUMMY + //set drive ability for clock + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, 3, FUN_DRV_S); + SET_PERI_REG_BITS(GPIO_PIN_MUX_REG[PSRAM_CLK_IO], FUN_DRV, 3, FUN_DRV_S); + break; + case PSRAM_CACHE_F40M_S40M: + extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M; + g_rom_spiflash_dummy_len_plus[1] = PSRAM_IO_MATRIX_DUMMY_40M; + SET_PERI_REG_BITS(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN_V, SPI_CACHE_DUMMY + PSRAM_IO_MATRIX_DUMMY_40M, SPI_USR_DUMMY_CYCLELEN_S); //DUMMY + //set drive ability for clock + SET_PERI_REG_BITS(PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, 2, FUN_DRV_S); + SET_PERI_REG_BITS(GPIO_PIN_MUX_REG[PSRAM_CLK_IO], FUN_DRV, 2, FUN_DRV_S); + break; + default: + break; + } + SET_PERI_REG_MASK(SPI_USER_REG(0), SPI_USR_DUMMY); // dummy en + + //select pin function gpio + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA2_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA3_U, PIN_FUNC_GPIO); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U, PIN_FUNC_GPIO); + //flash clock signal should come from IO MUX. + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U, FUNC_SD_CLK_SPICLK); +} + +//psram gpio init , different working frequency we have different solutions +esp_err_t IRAM_ATTR psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vaddrmode) //psram init +{ + uint32_t chip_ver = REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_VER_PKG); + uint32_t pkg_ver = chip_ver & 0x7; + if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32D2WDQ5) { + ESP_EARLY_LOGE(TAG, "ESP32D2WD do not support psram yet"); + return ESP_FAIL; + } else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD2) { + ESP_EARLY_LOGE(TAG, "ESP32PICOD2 do not support psram yet"); + return ESP_FAIL; + } else if (pkg_ver == EFUSE_RD_CHIP_VER_PKG_ESP32PICOD4) { + ESP_EARLY_LOGE(TAG, "ESP32PICOD4 do not support psram yet"); + return ESP_FAIL; + } + + /* note: If the third mode(80Mhz+80Mhz) is enabled, VSPI port will be occupied by the system, + Application code should never touch VSPI hardware in this case. We try to stop applications + from doing this using the drivers by claiming the port for ourselves*/ + #if CONFIG_SPIRAM_SPEED_80M + if (mode == PSRAM_CACHE_F80M_S80M) { + periph_module_enable(PERIPH_VSPI_MODULE); + bool r=spicommon_periph_claim(VSPI_HOST); + if (!r) { + return ESP_ERR_INVALID_STATE; + } + } + #endif + + WRITE_PERI_REG(GPIO_ENABLE_W1TC_REG, BIT(PSRAM_CLK_IO) | BIT(PSRAM_CS_IO)); //DISABLE OUPUT FOR IO16/17 + assert(mode < PSRAM_CACHE_MAX && "we don't support any other mode for now."); + s_psram_mode = mode; + + periph_module_enable(PERIPH_SPI_MODULE); + + WRITE_PERI_REG(SPI_EXT3_REG(0), 0x1); + CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_USR_PREP_HOLD_M); + + switch (mode) { + case PSRAM_CACHE_F80M_S80M: + psram_spi_init(PSRAM_SPI_1, mode); + CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_CS_HOLD); + gpio_matrix_out(PSRAM_CS_IO, SPICS1_OUT_IDX, 0, 0); + gpio_matrix_out(PSRAM_CLK_IO, VSPICLK_OUT_IDX, 0, 0); + //use spi3 clock,but use spi1 data/cs wires + //We get a solid 80MHz clock from SPI3 by setting it up, starting a transaction, waiting until it + //is in progress, then cutting the clock (but not the reset!) to that peripheral. + WRITE_PERI_REG(SPI_ADDR_REG(PSRAM_SPI_3), 32 << 24); + WRITE_PERI_REG(SPI_CLOCK_REG(PSRAM_SPI_3), SPI_CLK_EQU_SYSCLK_M); //SET 80M AND CLEAR OTHERS + SET_PERI_REG_MASK(SPI_CMD_REG(PSRAM_SPI_3), SPI_FLASH_READ_M); + uint32_t spi_status; + while (1) { + spi_status = READ_PERI_REG(SPI_EXT2_REG(PSRAM_SPI_3)); + if (spi_status != 0 && spi_status != 1) { + DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG, DPORT_SPI_CLK_EN_2); + break; + } + } + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: + psram_spi_init(PSRAM_SPI_1, mode); + CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_CS_HOLD); + gpio_matrix_out(PSRAM_CS_IO, SPICS1_OUT_IDX, 0, 0); + /* We need to delay CLK to the PSRAM with respect to the clock signal as output by the SPI peripheral. + We do this by routing it signal to signal 224/225, which are used as a loopback; the extra run through + the GPIO matrix causes the delay. We use GPIO20 (which is not in any package but has pad logic in + silicon) as a temporary pad for this. So the signal path is: + SPI CLK --> GPIO28 --> signal224(in then out) --> internal GPIO29 --> signal225(in then out) --> GPIO17(PSRAM CLK) + */ + gpio_matrix_out(PSRAM_INTERNAL_IO_28, SPICLK_OUT_IDX, 0, 0); + gpio_matrix_in(PSRAM_INTERNAL_IO_28, SIG_IN_FUNC224_IDX, 0); + gpio_matrix_out(PSRAM_INTERNAL_IO_29, SIG_IN_FUNC224_IDX, 0, 0); + gpio_matrix_in(PSRAM_INTERNAL_IO_29, SIG_IN_FUNC225_IDX, 0); + gpio_matrix_out(PSRAM_CLK_IO, SIG_IN_FUNC225_IDX, 0, 0); + break; + } + CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_CS_SETUP_M); + psram_gpio_config(mode); + WRITE_PERI_REG(GPIO_ENABLE_W1TS_REG, BIT(PSRAM_CS_IO)| BIT(PSRAM_CLK_IO)); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[PSRAM_CS_IO], PIN_FUNC_GPIO); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[PSRAM_CLK_IO], PIN_FUNC_GPIO); + uint32_t id; + psram_read_id(&id); + if (((id >> PSRAM_MFG_ID_S) & PSRAM_MFG_ID_M) != PSRAM_MFG_ID_V) { + return ESP_FAIL; + } + psram_enable_qio_mode(PSRAM_SPI_1); + + psram_cache_init(mode, vaddrmode); + return ESP_OK; +} + +//register initialization for sram cache params and r/w commands +static void IRAM_ATTR psram_cache_init(psram_cache_mode_t psram_cache_mode, psram_vaddr_mode_t vaddrmode) +{ + CLEAR_PERI_REG_MASK(SPI_CLOCK_REG(0), SPI_CLK_EQU_SYSCLK_M); + SET_PERI_REG_BITS(SPI_CLOCK_REG(0), SPI_CLKDIV_PRE_V, 0, SPI_CLKDIV_PRE_S); + SET_PERI_REG_BITS(SPI_CLOCK_REG(0), SPI_CLKCNT_N, 1, SPI_CLKCNT_N_S); + SET_PERI_REG_BITS(SPI_CLOCK_REG(0), SPI_CLKCNT_H, 0, SPI_CLKCNT_H_S); + SET_PERI_REG_BITS(SPI_CLOCK_REG(0), SPI_CLKCNT_L, 1, SPI_CLKCNT_L_S); + + switch (psram_cache_mode) { + case PSRAM_CACHE_F80M_S80M: + CLEAR_PERI_REG_MASK(SPI_DATE_REG(0), BIT(31)); //flash 1 div clk,80+40; + CLEAR_PERI_REG_MASK(SPI_DATE_REG(0), BIT(30)); //pre clk div , ONLY IF SPI/SRAM@ DIFFERENT SPEED,JUST FOR SPI0. FLASH DIV 2+SRAM DIV4 + WRITE_PERI_REG(SPI_CLOCK_REG(0), SPI_CLK_EQU_SYSCLK_M); //SET 1DIV CLOCK AND RESET OTHER PARAMS + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_USR_RD_SRAM_DUMMY_M); //enable cache read dummy + SET_PERI_REG_BITS(SPI_CACHE_SCTRL_REG(0), SPI_SRAM_DUMMY_CYCLELEN_V, PSRAM_FAST_READ_DUMMY + extra_dummy, + SPI_SRAM_DUMMY_CYCLELEN_S); //dummy, psram cache : 40m--+1dummy,80m--+2dummy + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_CACHE_SRAM_USR_RCMD_M); //enable user mode for cache read command + break; + case PSRAM_CACHE_F80M_S40M: + SET_PERI_REG_MASK(SPI_DATE_REG(0), BIT(31)); //flash 1 div clk + CLEAR_PERI_REG_MASK(SPI_DATE_REG(0), BIT(30)); //pre clk div , ONLY IF SPI/SRAM@ DIFFERENT SPEED,JUST FOR SPI0. + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_USR_RD_SRAM_DUMMY_M); //enable cache read dummy + SET_PERI_REG_BITS(SPI_CACHE_SCTRL_REG(0), SPI_SRAM_DUMMY_CYCLELEN_V, PSRAM_FAST_READ_DUMMY + extra_dummy, + SPI_SRAM_DUMMY_CYCLELEN_S); //dummy, psram cache : 40m--+1dummy,80m--+2dummy + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_CACHE_SRAM_USR_RCMD_M); //enable user mode for cache read command + break; + case PSRAM_CACHE_F40M_S40M: + default: + CLEAR_PERI_REG_MASK(SPI_DATE_REG(0), BIT(31)); //flash 1 div clk + CLEAR_PERI_REG_MASK(SPI_DATE_REG(0), BIT(30)); //pre clk div + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_USR_RD_SRAM_DUMMY_M); //enable cache read dummy + SET_PERI_REG_BITS(SPI_CACHE_SCTRL_REG(0), SPI_SRAM_DUMMY_CYCLELEN_V, PSRAM_FAST_READ_DUMMY + extra_dummy, + SPI_SRAM_DUMMY_CYCLELEN_S); //dummy, psram cache : 40m--+1dummy,80m--+2dummy + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_CACHE_SRAM_USR_RCMD_M); //enable user mode for cache read command + break; + } + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_CACHE_SRAM_USR_WCMD_M); // cache write command enable + SET_PERI_REG_BITS(SPI_CACHE_SCTRL_REG(0), SPI_SRAM_ADDR_BITLEN_V, 23, SPI_SRAM_ADDR_BITLEN_S); //write address for cache command. + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_USR_SRAM_QIO_M); //enable qio mode for cache command + CLEAR_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0), SPI_USR_SRAM_DIO_M); //disable dio mode for cache command + + + //config sram cache r/w command + switch (psram_cache_mode) { + case PSRAM_CACHE_F80M_S80M: //in this mode , no delay is needed + SET_PERI_REG_BITS(SPI_SRAM_DWR_CMD_REG(0), SPI_CACHE_SRAM_USR_WR_CMD_BITLEN, 7, + SPI_CACHE_SRAM_USR_WR_CMD_BITLEN_S); + SET_PERI_REG_BITS(SPI_SRAM_DWR_CMD_REG(0), SPI_CACHE_SRAM_USR_WR_CMD_VALUE, PSRAM_QUAD_WRITE, + SPI_CACHE_SRAM_USR_WR_CMD_VALUE_S); //0x38 + SET_PERI_REG_BITS(SPI_SRAM_DRD_CMD_REG(0), SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_V, 7, + SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_S); + SET_PERI_REG_BITS(SPI_SRAM_DRD_CMD_REG(0), SPI_CACHE_SRAM_USR_RD_CMD_VALUE_V, PSRAM_FAST_READ, + SPI_CACHE_SRAM_USR_RD_CMD_VALUE_S); //0x0b + break; + case PSRAM_CACHE_F80M_S40M: //is sram is @40M, need 2 cycles of delay + case PSRAM_CACHE_F40M_S40M: + default: + SET_PERI_REG_BITS(SPI_SRAM_DRD_CMD_REG(0), SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_V, 15, + SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_S); //read command length, 2 bytes(1byte for delay),sending in qio mode in cache + SET_PERI_REG_BITS(SPI_SRAM_DRD_CMD_REG(0), SPI_CACHE_SRAM_USR_RD_CMD_VALUE_V, ((PSRAM_FAST_READ) << 8), + SPI_CACHE_SRAM_USR_RD_CMD_VALUE_S); //0x0b, read command value,(0x00 for delay,0x0b for cmd) + SET_PERI_REG_BITS(SPI_SRAM_DWR_CMD_REG(0), SPI_CACHE_SRAM_USR_WR_CMD_BITLEN, 15, + SPI_CACHE_SRAM_USR_WR_CMD_BITLEN_S); //write command length,2 bytes(1byte for delay,send in qio mode in cache) + SET_PERI_REG_BITS(SPI_SRAM_DWR_CMD_REG(0), SPI_CACHE_SRAM_USR_WR_CMD_VALUE, ((PSRAM_QUAD_WRITE) << 8), + SPI_CACHE_SRAM_USR_WR_CMD_VALUE_S); //0x38, write command value,(0x00 for delay) + break; + } + + DPORT_CLEAR_PERI_REG_MASK(DPORT_PRO_CACHE_CTRL_REG, DPORT_PRO_DRAM_HL|DPORT_PRO_DRAM_SPLIT); + DPORT_CLEAR_PERI_REG_MASK(DPORT_APP_CACHE_CTRL_REG, DPORT_APP_DRAM_HL|DPORT_APP_DRAM_SPLIT); + if (vaddrmode == PSRAM_VADDR_MODE_LOWHIGH) { + DPORT_SET_PERI_REG_MASK(DPORT_PRO_CACHE_CTRL_REG, DPORT_PRO_DRAM_HL); + DPORT_SET_PERI_REG_MASK(DPORT_APP_CACHE_CTRL_REG, DPORT_APP_DRAM_HL); + } else if (vaddrmode == PSRAM_VADDR_MODE_EVENODD) { + DPORT_SET_PERI_REG_MASK(DPORT_PRO_CACHE_CTRL_REG, DPORT_PRO_DRAM_SPLIT); + DPORT_SET_PERI_REG_MASK(DPORT_APP_CACHE_CTRL_REG, DPORT_APP_DRAM_SPLIT); + } + + DPORT_CLEAR_PERI_REG_MASK(DPORT_PRO_CACHE_CTRL1_REG, DPORT_PRO_CACHE_MASK_DRAM1|DPORT_PRO_CACHE_MASK_OPSDRAM); //use Dram1 to visit ext sram. + //cache page mode : 1 -->16k 4 -->2k 0-->32k,(accord with the settings in cache_sram_mmu_set) + DPORT_SET_PERI_REG_BITS(DPORT_PRO_CACHE_CTRL1_REG, DPORT_PRO_CMMU_SRAM_PAGE_MODE, 0, DPORT_PRO_CMMU_SRAM_PAGE_MODE_S); + DPORT_CLEAR_PERI_REG_MASK(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MASK_DRAM1|DPORT_APP_CACHE_MASK_OPSDRAM); //use Dram1 to visit ext sram. + //cache page mode : 1 -->16k 4 -->2k 0-->32k,(accord with the settings in cache_sram_mmu_set) + DPORT_SET_PERI_REG_BITS(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CMMU_SRAM_PAGE_MODE, 0, DPORT_APP_CMMU_SRAM_PAGE_MODE_S); + + CLEAR_PERI_REG_MASK(SPI_PIN_REG(0), SPI_CS1_DIS_M); //ENABLE SPI0 CS1 TO PSRAM(CS0--FLASH; CS1--SRAM) + +} + +#endif // CONFIG_SPIRAM_SUPPORT diff --git a/cpu/esp32/esp-idf/esp_funcs.c b/cpu/esp32/esp-idf/esp_funcs.c new file mode 100644 index 0000000000000..131c4f9dfb5c5 --- /dev/null +++ b/cpu/esp32/esp-idf/esp_funcs.c @@ -0,0 +1,250 @@ +// 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. + +/* PLEASE NOTE: This file is a collection of required functions from + different files in ESP-IDF */ + +#define ENABLE_DEBUG 0 +#include "debug.h" +#include "common.h" + +#include +#include + +#include "esp_attr.h" +#include "sdkconfig.h" + +#include "driver/periph_ctrl.h" +#include "esp32/esp_spiram.h" +#include "freertos/FreeRTOS.h" +#include "heap/esp_heap_caps_init.h" +#include "log/esp_log.h" +#include "rom/rtc.h" +#include "rom/cache.h" +#include "rom/efuse.h" +#include "rom/uart.h" +#include "soc/cpu.h" +#include "soc/efuse_reg.h" +#include "soc/gpio_reg.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/timer_group_reg.h" +#include "soc/timer_group_struct.h" +#include "xtensa/core-macros.h" +#include "xtensa/xtensa_api.h" + +#include "syscalls.h" + +/* This function is not part on newlib API, it is defined in libc/stdio/local.h + * There is no nice way to get __cleanup member populated while avoiding __sinit, + * so extern declaration is used here. + */ +extern void _cleanup_r(struct _reent* r); + +/** + * This is the replacement for newlib's _REENT_INIT_PTR and __sinit. + * The problem with __sinit is that it allocates three FILE structures + * (stdin, stdout, stderr). Having individual standard streams for each task + * is a bit too much on a small embedded system. So we point streams + * to the streams of the global struct _reent, which are initialized in + * startup code. + */ +void IRAM_ATTR esp_reent_init(struct _reent* r) +{ + memset(r, 0, sizeof(*r)); + r->_stdout = _GLOBAL_REENT->_stdout; + r->_stderr = _GLOBAL_REENT->_stderr; + r->_stdin = _GLOBAL_REENT->_stdin; + r->__cleanup = &_cleanup_r; + r->__sdidinit = 1; + r->__sglue._next = NULL; + r->__sglue._niobs = 0; + r->__sglue._iobs = NULL; + r->_current_locale = "C"; +} + +/* source: /path/to/esp-idf/components/esp32/esp_panic.c */ +void IRAM_ATTR esp_panic_wdt_stop (void) +{ + WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, RTC_CNTL_WDT_WKEY_VALUE); + WRITE_PERI_REG(RTC_CNTL_WDTFEED_REG, 1); + REG_SET_FIELD(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_STG0, RTC_WDT_STG_SEL_OFF); + REG_CLR_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_EN); + WRITE_PERI_REG(RTC_CNTL_WDTWPROTECT_REG, 0); +} + +/* source: /path/to/esp-idf/components/esp32/esp_panic.c */ +void _esp_error_check_failed(esp_err_t rc, const char *file, int line, + const char *function, const char *expression) +{ + ets_printf("ESP_ERROR_CHECK failed: esp_err_t 0x%x at 0x%08x\n", + rc, (intptr_t)__builtin_return_address(0) - 3); + #if 0 /* TODO */ + if (spi_flash_cache_enabled()) { /* strings may be in flash cache */ + ets_printf("file: \"%s\" line %d\nfunc: %s\nexpression: %s\n", + file, line, function, expression); + } + invoke_abort(); + #endif + exit(1); + + while (1) {} +} + +/* source: /path/to/esp-idf/component/log/log.c */ +uint32_t IRAM_ATTR esp_log_timestamp(void) +{ + return system_get_time() / USEC_PER_MSEC; +} + +/* source: /path/to/esp-idf/component/log/log.c */ +void IRAM_ATTR esp_log_write(esp_log_level_t level, + const char* tag, const char* format, ...) +{ + (void)level; + + va_list list; + va_start(list, format); + ets_printf(format, list); + va_end(list); +} + +static bool _spi_ram_initialized = false; + +/* source: /path/to/esp-idf/component/esp32/cpu_start.c */ +void spi_ram_init(void) +{ + _spi_ram_initialized = false; + + #if CONFIG_SPIRAM_SUPPORT + esp_spiram_init_cache(); + if (esp_spiram_init() == ESP_OK) { + _spi_ram_initialized = true; + } + else { + ets_printf("Failed to init external SPI RAM\n"); + _spi_ram_initialized = false; + } + #else + ets_printf("External SPI RAM functions not enabled\n"); + #endif +} + +/* source: /path/to/esp-idf/component/esp32/cpu_start.c */ +void spi_ram_heap_init(void) +{ + #if CONFIG_SPIRAM_SUPPORT + + #if CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC + esp_err_t r=esp_spiram_add_to_heapalloc(); + if (r != ESP_OK) { + ets_printf("External SPI RAM could not be added to heap!\n"); + abort(); + } + + #if CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL + r=esp_spiram_reserve_dma_pool(CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL); + if (r != ESP_OK) { + ets_printf("Could not reserve internal/DMA pool!\n"); + abort(); + } + #endif /* CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL */ + + #if CONFIG_SPIRAM_USE_MALLOC + heap_caps_malloc_extmem_enable(CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL); + #endif /* CONFIG_SPIRAM_USE_MALLOC */ + + #endif /* CONFIG_SPIRAM_USE_CAPS_ALLOC || CONFIG_SPIRAM_USE_MALLOC */ + + #else /* CONFIG_SPIRAM_SUPPORT */ + ets_printf("External SPI RAM functions not enabled\n"); + #endif /* CONFIG_SPIRAM_SUPPORT */ +} + +/* + * If CONFIG_WIFI_LWIP_ALLOCATION_FROM_SPIRAM_FIRST is enabled. Prefer to + * allocate a chunk of memory in SPIRAM firstly. If failed, try to allocate + * it in internal memory then. + * + * source: /path/to/esp-idf/component/esp32/wifi_internal.c + */ +IRAM_ATTR void *wifi_malloc( size_t size ) +{ +#if CONFIG_WIFI_LWIP_ALLOCATION_FROM_SPIRAM_FIRST + return heap_caps_malloc_prefer(size, 2, + MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, + MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); +#else + return malloc(size); +#endif +} + +/* + * If CONFIG_WIFI_LWIP_ALLOCATION_FROM_SPIRAM_FIRST is enabled. Prefer to + * allocate a chunk of memory in SPIRAM firstly. If failed, try to allocate + * it in internal memory then. + * + * source: /path/to/esp-idf/component/esp32/wifi_internal.c + */ +IRAM_ATTR void *wifi_calloc( size_t n, size_t size ) +{ +#if CONFIG_WIFI_LWIP_ALLOCATION_FROM_SPIRAM_FIRST + return heap_caps_calloc_prefer(n, size, 2, + MALLOC_CAP_DEFAULT|MALLOC_CAP_SPIRAM, + MALLOC_CAP_DEFAULT|MALLOC_CAP_INTERNAL); +#else + return calloc(n, size); +#endif +} + +static const char* TAG = "system_api"; + +/* + * source: /path/to/esp-idf/component/esp32/system_api.c + */ +esp_err_t esp_efuse_mac_get_default(uint8_t* mac) +{ + uint32_t mac_low; + uint32_t mac_high; + uint8_t efuse_crc; + uint8_t calc_crc; + + mac_low = REG_READ(EFUSE_BLK0_RDATA1_REG); + mac_high = REG_READ(EFUSE_BLK0_RDATA2_REG); + + mac[0] = mac_high >> 8; + mac[1] = mac_high; + mac[2] = mac_low >> 24; + mac[3] = mac_low >> 16; + mac[4] = mac_low >> 8; + mac[5] = mac_low; + + efuse_crc = mac_high >> 16; + + calc_crc = esp_crc8(mac, 6); + + if (efuse_crc != calc_crc) { + // Small range of MAC addresses are accepted even if CRC is invalid. + // These addresses are reserved for Espressif internal use. + if ((mac_high & 0xFFFF) == 0x18fe) { + if ((mac_low >= 0x346a85c7) && (mac_low <= 0x346a85f8)) { + return ESP_OK; + } + } else { + ESP_LOGE(TAG, "Base MAC address from BLK0 of EFUSE CRC error, efuse_crc = 0x%02x; calc_crc = 0x%02x", efuse_crc, calc_crc); + abort(); + } + } + return ESP_OK; +} diff --git a/cpu/esp32/esp-idf/heap/Makefile b/cpu/esp32/esp-idf/heap/Makefile new file mode 100644 index 0000000000000..56a627064ba31 --- /dev/null +++ b/cpu/esp32/esp-idf/heap/Makefile @@ -0,0 +1,6 @@ +MODULE=esp_idf_heap + +include $(RIOTBASE)/Makefile.base + +INCLUDES += -I$(RIOTCPU)/$(CPU)/esp-idf/include/log +INCLUDES += -I$(SDK_DIR)/components/heap diff --git a/cpu/esp32/esp-idf/heap/heap_caps.c b/cpu/esp32/esp-idf/heap/heap_caps.c new file mode 100644 index 0000000000000..7d15a71c843e2 --- /dev/null +++ b/cpu/esp32/esp-idf/heap/heap_caps.c @@ -0,0 +1,450 @@ +// 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 +#include +#include +#include +#include +#include +#include "esp_attr.h" +#include "esp_heap_caps.h" +#include "multi_heap.h" +#include "esp_log.h" +#include "heap_private.h" + +/* +This file, combined with a region allocator that supports multiple heaps, solves the problem that the ESP32 has RAM +that's slightly heterogeneous. Some RAM can be byte-accessed, some allows only 32-bit accesses, some can execute memory, +some can be remapped by the MMU to only be accessed by a certain PID etc. In order to allow the most flexible memory +allocation possible, this code makes it possible to request memory that has certain capabilities. The code will then use +its knowledge of how the memory is configured along with a priority scheme to allocate that memory in the most sane way +possible. This should optimize the amount of RAM accessible to the code without hardwiring addresses. +*/ + +/* + This takes a memory chunk in a region that can be addressed as both DRAM as well as IRAM. It will convert it to + IRAM in such a way that it can be later freed. It assumes both the address as wel as the length to be word-aligned. + It returns a region that's 1 word smaller than the region given because it stores the original Dram address there. + + In theory, we can also make this work by prepending a struct that looks similar to the block link struct used by the + heap allocator itself, which will allow inspection tools relying on any block returned from any sort of malloc to + have such a block in front of it, work. We may do this later, if/when there is demand for it. For now, a simple + pointer is used. +*/ +IRAM_ATTR static void *dram_alloc_to_iram_addr(void *addr, size_t len) +{ + uint32_t dstart = (int)addr; //First word + uint32_t dend = ((int)addr) + len - 4; //Last word + assert(dstart >= SOC_DIRAM_DRAM_LOW); + assert(dend <= SOC_DIRAM_DRAM_HIGH); + assert((dstart & 3) == 0); + assert((dend & 3) == 0); + uint32_t istart = SOC_DIRAM_IRAM_LOW + (SOC_DIRAM_DRAM_HIGH - dend); + uint32_t *iptr = (uint32_t *)istart; + *iptr = dstart; + return (void *)(iptr + 1); +} + +/* return all possible capabilities (across all priorities) for a given heap */ +inline static uint32_t get_all_caps(const heap_t *heap) +{ + if (heap->heap == NULL) { + return 0; + } + uint32_t all_caps = 0; + for (int prio = 0; prio < SOC_MEMORY_TYPE_NO_PRIOS; prio++) { + all_caps |= heap->caps[prio]; + } + return all_caps; +} + +bool heap_caps_match(const heap_t *heap, uint32_t caps) +{ + return heap->heap != NULL && ((get_all_caps(heap) & caps) == caps); +} + +/* +Routine to allocate a bit of memory with certain capabilities. caps is a bitfield of MALLOC_CAP_* bits. +*/ +IRAM_ATTR void *heap_caps_malloc( size_t size, uint32_t caps ) +{ + void *ret = NULL; + + if (caps & MALLOC_CAP_EXEC) { + //MALLOC_CAP_EXEC forces an alloc from IRAM. There is a region which has both this as well as the following + //caps, but the following caps are not possible for IRAM. Thus, the combination is impossible and we return + //NULL directly, even although our heap capabilities (based on soc_memory_tags & soc_memory_regions) would + //indicate there is a tag for this. + if ((caps & MALLOC_CAP_8BIT) || (caps & MALLOC_CAP_DMA)) { + return NULL; + } + //If any, EXEC memory should be 32-bit aligned, so round up to the next multiple of 4. + size = (size + 3) & (~3); + } + for (int prio = 0; prio < SOC_MEMORY_TYPE_NO_PRIOS; prio++) { + //Iterate over heaps and check capabilities at this priority + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap->heap == NULL) { + continue; + } + if ((heap->caps[prio] & caps) != 0) { + //Heap has at least one of the caps requested. If caps has other bits set that this prio + //doesn't cover, see if they're available in other prios. + if ((get_all_caps(heap) & caps) == caps) { + //This heap can satisfy all the requested capabilities. See if we can grab some memory using it. + if ((caps & MALLOC_CAP_EXEC) && heap->start >= SOC_DIRAM_DRAM_LOW && heap->start < SOC_DIRAM_DRAM_HIGH) { + //This is special, insofar that what we're going to get back is a DRAM address. If so, + //we need to 'invert' it (lowest address in DRAM == highest address in IRAM and vice-versa) and + //add a pointer to the DRAM equivalent before the address we're going to return. + ret = multi_heap_malloc(heap->heap, size + 4); + if (ret != NULL) { + return dram_alloc_to_iram_addr(ret, size + 4); + } + } else { + //Just try to alloc, nothing special. + ret = multi_heap_malloc(heap->heap, size); + if (ret != NULL) { + return ret; + } + } + } + } + } + } + //Nothing usable found. + return NULL; +} + + +#define MALLOC_DISABLE_EXTERNAL_ALLOCS -1 +//Dual-use: -1 (=MALLOC_DISABLE_EXTERNAL_ALLOCS) disables allocations in external memory, >=0 sets the limit for allocations preferring internal memory. +static int malloc_alwaysinternal_limit=MALLOC_DISABLE_EXTERNAL_ALLOCS; + +void heap_caps_malloc_extmem_enable(size_t limit) +{ + malloc_alwaysinternal_limit=limit; +} + +/* + Default memory allocation implementation. Should return standard 8-bit memory. malloc() essentially resolves to this function. +*/ +IRAM_ATTR void *heap_caps_malloc_default( size_t size ) +{ + if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) { + return heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL); + } else { + void *r; + if ((int)size <= malloc_alwaysinternal_limit) { + r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL ); + } else { + r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM ); + } + if (r==NULL) { + //try again while being less picky + r=heap_caps_malloc( size, MALLOC_CAP_DEFAULT ); + } + return r; + } +} + +/* + Same for realloc() + Note: keep the logic in here the same as in heap_caps_malloc_default (or merge the two as soon as this gets more complex...) + */ +IRAM_ATTR void *heap_caps_realloc_default( void *ptr, size_t size ) +{ + if (malloc_alwaysinternal_limit==MALLOC_DISABLE_EXTERNAL_ALLOCS) { + return heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL ); + } else { + void *r; + if ((int)size <= malloc_alwaysinternal_limit) { + r=heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL ); + } else { + r=heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM ); + } + if (r==NULL && size>0) { + //We needed to allocate memory, but we didn't. Try again while being less picky. + r=heap_caps_realloc( ptr, size, MALLOC_CAP_DEFAULT ); + } + return r; + } +} + +/* + Memory allocation as preference in decreasing order. + */ +IRAM_ATTR void *heap_caps_malloc_prefer( size_t size, size_t num, ... ) +{ + va_list argp; + va_start( argp, num ); + void *r = NULL; + while (num--) { + uint32_t caps = va_arg( argp, uint32_t ); + r = heap_caps_malloc( size, caps ); + if (r != NULL) { + break; + } + } + va_end( argp ); + return r; +} + +/* + Memory reallocation as preference in decreasing order. + */ +IRAM_ATTR void *heap_caps_realloc_prefer( void *ptr, size_t size, size_t num, ... ) +{ + va_list argp; + va_start( argp, num ); + void *r = NULL; + while (num--) { + uint32_t caps = va_arg( argp, uint32_t ); + r = heap_caps_realloc( ptr, size, caps ); + if (r != NULL || size == 0) { + break; + } + } + va_end( argp ); + return r; +} + +/* + Memory callocation as preference in decreasing order. + */ +IRAM_ATTR void *heap_caps_calloc_prefer( size_t n, size_t size, size_t num, ... ) +{ + va_list argp; + va_start( argp, num ); + void *r = NULL; + while (num--) { + uint32_t caps = va_arg( argp, uint32_t ); + r = heap_caps_calloc( n, size, caps ); + if (r != NULL) break; + } + va_end( argp ); + return r; +} + +/* Find the heap which belongs to ptr, or return NULL if it's + not in any heap. + + (This confirms if ptr is inside the heap's region, doesn't confirm if 'ptr' + is an allocated block or is some other random address inside the heap.) +*/ +IRAM_ATTR static heap_t *find_containing_heap(void *ptr ) +{ + intptr_t p = (intptr_t)ptr; + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap->heap != NULL && p >= heap->start && p < heap->end) { + return heap; + } + } + return NULL; +} + +IRAM_ATTR void heap_caps_free( void *ptr) +{ + intptr_t p = (intptr_t)ptr; + + if (ptr == NULL) { + return; + } + + if ((p >= SOC_DIRAM_IRAM_LOW) && (p <= SOC_DIRAM_IRAM_HIGH)) { + //Memory allocated here is actually allocated in the DRAM alias region and + //cannot be de-allocated as usual. dram_alloc_to_iram_addr stores a pointer to + //the equivalent DRAM address, though; free that. + uint32_t *dramAddrPtr = (uint32_t *)ptr; + ptr = (void *)dramAddrPtr[-1]; + } + + heap_t *heap = find_containing_heap(ptr); + assert(heap != NULL && "free() target pointer is outside heap areas"); + multi_heap_free(heap->heap, ptr); +} + +IRAM_ATTR void *heap_caps_realloc( void *ptr, size_t size, int caps) +{ + if (ptr == NULL) { + return heap_caps_malloc(size, caps); + } + + if (size == 0) { + heap_caps_free(ptr); + return NULL; + } + + heap_t *heap = find_containing_heap(ptr); + + assert(heap != NULL && "realloc() pointer is outside heap areas"); + + // are the existing heap's capabilities compatible with the + // requested ones? + bool compatible_caps = (caps & (int)get_all_caps(heap)) == caps; + + if (compatible_caps) { + // try to reallocate this memory within the same heap + // (which will resize the block if it can) + void *r = multi_heap_realloc(heap->heap, ptr, size); + if (r != NULL) { + return r; + } + } + + // if we couldn't do that, try to see if we can reallocate + // in a different heap with requested capabilities. + void *new_p = heap_caps_malloc(size, caps); + if (new_p != NULL) { + size_t old_size = multi_heap_get_allocated_size(heap->heap, ptr); + assert(old_size > 0); + memcpy(new_p, ptr, MIN(size, old_size)); + heap_caps_free(ptr); + return new_p; + } + return NULL; +} + +IRAM_ATTR void *heap_caps_calloc( size_t n, size_t size, uint32_t caps) +{ + void *r; + r = heap_caps_malloc(n*size, caps); + if (r != NULL) { + bzero(r, n*size); + } + return r; +} + +size_t heap_caps_get_free_size( uint32_t caps ) +{ + size_t ret = 0; + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap_caps_match(heap, caps)) { + ret += multi_heap_free_size(heap->heap); + } + } + return ret; +} + +size_t heap_caps_get_minimum_free_size( uint32_t caps ) +{ + size_t ret = 0; + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap_caps_match(heap, caps)) { + ret += multi_heap_minimum_free_size(heap->heap); + } + } + return ret; +} + +size_t heap_caps_get_largest_free_block( uint32_t caps ) +{ + multi_heap_info_t info; + heap_caps_get_info(&info, caps); + return info.largest_free_block; +} + +void heap_caps_get_info( multi_heap_info_t *info, uint32_t caps ) +{ + bzero(info, sizeof(multi_heap_info_t)); + + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap_caps_match(heap, caps)) { + multi_heap_info_t hinfo; + multi_heap_get_info(heap->heap, &hinfo); + + info->total_free_bytes += hinfo.total_free_bytes; + info->total_allocated_bytes += hinfo.total_allocated_bytes; + info->largest_free_block = MAX(info->largest_free_block, + hinfo.largest_free_block); + info->minimum_free_bytes += hinfo.minimum_free_bytes; + info->allocated_blocks += hinfo.allocated_blocks; + info->free_blocks += hinfo.free_blocks; + info->total_blocks += hinfo.total_blocks; + } + } +} + +void heap_caps_print_heap_info( uint32_t caps ) +{ + multi_heap_info_t info; + printf("Heap summary for capabilities 0x%08X:\n", caps); + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap_caps_match(heap, caps)) { + multi_heap_get_info(heap->heap, &info); + + printf(" At 0x%08x len %d free %d allocated %d min_free %d\n", + heap->start, heap->end - heap->start, info.total_free_bytes, info.total_allocated_bytes, info.minimum_free_bytes); + printf(" largest_free_block %d alloc_blocks %d free_blocks %d total_blocks %d\n", + info.largest_free_block, info.allocated_blocks, + info.free_blocks, info.total_blocks); + } + } + printf(" Totals:\n"); + heap_caps_get_info(&info, caps); + + printf(" free %d allocated %d min_free %d largest_free_block %d\n", info.total_free_bytes, info.total_allocated_bytes, info.minimum_free_bytes, info.largest_free_block); +} + +bool heap_caps_check_integrity(uint32_t caps, bool print_errors) +{ + bool all_heaps = caps & MALLOC_CAP_INVALID; + bool valid = true; + + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap->heap != NULL + && (all_heaps || (get_all_caps(heap) & caps) == caps)) { + valid = multi_heap_check(heap->heap, print_errors) && valid; + } + } + + return valid; +} + +bool heap_caps_check_integrity_all(bool print_errors) +{ + return heap_caps_check_integrity(MALLOC_CAP_INVALID, print_errors); +} + +bool heap_caps_check_integrity_addr(intptr_t addr, bool print_errors) +{ + heap_t *heap = find_containing_heap((void *)addr); + if (heap == NULL) { + return false; + } + return multi_heap_check(heap->heap, print_errors); +} + +void heap_caps_dump(uint32_t caps) +{ + bool all_heaps = caps & MALLOC_CAP_INVALID; + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + if (heap->heap != NULL + && (all_heaps || (get_all_caps(heap) & caps) == caps)) { + multi_heap_dump(heap->heap); + } + } +} + +void heap_caps_dump_all(void) +{ + heap_caps_dump(MALLOC_CAP_INVALID); +} diff --git a/cpu/esp32/esp-idf/heap/heap_caps_init.c b/cpu/esp32/esp-idf/heap/heap_caps_init.c new file mode 100644 index 0000000000000..d6c3c03405183 --- /dev/null +++ b/cpu/esp32/esp-idf/heap/heap_caps_init.c @@ -0,0 +1,294 @@ +// 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 "heap_private.h" +#include +#include +#include + +#include "esp_log.h" +#include "multi_heap.h" +#include "esp_heap_caps_init.h" +#include "soc/soc_memory_layout.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +static const char *TAG = "heap_init"; + +/* Linked-list of registered heaps */ +struct registered_heap_ll registered_heaps; + +static void register_heap(heap_t *region) +{ + region->heap = multi_heap_register((void *)region->start, region->end - region->start); + if (region->heap != NULL) { + ESP_EARLY_LOGD(TAG, "New heap initialised at %p", region->heap); + } +} + +void heap_caps_enable_nonos_stack_heaps(void) +{ + heap_t *heap; + SLIST_FOREACH(heap, ®istered_heaps, next) { + // Assume any not-yet-registered heap is + // a nonos-stack heap + if (heap->heap == NULL) { + register_heap(heap); + if (heap->heap != NULL) { + multi_heap_set_lock(heap->heap, &heap->heap_mux); + } + } + } +} + +//Modify regions array to disable the given range of memory. +static void disable_mem_region(soc_memory_region_t *regions, intptr_t from, intptr_t to) +{ + //Align from and to on word boundaries + from = from & ~3; + to = (to + 3) & ~3; + + for (unsigned i = 0; i < soc_memory_region_count; i++) { + soc_memory_region_t *region = ®ions[i]; + + intptr_t regStart = region->start; + intptr_t regEnd = region->start + region->size; + if (regStart >= from && regEnd <= to) { + //Entire region falls in the range. Disable entirely. + regions[i].type = -1; + } else if (regStart >= from && regEnd > to && regStart < to) { + //Start of the region falls in the range. Modify address/len. + intptr_t overlap = to - regStart; + region->start += overlap; + region->size -= overlap; + if (region->iram_address) { + region->iram_address += overlap; + } + } else if (regStart < from && regEnd > from && regEnd <= to) { + //End of the region falls in the range. Modify length. + region->size -= regEnd - from; + } else if (regStart < from && regEnd > to) { + //Range punches a hole in the region! We do not support this. + ESP_EARLY_LOGE(TAG, "region %d: hole punching is not supported!", i); + regions->type = -1; //Just disable memory region. That'll teach them! + } + } +} + +/* +Warning: These variables are assumed to have the start and end of the data and iram +area used statically by the program, respectively. These variables are defined in the ld +file. +*/ +extern int _data_start, _heap_start, _init_start, _iram_text_end; + +/* +Initialize the heap allocator. We pass it a bunch of region descriptors, but we need to modify those first to accommodate for +the data as loaded by the bootloader. +ToDo: The regions are different when stuff like trace memory, BT, ... is used. Modify the regions struct on the fly for this. +Same with loading of apps. Same with using SPI RAM. +*/ +void heap_caps_init(void) +{ + /* Copy the soc_memory_regions data to the stack, so we can + manipulate it. */ + soc_memory_region_t regions[soc_memory_region_count]; + memcpy(regions, soc_memory_regions, sizeof(soc_memory_region_t)*soc_memory_region_count); + + //Disable the bits of memory where this code is loaded. + disable_mem_region(regions, (intptr_t)&_data_start, (intptr_t)&_heap_start); //DRAM used by bss/data static variables + disable_mem_region(regions, (intptr_t)&_init_start, (intptr_t)&_iram_text_end); //IRAM used by code + + // Disable all regions reserved on this SoC + for (unsigned i = 0; i < soc_reserved_region_count; i++) { + disable_mem_region(regions, soc_reserved_regions[i].start, + soc_reserved_regions[i].end); + } + + //The heap allocator will treat every region given to it as separate. In order to get bigger ranges of contiguous memory, + //it's useful to coalesce adjacent regions that have the same type. + + for (unsigned i = 1; i < soc_memory_region_count; i++) { + soc_memory_region_t *a = ®ions[i - 1]; + soc_memory_region_t *b = ®ions[i]; + if (b->start == (intptr_t)(a->start + a->size) && b->type == a->type ) { + a->type = -1; + b->start = a->start; + b->size += a->size; + } + } + + /* Count the heaps left after merging */ + size_t num_heaps = 0; + for (unsigned i = 0; i < soc_memory_region_count; i++) { + if (regions[i].type != 0xffffffff) { + num_heaps++; + } + } + + /* Start by allocating the registered heap data on the stack. + + Once we have a heap to copy it to, we will copy it to a heap buffer. + */ + heap_t temp_heaps[num_heaps]; + size_t heap_idx = 0; + + ESP_EARLY_LOGI(TAG, "Initializing. RAM available for dynamic allocation:"); + for (unsigned i = 0; i < soc_memory_region_count; i++) { + soc_memory_region_t *region = ®ions[i]; + const soc_memory_type_desc_t *type = &soc_memory_types[region->type]; + heap_t *heap = &temp_heaps[heap_idx]; + if (region->type == 0xffffffff) { + continue; + } + heap_idx++; + assert(heap_idx <= num_heaps); + + memcpy(heap->caps, type->caps, sizeof(heap->caps)); + heap->start = region->start; + heap->end = region->start + region->size; + vPortCPUInitializeMutex(&heap->heap_mux); + if (type->startup_stack) { + /* Will be registered when OS scheduler starts */ + heap->heap = NULL; + } else { + register_heap(heap); + } + SLIST_NEXT(heap, next) = NULL; + + ESP_EARLY_LOGI(TAG, "At %08X len %08X (%d KiB): %s", + region->start, region->size, region->size / 1024, type->name); + } + + assert(heap_idx == num_heaps); + + /* Allocate the permanent heap data that we'll use as a linked list at runtime. + + Allocate this part of data contiguously, even though it's a linked list... */ + assert(SLIST_EMPTY(®istered_heaps)); + + heap_t *heaps_array = NULL; + for (unsigned i = 0; i < num_heaps; i++) { + if (heap_caps_match(&temp_heaps[i], MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL)) { + /* use the first DRAM heap which can fit the data */ + heaps_array = multi_heap_malloc(temp_heaps[i].heap, sizeof(heap_t) * num_heaps); + if (heaps_array != NULL) { + break; + } + } + } + assert(heaps_array != NULL); /* if NULL, there's not enough free startup heap space */ + + memcpy(heaps_array, temp_heaps, sizeof(heap_t)*num_heaps); + + /* Iterate the heaps and set their locks, also add them to the linked list. */ + for (unsigned i = 0; i < num_heaps; i++) { + if (heaps_array[i].heap != NULL) { + multi_heap_set_lock(heaps_array[i].heap, &heaps_array[i].heap_mux); + } + if (i == 0) { + SLIST_INSERT_HEAD(®istered_heaps, &heaps_array[0], next); + } else { + SLIST_INSERT_AFTER(&heaps_array[i-1], &heaps_array[i], next); + } + } +} + +esp_err_t heap_caps_add_region(intptr_t start, intptr_t end) +{ + if (start == 0) { + return ESP_ERR_INVALID_ARG; + } + + for (unsigned i = 0; i < soc_memory_region_count; i++) { + const soc_memory_region_t *region = &soc_memory_regions[i]; + // Test requested start only as 'end' may be in a different region entry, assume 'end' has same caps + if (region->start <= start && (intptr_t)(region->start + region->size) > start) { + const uint32_t *caps = soc_memory_types[region->type].caps; + return heap_caps_add_region_with_caps(caps, start, end); + } + } + + return ESP_ERR_NOT_FOUND; +} + +esp_err_t heap_caps_add_region_with_caps(const uint32_t caps[], intptr_t start, intptr_t end) +{ + esp_err_t err = ESP_FAIL; + if (caps == NULL || start == 0 || end == 0 || end <= start) { + return ESP_ERR_INVALID_ARG; + } + + //Check if region overlaps the start and/or end of an existing region. If so, the + //region is invalid (or maybe added twice) + /* + * assume that in on region, start must be less than end (cannot equal to) !! + * Specially, the 4th scenario can be allowed. For example, allocate memory from heap, + * then change the capability and call this function to create a new region for special + * application. + * In the following chart, 'start = start' and 'end = end' is contained in 3rd scenario. + * This all equal scenario is incorrect because the same region cannot be add twice. For example, + * add the .bss memory to region twice, if not do the check, it will cause exception. + * + * the existing heap region s(tart) e(nd) + * |----------------------| + * 1.add region [Correct] (s1start && end > heap->start) + || (start < heap->end && end > heap->end)) { + return ESP_FAIL; + } + } + + heap_t *p_new = malloc(sizeof(heap_t)); + if (p_new == NULL) { + err = ESP_ERR_NO_MEM; + goto done; + } + memcpy(p_new->caps, caps, sizeof(p_new->caps)); + p_new->start = start; + p_new->end = end; + vPortCPUInitializeMutex(&p_new->heap_mux); + p_new->heap = multi_heap_register((void *)start, end - start); + SLIST_NEXT(p_new, next) = NULL; + if (p_new->heap == NULL) { + err = ESP_FAIL; + goto done; + } + multi_heap_set_lock(p_new->heap, &p_new->heap_mux); + + /* (This insertion is atomic to registered_heaps, so + we don't need to worry about thread safety for readers, + only for writers. */ + static _lock_t registered_heaps_write_lock; + _lock_acquire(®istered_heaps_write_lock); + SLIST_INSERT_HEAD(®istered_heaps, p_new, next); + _lock_release(®istered_heaps_write_lock); + + err = ESP_OK; + + done: + if (err != ESP_OK) { + free(p_new); + } + return err; +} diff --git a/cpu/esp32/esp-idf/heap/heap_trace.c b/cpu/esp32/esp-idf/heap/heap_trace.c new file mode 100644 index 0000000000000..9edd141e6c9d7 --- /dev/null +++ b/cpu/esp32/esp-idf/heap/heap_trace.c @@ -0,0 +1,426 @@ +// 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 +#include +#include + +#define HEAP_TRACE_SRCFILE /* don't warn on inclusion here */ +#include "esp_heap_trace.h" +#undef HEAP_TRACE_SRCFILE + +#include "esp_heap_caps.h" +#include "esp_attr.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "soc/soc_memory_layout.h" +#include "xtensa/hal.h" + +#include "heap_private.h" + +#define STACK_DEPTH CONFIG_HEAP_TRACING_STACK_DEPTH + +static portMUX_TYPE trace_mux = portMUX_INITIALIZER_UNLOCKED; +static bool tracing; +static heap_trace_mode_t mode; + +/* Buffer used for records, starting at offset 0 +*/ +static heap_trace_record_t *buffer; +static size_t total_records; + +/* Count of entries logged in the buffer. + + Maximum total_records +*/ +static size_t count; + +/* Actual number of allocations logged */ +static size_t total_allocations; + +/* Actual number of frees logged */ +static size_t total_frees; + +/* Has the buffer overflowed and lost trace entries? */ +static bool has_overflowed = false; + +esp_err_t heap_trace_init_standalone(heap_trace_record_t *record_buffer, size_t num_records) +{ +#ifndef CONFIG_HEAP_TRACING + return ESP_ERR_NOT_SUPPORTED; +#endif + + if (tracing) { + return ESP_ERR_INVALID_STATE; + } + buffer = record_buffer; + total_records = num_records; + memset(buffer, 0, num_records * sizeof(heap_trace_record_t)); + return ESP_OK; +} + +esp_err_t heap_trace_start(heap_trace_mode_t mode_param) +{ +#ifndef CONFIG_HEAP_TRACING + return ESP_ERR_NOT_SUPPORTED; +#endif + + if (buffer == NULL || total_records == 0) { + return ESP_ERR_INVALID_STATE; + } + taskENTER_CRITICAL(&trace_mux); + + tracing = false; + mode = mode_param; + count = 0; + total_allocations = 0; + total_frees = 0; + has_overflowed = false; + heap_trace_resume(); + + taskEXIT_CRITICAL(&trace_mux); + return ESP_OK; +} + +static esp_err_t set_tracing(bool enable) +{ +#ifndef CONFIG_HEAP_TRACING + return ESP_ERR_NOT_SUPPORTED; +#endif + if (tracing == enable) { + return ESP_ERR_INVALID_STATE; + } + tracing = enable; + return ESP_OK; +} + +esp_err_t heap_trace_stop(void) +{ + return set_tracing(false); +} + +esp_err_t heap_trace_resume(void) +{ + return set_tracing(true); +} + +size_t heap_trace_get_count(void) +{ + return count; +} + +esp_err_t heap_trace_get(size_t index, heap_trace_record_t *record) +{ +#ifndef CONFIG_HEAP_TRACING + return ESP_ERR_NOT_SUPPORTED; +#endif + if (record == NULL) { + return ESP_ERR_INVALID_STATE; + } + esp_err_t result = ESP_OK; + + taskENTER_CRITICAL(&trace_mux); + if (index >= count) { + result = ESP_ERR_INVALID_ARG; /* out of range for 'count' */ + } else { + memcpy(record, &buffer[index], sizeof(heap_trace_record_t)); + } + taskEXIT_CRITICAL(&trace_mux); + return result; +} + + +void heap_trace_dump(void) +{ +#ifndef CONFIG_HEAP_TRACING + printf("no data, heap tracing is disabled.\n"); + return; +#endif + size_t delta_size = 0; + size_t delta_allocs = 0; + printf("%u allocations trace (%u entry buffer)\n", + count, total_records); + size_t start_count = count; + for (unsigned i = 0; i < count; i++) { + heap_trace_record_t *rec = &buffer[i]; + + if (rec->address != NULL) { + printf("%d bytes (@ %p) allocated CPU %d ccount 0x%08x caller ", + rec->size, rec->address, rec->ccount & 1, rec->ccount & ~3); + for (int j = 0; j < STACK_DEPTH && rec->alloced_by[j] != 0; j++) { + printf("%p%s", rec->alloced_by[j], + (j < STACK_DEPTH - 1) ? ":" : ""); + } + + if (mode != HEAP_TRACE_ALL || STACK_DEPTH == 0 || rec->freed_by[0] == NULL) { + delta_size += rec->size; + delta_allocs++; + printf("\n"); + } else { + printf("\nfreed by "); + for (int j = 0; j < STACK_DEPTH; j++) { + printf("%p%s", rec->freed_by[j], + (j < STACK_DEPTH - 1) ? ":" : "\n"); + } + } + } + } + if (mode == HEAP_TRACE_ALL) { + printf("%u bytes alive in trace (%u/%u allocations)\n", + delta_size, delta_allocs, heap_trace_get_count()); + } else { + printf("%u bytes 'leaked' in trace (%u allocations)\n", delta_size, delta_allocs); + } + printf("total allocations %u total frees %u\n", total_allocations, total_frees); + if (start_count != count) { // only a problem if trace isn't stopped before dumping + printf("(NB: New entries were traced while dumping, so trace dump may have duplicate entries.)\n"); + } + if (has_overflowed) { + printf("(NB: Buffer has overflowed, so trace data is incomplete.)\n"); + } +} + +/* Add a new allocation to the heap trace records */ +static IRAM_ATTR void record_allocation(const heap_trace_record_t *record) +{ + taskENTER_CRITICAL(&trace_mux); + if (tracing) { + if (count == total_records) { + has_overflowed = true; + /* Move the whole buffer back one slot. + + This is a bit slow, compared to treating this buffer as a ringbuffer and rotating a head pointer. + + However, ringbuffer code gets tricky when we remove elements in mid-buffer (for leak trace mode) while + trying to keep track of an item count that may overflow. + */ + memmove(&buffer[0], &buffer[1], sizeof(heap_trace_record_t) * (total_records -1)); + count--; + } + // Copy new record into place + memcpy(&buffer[count], record, sizeof(heap_trace_record_t)); + count++; + total_allocations++; + } + taskEXIT_CRITICAL(&trace_mux); +} + +// remove a record, used when freeing +static void remove_record(int index); + +/* record a free event in the heap trace log + + For HEAP_TRACE_ALL, this means filling in the freed_by pointer. + For HEAP_TRACE_LEAKS, this means removing the record from the log. +*/ +static IRAM_ATTR void record_free(void *p, void **callers) +{ + taskENTER_CRITICAL(&trace_mux); + if (tracing && count > 0) { + total_frees++; + /* search backwards for the allocation record matching this free */ + int i; + for (i = count - 1; i >= 0; i--) { + if (buffer[i].address == p) { + break; + } + } + + if (i >= 0) { + if (mode == HEAP_TRACE_ALL) { + memcpy(buffer[i].freed_by, callers, sizeof(void *) * STACK_DEPTH); + } else { // HEAP_TRACE_LEAKS + // Leak trace mode, once an allocation is freed we remove it from the list + remove_record(i); + } + } + } + taskEXIT_CRITICAL(&trace_mux); +} + +/* remove the entry at 'index' from the ringbuffer of saved records */ +static IRAM_ATTR void remove_record(int index) +{ + if ((unsigned)index < count - 1) { + // Remove the buffer entry from the list + memmove(&buffer[index], &buffer[index+1], + sizeof(heap_trace_record_t) * (total_records - index - 1)); + } else { + // For last element, just zero it out to avoid ambiguity + memset(&buffer[index], 0, sizeof(heap_trace_record_t)); + } + count--; +} + +/* Encode the CPU ID in the LSB of the ccount value */ +inline static uint32_t get_ccount(void) +{ + uint32_t ccount = xthal_get_ccount() & ~3; +#ifndef CONFIG_FREERTOS_UNICORE + ccount |= xPortGetCoreID(); +#endif + return ccount; +} + +#define TEST_STACK(N) do { \ + if (STACK_DEPTH == N) { \ + return; \ + } \ + callers[N] = __builtin_return_address(N+offset); \ + if (!esp_ptr_executable(callers[N])) { \ + return; \ + } \ + } while(0); + +/* Static function to read the call stack for a traced heap call. + + Calls to __builtin_return_address are "unrolled" via TEST_STACK macro as gcc requires the + argument to be a compile-time constant. +*/ +static IRAM_ATTR __attribute__((noinline)) void get_call_stack(void **callers) +{ + const int offset = 2; // Caller is 2 stack frames deeper than we care about + bzero(callers, sizeof(void *) * STACK_DEPTH); + TEST_STACK(0); + TEST_STACK(1); + TEST_STACK(2); + TEST_STACK(3); + TEST_STACK(4); + TEST_STACK(5); + TEST_STACK(6); + TEST_STACK(7); + TEST_STACK(8); + TEST_STACK(9); +} + +_Static_assert(STACK_DEPTH >= 0 && STACK_DEPTH <= 10, "CONFIG_HEAP_TRACING_STACK_DEPTH must be in range 0-10"); + + +typedef enum { + TRACE_MALLOC_CAPS, + TRACE_MALLOC_DEFAULT +} trace_malloc_mode_t; + + +void *__real_heap_caps_malloc(size_t size, uint32_t caps); + +/* trace any 'malloc' event */ +static IRAM_ATTR __attribute__((noinline)) void *trace_malloc(size_t size, uint32_t caps, trace_malloc_mode_t mode) +{ + uint32_t ccount = get_ccount(); + void *p; + if ( mode == TRACE_MALLOC_CAPS ) { + p = __real_heap_caps_malloc(size, caps); + } else { //TRACE_MALLOC_DEFAULT + p = heap_caps_malloc_default(size); + } + + if (tracing && p != NULL) { + heap_trace_record_t rec = { + .address = p, + .ccount = ccount, + .size = size, + }; + get_call_stack(rec.alloced_by); + record_allocation(&rec); + } + return p; +} + +void __real_heap_caps_free(void *p); + +/* trace any 'free' event */ +static IRAM_ATTR __attribute__((noinline)) void trace_free(void *p) +{ + if (tracing && p != NULL) { + void *callers[STACK_DEPTH]; + get_call_stack(callers); + record_free(p, callers); + } + __real_heap_caps_free(p); +} + +void * __real_heap_caps_realloc(void *p, size_t size, uint32_t caps); + +/* trace any 'realloc' event */ +static IRAM_ATTR __attribute__((noinline)) void *trace_realloc(void *p, size_t size, uint32_t caps, trace_malloc_mode_t mode) +{ + void *callers[STACK_DEPTH]; + uint32_t ccount = get_ccount(); + if (tracing && p != NULL && size == 0) { + get_call_stack(callers); + record_free(p, callers); + } + void *r; + if (mode == TRACE_MALLOC_CAPS ) { + r = __real_heap_caps_realloc(p, size, caps); + } else { //TRACE_MALLOC_DEFAULT + r = heap_caps_realloc_default(p, size); + } + if (tracing && r != NULL) { + get_call_stack(callers); + if (p != NULL) { + /* trace realloc as free-then-alloc */ + record_free(p, callers); + } + heap_trace_record_t rec = { + .address = r, + .ccount = ccount, + .size = size, + }; + memcpy(rec.alloced_by, callers, sizeof(void *) * STACK_DEPTH); + record_allocation(&rec); + } + return r; +} + +/* Note: this changes the behaviour of libc malloc/realloc/free a bit, + as they no longer go via the libc functions in ROM. But more or less + the same in the end. */ + +IRAM_ATTR void *__wrap_malloc(size_t size) +{ + return trace_malloc(size, 0, TRACE_MALLOC_DEFAULT); +} + +IRAM_ATTR void __wrap_free(void *p) +{ + trace_free(p); +} + +IRAM_ATTR void *__wrap_realloc(void *p, size_t size) +{ + return trace_realloc(p, size, 0, TRACE_MALLOC_DEFAULT); +} + +IRAM_ATTR void *__wrap_calloc(size_t nmemb, size_t size) +{ + size = size * nmemb; + void *result = trace_malloc(size, 0, TRACE_MALLOC_DEFAULT); + if (result != NULL) { + memset(result, 0, size); + } + return result; +} + +IRAM_ATTR void *__wrap_heap_caps_malloc(size_t size, uint32_t caps) +{ + return trace_malloc(size, caps, TRACE_MALLOC_CAPS); +} + +IRAM_ATTR void __wrap_heap_caps_free(void *p) __attribute__((alias("__wrap_free"))); + +IRAM_ATTR void *__wrap_heap_caps_realloc(void *p, size_t size, uint32_t caps) +{ + return trace_realloc(p, size, caps, TRACE_MALLOC_CAPS); +} diff --git a/cpu/esp32/esp-idf/heap/multi_heap.c b/cpu/esp32/esp-idf/heap/multi_heap.c new file mode 100644 index 0000000000000..decf9d0932ee5 --- /dev/null +++ b/cpu/esp32/esp-idf/heap/multi_heap.c @@ -0,0 +1,711 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include "multi_heap_internal.h" + +/* Note: Keep platform-specific parts in this header, this source + file should depend on libc only */ +#include "multi_heap_platform.h" + +/* Defines compile-time configuration macros */ +#include "multi_heap_config.h" + +#ifndef MULTI_HEAP_POISONING +/* if no heap poisoning, public API aliases directly to these implementations */ +void *multi_heap_malloc(multi_heap_handle_t heap, size_t size) + __attribute__((alias("multi_heap_malloc_impl"))); + +void multi_heap_free(multi_heap_handle_t heap, void *p) + __attribute__((alias("multi_heap_free_impl"))); + +void *multi_heap_realloc(multi_heap_handle_t heap, void *p, size_t size) + __attribute__((alias("multi_heap_realloc_impl"))); + +size_t multi_heap_get_allocated_size(multi_heap_handle_t heap, void *p) + __attribute__((alias("multi_heap_get_allocated_size_impl"))); + +multi_heap_handle_t multi_heap_register(void *start, size_t size) + __attribute__((alias("multi_heap_register_impl"))); + +void multi_heap_get_info(multi_heap_handle_t heap, multi_heap_info_t *info) + __attribute__((alias("multi_heap_get_info_impl"))); + +size_t multi_heap_free_size(multi_heap_handle_t heap) + __attribute__((alias("multi_heap_free_size_impl"))); + +size_t multi_heap_minimum_free_size(multi_heap_handle_t heap) + __attribute__((alias("multi_heap_minimum_free_size_impl"))); + +#endif + +#define ALIGN(X) ((X) & ~(sizeof(void *)-1)) +#define ALIGN_UP(X) ALIGN((X)+sizeof(void *)-1) + +struct heap_block; + +/* Block in the heap + + Heap implementation uses two single linked lists, a block list (all blocks) and a free list (free blocks). + + 'header' holds a pointer to the next block (used or free) ORed with a free flag (the LSB of the pointer.) is_free() and get_next_block() utility functions allow typed access to these values. + + 'next_free' is valid if the block is free and is a pointer to the next block in the free list. +*/ +typedef struct heap_block { + intptr_t header; /* Encodes next block in heap (used or unused) and also free/used flag */ + union { + uint8_t data[1]; /* First byte of data, valid if block is used. Actual size of data is 'block_data_size(block)' */ + struct heap_block *next_free; /* Pointer to next free block, valid if block is free */ + }; +} heap_block_t; + +/* These masks apply to the 'header' field of heap_block_t */ +#define BLOCK_FREE_FLAG 0x1 /* If set, this block is free & next_free pointer is valid */ +#define NEXT_BLOCK_MASK (~3) /* AND header with this mask to get pointer to next block (free or used) */ + +/* Metadata header for the heap, stored at the beginning of heap space. + + 'first_block' is a "fake" first block, minimum length, used to provide a pointer to the first used & free block in + the heap. This block is never allocated or merged into an adjacent block. + + 'last_block' is a pointer to a final free block of length 0, which is added at the end of the heap when it is + registered. This block is also never allocated or merged into an adjacent block. + */ +typedef struct multi_heap_info { + void *lock; + size_t free_bytes; + size_t minimum_free_bytes; + heap_block_t *last_block; + heap_block_t first_block; /* initial 'free block', never allocated */ +} heap_t; + +/* Given a pointer to the 'data' field of a block (ie the previous malloc/realloc result), return a pointer to the + containing block. +*/ +static inline heap_block_t *get_block(const void *data_ptr) +{ + return (heap_block_t *)((char *)data_ptr - offsetof(heap_block_t, data)); +} + +/* Return the next sequential block in the heap. + */ +static inline heap_block_t *get_next_block(const heap_block_t *block) +{ + intptr_t next = block->header & NEXT_BLOCK_MASK; + if (next == 0) { + return NULL; /* last_block */ + } + assert(next > (intptr_t)block); + return (heap_block_t *)next; +} + +/* Return true if this block is free. */ +static inline bool is_free(const heap_block_t *block) +{ + return block->header & BLOCK_FREE_FLAG; +} + +/* Return true if this block is the first in the heap */ +static inline bool is_first_block(const heap_t *heap, const heap_block_t *block) +{ + return (block == &heap->first_block); +} + +/* Return true if this block is the last_block in the heap + (the only block with no next pointer) */ +static inline bool is_last_block(const heap_block_t *block) +{ + return (block->header & NEXT_BLOCK_MASK) == 0; +} + +/* Data size of the block (excludes this block's header) */ +static inline size_t block_data_size(const heap_block_t *block) +{ + intptr_t next = (intptr_t)block->header & NEXT_BLOCK_MASK; + intptr_t this = (intptr_t)block; + if (next == 0) { + return 0; /* this is the last block in the heap */ + } + return next - this - sizeof(block->header); +} + +/* Check a block is valid for this heap. Used to verify parameters. */ +static void assert_valid_block(const heap_t *heap, const heap_block_t *block) +{ + MULTI_HEAP_ASSERT(block >= &heap->first_block && block <= heap->last_block, + block); // block not in heap + if (heap < (const heap_t *)heap->last_block) { + const heap_block_t *next = get_next_block(block); + MULTI_HEAP_ASSERT(next >= &heap->first_block && next <= heap->last_block, block); // Next block not in heap + if (is_free(block)) { + // Check block->next_free is valid + MULTI_HEAP_ASSERT(block->next_free >= &heap->first_block && block->next_free <= heap->last_block, &block->next_free); + } + } +} + +/* Get the first free block before 'block' in the heap. 'block' can be a free block or in use. + + Result is always the closest free block to 'block' in the heap, that is located before 'block'. There may be multiple + allocated blocks between the result and 'block'. + + If 'block' is free, the result's 'next_free' pointer will already point to 'block'. + + Result will never be NULL, but it may be the header block heap->first_block. +*/ +static heap_block_t *get_prev_free_block(heap_t *heap, const heap_block_t *block) +{ + assert(!is_first_block(heap, block)); /* can't look for a block before first_block */ + + for (heap_block_t *b = &heap->first_block; b != NULL && b < block; b = b->next_free) { + MULTI_HEAP_ASSERT(is_free(b), b); // Block should be free + if (b->next_free == NULL || b->next_free >= block) { + if (is_free(block)) { + /* if block is on freelist, 'b' should be the item before it. */ + MULTI_HEAP_ASSERT(b->next_free == block, &b->next_free); + } + return b; /* b is the last free block before 'block' */ + } + } + abort(); /* There should always be a previous free block, even if it's heap->first_block */ +} + +/* Merge some block 'a' into the following block 'b'. + + If both blocks are free, resulting block is marked free. + If only one block is free, resulting block is marked in use. No data is moved. + + This operation may fail if block 'a' is the first block or 'b' is the last block, + the caller should check block_data_size() to know if anything happened here or not. +*/ +static heap_block_t *merge_adjacent(heap_t *heap, heap_block_t *a, heap_block_t *b) +{ + assert(a < b); + + /* Can't merge header blocks, just return the non-header block as-is */ + if (is_last_block(b)) { + return a; + } + if (is_first_block(heap, a)) { + return b; + } + + MULTI_HEAP_ASSERT(get_next_block(a) == b, a); // Blocks should be in order + + bool free = is_free(a) && is_free(b); /* merging two free blocks creates a free block */ + if (!free && (is_free(a) || is_free(b))) { + /* only one of these blocks is free, so resulting block will be a used block. + means we need to take the free block out of the free list + */ + heap_block_t *free_block = is_free(a) ? a : b; + heap_block_t *prev_free = get_prev_free_block(heap, free_block); + MULTI_HEAP_ASSERT(free_block->next_free > prev_free, &free_block->next_free); // Next free block should be after prev one + prev_free->next_free = free_block->next_free; + + heap->free_bytes -= block_data_size(free_block); + } + + a->header = b->header & NEXT_BLOCK_MASK; + MULTI_HEAP_ASSERT(a->header != 0, a); + if (free) { + a->header |= BLOCK_FREE_FLAG; + if (b->next_free != NULL) { + MULTI_HEAP_ASSERT(b->next_free > a, &b->next_free); + MULTI_HEAP_ASSERT(b->next_free > b, &b->next_free); + } + a->next_free = b->next_free; + + /* b's header can be put into the pool of free bytes */ + heap->free_bytes += sizeof(a->header); + } + +#ifdef MULTI_HEAP_POISONING_SLOW + /* b's former block header needs to be replaced with a fill pattern */ + multi_heap_internal_poison_fill_region(b, sizeof(heap_block_t), free); +#endif + + return a; +} + +/* Split a block so it can hold at least 'size' bytes of data, making any spare + space into a new free block. + + 'block' should be marked in-use when this function is called (implementation detail, this function + doesn't set the next_free pointer). + + 'prev_free_block' is the free block before 'block', if already known. Can be NULL if not yet known. + (This is a performance optimisation to avoid walking the freelist twice when possible.) +*/ +static void split_if_necessary(heap_t *heap, heap_block_t *block, size_t size, heap_block_t *prev_free_block) +{ + const size_t block_size = block_data_size(block); + MULTI_HEAP_ASSERT(!is_free(block), block); // split block shouldn't be free + MULTI_HEAP_ASSERT(size <= block_size, block); // size should be valid + size = ALIGN_UP(size); + + /* can't split the head or tail block */ + assert(!is_first_block(heap, block)); + assert(!is_last_block(block)); + + heap_block_t *new_block = (heap_block_t *)(block->data + size); + heap_block_t *next_block = get_next_block(block); + + if (is_free(next_block) && !is_last_block(next_block)) { + /* The next block is free, just extend it upwards. */ + new_block->header = next_block->header; + new_block->next_free = next_block->next_free; + if (prev_free_block == NULL) { + prev_free_block = get_prev_free_block(heap, block); + } + /* prev_free_block should point to the next block (which we found to be free). */ + MULTI_HEAP_ASSERT(prev_free_block->next_free == next_block, + &prev_free_block->next_free); // free blocks should be in order + /* Note: We have not introduced a new block header, hence the simple math. */ + heap->free_bytes += block_size - size; +#ifdef MULTI_HEAP_POISONING_SLOW + /* next_block header needs to be replaced with a fill pattern */ + multi_heap_internal_poison_fill_region(next_block, sizeof(heap_block_t), true /* free */); +#endif + } else { + /* Insert a free block between the current and the next one. */ + if (block_data_size(block) < size + sizeof(heap_block_t)) { + /* Can't split 'block' if we're not going to get a usable free block afterwards */ + return; + } + if (prev_free_block == NULL) { + prev_free_block = get_prev_free_block(heap, block); + } + new_block->header = block->header | BLOCK_FREE_FLAG; + new_block->next_free = prev_free_block->next_free; + /* prev_free_block should point to a free block after new_block */ + MULTI_HEAP_ASSERT(prev_free_block->next_free > new_block, + &prev_free_block->next_free); // free blocks should be in order + heap->free_bytes += block_data_size(new_block); + } + block->header = (intptr_t)new_block; + prev_free_block->next_free = new_block; +} + +size_t multi_heap_get_allocated_size_impl(multi_heap_handle_t heap, void *p) +{ + heap_block_t *pb = get_block(p); + + assert_valid_block(heap, pb); + MULTI_HEAP_ASSERT(!is_free(pb), pb); // block should be free + return block_data_size(pb); +} + +multi_heap_handle_t multi_heap_register_impl(void *start, size_t size) +{ + heap_t *heap = (heap_t *)ALIGN_UP((intptr_t)start); + uintptr_t end = ALIGN((uintptr_t)start + size); + if (end - (uintptr_t)start < sizeof(heap_t) + 2*sizeof(heap_block_t)) { + return NULL; /* 'size' is too small to fit a heap here */ + } + heap->lock = NULL; + heap->last_block = (heap_block_t *)(end - sizeof(heap_block_t)); + + /* first 'real' (allocatable) free block goes after the heap structure */ + heap_block_t *first_free_block = (heap_block_t *)((intptr_t)start + sizeof(heap_t)); + first_free_block->header = (intptr_t)heap->last_block | BLOCK_FREE_FLAG; + first_free_block->next_free = heap->last_block; + + /* last block is 'free' but has a NULL next pointer */ + heap->last_block->header = BLOCK_FREE_FLAG; + heap->last_block->next_free = NULL; + + /* first block also 'free' but has legitimate length, + malloc will never allocate into this block. */ + heap->first_block.header = (intptr_t)first_free_block | BLOCK_FREE_FLAG; + heap->first_block.next_free = first_free_block; + + /* free bytes is: + - total bytes in heap + - minus heap_t header at top (includes heap->first_block) + - minus header of first_free_block + - minus whole block at heap->last_block + */ + heap->free_bytes = ALIGN(size) - sizeof(heap_t) - sizeof(first_free_block->header) - sizeof(heap_block_t); + heap->minimum_free_bytes = heap->free_bytes; + + return heap; +} + +void multi_heap_set_lock(multi_heap_handle_t heap, void *lock) +{ + heap->lock = lock; +} + +inline void multi_heap_internal_lock(multi_heap_handle_t heap) +{ + MULTI_HEAP_LOCK(heap->lock); +} + +inline void multi_heap_internal_unlock(multi_heap_handle_t heap) +{ + MULTI_HEAP_UNLOCK(heap->lock); +} + +void *multi_heap_malloc_impl(multi_heap_handle_t heap, size_t size) +{ + heap_block_t *best_block = NULL; + heap_block_t *prev_free = NULL; + heap_block_t *prev = NULL; + size_t best_size = SIZE_MAX; + size = ALIGN_UP(size); + + if (size == 0 || heap == NULL) { + return NULL; + } + + multi_heap_internal_lock(heap); + + /* Note: this check must be done while holding the lock as both + malloc & realloc may temporarily shrink the free_bytes value + before they split a large block. This can result in false negatives, + especially if the heap is unfragmented. + */ + if (heap->free_bytes < size) { + MULTI_HEAP_UNLOCK(heap->lock); + return NULL; + } + + /* Find best free block to perform the allocation in */ + prev = &heap->first_block; + for (heap_block_t *b = heap->first_block.next_free; b != NULL; b = b->next_free) { + MULTI_HEAP_ASSERT(b > prev, &prev->next_free); // free blocks should be ascending in address + MULTI_HEAP_ASSERT(is_free(b), b); // block should be free + size_t bs = block_data_size(b); + if (bs >= size && bs < best_size) { + best_block = b; + best_size = bs; + prev_free = prev; + if (bs == size) { + break; /* we've found a perfect sized block */ + } + } + prev = b; + } + + if (best_block == NULL) { + multi_heap_internal_unlock(heap); + return NULL; /* No room in heap */ + } + + prev_free->next_free = best_block->next_free; + best_block->header &= ~BLOCK_FREE_FLAG; + + heap->free_bytes -= block_data_size(best_block); + + split_if_necessary(heap, best_block, size, prev_free); + + if (heap->free_bytes < heap->minimum_free_bytes) { + heap->minimum_free_bytes = heap->free_bytes; + } + + multi_heap_internal_unlock(heap); + + return best_block->data; +} + +void multi_heap_free_impl(multi_heap_handle_t heap, void *p) +{ + heap_block_t *pb = get_block(p); + + if (heap == NULL || p == NULL) { + return; + } + + multi_heap_internal_lock(heap); + + assert_valid_block(heap, pb); + MULTI_HEAP_ASSERT(!is_free(pb), pb); // block should not be free + MULTI_HEAP_ASSERT(!is_last_block(pb), pb); // block should not be last block + MULTI_HEAP_ASSERT(!is_first_block(heap, pb), pb); // block should not be first block + + heap_block_t *next = get_next_block(pb); + + /* Update freelist pointers */ + heap_block_t *prev_free = get_prev_free_block(heap, pb); + // freelist validity check + MULTI_HEAP_ASSERT(prev_free->next_free == NULL || prev_free->next_free > pb, &prev_free->next_free); + pb->next_free = prev_free->next_free; + prev_free->next_free = pb; + + /* Mark this block as free */ + pb->header |= BLOCK_FREE_FLAG; + + heap->free_bytes += block_data_size(pb); + + /* Try and merge previous free block into this one */ + if (get_next_block(prev_free) == pb) { + pb = merge_adjacent(heap, prev_free, pb); + } + + /* If next block is free, try to merge the two */ + if (is_free(next)) { + pb = merge_adjacent(heap, pb, next); + } + + multi_heap_internal_unlock(heap); +} + + +void *multi_heap_realloc_impl(multi_heap_handle_t heap, void *p, size_t size) +{ + heap_block_t *pb = get_block(p); + void *result; + size = ALIGN_UP(size); + + assert(heap != NULL); + + if (p == NULL) { + return multi_heap_malloc_impl(heap, size); + } + + assert_valid_block(heap, pb); + // non-null realloc arg should be allocated + MULTI_HEAP_ASSERT(!is_free(pb), pb); + + if (size == 0) { + /* note: calling multi_free_impl() here as we've already been + through any poison-unwrapping */ + multi_heap_free_impl(heap, p); + return NULL; + } + + if (heap == NULL) { + return NULL; + } + + multi_heap_internal_lock(heap); + result = NULL; + + if (size <= block_data_size(pb)) { + // Shrinking.... + split_if_necessary(heap, pb, size, NULL); + result = pb->data; + } + else if (heap->free_bytes < size - block_data_size(pb)) { + // Growing, but there's not enough total free space in the heap + multi_heap_internal_unlock(heap); + return NULL; + } + + // New size is larger than existing block + if (result == NULL) { + // See if we can grow into one or both adjacent blocks + heap_block_t *orig_pb = pb; + size_t orig_size = block_data_size(orig_pb); + heap_block_t *next = get_next_block(pb); + heap_block_t *prev = get_prev_free_block(heap, pb); + + // Can only grow into the previous free block if it's adjacent + size_t prev_grow_size = (get_next_block(prev) == pb) ? block_data_size(prev) : 0; + + // Can grow into next block? (we may also need to grow into 'prev' to get to our desired size) + if (is_free(next) && (block_data_size(pb) + block_data_size(next) + prev_grow_size >= size)) { + pb = merge_adjacent(heap, pb, next); + } + + // Can grow into previous block? + // (try this even if we're already big enough from growing into 'next', as it reduces fragmentation) + if (prev_grow_size > 0 && (block_data_size(pb) + prev_grow_size >= size)) { + pb = merge_adjacent(heap, prev, pb); + // this doesn't guarantee we'll be left with a big enough block, as it's + // possible for the merge to fail if prev == heap->first_block + } + + if (block_data_size(pb) >= size) { + memmove(pb->data, orig_pb->data, orig_size); + split_if_necessary(heap, pb, size, NULL); + result = pb->data; + } + } + + if (result == NULL) { + // Need to allocate elsewhere and copy data over + // + // (Calling _impl versions here as we've already been through any + // unwrapping for heap poisoning features.) + result = multi_heap_malloc_impl(heap, size); + if (result != NULL) { + memcpy(result, pb->data, block_data_size(pb)); + multi_heap_free_impl(heap, pb->data); + } + } + + if (heap->free_bytes < heap->minimum_free_bytes) { + heap->minimum_free_bytes = heap->free_bytes; + } + + multi_heap_internal_unlock(heap); + return result; +} + +#define FAIL_PRINT(MSG, ...) do { \ + if (print_errors) { \ + MULTI_HEAP_STDERR_PRINTF(MSG, __VA_ARGS__); \ + } \ + valid = false; \ + } \ + while(0) + +bool multi_heap_check(multi_heap_handle_t heap, bool print_errors) +{ + bool valid = true; + size_t total_free_bytes = 0; + assert(heap != NULL); + + multi_heap_internal_lock(heap); + + heap_block_t *prev = NULL; + heap_block_t *prev_free = NULL; + heap_block_t *expected_free = NULL; + + /* note: not using get_next_block() in loop, so that assertions aren't checked here */ + for(heap_block_t *b = &heap->first_block; b != NULL; b = (heap_block_t *)(b->header & NEXT_BLOCK_MASK)) { + if (b == prev) { + FAIL_PRINT("CORRUPT HEAP: Block %p points to itself\n", b); + goto done; + } + if (b < prev) { + FAIL_PRINT("CORRUPT HEAP: Block %p is before prev block %p\n", b, prev); + goto done; + } + if (b > heap->last_block || b < &heap->first_block) { + FAIL_PRINT("CORRUPT HEAP: Block %p is outside heap (last valid block %p)\n", b, prev); + goto done; + } + if (is_free(b)) { + if (prev != NULL && is_free(prev) && !is_first_block(heap, prev) && !is_last_block(b)) { + FAIL_PRINT("CORRUPT HEAP: Two adjacent free blocks found, %p and %p\n", prev, b); + } + if (expected_free != NULL && expected_free != b) { + FAIL_PRINT("CORRUPT HEAP: Prev free block %p pointed to next free %p but this free block is %p\n", + prev_free, expected_free, b); + } + prev_free = b; + expected_free = b->next_free; + if (!is_first_block(heap, b)) { + total_free_bytes += block_data_size(b); + } + } + prev = b; + +#ifdef MULTI_HEAP_POISONING + if (!is_last_block(b)) { + /* For slow heap poisoning, any block should contain correct poisoning patterns and/or fills */ + bool poison_ok; + if (is_free(b) && b != heap->last_block) { + uint32_t block_len = (intptr_t)get_next_block(b) - (intptr_t)b - sizeof(heap_block_t); + poison_ok = multi_heap_internal_check_block_poisoning(&b[1], block_len, true, print_errors); + } + else { + poison_ok = multi_heap_internal_check_block_poisoning(b->data, block_data_size(b), false, print_errors); + } + valid = poison_ok && valid; + } +#endif + + } /* for(heap_block_t b = ... */ + + if (prev != heap->last_block) { + FAIL_PRINT("CORRUPT HEAP: Last block %p not %p\n", prev, heap->last_block); + } + if (!is_free(heap->last_block)) { + FAIL_PRINT("CORRUPT HEAP: Expected prev block %p to be free\n", heap->last_block); + } + + if (heap->free_bytes != total_free_bytes) { + FAIL_PRINT("CORRUPT HEAP: Expected %u free bytes counted %u\n", (unsigned)heap->free_bytes, (unsigned)total_free_bytes); + } + + done: + multi_heap_internal_unlock(heap); + + return valid; +} + +void multi_heap_dump(multi_heap_handle_t heap) +{ + assert(heap != NULL); + + multi_heap_internal_lock(heap); + MULTI_HEAP_STDERR_PRINTF("Heap start %p end %p\nFirst free block %p\n", &heap->first_block, heap->last_block, heap->first_block.next_free); + for(heap_block_t *b = &heap->first_block; b != NULL; b = get_next_block(b)) { + MULTI_HEAP_STDERR_PRINTF("Block %p data size 0x%08x bytes next block %p", b, block_data_size(b), get_next_block(b)); + if (is_free(b)) { + MULTI_HEAP_STDERR_PRINTF(" FREE. Next free %p\n", b->next_free); + } else { + MULTI_HEAP_STDERR_PRINTF("%s", "\n"); /* C macros & optional __VA_ARGS__ */ + } + } + multi_heap_internal_unlock(heap); +} + +size_t multi_heap_free_size_impl(multi_heap_handle_t heap) +{ + if (heap == NULL) { + return 0; + } + return heap->free_bytes; +} + +size_t multi_heap_minimum_free_size_impl(multi_heap_handle_t heap) +{ + if (heap == NULL) { + return 0; + } + return heap->minimum_free_bytes; +} + +void multi_heap_get_info_impl(multi_heap_handle_t heap, multi_heap_info_t *info) +{ + memset(info, 0, sizeof(multi_heap_info_t)); + + if (heap == NULL) { + return; + } + + multi_heap_internal_lock(heap); + for(heap_block_t *b = get_next_block(&heap->first_block); !is_last_block(b); b = get_next_block(b)) { + info->total_blocks++; + if (is_free(b)) { + size_t s = block_data_size(b); + info->total_free_bytes += s; + if (s > info->largest_free_block) { + info->largest_free_block = s; + } + info->free_blocks++; + } else { + info->total_allocated_bytes += block_data_size(b); + info->allocated_blocks++; + } + } + + info->minimum_free_bytes = heap->minimum_free_bytes; + // heap has wrong total size (address printed here is not indicative of the real error) + MULTI_HEAP_ASSERT(info->total_free_bytes == heap->free_bytes, heap); + + multi_heap_internal_unlock(heap); + +} diff --git a/cpu/esp32/esp-idf/heap/multi_heap_poisoning.c b/cpu/esp32/esp-idf/heap/multi_heap_poisoning.c new file mode 100644 index 0000000000000..360ab906dd31e --- /dev/null +++ b/cpu/esp32/esp-idf/heap/multi_heap_poisoning.c @@ -0,0 +1,354 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "multi_heap_internal.h" + +/* Note: Keep platform-specific parts in this header, this source + file should depend on libc only */ +#include "multi_heap_platform.h" + +/* Defines compile-time configuration macros */ +#include "multi_heap_config.h" + +#ifdef MULTI_HEAP_POISONING + +/* Alias MULTI_HEAP_POISONING_SLOW to SLOW for better readabilty */ +#ifdef SLOW +#error "external header has defined SLOW" +#endif +#ifdef MULTI_HEAP_POISONING_SLOW +#define SLOW 1 +#endif + +#define MALLOC_FILL_PATTERN 0xce +#define FREE_FILL_PATTERN 0xfe + +#define HEAD_CANARY_PATTERN 0xABBA1234 +#define TAIL_CANARY_PATTERN 0xBAAD5678 + +typedef struct { + uint32_t head_canary; + size_t alloc_size; +} poison_head_t; + +typedef struct { + uint32_t tail_canary; +} poison_tail_t; + +#define POISON_OVERHEAD (sizeof(poison_head_t) + sizeof(poison_tail_t)) + +/* Given a "poisoned" region with pre-data header 'head', and actual data size 'alloc_size', fill in the head and tail + region checks. + + Returns the pointer to the actual usable data buffer (ie after 'head') +*/ +static uint8_t *poison_allocated_region(poison_head_t *head, size_t alloc_size) +{ + uint8_t *data = (uint8_t *)(&head[1]); /* start of data ie 'real' allocated buffer */ + poison_tail_t *tail = (poison_tail_t *)(data + alloc_size); + head->alloc_size = alloc_size; + head->head_canary = HEAD_CANARY_PATTERN; + + uint32_t tail_canary = TAIL_CANARY_PATTERN; + if ((intptr_t)tail % sizeof(void *) == 0) { + tail->tail_canary = tail_canary; + } else { + /* unaligned tail_canary */ + memcpy(&tail->tail_canary, &tail_canary, sizeof(uint32_t)); + } + + return data; +} + +/* Given a pointer to some allocated data, check the head & tail poison structures (before & after it) that were + previously injected by poison_allocated_region(). + + Returns a pointer to the poison header structure, or NULL if the poison structures are corrupt. +*/ +static poison_head_t *verify_allocated_region(void *data, bool print_errors) +{ + poison_head_t *head = (poison_head_t *)((intptr_t)data - sizeof(poison_head_t)); + poison_tail_t *tail = (poison_tail_t *)((intptr_t)data + head->alloc_size); + + /* check if the beginning of the data was overwritten */ + if (head->head_canary != HEAD_CANARY_PATTERN) { + if (print_errors) { + MULTI_HEAP_STDERR_PRINTF("CORRUPT HEAP: Bad head at %p. Expected 0x%08x got 0x%08x\n", &head->head_canary, + HEAD_CANARY_PATTERN, head->head_canary); + } + return NULL; + } + + /* check if the end of the data was overrun */ + uint32_t canary; + if ((intptr_t)tail % sizeof(void *) == 0) { + canary = tail->tail_canary; + } else { + /* tail is unaligned */ + memcpy(&canary, &tail->tail_canary, sizeof(canary)); + } + if (canary != TAIL_CANARY_PATTERN) { + if (print_errors) { + printf("CORRUPT HEAP: Bad tail at %p. Expected 0x%08x got 0x%08x\n", &tail->tail_canary, + TAIL_CANARY_PATTERN, canary); + } + return NULL; + } + + return head; +} + +#ifdef SLOW +/* Go through a region that should have the specified fill byte 'pattern', + verify it. + + if expect_free is true, expect FREE_FILL_PATTERN otherwise MALLOC_FILL_PATTERN. + + if swap_pattern is true, swap patterns in the buffer (ie replace MALLOC_FILL_PATTERN with FREE_FILL_PATTERN, and vice versa.) + + Returns true if verification checks out. +*/ +static bool verify_fill_pattern(void *data, size_t size, bool print_errors, bool expect_free, bool swap_pattern) +{ + const uint32_t FREE_FILL_WORD = (FREE_FILL_PATTERN << 24) | (FREE_FILL_PATTERN << 16) | (FREE_FILL_PATTERN << 8) | FREE_FILL_PATTERN; + const uint32_t MALLOC_FILL_WORD = (MALLOC_FILL_PATTERN << 24) | (MALLOC_FILL_PATTERN << 16) | (MALLOC_FILL_PATTERN << 8) | MALLOC_FILL_PATTERN; + + const uint32_t EXPECT_WORD = expect_free ? FREE_FILL_WORD : MALLOC_FILL_WORD; + const uint32_t REPLACE_WORD = expect_free ? MALLOC_FILL_WORD : FREE_FILL_WORD; + bool valid = true; + + /* Use 4-byte operations as much as possible */ + if ((intptr_t)data % 4 == 0) { + uint32_t *p = data; + while (size >= 4) { + if (*p != EXPECT_WORD) { + if (print_errors) { + MULTI_HEAP_STDERR_PRINTF("CORRUPT HEAP: Invalid data at %p. Expected 0x%08x got 0x%08x\n", p, EXPECT_WORD, *p); + } + valid = false; + } + if (swap_pattern) { + *p = REPLACE_WORD; + } + p++; + size -= 4; + } + data = p; + } + + uint8_t *p = data; + for (int i = 0; i < size; i++) { + if (p[i] != (uint8_t)EXPECT_WORD) { + if (print_errors) { + MULTI_HEAP_STDERR_PRINTF("CORRUPT HEAP: Invalid data at %p. Expected 0x%02x got 0x%02x\n", p, (uint8_t)EXPECT_WORD, *p); + } + valid = false; + } + if (swap_pattern) { + p[i] = (uint8_t)REPLACE_WORD; + } + } + return valid; +} +#endif + +void *multi_heap_malloc(multi_heap_handle_t heap, size_t size) +{ + multi_heap_internal_lock(heap); + poison_head_t *head = multi_heap_malloc_impl(heap, size + POISON_OVERHEAD); + uint8_t *data = NULL; + if (head != NULL) { + data = poison_allocated_region(head, size); +#ifdef SLOW + /* check everything we got back is FREE_FILL_PATTERN & swap for MALLOC_FILL_PATTERN */ + assert( verify_fill_pattern(data, size, true, true, true) ); +#endif + } + + multi_heap_internal_unlock(heap); + return data; +} + +void multi_heap_free(multi_heap_handle_t heap, void *p) +{ + if (p == NULL) { + return; + } + multi_heap_internal_lock(heap); + + poison_head_t *head = verify_allocated_region(p, true); + assert(head != NULL); + + #ifdef SLOW + /* replace everything with FREE_FILL_PATTERN, including the poison head/tail */ + memset(head, FREE_FILL_PATTERN, + head->alloc_size + POISON_OVERHEAD); + #endif + multi_heap_free_impl(heap, head); + + multi_heap_internal_unlock(heap); +} + +void *multi_heap_realloc(multi_heap_handle_t heap, void *p, size_t size) +{ + poison_head_t *head = NULL; + poison_head_t *new_head; + void *result = NULL; + + if (p == NULL) { + return multi_heap_malloc(heap, size); + } + if (size == 0) { + multi_heap_free(heap, p); + return NULL; + } + + /* p != NULL, size != 0 */ + head = verify_allocated_region(p, true); + assert(head != NULL); + + multi_heap_internal_lock(heap); + +#ifndef SLOW + new_head = multi_heap_realloc_impl(heap, head, size + POISON_OVERHEAD); + if (new_head != NULL) { + /* For "fast" poisoning, we only overwrite the head/tail of the new block so it's safe + to poison, so no problem doing this even if realloc resized in place. + */ + result = poison_allocated_region(new_head, size); + } +#else // SLOW + /* When slow poisoning is enabled, it becomes very fiddly to try and correctly fill memory when resizing in place + (where the buffer may be moved (including to an overlapping address with the old buffer), grown, or shrunk in + place.) + + For now we just malloc a new buffer, copy, and free. :| + + Note: If this ever changes, multi_heap defrag realloc test should be enabled. + */ + size_t orig_alloc_size = head->alloc_size; + + new_head = multi_heap_malloc_impl(heap, size + POISON_OVERHEAD); + if (new_head != NULL) { + result = poison_allocated_region(new_head, size); + memcpy(result, p, MIN(size, orig_alloc_size)); + multi_heap_free(heap, p); + } +#endif + + multi_heap_internal_unlock(heap); + + return result; +} + +size_t multi_heap_get_allocated_size(multi_heap_handle_t heap, void *p) +{ + poison_head_t *head = verify_allocated_region(p, true); + assert(head != NULL); + size_t result = multi_heap_get_allocated_size_impl(heap, head); + if (result > 0) { + return result - POISON_OVERHEAD; + } + return 0; +} + +multi_heap_handle_t multi_heap_register(void *start, size_t size) +{ + if (start != NULL) { + memset(start, FREE_FILL_PATTERN, size); + } + return multi_heap_register_impl(start, size); +} + +static inline void subtract_poison_overhead(size_t *arg) { + if (*arg > POISON_OVERHEAD) { + *arg -= POISON_OVERHEAD; + } else { + *arg = 0; + } +} + +void multi_heap_get_info(multi_heap_handle_t heap, multi_heap_info_t *info) +{ + multi_heap_get_info_impl(heap, info); + /* don't count the heap poison head & tail overhead in the allocated bytes size */ + info->total_allocated_bytes -= info->allocated_blocks * POISON_OVERHEAD; + /* trim largest_free_block to account for poison overhead */ + subtract_poison_overhead(&info->largest_free_block); + /* similarly, trim total_free_bytes so there's no suggestion that + a block this big may be available. */ + subtract_poison_overhead(&info->total_free_bytes); + subtract_poison_overhead(&info->minimum_free_bytes); +} + +size_t multi_heap_free_size(multi_heap_handle_t heap) +{ + size_t r = multi_heap_free_size_impl(heap); + subtract_poison_overhead(&r); + return r; +} + +size_t multi_heap_minimum_free_size(multi_heap_handle_t heap) +{ + size_t r = multi_heap_minimum_free_size_impl(heap); + subtract_poison_overhead(&r); + return r; +} + +/* Internal hooks used by multi_heap to manage poisoning, while keeping some modularity */ + +bool multi_heap_internal_check_block_poisoning(void *start, size_t size, bool is_free, bool print_errors) +{ + if (is_free) { +#ifdef SLOW + return verify_fill_pattern(start, size, print_errors, true, false); +#else + return true; /* can only verify empty blocks in SLOW mode */ +#endif + } else { + void *data = (void *)((intptr_t)start + sizeof(poison_head_t)); + poison_head_t *head = verify_allocated_region(data, print_errors); + if (head != NULL && head->alloc_size > size - POISON_OVERHEAD) { + /* block can be bigger than alloc_size, for reasons of alignment & fragmentation, + but block can never be smaller than head->alloc_size... */ + if (print_errors) { + MULTI_HEAP_STDERR_PRINTF("CORRUPT HEAP: Size at %p expected <=0x%08x got 0x%08x\n", &head->alloc_size, + size - POISON_OVERHEAD, head->alloc_size); + } + return false; + } + return head != NULL; + } +} + +void multi_heap_internal_poison_fill_region(void *start, size_t size, bool is_free) +{ + memset(start, is_free ? FREE_FILL_PATTERN : MALLOC_FILL_PATTERN, size); +} + +#else // !MULTI_HEAP_POISONING + +#ifdef MULTI_HEAP_POISONING_SLOW +#error "MULTI_HEAP_POISONING_SLOW requires MULTI_HEAP_POISONING" +#endif + +#endif // MULTI_HEAP_POISONING diff --git a/cpu/esp32/esp-idf/include/driver/gpio.h b/cpu/esp32/esp-idf/include/driver/gpio.h new file mode 100644 index 0000000000000..1213d1070d89a --- /dev/null +++ b/cpu/esp32/esp-idf/include/driver/gpio.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/* This file is just a mapper for source code compatibility with ESP-IDF */ + +#ifndef DRIVER_GPIO_H +#define DRIVER_GPIO_H + +#ifndef DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/* the order of these includes is important */ +#include "periph_conf.h" +#include "gpio_arch.h" + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* DRIVER_GPIO_H */ diff --git a/cpu/esp32/esp-idf/include/esp32/esp_clk.h b/cpu/esp32/esp-idf/include/esp32/esp_clk.h new file mode 100644 index 0000000000000..524762329b8be --- /dev/null +++ b/cpu/esp32/esp-idf/include/esp32/esp_clk.h @@ -0,0 +1,89 @@ +// Copyright 2015-2017 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. + +#ifndef ESP32_ESP_CLK_H +#define ESP32_ESP_CLK_H + +#ifndef DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file esp_clk.h + * + * This file contains declarations of clock related functions. + */ + +/** + * @brief Get the calibration value of RTC slow clock + * + * The value is in the same format as returned by rtc_clk_cal (microseconds, + * in Q13.19 fixed-point format). + * + * @return the calibration value obtained using rtc_clk_cal, at startup time + */ +uint32_t esp_clk_slowclk_cal_get(void); + +/** + * @brief Update the calibration value of RTC slow clock + * + * The value has to be in the same format as returned by rtc_clk_cal (microseconds, + * in Q13.19 fixed-point format). + * This value is used by timekeeping functions (such as gettimeofday) to + * calculate current time based on RTC counter value. + * @param value calibration value obtained using rtc_clk_cal + */ +void esp_clk_slowclk_cal_set(uint32_t value); + +/** + * @brief Return current CPU clock frequency + * When frequency switching is performed, this frequency may change. + * However it is guaranteed that the frequency never changes with a critical + * section. + * + * @return CPU clock frequency, in Hz + */ +int esp_clk_cpu_freq(void); + +/** + * @brief Return current APB clock frequency + * + * When frequency switching is performed, this frequency may change. + * However it is guaranteed that the frequency never changes with a critical + * section. + * + * @return APB clock frequency, in Hz + */ +int esp_clk_apb_freq(void); + + +/** + * @brief Read value of RTC counter, converting it to microseconds + * @attention The value returned by this function may change abruptly when + * calibration value of RTC counter is updated via esp_clk_slowclk_cal_set + * function. This should not happen unless application calls esp_clk_slowclk_cal_set. + * In ESP-IDF, esp_clk_slowclk_cal_set is only called in startup code. + * + * @return Value or RTC counter, expressed in microseconds + */ +uint64_t esp_clk_rtc_time(void); + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* ESP32_ESP_CLK_H */ diff --git a/cpu/esp32/esp-idf/include/esp32/esp_intr_alloc.h b/cpu/esp32/esp-idf/include/esp32/esp_intr_alloc.h new file mode 100644 index 0000000000000..b099e92b69787 --- /dev/null +++ b/cpu/esp32/esp-idf/include/esp32/esp_intr_alloc.h @@ -0,0 +1,284 @@ +// 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. + +#ifndef ESP32_ESP_INTR_ALLOC_H +#define ESP32_ESP_INTR_ALLOC_H + +#ifndef DOXYGEN + +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** @addtogroup Intr_Alloc + * @{ + */ + + +/** @brief Interrupt allocation flags + * + * These flags can be used to specify which interrupt qualities the + * code calling esp_intr_alloc* needs. + * + */ + +//Keep the LEVELx values as they are here; they match up with (1<3 + * is requested, because these types of interrupts aren't C-callable. + * @param arg Optional argument for passed to the interrupt handler + * @param ret_handle Pointer to an intr_handle_t to store a handle that can later be + * used to request details or free the interrupt. Can be NULL if no handle + * is required. + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_ERR_NOT_FOUND No free interrupt found with the specified flags + * ESP_OK otherwise + */ +esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle); + + +/** + * @brief Allocate an interrupt with the given parameters. + * + * + * This essentially does the same as esp_intr_alloc, but allows specifying a register and mask + * combo. For shared interrupts, the handler is only called if a read from the specified + * register, ANDed with the mask, returns non-zero. By passing an interrupt status register + * address and a fitting mask, this can be used to accelerate interrupt handling in the case + * a shared interrupt is triggered; by checking the interrupt statuses first, the code can + * decide which ISRs can be skipped + * + * @param source The interrupt source. One of the ETS_*_INTR_SOURCE interrupt mux + * sources, as defined in soc/soc.h, or one of the internal + * ETS_INTERNAL_*_INTR_SOURCE sources as defined in this header. + * @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the + * choice of interrupts that this routine can choose from. If this value + * is 0, it will default to allocating a non-shared interrupt of level + * 1, 2 or 3. If this is ESP_INTR_FLAG_SHARED, it will allocate a shared + * interrupt of level 1. Setting ESP_INTR_FLAG_INTRDISABLED will return + * from this function with the interrupt disabled. + * @param intrstatusreg The address of an interrupt status register + * @param intrstatusmask A mask. If a read of address intrstatusreg has any of the bits + * that are 1 in the mask set, the ISR will be called. If not, it will be + * skipped. + * @param handler The interrupt handler. Must be NULL when an interrupt of level >3 + * is requested, because these types of interrupts aren't C-callable. + * @param arg Optional argument for passed to the interrupt handler + * @param ret_handle Pointer to an intr_handle_t to store a handle that can later be + * used to request details or free the interrupt. Can be NULL if no handle + * is required. + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_ERR_NOT_FOUND No free interrupt found with the specified flags + * ESP_OK otherwise + */ +esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, void *arg, intr_handle_t *ret_handle); + + +/** + * @brief Disable and free an interrupt. + * + * Use an interrupt handle to disable the interrupt and release the resources + * associated with it. + * + * @note + * When the handler shares its source with other handlers, the interrupt status + * bits it's responsible for should be managed properly before freeing it. see + * ``esp_intr_disable`` for more details. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return ESP_ERR_INVALID_ARG if handle is invalid, or esp_intr_free runs on another core than + * where the interrupt is allocated on. + * ESP_OK otherwise + */ +esp_err_t esp_intr_free(intr_handle_t handle); + + +/** + * @brief Get CPU number an interrupt is tied to + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return The core number where the interrupt is allocated + */ +int esp_intr_get_cpu(intr_handle_t handle); + +/** + * @brief Get the allocated interrupt for a certain handle + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return The interrupt number + */ +int esp_intr_get_intno(intr_handle_t handle); + +/** + * @brief Disable the interrupt associated with the handle + * + * @note + * 1. For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the + * CPU the interrupt is allocated on. Other interrupts have no such restriction. + * 2. When several handlers sharing a same interrupt source, interrupt status bits, which are + * handled in the handler to be disabled, should be masked before the disabling, or handled + * in other enabled interrupts properly. Miss of interrupt status handling will cause infinite + * interrupt calls and finally system crash. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_OK otherwise + */ +esp_err_t esp_intr_disable(intr_handle_t handle); + +/** + * @brief Enable the interrupt associated with the handle + * + * @note For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the + * CPU the interrupt is allocated on. Other interrupts have no such restriction. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_OK otherwise + */ +esp_err_t esp_intr_enable(intr_handle_t handle); + +/** + * @brief Disable interrupts that aren't specifically marked as running from IRAM + */ +void esp_intr_noniram_disable(void); + + +/** + * @brief Re-enable interrupts disabled by esp_intr_noniram_disable + */ +void esp_intr_noniram_enable(void); + +/**@}*/ + + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* ESP32_ESP_INTR_ALLOC_H */ diff --git a/cpu/esp32/esp-idf/include/esp32/esp_phy_init.h b/cpu/esp32/esp-idf/include/esp32/esp_phy_init.h new file mode 100644 index 0000000000000..86b4a97eab89b --- /dev/null +++ b/cpu/esp32/esp-idf/include/esp32/esp_phy_init.h @@ -0,0 +1,161 @@ +// 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. + +#ifndef ESP32_ESP_PHY_INIT_H +#define ESP32_ESP_PHY_INIT_H + +#ifndef DOXYGEN + +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file PHY init parameters and API + */ + + +/** + * @brief Structure holding PHY init parameters + */ +typedef struct { + uint8_t params[128]; /*!< opaque PHY initialization parameters */ +} esp_phy_init_data_t; + +/** + * @brief Opaque PHY calibration data + */ +typedef struct { + uint8_t opaque[1904]; /*!< calibration data */ +} 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 Get PHY init data + * + * If "Use a partition to store PHY init data" option is set in menuconfig, + * This function will load PHY init data from a partition. Otherwise, + * PHY init data will be compiled into the application itself, and this function + * will return a pointer to PHY init data located in read-only memory (DROM). + * + * If "Use a partition to store PHY init data" option is enabled, this function + * may return NULL if the data loaded from flash is not valid. + * + * @note Call esp_phy_release_init_data to release the pointer obtained using + * this function after the call to esp_wifi_init. + * + * @return pointer to PHY init data structure + */ +const esp_phy_init_data_t* esp_phy_get_init_data(); + +/** + * @brief Release PHY init data + * @param data pointer to PHY init data structure obtained from + * esp_phy_get_init_data function + */ +void esp_phy_release_init_data(const esp_phy_init_data_t* data); + +/** + * @brief Function called by esp_phy_init to load PHY calibration data + * + * This is a convenience function which can be used to load PHY calibration + * data from NVS. Data can be stored to NVS using esp_phy_store_cal_data_to_nvs + * function. + * + * If calibration data is not present in the NVS, or + * data is not valid (was obtained for a chip with a different MAC address, + * or obtained for a different version of software), this function will + * return an error. + * + * If "Initialize PHY in startup code" option is set in menuconfig, this + * function will be used to load calibration data. To provide a different + * mechanism for loading calibration data, disable + * "Initialize PHY in startup code" option in menuconfig and call esp_phy_init + * function from the application. For an example usage of esp_phy_init and + * this function, see esp_phy_store_cal_data_to_nvs function in cpu_start.c + * + * @param out_cal_data pointer to calibration data structure to be filled with + * loaded data. + * @return ESP_OK on success + */ +esp_err_t esp_phy_load_cal_data_from_nvs(esp_phy_calibration_data_t* out_cal_data); + +/** + * @brief Function called by esp_phy_init to store PHY calibration data + * + * This is a convenience function which can be used to store PHY calibration + * data to the NVS. Calibration data is returned by esp_phy_init function. + * Data saved using this function to the NVS can later be loaded using + * esp_phy_store_cal_data_to_nvs function. + * + * If "Initialize PHY in startup code" option is set in menuconfig, this + * function will be used to store calibration data. To provide a different + * mechanism for storing calibration data, disable + * "Initialize PHY in startup code" option in menuconfig and call esp_phy_init + * function from the application. + * + * @param cal_data pointer to calibration data which has to be saved. + * @return ESP_OK on success + */ +esp_err_t esp_phy_store_cal_data_to_nvs(const esp_phy_calibration_data_t* cal_data); + +/** + * @brief Initialize PHY and RF module + * + * PHY and RF module should be initialized in order to use WiFi or BT. + * Now PHY and RF initializing job is done automatically when start WiFi or BT. Users should not + * call this API in their application. + * + * @param init_data PHY parameters. Default set of parameters can + * be obtained by calling esp_phy_get_default_init_data + * function. + * @param mode Calibration mode (Full, partial, or no calibration) + * @param[inout] calibration_data + * @return ESP_OK on success. + * @return ESP_FAIL on fail. + */ +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); + +/** + * @brief De-initialize PHY and RF module + * + * PHY module should be de-initialized in order to shutdown WiFi or BT. + * Now PHY and RF de-initializing job is done automatically when stop WiFi or BT. Users should not + * call this API in their application. + * + * @return ESP_OK on success. + */ +esp_err_t esp_phy_rf_deinit(void); + +/** + * @brief Load calibration data from NVS and initialize PHY and RF module + */ +void esp_phy_load_cal_and_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* ESP32_ESP_PHY_INIT_H */ diff --git a/cpu/esp32/esp-idf/include/esp32/esp_sleep.h b/cpu/esp32/esp-idf/include/esp32/esp_sleep.h new file mode 100644 index 0000000000000..8e92aabdf47a9 --- /dev/null +++ b/cpu/esp32/esp-idf/include/esp32/esp_sleep.h @@ -0,0 +1,307 @@ +// Copyright 2015-2017 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. + +#ifndef ESP32_ESP_SLEEP_H +#define ESP32_ESP_SLEEP_H + +#ifndef DOXYGEN + +#include +#include "esp_err.h" +#include "driver/gpio.h" +#include "driver/touch_pad.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Logic function used for EXT1 wakeup mode. + */ +typedef enum { + ESP_EXT1_WAKEUP_ALL_LOW = 0, //!< Wake the chip when all selected GPIOs go low + ESP_EXT1_WAKEUP_ANY_HIGH = 1 //!< Wake the chip when any of the selected GPIOs go high +} esp_sleep_ext1_wakeup_mode_t; + +/** + * @brief Power domains which can be powered down in sleep mode + */ +typedef enum { + ESP_PD_DOMAIN_RTC_PERIPH, //!< RTC IO, sensors and ULP co-processor + ESP_PD_DOMAIN_RTC_SLOW_MEM, //!< RTC slow memory + ESP_PD_DOMAIN_RTC_FAST_MEM, //!< RTC fast memory + ESP_PD_DOMAIN_MAX //!< Number of domains +} esp_sleep_pd_domain_t; + +/** + * @brief Power down options + */ +typedef enum { + ESP_PD_OPTION_OFF, //!< Power down the power domain in sleep mode + ESP_PD_OPTION_ON, //!< Keep power domain enabled during sleep mode + ESP_PD_OPTION_AUTO //!< Keep power domain enabled in sleep mode, if it is needed by one of the wakeup options. Otherwise power it down. +} esp_sleep_pd_option_t; + +/** + * @brief Sleep wakeup cause + */ +typedef enum { + ESP_SLEEP_WAKEUP_UNDEFINED, //! In case of deep sleep, reset was not caused by exit from deep sleep + ESP_SLEEP_WAKEUP_EXT0, //! Wakeup caused by external signal using RTC_IO + ESP_SLEEP_WAKEUP_EXT1, //! Wakeup caused by external signal using RTC_CNTL + ESP_SLEEP_WAKEUP_TIMER, //! Wakeup caused by timer + ESP_SLEEP_WAKEUP_TOUCHPAD, //! Wakeup caused by touchpad + ESP_SLEEP_WAKEUP_ULP, //! Wakeup caused by ULP program +} esp_sleep_wakeup_cause_t; + + +/** + * @brief Enable wakeup by ULP coprocessor + * @note In revisions 0 and 1 of the ESP32, ULP wakeup source + * can not be used when RTC_PERIPH power domain is forced + * to be powered on (ESP_PD_OPTION_ON) or when ext0 wakeup + * source is used. + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if ULP co-processor is not enabled or if wakeup triggers conflict + */ +esp_err_t esp_sleep_enable_ulp_wakeup(void); + +/** + * @brief Enable wakeup by timer + * @param time_in_us time before wakeup, in microseconds + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if value is out of range (TBD) + */ +esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us); + +/** + * @brief Enable wakeup by touch sensor + * + * @note In revisions 0 and 1 of the ESP32, touch wakeup source + * can not be used when RTC_PERIPH power domain is forced + * to be powered on (ESP_PD_OPTION_ON) or when ext0 wakeup + * source is used. + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if wakeup triggers conflict + */ +esp_err_t esp_sleep_enable_touchpad_wakeup(void); + +/** + * @brief Get the touch pad which caused wakeup + * + * If wakeup was caused by another source, this function will return TOUCH_PAD_MAX; + * + * @return touch pad which caused wakeup + */ +touch_pad_t esp_sleep_get_touchpad_wakeup_status(void); + +/** + * @brief Enable wakeup using a pin + * + * This function uses external wakeup feature of RTC_IO peripheral. + * It will work only if RTC peripherals are kept on during sleep. + * + * This feature can monitor any pin which is an RTC IO. Once the pin transitions + * into the state given by level argument, the chip will be woken up. + * + * @note This function does not modify pin configuration. The pin is + * configured in esp_sleep_start, immediately before entering sleep mode. + * + * @note In revisions 0 and 1 of the ESP32, ext0 wakeup source + * can not be used together with touch or ULP wakeup sources. + * + * @param gpio_num GPIO number used as wakeup source. Only GPIOs which are have RTC + * functionality can be used: 0,2,4,12-15,25-27,32-39. + * @param level input level which will trigger wakeup (0=low, 1=high) + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if the selected GPIO is not an RTC GPIO, + * or the mode is invalid + * - ESP_ERR_INVALID_STATE if wakeup triggers conflict + */ +esp_err_t esp_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level); + +/** + * @brief Enable wakeup using multiple pins + * + * This function uses external wakeup feature of RTC controller. + * It will work even if RTC peripherals are shut down during sleep. + * + * This feature can monitor any number of pins which are in RTC IOs. + * Once any of the selected pins goes into the state given by mode argument, + * the chip will be woken up. + * + * @note This function does not modify pin configuration. The pins are + * configured in esp_sleep_start, immediately before + * entering sleep mode. + * + * @note internal pullups and pulldowns don't work when RTC peripherals are + * shut down. In this case, external resistors need to be added. + * Alternatively, RTC peripherals (and pullups/pulldowns) may be + * kept enabled using esp_sleep_pd_config function. + * + * @param mask bit mask of GPIO numbers which will cause wakeup. Only GPIOs + * which are have RTC functionality can be used in this bit map: + * 0,2,4,12-15,25-27,32-39. + * @param mode select logic function used to determine wakeup condition: + * - ESP_EXT1_WAKEUP_ALL_LOW: wake up when all selected GPIOs are low + * - ESP_EXT1_WAKEUP_ANY_HIGH: wake up when any of the selected GPIOs is high + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if any of the selected GPIOs is not an RTC GPIO, + * or mode is invalid + */ +esp_err_t esp_sleep_enable_ext1_wakeup(uint64_t mask, esp_sleep_ext1_wakeup_mode_t mode); + + +/** + * @brief Get the bit mask of GPIOs which caused wakeup (ext1) + * + * If wakeup was caused by another source, this function will return 0. + * + * @return bit mask, if GPIOn caused wakeup, BIT(n) will be set + */ +uint64_t esp_sleep_get_ext1_wakeup_status(void); + +/** + * @brief Set power down mode for an RTC power domain in sleep mode + * + * If not set set using this API, all power domains default to ESP_PD_OPTION_AUTO. + * + * @param domain power domain to configure + * @param option power down option (ESP_PD_OPTION_OFF, ESP_PD_OPTION_ON, or ESP_PD_OPTION_AUTO) + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if either of the arguments is out of range + */ +esp_err_t esp_sleep_pd_config(esp_sleep_pd_domain_t domain, + esp_sleep_pd_option_t option); + +/** + * @brief Enter deep sleep with the configured wakeup options + * + * This function does not return. + */ +void esp_deep_sleep_start(void) __attribute__((noreturn)); + +/** + * @brief Enter light sleep with the configured wakeup options + * + * @return + * - ESP_OK on success (returned after wakeup) + * - ESP_ERR_INVALID_STATE if WiFi or BT is not stopped + */ +esp_err_t esp_light_sleep_start(void); + +/** + * @brief Enter deep-sleep mode + * + * The device will automatically wake up after the deep-sleep time + * Upon waking up, the device calls deep sleep wake stub, and then proceeds + * to load application. + * + * Call to this function is equivalent to a call to esp_deep_sleep_enable_timer_wakeup + * followed by a call to esp_deep_sleep_start. + * + * esp_deep_sleep does not shut down WiFi, BT, and higher level protocol + * connections gracefully. + * Make sure relevant WiFi and BT stack functions are called to close any + * connections and deinitialize the peripherals. These include: + * - esp_bluedroid_disable + * - esp_bt_controller_disable + * - esp_wifi_stop + * + * This function does not return. + * + * @param time_in_us deep-sleep time, unit: microsecond + */ +void esp_deep_sleep(uint64_t time_in_us) __attribute__((noreturn)); + +/** + * @brief Enter deep-sleep mode + * + * Function has been renamed to esp_deep_sleep. + * This name is deprecated and will be removed in a future version. + * + * @param time_in_us deep-sleep time, unit: microsecond + */ +void system_deep_sleep(uint64_t time_in_us) __attribute__((noreturn, deprecated)); + + +/** + * @brief Get the source which caused wakeup from sleep + * + * @return wakeup cause, or ESP_DEEP_SLEEP_WAKEUP_UNDEFINED if reset happened for reason other than deep sleep wakeup + */ +esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause(void); + + +/** + * @brief Default stub to run on wake from deep sleep. + * + * Allows for executing code immediately on wake from sleep, before + * the software bootloader or ESP-IDF app has started up. + * + * This function is weak-linked, so you can implement your own version + * to run code immediately when the chip wakes from + * sleep. + * + * See docs/deep-sleep-stub.rst for details. + */ +void esp_wake_deep_sleep(void); + +/** + * @brief Function type for stub to run on wake from sleep. + * + */ +typedef void (*esp_deep_sleep_wake_stub_fn_t)(void); + +/** + * @brief Install a new stub at runtime to run on wake from deep sleep + * + * If implementing esp_wake_deep_sleep() then it is not necessary to + * call this function. + * + * However, it is possible to call this function to substitute a + * different deep sleep stub. Any function used as a deep sleep stub + * must be marked RTC_IRAM_ATTR, and must obey the same rules given + * for esp_wake_deep_sleep(). + */ +void esp_set_deep_sleep_wake_stub(esp_deep_sleep_wake_stub_fn_t new_stub); + +/** + * @brief Get current wake from deep sleep stub + * @return Return current wake from deep sleep stub, or NULL if + * no stub is installed. + */ +esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void); + +/** + * @brief The default esp-idf-provided esp_wake_deep_sleep() stub. + * + * See docs/deep-sleep-stub.rst for details. + */ +void esp_default_wake_deep_sleep(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* ESP32_ESP_SLEEP_H */ diff --git a/cpu/esp32/esp-idf/include/esp32/esp_spiram.h b/cpu/esp32/esp-idf/include/esp32/esp_spiram.h new file mode 100644 index 0000000000000..7c90c9977e7c6 --- /dev/null +++ b/cpu/esp32/esp-idf/include/esp32/esp_spiram.h @@ -0,0 +1,100 @@ +// Copyright 2015-2017 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. + + +#ifndef ESP32_ESP_SPIRAM_H +#define ESP32_ESP_SPIRAM_H + +#ifndef DOXYGEN + +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize spiram interface/hardware. Normally called from cpu_start.c. + * + * @return ESP_OK on success + */ +esp_err_t esp_spiram_init(void); + +/** + * @brief Configure Cache/MMU for access to external SPI RAM. + * + * Normally this function is called from cpu_start, if CONFIG_SPIRAM_BOOT_INIT + * option is enabled. Applications which need to enable SPI RAM at run time + * can disable CONFIG_SPIRAM_BOOT_INIT, and call this function later. + * + * @attention this function must be called with flash cache disabled. + */ +void esp_spiram_init_cache(void); + + +/** + * @brief Memory test for SPI RAM. Should be called after SPI RAM is initialized and + * (in case of a dual-core system) the app CPU is online. This test overwrites the + * memory with crap, so do not call after e.g. the heap allocator has stored important + * stuff in SPI RAM. + * + * @return true on success, false on failed memory test + */ +bool esp_spiram_test(void); + + +/** + * @brief Add the initialized SPI RAM to the heap allocator. + */ +esp_err_t esp_spiram_add_to_heapalloc(void); + + +/** + * @brief Get the size of the attached SPI RAM chip selected in menuconfig + * + * @return Size in bytes, or 0 if no external RAM chip support compiled in. + */ +size_t esp_spiram_get_size(void); + + +/** + * @brief Force a writeback of the data in the SPI RAM cache. This is to be called whenever + * cache is disabled, because disabling cache on the ESP32 discards the data in the SPI + * RAM cache. + * + * This is meant for use from within the SPI flash code. + */ +void esp_spiram_writeback_cache(void); + + + +/** + * @brief Reserve a pool of internal memory for specific DMA/internal allocations + * + * @param size Size of reserved pool in bytes + * + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM when no memory available for pool + */ +esp_err_t esp_spiram_reserve_dma_pool(size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* ESP32_ESP_SPIRAM_H */ diff --git a/cpu/esp32/esp-idf/include/esp32/phy.h b/cpu/esp32/esp-idf/include/esp32/phy.h new file mode 100644 index 0000000000000..db6e0461e6aee --- /dev/null +++ b/cpu/esp32/esp-idf/include/esp32/phy.h @@ -0,0 +1,74 @@ +// 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. + +#ifndef ESP32_PHY_H +#define ESP32_PHY_H + +#ifndef DOXYGEN + +#include "esp_phy_init.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file phy.h + * @brief Declarations for functions provided by libphy.a + */ + +/** + * @brief Return ROM function pointer table from PHY library. + */ +void phy_get_romfunc_addr(void); + +/** + * @brief Initialize PHY module and do RF calibration + * @param[in] init_data Initialization parameters to be used by the PHY + * @param[inout] cal_data As input, calibration data previously obtained. As output, will contain new calibration data. + * @param[in] cal_mode RF calibration mode + * @return reserved for future use + */ +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); + +/** + * @brief Get the format version of calibration data used by PHY library. + * @return Format version number, OR'ed with BIT(16) if PHY is in WIFI only mode. + */ +uint32_t phy_get_rf_cal_version(void); + +/** + * @brief Set RF/BB for only WIFI mode or coexist(WIFI & BT) mode + * @param[in] true is for only WIFI mode, false is for coexist mode. default is 0. + * @return NULL + */ +void phy_set_wifi_mode_only(bool wifi_only); + +/** + * @brief Set BT the highest priority in coexist mode. + * @return NULL + */ +void coex_bt_high_prio(void); + +/** + * @brief Shutdown PHY and RF. + */ +void phy_close_rf(void); + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* ESP32_PHY_H */ diff --git a/cpu/esp32/esp-idf/include/heap/esp_heap_caps.h b/cpu/esp32/esp-idf/include/heap/esp_heap_caps.h new file mode 100644 index 0000000000000..cd20906ecf474 --- /dev/null +++ b/cpu/esp32/esp-idf/include/heap/esp_heap_caps.h @@ -0,0 +1,322 @@ +// 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. + +#ifndef HEAP_ESP_HEAP_CAPS_H +#define HEAP_ESP_HEAP_CAPS_H + +#ifndef DOXYGEN + +#include +#include +#include "multi_heap.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Flags to indicate the capabilities of the various memory systems + */ +#define MALLOC_CAP_EXEC (1<<0) ///< Memory must be able to run executable code +#define MALLOC_CAP_32BIT (1<<1) ///< Memory must allow for aligned 32-bit data accesses +#define MALLOC_CAP_8BIT (1<<2) ///< Memory must allow for 8/16/...-bit data accesses +#define MALLOC_CAP_DMA (1<<3) ///< Memory must be able to accessed by DMA +#define MALLOC_CAP_PID2 (1<<4) ///< Memory must be mapped to PID2 memory space (PIDs are not currently used) +#define MALLOC_CAP_PID3 (1<<5) ///< Memory must be mapped to PID3 memory space (PIDs are not currently used) +#define MALLOC_CAP_PID4 (1<<6) ///< Memory must be mapped to PID4 memory space (PIDs are not currently used) +#define MALLOC_CAP_PID5 (1<<7) ///< Memory must be mapped to PID5 memory space (PIDs are not currently used) +#define MALLOC_CAP_PID6 (1<<8) ///< Memory must be mapped to PID6 memory space (PIDs are not currently used) +#define MALLOC_CAP_PID7 (1<<9) ///< Memory must be mapped to PID7 memory space (PIDs are not currently used) +#define MALLOC_CAP_SPIRAM (1<<10) ///< Memory must be in SPI RAM +#define MALLOC_CAP_INTERNAL (1<<11) ///< Memory must be internal; specifically it should not disappear when flash/spiram cache is switched off +#define MALLOC_CAP_DEFAULT (1<<12) ///< Memory can be returned in a non-capability-specific memory allocation (e.g. malloc(), calloc()) call +#define MALLOC_CAP_INVALID (1<<31) ///< Memory can't be used / list end marker + +/** + * @brief Allocate a chunk of memory which has the given capabilities + * + * Equivalent semantics to libc malloc(), for capability-aware memory. + * + * In IDF, ``malloc(p)`` is equivalent to ``heap_caps_malloc(p, MALLOC_CAP_8BIT)``. + * + * @param size Size, in bytes, of the amount of memory to allocate + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory to be returned + * + * @return A pointer to the memory allocated on success, NULL on failure + */ +void *heap_caps_malloc(size_t size, uint32_t caps); + + +/** + * @brief Free memory previously allocated via heap_caps_malloc() or heap_caps_realloc(). + * + * Equivalent semantics to libc free(), for capability-aware memory. + * + * In IDF, ``free(p)`` is equivalent to ``heap_caps_free(p)``. + * + * @param ptr Pointer to memory previously returned from heap_caps_malloc() or heap_caps_realloc(). Can be NULL. + */ +void heap_caps_free( void *ptr); + +/** + * @brief Reallocate memory previously allocated via heap_caps_malloc() or heaps_caps_realloc(). + * + * Equivalent semantics to libc realloc(), for capability-aware memory. + * + * In IDF, ``realloc(p, s)`` is equivalent to ``heap_caps_realloc(p, s, MALLOC_CAP_8BIT)``. + * + * 'caps' parameter can be different to the capabilities that any original 'ptr' was allocated with. In this way, + * realloc can be used to "move" a buffer if necessary to ensure it meets a new set of capabilities. + * + * @param ptr Pointer to previously allocated memory, or NULL for a new allocation. + * @param size Size of the new buffer requested, or 0 to free the buffer. + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory desired for the new allocation. + * + * @return Pointer to a new buffer of size 'size' with capabilities 'caps', or NULL if allocation failed. + */ +void *heap_caps_realloc( void *ptr, size_t size, int caps); + +/** + * @brief Allocate a chunk of memory which has the given capabilities. The initialized value in the memory is set to zero. + * + * Equivalent semantics to libc calloc(), for capability-aware memory. + * + * In IDF, ``calloc(p)`` is equivalent to ``heaps_caps_calloc(p, MALLOC_CAP_8BIT)``. + * + * @param n Number of continuing chunks of memory to allocate + * @param size Size, in bytes, of a chunk of memory to allocate + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory to be returned + * + * @return A pointer to the memory allocated on success, NULL on failure + */ +void *heap_caps_calloc(size_t n, size_t size, uint32_t caps); + +/** + * @brief Get the total free size of all the regions that have the given capabilities + * + * This function takes all regions capable of having the given capabilities allocated in them + * and adds up the free space they have. + * + * Note that because of heap fragmentation it is probably not possible to allocate a single block of memory + * of this size. Use heap_caps_get_largest_free_block() for this purpose. + + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory + * + * @return Amount of free bytes in the regions + */ +size_t heap_caps_get_free_size( uint32_t caps ); + + +/** + * @brief Get the total minimum free memory of all regions with the given capabilities + * + * This adds all the low water marks of the regions capable of delivering the memory + * with the given capabilities. + * + * Note the result may be less than the global all-time minimum available heap of this kind, as "low water marks" are + * tracked per-region. Individual regions' heaps may have reached their "low water marks" at different points in time. However + * this result still gives a "worst case" indication for all-time minimum free heap. + * + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory + * + * @return Amount of free bytes in the regions + */ +size_t heap_caps_get_minimum_free_size( uint32_t caps ); + +/** + * @brief Get the largest free block of memory able to be allocated with the given capabilities. + * + * Returns the largest value of ``s`` for which ``heap_caps_malloc(s, caps)`` will succeed. + * + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory + * + * @return Size of largest free block in bytes. + */ +size_t heap_caps_get_largest_free_block( uint32_t caps ); + + +/** + * @brief Get heap info for all regions with the given capabilities. + * + * Calls multi_heap_info() on all heaps which share the given capabilities. The information returned is an aggregate + * across all matching heaps. The meanings of fields are the same as defined for multi_heap_info_t, except that + * ``minimum_free_bytes`` has the same caveats described in heap_caps_get_minimum_free_size(). + * + * @param info Pointer to a structure which will be filled with relevant + * heap metadata. + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory + * + */ +void heap_caps_get_info( multi_heap_info_t *info, uint32_t caps ); + + +/** + * @brief Print a summary of all memory with the given capabilities. + * + * Calls multi_heap_info on all heaps which share the given capabilities, and + * prints a two-line summary for each, then a total summary. + * + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory + * + */ +void heap_caps_print_heap_info( uint32_t caps ); + +/** + * @brief Check integrity of all heap memory in the system. + * + * Calls multi_heap_check on all heaps. Optionally print errors if heaps are corrupt. + * + * Calling this function is equivalent to calling heap_caps_check_integrity + * with the caps argument set to MALLOC_CAP_INVALID. + * + * @param print_errors Print specific errors if heap corruption is found. + * + * @return True if all heaps are valid, False if at least one heap is corrupt. + */ +bool heap_caps_check_integrity_all(bool print_errors); + +/** + * @brief Check integrity of all heaps with the given capabilities. + * + * Calls multi_heap_check on all heaps which share the given capabilities. Optionally + * print errors if the heaps are corrupt. + * + * See also heap_caps_check_integrity_all to check all heap memory + * in the system and heap_caps_check_integrity_addr to check memory + * around a single address. + * + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory + * @param print_errors Print specific errors if heap corruption is found. + * + * @return True if all heaps are valid, False if at least one heap is corrupt. + */ +bool heap_caps_check_integrity(uint32_t caps, bool print_errors); + +/** + * @brief Check integrity of heap memory around a given address. + * + * This function can be used to check the integrity of a single region of heap memory, + * which contains the given address. + * + * This can be useful if debugging heap integrity for corruption at a known address, + * as it has a lower overhead than checking all heap regions. Note that if the corrupt + * address moves around between runs (due to timing or other factors) then this approach + * won't work and you should call heap_caps_check_integrity or + * heap_caps_check_integrity_all instead. + * + * @note The entire heap region around the address is checked, not only the adjacent + * heap blocks. + * + * @param addr Address in memory. Check for corruption in region containing this address. + * @param print_errors Print specific errors if heap corruption is found. + * + * @return True if the heap containing the specified address is valid, + * False if at least one heap is corrupt or the address doesn't belong to a heap region. + */ +bool heap_caps_check_integrity_addr(intptr_t addr, bool print_errors); + +/** + * @brief Enable malloc() in external memory and set limit below which + * malloc() attempts are placed in internal memory. + * + * When external memory is in use, the allocation strategy is to initially try to + * satisfy smaller allocation requests with internal memory and larger requests + * with external memory. This sets the limit between the two, as well as generally + * enabling allocation in external memory. + * + * @param limit Limit, in bytes. + */ +void heap_caps_malloc_extmem_enable(size_t limit); + +/** + * @brief Allocate a chunk of memory as preference in decreasing order. + * + * @attention The variable parameters are bitwise OR of MALLOC_CAP_* flags indicating the type of memory. + * This API prefers to allocate memory with the first parameter. If failed, allocate memory with + * the next parameter. It will try in this order until allocating a chunk of memory successfully + * or fail to allocate memories with any of the parameters. + * + * @param size Size, in bytes, of the amount of memory to allocate + * @param num Number of variable paramters + * + * @return A pointer to the memory allocated on success, NULL on failure + */ +void *heap_caps_malloc_prefer( size_t size, size_t num, ... ); + +/** + * @brief Allocate a chunk of memory as preference in decreasing order. + * + * @param ptr Pointer to previously allocated memory, or NULL for a new allocation. + * @param size Size of the new buffer requested, or 0 to free the buffer. + * @param num Number of variable paramters + * + * @return Pointer to a new buffer of size 'size', or NULL if allocation failed. + */ +void *heap_caps_realloc_prefer( void *ptr, size_t size, size_t num, ... ); + +/** + * @brief Allocate a chunk of memory as preference in decreasing order. + * + * @param n Number of continuing chunks of memory to allocate + * @param size Size, in bytes, of a chunk of memory to allocate + * @param num Number of variable paramters + * + * @return A pointer to the memory allocated on success, NULL on failure + */ +void *heap_caps_calloc_prefer( size_t n, size_t size, size_t num, ... ); + +/** + * @brief Dump the full structure of all heaps with matching capabilities. + * + * Prints a large amount of output to serial (because of locking limitations, + * the output bypasses stdout/stderr). For each (variable sized) block + * in each matching heap, the following output is printed on a single line: + * + * - Block address (the data buffer returned by malloc is 4 bytes after this + * if heap debugging is set to Basic, or 8 bytes otherwise). + * - Data size (the data size may be larger than the size requested by malloc, + * either due to heap fragmentation or because of heap debugging level). + * - Address of next block in the heap. + * - If the block is free, the address of the next free block is also printed. + * + * @param caps Bitwise OR of MALLOC_CAP_* flags indicating the type + * of memory + */ +void heap_caps_dump(uint32_t caps); + +/** + * @brief Dump the full structure of all heaps. + * + * Covers all registered heaps. Prints a large amount of output to serial. + * + * Output is the same as for heap_caps_dump. + * + */ +void heap_caps_dump_all(void); + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* HEAP_ESP_HEAP_CAPS_H */ diff --git a/cpu/esp32/esp-idf/include/heap/esp_heap_caps_init.h b/cpu/esp32/esp-idf/include/heap/esp_heap_caps_init.h new file mode 100644 index 0000000000000..eef3a47429e6b --- /dev/null +++ b/cpu/esp32/esp-idf/include/heap/esp_heap_caps_init.h @@ -0,0 +1,98 @@ +// Copyright 2017 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. + +#ifndef HEAP_ESP_HEAP_CAPS_INIT_H +#define HEAP_ESP_HEAP_CAPS_INIT_H + +#ifndef DOXYGEN + +#include "esp_err.h" +#include "esp_heap_caps.h" +#include "soc/soc_memory_layout.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize the capability-aware heap allocator. + * + * This is called once in the IDF startup code. Do not call it + * at other times. + */ +void heap_caps_init(void); + +/** + * @brief Enable heap(s) in memory regions where the startup stacks are located. + * + * On startup, the pro/app CPUs have a certain memory region they use as stack, so we + * cannot do allocations in the regions these stack frames are. When FreeRTOS is + * completely started, they do not use that memory anymore and heap(s) there can + * be enabled. + */ +void heap_caps_enable_nonos_stack_heaps(void); + +/** + * @brief Add a region of memory to the collection of heaps at runtime. + * + * Most memory regions are defined in soc_memory_layout.c for the SoC, + * and are registered via heap_caps_init(). Some regions can't be used + * immediately and are later enabled via heap_caps_enable_nonos_stack_heaps(). + * + * Call this function to add a region of memory to the heap at some later time. + * + * This function does not consider any of the "reserved" regions or other data in soc_memory_layout, caller needs to + * consider this themselves. + * + * All memory within the region specified by start & end parameters must be otherwise unused. + * + * The capabilities of the newly registered memory will be determined by the start address, as looked up in the regions + * specified in soc_memory_layout.c. + * + * Use heap_caps_add_region_with_caps() to register a region with custom capabilities. + * + * @param start Start address of new region. + * @param end End address of new region. + * + * @return ESP_OK on success, ESP_ERR_INVALID_ARG if a parameter is invalid, ESP_ERR_NOT_FOUND if the + * specified start address doesn't reside in a known region, or any error returned by heap_caps_add_region_with_caps(). + */ +esp_err_t heap_caps_add_region(intptr_t start, intptr_t end); + + +/** + * @brief Add a region of memory to the collection of heaps at runtime, with custom capabilities. + * + * Similar to heap_caps_add_region(), only custom memory capabilities are specified by the caller. + * + * @param caps Ordered array of capability masks for the new region, in order of priority. Must have length + * SOC_MEMORY_TYPE_NO_PRIOS. Does not need to remain valid after the call returns. + * @param start Start address of new region. + * @param end End address of new region. + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_ARG if a parameter is invalid + * - ESP_ERR_NO_MEM if no memory to register new heap. + * - ESP_FAIL if region overlaps the start and/or end of an existing region + */ +esp_err_t heap_caps_add_region_with_caps(const uint32_t caps[], intptr_t start, intptr_t end); + + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* HEAP_ESP_HEAP_CAPS_INIT_H */ diff --git a/cpu/esp32/esp-idf/include/log/esp_log.h b/cpu/esp32/esp-idf/include/log/esp_log.h new file mode 100644 index 0000000000000..164c08ab66627 --- /dev/null +++ b/cpu/esp32/esp-idf/include/log/esp_log.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/* This file is just a mapper for source code compatibility with ESP-IDF */ + +#ifndef ESP32_ESP_LOG_H +#define ESP32_ESP_LOG_H + +#ifndef DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +#include "common.h" + +/** + * @brief Log level + * + */ +typedef enum { + ESP_LOG_NONE, /*!< No log output */ + ESP_LOG_ERROR, /*!< Critical errors, software module can not recover on its own */ + ESP_LOG_WARN, /*!< Error conditions from which recovery measures have been taken */ + ESP_LOG_INFO, /*!< Information messages which describe normal flow of events */ + ESP_LOG_DEBUG, /*!< Extra information which is not necessary for normal use (values, pointers, sizes, etc). */ + ESP_LOG_VERBOSE /*!< Bigger chunks of debugging information, or frequent messages which can potentially flood the output. */ +} esp_log_level_t; + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* ESP32_SP_LOG_H */ diff --git a/cpu/esp32/esp-idf/include/newlib/ctype.h b/cpu/esp32/esp-idf/include/newlib/ctype.h new file mode 100644 index 0000000000000..926e0ee406e86 --- /dev/null +++ b/cpu/esp32/esp-idf/include/newlib/ctype.h @@ -0,0 +1,117 @@ +#ifndef NEWLIB_CTYPE_H +#define NEWLIB_CTYPE_H + +#ifndef DOXYGEN + +#include "_ansi.h" + +_BEGIN_STD_C + +int _EXFUN(isalnum, (int __c)); +int _EXFUN(isalpha, (int __c)); +int _EXFUN(iscntrl, (int __c)); +int _EXFUN(isdigit, (int __c)); +int _EXFUN(isgraph, (int __c)); +int _EXFUN(islower, (int __c)); +int _EXFUN(isprint, (int __c)); +int _EXFUN(ispunct, (int __c)); +int _EXFUN(isspace, (int __c)); +int _EXFUN(isupper, (int __c)); +int _EXFUN(isxdigit,(int __c)); +int _EXFUN(tolower, (int __c)); +int _EXFUN(toupper, (int __c)); + +#if !defined(__STRICT_ANSI__) || defined(__cplusplus) || __STDC_VERSION__ >= 199901L +int _EXFUN(isblank, (int __c)); +#endif + +#ifndef __STRICT_ANSI__ +int _EXFUN(isascii, (int __c)); +int _EXFUN(toascii, (int __c)); +#define _tolower(__c) ((unsigned char)(__c) - 'A' + 'a') +#define _toupper(__c) ((unsigned char)(__c) - 'a' + 'A') +#endif + +#define _U 01 +#define _L 02 +#define _N 04 +#define _S 010 +#define _P 020 +#define _C 040 +#define _X 0100 +#define _B 0200 + +extern +#ifndef _MB_CAPABLE +_CONST +#endif +__IMPORT char * _CONST __ctype_ptr__; + +#ifndef __cplusplus +/* These macros are intentionally written in a manner that will trigger + a gcc -Wall warning if the user mistakenly passes a 'char' instead + of an int containing an 'unsigned char'. Note that the sizeof will + always be 1, which is what we want for mapping EOF to __ctype_ptr__[0]; + the use of a raw index inside the sizeof triggers the gcc warning if + __c was of type char, and sizeof masks side effects of the extra __c. + Meanwhile, the real index to __ctype_ptr__+1 must be cast to int, + since isalpha(0x100000001LL) must equal isalpha(1), rather than being + an out-of-bounds reference on a 64-bit machine. */ +#define __ctype_lookup(__c) ((__ctype_ptr__+sizeof(""[__c]))[(int)(__c)]) + +#define isalpha(__c) (__ctype_lookup(__c)&(_U|_L)) +#define isupper(__c) ((__ctype_lookup(__c)&(_U|_L))==_U) +#define islower(__c) ((__ctype_lookup(__c)&(_U|_L))==_L) +#define isdigit(__c) (__ctype_lookup(__c)&_N) +#define isxdigit(__c) (__ctype_lookup(__c)&(_X|_N)) +#define isspace(__c) (__ctype_lookup(__c)&_S) +#define ispunct(__c) (__ctype_lookup(__c)&_P) +#define isalnum(__c) (__ctype_lookup(__c)&(_U|_L|_N)) +#define isprint(__c) (__ctype_lookup(__c)&(_P|_U|_L|_N|_B)) +#define isgraph(__c) (__ctype_lookup(__c)&(_P|_U|_L|_N)) +#define iscntrl(__c) (__ctype_lookup(__c)&_C) + +#if defined(__GNUC__) && \ + (!defined(__STRICT_ANSI__) || __STDC_VERSION__ >= 199901L) +#define isblank(__c) \ + __extension__ ({ __typeof__ (__c) __x = (__c); \ + (__ctype_lookup(__x)&_B) || (int) (__x) == '\t';}) +#endif + + +/* Non-gcc versions will get the library versions, and will be + slightly slower. These macros are not NLS-aware so they are + disabled if the system supports the extended character sets. */ +# if defined(__GNUC__) +# if !defined (_MB_EXTENDED_CHARSETS_ISO) && !defined (_MB_EXTENDED_CHARSETS_WINDOWS) +# define toupper(__c) \ + __extension__ ({ __typeof__ (__c) __x = (__c); \ + islower (__x) ? (int) __x - 'a' + 'A' : (int) __x;}) +# define tolower(__c) \ + __extension__ ({ __typeof__ (__c) __x = (__c); \ + isupper (__x) ? (int) __x - 'A' + 'a' : (int) __x;}) +# else /* _MB_EXTENDED_CHARSETS* */ +/* Allow a gcc warning if the user passed 'char', but defer to the + function. */ +# define toupper(__c) \ + __extension__ ({ __typeof__ (__c) __x = (__c); \ + (void) __ctype_ptr__[__x]; (toupper) (__x);}) +# define tolower(__c) \ + __extension__ ({ __typeof__ (__c) __x = (__c); \ + (void) __ctype_ptr__[__x]; (tolower) (__x);}) +# endif /* _MB_EXTENDED_CHARSETS* */ +# endif /* __GNUC__ */ +#endif /* !__cplusplus */ + +#ifndef __STRICT_ANSI__ +#define isascii(__c) ((unsigned)(__c)<=0177) +#define toascii(__c) ((__c)&0177) +#endif + +/* For C++ backward-compatibility only. */ +extern __IMPORT _CONST char _ctype_[]; + +_END_STD_C + +#endif /* DOXYGEN */ +#endif /* NEWLIB_CTYPE_H */ diff --git a/cpu/esp32/esp-idf/include/soc/cpu.h b/cpu/esp32/esp-idf/include/soc/cpu.h new file mode 100644 index 0000000000000..3aabc7923b5be --- /dev/null +++ b/cpu/esp32/esp-idf/include/soc/cpu.h @@ -0,0 +1,115 @@ +// Copyright 2010-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. + +#ifndef SOC_CPU_H +#define SOC_CPU_H + +#ifndef DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include "xtensa/corebits.h" + +/* C macros for xtensa special register read/write/exchange */ + +#define RSR(reg, curval) asm volatile ("rsr %0, " #reg : "=r" (curval)); +#define WSR(reg, newval) asm volatile ("wsr %0, " #reg : : "r" (newval)); +#define XSR(reg, swapval) asm volatile ("xsr %0, " #reg : "+r" (swapval)); + +/** @brief Read current stack pointer address + * + */ +static inline void *get_sp(void) +{ + void *sp; + asm volatile ("mov %0, sp;" : "=r" (sp)); + return sp; +} + +/* Functions to set page attributes for Region Protection option in the CPU. + * See Xtensa ISA Reference manual for explanation of arguments (section 4.6.3.2). + */ + +static inline void cpu_write_dtlb(uint32_t vpn, unsigned attr) +{ + asm volatile ("wdtlb %1, %0; dsync\n" :: "r" (vpn), "r" (attr)); +} + + +static inline void cpu_write_itlb(unsigned vpn, unsigned attr) +{ + asm volatile ("witlb %1, %0; isync\n" :: "r" (vpn), "r" (attr)); +} + +/** + * @brief Configure memory region protection + * + * Make page 0 access raise an exception. + * Also protect some other unused pages so we can catch weirdness. + * Useful attribute values: + * 0 — cached, RW + * 2 — bypass cache, RWX (default value after CPU reset) + * 15 — no access, raise exception + */ + +static inline void cpu_configure_region_protection(void) +{ + const uint32_t pages_to_protect[] = {0x00000000, 0x80000000, 0xa0000000, 0xc0000000, 0xe0000000}; + for (unsigned i = 0; i < sizeof(pages_to_protect)/sizeof(pages_to_protect[0]); ++i) { + cpu_write_dtlb(pages_to_protect[i], 0xf); + cpu_write_itlb(pages_to_protect[i], 0xf); + } + cpu_write_dtlb(0x20000000, 0); + cpu_write_itlb(0x20000000, 0); +} + +/** + * @brief Stall CPU using RTC controller + * @param cpu_id ID of the CPU to stall (0 = PRO, 1 = APP) + */ +void esp_cpu_stall(int cpu_id); + +/** + * @brief Un-stall CPU using RTC controller + * @param cpu_id ID of the CPU to un-stall (0 = PRO, 1 = APP) + */ +void esp_cpu_unstall(int cpu_id); + +/** + * @brief Reset CPU using RTC controller + * @param cpu_id ID of the CPU to reset (0 = PRO, 1 = APP) + */ +void esp_cpu_reset(int cpu_id); + + +/** + * @brief Returns true if a JTAG debugger is attached to CPU + * OCD (on chip debug) port. + * + * @note If "Make exception and panic handlers JTAG/OCD aware" + * is disabled, this function always returns false. + */ +bool esp_cpu_in_ocd_debug_mode(void); + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* SOC_CPU_H */ diff --git a/cpu/esp32/esp-idf/include/soc/rtc.h b/cpu/esp32/esp-idf/include/soc/rtc.h new file mode 100644 index 0000000000000..418334bc68b93 --- /dev/null +++ b/cpu/esp32/esp-idf/include/soc/rtc.h @@ -0,0 +1,617 @@ +// Copyright 2015-2017 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. + +#ifndef SOC_RTC_H +#define SOC_RTC_H + +#ifndef DOXYGEN + +#include +#include +#include +#include "soc/soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file rtc.h + * @brief Low-level RTC power, clock, and sleep functions. + * + * Functions in this file facilitate configuration of ESP32's RTC_CNTL peripheral. + * RTC_CNTL peripheral handles many functions: + * - enables/disables clocks and power to various parts of the chip; this is + * done using direct register access (forcing power up or power down) or by + * allowing state machines to control power and clocks automatically + * - handles sleep and wakeup functions + * - maintains a 48-bit counter which can be used for timekeeping + * + * These functions are not thread safe, and should not be viewed as high level + * APIs. For example, while this file provides a function which can switch + * CPU frequency, this function is on its own is not sufficient to implement + * frequency switching in ESP-IDF context: some coordination with RTOS, + * peripheral drivers, and WiFi/BT stacks is also required. + * + * These functions will normally not be used in applications directly. + * ESP-IDF provides, or will provide, drivers and other facilities to use + * RTC subsystem functionality. + * + * The functions are loosely split into the following groups: + * - rtc_clk: clock switching, calibration + * - rtc_time: reading RTC counter, conversion between counter values and time + * - rtc_sleep: entry into sleep modes + * - rtc_init: initialization + */ + + +/** + * @brief Possible main XTAL frequency values. + * + * Enum values should be equal to frequency in MHz. + */ +typedef enum { + RTC_XTAL_FREQ_AUTO = 0, //!< Automatic XTAL frequency detection + RTC_XTAL_FREQ_40M = 40, //!< 40 MHz XTAL + RTC_XTAL_FREQ_26M = 26, //!< 26 MHz XTAL + RTC_XTAL_FREQ_24M = 24, //!< 24 MHz XTAL +} rtc_xtal_freq_t; + +/** + * @brief CPU frequency values + */ +typedef enum { + RTC_CPU_FREQ_XTAL = 0, //!< Main XTAL frequency + RTC_CPU_FREQ_80M = 1, //!< 80 MHz + RTC_CPU_FREQ_160M = 2, //!< 160 MHz + RTC_CPU_FREQ_240M = 3, //!< 240 MHz + RTC_CPU_FREQ_2M = 4, //!< 2 MHz +} rtc_cpu_freq_t; + +/** + * @brief RTC SLOW_CLK frequency values + */ +typedef enum { + RTC_SLOW_FREQ_RTC = 0, //!< Internal 150 kHz RC oscillator + RTC_SLOW_FREQ_32K_XTAL = 1, //!< External 32 kHz XTAL + RTC_SLOW_FREQ_8MD256 = 2, //!< Internal 8 MHz RC oscillator, divided by 256 +} rtc_slow_freq_t; + +/** + * @brief RTC FAST_CLK frequency values + */ +typedef enum { + RTC_FAST_FREQ_XTALD4 = 0, //!< Main XTAL, divided by 4 + RTC_FAST_FREQ_8M = 1, //!< Internal 8 MHz RC oscillator +} rtc_fast_freq_t; + +/* With the default value of CK8M_DFREQ, 8M clock frequency is 8.5 MHz +/- 7% */ +#define RTC_FAST_CLK_FREQ_APPROX 8500000 + +/** + * @brief Clock source to be calibrated using rtc_clk_cal function + */ +typedef enum { + RTC_CAL_RTC_MUX = 0, //!< Currently selected RTC SLOW_CLK + RTC_CAL_8MD256 = 1, //!< Internal 8 MHz RC oscillator, divided by 256 + RTC_CAL_32K_XTAL = 2 //!< External 32 kHz XTAL +} rtc_cal_sel_t; + +/** + * Initialization parameters for rtc_clk_init + */ +typedef struct { + rtc_xtal_freq_t xtal_freq : 8; //!< Main XTAL frequency + rtc_cpu_freq_t cpu_freq : 3; //!< CPU frequency to set + rtc_fast_freq_t fast_freq : 1; //!< RTC_FAST_CLK frequency to set + rtc_slow_freq_t slow_freq : 2; //!< RTC_SLOW_CLK frequency to set + uint32_t clk_8m_div : 3; //!< RTC 8M clock divider (division is by clk_8m_div+1, i.e. 0 means 8MHz frequency) + uint32_t slow_clk_dcap : 8; //!< RTC 150k clock adjustment parameter (higher value leads to lower frequency) + uint32_t clk_8m_dfreq : 8; //!< RTC 8m clock adjustment parameter (higher value leads to higher frequency) +} rtc_clk_config_t; + +/** + * Default initializer for rtc_clk_config_t + */ +#define RTC_CLK_CONFIG_DEFAULT() { \ + .xtal_freq = RTC_XTAL_FREQ_AUTO, \ + .cpu_freq = RTC_CPU_FREQ_80M, \ + .fast_freq = RTC_FAST_FREQ_8M, \ + .slow_freq = RTC_SLOW_FREQ_RTC, \ + .clk_8m_div = 0, \ + .slow_clk_dcap = RTC_CNTL_SCK_DCAP_DEFAULT, \ + .clk_8m_dfreq = RTC_CNTL_CK8M_DFREQ_DEFAULT, \ +} + +/** + * Initialize clocks and set CPU frequency + * + * If cfg.xtal_freq is set to RTC_XTAL_FREQ_AUTO, this function will attempt + * to auto detect XTAL frequency. Auto detection is performed by comparing + * XTAL frequency with the frequency of internal 8MHz oscillator. Note that at + * high temperatures the frequency of the internal 8MHz oscillator may drift + * enough for auto detection to be unreliable. + * Auto detection code will attempt to distinguish between 26MHz and 40MHz + * crystals. 24 MHz crystals are not supported by auto detection code. + * If XTAL frequency can not be auto detected, this 26MHz frequency will be used. + * + * @param cfg clock configuration as rtc_clk_config_t + */ +void rtc_clk_init(rtc_clk_config_t cfg); + +/** + * @brief Get main XTAL frequency + * + * This is the value stored in RTC register RTC_XTAL_FREQ_REG by the bootloader. As passed to + * rtc_clk_init function, or if the value was RTC_XTAL_FREQ_AUTO, the detected + * XTAL frequency. + * + * @return XTAL frequency, one of rtc_xtal_freq_t + */ +rtc_xtal_freq_t rtc_clk_xtal_freq_get(void); + +/** + * @brief Update XTAL frequency + * + * Updates the XTAL value stored in RTC_XTAL_FREQ_REG. Usually this value is ignored + * after startup. + * + * @param xtal_freq New frequency value + */ +void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq); + +/** + * @brief Enable or disable 32 kHz XTAL oscillator + * @param en true to enable, false to disable + */ +void rtc_clk_32k_enable(bool en); + +/** + * @brief Get the state of 32k XTAL oscillator + * @return true if 32k XTAL oscillator has been enabled + */ +bool rtc_clk_32k_enabled(void); + +/** + * @brief Enable 32k oscillator, configuring it for fast startup time. + * Note: to achieve higher frequency stability, rtc_clk_32k_enable function + * must be called one the 32k XTAL oscillator has started up. This function + * will initially disable the 32k XTAL oscillator, so it should not be called + * when the system is using 32k XTAL as RTC_SLOW_CLK. + */ +void rtc_clk_32k_bootstrap(void); + +/** + * @brief Enable or disable 8 MHz internal oscillator + * + * Output from 8 MHz internal oscillator is passed into a configurable + * divider, which by default divides the input clock frequency by 256. + * Output of the divider may be used as RTC_SLOW_CLK source. + * Output of the divider is referred to in register descriptions and code as + * 8md256 or simply d256. Divider values other than 256 may be configured, but + * this facility is not currently needed, so is not exposed in the code. + * + * When 8MHz/256 divided output is not needed, the divider should be disabled + * to reduce power consumption. + * + * @param clk_8m_en true to enable 8MHz generator + * @param d256_en true to enable /256 divider + */ +void rtc_clk_8m_enable(bool clk_8m_en, bool d256_en); + +/** + * @brief Get the state of 8 MHz internal oscillator + * @return true if the oscillator is enabled + */ +bool rtc_clk_8m_enabled(void); + +/** + * @brief Get the state of /256 divider which is applied to 8MHz clock + * @return true if the divided output is enabled + */ +bool rtc_clk_8md256_enabled(void); + +/** + * @brief Enable or disable APLL + * + * Output frequency is given by the formula: + * apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536)/((o_div + 2) * 2) + * + * The dividend in this expression should be in the range of 240 - 600 MHz. + * + * In rev. 0 of ESP32, sdm0 and sdm1 are unused and always set to 0. + * + * @param enable true to enable, false to disable + * @param sdm0 frequency adjustment parameter, 0..255 + * @param sdm1 frequency adjustment parameter, 0..255 + * @param sdm2 frequency adjustment parameter, 0..63 + * @param o_div frequency divider, 0..31 + */ +void rtc_clk_apll_enable(bool enable, uint32_t sdm0, uint32_t sdm1, + uint32_t sdm2, uint32_t o_div); + +/** + * @brief Select source for RTC_SLOW_CLK + * @param slow_freq clock source (one of rtc_slow_freq_t values) + */ +void rtc_clk_slow_freq_set(rtc_slow_freq_t slow_freq); + +/** + * @brief Get the RTC_SLOW_CLK source + * @return currently selected clock source (one of rtc_slow_freq_t values) + */ +rtc_slow_freq_t rtc_clk_slow_freq_get(void); + +/** + * @brief Get the approximate frequency of RTC_SLOW_CLK, in Hz + * + * - if RTC_SLOW_FREQ_RTC is selected, returns ~150000 + * - if RTC_SLOW_FREQ_32K_XTAL is selected, returns 32768 + * - if RTC_SLOW_FREQ_8MD256 is selected, returns ~33000 + * + * rtc_clk_cal function can be used to get more precise value by comparing + * RTC_SLOW_CLK frequency to the frequency of main XTAL. + * + * @return RTC_SLOW_CLK frequency, in Hz + */ +uint32_t rtc_clk_slow_freq_get_hz(void); + +/** + * @brief Select source for RTC_FAST_CLK + * @param fast_freq clock source (one of rtc_fast_freq_t values) + */ +void rtc_clk_fast_freq_set(rtc_fast_freq_t fast_freq); + +/** + * @brief Get the RTC_FAST_CLK source + * @return currently selected clock source (one of rtc_fast_freq_t values) + */ +rtc_fast_freq_t rtc_clk_fast_freq_get(void); + +/** + * @brief Switch CPU frequency + * + * If a PLL-derived frequency is requested (80, 160, 240 MHz), this function + * will enable the PLL. Otherwise, PLL will be disabled. + * Note: this function is not optimized for switching speed. It may take several + * hundred microseconds to perform frequency switch. + * + * @param cpu_freq new CPU frequency + */ +void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq); + +/** + * @brief Switch CPU frequency + * + * This is a faster version of rtc_clk_cpu_freq_set, which can handle some of + * the frequency switch paths (XTAL -> PLL, PLL -> XTAL). + * When switching from PLL to XTAL, PLL is not disabled (unlike rtc_clk_cpu_freq_set). + * When switching back from XTAL to PLL, only the same PLL can be used. + * Therefore it is not possible to switch 240 -> XTAL -> (80 or 160) using this + * function. + * + * For unsupported cases, this function falls back to rtc_clk_cpu_freq_set. + * + * Unlike rtc_clk_cpu_freq_set, this function relies on static data, so it is + * less safe to use it e.g. from a panic handler (when memory might be corrupted). + * + * @param cpu_freq new CPU frequency + */ +void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq); + +/** + * @brief Get the currently selected CPU frequency + * + * Although CPU can be clocked by APLL and RTC 8M sources, such support is not + * exposed through this library. As such, this function will not return + * meaningful values when these clock sources are configured (e.g. using direct + * access to clock selection registers). In debug builds, it will assert; in + * release builds, it will return RTC_CPU_FREQ_XTAL. + * + * @return CPU frequency (one of rtc_cpu_freq_t values) + */ +rtc_cpu_freq_t rtc_clk_cpu_freq_get(void); + +/** + * @brief Get corresponding frequency value for rtc_cpu_freq_t enum value + * @param cpu_freq CPU frequency, on of rtc_cpu_freq_t values + * @return CPU frequency, in HZ + */ +uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq); + +/** + * @brief Get rtc_cpu_freq_t enum value for given CPU frequency + * @param cpu_freq_mhz CPU frequency, one of 80, 160, 240, 2, and XTAL frequency + * @param[out] out_val output, rtc_cpu_freq_t value corresponding to the frequency + * @return true if the given frequency value matches one of enum values + */ + bool rtc_clk_cpu_freq_from_mhz(int cpu_freq_mhz, rtc_cpu_freq_t* out_val); + +/** + * @brief Store new APB frequency value into RTC_APB_FREQ_REG + * + * This function doesn't change any hardware clocks. + * + * Functions which perform frequency switching and change APB frequency call + * this function to update the value of APB frequency stored in RTC_APB_FREQ_REG + * (one of RTC general purpose retention registers). This should not normally + * be called from application code. + * + * @param apb_freq new APB frequency, in Hz + */ +void rtc_clk_apb_freq_update(uint32_t apb_freq); + +/** + * @brief Get the current stored APB frequency. + * @return The APB frequency value as last set via rtc_clk_apb_freq_update(), in Hz. + */ +uint32_t rtc_clk_apb_freq_get(void); + +#define RTC_CLK_CAL_FRACT 19 //!< Number of fractional bits in values returned by rtc_clk_cal + +/** + * @brief Measure RTC slow clock's period, based on main XTAL frequency + * + * This function will time out and return 0 if the time for the given number + * of cycles to be counted exceeds the expected time twice. This may happen if + * 32k XTAL is being calibrated, but the oscillator has not started up (due to + * incorrect loading capacitance, board design issue, or lack of 32 XTAL on board). + * + * @param cal_clk clock to be measured + * @param slow_clk_cycles number of slow clock cycles to average + * @return average slow clock period in microseconds, Q13.19 fixed point format, + * or 0 if calibration has timed out + */ +uint32_t rtc_clk_cal(rtc_cal_sel_t cal_clk, uint32_t slow_clk_cycles); + +/** + * @brief Measure ratio between XTAL frequency and RTC slow clock frequency + * @param cal_clk slow clock to be measured + * @param slow_clk_cycles number of slow clock cycles to average + * @return average ratio between XTAL frequency and slow clock frequency, + * Q13.19 fixed point format, or 0 if calibration has timed out. + */ +uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slow_clk_cycles); + +/** + * @brief Convert time interval from microseconds to RTC_SLOW_CLK cycles + * @param time_in_us Time interval in microseconds + * @param slow_clk_period Period of slow clock in microseconds, Q13.19 + * fixed point format (as returned by rtc_slowck_cali). + * @return number of slow clock cycles + */ +uint64_t rtc_time_us_to_slowclk(uint64_t time_in_us, uint32_t period); + +/** + * @brief Convert time interval from RTC_SLOW_CLK to microseconds + * @param time_in_us Time interval in RTC_SLOW_CLK cycles + * @param slow_clk_period Period of slow clock in microseconds, Q13.19 + * fixed point format (as returned by rtc_slowck_cali). + * @return time interval in microseconds + */ +uint64_t rtc_time_slowclk_to_us(uint64_t rtc_cycles, uint32_t period); + +/** + * @brief Get current value of RTC counter + * + * RTC has a 48-bit counter which is incremented by 2 every 2 RTC_SLOW_CLK + * cycles. Counter value is not writable by software. The value is not adjusted + * when switching to a different RTC_SLOW_CLK source. + * + * Note: this function may take up to 1 RTC_SLOW_CLK cycle to execute + * + * @return current value of RTC counter + */ +uint64_t rtc_time_get(void); + +/** + * @brief Busy loop until next RTC_SLOW_CLK cycle + * + * This function returns not earlier than the next RTC_SLOW_CLK clock cycle. + * In some cases (e.g. when RTC_SLOW_CLK cycle is very close), it may return + * one RTC_SLOW_CLK cycle later. + */ +void rtc_clk_wait_for_slow_cycle(void); + +/** + * @brief sleep configuration for rtc_sleep_init function + */ +typedef struct { + uint32_t soc_clk_sel : 2; //!< SoC clock select, see RTC_CNTL_SOC_CLK_SEL + uint32_t lslp_mem_inf_fpu : 1; //!< force normal voltage in sleep mode (digital domain memory) + uint32_t rtc_mem_inf_fpu : 1; //!< force normal voltage in sleep mode (RTC memory) + uint32_t rtc_mem_inf_follow_cpu : 1;//!< keep low voltage in sleep mode (even if ULP/touch is used) + uint32_t rtc_fastmem_pd_en : 1; //!< power down RTC fast memory + uint32_t rtc_slowmem_pd_en : 1; //!< power down RTC slow memory + uint32_t rtc_peri_pd_en : 1; //!< power down RTC peripherals + uint32_t wifi_pd_en : 1; //!< power down WiFi + uint32_t rom_mem_pd_en : 1; //!< power down main RAM and ROM + uint32_t deep_slp : 1; //!< power down digital domain + uint32_t wdt_flashboot_mod_en : 1; //!< enable WDT flashboot mode + uint32_t dig_dbias_wak : 3; //!< set bias for digital domain, in active mode + uint32_t dig_dbias_slp : 3; //!< set bias for digital domain, in sleep mode + uint32_t rtc_dbias_wak : 3; //!< set bias for RTC domain, in active mode + uint32_t rtc_dbias_slp : 3; //!< set bias for RTC domain, in sleep mode + uint32_t lslp_meminf_pd : 1; //!< remove all peripheral force power up flags + uint32_t vddsdio_pd_en : 1; //!< power down VDDSDIO regulator +} rtc_sleep_config_t; + +/** + * Default initializer for rtc_sleep_config_t + * + * This initializer sets all fields to "reasonable" values (e.g. suggested for + * production use) based on a combination of RTC_SLEEP_PD_x flags. + * + * @param RTC_SLEEP_PD_x flags combined using bitwise OR + */ +#define RTC_SLEEP_CONFIG_DEFAULT(sleep_flags) { \ + .soc_clk_sel = RTC_CNTL_SOC_CLK_SEL_XTL, \ + .lslp_mem_inf_fpu = 0, \ + .rtc_mem_inf_fpu = 0, \ + .rtc_mem_inf_follow_cpu = ((sleep_flags) & RTC_SLEEP_PD_RTC_MEM_FOLLOW_CPU) ? 1 : 0, \ + .rtc_fastmem_pd_en = ((sleep_flags) & RTC_SLEEP_PD_RTC_FAST_MEM) ? 1 : 0, \ + .rtc_slowmem_pd_en = ((sleep_flags) & RTC_SLEEP_PD_RTC_SLOW_MEM) ? 1 : 0, \ + .rtc_peri_pd_en = ((sleep_flags) & RTC_SLEEP_PD_RTC_PERIPH) ? 1 : 0, \ + .wifi_pd_en = 0, \ + .rom_mem_pd_en = 0, \ + .deep_slp = ((sleep_flags) & RTC_SLEEP_PD_DIG) ? 1 : 0, \ + .wdt_flashboot_mod_en = 0, \ + .dig_dbias_wak = RTC_CNTL_DBIAS_1V10, \ + .dig_dbias_slp = RTC_CNTL_DBIAS_0V90, \ + .rtc_dbias_wak = RTC_CNTL_DBIAS_1V10, \ + .rtc_dbias_slp = RTC_CNTL_DBIAS_0V90, \ + .lslp_meminf_pd = 1, \ + .vddsdio_pd_en = ((sleep_flags) & RTC_SLEEP_PD_VDDSDIO) ? 1 : 0, \ +}; + +#define RTC_SLEEP_PD_DIG BIT(0) //!< Deep sleep (power down digital domain) +#define RTC_SLEEP_PD_RTC_PERIPH BIT(1) //!< Power down RTC peripherals +#define RTC_SLEEP_PD_RTC_SLOW_MEM BIT(2) //!< Power down RTC SLOW memory +#define RTC_SLEEP_PD_RTC_FAST_MEM BIT(3) //!< Power down RTC FAST memory +#define RTC_SLEEP_PD_RTC_MEM_FOLLOW_CPU BIT(4) //!< RTC FAST and SLOW memories are automatically powered up and down along with the CPU +#define RTC_SLEEP_PD_VDDSDIO BIT(5) //!< Power down VDDSDIO regulator + +/** + * @brief Prepare the chip to enter sleep mode + * + * This function configures various power control state machines to handle + * entry into light sleep or deep sleep mode, switches APB and CPU clock source + * (usually to XTAL), and sets bias voltages for digital and RTC power domains. + * + * This function does not actually enter sleep mode; this is done using + * rtc_sleep_start function. Software may do some other actions between + * rtc_sleep_init and rtc_sleep_start, such as set wakeup timer and configure + * wakeup sources. + * @param cfg sleep mode configuration + */ +void rtc_sleep_init(rtc_sleep_config_t cfg); + + +/** + * @brief Set target value of RTC counter for RTC_TIMER_TRIG_EN wakeup source + * @param t value of RTC counter at which wakeup from sleep will happen; + * only the lower 48 bits are used + */ +void rtc_sleep_set_wakeup_time(uint64_t t); + + +#define RTC_EXT0_TRIG_EN BIT(0) //!< EXT0 GPIO wakeup +#define RTC_EXT1_TRIG_EN BIT(1) //!< EXT1 GPIO wakeup +#define RTC_GPIO_TRIG_EN BIT(2) //!< GPIO wakeup (light sleep only) +#define RTC_TIMER_TRIG_EN BIT(3) //!< Timer wakeup +#define RTC_SDIO_TRIG_EN BIT(4) //!< SDIO wakeup (light sleep only) +#define RTC_MAC_TRIG_EN BIT(5) //!< MAC wakeup (light sleep only) +#define RTC_UART0_TRIG_EN BIT(6) //!< UART0 wakeup (light sleep only) +#define RTC_UART1_TRIG_EN BIT(7) //!< UART1 wakeup (light sleep only) +#define RTC_TOUCH_TRIG_EN BIT(8) //!< Touch wakeup +#define RTC_ULP_TRIG_EN BIT(9) //!< ULP wakeup +#define RTC_BT_TRIG_EN BIT(10) //!< BT wakeup (light sleep only) + +/** + * @brief Enter deep or light sleep mode + * + * This function enters the sleep mode previously configured using rtc_sleep_init + * function. Before entering sleep, software should configure wake up sources + * appropriately (set up GPIO wakeup registers, timer wakeup registers, + * and so on). + * + * If deep sleep mode was configured using rtc_sleep_init, and sleep is not + * rejected by hardware (based on reject_opt flags), this function never returns. + * When the chip wakes up from deep sleep, CPU is reset and execution starts + * from ROM bootloader. + * + * If light sleep mode was configured using rtc_sleep_init, this function + * returns on wakeup, or if sleep is rejected by hardware. + * + * @param wakeup_opt bit mask wake up reasons to enable (RTC_xxx_TRIG_EN flags + * combined with OR) + * @param reject_opt bit mask of sleep reject reasons: + * - RTC_CNTL_GPIO_REJECT_EN + * - RTC_CNTL_SDIO_REJECT_EN + * These flags are used to prevent entering sleep when e.g. + * an external host is communicating via SDIO slave + * @return non-zero if sleep was rejected by hardware + */ +uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt); + +/** + * RTC power and clock control initialization settings + */ +typedef struct { + uint32_t ck8m_wait : 8; //!< Number of rtc_fast_clk cycles to wait for 8M clock to be ready + uint32_t xtal_wait : 8; //!< Number of rtc_fast_clk cycles to wait for XTAL clock to be ready + uint32_t pll_wait : 8; //!< Number of rtc_fast_clk cycles to wait for PLL to be ready + uint32_t clkctl_init : 1; //!< Perform clock control related initialization + uint32_t pwrctl_init : 1; //!< Perform power control related initialization + uint32_t rtc_dboost_fpd : 1; //!< Force power down RTC_DBOOST +} rtc_config_t; + +/** + * Default initializer of rtc_config_t. + * + * This initializer sets all fields to "reasonable" values (e.g. suggested for + * production use). + */ +#define RTC_CONFIG_DEFAULT() {\ + .ck8m_wait = RTC_CNTL_CK8M_WAIT_DEFAULT, \ + .xtal_wait = RTC_CNTL_XTL_BUF_WAIT_DEFAULT, \ + .pll_wait = RTC_CNTL_PLL_BUF_WAIT_DEFAULT, \ + .clkctl_init = 1, \ + .pwrctl_init = 1, \ + .rtc_dboost_fpd = 1 \ +} + +/** + * Initialize RTC clock and power control related functions + * @param cfg configuration options as rtc_config_t + */ +void rtc_init(rtc_config_t cfg); + +/** + * Structure describing vddsdio configuration + */ +typedef struct { + uint32_t force : 1; //!< If 1, use configuration from RTC registers; if 0, use EFUSE/bootstrapping pins. + uint32_t enable : 1; //!< Enable VDDSDIO regulator + uint32_t tieh : 1; //!< Select VDDSDIO voltage: 1 — 1.8V, 0 — 3.3V + uint32_t drefh : 2; //!< Tuning parameter for VDDSDIO regulator + uint32_t drefm : 2; //!< Tuning parameter for VDDSDIO regulator + uint32_t drefl : 2; //!< Tuning parameter for VDDSDIO regulator +} rtc_vddsdio_config_t; + +/** + * Get current VDDSDIO configuration + * If VDDSDIO configuration is overridden by RTC, get values from RTC + * Otherwise, if VDDSDIO is configured by EFUSE, get values from EFUSE + * Otherwise, use default values and the level of MTDI bootstrapping pin. + * @return currently used VDDSDIO configuration + */ +rtc_vddsdio_config_t rtc_vddsdio_get_config(void); + +/** + * Set new VDDSDIO configuration using RTC registers. + * If config.force == 1, this overrides configuration done using bootstrapping + * pins and EFUSE. + * + * @param config new VDDSDIO configuration + */ +void rtc_vddsdio_set_config(rtc_vddsdio_config_t config); + + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* SOC_RTC_H */ diff --git a/cpu/esp32/esp-idf/include/spi_flash/esp_partition.h b/cpu/esp32/esp-idf/include/spi_flash/esp_partition.h new file mode 100644 index 0000000000000..d44f2e6747cab --- /dev/null +++ b/cpu/esp32/esp-idf/include/spi_flash/esp_partition.h @@ -0,0 +1,297 @@ +// 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. + +#ifndef SPI_FLASH_ESP_PARTITION_H +#define SPI_FLASH_ESP_PARTITION_H + +#ifndef DOXYGEN + +#include +#include +#include +#include "esp_err.h" +#include "esp_spi_flash.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file esp_partition.h + * @brief Partition APIs + */ + + +/** + * @brief Partition type + * @note Keep this enum in sync with PartitionDefinition class gen_esp32part.py + */ +typedef enum { + ESP_PARTITION_TYPE_APP = 0x00, //!< Application partition type + ESP_PARTITION_TYPE_DATA = 0x01, //!< Data partition type +} esp_partition_type_t; + +/** + * @brief Partition subtype + * @note Keep this enum in sync with PartitionDefinition class gen_esp32part.py + */ +typedef enum { + ESP_PARTITION_SUBTYPE_APP_FACTORY = 0x00, //!< Factory application partition + ESP_PARTITION_SUBTYPE_APP_OTA_MIN = 0x10, //!< Base for OTA partition subtypes + ESP_PARTITION_SUBTYPE_APP_OTA_0 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 0, //!< OTA partition 0 + ESP_PARTITION_SUBTYPE_APP_OTA_1 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 1, //!< OTA partition 1 + ESP_PARTITION_SUBTYPE_APP_OTA_2 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 2, //!< OTA partition 2 + ESP_PARTITION_SUBTYPE_APP_OTA_3 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 3, //!< OTA partition 3 + ESP_PARTITION_SUBTYPE_APP_OTA_4 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 4, //!< OTA partition 4 + ESP_PARTITION_SUBTYPE_APP_OTA_5 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 5, //!< OTA partition 5 + ESP_PARTITION_SUBTYPE_APP_OTA_6 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 6, //!< OTA partition 6 + ESP_PARTITION_SUBTYPE_APP_OTA_7 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 7, //!< OTA partition 7 + ESP_PARTITION_SUBTYPE_APP_OTA_8 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 8, //!< OTA partition 8 + ESP_PARTITION_SUBTYPE_APP_OTA_9 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 9, //!< OTA partition 9 + ESP_PARTITION_SUBTYPE_APP_OTA_10 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 10,//!< OTA partition 10 + ESP_PARTITION_SUBTYPE_APP_OTA_11 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 11,//!< OTA partition 11 + ESP_PARTITION_SUBTYPE_APP_OTA_12 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 12,//!< OTA partition 12 + ESP_PARTITION_SUBTYPE_APP_OTA_13 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 13,//!< OTA partition 13 + ESP_PARTITION_SUBTYPE_APP_OTA_14 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 14,//!< OTA partition 14 + ESP_PARTITION_SUBTYPE_APP_OTA_15 = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 15,//!< OTA partition 15 + ESP_PARTITION_SUBTYPE_APP_OTA_MAX = ESP_PARTITION_SUBTYPE_APP_OTA_MIN + 16,//!< Max subtype of OTA partition + ESP_PARTITION_SUBTYPE_APP_TEST = 0x20, //!< Test application partition + + ESP_PARTITION_SUBTYPE_DATA_OTA = 0x00, //!< OTA selection partition + ESP_PARTITION_SUBTYPE_DATA_PHY = 0x01, //!< PHY init data partition + ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02, //!< NVS partition + ESP_PARTITION_SUBTYPE_DATA_COREDUMP = 0x03, //!< COREDUMP partition + + ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD = 0x80, //!< ESPHTTPD partition + ESP_PARTITION_SUBTYPE_DATA_FAT = 0x81, //!< FAT partition + ESP_PARTITION_SUBTYPE_DATA_SPIFFS = 0x82, //!< SPIFFS partition + + ESP_PARTITION_SUBTYPE_ANY = 0xff, //!< Used to search for partitions with any subtype +} esp_partition_subtype_t; + +/** + * @brief Convenience macro to get esp_partition_subtype_t value for the i-th OTA partition + */ +#define ESP_PARTITION_SUBTYPE_OTA(i) ((esp_partition_subtype_t)(ESP_PARTITION_SUBTYPE_APP_OTA_MIN + ((i) & 0xf))) + +/** + * @brief Opaque partition iterator type + */ +typedef struct esp_partition_iterator_opaque_* esp_partition_iterator_t; + +/** + * @brief partition information structure + * + * This is not the format in flash, that format is esp_partition_info_t. + * + * However, this is the format used by this API. + */ +typedef struct { + esp_partition_type_t type; /*!< partition type (app/data) */ + esp_partition_subtype_t subtype; /*!< partition subtype */ + uint32_t address; /*!< starting address of the partition in flash */ + uint32_t size; /*!< size of the partition, in bytes */ + char label[17]; /*!< partition label, zero-terminated ASCII string */ + bool encrypted; /*!< flag is set to true if partition is encrypted */ +} esp_partition_t; + +/** + * @brief Find partition based on one or more parameters + * + * @param type Partition type, one of esp_partition_type_t values + * @param subtype Partition subtype, one of esp_partition_subtype_t values. + * To find all partitions of given type, use + * ESP_PARTITION_SUBTYPE_ANY. + * @param label (optional) Partition label. Set this value if looking + * for partition with a specific name. Pass NULL otherwise. + * + * @return iterator which can be used to enumerate all the partitions found, + * or NULL if no partitions were found. + * Iterator obtained through this function has to be released + * using esp_partition_iterator_release when not used any more. + */ +esp_partition_iterator_t esp_partition_find(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* label); + +/** + * @brief Find first partition based on one or more parameters + * + * @param type Partition type, one of esp_partition_type_t values + * @param subtype Partition subtype, one of esp_partition_subtype_t values. + * To find all partitions of given type, use + * ESP_PARTITION_SUBTYPE_ANY. + * @param label (optional) Partition label. Set this value if looking + * for partition with a specific name. Pass NULL otherwise. + * + * @return pointer to esp_partition_t structure, or NULL if no partition is found. + * This pointer is valid for the lifetime of the application. + */ +const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* label); + +/** + * @brief Get esp_partition_t structure for given partition + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + * @return pointer to esp_partition_t structure. This pointer is valid for the lifetime + * of the application. + */ +const esp_partition_t* esp_partition_get(esp_partition_iterator_t iterator); + +/** + * @brief Move partition iterator to the next partition found + * + * Any copies of the iterator will be invalid after this call. + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + * @return NULL if no partition was found, valid esp_partition_iterator_t otherwise. + */ +esp_partition_iterator_t esp_partition_next(esp_partition_iterator_t iterator); + +/** + * @brief Release partition iterator + * + * @param iterator Iterator obtained using esp_partition_find. Must be non-NULL. + * + */ +void esp_partition_iterator_release(esp_partition_iterator_t iterator); + +/** + * @brief Verify partition data + * + * Given a pointer to partition data, verify this partition exists in the partition table (all fields match.) + * + * This function is also useful to take partition data which may be in a RAM buffer and convert it to a pointer to the + * permanent partition data stored in flash. + * + * Pointers returned from this function can be compared directly to the address of any pointer returned from + * esp_partition_get(), as a test for equality. + * + * @param partition Pointer to partition data to verify. Must be non-NULL. All fields of this structure must match the + * partition table entry in flash for this function to return a successful match. + * + * @return + * - If partition not found, returns NULL. + * - If found, returns a pointer to the esp_partition_t structure in flash. This pointer is always valid for the lifetime of the application. + */ +const esp_partition_t *esp_partition_verify(const esp_partition_t *partition); + +/** + * @brief Read data from the partition + * + * @param partition Pointer to partition structure obtained using + * esp_partition_find_first or esp_partition_get. + * Must be non-NULL. + * @param dst Pointer to the buffer where data should be stored. + * Pointer must be non-NULL and buffer must be at least 'size' bytes long. + * @param src_offset Address of the data to be read, relative to the + * beginning of the partition. + * @param size Size of data to be read, in bytes. + * + * @return ESP_OK, if data was read successfully; + * ESP_ERR_INVALID_ARG, if src_offset exceeds partition size; + * ESP_ERR_INVALID_SIZE, if read would go out of bounds of the partition; + * or one of error codes from lower-level flash driver. + */ +esp_err_t esp_partition_read(const esp_partition_t* partition, + size_t src_offset, void* dst, size_t size); + +/** + * @brief Write data to the partition + * + * Before writing data to flash, corresponding region of flash needs to be erased. + * This can be done using esp_partition_erase_range function. + * + * Partitions marked with an encryption flag will automatically be + * written via the spi_flash_write_encrypted() function. If writing to + * an encrypted partition, all write offsets and lengths must be + * multiples of 16 bytes. See the spi_flash_write_encrypted() function + * for more details. Unencrypted partitions do not have this + * restriction. + * + * @param partition Pointer to partition structure obtained using + * esp_partition_find_first or esp_partition_get. + * Must be non-NULL. + * @param dst_offset Address where the data should be written, relative to the + * beginning of the partition. + * @param src Pointer to the source buffer. Pointer must be non-NULL and + * buffer must be at least 'size' bytes long. + * @param size Size of data to be written, in bytes. + * + * @note Prior to writing to flash memory, make sure it has been erased with + * esp_partition_erase_range call. + * + * @return ESP_OK, if data was written successfully; + * ESP_ERR_INVALID_ARG, if dst_offset exceeds partition size; + * ESP_ERR_INVALID_SIZE, if write would go out of bounds of the partition; + * or one of error codes from lower-level flash driver. + */ +esp_err_t esp_partition_write(const esp_partition_t* partition, + size_t dst_offset, const void* src, size_t size); + +/** + * @brief Erase part of the partition + * + * @param partition Pointer to partition structure obtained using + * esp_partition_find_first or esp_partition_get. + * Must be non-NULL. + * @param start_addr Address where erase operation should start. Must be aligned + * to 4 kilobytes. + * @param size Size of the range which should be erased, in bytes. + * Must be divisible by 4 kilobytes. + * + * @return ESP_OK, if the range was erased successfully; + * ESP_ERR_INVALID_ARG, if iterator or dst are NULL; + * ESP_ERR_INVALID_SIZE, if erase would go out of bounds of the partition; + * or one of error codes from lower-level flash driver. + */ +esp_err_t esp_partition_erase_range(const esp_partition_t* partition, + uint32_t start_addr, uint32_t size); + +/** + * @brief Configure MMU to map partition into data memory + * + * Unlike spi_flash_mmap function, which requires a 64kB aligned base address, + * this function doesn't impose such a requirement. + * If offset results in a flash address which is not aligned to 64kB boundary, + * address will be rounded to the lower 64kB boundary, so that mapped region + * includes requested range. + * Pointer returned via out_ptr argument will be adjusted to point to the + * requested offset (not necessarily to the beginning of mmap-ed region). + * + * To release mapped memory, pass handle returned via out_handle argument to + * spi_flash_munmap function. + * + * @param partition Pointer to partition structure obtained using + * esp_partition_find_first or esp_partition_get. + * Must be non-NULL. + * @param offset Offset from the beginning of partition where mapping should start. + * @param size Size of the area to be mapped. + * @param memory Memory space where the region should be mapped + * @param out_ptr Output, pointer to the mapped memory region + * @param out_handle Output, handle which should be used for spi_flash_munmap call + * + * @return ESP_OK, if successful + */ +esp_err_t esp_partition_mmap(const esp_partition_t* partition, uint32_t offset, uint32_t size, + spi_flash_mmap_memory_t memory, + const void** out_ptr, spi_flash_mmap_handle_t* out_handle); + + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* SPI_FLASH_ESP_PARTITION_H */ diff --git a/cpu/esp32/esp-idf/include/spi_flash/esp_spi_flash.h b/cpu/esp32/esp-idf/include/spi_flash/esp_spi_flash.h new file mode 100644 index 0000000000000..52e78594eff62 --- /dev/null +++ b/cpu/esp32/esp-idf/include/spi_flash/esp_spi_flash.h @@ -0,0 +1,408 @@ +// 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. + +#ifndef SPI_FLASH_ESP_SPI_FLASH_H +#define SPI_FLASH_ESP_SPI_FLASH_H + +#ifndef DOXYGEN + +#include +#include +#include +#include "esp_err.h" +#include "sdkconfig.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ESP_ERR_FLASH_BASE 0x10010 +#define ESP_ERR_FLASH_OP_FAIL (ESP_ERR_FLASH_BASE + 1) +#define ESP_ERR_FLASH_OP_TIMEOUT (ESP_ERR_FLASH_BASE + 2) + +#define SPI_FLASH_SEC_SIZE 4096 /**< SPI Flash sector size */ + +#define SPI_FLASH_MMU_PAGE_SIZE 0x10000 /**< Flash cache MMU mapping page size */ + +/** + * @brief Initialize SPI flash access driver + * + * This function must be called exactly once, before any other + * spi_flash_* functions are called. + * Currently this function is called from startup code. There is + * no need to call it from application code. + * + */ +void spi_flash_init(void); + +/** + * @brief Get flash chip size, as set in binary image header + * + * @note This value does not necessarily match real flash size. + * + * @return size of flash chip, in bytes + */ +size_t spi_flash_get_chip_size(void); + +/** + * @brief Erase the Flash sector. + * + * @param sector Sector number, the count starts at sector 0, 4KB per sector. + * + * @return esp_err_t + */ +esp_err_t spi_flash_erase_sector(size_t sector); + +/** + * @brief Erase a range of flash sectors + * + * @param start_address Address where erase operation has to start. + * Must be 4kB-aligned + * @param size Size of erased range, in bytes. Must be divisible by 4kB. + * + * @return esp_err_t + */ +esp_err_t spi_flash_erase_range(size_t start_address, size_t size); + + +/** + * @brief Write data to Flash. + * + * @note If source address is in DROM, this function will return + * ESP_ERR_INVALID_ARG. + * + * @param dest_addr destination address in Flash. Must be a multiple of 4 bytes. + * @param src pointer to the source buffer. + * @param size length of data, in bytes. Must be a multiple of 4 bytes. + * + * @return esp_err_t + */ +esp_err_t spi_flash_write(size_t dest_addr, const void *src, size_t size); + + +/** + * @brief Write data encrypted to Flash. + * + * @note Flash encryption must be enabled for this function to work. + * + * @note Flash encryption must be enabled when calling this function. + * If flash encryption is disabled, the function returns + * ESP_ERR_INVALID_STATE. Use esp_flash_encryption_enabled() + * function to determine if flash encryption is enabled. + * + * @note Both dest_addr and size must be multiples of 16 bytes. For + * absolute best performance, both dest_addr and size arguments should + * be multiples of 32 bytes. + * + * @param dest_addr destination address in Flash. Must be a multiple of 16 bytes. + * @param src pointer to the source buffer. + * @param size length of data, in bytes. Must be a multiple of 16 bytes. + * + * @return esp_err_t + */ +esp_err_t spi_flash_write_encrypted(size_t dest_addr, const void *src, size_t size); + +/** + * @brief Read data from Flash. + * + * @param src_addr source address of the data in Flash. + * @param dest pointer to the destination buffer + * @param size length of data + * + * @return esp_err_t + */ +esp_err_t spi_flash_read(size_t src_addr, void *dest, size_t size); + + +/** + * @brief Read data from Encrypted Flash. + * + * If flash encryption is enabled, this function will transparently decrypt data as it is read. + * If flash encryption is not enabled, this function behaves the same as spi_flash_read(). + * + * See esp_flash_encryption_enabled() for a function to check if flash encryption is enabled. + * + * @param src source address of the data in Flash. + * @param dest pointer to the destination buffer + * @param size length of data + * + * @return esp_err_t + */ +esp_err_t spi_flash_read_encrypted(size_t src, void *dest, size_t size); + +/** + * @brief Enumeration which specifies memory space requested in an mmap call + */ +typedef enum { + SPI_FLASH_MMAP_DATA, /**< map to data memory (Vaddr0), allows byte-aligned access, 4 MB total */ + SPI_FLASH_MMAP_INST, /**< map to instruction memory (Vaddr1-3), allows only 4-byte-aligned access, 11 MB total */ +} spi_flash_mmap_memory_t; + +/** + * @brief Opaque handle for memory region obtained from spi_flash_mmap. + */ +typedef uint32_t spi_flash_mmap_handle_t; + +/** + * @brief Map region of flash memory into data or instruction address space + * + * This function allocates sufficient number of 64k MMU pages and configures + * them to map request region of flash memory into data address space or into + * instruction address space. It may reuse MMU pages which already provide + * required mapping. As with any allocator, there is possibility of fragmentation + * of address space if mmap/munmap are heavily used. To troubleshoot issues with + * page allocation, use spi_flash_mmap_dump function. + * + * @param src_addr Physical address in flash where requested region starts. + * This address *must* be aligned to 64kB boundary + * (SPI_FLASH_MMU_PAGE_SIZE). + * @param size Size of region which has to be mapped. This size will be rounded + * up to a 64k boundary. + * @param memory Memory space where the region should be mapped + * @param out_ptr Output, pointer to the mapped memory region + * @param out_handle Output, handle which should be used for spi_flash_munmap call + * + * @return ESP_OK on success, ESP_ERR_NO_MEM if pages can not be allocated + */ +esp_err_t spi_flash_mmap(size_t src_addr, size_t size, spi_flash_mmap_memory_t memory, + const void** out_ptr, spi_flash_mmap_handle_t* out_handle); + +/** + * @brief Map sequences of pages of flash memory into data or instruction address space + * + * This function allocates sufficient number of 64k MMU pages and configures + * them to map the indicated pages of flash memory contiguously into data address + * space or into instruction address space. In this respect, it works in a similar + * way as spi_flash_mmap but it allows mapping a (maybe non-contiguous) set of pages + * into a contiguous region of memory. + * + * @param pages An array of numbers indicating the 64K pages in flash to be mapped + * contiguously into memory. These indicate the indexes of the 64K pages, + * not the byte-size addresses as used in other functions. + * @param pagecount Size of the pages array + * @param memory Memory space where the region should be mapped + * @param out_ptr Output, pointer to the mapped memory region + * @param out_handle Output, handle which should be used for spi_flash_munmap call + * + * @return ESP_OK on success, ESP_ERR_NO_MEM if pages can not be allocated + */ +esp_err_t spi_flash_mmap_pages(int *pages, size_t pagecount, spi_flash_mmap_memory_t memory, + const void** out_ptr, spi_flash_mmap_handle_t* out_handle); + + +/** + * @brief Release region previously obtained using spi_flash_mmap + * + * @note Calling this function will not necessarily unmap memory region. + * Region will only be unmapped when there are no other handles which + * reference this region. In case of partially overlapping regions + * it is possible that memory will be unmapped partially. + * + * @param handle Handle obtained from spi_flash_mmap + */ +void spi_flash_munmap(spi_flash_mmap_handle_t handle); + +/** + * @brief Display information about mapped regions + * + * This function lists handles obtained using spi_flash_mmap, along with range + * of pages allocated to each handle. It also lists all non-zero entries of + * MMU table and corresponding reference counts. + */ +void spi_flash_mmap_dump(void); + +/** + * @brief get free pages number which can be mmap + * + * This function will return free page number of the mmu table which can mmap, + * when you want to call spi_flash_mmap to mmap an ranger of flash data to Dcache or Icache + * memmory region, maybe the size of MMU table will exceed,so if you are not sure the + * size need mmap is ok, can call the interface and watch how many MMU table page can be + * mmaped. + * + * @param memory memmory type of MMU table free page + * + * @return number of free pages which can be mmaped + */ +uint32_t spi_flash_mmap_get_free_pages(spi_flash_mmap_memory_t memory); + + +#define SPI_FLASH_CACHE2PHYS_FAIL UINT32_MAX /* +#include +#include +#include +#include "rom/ets_sys.h" +#include "rom/rtc.h" +#include "rom/uart.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/rtc_io_reg.h" +#include "soc/sens_reg.h" +#include "soc/dport_reg.h" +#include "soc/efuse_reg.h" +#include "soc/apb_ctrl_reg.h" +#include "i2c_rtc_clk.h" +#include "soc_log.h" +#include "sdkconfig.h" +#include "xtensa/core-macros.h" + + +#define MHZ (1000000) + +/* Frequency of the 8M oscillator is 8.5MHz +/- 5%, at the default DCAP setting */ +#define RTC_FAST_CLK_FREQ_8M 8500000 +#define RTC_SLOW_CLK_FREQ_150K 150000 +#define RTC_SLOW_CLK_FREQ_8MD256 (RTC_FAST_CLK_FREQ_8M / 256) +#define RTC_SLOW_CLK_FREQ_32K 32768 + +static const char* TAG = "rtc_clk"; + +/* Various constants related to the analog internals of the chip. + * Defined here because they don't have any use outside of this file. + */ + +#define BBPLL_ENDIV5_VAL_320M 0x43 +#define BBPLL_BBADC_DSMP_VAL_320M 0x84 +#define BBPLL_ENDIV5_VAL_480M 0xc3 +#define BBPLL_BBADC_DSMP_VAL_480M 0x74 + +#define APLL_SDM_STOP_VAL_1 0x09 +#define APLL_SDM_STOP_VAL_2_REV0 0x69 +#define APLL_SDM_STOP_VAL_2_REV1 0x49 + +#define APLL_CAL_DELAY_1 0x0f +#define APLL_CAL_DELAY_2 0x3f +#define APLL_CAL_DELAY_3 0x1f + +#define XTAL_32K_DAC_VAL 1 +#define XTAL_32K_DRES_VAL 3 +#define XTAL_32K_DBIAS_VAL 0 + +#define XTAL_32K_BOOTSTRAP_DAC_VAL 3 +#define XTAL_32K_BOOTSTRAP_DRES_VAL 3 +#define XTAL_32K_BOOTSTRAP_DBIAS_VAL 0 +#define XTAL_32K_BOOTSTRAP_TIME_US 7 + +/* Delays for various clock sources to be enabled/switched. + * All values are in microseconds. + * TODO: some of these are excessive, and should be reduced. + */ +#define DELAY_PLL_DBIAS_RAISE 3 +#define DELAY_PLL_ENABLE_WITH_150K 80 +#define DELAY_PLL_ENABLE_WITH_32K 160 +#define DELAY_FAST_CLK_SWITCH 3 +#define DELAY_SLOW_CLK_SWITCH 300 +#define DELAY_8M_ENABLE 50 + +/* Number of 8M/256 clock cycles to use for XTAL frequency estimation. + * 10 cycles will take approximately 300 microseconds. + */ +#define XTAL_FREQ_EST_CYCLES 10 + +/* Core voltage needs to be increased in two cases: + * 1. running at 240 MHz + * 2. running with 80MHz Flash frequency + */ +#ifdef CONFIG_ESPTOOLPY_FLASHFREQ_80M +#define DIG_DBIAS_80M_160M RTC_CNTL_DBIAS_1V25 +#else +#define DIG_DBIAS_80M_160M RTC_CNTL_DBIAS_1V10 +#endif +#define DIG_DBIAS_240M RTC_CNTL_DBIAS_1V25 +#define DIG_DBIAS_XTAL RTC_CNTL_DBIAS_1V10 +#define DIG_DBIAS_2M RTC_CNTL_DBIAS_1V00 + + +static rtc_cpu_freq_t s_cur_freq = RTC_CPU_FREQ_XTAL; +static int s_pll_freq = 0; + +static void rtc_clk_32k_enable_internal(int dac, int dres, int dbias) +{ + SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL); + CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, + RTC_IO_X32P_RDE | RTC_IO_X32P_RUE | RTC_IO_X32N_RUE | + RTC_IO_X32N_RDE | RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL); + REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DAC_XTAL_32K, dac); + REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DRES_XTAL_32K, dres); + REG_SET_FIELD(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_DBIAS_XTAL_32K, dbias); + SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K); +} + +void rtc_clk_32k_enable(bool enable) +{ + if (enable) { + rtc_clk_32k_enable_internal(XTAL_32K_DAC_VAL, XTAL_32K_DRES_VAL, XTAL_32K_DBIAS_VAL); + } else { + CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K); + } +} + +void rtc_clk_32k_bootstrap(void) +{ + CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K); + SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_RUE | RTC_IO_X32N_RDE); + ets_delay_us(XTAL_32K_BOOTSTRAP_TIME_US); + rtc_clk_32k_enable_internal(XTAL_32K_BOOTSTRAP_DAC_VAL, + XTAL_32K_BOOTSTRAP_DRES_VAL, XTAL_32K_BOOTSTRAP_DBIAS_VAL); +} + +bool rtc_clk_32k_enabled(void) +{ + return GET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_XPD_XTAL_32K) != 0; +} + +void rtc_clk_8m_enable(bool clk_8m_en, bool d256_en) +{ + if (clk_8m_en) { + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M); + /* no need to wait once enabled by software */ + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, 1); + if (d256_en) { + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M_DIV); + } else { + SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M_DIV); + } + ets_delay_us(DELAY_8M_ENABLE); + } else { + SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M); + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, RTC_CNTL_CK8M_WAIT_DEFAULT); + } +} + +bool rtc_clk_8m_enabled(void) +{ + return GET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M) == 0; +} + +bool rtc_clk_8md256_enabled(void) +{ + return GET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M_DIV) == 0; +} + +void rtc_clk_apll_enable(bool enable, uint32_t sdm0, uint32_t sdm1, uint32_t sdm2, uint32_t o_div) +{ + REG_SET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD, enable ? 0 : 1); + REG_SET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PU, enable ? 1 : 0); + + if (!enable && + REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL) != RTC_CNTL_SOC_CLK_SEL_PLL) { + REG_SET_BIT(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FORCE_PD); + } else { + REG_CLR_BIT(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FORCE_PD); + } + + if (enable) { + uint8_t sdm_stop_val_2 = APLL_SDM_STOP_VAL_2_REV1; + uint32_t is_rev0 = (GET_PERI_REG_BITS2(EFUSE_BLK0_RDATA3_REG, 1, 15) == 0); + if (is_rev0) { + sdm0 = 0; + sdm1 = 0; + sdm_stop_val_2 = APLL_SDM_STOP_VAL_2_REV0; + } + I2C_WRITEREG_MASK_RTC(I2C_APLL, I2C_APLL_DSDM2, sdm2); + I2C_WRITEREG_MASK_RTC(I2C_APLL, I2C_APLL_DSDM0, sdm0); + I2C_WRITEREG_MASK_RTC(I2C_APLL, I2C_APLL_DSDM1, sdm1); + I2C_WRITEREG_RTC(I2C_APLL, I2C_APLL_SDM_STOP, APLL_SDM_STOP_VAL_1); + I2C_WRITEREG_RTC(I2C_APLL, I2C_APLL_SDM_STOP, sdm_stop_val_2); + I2C_WRITEREG_MASK_RTC(I2C_APLL, I2C_APLL_OR_OUTPUT_DIV, o_div); + + /* calibration */ + I2C_WRITEREG_RTC(I2C_APLL, I2C_APLL_IR_CAL_DELAY, APLL_CAL_DELAY_1); + I2C_WRITEREG_RTC(I2C_APLL, I2C_APLL_IR_CAL_DELAY, APLL_CAL_DELAY_2); + I2C_WRITEREG_RTC(I2C_APLL, I2C_APLL_IR_CAL_DELAY, APLL_CAL_DELAY_3); + + /* wait for calibration end */ + while (!(I2C_READREG_MASK_RTC(I2C_APLL, I2C_APLL_OR_CAL_END))) { + /* use ets_delay_us so the RTC bus doesn't get flooded */ + ets_delay_us(1); + } + } +} + +void rtc_clk_slow_freq_set(rtc_slow_freq_t slow_freq) +{ + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL, slow_freq); + ets_delay_us(DELAY_SLOW_CLK_SWITCH); +} + +rtc_slow_freq_t rtc_clk_slow_freq_get(void) +{ + return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL); +} + +uint32_t rtc_clk_slow_freq_get_hz(void) +{ + switch(rtc_clk_slow_freq_get()) { + case RTC_SLOW_FREQ_RTC: return RTC_SLOW_CLK_FREQ_150K; + case RTC_SLOW_FREQ_32K_XTAL: return RTC_SLOW_CLK_FREQ_32K; + case RTC_SLOW_FREQ_8MD256: return RTC_SLOW_CLK_FREQ_8MD256; + } + return 0; +} + +void rtc_clk_fast_freq_set(rtc_fast_freq_t fast_freq) +{ + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL, fast_freq); + ets_delay_us(DELAY_FAST_CLK_SWITCH); +} + +rtc_fast_freq_t rtc_clk_fast_freq_get(void) +{ + return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL); +} + +void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq) +{ + uint8_t div_ref; + uint8_t div7_0; + uint8_t div10_8; + uint8_t lref; + uint8_t dcur; + uint8_t bw; + + if (cpu_freq != RTC_CPU_FREQ_240M) { + /* Raise the voltage, if needed */ + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M); + /* Configure 320M PLL */ + switch (xtal_freq) { + case RTC_XTAL_FREQ_40M: + div_ref = 0; + div7_0 = 32; + div10_8 = 0; + lref = 0; + dcur = 6; + bw = 3; + break; + case RTC_XTAL_FREQ_26M: + div_ref = 12; + div7_0 = 224; + div10_8 = 4; + lref = 1; + dcur = 0; + bw = 1; + break; + case RTC_XTAL_FREQ_24M: + div_ref = 11; + div7_0 = 224; + div10_8 = 4; + lref = 1; + dcur = 0; + bw = 1; + break; + default: + div_ref = 12; + div7_0 = 224; + div10_8 = 4; + lref = 0; + dcur = 0; + bw = 0; + break; + } + I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_ENDIV5, BBPLL_ENDIV5_VAL_320M); + I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_BBADC_DSMP, BBPLL_BBADC_DSMP_VAL_320M); + } else { + /* Raise the voltage */ + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_240M); + ets_delay_us(DELAY_PLL_DBIAS_RAISE); + /* Configure 480M PLL */ + switch (xtal_freq) { + case RTC_XTAL_FREQ_40M: + div_ref = 0; + div7_0 = 28; + div10_8 = 0; + lref = 0; + dcur = 6; + bw = 3; + break; + case RTC_XTAL_FREQ_26M: + div_ref = 12; + div7_0 = 144; + div10_8 = 4; + lref = 1; + dcur = 0; + bw = 1; + break; + case RTC_XTAL_FREQ_24M: + div_ref = 11; + div7_0 = 144; + div10_8 = 4; + lref = 1; + dcur = 0; + bw = 1; + break; + default: + div_ref = 12; + div7_0 = 224; + div10_8 = 4; + lref = 0; + dcur = 0; + bw = 0; + break; + } + I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_ENDIV5, BBPLL_ENDIV5_VAL_480M); + I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_BBADC_DSMP, BBPLL_BBADC_DSMP_VAL_480M); + } + + uint8_t i2c_bbpll_lref = (lref << 7) | (div10_8 << 4) | (div_ref); + uint8_t i2c_bbpll_div_7_0 = div7_0; + uint8_t i2c_bbpll_dcur = (bw << 6) | dcur; + I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_LREF, i2c_bbpll_lref); + I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0); + I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur); + uint32_t delay_pll_en = (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) ? + DELAY_PLL_ENABLE_WITH_150K : DELAY_PLL_ENABLE_WITH_32K; + ets_delay_us(delay_pll_en); +} + +/** + * Switch to XTAL frequency. Does not disable the PLL. + */ +static void rtc_clk_cpu_freq_to_xtal(void) +{ + rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); + ets_update_cpu_frequency(xtal_freq); + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL); + REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, 0); + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL); + DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0); // clear DPORT_CPUPERIOD_SEL + + rtc_clk_apb_freq_update(xtal_freq * MHZ); + s_cur_freq = RTC_CPU_FREQ_XTAL; +} + +/** + * Switch to one of PLL-based frequencies. Current frequency can be XTAL or PLL. + * PLL must already be enabled. + * If switching between frequencies derived from different PLLs (320M and 480M), + * fall back to rtc_clk_cpu_freq_set. + * @param cpu_freq new CPU frequency + */ +static void rtc_clk_cpu_freq_to_pll(rtc_cpu_freq_t cpu_freq) +{ + int freq = 0; + if ((cpu_freq == RTC_CPU_FREQ_240M && s_pll_freq == 320) || + (cpu_freq != RTC_CPU_FREQ_240M && s_pll_freq == 240)) { + /* need to switch PLLs, fall back to full implementation */ + rtc_clk_cpu_freq_set(cpu_freq); + return; + } + + if (cpu_freq == RTC_CPU_FREQ_80M) { + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M); + DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0); + freq = 80; + } else if (cpu_freq == RTC_CPU_FREQ_160M) { + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M); + DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 1); + freq = 160; + } else if (cpu_freq == RTC_CPU_FREQ_240M) { + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_240M); + DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 2); + freq = 240; + } + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL); + rtc_clk_apb_freq_update(80 * MHZ); + ets_update_cpu_frequency(freq); + s_cur_freq = cpu_freq; +} + +void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq) +{ + if (cpu_freq == s_cur_freq) { + return; + } else if (cpu_freq == RTC_CPU_FREQ_2M || s_cur_freq == RTC_CPU_FREQ_2M) { + /* fall back to full implementation if switch to/from 2M is needed */ + rtc_clk_cpu_freq_set(cpu_freq); + } else if (cpu_freq == RTC_CPU_FREQ_XTAL) { + rtc_clk_cpu_freq_to_xtal(); + } else if (cpu_freq > RTC_CPU_FREQ_XTAL) { + rtc_clk_cpu_freq_to_pll(cpu_freq); + rtc_clk_wait_for_slow_cycle(); + } +} + +void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) +{ + rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); + /* Switch CPU to XTAL frequency first */ + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL); + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL); + REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, 0); + ets_update_cpu_frequency(xtal_freq); + + /* Frequency switch is synchronized to SLOW_CLK cycle. Wait until the switch + * is complete before disabling the PLL. + */ + rtc_clk_wait_for_slow_cycle(); + + DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0); + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, + RTC_CNTL_BB_I2C_FORCE_PD | RTC_CNTL_BBPLL_FORCE_PD | + RTC_CNTL_BBPLL_I2C_FORCE_PD); + s_pll_freq = 0; + rtc_clk_apb_freq_update(xtal_freq * MHZ); + + /* is APLL under force power down? */ + uint32_t apll_fpd = REG_GET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD); + if (apll_fpd) { + /* then also power down the internal I2C bus */ + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FORCE_PD); + } + /* now switch to the desired frequency */ + if (cpu_freq == RTC_CPU_FREQ_XTAL) { + /* already at XTAL, nothing to do */ + } else if (cpu_freq == RTC_CPU_FREQ_2M) { + /* set up divider to produce 2MHz from XTAL */ + REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, (xtal_freq / 2) - 1); + ets_update_cpu_frequency(2); + rtc_clk_apb_freq_update(2 * MHZ); + /* lower the voltage */ + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_2M); + } else { + /* use PLL as clock source */ + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, + RTC_CNTL_BIAS_I2C_FORCE_PD | RTC_CNTL_BB_I2C_FORCE_PD | + RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD); + rtc_clk_bbpll_set(xtal_freq, cpu_freq); + if (cpu_freq == RTC_CPU_FREQ_80M) { + DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0); + ets_update_cpu_frequency(80); + s_pll_freq = 320; + } else if (cpu_freq == RTC_CPU_FREQ_160M) { + DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 1); + ets_update_cpu_frequency(160); + s_pll_freq = 320; + } else if (cpu_freq == RTC_CPU_FREQ_240M) { + DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 2); + ets_update_cpu_frequency(240); + s_pll_freq = 480; + } + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL); + rtc_clk_wait_for_slow_cycle(); + rtc_clk_apb_freq_update(80 * MHZ); + } + s_cur_freq = cpu_freq; +} + +rtc_cpu_freq_t rtc_clk_cpu_freq_get(void) +{ + uint32_t soc_clk_sel = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL); + switch (soc_clk_sel) { + case RTC_CNTL_SOC_CLK_SEL_XTL: { + uint32_t pre_div = REG_GET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT); + if (pre_div == 0) { + return RTC_CPU_FREQ_XTAL; + } else if (pre_div == rtc_clk_xtal_freq_get() / 2 - 1) { + return RTC_CPU_FREQ_2M; + } else { + assert(false && "unsupported frequency"); + } + break; + } + case RTC_CNTL_SOC_CLK_SEL_PLL: { + uint32_t cpuperiod_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL); + if (cpuperiod_sel == 0) { + return RTC_CPU_FREQ_80M; + } else if (cpuperiod_sel == 1) { + return RTC_CPU_FREQ_160M; + } else if (cpuperiod_sel == 2) { + return RTC_CPU_FREQ_240M; + } else { + assert(false && "unsupported frequency"); + } + break; + } + case RTC_CNTL_SOC_CLK_SEL_APLL: + case RTC_CNTL_SOC_CLK_SEL_8M: + default: + assert(false && "unsupported frequency"); + } + return RTC_CNTL_SOC_CLK_SEL_XTL; +} + +uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq) +{ + switch (cpu_freq) { + case RTC_CPU_FREQ_XTAL: + return ((uint32_t) rtc_clk_xtal_freq_get()) * MHZ; + case RTC_CPU_FREQ_2M: + return 2 * MHZ; + case RTC_CPU_FREQ_80M: + return 80 * MHZ; + case RTC_CPU_FREQ_160M: + return 160 * MHZ; + case RTC_CPU_FREQ_240M: + return 240 * MHZ; + default: + assert(false && "invalid rtc_cpu_freq_t value"); + return 0; + } +} + +bool rtc_clk_cpu_freq_from_mhz(int mhz, rtc_cpu_freq_t* out_val) +{ + if (mhz == 240) { + *out_val = RTC_CPU_FREQ_240M; + } else if (mhz == 160) { + *out_val = RTC_CPU_FREQ_160M; + } else if (mhz == 80) { + *out_val = RTC_CPU_FREQ_80M; + } else if (mhz == (int) rtc_clk_xtal_freq_get()) { + *out_val = RTC_CPU_FREQ_XTAL; + } else if (mhz == 2) { + *out_val = RTC_CPU_FREQ_2M; + } else { + return false; + } + return true; +} + +/* Values of RTC_XTAL_FREQ_REG and RTC_APB_FREQ_REG are stored as two copies in + * lower and upper 16-bit halves. These are the routines to work with such a + * representation. + */ +static bool clk_val_is_valid(uint32_t val) { + return (val & 0xffff) == ((val >> 16) & 0xffff) && + val != 0 && + val != UINT32_MAX; +} + +static uint32_t reg_val_to_clk_val(uint32_t val) { + return val & UINT16_MAX; +} + +static uint32_t clk_val_to_reg_val(uint32_t val) { + return (val & UINT16_MAX) | ((val & UINT16_MAX) << 16); +} + +rtc_xtal_freq_t rtc_clk_xtal_freq_get(void) +{ + /* We may have already written XTAL value into RTC_XTAL_FREQ_REG */ + uint32_t xtal_freq_reg = READ_PERI_REG(RTC_XTAL_FREQ_REG); + if (!clk_val_is_valid(xtal_freq_reg)) { + SOC_LOGW(TAG, "invalid RTC_XTAL_FREQ_REG value: 0x%08x", xtal_freq_reg); + return RTC_XTAL_FREQ_AUTO; + } + return reg_val_to_clk_val(xtal_freq_reg); +} + +void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq) +{ + WRITE_PERI_REG(RTC_XTAL_FREQ_REG, clk_val_to_reg_val(xtal_freq)); +} + +static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate(void) +{ + /* Enable 8M/256 clock if needed */ + const bool clk_8m_enabled = rtc_clk_8m_enabled(); + const bool clk_8md256_enabled = rtc_clk_8md256_enabled(); + if (!clk_8md256_enabled) { + rtc_clk_8m_enable(true, true); + } + + uint64_t cal_val = rtc_clk_cal_ratio(RTC_CAL_8MD256, XTAL_FREQ_EST_CYCLES); + /* cal_val contains period of 8M/256 clock in XTAL clock cycles + * (shifted by RTC_CLK_CAL_FRACT bits). + * Xtal frequency will be (cal_val * 8M / 256) / 2^19 + */ + uint32_t freq_mhz = (cal_val * (RTC_FAST_CLK_FREQ_APPROX / MHZ) / 256 ) >> RTC_CLK_CAL_FRACT; + /* Guess the XTAL type. For now, only 40 and 26MHz are supported. + */ + switch (freq_mhz) { + case 21 ... 31: + return RTC_XTAL_FREQ_26M; + case 32 ... 33: + SOC_LOGW(TAG, "Potentially bogus XTAL frequency: %d MHz, guessing 26 MHz", freq_mhz); + return RTC_XTAL_FREQ_26M; + case 34 ... 35: + SOC_LOGW(TAG, "Potentially bogus XTAL frequency: %d MHz, guessing 40 MHz", freq_mhz); + return RTC_XTAL_FREQ_40M; + case 36 ... 45: + return RTC_XTAL_FREQ_40M; + default: + SOC_LOGW(TAG, "Bogus XTAL frequency: %d MHz", freq_mhz); + return RTC_XTAL_FREQ_AUTO; + } + /* Restore 8M and 8md256 clocks to original state */ + rtc_clk_8m_enable(clk_8m_enabled, clk_8md256_enabled); +} + +void rtc_clk_apb_freq_update(uint32_t apb_freq) +{ + WRITE_PERI_REG(RTC_APB_FREQ_REG, clk_val_to_reg_val(apb_freq >> 12)); +} + +uint32_t rtc_clk_apb_freq_get(void) +{ + uint32_t freq_hz = reg_val_to_clk_val(READ_PERI_REG(RTC_APB_FREQ_REG)) << 12; + // round to the nearest MHz + freq_hz += MHZ / 2; + uint32_t remainder = freq_hz % MHZ; + return freq_hz - remainder; +} + + +void rtc_clk_init(rtc_clk_config_t cfg) +{ + rtc_cpu_freq_t cpu_source_before = rtc_clk_cpu_freq_get(); + + /* If we get a TG WDT system reset while running at 240MHz, + * DPORT_CPUPERIOD_SEL register will be reset to 0 resulting in 120MHz + * APB and CPU frequencies after reset. This will cause issues with XTAL + * frequency estimation, so we switch to XTAL frequency first. + * + * Ideally we would only do this if RTC_CNTL_SOC_CLK_SEL == PLL and + * PLL is configured for 480M, but it takes less time to switch to 40M and + * run the following code than querying the PLL does. + */ + if (REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL) == RTC_CNTL_SOC_CLK_SEL_PLL) { + rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL); + } + + /* Set tuning parameters for 8M and 150k clocks. + * Note: this doesn't attempt to set the clocks to precise frequencies. + * Instead, we calibrate these clocks against XTAL frequency later, when necessary. + * - SCK_DCAP value controls tuning of 150k clock. + * The higher the value of DCAP is, the lower is the frequency. + * - CK8M_DFREQ value controls tuning of 8M clock. + * CLK_8M_DFREQ constant gives the best temperature characteristics. + */ + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_SCK_DCAP, cfg.slow_clk_dcap); + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DFREQ, cfg.clk_8m_dfreq); + + /* Configure 8M clock division */ + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, cfg.clk_8m_div); + + /* Enable the internal bus used to configure PLLs */ + SET_PERI_REG_BITS(ANA_CONFIG_REG, ANA_CONFIG_M, ANA_CONFIG_M, ANA_CONFIG_S); + CLEAR_PERI_REG_MASK(ANA_CONFIG_REG, I2C_APLL_M | I2C_BBPLL_M); + + /* Estimate XTAL frequency */ + rtc_xtal_freq_t xtal_freq = cfg.xtal_freq; + if (xtal_freq == RTC_XTAL_FREQ_AUTO) { + if (clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) { + /* XTAL frequency has already been set, use existing value */ + xtal_freq = rtc_clk_xtal_freq_get(); + } else { + /* Not set yet, estimate XTAL frequency based on RTC_FAST_CLK */ + xtal_freq = rtc_clk_xtal_freq_estimate(); + if (xtal_freq == RTC_XTAL_FREQ_AUTO) { + SOC_LOGW(TAG, "Can't estimate XTAL frequency, assuming 26MHz"); + xtal_freq = RTC_XTAL_FREQ_26M; + } + } + } else if (!clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) { + /* Exact frequency was set in sdkconfig, but still warn if autodetected + * frequency is different. If autodetection failed, worst case we get a + * bit of garbage output. + */ + rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate(); + if (est_xtal_freq != xtal_freq) { + SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.", + xtal_freq, est_xtal_freq); + } + } + uart_tx_wait_idle(0); + rtc_clk_xtal_freq_update(xtal_freq); + rtc_clk_apb_freq_update(xtal_freq * MHZ); + /* Set CPU frequency */ + rtc_clk_cpu_freq_set(cfg.cpu_freq); + + /* Re-calculate the ccount to make time calculation correct. */ + uint32_t freq_before = rtc_clk_cpu_freq_value(cpu_source_before) / MHZ; + uint32_t freq_after = rtc_clk_cpu_freq_value(cfg.cpu_freq) / MHZ; + XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before ); + + /* Slow & fast clocks setup */ + if (cfg.slow_freq == RTC_SLOW_FREQ_32K_XTAL) { + rtc_clk_32k_enable(true); + } + if (cfg.fast_freq == RTC_FAST_FREQ_8M) { + bool need_8md256 = cfg.slow_freq == RTC_SLOW_FREQ_8MD256; + rtc_clk_8m_enable(true, need_8md256); + } + rtc_clk_fast_freq_set(cfg.fast_freq); + rtc_clk_slow_freq_set(cfg.slow_freq); +} + +/* Name used in libphy.a:phy_chip_v7.o + * TODO: update the library to use rtc_clk_xtal_freq_get + */ +rtc_xtal_freq_t rtc_get_xtal(void) __attribute__((alias("rtc_clk_xtal_freq_get"))); diff --git a/cpu/esp32/esp-idf/soc/rtc_init.c b/cpu/esp32/esp-idf/soc/rtc_init.c new file mode 100644 index 0000000000000..a4ed5b5621724 --- /dev/null +++ b/cpu/esp32/esp-idf/soc/rtc_init.c @@ -0,0 +1,154 @@ +// Copyright 2015-2017 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 + +#include "soc/soc.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/dport_reg.h" +#include "soc/efuse_reg.h" +#include "soc/gpio_reg.h" + +#ifndef RTC_CNTL_DBG_ATTEN_DEFAULT +#define RTC_CNTL_DBG_ATTEN_DEFAULT 0x3 +#endif + +void rtc_init(rtc_config_t cfg) +{ + CLEAR_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PVTMON_PU); + + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_PLL_BUF_WAIT, cfg.pll_wait); + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_XTL_BUF_WAIT, cfg.xtal_wait); + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, cfg.ck8m_wait); + + REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DBG_ATTEN, RTC_CNTL_DBG_ATTEN_DEFAULT); + SET_PERI_REG_MASK(RTC_CNTL_BIAS_CONF_REG, + RTC_CNTL_DEC_HEARTBEAT_WIDTH | RTC_CNTL_INC_HEARTBEAT_PERIOD); + + /* Reset RTC bias to default value (needed if waking up from deep sleep) */ + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DBIAS_WAK, RTC_CNTL_DBIAS_1V10); + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DBIAS_SLP, RTC_CNTL_DBIAS_1V10); + + if (cfg.clkctl_init) { + //clear CMMU clock force on + DPORT_CLEAR_PERI_REG_MASK(DPORT_PRO_CACHE_CTRL1_REG, DPORT_PRO_CMMU_FORCE_ON); + DPORT_CLEAR_PERI_REG_MASK(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CMMU_FORCE_ON); + //clear rom clock force on + DPORT_SET_PERI_REG_BITS(DPORT_ROM_FO_CTRL_REG, DPORT_SHARE_ROM_FO, 0, DPORT_SHARE_ROM_FO_S); + DPORT_CLEAR_PERI_REG_MASK(DPORT_ROM_FO_CTRL_REG, DPORT_APP_ROM_FO); + DPORT_CLEAR_PERI_REG_MASK(DPORT_ROM_FO_CTRL_REG, DPORT_PRO_ROM_FO); + //clear sram clock force on + DPORT_CLEAR_PERI_REG_MASK(DPORT_SRAM_FO_CTRL_0_REG, DPORT_SRAM_FO_0); + DPORT_CLEAR_PERI_REG_MASK(DPORT_SRAM_FO_CTRL_1_REG, DPORT_SRAM_FO_1); + //clear tag clock force on + DPORT_CLEAR_PERI_REG_MASK(DPORT_TAG_FO_CTRL_REG, DPORT_APP_CACHE_TAG_FORCE_ON); + DPORT_CLEAR_PERI_REG_MASK(DPORT_TAG_FO_CTRL_REG, DPORT_PRO_CACHE_TAG_FORCE_ON); + } + + if (cfg.pwrctl_init) { + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_FORCE_PU); + //cancel xtal force pu + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_XTL_FORCE_PU); + //cancel BIAS force pu + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_CORE_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_FORCE_NOSLEEP); + // bias follow 8M + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_CORE_FOLW_8M); + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FOLW_8M); + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_SLEEP_FOLW_8M); + // CLEAR APLL close + CLEAR_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PU); + SET_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD); + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BBPLL_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BBPLL_I2C_FORCE_PU); + //cancel RTC REG force PU + CLEAR_PERI_REG_MASK(RTC_CNTL_REG, RTC_CNTL_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_REG, RTC_CNTL_DBOOST_FORCE_PU); + if (cfg.rtc_dboost_fpd) { + SET_PERI_REG_MASK(RTC_CNTL_REG, RTC_CNTL_DBOOST_FORCE_PD); + } else { + CLEAR_PERI_REG_MASK(RTC_CNTL_REG, RTC_CNTL_DBOOST_FORCE_PD); + } + //cancel digital pu force + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_LSLP_MEM_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_DG_WRAP_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_WIFI_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_CPU_ROM_RAM_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_MEM_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_PWC_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_WRAP_FORCE_NOISO); + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_WIFI_FORCE_NOISO); + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_CPU_ROM_RAM_FORCE_NOISO); + CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_MEM_FORCE_NOISO); + CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_FORCE_NOISO); + //cancel digital PADS force no iso + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_PAD_FORCE_UNHOLD); + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_PAD_FORCE_NOISO); + } +} + +rtc_vddsdio_config_t rtc_vddsdio_get_config(void) +{ + rtc_vddsdio_config_t result; + uint32_t sdio_conf_reg = REG_READ(RTC_CNTL_SDIO_CONF_REG); + result.drefh = (sdio_conf_reg & RTC_CNTL_DREFH_SDIO_M) >> RTC_CNTL_DREFH_SDIO_S; + result.drefm = (sdio_conf_reg & RTC_CNTL_DREFM_SDIO_M) >> RTC_CNTL_DREFM_SDIO_S; + result.drefl = (sdio_conf_reg & RTC_CNTL_DREFL_SDIO_M) >> RTC_CNTL_DREFL_SDIO_S; + if (sdio_conf_reg & RTC_CNTL_SDIO_FORCE) { + // Get configuration from RTC + result.force = 1; + result.enable = (sdio_conf_reg & RTC_CNTL_XPD_SDIO_REG_M) >> RTC_CNTL_XPD_SDIO_REG_S; + result.tieh = (sdio_conf_reg & RTC_CNTL_SDIO_TIEH_M) >> RTC_CNTL_SDIO_TIEH_S; + return result; + } + uint32_t efuse_reg = REG_READ(EFUSE_BLK0_RDATA4_REG); + if (efuse_reg & EFUSE_RD_SDIO_FORCE) { + // Get configuration from EFUSE + result.force = 0; + result.enable = (efuse_reg & EFUSE_RD_XPD_SDIO_REG_M) >> EFUSE_RD_XPD_SDIO_REG_S; + result.tieh = (efuse_reg & EFUSE_RD_SDIO_TIEH_M) >> EFUSE_RD_SDIO_TIEH_S; + //DREFH/M/L eFuse are used for EFUSE_ADC_VREF instead. Therefore tuning + //will only be available on older chips that don't have EFUSE_ADC_VREF + if(REG_GET_FIELD(EFUSE_BLK0_RDATA3_REG ,EFUSE_RD_BLK3_PART_RESERVE) == 0){ + //BLK3_PART_RESERVE indicates the presence of EFUSE_ADC_VREF + // in this case, DREFH/M/L are also set from EFUSE + result.drefh = (efuse_reg & EFUSE_RD_SDIO_DREFH_M) >> EFUSE_RD_SDIO_DREFH_S; + result.drefm = (efuse_reg & EFUSE_RD_SDIO_DREFM_M) >> EFUSE_RD_SDIO_DREFM_S; + result.drefl = (efuse_reg & EFUSE_RD_SDIO_DREFL_M) >> EFUSE_RD_SDIO_DREFL_S; + } + return result; + } + + // Otherwise, VDD_SDIO is controlled by bootstrapping pin + uint32_t strap_reg = REG_READ(GPIO_STRAP_REG); + result.force = 0; + result.tieh = (strap_reg & BIT(5)) ? 0 : 1; + result.enable = result.tieh == 0; // only power on the regulator if VDD=1.8 + return result; +} + +void rtc_vddsdio_set_config(rtc_vddsdio_config_t config) +{ + uint32_t val = 0; + val |= (config.force << RTC_CNTL_SDIO_FORCE_S); + val |= (config.enable << RTC_CNTL_XPD_SDIO_REG_S); + val |= (config.drefh << RTC_CNTL_DREFH_SDIO_S); + val |= (config.drefm << RTC_CNTL_DREFM_SDIO_S); + val |= (config.drefl << RTC_CNTL_DREFL_SDIO_S); + val |= (config.tieh << RTC_CNTL_SDIO_TIEH_S); + val |= RTC_CNTL_SDIO_PD_EN; + REG_WRITE(RTC_CNTL_SDIO_CONF_REG, val); +} diff --git a/cpu/esp32/esp-idf/soc/rtc_sleep.c b/cpu/esp32/esp-idf/soc/rtc_sleep.c new file mode 100644 index 0000000000000..82a04d8caba86 --- /dev/null +++ b/cpu/esp32/esp-idf/soc/rtc_sleep.c @@ -0,0 +1,234 @@ +// Copyright 2015-2017 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 +#include "soc/soc.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/dport_reg.h" +#include "soc/rtc.h" +#include "soc/i2s_reg.h" +#include "soc/timer_group_reg.h" +#include "soc/bb_reg.h" +#include "soc/nrx_reg.h" +#include "soc/fe_reg.h" +#include "soc/rtc.h" +#include "rom/ets_sys.h" + +#define MHZ (1000000) + +/* Various delays to be programmed into power control state machines */ +#define ROM_RAM_POWERUP_DELAY 3 +#define ROM_RAM_WAIT_DELAY 3 +#define WIFI_POWERUP_DELAY 3 +#define WIFI_WAIT_DELAY 3 +#define RTC_POWERUP_DELAY 3 +#define RTC_WAIT_DELAY 3 +#define DG_WRAP_POWERUP_DELAY 3 +#define DG_WRAP_WAIT_DELAY 3 +#define RTC_MEM_POWERUP_DELAY 3 +#define RTC_MEM_WAIT_DELAY 3 + +/** + * @brief Power down flags for rtc_sleep_pd function + */ +typedef struct { + uint32_t dig_pd : 1; //!< Set to 1 to power down digital part in sleep + uint32_t rtc_pd : 1; //!< Set to 1 to power down RTC memories in sleep + uint32_t cpu_pd : 1; //!< Set to 1 to power down digital memories and CPU in sleep + uint32_t i2s_pd : 1; //!< Set to 1 to power down I2S in sleep + uint32_t bb_pd : 1; //!< Set to 1 to power down WiFi in sleep + uint32_t nrx_pd : 1; //!< Set to 1 to power down WiFi in sleep + uint32_t fe_pd : 1; //!< Set to 1 to power down WiFi in sleep +} rtc_sleep_pd_config_t; + +/** + * Initializer for rtc_sleep_pd_config_t which sets all flags to the same value + */ +#define RTC_SLEEP_PD_CONFIG_ALL(val) {\ + .dig_pd = (val), \ + .rtc_pd = (val), \ + .cpu_pd = (val), \ + .i2s_pd = (val), \ + .bb_pd = (val), \ + .nrx_pd = (val), \ + .fe_pd = (val), \ +} + +/** + * Configure whether certain peripherals are powered down in deep sleep + * @param cfg power down flags as rtc_sleep_pd_config_t structure + */ +static void rtc_sleep_pd(rtc_sleep_pd_config_t cfg) +{ + REG_SET_FIELD(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_LSLP_MEM_FORCE_PU, ~cfg.dig_pd); + REG_SET_FIELD(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_FORCE_LPU, ~cfg.rtc_pd); + REG_SET_FIELD(RTC_CNTL_PWC_REG, RTC_CNTL_FASTMEM_FORCE_LPU, ~cfg.rtc_pd); + DPORT_REG_SET_FIELD(DPORT_MEM_PD_MASK_REG, DPORT_LSLP_MEM_PD_MASK, ~cfg.cpu_pd); + REG_SET_FIELD(I2S_PD_CONF_REG(0), I2S_PLC_MEM_FORCE_PU, ~cfg.i2s_pd); + REG_SET_FIELD(I2S_PD_CONF_REG(0), I2S_FIFO_FORCE_PU, ~cfg.i2s_pd); + REG_SET_FIELD(BBPD_CTRL, BB_FFT_FORCE_PU, ~cfg.bb_pd); + REG_SET_FIELD(BBPD_CTRL, BB_DC_EST_FORCE_PU, ~cfg.bb_pd); + REG_SET_FIELD(NRXPD_CTRL, NRX_RX_ROT_FORCE_PU, ~cfg.nrx_pd); + REG_SET_FIELD(NRXPD_CTRL, NRX_VIT_FORCE_PU, ~cfg.nrx_pd); + REG_SET_FIELD(NRXPD_CTRL, NRX_DEMAP_FORCE_PU, ~cfg.nrx_pd); + REG_SET_FIELD(FE_GEN_CTRL, FE_IQ_EST_FORCE_PU, ~cfg.fe_pd); + REG_SET_FIELD(FE2_TX_INTERP_CTRL, FE2_TX_INF_FORCE_PU, ~cfg.fe_pd); +} + +void rtc_sleep_init(rtc_sleep_config_t cfg) +{ + rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, cfg.soc_clk_sel); + + //set 5 PWC state machine times to fit in main state machine time + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_PLL_BUF_WAIT, 1); + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_XTL_BUF_WAIT, RTC_CNTL_XTL_BUF_WAIT_DEFAULT); + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, RTC_CNTL_CK8M_WAIT_DEFAULT); + //set rom&ram timer + REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_ROM_RAM_POWERUP_TIMER, ROM_RAM_POWERUP_DELAY); + REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_ROM_RAM_WAIT_TIMER, ROM_RAM_WAIT_DELAY); + //set wifi timer + REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_WIFI_POWERUP_TIMER, WIFI_POWERUP_DELAY); + REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_WIFI_WAIT_TIMER, WIFI_WAIT_DELAY); + //set rtc peri timer + REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_POWERUP_TIMER, RTC_POWERUP_DELAY); + REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_WAIT_TIMER, RTC_WAIT_DELAY); + //set digital wrap timer + REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_DG_WRAP_POWERUP_TIMER, DG_WRAP_POWERUP_DELAY); + REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_DG_WRAP_WAIT_TIMER, DG_WRAP_WAIT_DELAY); + //set rtc memory timer + REG_SET_FIELD(RTC_CNTL_TIMER5_REG, RTC_CNTL_RTCMEM_POWERUP_TIMER, RTC_MEM_POWERUP_DELAY); + REG_SET_FIELD(RTC_CNTL_TIMER5_REG, RTC_CNTL_RTCMEM_WAIT_TIMER, RTC_MEM_WAIT_DELAY); + + if (cfg.soc_clk_sel == RTC_CNTL_SOC_CLK_SEL_PLL) { + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_PLL_BUF_WAIT, RTC_CNTL_PLL_BUF_WAIT_DEFAULT); + } else if (cfg.soc_clk_sel == RTC_CNTL_SOC_CLK_SEL_XTL) { + ets_update_cpu_frequency(xtal_freq); + rtc_clk_apb_freq_update(xtal_freq * MHZ); + } else if (cfg.soc_clk_sel == RTC_CNTL_SOC_CLK_SEL_8M) { + ets_update_cpu_frequency(8); + rtc_clk_apb_freq_update(8 * MHZ); + } + + if (cfg.lslp_mem_inf_fpu) { + SET_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_LSLP_MEM_FORCE_PU); + } else { + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_LSLP_MEM_FORCE_PU); + } + + rtc_sleep_pd_config_t pd_cfg = RTC_SLEEP_PD_CONFIG_ALL(cfg.lslp_meminf_pd); + rtc_sleep_pd(pd_cfg); + + if (cfg.rtc_mem_inf_fpu) { + SET_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_MEM_FORCE_PU); + } else { + CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_MEM_FORCE_PU); + } + + if (cfg.rtc_mem_inf_follow_cpu) { + SET_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_MEM_FOLW_CPU); + } else { + CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_MEM_FOLW_CPU); + } + + if (cfg.rtc_fastmem_pd_en) { + SET_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_FASTMEM_PD_EN); + CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_FASTMEM_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_FASTMEM_FORCE_NOISO); + } else { + CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_FASTMEM_PD_EN); + SET_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_FASTMEM_FORCE_PU); + SET_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_FASTMEM_FORCE_NOISO); + } + + if (cfg.rtc_slowmem_pd_en) { + SET_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_PD_EN); + CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_FORCE_NOISO); + } else { + CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_PD_EN); + SET_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_FORCE_PU); + SET_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_SLOWMEM_FORCE_NOISO); + } + + if (cfg.rtc_peri_pd_en) { + SET_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_PD_EN); + } else { + CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_PD_EN); + } + + if (cfg.wifi_pd_en) { + SET_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_WIFI_PD_EN); + } else { + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_WIFI_PD_EN); + } + + if (cfg.rom_mem_pd_en) { + SET_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_CPU_ROM_RAM_PD_EN); + } else { + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_CPU_ROM_RAM_PD_EN); + } + + if (cfg.deep_slp) { + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, + RTC_CNTL_DG_PAD_FORCE_ISO | RTC_CNTL_DG_PAD_FORCE_NOISO); + SET_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_DG_WRAP_PD_EN); + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, + RTC_CNTL_DG_WRAP_FORCE_PU | RTC_CNTL_DG_WRAP_FORCE_PD); + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_FORCE_NOSLEEP); + + // Shut down parts of RTC which may have been left enabled by the wireless drivers + CLEAR_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG, + RTC_CNTL_CKGEN_I2C_PU | RTC_CNTL_PLL_I2C_PU | + RTC_CNTL_RFRX_PBUS_PU | RTC_CNTL_TXRF_I2C_PU); + } else { + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_DG_WRAP_PD_EN); + REG_SET_FIELD(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_DBG_ATTEN, 0); + } + + /* enable VDDSDIO control by state machine */ + REG_CLR_BIT(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_FORCE); + REG_SET_FIELD(RTC_CNTL_SDIO_CONF_REG, RTC_CNTL_SDIO_PD_EN, cfg.vddsdio_pd_en); + + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DBIAS_SLP, cfg.rtc_dbias_slp); + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DBIAS_WAK, cfg.rtc_dbias_wak); + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, cfg.dig_dbias_wak); + REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_SLP, cfg.dig_dbias_slp); +} + +void rtc_sleep_set_wakeup_time(uint64_t t) +{ + WRITE_PERI_REG(RTC_CNTL_SLP_TIMER0_REG, t & UINT32_MAX); + WRITE_PERI_REG(RTC_CNTL_SLP_TIMER1_REG, t >> 32); +} + +uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt) +{ + REG_SET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_ENA, wakeup_opt); + WRITE_PERI_REG(RTC_CNTL_SLP_REJECT_CONF_REG, reject_opt); + + /* Start entry into sleep mode */ + SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SLEEP_EN); + + while (GET_PERI_REG_MASK(RTC_CNTL_INT_RAW_REG, + RTC_CNTL_SLP_REJECT_INT_RAW | RTC_CNTL_SLP_WAKEUP_INT_RAW) == 0) { + ; + } + /* In deep sleep mode, we never get here */ + uint32_t reject = REG_GET_FIELD(RTC_CNTL_INT_RAW_REG, RTC_CNTL_SLP_REJECT_INT_RAW); + SET_PERI_REG_MASK(RTC_CNTL_INT_CLR_REG, + RTC_CNTL_SLP_REJECT_INT_CLR | RTC_CNTL_SLP_WAKEUP_INT_CLR); + return reject; +} diff --git a/cpu/esp32/esp-idf/soc/rtc_time.c b/cpu/esp32/esp-idf/soc/rtc_time.c new file mode 100644 index 0000000000000..3fb6a0a604ebf --- /dev/null +++ b/cpu/esp32/esp-idf/soc/rtc_time.c @@ -0,0 +1,153 @@ +// Copyright 2015-2017 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 +#include "rom/ets_sys.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/timer_group_reg.h" + +#define MHZ (1000000) + +/* Calibration of RTC_SLOW_CLK is performed using a special feature of TIMG0. + * This feature counts the number of XTAL clock cycles within a given number of + * RTC_SLOW_CLK cycles. + * + * Slow clock calibration feature has two modes of operation: one-off and cycling. + * In cycling mode (which is enabled by default on SoC reset), counting of XTAL + * cycles within RTC_SLOW_CLK cycle is done continuously. Cycling mode is enabled + * using TIMG_RTC_CALI_START_CYCLING bit. In one-off mode counting is performed + * once, and TIMG_RTC_CALI_RDY bit is set when counting is done. One-off mode is + * enabled using TIMG_RTC_CALI_START bit. + */ + +/** + * @brief Clock calibration function used by rtc_clk_cal and rtc_clk_cal_ratio + * @param cal_clk which clock to calibrate + * @param slowclk_cycles number of slow clock cycles to count + * @return number of XTAL clock cycles within the given number of slow clock cycles + */ +static uint32_t rtc_clk_cal_internal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles) +{ + /* Enable requested clock (150k clock is always on) */ + if (cal_clk == RTC_CAL_32K_XTAL) { + SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN); + } + if (cal_clk == RTC_CAL_8MD256) { + SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_D256_EN); + } + /* Prepare calibration */ + REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_CLK_SEL, cal_clk); + CLEAR_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START_CYCLING); + REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_MAX, slowclk_cycles); + /* Figure out how long to wait for calibration to finish */ + uint32_t expected_freq; + rtc_slow_freq_t slow_freq = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL); + if (cal_clk == RTC_CAL_32K_XTAL || + (cal_clk == RTC_CAL_RTC_MUX && slow_freq == RTC_SLOW_FREQ_32K_XTAL)) { + expected_freq = 32768; /* standard 32k XTAL */ + } else if (cal_clk == RTC_CAL_8MD256 || + (cal_clk == RTC_CAL_RTC_MUX && slow_freq == RTC_SLOW_FREQ_8MD256)) { + expected_freq = RTC_FAST_CLK_FREQ_APPROX / 256; + } else { + expected_freq = 150000; /* 150k internal oscillator */ + } + uint32_t us_time_estimate = (uint32_t) (((uint64_t) slowclk_cycles) * MHZ / expected_freq); + /* Start calibration */ + CLEAR_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START); + SET_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START); + /* Wait the expected time calibration should take. + * TODO: if running under RTOS, and us_time_estimate > RTOS tick, use the + * RTOS delay function. + */ + ets_delay_us(us_time_estimate); + /* Wait for calibration to finish up to another us_time_estimate */ + int timeout_us = us_time_estimate; + while (!GET_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_RDY) && + timeout_us > 0) { + timeout_us--; + ets_delay_us(1); + } + if (cal_clk == RTC_CAL_32K_XTAL) { + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN); + } + if (cal_clk == RTC_CAL_8MD256) { + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_D256_EN); + } + if (timeout_us == 0) { + /* timed out waiting for calibration */ + return 0; + } + + return REG_GET_FIELD(TIMG_RTCCALICFG1_REG(0), TIMG_RTC_CALI_VALUE); +} + +uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles) +{ + uint64_t xtal_cycles = rtc_clk_cal_internal(cal_clk, slowclk_cycles); + uint64_t ratio_64 = ((xtal_cycles << RTC_CLK_CAL_FRACT)) / slowclk_cycles; + uint32_t ratio = (uint32_t)(ratio_64 & UINT32_MAX); + return ratio; +} + +uint32_t rtc_clk_cal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles) +{ + rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); + uint64_t xtal_cycles = rtc_clk_cal_internal(cal_clk, slowclk_cycles); + uint64_t divider = ((uint64_t)xtal_freq) * slowclk_cycles; + uint64_t period_64 = ((xtal_cycles << RTC_CLK_CAL_FRACT) + divider / 2 - 1) / divider; + uint32_t period = (uint32_t)(period_64 & UINT32_MAX); + return period; +} + +uint64_t rtc_time_us_to_slowclk(uint64_t time_in_us, uint32_t period) +{ + /* Overflow will happen in this function if time_in_us >= 2^45, which is about 400 days. + * TODO: fix overflow. + */ + return (time_in_us << RTC_CLK_CAL_FRACT) / period; +} + +uint64_t rtc_time_slowclk_to_us(uint64_t rtc_cycles, uint32_t period) +{ + return (rtc_cycles * period) >> RTC_CLK_CAL_FRACT; +} + +uint64_t rtc_time_get(void) +{ + SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE); + while (GET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_VALID) == 0) { + ets_delay_us(1); // might take 1 RTC slowclk period, don't flood RTC bus + } + SET_PERI_REG_MASK(RTC_CNTL_INT_CLR_REG, RTC_CNTL_TIME_VALID_INT_CLR); + uint64_t t = READ_PERI_REG(RTC_CNTL_TIME0_REG); + t |= ((uint64_t) READ_PERI_REG(RTC_CNTL_TIME1_REG)) << 32; + return t; +} + +void rtc_clk_wait_for_slow_cycle(void) +{ + REG_CLR_BIT(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START_CYCLING | TIMG_RTC_CALI_START); + REG_CLR_BIT(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_RDY); + REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_CLK_SEL, RTC_CAL_RTC_MUX); + /* Request to run calibration for 0 slow clock cycles. + * RDY bit will be set on the nearest slow clock cycle. + */ + REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_MAX, 0); + REG_SET_BIT(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START); + ets_delay_us(1); /* RDY needs some time to go low */ + while (!GET_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_RDY)) { + ets_delay_us(1); + } +} diff --git a/cpu/esp32/esp-idf/soc/soc_cpu.h b/cpu/esp32/esp-idf/soc/soc_cpu.h new file mode 100644 index 0000000000000..efdc2a2f8d885 --- /dev/null +++ b/cpu/esp32/esp-idf/soc/soc_cpu.h @@ -0,0 +1,115 @@ +// Copyright 2010-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. + +#ifndef SOC_CPU_H +#define SOC_CPU_H + +#ifndef DOXYGEN + +#include +#include +#include +#include "xtensa/corebits.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* C macros for xtensa special register read/write/exchange */ + +#define RSR(reg, curval) asm volatile ("rsr %0, " #reg : "=r" (curval)); +#define WSR(reg, newval) asm volatile ("wsr %0, " #reg : : "r" (newval)); +#define XSR(reg, swapval) asm volatile ("xsr %0, " #reg : "+r" (swapval)); + +/** @brief Read current stack pointer address + * + */ +static inline void *get_sp(void) +{ + void *sp; + asm volatile ("mov %0, sp;" : "=r" (sp)); + return sp; +} + +/* Functions to set page attributes for Region Protection option in the CPU. + * See Xtensa ISA Reference manual for explanation of arguments (section 4.6.3.2). + */ + +static inline void cpu_write_dtlb(uint32_t vpn, unsigned attr) +{ + asm volatile ("wdtlb %1, %0; dsync\n" :: "r" (vpn), "r" (attr)); +} + + +static inline void cpu_write_itlb(unsigned vpn, unsigned attr) +{ + asm volatile ("witlb %1, %0; isync\n" :: "r" (vpn), "r" (attr)); +} + +/** + * @brief Configure memory region protection + * + * Make page 0 access raise an exception. + * Also protect some other unused pages so we can catch weirdness. + * Useful attribute values: + * 0 — cached, RW + * 2 — bypass cache, RWX (default value after CPU reset) + * 15 — no access, raise exception + */ + +static inline void cpu_configure_region_protection(void) +{ + const uint32_t pages_to_protect[] = {0x00000000, 0x80000000, 0xa0000000, 0xc0000000, 0xe0000000}; + for (unsigned i = 0; i < sizeof(pages_to_protect)/sizeof(pages_to_protect[0]); ++i) { + cpu_write_dtlb(pages_to_protect[i], 0xf); + cpu_write_itlb(pages_to_protect[i], 0xf); + } + cpu_write_dtlb(0x20000000, 0); + cpu_write_itlb(0x20000000, 0); +} + +/** + * @brief Stall CPU using RTC controller + * @param cpu_id ID of the CPU to stall (0 = PRO, 1 = APP) + */ +void esp_cpu_stall(int cpu_id); + +/** + * @brief Un-stall CPU using RTC controller + * @param cpu_id ID of the CPU to un-stall (0 = PRO, 1 = APP) + */ +void esp_cpu_unstall(int cpu_id); + +/** + * @brief Reset CPU using RTC controller + * @param cpu_id ID of the CPU to reset (0 = PRO, 1 = APP) + */ +void esp_cpu_reset(int cpu_id); + + +/** + * @brief Returns true if a JTAG debugger is attached to CPU + * OCD (on chip debug) port. + * + * @note If "Make exception and panic handlers JTAG/OCD aware" + * is disabled, this function always returns false. + */ +bool esp_cpu_in_ocd_debug_mode(void); + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* SOC_CPU_H */ diff --git a/cpu/esp32/esp-idf/soc/soc_memory_layout.c b/cpu/esp32/esp-idf/soc/soc_memory_layout.c new file mode 100644 index 0000000000000..f91516334b897 --- /dev/null +++ b/cpu/esp32/esp-idf/soc/soc_memory_layout.c @@ -0,0 +1,174 @@ +// Copyright 2010-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. +#ifndef BOOTLOADER_BUILD + +#include +#include + +#include "soc/soc.h" +#include "soc/soc_memory_layout.h" +#include "esp_heap_caps.h" +#include "sdkconfig.h" + +/* Memory layout for ESP32 SoC */ + +/* +Memory type descriptors. These describe the capabilities of a type of memory in the SoC. Each type of memory +map consist of one or more regions in the address space. + +Each type contains an array of prioritised capabilities; types with later entries are only taken if earlier +ones can't fulfill the memory request. + +The prioritised capabilities work roughly like this: +- For a normal malloc (MALLOC_CAP_DEFAULT), give away the DRAM-only memory first, then pass off any dual-use IRAM regions, + finally eat into the application memory. +- For a malloc where 32-bit-aligned-only access is okay, first allocate IRAM, then DRAM, finally application IRAM. +- Application mallocs (PIDx) will allocate IRAM first, if possible, then DRAM. +- Most other malloc caps only fit in one region anyway. + +*/ +const soc_memory_type_desc_t soc_memory_types[] = { + //Type 0: Plain ole D-port RAM + { "DRAM", { MALLOC_CAP_8BIT|MALLOC_CAP_DEFAULT, MALLOC_CAP_INTERNAL|MALLOC_CAP_DMA|MALLOC_CAP_32BIT, 0 }, false, false}, + //Type 1: Plain ole D-port RAM which has an alias on the I-port + //(This DRAM is also the region used by ROM during startup) + { "D/IRAM", { 0, MALLOC_CAP_DMA|MALLOC_CAP_8BIT|MALLOC_CAP_INTERNAL|MALLOC_CAP_DEFAULT, MALLOC_CAP_32BIT|MALLOC_CAP_EXEC }, true, true}, + //Type 2: IRAM + { "IRAM", { MALLOC_CAP_EXEC|MALLOC_CAP_32BIT|MALLOC_CAP_INTERNAL, 0, 0 }, false, false}, + //Type 3-8: PID 2-7 IRAM + { "PID2IRAM", { MALLOC_CAP_PID2|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false}, + { "PID3IRAM", { MALLOC_CAP_PID3|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false}, + { "PID4IRAM", { MALLOC_CAP_PID4|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false}, + { "PID5IRAM", { MALLOC_CAP_PID5|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false}, + { "PID6IRAM", { MALLOC_CAP_PID6|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false}, + { "PID7IRAM", { MALLOC_CAP_PID7|MALLOC_CAP_INTERNAL, 0, MALLOC_CAP_EXEC|MALLOC_CAP_32BIT }, false, false}, + //Type 9-14: PID 2-7 DRAM + { "PID2DRAM", { MALLOC_CAP_PID2|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false}, + { "PID3DRAM", { MALLOC_CAP_PID3|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false}, + { "PID4DRAM", { MALLOC_CAP_PID4|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false}, + { "PID5DRAM", { MALLOC_CAP_PID5|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false}, + { "PID6DRAM", { MALLOC_CAP_PID6|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false}, + { "PID7DRAM", { MALLOC_CAP_PID7|MALLOC_CAP_INTERNAL, MALLOC_CAP_8BIT, MALLOC_CAP_32BIT|MALLOC_CAP_DEFAULT }, false, false}, + //Type 15: SPI SRAM data + { "SPIRAM", { MALLOC_CAP_SPIRAM|MALLOC_CAP_DEFAULT, 0, MALLOC_CAP_8BIT|MALLOC_CAP_32BIT}, false, false}, +}; + +const size_t soc_memory_type_count = sizeof(soc_memory_types)/sizeof(soc_memory_type_desc_t); + +/* +Region descriptors. These describe all regions of memory available, and map them to a type in the above type. + +Because of requirements in the coalescing code which merges adjacent regions, this list should always be sorted +from low to high start address. +*/ +const soc_memory_region_t soc_memory_regions[] = { + { 0x3F800000, 0x400000, 15, 0}, //SPI SRAM, if available + { 0x3FFAE000, 0x2000, 0, 0}, //pool 16 <- used for rom code + { 0x3FFB0000, 0x8000, 0, 0}, //pool 15 <- if BT is enabled, used as BT HW shared memory + { 0x3FFB8000, 0x8000, 0, 0}, //pool 14 <- if BT is enabled, used data memory for BT ROM functions. + { 0x3FFC0000, 0x2000, 0, 0}, //pool 10-13, mmu page 0 + { 0x3FFC2000, 0x2000, 0, 0}, //pool 10-13, mmu page 1 + { 0x3FFC4000, 0x2000, 0, 0}, //pool 10-13, mmu page 2 + { 0x3FFC6000, 0x2000, 0, 0}, //pool 10-13, mmu page 3 + { 0x3FFC8000, 0x2000, 0, 0}, //pool 10-13, mmu page 4 + { 0x3FFCA000, 0x2000, 0, 0}, //pool 10-13, mmu page 5 + { 0x3FFCC000, 0x2000, 0, 0}, //pool 10-13, mmu page 6 + { 0x3FFCE000, 0x2000, 0, 0}, //pool 10-13, mmu page 7 + { 0x3FFD0000, 0x2000, 0, 0}, //pool 10-13, mmu page 8 + { 0x3FFD2000, 0x2000, 0, 0}, //pool 10-13, mmu page 9 + { 0x3FFD4000, 0x2000, 0, 0}, //pool 10-13, mmu page 10 + { 0x3FFD6000, 0x2000, 0, 0}, //pool 10-13, mmu page 11 + { 0x3FFD8000, 0x2000, 0, 0}, //pool 10-13, mmu page 12 + { 0x3FFDA000, 0x2000, 0, 0}, //pool 10-13, mmu page 13 + { 0x3FFDC000, 0x2000, 0, 0}, //pool 10-13, mmu page 14 + { 0x3FFDE000, 0x2000, 0, 0}, //pool 10-13, mmu page 15 + { 0x3FFE0000, 0x4000, 1, 0x400BC000}, //pool 9 blk 1 + { 0x3FFE4000, 0x4000, 1, 0x400B8000}, //pool 9 blk 0 + { 0x3FFE8000, 0x8000, 1, 0x400B0000}, //pool 8 <- can be remapped to ROM, used for MAC dump + { 0x3FFF0000, 0x8000, 1, 0x400A8000}, //pool 7 <- can be used for MAC dump + { 0x3FFF8000, 0x4000, 1, 0x400A4000}, //pool 6 blk 1 <- can be used as trace memory + { 0x3FFFC000, 0x4000, 1, 0x400A0000}, //pool 6 blk 0 <- can be used as trace memory + { 0x40070000, 0x8000, 2, 0}, //pool 0 + { 0x40078000, 0x8000, 2, 0}, //pool 1 + { 0x40080000, 0x2000, 2, 0}, //pool 2-5, mmu page 0 + { 0x40082000, 0x2000, 2, 0}, //pool 2-5, mmu page 1 + { 0x40084000, 0x2000, 2, 0}, //pool 2-5, mmu page 2 + { 0x40086000, 0x2000, 2, 0}, //pool 2-5, mmu page 3 + { 0x40088000, 0x2000, 2, 0}, //pool 2-5, mmu page 4 + { 0x4008A000, 0x2000, 2, 0}, //pool 2-5, mmu page 5 + { 0x4008C000, 0x2000, 2, 0}, //pool 2-5, mmu page 6 + { 0x4008E000, 0x2000, 2, 0}, //pool 2-5, mmu page 7 + { 0x40090000, 0x2000, 2, 0}, //pool 2-5, mmu page 8 + { 0x40092000, 0x2000, 2, 0}, //pool 2-5, mmu page 9 + { 0x40094000, 0x2000, 2, 0}, //pool 2-5, mmu page 10 + { 0x40096000, 0x2000, 2, 0}, //pool 2-5, mmu page 11 + { 0x40098000, 0x2000, 2, 0}, //pool 2-5, mmu page 12 + { 0x4009A000, 0x2000, 2, 0}, //pool 2-5, mmu page 13 + { 0x4009C000, 0x2000, 2, 0}, //pool 2-5, mmu page 14 + { 0x4009E000, 0x2000, 2, 0}, //pool 2-5, mmu page 15 +}; + +const size_t soc_memory_region_count = sizeof(soc_memory_regions)/sizeof(soc_memory_region_t); + + +/* Reserved memory regions + + These are removed from the soc_memory_regions array when heaps are created. + */ +const soc_reserved_region_t soc_reserved_regions[] = { + { 0x40070000, 0x40078000 }, //CPU0 cache region + { 0x40078000, 0x40080000 }, //CPU1 cache region + + /* Warning: The ROM stack is located in the 0x3ffe0000 area. We do not specifically disable that area here because + after the scheduler has started, the ROM stack is not used anymore by anything. We handle it instead by not allowing + any mallocs memory regions with the startup_stack flag set (these are the IRAM/DRAM region) until the + scheduler has started. + + The 0x3ffe0000 region also contains static RAM for various ROM functions. The following lines + reserve the regions for UART and ETSC, so these functions are usable. Libraries like xtos, which are + not usable in FreeRTOS anyway, are commented out in the linker script so they cannot be used; we + do not disable their memory regions here and they will be used as general purpose heap memory. + + Enabling the heap allocator for this region but disabling allocation here until FreeRTOS is started up + is a somewhat risky action in theory, because on initializing the allocator, the multi_heap implementation + will go and write metadata at the start and end of all regions. For the ESP32, these linked + list entries happen to end up in a region that is not touched by the stack; they can be placed safely there. + */ + + { 0x3ffe0000, 0x3ffe0440 }, //Reserve ROM PRO data region +#if !CONFIG_FREERTOS_UNICORE + { 0x3ffe4000, 0x3ffe4350 }, //Reserve ROM APP data region +#endif + +#if CONFIG_BT_ENABLED + { 0x3ffb0000, 0x3ffc0000 }, //Reserve BT hardware shared memory & BT data region + { 0x3ffae000, 0x3ffaff10 }, //Reserve ROM data region, inc region needed for BT ROM routines +#else + { 0x3ffae000, 0x3ffae6e0 }, //Reserve ROM data region +#endif + +#if CONFIG_MEMMAP_TRACEMEM +#if CONFIG_MEMMAP_TRACEMEM_TWOBANKS + { 0x3fff8000, 0x40000000 }, //Reserve trace mem region +#else + { 0x3fff8000, 0x3fffc000 }, //Reserve trace mem region +#endif +#endif + + { 0x3f800000, 0x3fC00000 }, //SPI RAM gets added later if needed, in spiram.c; reserve it for now +}; + +const size_t soc_reserved_region_count = sizeof(soc_reserved_regions)/sizeof(soc_reserved_region_t); + +#endif diff --git a/cpu/esp32/esp-idf/spi_flash/Makefile b/cpu/esp32/esp-idf/spi_flash/Makefile new file mode 100644 index 0000000000000..3b7a59fb3cbbe --- /dev/null +++ b/cpu/esp32/esp-idf/spi_flash/Makefile @@ -0,0 +1,5 @@ +MODULE=esp_idf_spi_flash + +include $(RIOTBASE)/Makefile.base + +INCLUDES += -I$(SDK_DIR)/components/bootloader_support/include diff --git a/cpu/esp32/esp-idf/spi_flash/spi_flash_rom_patch.c b/cpu/esp32/esp-idf/spi_flash/spi_flash_rom_patch.c new file mode 100644 index 0000000000000..209ab7ff9f296 --- /dev/null +++ b/cpu/esp32/esp-idf/spi_flash/spi_flash_rom_patch.c @@ -0,0 +1,662 @@ +// 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 "rom/ets_sys.h" +#include "rom/gpio.h" +#include "rom/spi_flash.h" +#include "sdkconfig.h" + +#define SPI_IDX 1 +#define OTH_IDX 0 + +extern esp_rom_spiflash_chip_t g_rom_spiflash_chip; + +esp_rom_spiflash_result_t esp_rom_spiflash_wait_idle(esp_rom_spiflash_chip_t *spi) +{ + uint32_t status; + + //wait for spi control ready + while ((REG_READ(SPI_EXT2_REG(1)) & SPI_ST)) { + } + while ((REG_READ(SPI_EXT2_REG(0)) & SPI_ST)) { + } + //wait for flash status ready + if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_read_status(spi, &status)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + return ESP_ROM_SPIFLASH_RESULT_OK; +} + + +/* Modified version of esp_rom_spiflash_unlock() that replaces version in ROM. + + This works around a bug where esp_rom_spiflash_unlock sometimes reads the wrong + high status byte (RDSR2 result) and then copies it back to the + flash status, which can cause the CMP bit or Status Register + Protect bit to become set. + + Like other ROM SPI functions, this function is not designed to be + called directly from an RTOS environment without taking precautions + about interrupts, CPU coordination, flash mapping. However some of + the functions in esp_spi_flash.c call it. + */ +esp_rom_spiflash_result_t esp_rom_spiflash_unlock(void) +{ + uint32_t status; + + esp_rom_spiflash_wait_idle(&g_rom_spiflash_chip); + + if (esp_rom_spiflash_read_statushigh(&g_rom_spiflash_chip, &status) != ESP_ROM_SPIFLASH_RESULT_OK) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + /* Clear all bits except QIE, if it is set. + (This is different from ROM esp_rom_spiflash_unlock, which keeps all bits as-is.) + */ + status &= ESP_ROM_SPIFLASH_QE; + + esp_rom_spiflash_wait_idle(&g_rom_spiflash_chip); + REG_WRITE(SPI_CMD_REG(SPI_IDX), SPI_FLASH_WREN); + while (REG_READ(SPI_CMD_REG(SPI_IDX)) != 0) { + } + esp_rom_spiflash_wait_idle(&g_rom_spiflash_chip); + + SET_PERI_REG_MASK(SPI_CTRL_REG(SPI_IDX), SPI_WRSR_2B); + if (esp_rom_spiflash_write_status(&g_rom_spiflash_chip, status) != ESP_ROM_SPIFLASH_RESULT_OK) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + return ESP_ROM_SPIFLASH_RESULT_OK; +} + + +#if CONFIG_SPI_FLASH_ROM_DRIVER_PATCH + +extern uint8_t g_rom_spiflash_dummy_len_plus[]; + + +static esp_rom_spiflash_result_t esp_rom_spiflash_enable_write(esp_rom_spiflash_chip_t *spi); + +//only support spi1 +static esp_rom_spiflash_result_t esp_rom_spiflash_erase_chip_internal(esp_rom_spiflash_chip_t *spi) +{ + esp_rom_spiflash_wait_idle(spi); + + // Chip erase. + WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_CE); + while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0); + + // check erase is finished. + esp_rom_spiflash_wait_idle(spi); + + return ESP_ROM_SPIFLASH_RESULT_OK; +} + +//only support spi1 +static esp_rom_spiflash_result_t esp_rom_spiflash_erase_sector_internal(esp_rom_spiflash_chip_t *spi, uint32_t addr) +{ + //check if addr is 4k alignment + if (0 != (addr & 0xfff)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + esp_rom_spiflash_wait_idle(spi); + + // sector erase 4Kbytes erase is sector erase. + WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, addr & 0xffffff); + WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_SE); + while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0); + + esp_rom_spiflash_wait_idle(spi); + + return ESP_ROM_SPIFLASH_RESULT_OK; +} + +//only support spi1 +static esp_rom_spiflash_result_t esp_rom_spiflash_erase_block_internal(esp_rom_spiflash_chip_t *spi, uint32_t addr) +{ + esp_rom_spiflash_wait_idle(spi); + + // sector erase 4Kbytes erase is sector erase. + WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, addr & 0xffffff); + WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_BE); + while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0); + + esp_rom_spiflash_wait_idle(spi); + + return ESP_ROM_SPIFLASH_RESULT_OK; +} + +//only support spi1 +static esp_rom_spiflash_result_t esp_rom_spiflash_program_page_internal(esp_rom_spiflash_chip_t *spi, uint32_t spi_addr, + uint32_t *addr_source, int32_t byte_length) +{ + uint32_t temp_addr; + int32_t temp_bl; + uint8_t i; + uint8_t remain_word_num; + + //check 4byte alignment + if (0 != (byte_length & 0x3)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + //check if write in one page + if ((spi->page_size) < ((spi_addr % (spi->page_size)) + byte_length)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + esp_rom_spiflash_wait_idle(spi); + + temp_addr = spi_addr; + temp_bl = byte_length; + + while (temp_bl > 0 ) { + if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_enable_write(spi)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + if ( temp_bl >= ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM ) { + WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, (temp_addr & 0xffffff) | ( ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM << ESP_ROM_SPIFLASH_BYTES_LEN )); // 32 byte a block + + for (i = 0; i < (ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM >> 2); i++) { + WRITE_PERI_REG(PERIPHS_SPI_FLASH_C0 + i * 4, *addr_source++); + } + temp_bl = temp_bl - 32; + temp_addr = temp_addr + 32; + } else { + WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, (temp_addr & 0xffffff) | (temp_bl << ESP_ROM_SPIFLASH_BYTES_LEN )); + + remain_word_num = (0 == (temp_bl & 0x3)) ? (temp_bl >> 2) : (temp_bl >> 2) + 1; + for (i = 0; i < remain_word_num; i++) { + WRITE_PERI_REG(PERIPHS_SPI_FLASH_C0 + i * 4, *addr_source++); + temp_bl = temp_bl - 4; + } + temp_bl = 0; + } + + WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_PP); + while ( READ_PERI_REG(PERIPHS_SPI_FLASH_CMD ) != 0 ); + + esp_rom_spiflash_wait_idle(spi); + } + + return ESP_ROM_SPIFLASH_RESULT_OK; +} + +//only support spi1 +static esp_rom_spiflash_result_t esp_rom_spiflash_read_data(esp_rom_spiflash_chip_t *spi, uint32_t flash_addr, + uint32_t *addr_dest, int32_t byte_length) +{ + uint32_t temp_addr; + int32_t temp_length; + uint8_t i; + uint8_t remain_word_num; + + //address range check + if ((flash_addr + byte_length) > (spi->chip_size)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + temp_addr = flash_addr; + temp_length = byte_length; + + esp_rom_spiflash_wait_idle(spi); + + while (temp_length > 0) { + if (temp_length >= ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM) { + //WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, temp_addr |(ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM << ESP_ROM_SPIFLASH_BYTES_LEN)); + REG_WRITE(SPI_MISO_DLEN_REG(1), ((ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM << 3) - 1) << SPI_USR_MISO_DBITLEN_S); + WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, temp_addr << 8); + REG_WRITE(PERIPHS_SPI_FLASH_CMD, SPI_USR); + while (REG_READ(PERIPHS_SPI_FLASH_CMD) != 0); + + for (i = 0; i < (ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM >> 2); i++) { + *addr_dest++ = READ_PERI_REG(PERIPHS_SPI_FLASH_C0 + i * 4); + } + temp_length = temp_length - ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM; + temp_addr = temp_addr + ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM; + } else { + //WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, temp_addr |(temp_length << ESP_ROM_SPIFLASH_BYTES_LEN )); + WRITE_PERI_REG(PERIPHS_SPI_FLASH_ADDR, temp_addr << 8); + REG_WRITE(SPI_MISO_DLEN_REG(1), ((ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM << 3) - 1) << SPI_USR_MISO_DBITLEN_S); + REG_WRITE(PERIPHS_SPI_FLASH_CMD, SPI_USR); + while (REG_READ(PERIPHS_SPI_FLASH_CMD) != 0); + + remain_word_num = (0 == (temp_length & 0x3)) ? (temp_length >> 2) : (temp_length >> 2) + 1; + for (i = 0; i < remain_word_num; i++) { + *addr_dest++ = READ_PERI_REG(PERIPHS_SPI_FLASH_C0 + i * 4); + } + temp_length = 0; + } + } + + return ESP_ROM_SPIFLASH_RESULT_OK; +} + +esp_rom_spiflash_result_t esp_rom_spiflash_read_status(esp_rom_spiflash_chip_t *spi, uint32_t *status) +{ + uint32_t status_value = ESP_ROM_SPIFLASH_BUSY_FLAG; + + if (g_rom_spiflash_dummy_len_plus[1] == 0) { + while (ESP_ROM_SPIFLASH_BUSY_FLAG == (status_value & ESP_ROM_SPIFLASH_BUSY_FLAG)) { + WRITE_PERI_REG(PERIPHS_SPI_FLASH_STATUS, 0); // clear regisrter + WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_RDSR); + while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0); + + status_value = READ_PERI_REG(PERIPHS_SPI_FLASH_STATUS) & (spi->status_mask); + } + } else { + while (ESP_ROM_SPIFLASH_BUSY_FLAG == (status_value & ESP_ROM_SPIFLASH_BUSY_FLAG)) { + esp_rom_spiflash_read_user_cmd(&status_value, 0x05); + } + } + *status = status_value; + + return ESP_ROM_SPIFLASH_RESULT_OK; +} + +esp_rom_spiflash_result_t esp_rom_spiflash_read_statushigh(esp_rom_spiflash_chip_t *spi, uint32_t *status) +{ + esp_rom_spiflash_result_t ret; + esp_rom_spiflash_wait_idle(&g_rom_spiflash_chip); + ret = esp_rom_spiflash_read_user_cmd(status, 0x35); + *status = *status << 8; + return ret; +} + +esp_rom_spiflash_result_t esp_rom_spiflash_write_status(esp_rom_spiflash_chip_t *spi, uint32_t status_value) +{ + esp_rom_spiflash_wait_idle(spi); + + // update status value by status_value + WRITE_PERI_REG(PERIPHS_SPI_FLASH_STATUS, status_value); // write status regisrter + WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_WRSR); + while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0); + esp_rom_spiflash_wait_idle(spi); + + return ESP_ROM_SPIFLASH_RESULT_OK; +} + +static esp_rom_spiflash_result_t esp_rom_spiflash_enable_write(esp_rom_spiflash_chip_t *spi) +{ + uint32_t flash_status = 0; + + esp_rom_spiflash_wait_idle(spi); + + //enable write + WRITE_PERI_REG(PERIPHS_SPI_FLASH_CMD, SPI_FLASH_WREN); // enable write operation + while (READ_PERI_REG(PERIPHS_SPI_FLASH_CMD) != 0); + + // make sure the flash is ready for writing + while (ESP_ROM_SPIFLASH_WRENABLE_FLAG != (flash_status & ESP_ROM_SPIFLASH_WRENABLE_FLAG)) { + esp_rom_spiflash_read_status(spi, &flash_status); + } + + return ESP_ROM_SPIFLASH_RESULT_OK; +} + +static void spi_cache_mode_switch(uint32_t modebit) +{ + if ((modebit & SPI_FREAD_QIO) && (modebit & SPI_FASTRD_MODE)) { + REG_CLR_BIT(SPI_USER_REG(0), SPI_USR_MOSI); + REG_SET_BIT(SPI_USER_REG(0), SPI_USR_MISO | SPI_USR_DUMMY | SPI_USR_ADDR); + REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_ADDR_BITLEN, SPI0_R_QIO_ADDR_BITSLEN); + REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, SPI0_R_QIO_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[0]); + REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0xEB); + } else if (modebit & SPI_FASTRD_MODE) { + REG_CLR_BIT(SPI_USER_REG(0), SPI_USR_MOSI); + REG_SET_BIT(SPI_USER_REG(0), SPI_USR_MISO | SPI_USR_DUMMY | SPI_USR_ADDR); + REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_ADDR_BITLEN, SPI0_R_FAST_ADDR_BITSLEN); + if ((modebit & SPI_FREAD_QUAD)) { + REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0x6B); + REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, SPI0_R_FAST_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[0]); + } else if ((modebit & SPI_FREAD_DIO)) { + REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, SPI0_R_DIO_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[0]); + REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0xBB); + } else if ((modebit & SPI_FREAD_DUAL)) { + REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, SPI0_R_FAST_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[0]); + REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0x3B); + } else { + REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, SPI0_R_FAST_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[0]); + REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0x0B); + } + } else { + REG_CLR_BIT(SPI_USER_REG(0), SPI_USR_MOSI); + if (g_rom_spiflash_dummy_len_plus[0] == 0) { + REG_CLR_BIT(SPI_USER_REG(0), SPI_USR_DUMMY); + } else { + REG_SET_BIT(SPI_USER_REG(0), SPI_USR_DUMMY); + REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_DUMMY_CYCLELEN, g_rom_spiflash_dummy_len_plus[0] - 1); + } + REG_SET_BIT(SPI_USER_REG(0), SPI_USR_MISO | SPI_USR_ADDR); + REG_SET_FIELD(SPI_USER1_REG(0), SPI_USR_ADDR_BITLEN, SPI0_R_SIO_ADDR_BITSLEN); + REG_SET_FIELD(SPI_USER2_REG(0), SPI_USR_COMMAND_VALUE, 0x03); + } + +} + +esp_rom_spiflash_result_t esp_rom_spiflash_lock(void) +{ + uint32_t status; + + //read QE bit, not write if not QE + if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_read_statushigh(&g_rom_spiflash_chip, &status)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + //enable 2 byte status writing + SET_PERI_REG_MASK(PERIPHS_SPI_FLASH_CTRL, ESP_ROM_SPIFLASH_TWO_BYTE_STATUS_EN); + + if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_enable_write(&g_rom_spiflash_chip)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_write_status(&g_rom_spiflash_chip, status | ESP_ROM_SPIFLASH_WR_PROTECT)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + return ESP_ROM_SPIFLASH_RESULT_OK; +} + + +esp_rom_spiflash_result_t esp_rom_spiflash_config_readmode(esp_rom_spiflash_read_mode_t mode) +{ + uint32_t modebit; + + while ((REG_READ(SPI_EXT2_REG(1)) & SPI_ST)) { + } + while ((REG_READ(SPI_EXT2_REG(0)) & SPI_ST)) { + } + //clear old mode bit + CLEAR_PERI_REG_MASK(PERIPHS_SPI_FLASH_CTRL, SPI_FREAD_QIO | SPI_FREAD_QUAD | SPI_FREAD_DIO | SPI_FREAD_DUAL | SPI_FASTRD_MODE); + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(0), SPI_FREAD_QIO | SPI_FREAD_QUAD | SPI_FREAD_DIO | SPI_FREAD_DUAL | SPI_FASTRD_MODE); + //configure read mode + switch (mode) { + case ESP_ROM_SPIFLASH_QIO_MODE : modebit = SPI_FREAD_QIO | SPI_FASTRD_MODE; break; + case ESP_ROM_SPIFLASH_QOUT_MODE : modebit = SPI_FREAD_QUAD | SPI_FASTRD_MODE; break; + case ESP_ROM_SPIFLASH_DIO_MODE : modebit = SPI_FREAD_DIO | SPI_FASTRD_MODE; break; + case ESP_ROM_SPIFLASH_DOUT_MODE : modebit = SPI_FREAD_DUAL | SPI_FASTRD_MODE; break; + case ESP_ROM_SPIFLASH_FASTRD_MODE: modebit = SPI_FASTRD_MODE; break; + case ESP_ROM_SPIFLASH_SLOWRD_MODE: modebit = 0; break; + default : modebit = 0; + } + + SET_PERI_REG_MASK(PERIPHS_SPI_FLASH_CTRL, modebit); + SET_PERI_REG_MASK(SPI_CTRL_REG(0), modebit); + spi_cache_mode_switch(modebit); + + return ESP_ROM_SPIFLASH_RESULT_OK; +} + +esp_rom_spiflash_result_t esp_rom_spiflash_erase_chip(void) +{ + if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_enable_write(&g_rom_spiflash_chip)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_erase_chip_internal(&g_rom_spiflash_chip)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + return ESP_ROM_SPIFLASH_RESULT_OK; +} + +esp_rom_spiflash_result_t esp_rom_spiflash_erase_block(uint32_t block_num) +{ + // flash write is always 1 line currently + REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY); + REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, ESP_ROM_SPIFLASH_W_SIO_ADDR_BITSLEN); + + //check program size + if (block_num >= ((g_rom_spiflash_chip.chip_size) / (g_rom_spiflash_chip.block_size))) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_enable_write(&g_rom_spiflash_chip)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_erase_block_internal(&g_rom_spiflash_chip, block_num * (g_rom_spiflash_chip.block_size))) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + return ESP_ROM_SPIFLASH_RESULT_OK; +} + +esp_rom_spiflash_result_t esp_rom_spiflash_erase_sector(uint32_t sector_num) +{ + // flash write is always 1 line currently + REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY); + REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, ESP_ROM_SPIFLASH_W_SIO_ADDR_BITSLEN); + + //check program size + if (sector_num >= ((g_rom_spiflash_chip.chip_size) / (g_rom_spiflash_chip.sector_size))) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_enable_write(&g_rom_spiflash_chip)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_erase_sector_internal(&g_rom_spiflash_chip, sector_num * (g_rom_spiflash_chip.sector_size))) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + return ESP_ROM_SPIFLASH_RESULT_OK; +} + +esp_rom_spiflash_result_t esp_rom_spiflash_write(uint32_t target, const uint32_t *src_addr, int32_t len) +{ + uint32_t page_size; + uint32_t pgm_len; + + // flash write is always 1 line currently + REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY); + REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, ESP_ROM_SPIFLASH_W_SIO_ADDR_BITSLEN); + + //check program size + if ( (target + len) > (g_rom_spiflash_chip.chip_size)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + page_size = g_rom_spiflash_chip.page_size; + pgm_len = page_size - (target % page_size); + if ((uint32_t)len < pgm_len) { + if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_program_page_internal(&g_rom_spiflash_chip, + target, (uint32_t *)src_addr, len)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + } else { + if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_program_page_internal(&g_rom_spiflash_chip, + target, (uint32_t *)src_addr, pgm_len)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + //whole page program + uint32_t pgm_num = (len - pgm_len) / page_size; + uint8_t i; + for (i = 0; i < pgm_num; i++) { + if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_program_page_internal(&g_rom_spiflash_chip, + target + pgm_len, (uint32_t *)src_addr + (pgm_len >> 2), page_size)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + pgm_len += page_size; + } + + //remain parts to program + if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_program_page_internal(&g_rom_spiflash_chip, + target + pgm_len, (uint32_t *)src_addr + (pgm_len >> 2), len - pgm_len)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + } + return ESP_ROM_SPIFLASH_RESULT_OK; +} + +esp_rom_spiflash_result_t esp_rom_spiflash_write_encrypted(uint32_t flash_addr, uint32_t *data, uint32_t len) +{ + esp_rom_spiflash_result_t ret = ESP_ROM_SPIFLASH_RESULT_OK; + uint32_t i; + + if ((flash_addr & 0x1f) || (len & 0x1f)) { //check 32 byte alignment + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + esp_rom_spiflash_write_encrypted_enable(); + + for (i = 0; i < (len >> 5); i++) { + if ((ret = esp_rom_spiflash_prepare_encrypted_data(flash_addr + (i << 5), data + (i << 3))) != ESP_ROM_SPIFLASH_RESULT_OK) { + break; + } + + if ((ret = esp_rom_spiflash_write(flash_addr + (i << 5), data, 32)) != ESP_ROM_SPIFLASH_RESULT_OK) { + break; + } + } + + esp_rom_spiflash_write_encrypted_disable(); + + return ret; +} + + +esp_rom_spiflash_result_t esp_rom_spiflash_read(uint32_t target, uint32_t *dest_addr, int32_t len) +{ + // QIO or SIO, non-QIO regard as SIO + uint32_t modebit; + modebit = READ_PERI_REG(PERIPHS_SPI_FLASH_CTRL); + if ((modebit & SPI_FREAD_QIO) && (modebit & SPI_FASTRD_MODE)) { + REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_MOSI); + REG_SET_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_MISO | SPI_USR_DUMMY | SPI_USR_ADDR); + REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, SPI1_R_QIO_ADDR_BITSLEN); + REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_DUMMY_CYCLELEN, SPI1_R_QIO_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[1]); + //REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG2, SPI_USR_COMMAND_VALUE, 0xEB); + REG_WRITE(PERIPHS_SPI_FLASH_USRREG2, (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0xEB); + } else if (modebit & SPI_FASTRD_MODE) { + REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_MOSI); + REG_SET_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_MISO | SPI_USR_ADDR); + if (modebit & SPI_FREAD_DIO) { + if (g_rom_spiflash_dummy_len_plus[1] == 0) { + REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY); + REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, SPI1_R_DIO_ADDR_BITSLEN); + REG_WRITE(PERIPHS_SPI_FLASH_USRREG2, (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0xBB); + } else { + REG_SET_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY); + REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, SPI1_R_DIO_ADDR_BITSLEN); + REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_DUMMY_CYCLELEN, g_rom_spiflash_dummy_len_plus[1] - 1); + REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG2, SPI_USR_COMMAND_VALUE, 0xBB); + } + } else { + if ((modebit & SPI_FREAD_QUAD)) { + //REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG2, SPI_USR_COMMAND_VALUE, 0x6B); + REG_WRITE(PERIPHS_SPI_FLASH_USRREG2, (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0x6B); + } else if ((modebit & SPI_FREAD_DUAL)) { + //REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG2, SPI_USR_COMMAND_VALUE, 0x3B); + REG_WRITE(PERIPHS_SPI_FLASH_USRREG2, (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0x3B); + } else { + //REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG2, SPI_USR_COMMAND_VALUE, 0x0B); + REG_WRITE(PERIPHS_SPI_FLASH_USRREG2, (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0x0B); + } + REG_SET_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY); + REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, SPI1_R_FAST_ADDR_BITSLEN); + REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_DUMMY_CYCLELEN, SPI1_R_FAST_DUMMY_CYCLELEN + g_rom_spiflash_dummy_len_plus[1]); + } + } else { + REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_MOSI); + if (g_rom_spiflash_dummy_len_plus[1] == 0) { + REG_CLR_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY); + } else { + REG_SET_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_DUMMY); + REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_DUMMY_CYCLELEN, g_rom_spiflash_dummy_len_plus[1] - 1); + } + REG_SET_BIT(PERIPHS_SPI_FLASH_USRREG, SPI_USR_MISO | SPI_USR_ADDR); + REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG1, SPI_USR_ADDR_BITLEN, SPI1_R_SIO_ADDR_BITSLEN); + //REG_SET_FIELD(PERIPHS_SPI_FLASH_USRREG2, SPI_USR_COMMAND_VALUE, 0x03); + REG_WRITE(PERIPHS_SPI_FLASH_USRREG2, (0x7 << SPI_USR_COMMAND_BITLEN_S) | 0x03); + } + + if ( ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_read_data(&g_rom_spiflash_chip, target, dest_addr, len)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + return ESP_ROM_SPIFLASH_RESULT_OK; +} + +esp_rom_spiflash_result_t esp_rom_spiflash_erase_area(uint32_t start_addr, uint32_t area_len) +{ + int32_t total_sector_num; + int32_t head_sector_num; + uint32_t sector_no; + uint32_t sector_num_per_block; + + //set read mode to Fastmode ,not QDIO mode for erase + // + // TODO: this is probably a bug as it doesn't re-enable QIO mode, not serious as this + // function is not used in IDF. + esp_rom_spiflash_config_readmode(ESP_ROM_SPIFLASH_SLOWRD_MODE); + + //check if area is oversize of flash + if ((start_addr + area_len) > g_rom_spiflash_chip.chip_size) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + //start_addr is aligned as sector boundary + if (0 != (start_addr % g_rom_spiflash_chip.sector_size)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + //Unlock flash to enable erase + if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_unlock(/*&g_rom_spiflash_chip*/)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + + sector_no = start_addr / g_rom_spiflash_chip.sector_size; + sector_num_per_block = g_rom_spiflash_chip.block_size / g_rom_spiflash_chip.sector_size; + total_sector_num = (0 == (area_len % g_rom_spiflash_chip.sector_size)) ? area_len / g_rom_spiflash_chip.sector_size : + 1 + (area_len / g_rom_spiflash_chip.sector_size); + + //check if erase area reach over block boundary + head_sector_num = sector_num_per_block - (sector_no % sector_num_per_block); + + head_sector_num = (head_sector_num >= total_sector_num) ? total_sector_num : head_sector_num; + + //JJJ, BUG of 6.0 erase + //middle part of area is aligned by blocks + total_sector_num -= head_sector_num; + + //head part of area is erased + while (0 != head_sector_num) { + if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_erase_sector(sector_no)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + sector_no++; + head_sector_num--; + } + while (total_sector_num > (int32_t)sector_num_per_block) { + if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_erase_block(sector_no / sector_num_per_block)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + sector_no += sector_num_per_block; + total_sector_num -= sector_num_per_block; + } + + //tail part of area burn + while (0 < total_sector_num) { + if (ESP_ROM_SPIFLASH_RESULT_OK != esp_rom_spiflash_erase_sector(sector_no)) { + return ESP_ROM_SPIFLASH_RESULT_ERR; + } + sector_no++; + total_sector_num--; + } + + return ESP_ROM_SPIFLASH_RESULT_OK; +} + +#endif diff --git a/cpu/esp32/esp/LICENSE b/cpu/esp32/esp/LICENSE new file mode 100644 index 0000000000000..08d96c4a59874 --- /dev/null +++ b/cpu/esp32/esp/LICENSE @@ -0,0 +1,12 @@ +Copyright (c) 2015, SuperHouse Automation Pty Ltd +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 of the copyright holder 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 HOLDER 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. diff --git a/cpu/esp32/esp/README.md b/cpu/esp32/esp/README.md new file mode 100644 index 0000000000000..cc57fa847b3f0 --- /dev/null +++ b/cpu/esp32/esp/README.md @@ -0,0 +1,3 @@ +All files in this directory are part of [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos.git). They are under the copyright of their respective owners. + +All of these files are BSD Licensed as described in the file [LICENSE](https://github.com/SuperHouse/esp-open-rtos/blob/master/LICENSE). diff --git a/cpu/esp32/esp/common_macros.h b/cpu/esp32/esp/common_macros.h new file mode 100644 index 0000000000000..9189f9e60e62e --- /dev/null +++ b/cpu/esp32/esp/common_macros.h @@ -0,0 +1,141 @@ +/* Some common compiler macros + * + * Not esp8266-specific. + * + * Part of esp-open-rtos + * Copyright (C) 2015 Superhouse Automation Pty Ltd + * BSD Licensed as described in the file LICENSE + */ + +/* +Copyright (c) 2015, SuperHouse Automation Pty Ltd +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 of the copyright holder 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 HOLDER 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. +*/ + +#ifndef COMMON_MACROS_H +#define COMMON_MACROS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define UNUSED __attributed((unused)) + +/* These macros convert values to/from bitfields specified by *_M and *_S (mask + * and shift) constants. Used primarily with ESP8266 register access. + */ + +#define VAL2FIELD(fieldname, value) ((value) << fieldname##_S) +#define FIELD2VAL(fieldname, regbits) (((regbits) >> fieldname##_S) & fieldname##_M) + +#define FIELD_MASK(fieldname) (fieldname##_M << fieldname##_S) +#define SET_FIELD(regbits, fieldname, value) (((regbits) & ~FIELD_MASK(fieldname)) | VAL2FIELD(fieldname, value)) + +/* VAL2FIELD/SET_FIELD do not normally check to make sure that the passed value + * will fit in the specified field (without clobbering other bits). This makes + * them faster and is usually fine. If you do need to make sure that the value + * will not overflow the field, use VAL2FIELD_M or SET_FIELD_M (which will + * first mask the supplied value to only the allowed number of bits) instead. + */ +#define VAL2FIELD_M(fieldname, value) (((value) & fieldname##_M) << fieldname##_S) +#define SET_FIELD_M(regbits, fieldname, value) (((regbits) & ~FIELD_MASK(fieldname)) | VAL2FIELD_M(fieldname, value)) + +/* Set bits in reg with specified mask. + */ +#define SET_MASK_BITS(reg, mask) (reg) |= (mask) + +/* Clear bits in reg with specified mask + */ +#define CLEAR_MASK_BITS(reg, mask) (reg) &= ~(mask) + +/* Use the IRAM macro to place functions into Instruction RAM (IRAM) + instead of flash (aka irom). + + (This is the opposite to the Espressif SDK, where functions default + to being placed in IRAM but the ICACHE_FLASH_ATTR attribute will + place them in flash.) + + Use the IRAM attribute for functions which are called when the + flash may not be available (for example during NMI exceptions), or + for functions which are called very frequently and need high + performance. + + Usage example: + + void IRAM high_performance_function(void) + { + // do important thing here + } + + Bear in mind IRAM is limited (32KB), compared to up to 1MB of flash. +*/ +#define IRAM __attribute__((section(".iram1.text"))) + +/* Use the RAM macro to place constant data (rodata) into RAM (data + RAM) instead of the default placement in flash. This is useful for + constant data which needs high performance access. + + Usage example: + + const RAM uint8_t constants[] = { 1, 2, 3, 7 }; + + When placing string literals in RAM, they need to be declared with + the type "const char[]" not "const char *" + + Usage example: + + const RAM char hello_world[] = "Hello World"; +*/ +#define RAM __attribute__((section(".data"))) + +/* Use the IRAM_DATA macro to place data into Instruction RAM (IRAM) + instead of the default of flash (for constant data) or data RAM + (for non-constant data). + + This may be useful to free up data RAM. However all data read from + any instruction space (either IRAM or Flash) must be 32-bit aligned + word reads. Reading unaligned data stored with IRAM_DATA will be + slower than reading data stored in RAM. You can't perform unaligned + writes to IRAM. +*/ +#define IRAM_DATA __attribute__((section(".iram1.data"))) + +/* Use the IROM macro to store constant values in IROM flash. In + esp-open-rtos this is already the default location for most constant + data (rodata), so you don't need this attribute in 99% of cases. + + The exceptions are to mark data in the core & freertos libraries, + where the default for constant data storage is RAM. + + (Unlike the Espressif SDK you don't need to use an attribute like + ICACHE_FLASH_ATTR for functions, they go into flash by default.) + + Important to note: IROM flash is accessed via 32-bit word aligned + reads. esp-open-rtos does some magic to "fix" unaligned reads, but + performance is reduced. +*/ +#ifdef __cplusplus + #define IROM __attribute__((section(".irom0.text"))) + #define IROM_LIT __attribute__((section(".irom0.literal"))) +#else + #define IROM __attribute__((section(".irom0.text"))) + #define IROM_LIT __attribute__((section(".irom0.literal"))) const +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* COMMON_MACROS_H */ diff --git a/cpu/esp32/esp/xtensa_ops.h b/cpu/esp32/esp/xtensa_ops.h new file mode 100644 index 0000000000000..12006843aab3a --- /dev/null +++ b/cpu/esp32/esp/xtensa_ops.h @@ -0,0 +1,65 @@ +/** xtensa_ops.h + * + * Special macros/etc which deal with Xtensa-specific architecture/CPU + * considerations. + * + * Part of esp-open-rtos + * Copyright (C) 2015 Superhouse Automation Pty Ltd + * BSD Licensed as described in the file LICENSE + */ + +/* +Copyright (c) 2015, SuperHouse Automation Pty Ltd +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 of the copyright holder 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 HOLDER 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. +*/ + +#ifndef XTENSA_OPS_H +#define XTENSA_OPS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Read stack pointer to variable. + * + * Note that the compiler will push a stack frame (minimum 16 bytes) + * in the prelude of a C function that calls any other functions. + */ +#define SP(var) __asm__ volatile ("mov %0, a1" : "=r" (var)) + +/* Read the function return address to a variable. + * + * Depends on the containing function being simple enough that a0 is + * being used as a working register. + */ +#define RETADDR(var) __asm__ volatile ("mov %0, a0" : "=r" (var)) + +// GCC macros for reading, writing, and exchanging Xtensa processor special +// registers: + +#define RSR(var, reg) __asm__ volatile ("rsr %0, " #reg : "=r" (var)); +#define WSR(var, reg) __asm__ volatile ("wsr %0, " #reg : : "r" (var)); +#define XSR(var, reg) __asm__ volatile ("xsr %0, " #reg : "+r" (var)); + +// GCC macros for performing associated "*sync" opcodes + +#define ISYNC() __asm__ volatile ( "isync" ) +#define RSYNC() __asm__ volatile ( "rsync" ) +#define ESYNC() __asm__ volatile ( "esync" ) +#define DSYNC() __asm__ volatile ( "dsync" ) + +#ifdef __cplusplus +} +#endif + +#endif /* XTENSA_OPS_H */ diff --git a/cpu/esp32/exceptions.c b/cpu/esp32/exceptions.c new file mode 100644 index 0000000000000..65584c0810475 --- /dev/null +++ b/cpu/esp32/exceptions.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief ESP32 exception handling + * + * @author Gunar Schorcht + * @} + */ + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#include +#include +#include + +#include "common.h" +#include "irq.h" +#include "log.h" +#include "periph/pm.h" +#include "ps.h" + +#include "esp/common_macros.h" +#include "esp/xtensa_ops.h" +#include "rom/ets_sys.h" +#include "rom/rtc.h" +#include "rom/uart.h" +#include "sdkconfig.h" +#include "xtensa/corebits.h" +#include "freertos/xtensa_api.h" + +#ifdef ENABLE_GDBSTUB +#include "esp_gdbstub.h" +#endif + +#ifdef ESP_IDF_HEAP_USED +#include "heap/esp_heap_caps.h" +#endif + +extern void malloc_stats (void); +extern unsigned int get_free_heap_size (void); +extern uint8_t _eheap; /* end of heap (defined in esp32.common.ld) */ +extern uint8_t _sheap; /* start of heap (defined in esp32.common.ld) */ + +static const char* exception_names [] = +{ + "IllegalInstructionCause", /* 0 */ + "SyscallCause", /* 1 */ + "InstructionFetchErrorCause", /* 2 */ + "LoadStoreErrorCause", /* 3 */ + "Level1InterruptCause", /* 4 */ + "AllocaCause", /* 5 */ + "IntegerDivideByZeroCause", /* 6 */ + "", /* 7 - reserved */ + "PrivilegedCause", /* 8 */ + "LoadStoreAlignmentCause", /* 9 */ + "", /* 10 - reserved */ + "", /* 11 - reserved */ + "InstrPIFDataErrorCause", /* 12 */ + "LoadStorePIFDataErrorCause", /* 13 */ + "InstrPIFAddrErrorCause", /* 14 */ + "LoadStorePIFAddrErrorCause", /* 15 */ + "InstTLBMissCause", /* 16 */ + "InstTLBMultiHitCause", /* 17 */ + "InstFetchPrivilegeCause", /* 18 */ + "", /* 19 - reserved */ + "InstFetchProhibitedCause", /* 20 */ + "", /* 21 - reserved */ + "", /* 22 - reserved */ + "", /* 23 - reserved */ + "LoadStoreTLBMissCause", /* 24 */ + "LoadStoreTLBMultiHitCause", /* 25 */ + "LoadStorePrivilegeCause", /* 26 */ + "", /* 27 - reserved */ + "LoadProhibitedCause", /* 28 */ + "StoreProhibitedCause", /* 29 */ + "", /* 30 - reserved */ + "", /* 31 - reserved */ + "Coprocessor0Disabled", /* 32 */ + "Coprocessor1Disabled", /* 33 */ + "Coprocessor2Disabled", /* 34 */ + "Coprocessor3Disabled", /* 35 */ + "Coprocessor4Disabled", /* 36 */ + "Coprocessor5Disabled", /* 37 */ + "Coprocessor6Disabled", /* 38 */ + "Coprocessor7Disabled", /* 39 */ +}; + +const char *reg_names[] = { + "pc ", "ps ", + "a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ", + "a6 ", "a7 ", "a8 ", "a9 ", "a10 ", "a11 ", + "a12 ", "a13 ", "a14 ", "A15 ", "SAR ", + "exccause", "excvaddr", "lbeg ", "lend ", "lcount " +}; + +void IRAM NORETURN exception_handler (XtExcFrame *frame) +{ + uint32_t excsave1; + uint32_t epc1; + uint32_t epc2; + uint32_t epc3; + uint32_t epc4; + RSR(excsave1, excsave1); + RSR(epc1, epc1); + RSR(epc2, epc2); + RSR(epc3, epc3); + RSR(epc4, epc4); + + #ifdef ENABLE_GDBSTUB + esp_gdbstub_panic_handler(frame); + #endif + + ets_printf("EXCEPTION!! exccause=%d (%s) @%08lx excvaddr=%08lx\n\n", + frame->exccause, exception_names[frame->exccause], + excsave1, frame->excvaddr); + + #if defined(DEVELHELP) + #if defined(MODULE_PS) + ets_printf("processes:\n"); + ps(); + ets_printf("\n"); + #endif /* MODULE_PS */ + #ifdef ESP_IDF_HEAP_USED + heap_caps_print_heap_info(MALLOC_CAP_DEFAULT); + #else + ets_printf("\nheap: %lu (free %lu) byte\n", &_eheap - &_sheap, get_free_heap_size()); + #endif /* ESP_IDF_HEAP_USED */ + + ets_printf("\nregister set\n"); + ets_printf("pc : %08lx\t", frame->pc); + ets_printf("ps : %08lx\t", frame->ps); + ets_printf("exccause: %08lx\t", frame->exccause); + ets_printf("excvaddr: %08lx\n", frame->excvaddr); + ets_printf("epc1 : %08lx\t", epc1); + ets_printf("epc2 : %08lx\t", epc2); + ets_printf("epc3 : %08lx\t", epc3); + ets_printf("epc4 : %08lx\n", epc4); + ets_printf("a0 : %08lx\t", frame->a0); + ets_printf("a1 : %08lx\t", frame->a1); + ets_printf("a2 : %08lx\t", frame->a2); + ets_printf("a3 : %08lx\n", frame->a3); + ets_printf("a4 : %08lx\t", frame->a4); + ets_printf("a5 : %08lx\t", frame->a5); + ets_printf("a6 : %08lx\t", frame->a6); + ets_printf("a7 : %08lx\n", frame->a7); + ets_printf("a8 : %08lx\t", frame->a8); + ets_printf("a9 : %08lx\t", frame->a9); + ets_printf("a10 : %08lx\t", frame->a10); + ets_printf("a11 : %08lx\n", frame->a11); + ets_printf("a12 : %08lx\t", frame->a12); + ets_printf("a13 : %08lx\t", frame->a13); + ets_printf("a14 : %08lx\t", frame->a14); + ets_printf("a15 : %08lx\n", frame->a15); + ets_printf("lbeg : %08lx\t", frame->lbeg); + ets_printf("lend : %08lx\t", frame->lend); + ets_printf("lcount : %08lx\n", frame->lcount); + #endif /* DEVELHELP */ + + /* restart */ + /* TODO: Improvement + Normally, we should try to restart the system. However, this + will not work after some exceptions, e.g., the LoadStoreErrorCause. + Therefore, we break the execution and wait for the WDT reset. Maybe + there is better way. If debugger is active, 'break 0,0' stops + execution in debugger. */ + __asm__ volatile ("break 0,0"); + + UNREACHABLE(); +} + +void init_exceptions (void) +{ + xt_set_exception_handler(EXCCAUSE_UNALIGNED, exception_handler); + xt_set_exception_handler(EXCCAUSE_ILLEGAL, exception_handler); + xt_set_exception_handler(EXCCAUSE_INSTR_ERROR, exception_handler); + xt_set_exception_handler(EXCCAUSE_LOAD_STORE_ERROR, exception_handler); + xt_set_exception_handler(EXCCAUSE_LOAD_PROHIBITED, exception_handler); + xt_set_exception_handler(EXCCAUSE_STORE_PROHIBITED, exception_handler); + xt_set_exception_handler(EXCCAUSE_PRIVILEGED, exception_handler); +} + +void IRAM NORETURN panic_arch(void) +{ + #if defined(DEVELHELP) + #ifdef ESP_IDF_HEAP_USED + heap_caps_print_heap_info(MALLOC_CAP_DEFAULT); + #else + ets_printf("\nheap: %lu (free %lu) byte\n", &_eheap - &_sheap, get_free_heap_size()); + #endif /* ESP_IDF_HEAP_USED */ + #endif /* DEVELHELP */ + + /* restart */ + software_reset(); + + UNREACHABLE(); +} diff --git a/cpu/esp32/gen_esp32part.py b/cpu/esp32/gen_esp32part.py new file mode 100755 index 0000000000000..66b4f87f3d449 --- /dev/null +++ b/cpu/esp32/gen_esp32part.py @@ -0,0 +1,449 @@ +#!/usr/bin/env python +# +# ESP32 partition table generation tool +# +# Converts partition tables to/from CSV and binary formats. +# +# See http://esp-idf.readthedocs.io/en/latest/api-guides/partition-tables.html +# for explanation of partition table structure and uses. +# +# 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. +from __future__ import print_function, division +import argparse +import os +import re +import struct +import sys +import hashlib +import binascii + +MAX_PARTITION_LENGTH = 0xC00 # 3K for partition data (96 entries) leaves 1K in a 4K sector for signature +MD5_PARTITION_BEGIN = b"\xEB\xEB" + b"\xFF" * 14 # The first 2 bytes are like magic numbers for MD5 sum + +__version__ = '1.0' + +quiet = False +md5sum = True + + +def status(msg): + """ Print status message to stderr """ + if not quiet: + critical(msg) + + +def critical(msg): + """ Print critical message to stderr """ + if not quiet: + sys.stderr.write(msg) + sys.stderr.write('\n') + + +class PartitionTable(list): + def __init__(self): + super(PartitionTable, self).__init__(self) + + @classmethod + def from_csv(cls, csv_contents): + res = PartitionTable() + lines = csv_contents.splitlines() + + def expand_vars(f): + f = os.path.expandvars(f) + m = re.match(r'(?= MAX_PARTITION_LENGTH: + raise InputError("Binary partition table length (%d) longer than max" % len(result)) + result += b"\xFF" * (MAX_PARTITION_LENGTH - len(result)) # pad the sector, for signing + return result + + def to_csv(self, simple_formatting=False): + rows = ["# Espressif ESP32 Partition Table", + "# Name, Type, SubType, Offset, Size, Flags"] + rows += [x.to_csv(simple_formatting) for x in self] + return "\n".join(rows) + "\n" + + +class PartitionDefinition(object): + APP_TYPE = 0x00 + DATA_TYPE = 0x01 + TYPES = { + "app": APP_TYPE, + "data": DATA_TYPE, + } + + # Keep this map in sync with esp_partition_subtype_t enum in esp_partition.h + SUBTYPES = { + APP_TYPE: { + "factory": 0x00, + "test": 0x20, + }, + DATA_TYPE: { + "ota": 0x00, + "phy": 0x01, + "nvs": 0x02, + "coredump": 0x03, + "esphttpd": 0x80, + "fat": 0x81, + "spiffs": 0x82, + }, + } + + MAGIC_BYTES = b"\xAA\x50" + + ALIGNMENT = { + APP_TYPE: 0x10000, + DATA_TYPE: 0x04, + } + + # dictionary maps flag name (as used in CSV flags list, property name) + # to bit set in flags words in binary format + FLAGS = { + "encrypted": 0 + } + + # add subtypes for the 16 OTA slot values ("ota_XXX, etc.") + for ota_slot in range(16): + SUBTYPES[TYPES["app"]]["ota_%d" % ota_slot] = 0x10 + ota_slot + + def __init__(self): + self.name = "" + self.type = None + self.subtype = None + self.offset = None + self.size = None + self.encrypted = False + + @classmethod + def from_csv(cls, line): + """ Parse a line from the CSV """ + line_w_defaults = line + ",,,," # lazy way to support default fields + fields = [f.strip() for f in line_w_defaults.split(",")] + + res = PartitionDefinition() + res.name = fields[0] + res.type = res.parse_type(fields[1]) + res.subtype = res.parse_subtype(fields[2]) + res.offset = res.parse_address(fields[3]) + res.size = res.parse_address(fields[4]) + if res.size is None: + raise InputError("Size field can't be empty") + + flags = fields[5].split(":") + for flag in flags: + if flag in cls.FLAGS: + setattr(res, flag, True) + elif len(flag) > 0: + raise InputError("CSV flag column contains unknown flag '%s'" % (flag)) + + return res + + def __eq__(self, other): + return self.name == other.name and self.type == other.type \ + and self.subtype == other.subtype and self.offset == other.offset \ + and self.size == other.size + + def __repr__(self): + def maybe_hex(x): + return "0x%x" % x if x is not None else "None" + return "PartitionDefinition('%s', 0x%x, 0x%x, %s, %s)" % (self.name, self.type, + self.subtype or 0, + maybe_hex(self.offset), + maybe_hex(self.size)) + + def __str__(self): + return "Part '%s' %d/%d @ 0x%x size 0x%x" % (self.name, self.type, + self.subtype, self.offset or -1, + self.size or -1) + + def __cmp__(self, other): + return self.offset - other.offset + + def parse_type(self, strval): + if strval == "": + raise InputError("Field 'type' can't be left empty.") + return parse_int(strval, self.TYPES) + + def parse_subtype(self, strval): + if strval == "": + return 0 # default + return parse_int(strval, self.SUBTYPES.get(self.type, {})) + + def parse_address(self, strval): + if strval == "": + return None # PartitionTable will fill in default + return parse_int(strval) + + def verify(self): + if self.type is None: + raise ValidationError(self, "Type field is not set") + if self.subtype is None: + raise ValidationError(self, "Subtype field is not set") + if self.offset is None: + raise ValidationError(self, "Offset field is not set") + align = self.ALIGNMENT.get(self.type, 4) + if self.offset % align: + raise ValidationError(self, "Offset 0x%x is not aligned to 0x%x" % (self.offset, align)) + if self.size is None: + raise ValidationError(self, "Size field is not set") + + STRUCT_FORMAT = "<2sBBLL16sL" + + @classmethod + def from_binary(cls, b): + if len(b) != 32: + raise InputError("Partition definition length must be exactly 32 bytes. Got %d bytes." % len(b)) + res = cls() + (magic, res.type, res.subtype, res.offset, + res.size, res.name, flags) = struct.unpack(cls.STRUCT_FORMAT, b) + if b"\x00" in res.name: # strip null byte padding from name string + res.name = res.name[:res.name.index(b"\x00")] + res.name = res.name.decode() + if magic != cls.MAGIC_BYTES: + raise InputError("Invalid magic bytes (%r) for partition definition" % magic) + for flag, bit in cls.FLAGS.items(): + if flags & (1 << bit): + setattr(res, flag, True) + flags &= ~(1 << bit) + if flags != 0: + critical("WARNING: Partition definition had unknown flag(s) 0x%08x. Newer binary format?" % flags) + return res + + def get_flags_list(self): + return [flag for flag in self.FLAGS.keys() if getattr(self, flag)] + + def to_binary(self): + flags = sum((1 << self.FLAGS[flag]) for flag in self.get_flags_list()) + return struct.pack(self.STRUCT_FORMAT, + self.MAGIC_BYTES, + self.type, self.subtype, + self.offset, self.size, + self.name.encode(), + flags) + + def to_csv(self, simple_formatting=False): + def addr_format(a, include_sizes): + if not simple_formatting and include_sizes: + for (val, suffix) in [(0x100000, "M"), (0x400, "K")]: + if a % val == 0: + return "%d%s" % (a // val, suffix) + return "0x%x" % a + + def lookup_keyword(t, keywords): + for k, v in keywords.items(): + if simple_formatting is False and t == v: + return k + return "%d" % t + + def generate_text_flags(): + """ colon-delimited list of flags """ + return ":".join(self.get_flags_list()) + + return ",".join([self.name, + lookup_keyword(self.type, self.TYPES), + lookup_keyword(self.subtype, self.SUBTYPES.get(self.type, {})), + addr_format(self.offset, False), + addr_format(self.size, True), + generate_text_flags()]) + + +def parse_int(v, keywords={}): + """Generic parser for integer fields - int(x,0) with provision for + k/m/K/M suffixes and 'keyword' value lookup. + """ + try: + for letter, multiplier in [("k", 1024), ("m", 1024*1024)]: + if v.lower().endswith(letter): + return parse_int(v[:-1], keywords) * multiplier + return int(v, 0) + except ValueError: + if len(keywords) == 0: + raise InputError("Invalid field value %s" % v) + try: + return keywords[v.lower()] + except KeyError: + raise InputError("Value '%s' is not valid. Known keywords: %s" % (v, ", ".join(keywords))) + + +def main(): + global quiet + global md5sum + parser = argparse.ArgumentParser(description='ESP32 partition table utility') + + parser.add_argument('--flash-size', + help='Optional flash size limit, checks partition table fits in flash', + nargs='?', choices=['1MB', '2MB', '4MB', '8MB', '16MB']) + parser.add_argument('--disable-md5sum', help='Disable md5 checksum for the partition table', + default=False, action='store_true') + parser.add_argument('--verify', '-v', help='Verify partition table fields', + default=True, action='store_false') + parser.add_argument('--quiet', '-q', help="Don't print status messages to stderr", + action='store_true') + + parser.add_argument('input', + help='Path to CSV or binary file to parse. Will use stdin if omitted.', + type=argparse.FileType('rb'), default=sys.stdin) + parser.add_argument('output', help='Path to output converted binary or CSV file. Will use ' + 'stdout if omitted, unless the --display argument is also passed (in ' + 'which case only the summary is printed.)', + nargs='?', + default='-') + + args = parser.parse_args() + + quiet = args.quiet + md5sum = not args.disable_md5sum + input = args.input.read() + input_is_binary = input[0:2] == PartitionDefinition.MAGIC_BYTES + if input_is_binary: + status("Parsing binary partition input...") + table = PartitionTable.from_binary(input) + else: + input = input.decode() + status("Parsing CSV input...") + table = PartitionTable.from_csv(input) + + if args.verify: + status("Verifying table...") + table.verify() + + if args.flash_size: + size_mb = int(args.flash_size.replace("MB", "")) + size = size_mb * 1024 * 1024 # flash memory uses honest megabytes! + table_size = table.flash_size() + if size < table_size: + raise InputError("Partitions defined in '%s' occupy %.1fMB of flash (%d bytes) which " + "does not fit in configured flash size %dMB. Change the flash size " + "in menuconfig under the 'Serial Flasher Config' menu." % + (args.input.name, table_size / 1024.0 / 1024.0, table_size, size_mb)) + + if input_is_binary: + output = table.to_csv() + with sys.stdout if args.output == '-' else open(args.output, 'w') as f: + f.write(output) + else: + output = table.to_binary() + with sys.stdout.buffer if args.output == '-' else open(args.output, 'wb') as f: + f.write(output) + + +class InputError(RuntimeError): + def __init__(self, e): + super(InputError, self).__init__(e) + + +class ValidationError(InputError): + def __init__(self, partition, message): + super(ValidationError, self).__init__( + "Partition %s invalid: %s" % (partition.name, message)) + + +if __name__ == '__main__': + try: + main() + except InputError as e: + print(e, file=sys.stderr) + sys.exit(2) diff --git a/cpu/esp32/include/adc_arch.h b/cpu/esp32/include/adc_arch.h new file mode 100644 index 0000000000000..a33910e2e36de --- /dev/null +++ b/cpu/esp32/include/adc_arch.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @ingroup drivers_periph_gpio + * @{ + * + * @file + * @brief Architecture specific ADC functions ESP32 + * + * @author Gunar Schorcht + * @} + */ + +#ifndef ADC_ARCH_H +#define ADC_ARCH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "periph/gpio.h" +#include "periph/adc.h" + +/** + * @brief Attenuations that can be set for ADC lines + */ +typedef enum { + ADC_ATTENUATION_0_DB = 0, /**< full-range is about 1.1 V (Vref) */ + ADC_ATTENUATION_3_DB, /**< full-range is about 1.5 V */ + ADC_ATTENUATION_6_DB, /**< full-range is about 2.2 V */ + ADC_ATTENUATION_11_DB /**< full-range is about 3.3 V */ +} adc_attenuation_t; + +/** + * @brief Set the attenuation for the ADC line. Default attenuation is 11 dB. + * + * For each ADC line, an attenuation of the input signal can be defined + * separately. This results in different full ranges of the measurable voltage + * at the input. The attenuation can be set to 0 dB, 3 dB, 6 dB and 11 dB, + * with 11 dB being the standard attenuation. Since an ADC input is measured + * against a reference voltage Vref of 1.1 V, approximately the following + * measurement ranges are given when using a corresponding attenuation: + * + * 0 dB: 0 ... 1.1V + * 3 dB: 0 ... 1.5V + * 6 dB: 0 ... 2.2V + * 11 dB 0 ... 3.3V + * + * Please note: The reference voltage Vref can vary from device to device in + * the range of 1.0V and 1.2V. The Vref of a device can be read with the + * function *adc_vref_to_gpio25* at the pin GPIO 23. The results of the ADC + * input can then be adjusted accordingly. + * + * @param line ADC line for which the attenuation is set + * @param atten Attenuation, see type definition of *adc_attenuation_t + * @return 0 on success + * @return -1 on invalid ADC line + */ +int adc_set_attenuation(adc_t line, adc_attenuation_t atten); + +/** + * @brief Output ADC reference voltage to GPIO25 + * + * @return 0 on success + * @return -1 on invalid ADC line + */ +int adc_vref_to_gpio25 (void); + +/** + * @brief Configure sleep mode for an GPIO pin if the pin is an RTCIO pin + * @param pin GPIO pin + * @param mode active in sleep mode if true + * @param input as input if true, as output otherwise + * @return 0 success + * @return -1 on invalid pin + */ +int rtcio_config_sleep_mode (gpio_t pin, bool mode, bool input); + +#ifdef __cplusplus +} +#endif + +#endif /* ADC_ARCH_H */ diff --git a/cpu/esp32/include/common.h b/cpu/esp32/include/common.h new file mode 100644 index 0000000000000..fc5eb3e294a03 --- /dev/null +++ b/cpu/esp32/include/common.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief Common helper macros + * + * @author Gunar Schorcht + * + */ + +#ifndef COMMON_H +#define COMMON_H + +#ifndef DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +#include "rom/ets_sys.h" + +#define asm __asm__ + +/** string representation of x */ +#ifndef XTSTR +#define _XTSTR(x) # x +#define XTSTR(x) _XTSTR(x) +#endif /* XSTR */ + +#if !defined(ICACHE_FLASH) +#ifndef ICACHE_RAM_ATTR +/** Places the code with this attribute in the IRAM. */ +#define ICACHE_RAM_ATTR __attribute__((section(".iram0.text"))) +#endif +#else /* ICACHE_FLASH */ +#ifndef ICACHE_RAM_ATTR +#define ICACHE_RAM_ATTR +#endif +#endif /* ICACHE_FLASH */ + +/** Print out a message that function is not yet implementd */ +#define NOT_YET_IMPLEMENTED() LOG_INFO("%s not yet implemented\n", __func__) +/** Print out a message that function is not supported */ +#define NOT_SUPPORTED() LOG_INFO("%s not supported\n", __func__) + +/** definitions for source code compatibility with ESP-IDF */ +#define ESP_EARLY_LOGE(tag, fmt, ...) ets_printf("%s(err): " fmt "\n", tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGW(tag, fmt, ...) ets_printf("%s(warn): " fmt "\n", tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGI(tag, fmt, ...) ets_printf("%s(info): " fmt "\n", tag, ##__VA_ARGS__) +#define ESP_LOGE(tag, fmt, ...) ets_printf("%s(err): " fmt "\n", tag, ##__VA_ARGS__) +#define ESP_LOGW(tag, fmt, ...) ets_printf("%s(warn): " fmt "\n", tag, ##__VA_ARGS__) +#define ESP_LOGI(tag, fmt, ...) ets_printf("%s(info): " fmt "\n", tag, ##__VA_ARGS__) + +#if ENABLE_DEBUG +/** + * @brief Parameter check with return a value. + * + * If ENABLE_DEBUG is true, the macro checks a condition and returns with a value + * if the condition is not fulfilled. + * @param cond the condition + * @param err the return value in the case the condition is not fulfilled. + */ +#define CHECK_PARAM_RET(cond,err) if (!(cond)) \ + { \ + DEBUG("%s parameter condition (" #cond ") " \ + "not fulfilled\n", __func__); \ + return err; \ + } + +/** + * @brief Parameter check without return value. + * + * If ENABLE_DEBUG is true, the macro checks a condition and returns without a + * value if the condition is not fulfilled. + * @param cond the condition + */ +#define CHECK_PARAM(cond) if (!(cond)) \ + { \ + DEBUG("%s parameter condition (" #cond ") " \ + "not fulfilled\n", __func__); \ + return; \ + } + +#define ESP_EARLY_LOGD(tag, fmt, ...) ets_printf("%s(dbg): " fmt "\n", tag, ##__VA_ARGS__) +#define ESP_EARLY_LOGV(tag, fmt, ...) ets_printf("%s: " fmt "\n", tag, ##__VA_ARGS__) +#define ESP_LOGD(tag, fmt, ...) ets_printf("%s(dbg): " fmt "\n", tag, ##__VA_ARGS__) +#define ESP_LOGV(tag, fmt, ...) ets_printf("%s: " fmt "\n", tag, ##__VA_ARGS__) + +#else /* ENABLE_DEBUG */ + +#define CHECK_PARAM_RET(cond,err) if (!(cond)) return err; +#define CHECK_PARAM(cond) if (!(cond)) return; + +#define ESP_EARLY_LOGD( tag, format, ... ) (void)tag +#define ESP_EARLY_LOGV( tag, format, ... ) (void)tag +#define ESP_LOGD( tag, format, ... ) (void)tag +#define ESP_LOGV( tag, format, ... ) (void)tag + +#endif /* ENABLE_DEBUG */ + +/** gives the minimum of a and b */ +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/** gives the maximum of a and b */ +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +/** + * @brief funcion name mappings for source code compatibility with ESP8266 port + * @{ + */ +#define system_get_cpu_freq ets_get_cpu_frequency +#define system_update_cpu_freq ets_update_cpu_frequency +/** @} */ + +/** @} */ + +/** microseconds per millisecond */ +#ifndef USEC_PER_MSEC +#define USEC_PER_MSEC 1000UL +#endif + +#ifndef MSEC_PER_SEC +#define MSEC_PER_SEC 1000UL +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ + +#endif /* COMMON_H */ diff --git a/cpu/esp32/include/cpu.h b/cpu/esp32/include/cpu.h new file mode 100644 index 0000000000000..c43a1fdaf2f87 --- /dev/null +++ b/cpu/esp32/include/cpu.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_32 + * @{ + * + * @file + * @brief CPU common functions + * + * @author Gunar Schorcht + */ + +#ifndef CPU_H +#define CPU_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "irq.h" + +#define PROVIDES_PM_SET_LOWEST + +/** + * @brief Print the last instruction's address + * + * @todo: Not supported + */ +static inline void cpu_print_last_instruction(void) +{ + /* This function must exist else RIOT won't compile */ +} + +#ifdef __cplusplus +} +#endif + +#endif /* CPU_H */ +/** @} */ diff --git a/cpu/esp32/include/cpu_conf.h b/cpu/esp32/include/cpu_conf.h new file mode 100644 index 0000000000000..94ac26d7fad96 --- /dev/null +++ b/cpu/esp32/include/cpu_conf.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief CPU specific configuration options + * + * @author Gunar Schorcht + */ + +#ifndef CPU_CONF_H +#define CPU_CONF_H + +#include +#include "xtensa_conf.h" +#include "xtensa/xtensa_context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Stack size configuration + * @{ + */ +#define THREAD_EXTRA_STACKSIZE_PRINTF (0) +#define THREAD_STACKSIZE_DEFAULT (2048) +#define THREAD_STACKSIZE_IDLE (2048) +#define THREAD_STACKSIZE_MAIN (3072) +/** @} */ + +/** + * Buffer size used for printf functions (maximum length of formatted output). + */ +#define PRINTF_BUFSIZ 256 + +#ifdef __cplusplus +} +#endif /* CPU_CONF_H */ + +#endif /* CPU_CONF_H */ +/** @} */ diff --git a/cpu/esp32/include/exceptions.h b/cpu/esp32/include/exceptions.h new file mode 100644 index 0000000000000..104c76f178ccc --- /dev/null +++ b/cpu/esp32/include/exceptions.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief ESP32 exception handling + * + * @author Gunar Schorcht + * @} + */ + +#ifndef EXCEPTIONS_H +#define EXCEPTIONS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** Initalize exception handler */ +extern void init_exceptions(void); + +#ifdef __cplusplus +} +#endif + +#endif /* EXCEPTIONS_H */ diff --git a/cpu/esp32/include/freertos/FreeRTOS.h b/cpu/esp32/include/freertos/FreeRTOS.h new file mode 100644 index 0000000000000..0efbe9acf49fb --- /dev/null +++ b/cpu/esp32/include/freertos/FreeRTOS.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + * FreeRTOS to RIOT-OS adaption module for source code compatibility + */ + +#ifndef FREERTOS_FREERTOS_H +#define FREERTOS_FREERTOS_H + +#ifndef DOXYGEN + +#include "portmacro.h" +#include "semphr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int32_t BaseType_t; +typedef uint32_t UBaseType_t; + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* FREERTOS_FREERTOS_H */ diff --git a/cpu/esp32/include/freertos/portmacro.h b/cpu/esp32/include/freertos/portmacro.h new file mode 100644 index 0000000000000..ea7b4b5fa70dd --- /dev/null +++ b/cpu/esp32/include/freertos/portmacro.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + * FreeRTOS to RIOT-OS adaption module for source code compatibility + */ + +#ifndef FREERTOS_PORTMACRO_H +#define FREERTOS_PORTMACRO_H + +#ifndef DOXYGEN + +#include "stdlib.h" + +#include "mutex.h" +#include "irq.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define portMUX_TYPE mutex_t +#define portMUX_INITIALIZER_UNLOCKED MUTEX_INIT +#define portENTER_CRITICAL(pm) mutex_lock(pm) +#define portEXIT_CRITICAL(pm) mutex_unlock(pm) +#define portENTER_CRITICAL_NESTED irq_disable +#define portEXIT_CRITICAL_NESTED irq_restore + +#define taskENTER_CRITICAL(mux) portENTER_CRITICAL(mux) +#define taskEXIT_CRITICAL(mux) portEXIT_CRITICAL(mux) + +#define xPortGetCoreID() PRO_CPU_NUM + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* FREERTOS_PORTMACRO_H */ diff --git a/cpu/esp32/include/freertos/semphr.h b/cpu/esp32/include/freertos/semphr.h new file mode 100644 index 0000000000000..27168aa70a8b8 --- /dev/null +++ b/cpu/esp32/include/freertos/semphr.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + * FreeRTOS to RIOT-OS adaption module for source code compatibility + */ + +#ifndef FREERTOS_SEMPHR_H +#define FREERTOS_SEMPHR_H + +#ifndef DOXYGEN + +#include "stdlib.h" + +#include "mutex.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SemaphoreHandle_t mutex_t* + +static inline SemaphoreHandle_t xSemaphoreCreateMutex(void) +{ + mutex_t* _tmp = (mutex_t*)malloc (sizeof(mutex_t)); + mutex_init(_tmp); + return _tmp; +} + +static inline void vSemaphoreDelete( SemaphoreHandle_t xSemaphore ) +{ + free(xSemaphore); +} + +#define vPortCPUInitializeMutex(m) mutex_init(m) +#define xSemaphoreTake(s,to) mutex_lock(s) +#define xSemaphoreGive(s) mutex_unlock(s) + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* FREERTOS_SEMPHR_H */ diff --git a/cpu/esp32/include/freertos/task.h b/cpu/esp32/include/freertos/task.h new file mode 100644 index 0000000000000..868fbaa86985b --- /dev/null +++ b/cpu/esp32/include/freertos/task.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + * FreeRTOS to RIOT-OS adaption module for source code compatibility + */ + +#ifndef FREERTOS_TASK_H +#define FREERTOS_TASK_H + +#ifndef DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* FREERTOS_TASK_H */ diff --git a/cpu/esp32/include/freertos/xtensa_api.h b/cpu/esp32/include/freertos/xtensa_api.h new file mode 100644 index 0000000000000..70db1b55bca72 --- /dev/null +++ b/cpu/esp32/include/freertos/xtensa_api.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + * FreeRTOS to RIOT-OS adaption module for source code compatibility + */ + +#ifndef FREERTOS_XTENSA_API_H +#define FREERTOS_XTENSA_API_H + +#include "xtensa/xtensa_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* FREERTOS_XTENSA_API_H */ diff --git a/cpu/esp32/include/gpio_arch.h b/cpu/esp32/include/gpio_arch.h new file mode 100644 index 0000000000000..0a99d953351a6 --- /dev/null +++ b/cpu/esp32/include/gpio_arch.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @ingroup drivers_periph_gpio + * @{ + * + * @file + * @brief Architecture specific GPIO functions ESP32 + * + * @author Gunar Schorcht + * @} + */ + +#ifndef GPIO_ARCH_H +#define GPIO_ARCH_H + +#include "periph/gpio.h" + +#ifndef DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Definition of possible GPIO usage types (for internal use only) + */ +typedef enum +{ + _GPIO = 0, /**< pin used as standard GPIO */ + _ADC, /**< pin used as ADC input */ + _DAC, /**< pin used as DAC output */ + _I2C, /**< pin used as I2C signal */ + _PWM, /**< pin used as PWM output */ + _SPI, /**< pin used as SPI interface */ + _SPIF, /**< pin used as SPI flash interface */ + _UART, /**< pin used as UART interface */ + _NOT_EXIST /**< pin cannot be used at all */ +} _gpio_pin_usage_t; + +/** + * @brief Table of the usage type of each GPIO pin + */ +extern _gpio_pin_usage_t _gpio_pin_usage []; + +/** + * @brief String representation of usage types + */ +extern const char* _gpio_pin_usage_str[]; + +/** + * @brief Table of GPIO to IOMUX register mappings + */ +extern const uint32_t _gpio_to_iomux_reg[]; +#define GPIO_PIN_MUX_REG _gpio_to_iomux_reg + +/** + * @brief Disable the pullup of the pin + */ +void gpio_pullup_dis (gpio_t pin); + +/** + * @brief Returns the RTCIO pin number or -1 if the pin is not an RTCIO pin + */ +int8_t gpio_is_rtcio (gpio_t pin); + +/** + * @brief Configure sleep mode for an GPIO pin if the pin is an RTCIO pin + * @param pin GPIO pin + * @param mode active in sleep mode if true + * @param input as input if true, as output otherwise + * @return 0 success + * @return -1 on invalid pin + */ +int gpio_config_sleep_mode (gpio_t pin, bool sleep_mode, bool input); + +/** + * @brief extern declaratoin of ROM functions to avoid compilation conflicts + */ +void gpio_matrix_in(uint32_t gpio, uint32_t signal_idx, bool inv); +void gpio_matrix_out(uint32_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv); + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* GPIO_ARCH_H */ diff --git a/cpu/esp32/include/irq_arch.h b/cpu/esp32/include/irq_arch.h new file mode 100644 index 0000000000000..4b729162a23b7 --- /dev/null +++ b/cpu/esp32/include/irq_arch.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief Implementation of the kernels irq interface + * + * @author Gunar Schorcht + * + * @} + */ + +#ifndef IRQ_ARCH_H +#define IRQ_ARCH_H + +#include "irq.h" +#include "sched.h" +#include "thread.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Indicates the interrupt nesting depth + * + * The variable is increment on entry into and decremented on exit from an ISR. + */ +extern uint32_t irq_interrupt_nesting; + +/** + * @brief fixed allocated CPU interrupt numbers that are used by RIOT + * @{ + */ +#define CPU_INUM_GPIO 2 /* level interrupt, low priority = 1 */ +#define CPU_INUM_TIMER 3 /* level interrupt, low priority = 1 */ +#define CPU_INUM_UART 5 /* level interrupt, low priority = 1 */ +#define CPU_INUM_I2C 12 /* level interrupt, low priority = 1 */ +#define CPU_INUM_WDT 13 /* level interrupt, low priority = 1 */ +/** @} */ + +#if defined(SDK_INT_HANDLING) || defined(DOXYGEN) +/** + * @brief Macros that have to be used on entry into and reset from an ISR + * + * NOTE: since they use a local variable they can be used only in same function + * @{ + */ +/** Macro that has to be used at the entry point of an ISR */ +#define irq_isr_enter() int _irq_state = irq_disable (); \ + irq_interrupt_nesting++; + +/** Macro that has to be used at the exit point of an ISR */ +#define irq_isr_exit() if (irq_interrupt_nesting) \ + irq_interrupt_nesting--; \ + irq_restore (_irq_state); \ + if (sched_context_switch_request) \ + thread_yield(); + +#else /* SDK_INT_HANDLING */ + +/* in non SDK task handling all the stuff is done in _frxt_int_enter and _frxt_int_exit */ +#define irq_isr_enter() /* int _irq_state = irq_disable (); \ + irq_interrupt_nesting++; */ + +#define irq_isr_exit() /* if (irq_interrupt_nesting) \ + irq_interrupt_nesting--; \ + irq_restore (_irq_state); */ + +#endif /* SDK_INT_HANDLING */ + +/** + * @brief Macros to enter and exit from critical region + * + * NOTE: since they use a local variable they can be used only in same function + * @{ + */ +#define critical_enter() int _irq_state = irq_disable () +#define critical_exit() irq_restore(_irq_state) +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* IRQ_ARCH_H */ diff --git a/cpu/esp32/include/periph_cpu.h b/cpu/esp32/include/periph_cpu.h new file mode 100644 index 0000000000000..6a52a55b28157 --- /dev/null +++ b/cpu/esp32/include/periph_cpu.h @@ -0,0 +1,553 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief CPU specific definitions and functions for peripheral handling + * + * @author Gunar Schorcht + */ + +#ifndef PERIPH_CPU_H +#define PERIPH_CPU_H + +#include + +/* we first include board.h since board-specific configurations can override + the default CPU peripheral configurations and have precedence over them */ +#include "board.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Power management configuration + * @{ + */ +#define PROVIDES_PM_SET_LOWEST +#define PROVIDES_PM_RESTART +#define PROVIDES_PM_OFF +/** @} */ + +/** + * @brief Length of the CPU_ID in octets + */ +#define CPUID_LEN (7U) + +/** + * @brief Available ports on the ESP32 + * @{ + */ +#define PORT_GPIO 0 /**< port GPIO */ +/** @} */ + +/** + * @brief Definition of a fitting UNDEF value + */ +#define GPIO_UNDEF (0xff) + +/** + * @brief Define CPU specific GPIO pin generator macro + */ +#define GPIO_PIN(x, y) ((x << 4) | y) + +/** + * @brief Define CPU specific number of GPIO pins + * @{ + */ +#define GPIO_PIN_NUMOF 40 +#ifndef GPIO_PIN_COUNT +#define GPIO_PIN_COUNT GPIO_PIN_NUMOF +#endif +/** @} */ + +/** + * @name Predefined GPIO names + * @{ + */ +#define GPIO0 (GPIO_PIN(PORT_GPIO,0)) +#define GPIO1 (GPIO_PIN(PORT_GPIO,1)) +#define GPIO2 (GPIO_PIN(PORT_GPIO,2)) +#define GPIO3 (GPIO_PIN(PORT_GPIO,3)) +#define GPIO4 (GPIO_PIN(PORT_GPIO,4)) +#define GPIO5 (GPIO_PIN(PORT_GPIO,5)) +#define GPIO6 (GPIO_PIN(PORT_GPIO,6)) +#define GPIO7 (GPIO_PIN(PORT_GPIO,7)) +#define GPIO8 (GPIO_PIN(PORT_GPIO,8)) +#define GPIO9 (GPIO_PIN(PORT_GPIO,9)) +#define GPIO10 (GPIO_PIN(PORT_GPIO,10)) +#define GPIO11 (GPIO_PIN(PORT_GPIO,11)) +#define GPIO12 (GPIO_PIN(PORT_GPIO,12)) +#define GPIO13 (GPIO_PIN(PORT_GPIO,13)) +#define GPIO14 (GPIO_PIN(PORT_GPIO,14)) +#define GPIO15 (GPIO_PIN(PORT_GPIO,15)) +#define GPIO16 (GPIO_PIN(PORT_GPIO,16)) +#define GPIO17 (GPIO_PIN(PORT_GPIO,17)) +#define GPIO18 (GPIO_PIN(PORT_GPIO,18)) +#define GPIO19 (GPIO_PIN(PORT_GPIO,19)) +/* GPIO 20 is not available */ +#define GPIO21 (GPIO_PIN(PORT_GPIO,21)) +#define GPIO22 (GPIO_PIN(PORT_GPIO,22)) +#define GPIO23 (GPIO_PIN(PORT_GPIO,23)) +/* GPIO 24 is not available */ +#define GPIO25 (GPIO_PIN(PORT_GPIO,25)) +#define GPIO26 (GPIO_PIN(PORT_GPIO,26)) +#define GPIO27 (GPIO_PIN(PORT_GPIO,27)) +/* GPIOs 28 ...32 are not available */ +#define GPIO32 (GPIO_PIN(PORT_GPIO,32)) +#define GPIO33 (GPIO_PIN(PORT_GPIO,33)) +/* GPIOs 34 ... 39 can only be used as inputs and do not have pullups/pulldowns */ +#define GPIO34 (GPIO_PIN(PORT_GPIO,34)) +#define GPIO35 (GPIO_PIN(PORT_GPIO,35)) +#define GPIO36 (GPIO_PIN(PORT_GPIO,36)) +#define GPIO37 (GPIO_PIN(PORT_GPIO,37)) +#define GPIO38 (GPIO_PIN(PORT_GPIO,38)) +#define GPIO39 (GPIO_PIN(PORT_GPIO,39)) +/** @} */ + +/** + * @brief Override mode flank selection values + * + * @{ + */ +#define HAVE_GPIO_FLANK_T +typedef enum { + GPIO_NONE = 0, + GPIO_RISING = 1, /**< emit interrupt on rising flank */ + GPIO_FALLING = 2, /**< emit interrupt on falling flank */ + GPIO_BOTH = 3, /**< emit interrupt on both flanks */ + GPIO_LOW = 4, /**< emit interrupt on low level */ + GPIO_HIGH = 5 /**< emit interrupt on low level */ +} gpio_flank_t; + +/** @} */ + +/** + * @brief Override GPIO modes + * + * @{ + */ +#define HAVE_GPIO_MODE_T +typedef enum { + GPIO_IN, /**< input */ + GPIO_IN_PD, /**< input with pull-down */ + GPIO_IN_PU, /**< input with pull-up */ + GPIO_OUT, /**< output */ + GPIO_OD, /**< open-drain output */ + GPIO_OD_PU, /**< open-drain output with pull-up */ + GPIO_IN_OUT, /**< input and output */ + GPIO_IN_OD, /**< input and open-drain output */ + GPIO_IN_OD_PU /**< input and open-drain output */ +} gpio_mode_t; +/** @} */ + +/** + * @name ADC configuration + * + * ESP32 integrates two 12-bit ADCs (ADC1 and ADC2) capable of measuring up to + * 18 analog signals in total. Most of these ADC channels are either connected + * to a number of intergrated sensors like a Hall sensors, touch sensors and a + * temperature sensor or can be connected with certain GPIOs. Integrated sensors + * are disabled in RIOT's implementation and are not accessible. Thus, up to 18 + * GPIOs, the RTC GPIOs, can be used as ADC inputs: + * + * - ADC1 supports 8 channels: GPIO 32-39 + * - ADC2 supports 10 channels: GPIO 0, 2, 4, 12-15, 25-27 + * + * Please note: GPIO37 and GPIO38 are normally not broken out on ESP32 + * modules and are therefore not usable. + * + * @{ + */ +/** + * @brief Possible ADC resolution settings + */ +#define HAVE_ADC_RES_T +typedef enum { + ADC_RES_9BIT = 0, /**< ADC resolution: 9 bit */ + ADC_RES_10BIT, /**< ADC resolution: 10 bit */ + ADC_RES_11BIT, /**< ADC resolution: 11 bit */ + ADC_RES_12BIT, /**< ADC resolution: 12 bit */ +} adc_res_t; + +/** @} */ + +/** + * @brief Number of ADC cahnnels that could be used at maximum. + */ +#define ADC_NUMOF_MAX 16 + +/** + * @brief Declaration of GPIOs that can be used as ADC channels + * + * ADC_GPIOS declares the GPIOs that could be used as ADC channels by + * the application. The order of the listed GPIOs determines the mapping + * between the RIOT ADC lines and the GPIOs. The definition of ADC_NUMOF is + * derived automatically from ADC_GPIOS and must not be changed. + * + * ADC_GPIOS can be defined in *board.h* to override the default configuration + * with a board-specific definition. + * + * PLEASE NOTE: ADC_GPIOS must not be empty. If no ADC pins are to be used, + * just omit the ADC_GPIOS declaration and declare ADC_GPIOS_NOT_AVAILABLE + * instead. + * + * The following standard declaration of ADC_GPIOS contains all GPIOs that + * might be used as ADC channels. Some of the GPIOs (e.g. GPIO0, GPIO2, + * GPIO15, GPIO33, GPIO36) have special meanings on some boards and cannot + * be used freely. GPIO37 and GPIO38 are normally not broken out on ESP32 + * modules. Override ADC_GPIOS for ADC channels according to your board + * configuration. + * + * PLEASE NOTE: As long as the GPIOs listed in ADC_GPIOS are not initialized + * as ADC channels with the *adc_init* function, they can be used for all + * other uses of GPIOs. + */ +#if !defined(ADC_GPIOS) && !defined(ADC_GPIOS_NOT_AVAILABLE) +#define ADC_GPIOS { GPIO0 , GPIO2 , GPIO4 , GPIO12, GPIO13, GPIO14, \ + GPIO15, GPIO25, GPIO26, GPIO27, GPIO32, GPIO33, \ + GPIO34, GPIO35, GPIO36, GPIO39 } +#endif + +/** Map of RIOT ADC lines to GPIOs */ +#ifdef ADC_GPIOS +static const uint32_t ADC_PINS[] = ADC_GPIOS; +#endif + +/** + * @brief Number of ADC intputs determined from GPIOs declared in ADC_GPIOS + * This define must not be changed. + */ +#ifdef ADC_GPIOS +#define ADC_NUMOF (sizeof(ADC_PINS)/sizeof(uint32_t)) +#else +#define ADC_NUMOF (0) +#endif +/** @} */ + +/** + * @name DAC configuration + * + * ESP32 supports 2 DAC lines at GPIO25 and GPIO26. These DACs have a width of + * 8 bits and produce voltages in the range from 0 V to 3.3 V (VDD_A). The 16 + * bits DAC values given as parameter of function *dac_set* are down-scaled + * to 8 bit. + * + * @{ + */ + +#if !defined(DAC_GPIOS) && !defined(DAC_GPIOS_NOT_AVAILABLE) +/** + * @brief Declaration of GPIOs that can be used as DAC channels + * + * DAC_GPIOS declares the GPIOs that could be used as DAC channels by + * the application. The order of the listed GPIOs determines the mapping + * between the RIOT DAC lines and the GPIOs. The definition of DAC_NUMOF is + * derived automatically from DAC_GPIOs and must not be changed. + * + * DAC_GPIOS can be defined in *board.h* to override the default configuration + * with a board-specific definition. + * + * PLEASE NOTE: DAC_GPIOS must not be empty. If no DAC pins are to be used, + * just omit the DAC_GPIOS declaration and declare DAC_GPIOS_NOT_AVAILABLE. + * + * The following standard declaration of DAC_GPIOS contains all GPIOs that can + * be used as DAC channels. + * + * PLEASE NOTE: As long as the GPIOs listed in DACC_GPIOS are not initialized + * as DAC channels with the *dac_init* function, they can be used for all + * other uses of GPIOs. + */ +#define DAC_GPIOS { GPIO25, GPIO26 } +#endif + +/** Map of RIOT DAC lines to GPIO */ +#ifdef DAC_GPIOS +static const uint32_t DAC_PINS[] = DAC_GPIOS; +#endif + +/** + * @brief Number of DAC intputs determined from GPIOs declared in ADC_GPIOS + * This define must not be changed. + */ +#ifdef DAC_GPIOS +#define DAC_NUMOF (sizeof(DAC_PINS)/sizeof(uint32_t)) +#else +#define DAC_NUMOF (0) +#endif +/** @} */ + +/** + * @name I2C configuration + * + * ESP32 has two built-in I2C interfaces. The configuration determines + * which GPIO pins are used for these interfacese. The default configuration + * below can be overriden in *board.h* with a board-specific definition. If + * a board does not provide I2C interfaces define I2C0_NOT_AVAILABLE and + * I2C1_NOT_AVAILABLE, respectively in board definition. + * @{ + */ +#if !defined(I2C0_NOT_AVAILABLE) && !defined(I2C0_SCL) +#define I2C0_SCL GPIO22 +#define I2C0_SDA GPIO21 +#endif + +/** + * @brief Number of I2C devices determined from IC2 pin declarations. + */ +#if defined(I2C0_SCL) && defined(I2C1_SCL) +#define I2C_NUMOF 2 +#elif defined(I2C0_SCL) || defined(I2C1_SCL) +#define I2C_NUMOF 1 +#else +#define I2C_NUMOF 0 +#endif + +/** @} */ + +/** + * @name PWM configuration + * @{ + * + * PWM implementation uses ESP32's high-speed MCPWM modules. ESP32 has 2 such + * modules, each with up to 6 channels. Thus, the maximum number of PWM + * devices is 2 and the maximum total number of PWM channels is 12. + */ + +/** + * @brief Maximum number of channels per PWM device. + */ +#define PWM_CHANNEL_NUM_DEV_MAX (6) + +/** + * @brief Declaration of GPIOs that can be used as PWM channels. + * + * The defines PWM0_GPIOS and PWM1_GPIOS declare which GPIOs are used as PWM + * channels by PWM_DEV(0) and PWM_DEV(1), respectively. As long as the + * respective PWM device is not initialized with the *pwm_init* function these + * GPIOs can be used for other purposes. + * + * The default configuration below just serves as an example showing the 2 PWM + * devices PWM0 and PWM1. This configuration should be overridden by the board + * definition in file *board.h*. + * + * PLEASE NOTE: The definition of PWM0_GPIOS and PWM1_GPIOS can be omitted or + * empty. In the latter case, they must at least contain the curly braces. + * The corresponding PWM device can not be used in this case. + * + * PLEASE NOTE: As long as a PWM device has not been initialized with the + * pwm_init* function, the GPIOs declared as PWM channels for this device can + * be used for all other uses of GPIOs. + */ +#if !defined(PWM0_GPIOS) && !defined(PWM0_GPIOS_NOT_AVAILABLE) +#define PWM0_GPIOS { GPIO25, GPIO26, GPIO27 } +#endif + +#if !defined(PWM1_GPIOS) && !defined(PWM1_GPIOS_NOT_AVAILABLE) +#define PWM1_GPIOS { GPIO17, GPIO18 } +#endif + +/** + * @brief Number of PWM devices determined from PWM0_GPIOS and PWM1_GPIOS. + */ +#if defined(PWM0_GPIOS) && defined(PWM1_GPIOS) +#define PWM_NUMOF (2) +#elif defined(PWM0_GPIOS) || defined(PWM1_GPIOS) +#define PWM_NUMOF (1) +#else +#define PWM_NUMOF (0) +#endif +/** @} */ + +/** + * @name SPI configuration + * + * ESP32 has four SPI controllers: + * + * - controller SPI0 is reserved for accessing flash memory + * - controller SPI1 realizes interface FSPI which shares its signals with SPI0 + * - controller SPI2 realizes interface HSPI that can be used for peripherals + * - controller SPI3 realizes interface VSPI that can be used for peripherals + * + * Thus, at most three interfaces can be used: + * + * - VSPI: realized by controller SPI3 and mapped to SPI_DEV(0) + * - HSPI: realized by controller SPI2 and mapped to SPI_DEV(1) + * - FSPI: realized by controller SPI1 and mapped to SPI_DEV(2). + * + * PLEASE NOTE: + * - In order to make it clear to the interface SPI_DEV (2) that we are talking + * about the interface that shares its bus signals with the controller for + * access to the flash memory, we use the identifier FSPI here. In the + * technical reference, this interface is misleadingly called simply SPI. + * - Since the FSPI interface SPI_DEV(2) shares its bus signals with flash + * memory interface and other external memories, you can only use SPI_DEV(2) + * to attach external memory with same SPI mode and same bus speed but with + * a different CS. + * - Using SPI_DEV(2) for anything else can disturb flash memory access which + * causes a number of problems. If not really necessary, you should not use + * this interface. + * + * Predefined SPI pin configurations below correspond to their direct I/O + * pin configuration, but can be changed. + * @{ + */ +#define SPI_DEV(x) (x) +#define SPI_NUMOF 3 + +/* pin configuration for SPI_DEV(0), the VSPI interface, can be changed */ +#if defined(SPI0_NOT_AVAILABLE) +#define SPI0_SCK GPIO_UNDEF +#define SPI0_MISO GPIO_UNDEF +#define SPI0_MOSI GPIO_UNDEF +#define SPI0_CS0 GPIO_UNDEF +#elif !defined(SPI0_SCK) && !defined(SPI0_MISO) && !defined(SPI0_MOSI) && !defined(SPI0_CS0) +#define SPI0_SCK GPIO18 /* direct I/O pin VSPICLK */ +#define SPI0_MISO GPIO19 /* direct I/O pin VSPIQ */ +#define SPI0_MOSI GPIO23 /* direct I/O pin VSPID */ +#define SPI0_CS0 GPIO5 /* direct I/O pin VSPICS0 */ +#endif + +/* pin configuration for SPI_DEV(1), the HSPI interface, can be changed */ +#if defined(SPI1_NOT_AVAILABLE) +#define SPI1_SCK GPIO_UNDEF +#define SPI1_MISO GPIO_UNDEF +#define SPI1_MOSI GPIO_UNDEF +#define SPI1_CS0 GPIO_UNDEF +#elif !defined(SPI1_SCK) && !defined(SPI1_MISO) && !defined(SPI1_MOSI) && !defined(SPI1_CS0) +#define SPI1_SCK GPIO14 /* direct I/O pin HSPICLK */ +#define SPI1_MISO GPIO12 /* direct I/O pin HSPIQ */ +#define SPI1_MOSI GPIO13 /* direct I/O pin HSPID */ +#define SPI1_CS0 GPIO15 /* direct I/O pin HSPICS0 */ +#endif + +/* pin configuration for SPI_DEV(2) (the FSPI interface), CANNOT be changed + PLEASE NOTE: Be careful when you use this interface. Please use another CS + signal in your application since GPIO11 is used to select the built-in flash + memory. */ +#if !defined(SPI2_SCK) && !defined(SPI2_MISO) && !defined(SPI2_MOSI) && !defined(SPI2_CS0) +#define SPI2_SCK GPIO6 /* direct I/O pin FSPICLK labeled CLK */ +#define SPI2_MISO GPIO7 /* direct I/O pin FSPIQ labeled DAT0 or SD0 */ +#define SPI2_MOSI GPIO8 /* direct I/O pin FSPID labeled DAT1 or SD1 */ +#define SPI2_CS0 GPIO11 /* direct I/O pin FSPICS0 labeled CMD, MUST be changed */ +#endif + +#define PERIPH_SPI_NEEDS_TRANSFER_BYTE +#define PERIPH_SPI_NEEDS_TRANSFER_REG +#define PERIPH_SPI_NEEDS_TRANSFER_REGS +/** @} */ + +#if defined(MODULE_MTD) || defined(DOXYGEN) +#include "mtd.h" +/** + * @name SPIFFS configuration + * + * Using the MTD implementation with the SPIFFS module, the SPI built-in + * flash memory can be used as the SPIFFS drive. The only configurable part + * in board definitions is the declaration of the system MTD device and its + * name. + * + * @{ + */ + +#ifndef MTD_0 +/** Declare the system MTD device name. */ +#define MTD_0 mtd0 + +/** Pointer to the system MTD device. */ +extern mtd_dev_t *mtd0; +#endif /* MTD_0 */ +/** @} */ +#endif /* defined(MODULE_MTD) || defined(DOXYGEN) */ + +#if defined(MODULE_SPIFFS) && !defined(DOXYGEN) +#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 1 +#define SPIFFS_READ_ONLY 0 +#define SPIFFS_SINGLETON 0 +#define SPIFFS_HAL_CALLBACK_EXTRA 1 +#define SPIFFS_CACHE 1 + +#if SPIFFS_SINGLETON == 1 +#define SPIFFS_CFG_PHYS_SZ(ignore) (0x70000) +#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (4096) +#define SPIFFS_CFG_PHYS_ADDR(ignore) (0) +#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256) +#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (65536) +#endif /* SPIFFS_SINGLETON */ +#endif /* defined(MODULE_SPIFFS) && !defined(DOXYGEN) */ + +#if SPIFFS_HAL_CALLBACK_EXTRA == 0 +/** Declare the SPIFFS MTD device name. */ +#define SPIFFS_MTD_DEV (MTD_0) +#endif + +/** + * @name Timer configuration depenend on which implementation is used + * @{ + */ +#ifdef HW_COUNTER_USED +/** hardware ccount/ccompare registers are used for timer implementation */ +#define TIMER_NUMOF (2) +#define TIMER_CHANNELS (1) +#else +/** hardware timer modules are used for timer implementation (default) */ +#define TIMER_NUMOF (3) +#define TIMER_CHANNELS (1) +#endif + +/** Timer used for system time */ +#define TIMER_SYSTEM TIMERG0.hw_timer[0] + +/** @} */ + +/** + * @name UART cofiguration + * + * ESP32 supports up to three UART devices UART_DEV(0) pin configuration is + * fix. All ESP32 boards use it as standard configuration. + * + * UART_DEV(0).TXD GPIO1 + * UART_DEV(0).RXD GPIO3 + * + * The pin configuration of UART_DEV(1) and UART_DEV(2) can be changed. + * If the according UART interfaces are not to be used, declare + * UART1_NOT_AVAILABLE and/or UART2_NOT_AVAILABLE instead. + * + * @{ + */ + +#if !defined(UART1_NOT_AVAILABLE) && !defined(UART1_TXD) && !defined(UART1_RXD) +#define UART1_TXD GPIO10 /* direct I/O pin for UART_DEV(1), can be changed */ +#define UART1_RXD GPIO9 /* direct I/O pin for UART_DEV(1), can be changed */ +#endif + +#if !defined(UART2_NOT_AVAILABLE) && !defined(UART2_TXD) && !defined(UART2_RXD) +#define UART2_TXD GPIO17 /* direct I/O pin for UART_DEV(2), can be changed */ +#define UART2_RXD GPIO16 /* direct I/O pin for UART_DEV(2), can be changed */ +#endif + +#if !defined(UART1_NOT_AVAILABLE) && !defined(UART2_NOT_AVAILABLE) +/** Number of UART interfaces */ +#define UART_NUMOF 3 +#elif defined(UART1_NOT_AVAILABLE) && defined(UART2_NOT_AVAILABLE) +#define UART_NUMOF 1 +#else +#define UART_NUMOF 2 +#endif +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* PERIPH_CPU_H */ +/** @} */ diff --git a/cpu/esp32/include/sdkconfig.h b/cpu/esp32/include/sdkconfig.h new file mode 100644 index 0000000000000..fafe1a66a9eb6 --- /dev/null +++ b/cpu/esp32/include/sdkconfig.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief Default compiled in configuration + * + * @author Gunar Schorcht + * + */ + +#ifndef SDKCONFIG_H +#define SDKCONFIG_H + +#ifndef DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ +#define CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ 80 +#endif + +#ifndef CONFIG_CONSOLE_UART_NUM +#define CONFIG_CONSOLE_UART_NUM 0 +#endif +#ifndef CONFIG_CONSOLE_UART_BAUDRATE +#define CONFIG_CONSOLE_UART_BAUDRATE 115200 +#endif + +#define CONFIG_BT_ENABLED 0 +#define CONFIG_BT_RESERVE_DRAM 0 +#define CONFIG_TRACEMEM_RESERVE_DRAM 0 +#define CONFIG_ULP_COPROC_RESERVE_MEM 0 + +#define CONFIG_ESP32_PHY_MAX_TX_POWER 20 + +#define CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES 100 +#define CONFIG_ESP32_RTC_CLK_CAL_CYCLES 1024 + +#ifdef SPI_RAM_USED +#define CONFIG_SPIRAM_SUPPORT 1 +#else +#define CONFIG_SPIRAM_SUPPORT 0 +#endif +#define CONFIG_SPIRAM_SPEED_40M 1 +#define CONFIG_SPIRAM_SIZE 4194304 +#define CONFIG_SPIRAM_BOOT_INIT 1 +#define CONFIG_SPIRAM_USE_MALLOC 1 +#define CONFIG_SPIRAM_TYPE_ESPPSRAM32 1 +#define CONFIG_SPIRAM_MEMTEST 1 +#define CONFIG_SPIRAM_CACHE_WORKAROUND 1 +#define CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL 16384 +#define CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL 32768 + +#define CONFIG_ESPTOOLPY_FLASHFREQ_40M 1 +#if defined(FLASH_MODE_QIO) +#define CONFIG_FLASHMODE_QIO 1 +#elif defined(FLASH_MODE_QOUT) +#define CONFIG_FLASHMODE_QOUT 1 +#elif defined(FLASH_MODE_DIO) +#define CONFIG_FLASHMODE_DIO 1 +#else +#define CONFIG_FLASHMODE_DOUT 1 +#endif + +#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 +#define CONFIG_NEWLIB_NANO_FORMAT 1 +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* SDKCONFIG_H */ diff --git a/cpu/esp32/include/stdio.h b/cpu/esp32/include/stdio.h new file mode 100644 index 0000000000000..4fc9320acf48b --- /dev/null +++ b/cpu/esp32/include/stdio.h @@ -0,0 +1,748 @@ +/** + * This file is a modification of the original file to overwrite the *putchar* + * and *getchar* macros in the case the *uart_stdio* module is used. If the + * *uart_stdio* module is used, *putchar* and *getchar* are redirections to + * according *uart_stdio_* functions. + */ + +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#)stdio.h 5.3 (Berkeley) 3/15/86 + */ + +/* + * NB: to fit things in six character monocase externals, the + * stdio code uses the prefix `__s' for stdio objects, typically + * followed by a three-character attempt at a mnemonic. + */ + +#ifndef STDIO_H +#define STDIO_H + +#ifndef DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +#include "_ansi.h" + +#define _FSTDIO /* ``function stdio'' */ + +#define __need_size_t +#define __need_NULL +#include +#include + +#define __need___va_list +#include + +/* + * defines __FILE, _fpos_t. + * They must be defined there because struct _reent needs them (and we don't + * want reent.h to include this file. + */ + +#include +#include + +_BEGIN_STD_C + +typedef __FILE FILE; + +#ifdef __CYGWIN__ +typedef _fpos64_t fpos_t; +#else +typedef _fpos_t fpos_t; +#ifdef __LARGE64_FILES +typedef _fpos64_t fpos64_t; +#endif +#endif /* !__CYGWIN__ */ + +#include + +#define __SLBF 0x0001 /* line buffered */ +#define __SNBF 0x0002 /* unbuffered */ +#define __SRD 0x0004 /* OK to read */ +#define __SWR 0x0008 /* OK to write */ + /* RD and WR are never simultaneously asserted */ +#define __SRW 0x0010 /* open for reading & writing */ +#define __SEOF 0x0020 /* found EOF */ +#define __SERR 0x0040 /* found error */ +#define __SMBF 0x0080 /* _buf is from malloc */ +#define __SAPP 0x0100 /* fdopen()ed in append mode - so must write to end */ +#define __SSTR 0x0200 /* this is an sprintf/snprintf string */ +#define __SOPT 0x0400 /* do fseek() optimisation */ +#define __SNPT 0x0800 /* do not do fseek() optimisation */ +#define __SOFF 0x1000 /* set iff _offset is in fact correct */ +#define __SORD 0x2000 /* true => stream orientation (byte/wide) decided */ +#if defined(__CYGWIN__) +#define __SCLE 0x4000 /* convert line endings CR/LF <-> NL */ +#endif +#define __SL64 0x8000 /* is 64-bit offset large file */ + +/* _flags2 flags */ +#define __SNLK 0x0001 /* stdio functions do not lock streams themselves */ +#define __SWID 0x2000 /* true => stream orientation wide, false => byte, only valid if __SORD in _flags is true */ + +/* + * The following three definitions are for ANSI C, which took them + * from System V, which stupidly took internal interface macros and + * made them official arguments to setvbuf(), without renaming them. + * Hence, these ugly _IOxxx names are *supposed* to appear in user code. + * + * Although these happen to match their counterparts above, the + * implementation does not rely on that (so these could be renumbered). + */ +#define _IOFBF 0 /* setvbuf should set fully buffered */ +#define _IOLBF 1 /* setvbuf should set line buffered */ +#define _IONBF 2 /* setvbuf should set unbuffered */ + +#define EOF (-1) + +#ifdef __BUFSIZ__ +#define BUFSIZ __BUFSIZ__ +#else +#define BUFSIZ 1024 +#endif + +#ifdef __FOPEN_MAX__ +#define FOPEN_MAX __FOPEN_MAX__ +#else +#define FOPEN_MAX 20 +#endif + +#ifdef __FILENAME_MAX__ +#define FILENAME_MAX __FILENAME_MAX__ +#else +#define FILENAME_MAX 1024 +#endif + +#ifdef __L_tmpnam__ +#define L_tmpnam __L_tmpnam__ +#else +#define L_tmpnam FILENAME_MAX +#endif + +#ifndef __STRICT_ANSI__ +#define P_tmpdir "/tmp" +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 /* set file offset to offset */ +#endif +#ifndef SEEK_CUR +#define SEEK_CUR 1 /* set file offset to current plus offset */ +#endif +#ifndef SEEK_END +#define SEEK_END 2 /* set file offset to EOF plus offset */ +#endif + +#define TMP_MAX 26 + +#define stdin (_REENT->_stdin) +#define stdout (_REENT->_stdout) +#define stderr (_REENT->_stderr) + +#define _stdin_r(x) ((x)->_stdin) +#define _stdout_r(x) ((x)->_stdout) +#define _stderr_r(x) ((x)->_stderr) + +/* + * Functions defined in ANSI C standard. + */ + +#ifndef __VALIST +#ifdef __GNUC__ +#define __VALIST __gnuc_va_list +#else +#define __VALIST char* +#endif +#endif + +FILE * _EXFUN(tmpfile, (void)); +char * _EXFUN(tmpnam, (char *)); +#if __BSD_VISIBLE || __XSI_VISIBLE || __POSIX_VISIBLE >= 200112 +char * _EXFUN(tempnam, (const char *, const char *)); +#endif +int _EXFUN(fclose, (FILE *)); +int _EXFUN(fflush, (FILE *)); +FILE * _EXFUN(freopen, (const char *__restrict, const char *__restrict, FILE *__restrict)); +void _EXFUN(setbuf, (FILE *__restrict, char *__restrict)); +int _EXFUN(setvbuf, (FILE *__restrict, char *__restrict, int, size_t)); +int _EXFUN(fprintf, (FILE *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +int _EXFUN(fscanf, (FILE *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__scanf__, 2, 3)))); +int _EXFUN(printf, (const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 1, 2)))); +int _EXFUN(scanf, (const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__scanf__, 1, 2)))); +int _EXFUN(sscanf, (const char *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__scanf__, 2, 3)))); +int _EXFUN(vfprintf, (FILE *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(vprintf, (const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 1, 0)))); +int _EXFUN(vsprintf, (char *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(fgetc, (FILE *)); +char * _EXFUN(fgets, (char *__restrict, int, FILE *__restrict)); +int _EXFUN(fputc, (int, FILE *)); +int _EXFUN(fputs, (const char *__restrict, FILE *__restrict)); +int _EXFUN(getc, (FILE *)); +int _EXFUN(getchar, (void)); +char * _EXFUN(gets, (char *)); +int _EXFUN(putc, (int, FILE *)); +int _EXFUN(putchar, (int)); +int _EXFUN(puts, (const char *)); +int _EXFUN(ungetc, (int, FILE *)); +size_t _EXFUN(fread, (_PTR __restrict, size_t _size, size_t _n, FILE *__restrict)); +size_t _EXFUN(fwrite, (const _PTR __restrict , size_t _size, size_t _n, FILE *)); +#ifdef _COMPILING_NEWLIB +int _EXFUN(fgetpos, (FILE *, _fpos_t *)); +#else +int _EXFUN(fgetpos, (FILE *__restrict, fpos_t *__restrict)); +#endif +int _EXFUN(fseek, (FILE *, long, int)); +#ifdef _COMPILING_NEWLIB +int _EXFUN(fsetpos, (FILE *, const _fpos_t *)); +#else +int _EXFUN(fsetpos, (FILE *, const fpos_t *)); +#endif +long _EXFUN(ftell, ( FILE *)); +void _EXFUN(rewind, (FILE *)); +void _EXFUN(clearerr, (FILE *)); +int _EXFUN(feof, (FILE *)); +int _EXFUN(ferror, (FILE *)); +void _EXFUN(perror, (const char *)); +#ifndef _REENT_ONLY +FILE * _EXFUN(fopen, (const char *__restrict _name, const char *__restrict _type)); +int _EXFUN(sprintf, (char *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +int _EXFUN(remove, (const char *)); +int _EXFUN(rename, (const char *, const char *)); +#ifdef _COMPILING_NEWLIB +int _EXFUN(_rename, (const char *, const char *)); +#endif +#endif +#if !defined(__STRICT_ANSI__) || defined(__USE_XOPEN2K) +#ifdef _COMPILING_NEWLIB +int _EXFUN(fseeko, (FILE *, _off_t, int)); +_off_t _EXFUN(ftello, ( FILE *)); +#else +int _EXFUN(fseeko, (FILE *, off_t, int)); +off_t _EXFUN(ftello, ( FILE *)); +#endif +#endif +#if __GNU_VISIBLE +int _EXFUN(fcloseall, (_VOID)); +#endif +#if !defined(__STRICT_ANSI__) || (__STDC_VERSION__ >= 199901L) || (__cplusplus >= 201103L) +#ifndef _REENT_ONLY +int _EXFUN(asiprintf, (char **, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +char * _EXFUN(asniprintf, (char *, size_t *, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +char * _EXFUN(asnprintf, (char *__restrict, size_t *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(asprintf, (char **__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +#ifndef diprintf +int _EXFUN(diprintf, (int, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +#endif +int _EXFUN(fiprintf, (FILE *, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +int _EXFUN(fiscanf, (FILE *, const char *, ...) + _ATTRIBUTE ((__format__ (__scanf__, 2, 3)))); +int _EXFUN(iprintf, (const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 1, 2)))); +int _EXFUN(iscanf, (const char *, ...) + _ATTRIBUTE ((__format__ (__scanf__, 1, 2)))); +int _EXFUN(siprintf, (char *, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +int _EXFUN(siscanf, (const char *, const char *, ...) + _ATTRIBUTE ((__format__ (__scanf__, 2, 3)))); +int _EXFUN(snprintf, (char *__restrict, size_t, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(sniprintf, (char *, size_t, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(vasiprintf, (char **, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +char * _EXFUN(vasniprintf, (char *, size_t *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +char * _EXFUN(vasnprintf, (char *, size_t *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(vasprintf, (char **, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(vdiprintf, (int, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(vfiprintf, (FILE *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(vfiscanf, (FILE *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 2, 0)))); +int _EXFUN(vfscanf, (FILE *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 2, 0)))); +int _EXFUN(viprintf, (const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 1, 0)))); +int _EXFUN(viscanf, (const char *, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 1, 0)))); +int _EXFUN(vscanf, (const char *, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 1, 0)))); +int _EXFUN(vsiprintf, (char *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(vsiscanf, (const char *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 2, 0)))); +int _EXFUN(vsniprintf, (char *, size_t, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(vsnprintf, (char *__restrict, size_t, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(vsscanf, (const char *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 2, 0)))); +#endif /* !_REENT_ONLY */ +#endif /* !__STRICT_ANSI__ */ + +/* + * Routines in POSIX 1003.1:2001. + */ + +#ifndef __STRICT_ANSI__ +#ifndef _REENT_ONLY +FILE * _EXFUN(fdopen, (int, const char *)); +#endif +int _EXFUN(fileno, (FILE *)); +int _EXFUN(getw, (FILE *)); +int _EXFUN(pclose, (FILE *)); +FILE * _EXFUN(popen, (const char *, const char *)); +int _EXFUN(putw, (int, FILE *)); +void _EXFUN(setbuffer, (FILE *, char *, int)); +int _EXFUN(setlinebuf, (FILE *)); +int _EXFUN(getc_unlocked, (FILE *)); +int _EXFUN(getchar_unlocked, (void)); +void _EXFUN(flockfile, (FILE *)); +int _EXFUN(ftrylockfile, (FILE *)); +void _EXFUN(funlockfile, (FILE *)); +int _EXFUN(putc_unlocked, (int, FILE *)); +int _EXFUN(putchar_unlocked, (int)); +#endif /* ! __STRICT_ANSI__ */ + +/* + * Routines in POSIX 1003.1:200x. + */ + +#ifndef __STRICT_ANSI__ +# ifndef _REENT_ONLY +# ifndef dprintf +int _EXFUN(dprintf, (int, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +# endif +FILE * _EXFUN(fmemopen, (void *__restrict, size_t, const char *__restrict)); +/* getdelim - see __getdelim for now */ +/* getline - see __getline for now */ +FILE * _EXFUN(open_memstream, (char **, size_t *)); +#if __BSD_VISIBLE || __POSIX_VISIBLE >= 200809 +int _EXFUN(renameat, (int, const char *, int, const char *)); +#endif +int _EXFUN(vdprintf, (int, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +# endif +#endif + +/* + * Recursive versions of the above. + */ + +int _EXFUN(_asiprintf_r, (struct _reent *, char **, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +char * _EXFUN(_asniprintf_r, (struct _reent *, char *, size_t *, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 4, 5)))); +char * _EXFUN(_asnprintf_r, (struct _reent *, char *__restrict, size_t *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 4, 5)))); +int _EXFUN(_asprintf_r, (struct _reent *, char **__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(_diprintf_r, (struct _reent *, int, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(_dprintf_r, (struct _reent *, int, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(_fclose_r, (struct _reent *, FILE *)); +int _EXFUN(_fcloseall_r, (struct _reent *)); +FILE * _EXFUN(_fdopen_r, (struct _reent *, int, const char *)); +int _EXFUN(_fflush_r, (struct _reent *, FILE *)); +int _EXFUN(_fgetc_r, (struct _reent *, FILE *)); +int _EXFUN(_fgetc_unlocked_r, (struct _reent *, FILE *)); +char * _EXFUN(_fgets_r, (struct _reent *, char *__restrict, int, FILE *__restrict)); +char * _EXFUN(_fgets_unlocked_r, (struct _reent *, char *__restrict, int, FILE *__restrict)); +#ifdef _COMPILING_NEWLIB +int _EXFUN(_fgetpos_r, (struct _reent *, FILE *__restrict, _fpos_t *__restrict)); +int _EXFUN(_fsetpos_r, (struct _reent *, FILE *, const _fpos_t *)); +#else +int _EXFUN(_fgetpos_r, (struct _reent *, FILE *, fpos_t *)); +int _EXFUN(_fsetpos_r, (struct _reent *, FILE *, const fpos_t *)); +#endif +int _EXFUN(_fiprintf_r, (struct _reent *, FILE *, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(_fiscanf_r, (struct _reent *, FILE *, const char *, ...) + _ATTRIBUTE ((__format__ (__scanf__, 3, 4)))); +FILE * _EXFUN(_fmemopen_r, (struct _reent *, void *__restrict, size_t, const char *__restrict)); +FILE * _EXFUN(_fopen_r, (struct _reent *, const char *__restrict, const char *__restrict)); +FILE * _EXFUN(_freopen_r, (struct _reent *, const char *__restrict, const char *__restrict, FILE *__restrict)); +int _EXFUN(_fprintf_r, (struct _reent *, FILE *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(_fpurge_r, (struct _reent *, FILE *)); +int _EXFUN(_fputc_r, (struct _reent *, int, FILE *)); +int _EXFUN(_fputc_unlocked_r, (struct _reent *, int, FILE *)); +int _EXFUN(_fputs_r, (struct _reent *, const char *__restrict, FILE *__restrict)); +int _EXFUN(_fputs_unlocked_r, (struct _reent *, const char *__restrict, FILE *__restrict)); +size_t _EXFUN(_fread_r, (struct _reent *, _PTR __restrict, size_t _size, size_t _n, FILE *__restrict)); +size_t _EXFUN(_fread_unlocked_r, (struct _reent *, _PTR __restrict, size_t _size, size_t _n, FILE *__restrict)); +int _EXFUN(_fscanf_r, (struct _reent *, FILE *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__scanf__, 3, 4)))); +int _EXFUN(_fseek_r, (struct _reent *, FILE *, long, int)); +int _EXFUN(_fseeko_r,(struct _reent *, FILE *, _off_t, int)); +long _EXFUN(_ftell_r, (struct _reent *, FILE *)); +_off_t _EXFUN(_ftello_r,(struct _reent *, FILE *)); +void _EXFUN(_rewind_r, (struct _reent *, FILE *)); +size_t _EXFUN(_fwrite_r, (struct _reent *, const _PTR __restrict, size_t _size, size_t _n, FILE *__restrict)); +size_t _EXFUN(_fwrite_unlocked_r, (struct _reent *, const _PTR __restrict, size_t _size, size_t _n, FILE *__restrict)); +int _EXFUN(_getc_r, (struct _reent *, FILE *)); +int _EXFUN(_getc_unlocked_r, (struct _reent *, FILE *)); +int _EXFUN(_getchar_r, (struct _reent *)); +int _EXFUN(_getchar_unlocked_r, (struct _reent *)); +char * _EXFUN(_gets_r, (struct _reent *, char *)); +int _EXFUN(_iprintf_r, (struct _reent *, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +int _EXFUN(_iscanf_r, (struct _reent *, const char *, ...) + _ATTRIBUTE ((__format__ (__scanf__, 2, 3)))); +FILE * _EXFUN(_open_memstream_r, (struct _reent *, char **, size_t *)); +void _EXFUN(_perror_r, (struct _reent *, const char *)); +int _EXFUN(_printf_r, (struct _reent *, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 2, 3)))); +int _EXFUN(_putc_r, (struct _reent *, int, FILE *)); +int _EXFUN(_putc_unlocked_r, (struct _reent *, int, FILE *)); +int _EXFUN(_putchar_unlocked_r, (struct _reent *, int)); +int _EXFUN(_putchar_r, (struct _reent *, int)); +int _EXFUN(_puts_r, (struct _reent *, const char *)); +int _EXFUN(_remove_r, (struct _reent *, const char *)); +int _EXFUN(_rename_r, (struct _reent *, + const char *_old, const char *_new)); +int _EXFUN(_scanf_r, (struct _reent *, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__scanf__, 2, 3)))); +int _EXFUN(_siprintf_r, (struct _reent *, char *, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(_siscanf_r, (struct _reent *, const char *, const char *, ...) + _ATTRIBUTE ((__format__ (__scanf__, 3, 4)))); +int _EXFUN(_sniprintf_r, (struct _reent *, char *, size_t, const char *, ...) + _ATTRIBUTE ((__format__ (__printf__, 4, 5)))); +int _EXFUN(_snprintf_r, (struct _reent *, char *__restrict, size_t, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 4, 5)))); +int _EXFUN(_sprintf_r, (struct _reent *, char *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__printf__, 3, 4)))); +int _EXFUN(_sscanf_r, (struct _reent *, const char *__restrict, const char *__restrict, ...) + _ATTRIBUTE ((__format__ (__scanf__, 3, 4)))); +char * _EXFUN(_tempnam_r, (struct _reent *, const char *, const char *)); +FILE * _EXFUN(_tmpfile_r, (struct _reent *)); +char * _EXFUN(_tmpnam_r, (struct _reent *, char *)); +int _EXFUN(_ungetc_r, (struct _reent *, int, FILE *)); +int _EXFUN(_vasiprintf_r, (struct _reent *, char **, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +char * _EXFUN(_vasniprintf_r, (struct _reent*, char *, size_t *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 4, 0)))); +char * _EXFUN(_vasnprintf_r, (struct _reent*, char *, size_t *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 4, 0)))); +int _EXFUN(_vasprintf_r, (struct _reent *, char **, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(_vdiprintf_r, (struct _reent *, int, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(_vdprintf_r, (struct _reent *, int, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(_vfiprintf_r, (struct _reent *, FILE *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(_vfiscanf_r, (struct _reent *, FILE *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 3, 0)))); +int _EXFUN(_vfprintf_r, (struct _reent *, FILE *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(_vfscanf_r, (struct _reent *, FILE *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 3, 0)))); +int _EXFUN(_viprintf_r, (struct _reent *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(_viscanf_r, (struct _reent *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 2, 0)))); +int _EXFUN(_vprintf_r, (struct _reent *, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 2, 0)))); +int _EXFUN(_vscanf_r, (struct _reent *, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 2, 0)))); +int _EXFUN(_vsiprintf_r, (struct _reent *, char *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(_vsiscanf_r, (struct _reent *, const char *, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 3, 0)))); +int _EXFUN(_vsniprintf_r, (struct _reent *, char *, size_t, const char *, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 4, 0)))); +int _EXFUN(_vsnprintf_r, (struct _reent *, char *__restrict, size_t, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 4, 0)))); +int _EXFUN(_vsprintf_r, (struct _reent *, char *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__printf__, 3, 0)))); +int _EXFUN(_vsscanf_r, (struct _reent *, const char *__restrict, const char *__restrict, __VALIST) + _ATTRIBUTE ((__format__ (__scanf__, 3, 0)))); + +/* Other extensions. */ + +int _EXFUN(fpurge, (FILE *)); +ssize_t _EXFUN(__getdelim, (char **, size_t *, int, FILE *)); +ssize_t _EXFUN(__getline, (char **, size_t *, FILE *)); + +#if __BSD_VISIBLE +void _EXFUN(clearerr_unlocked, (FILE *)); +int _EXFUN(feof_unlocked, (FILE *)); +int _EXFUN(ferror_unlocked, (FILE *)); +int _EXFUN(fileno_unlocked, (FILE *)); +int _EXFUN(fflush_unlocked, (FILE *)); +int _EXFUN(fgetc_unlocked, (FILE *)); +int _EXFUN(fputc_unlocked, (int, FILE *)); +size_t _EXFUN(fread_unlocked, (_PTR __restrict, size_t _size, size_t _n, FILE *__restrict)); +size_t _EXFUN(fwrite_unlocked, (const _PTR __restrict , size_t _size, size_t _n, FILE *)); +#endif + +#if __GNU_VISIBLE +char * _EXFUN(fgets_unlocked, (char *__restrict, int, FILE *__restrict)); +int _EXFUN(fputs_unlocked, (const char *__restrict, FILE *__restrict)); +#endif + +#ifdef __LARGE64_FILES +#if !defined(__CYGWIN__) || defined(_COMPILING_NEWLIB) +FILE * _EXFUN(fdopen64, (int, const char *)); +FILE * _EXFUN(fopen64, (const char *, const char *)); +FILE * _EXFUN(freopen64, (_CONST char *, _CONST char *, FILE *)); +_off64_t _EXFUN(ftello64, (FILE *)); +_off64_t _EXFUN(fseeko64, (FILE *, _off64_t, int)); +int _EXFUN(fgetpos64, (FILE *, _fpos64_t *)); +int _EXFUN(fsetpos64, (FILE *, const _fpos64_t *)); +FILE * _EXFUN(tmpfile64, (void)); + +FILE * _EXFUN(_fdopen64_r, (struct _reent *, int, const char *)); +FILE * _EXFUN(_fopen64_r, (struct _reent *,const char *, const char *)); +FILE * _EXFUN(_freopen64_r, (struct _reent *, _CONST char *, _CONST char *, FILE *)); +_off64_t _EXFUN(_ftello64_r, (struct _reent *, FILE *)); +_off64_t _EXFUN(_fseeko64_r, (struct _reent *, FILE *, _off64_t, int)); +int _EXFUN(_fgetpos64_r, (struct _reent *, FILE *, _fpos64_t *)); +int _EXFUN(_fsetpos64_r, (struct _reent *, FILE *, const _fpos64_t *)); +FILE * _EXFUN(_tmpfile64_r, (struct _reent *)); +#endif /* !__CYGWIN__ */ +#endif /* __LARGE64_FILES */ + +/* + * Routines internal to the implementation. + */ + +int _EXFUN(__srget_r, (struct _reent *, FILE *)); +int _EXFUN(__swbuf_r, (struct _reent *, int, FILE *)); + +/* + * Stdio function-access interface. + */ + +#ifndef __STRICT_ANSI__ +# ifdef __LARGE64_FILES +FILE *_EXFUN(funopen,(const _PTR __cookie, + int (*__readfn)(_PTR __c, char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + int (*__writefn)(_PTR __c, const char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + _fpos64_t (*__seekfn)(_PTR __c, _fpos64_t __off, int __whence), + int (*__closefn)(_PTR __c))); +FILE *_EXFUN(_funopen_r,(struct _reent *, const _PTR __cookie, + int (*__readfn)(_PTR __c, char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + int (*__writefn)(_PTR __c, const char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + _fpos64_t (*__seekfn)(_PTR __c, _fpos64_t __off, int __whence), + int (*__closefn)(_PTR __c))); +# else +FILE *_EXFUN(funopen,(const _PTR __cookie, + int (*__readfn)(_PTR __cookie, char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + int (*__writefn)(_PTR __cookie, const char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + fpos_t (*__seekfn)(_PTR __cookie, fpos_t __off, int __whence), + int (*__closefn)(_PTR __cookie))); +FILE *_EXFUN(_funopen_r,(struct _reent *, const _PTR __cookie, + int (*__readfn)(_PTR __cookie, char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + int (*__writefn)(_PTR __cookie, const char *__buf, + _READ_WRITE_BUFSIZE_TYPE __n), + fpos_t (*__seekfn)(_PTR __cookie, fpos_t __off, int __whence), + int (*__closefn)(_PTR __cookie))); +# endif /* !__LARGE64_FILES */ + +# define fropen(__cookie, __fn) funopen(__cookie, __fn, (int (*)())0, \ + (fpos_t (*)())0, (int (*)())0) +# define fwopen(__cookie, __fn) funopen(__cookie, (int (*)())0, __fn, \ + (fpos_t (*)())0, (int (*)())0) + +typedef ssize_t cookie_read_function_t(void *__cookie, char *__buf, size_t __n); +typedef ssize_t cookie_write_function_t(void *__cookie, const char *__buf, + size_t __n); +# ifdef __LARGE64_FILES +typedef int cookie_seek_function_t(void *__cookie, _off64_t *__off, + int __whence); +# else +typedef int cookie_seek_function_t(void *__cookie, off_t *__off, int __whence); +# endif /* !__LARGE64_FILES */ +typedef int cookie_close_function_t(void *__cookie); +typedef struct +{ + /* These four struct member names are dictated by Linux; hopefully, + they don't conflict with any macros. */ + cookie_read_function_t *read; + cookie_write_function_t *write; + cookie_seek_function_t *seek; + cookie_close_function_t *close; +} cookie_io_functions_t; +FILE *_EXFUN(fopencookie,(void *__cookie, + const char *__mode, cookie_io_functions_t __functions)); +FILE *_EXFUN(_fopencookie_r,(struct _reent *, void *__cookie, + const char *__mode, cookie_io_functions_t __functions)); +#endif /* ! __STRICT_ANSI__ */ + +#ifndef __CUSTOM_FILE_IO__ +/* + * The __sfoo macros are here so that we can + * define function versions in the C library. + */ +#define __sgetc_raw_r(__ptr, __f) (--(__f)->_r < 0 ? __srget_r(__ptr, __f) : (int)(*(__f)->_p++)) + +#ifdef __SCLE +/* For a platform with CR/LF, additional logic is required by + __sgetc_r which would otherwise simply be a macro; therefore we + use an inlined function. The function is only meant to be inlined + in place as used and the function body should never be emitted. + + There are two possible means to this end when compiling with GCC, + one when compiling with a standard C99 compiler, and for other + compilers we're just stuck. At the moment, this issue only + affects the Cygwin target, so we'll most likely be using GCC. */ + +_ELIDABLE_INLINE int __sgetc_r(struct _reent *__ptr, FILE *__p); + +_ELIDABLE_INLINE int __sgetc_r(struct _reent *__ptr, FILE *__p) + { + int __c = __sgetc_raw_r(__ptr, __p); + if ((__p->_flags & __SCLE) && (__c == '\r')) + { + int __c2 = __sgetc_raw_r(__ptr, __p); + if (__c2 == '\n') + __c = __c2; + else + ungetc(__c2, __p); + } + return __c; + } +#else +#define __sgetc_r(__ptr, __p) __sgetc_raw_r(__ptr, __p) +#endif + +#ifdef _never /* __GNUC__ */ +/* If this inline is actually used, then systems using coff debugging + info get hopelessly confused. 21sept93 rich@cygnus.com. */ +_ELIDABLE_INLINE int __sputc_r(struct _reent *_ptr, int _c, FILE *_p) { + if (--_p->_w >= 0 || (_p->_w >= _p->_lbfsize && (char)_c != '\n')) + return (*_p->_p++ = _c); + else + return (__swbuf_r(_ptr, _c, _p)); +} +#else +/* + * This has been tuned to generate reasonable code on the vax using pcc + */ +#define __sputc_raw_r(__ptr, __c, __p) \ + (--(__p)->_w < 0 ? \ + (__p)->_w >= (__p)->_lbfsize ? \ + (*(__p)->_p = (__c)), *(__p)->_p != '\n' ? \ + (int)*(__p)->_p++ : \ + __swbuf_r(__ptr, '\n', __p) : \ + __swbuf_r(__ptr, (int)(__c), __p) : \ + (*(__p)->_p = (__c), (int)*(__p)->_p++)) +#ifdef __SCLE +#define __sputc_r(__ptr, __c, __p) \ + ((((__p)->_flags & __SCLE) && ((__c) == '\n')) \ + ? __sputc_raw_r(__ptr, '\r', (__p)) : 0 , \ + __sputc_raw_r((__ptr), (__c), (__p))) +#else +#define __sputc_r(__ptr, __c, __p) __sputc_raw_r(__ptr, __c, __p) +#endif +#endif + +#define __sfeof(p) ((int)(((p)->_flags & __SEOF) != 0)) +#define __sferror(p) ((int)(((p)->_flags & __SERR) != 0)) +#define __sclearerr(p) ((void)((p)->_flags &= ~(__SERR|__SEOF))) +#define __sfileno(p) ((p)->_file) + +#ifndef _REENT_SMALL +#define feof(p) __sfeof(p) +#define ferror(p) __sferror(p) +#define clearerr(p) __sclearerr(p) + +#if __BSD_VISIBLE +#define feof_unlocked(p) __sfeof(p) +#define ferror_unlocked(p) __sferror(p) +#define clearerr_unlocked(p) __sclearerr(p) +#endif /* __BSD_VISIBLE */ +#endif /* _REENT_SMALL */ + +#if 0 /*ndef __STRICT_ANSI__ - FIXME: must initialize stdio first, use fn */ +#define fileno(p) __sfileno(p) +#endif + +#ifndef __CYGWIN__ +#ifndef lint +#define getc(fp) __sgetc_r(_REENT, fp) +#define putc(x, fp) __sputc_r(_REENT, x, fp) +#endif /* lint */ +#endif /* __CYGWIN__ */ + +#ifndef __STRICT_ANSI__ +/* fast always-buffered version, true iff error */ +#define fast_putc(x,p) (--(p)->_w < 0 ? \ + __swbuf_r(_REENT, (int)(x), p) == EOF : (*(p)->_p = (x), (p)->_p++, 0)) + +#define L_cuserid 9 /* posix says it goes in stdio.h :( */ +#ifdef __CYGWIN__ +#define L_ctermid 16 +#endif +#endif + +#endif /* !__CUSTOM_FILE_IO__ */ + +#define getchar() getc(stdin) +#define putchar(x) putc(x, stdout) + +#ifndef __STRICT_ANSI__ +#define getchar_unlocked() getc_unlocked(stdin) +#define putchar_unlocked(x) putc_unlocked(x, stdout) +#endif + +_END_STD_C + +#undef putchar +#undef getchar + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* STDIO_H */ diff --git a/cpu/esp32/include/sys/types.h b/cpu/esp32/include/sys/types.h new file mode 100644 index 0000000000000..385c16aa1d55f --- /dev/null +++ b/cpu/esp32/include/sys/types.h @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief This file is a modification of original sys/types.h + * + * @author Gunar Schorcht + * + * This file is just a wrapper around sys/types.h to fix missing types + * fsblkcnt_t and fsfilcnt_t needed in statvfs.h + */ + + +/* unified sys/types.h: + start with sef's sysvi386 version. + merge go32 version -- a few ifdefs. + h8300hms, h8300xray, and sysvnecv70 disagree on the following types: + + typedef int gid_t; + typedef int uid_t; + typedef int dev_t; + typedef int ino_t; + typedef int mode_t; + typedef int caddr_t; + + however, these aren't "reasonable" values, the sysvi386 ones make far + more sense, and should work sufficiently well (in particular, h8300 + doesn't have a stat, and the necv70 doesn't matter.) -- eichin + */ + +#ifndef SYS_TYPES_H +#define SYS_TYPES_H + +#ifndef DOXYGEN + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _FSBLKCNT_T_DECLARED +#include +typedef uint32_t fsblkcnt_t; +typedef uint32_t fsfilcnt_t; +#define _FSBLKCNT_T_DECLARED +#endif + +#ifndef _SYS_TYPES_H + +#include <_ansi.h> + +#ifndef __INTTYPES_DEFINED__ +#define __INTTYPES_DEFINED__ + +#include + +#if defined(__rtems__) || defined(__XMK__) +/* + * The following section is RTEMS specific and is needed to more + * closely match the types defined in the BSD sys/types.h. + * This is needed to let the RTEMS/BSD TCP/IP stack compile. + */ + +/* deprecated */ +#if ___int8_t_defined +typedef __uint8_t u_int8_t; +#endif +#if ___int16_t_defined +typedef __uint16_t u_int16_t; +#endif +#if ___int32_t_defined +typedef __uint32_t u_int32_t; +#endif + +#if ___int64_t_defined +typedef __uint64_t u_int64_t; + +/* deprecated */ +typedef __uint64_t u_quad_t; +typedef __int64_t quad_t; +typedef quad_t * qaddr_t; +#endif + +#endif + +#endif /* ! __INTTYPES_DEFINED */ + +#ifndef __need_inttypes + +#define _SYS_TYPES_H +#include + +#ifdef __i386__ +#if defined (GO32) || defined (__MSDOS__) +#define __MS_types__ +#endif +#endif + +# include +# include + +/* To ensure the stat struct's layout doesn't change when sizeof(int), etc. + changes, we assume sizeof short and long never change and have all types + used to define struct stat use them and not int where possible. + Where not possible, _ST_INTxx are used. It would be preferable to not have + such assumptions, but until the extra fluff is necessary, it's avoided. + No 64 bit targets use stat yet. What to do about them is postponed + until necessary. */ +#ifdef __GNUC__ +#define _ST_INT32 __attribute__ ((__mode__ (__SI__))) +#else +#define _ST_INT32 +#endif + +# ifndef _POSIX_SOURCE + +# define physadr physadr_t +# define quad quad_t + +#ifndef _BSDTYPES_DEFINED +/* also defined in mingw/gmon.h and in w32api/winsock[2].h */ +#ifndef __u_char_defined +typedef unsigned char u_char; +#define __u_char_defined +#endif +#ifndef __u_short_defined +typedef unsigned short u_short; +#define __u_short_defined +#endif +#ifndef __u_int_defined +typedef unsigned int u_int; +#define __u_int_defined +#endif +#ifndef __u_long_defined +typedef unsigned long u_long; +#define __u_long_defined +#endif +#define _BSDTYPES_DEFINED +#endif + +typedef unsigned short ushort; /* System V compatibility */ +typedef unsigned int uint; /* System V compatibility */ +typedef unsigned long ulong; /* System V compatibility */ +# endif /*!_POSIX_SOURCE */ + +#ifndef __clock_t_defined +typedef _CLOCK_T_ clock_t; +#define __clock_t_defined +#endif + +#ifndef __time_t_defined +typedef _TIME_T_ time_t; +#define __time_t_defined +#endif + +#ifndef __timespec_defined +#define __timespec_defined +/* Time Value Specification Structures, P1003.1b-1993, p. 261 */ + +struct timespec { + time_t tv_sec; /* Seconds */ + long tv_nsec; /* Nanoseconds */ +}; +#endif + +struct itimerspec { + struct timespec it_interval; /* Timer period */ + struct timespec it_value; /* Timer expiration */ +}; + +#ifndef __daddr_t_defined +typedef long daddr_t; +#define __daddr_t_defined +#endif +#ifndef __caddr_t_defined +typedef char * caddr_t; +#define __caddr_t_defined +#endif + +#ifndef __CYGWIN__ +#if defined(__MS_types__) || defined(__rtems__) || \ + defined(__sparc__) || defined(__SPU__) +typedef unsigned long ino_t; +#else +typedef unsigned short ino_t; +#endif +#endif /*__CYGWIN__*/ + +#ifdef __MS_types__ +typedef unsigned long vm_offset_t; +typedef unsigned long vm_size_t; + +#define __BIT_TYPES_DEFINED__ + +typedef signed char int8_t; +typedef unsigned char u_int8_t; +typedef short int16_t; +typedef unsigned short u_int16_t; +typedef int int32_t; +typedef unsigned int u_int32_t; +typedef long long int64_t; +typedef unsigned long long u_int64_t; +typedef int32_t register_t; +#endif /* __MS_types__ */ + +/* + * All these should be machine specific - right now they are all broken. + * However, for all of Cygnus' embedded targets, we want them to all be + * the same. Otherwise things like sizeof (struct stat) might depend on + * how the file was compiled (e.g. -mint16 vs -mint32, etc.). + */ + +#ifndef __CYGWIN__ /* which defines these types in it's own types.h. */ +typedef _off_t off_t; +typedef __dev_t dev_t; +typedef __uid_t uid_t; +typedef __gid_t gid_t; +#endif + +#if defined(__XMK__) +typedef signed char pid_t; +#else +typedef int pid_t; +#endif + +#if defined(__rtems__) +typedef _mode_t mode_t; +#endif + +#ifndef __CYGWIN__ +typedef long key_t; +#endif +typedef _ssize_t ssize_t; + +#if !defined(__CYGWIN__) && !defined(__rtems__) +#ifdef __MS_types__ +typedef char * addr_t; +typedef int mode_t; +#else +#if defined (__sparc__) && !defined (__sparc_v9__) +#ifdef __svr4__ +typedef unsigned long mode_t; +#else +typedef unsigned short mode_t; +#endif +#else +typedef unsigned int mode_t _ST_INT32; +#endif +#endif /* ! __MS_types__ */ +#endif /*__CYGWIN__*/ + +typedef unsigned short nlink_t; + +/* We don't define fd_set and friends if we are compiling POSIX + source, or if we have included (or may include as indicated + by __USE_W32_SOCKETS) the W32api winsock[2].h header which + defines Windows versions of them. Note that a program which + includes the W32api winsock[2].h header must know what it is doing; + it must not call the cygwin32 select function. +*/ +# if !(defined (_POSIX_SOURCE) || defined (_WINSOCK_H) || defined (_WINSOCKAPI_) || defined (__USE_W32_SOCKETS)) +# define _SYS_TYPES_FD_SET +# define NBBY 8 /* number of bits in a byte */ +/* + * Select uses bit masks of file descriptors in longs. + * These macros manipulate such bit fields (the filesystem macros use chars). + * FD_SETSIZE may be defined by the user, but the default here + * should be >= NOFILE (param.h). + */ +# ifndef FD_SETSIZE +# define FD_SETSIZE 64 +# endif + +typedef long fd_mask; +# define NFDBITS (sizeof (fd_mask) * NBBY) /* bits per mask */ +# ifndef howmany +# define howmany(x,y) (((x)+((y)-1))/(y)) +# endif + +/* We use a macro for fd_set so that including Sockets.h afterwards + can work. */ +typedef struct _types_fd_set { + fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)]; +} _types_fd_set; + +#define fd_set _types_fd_set + +# define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1L << ((n) % NFDBITS))) +# define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1L << ((n) % NFDBITS))) +# define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1L << ((n) % NFDBITS))) +# define FD_ZERO(p) (__extension__ (void)({ \ + size_t __i; \ + char *__tmp = (char *)p; \ + for (__i = 0; __i < sizeof (*(p)); ++__i) \ + *__tmp++ = 0; \ +})) + +# endif /* !(defined (_POSIX_SOURCE) || defined (_WINSOCK_H) || defined (_WINSOCKAPI_) || defined (__USE_W32_SOCKETS)) */ + +#undef __MS_types__ +#undef _ST_INT32 + + +#ifndef __clockid_t_defined +typedef _CLOCKID_T_ clockid_t; +#define __clockid_t_defined +#endif + +#ifndef __timer_t_defined +typedef _TIMER_T_ timer_t; +#define __timer_t_defined +#endif + +typedef unsigned long useconds_t; +typedef long suseconds_t; + +#endif /* !__need_inttypes */ + +#undef __need_inttypes + +#endif /* _SYS_TYPES_H */ + +#ifdef __cplusplus +} +#endif + +#endif /* DOXYGEN */ +#endif /* SYS_TYPES_H */ diff --git a/cpu/esp32/include/syscalls.h b/cpu/esp32/include/syscalls.h new file mode 100644 index 0000000000000..0d5484e89c7e1 --- /dev/null +++ b/cpu/esp32/include/syscalls.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief Implementation of required system calls + * + * @author Gunar Schorcht + * + * @} + */ + +#ifndef SYSCALLS_H +#define SYSCALLS_H + +#include +#include +#include + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Necessary initializations of system call functions */ +void syscalls_init (void); + +/** System standard printf function */ +int printf(const char* format, ...); + +/** Determine free heap size */ +unsigned int get_free_heap_size (void); + +/** Time since boot in us (32bit version) */ +uint32_t system_get_time (void); + +/** Time since boot in us (64bit version) */ +uint64_t system_get_time_64 (void); + +/** initialize system watchdog timer ans start it */ +void system_wdt_init (void); + +/** start the initialized system watchdog timer */ +void system_wdt_start (void); + +/** start the running system watchdog timer */ +void system_wdt_stop (void); + +/** reset the system watchdog timer */ +void system_wdt_feed (void); + +#ifdef __cplusplus +} +#endif + +#endif /* SYSCALLS_H */ diff --git a/cpu/esp32/include/thread_arch.h b/cpu/esp32/include/thread_arch.h new file mode 100644 index 0000000000000..b454472e89896 --- /dev/null +++ b/cpu/esp32/include/thread_arch.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief Implementation of the kernel's architecture dependent thread interface + * + * @author Gunar Schorcht + * + * @} + */ + +#ifndef THREAD_ARCH_H +#define THREAD_ARCH_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes the ISR stack + * Initializes the ISR stack with predefined values (address value) to be able to + * measure used ISR stack size. + */ +extern void thread_isr_stack_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* THREAD_ARCH_H */ diff --git a/cpu/esp32/include/tools.h b/cpu/esp32/include/tools.h new file mode 100644 index 0000000000000..281c118ddb04b --- /dev/null +++ b/cpu/esp32/include/tools.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief Implementation of some tools + * + * @author Gunar Schorcht + * + * @} + */ + +#ifndef TOOLS_H +#define TOOLS_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Hexdump of memory + * @param[in] addr start address in memory + * @param[in] num number of items to dump + * @param[in] width format of the items + * @param[in] per_line number of items per line + */ +void hexdump (const void* addr, uint32_t num, char width, uint8_t per_line); + +#ifdef __cplusplus +} +#endif + +#endif /* TOOLS_H */ diff --git a/cpu/esp32/include/xtensa_conf.h b/cpu/esp32/include/xtensa_conf.h new file mode 100644 index 0000000000000..9c964faa6b267 --- /dev/null +++ b/cpu/esp32/include/xtensa_conf.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief Xtensa ASM code specific configuration options + * + * @author Gunar Schorcht + */ + +#ifndef XTENSA_CONF_H +#define XTENSA_CONF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Xtensa ASM code specific default stack sizes + * @{ + */ +#if defined(SDK_INT_HANDLING) +#define ISR_STACKSIZE (8) +#else +#define ISR_STACKSIZE (2048) +#endif +/** @} */ + +#ifdef __cplusplus +} +#endif +#endif /* XTENSA_CONF_H */ +/** @} */ diff --git a/cpu/esp32/irq_arch.c b/cpu/esp32/irq_arch.c new file mode 100644 index 0000000000000..d779c82d7b2af --- /dev/null +++ b/cpu/esp32/irq_arch.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief Implementation of the kernels irq interface + * + * @author Gunar Schorcht + * + * @} + */ + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#include +#include + +#include "irq.h" +#include "cpu.h" + +#include "common.h" +#include "esp/common_macros.h" +#include "esp/xtensa_ops.h" +#include "rom/ets_sys.h" +#include "xtensa/xtensa_context.h" + +extern unsigned _xtos_set_intlevel(unsigned intlevel); + +/** + * @brief Set on entry into and reset on exit from an ISR + */ +volatile uint32_t irq_interrupt_nesting = 0; + +/** + * @brief Disable all maskable interrupts + */ +unsigned int IRAM irq_disable(void) +{ + uint32_t _saved_intlevel; + + /* read and set interrupt level with one asm instruction (RSIL) */ + __asm__ volatile ("rsil %0, " XTSTR(XCHAL_NUM_INTLEVELS+1) : "=a" (_saved_intlevel)); + /* mask out everything else of the PS register that do not belong to + interrupt level (bits 3..0) */ + _saved_intlevel &= 0xf; + + DEBUG ("%s new %08lx (old %08lx)\n", __func__, + XCHAL_NUM_INTLEVELS + 1, _saved_intlevel); + return _saved_intlevel; +} + +/** + * @brief Enable all maskable interrupts + */ +unsigned int IRAM irq_enable(void) +{ + uint32_t _saved_intlevel; + + /* read and set interrupt level with one asm instruction (RSIL) */ + __asm__ volatile ("rsil %0, 0" : "=a" (_saved_intlevel)); + /* mask out everything else of the PS register that do not belong to + interrupt level (bits 3..0) */ + _saved_intlevel &= 0xf; + + DEBUG ("%s new %08lx (old %08lx)\n", __func__, 0, _saved_intlevel); + return _saved_intlevel; +} + +/** + * @brief Restore the state of the IRQ flags + */ +void IRAM irq_restore(unsigned int state) +{ + /* restore the interrupt level using a rom function, performance is not + important here */ + #if 0 + __asm__ volatile ("wsr %0, ps; rsync" :: "a" (state)); + DEBUG ("%s %02x\n", __func__, state); + #else + unsigned _saved_intlevel = _xtos_set_intlevel(state); + DEBUG ("%s new %08lx (old %08lx)\n", __func__, state, _saved_intlevel); + #endif +} + +/** + * @brief See if the current context is inside an ISR + */ +int IRAM irq_is_in(void) +{ + DEBUG("irq_interrupt_nesting = %d\n", irq_interrupt_nesting); + return irq_interrupt_nesting; +} diff --git a/cpu/esp32/ld/README.md b/cpu/esp32/ld/README.md new file mode 100644 index 0000000000000..e7d5c25075832 --- /dev/null +++ b/cpu/esp32/ld/README.md @@ -0,0 +1 @@ +The files in this directory are originally from the [ESP-IDF](https://github.com/espressif/esp-idf) and modified for RIOT-OS. All these files are under the copyright of their respective ownwers and licensed under the Apache License, Version 2.0, [see](https://github.com/espressif/esp-idf/blob/master/LICENSE). diff --git a/cpu/esp32/ld/esp32.common.ld b/cpu/esp32/ld/esp32.common.ld new file mode 100644 index 0000000000000..03b6fa8e24909 --- /dev/null +++ b/cpu/esp32/ld/esp32.common.ld @@ -0,0 +1,241 @@ +/* Default entry point: */ +ENTRY(call_start_cpu0); + +SECTIONS +{ + /* RTC fast memory holds RTC wake stub code, + including from any source file named rtc_wake_stub*.c + */ + .rtc.text : + { + . = ALIGN(4); + *(.rtc.literal .rtc.text) + *rtc_wake_stub*.o(.literal .text .literal.* .text.*) + } >rtc_iram_seg + + /* RTC slow memory holds RTC wake stub + data/rodata, including from any source file + named rtc_wake_stub*.c + */ + .rtc.data : + { + _rtc_data_start = ABSOLUTE(.); + *(.rtc.data) + *(.rtc.rodata) + *rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*) + _rtc_data_end = ABSOLUTE(.); + } > rtc_slow_seg + + /* RTC bss, from any source file named rtc_wake_stub*.c */ + .rtc.bss (NOLOAD) : + { + _rtc_bss_start = ABSOLUTE(.); + *rtc_wake_stub*.o(.bss .bss.*) + *rtc_wake_stub*.o(COMMON) + _rtc_bss_end = ABSOLUTE(.); + } > rtc_slow_seg + + /* Send .iram0 code to iram */ + .iram0.vectors : + { + /* Vectors go to IRAM */ + _init_start = ABSOLUTE(.); + /* Vectors according to builds/RF-2015.2-win32/esp108_v1_2_s5_512int_2/config.html */ + . = 0x0; + KEEP(*(.WindowVectors.text)); + . = 0x180; + KEEP(*(.Level2InterruptVector.text)); + . = 0x1c0; + KEEP(*(.Level3InterruptVector.text)); + . = 0x200; + KEEP(*(.Level4InterruptVector.text)); + . = 0x240; + KEEP(*(.Level5InterruptVector.text)); + . = 0x280; + KEEP(**(.DebugExceptionVector.text)); + . = 0x2c0; + KEEP(*(.NMIExceptionVector.text)); + . = 0x300; + KEEP(*(.KernelExceptionVector.text)); + . = 0x340; + KEEP(*(.UserExceptionVector.text)); + . = 0x3C0; + KEEP(*(.DoubleExceptionVector.text)); + . = 0x400; + *(.*Vector.literal) + + *(.UserEnter.literal); + *(.UserEnter.text); + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + _init_end = ABSOLUTE(.); + + /* This goes here, not at top of linker script, so addr2line finds it last, + and uses it in preference to the first symbol in IRAM */ + _iram_start = ABSOLUTE(0); + } > iram0_0_seg + + .iram0.text : + { + /* Code marked as runnning out of IRAM */ + _iram_text_start = ABSOLUTE(.); + *(.iram1 .iram1.*) + + *libhal.a:(.literal .text .literal.* .text.*) + *libgcc.a:lib2funcs.o(.literal .text .literal.* .text.*) + *libgcov.a:(.literal .text .literal.* .text.*) + /* *libc.a:(.literal .text .literal.* .text.*) */ + + /* Xtensa basic functionality written in assembler should be placed in iram */ + *xtensa.a:*(.literal .text .literal.* .text.*) + /* ESP-IDF parts that have to run in IRAM */ + *esp_idf_heap.a:*(.literal .text .literal.* .text.*) + *esp_idf_spi_flash.a:*(.literal .text .literal.* .text.*) + /* parts of RIOT that have to run in IRAM */ + *core.a:*(.literal .text .literal.* .text.*) + /* *spiffs_fs.a:*(.literal .text .literal.* .text.*) */ + /* *spiffs.a:*(.literal .text .literal.* .text.*) */ + + /* part of RIOT ports that have to run in IRAM */ + *cpu.a:*(.literal .text .literal.* .text.*) + *periph.a:*(.literal .text .literal.* .text.*) + + INCLUDE esp32.spiram.rom-functions-iram.ld + _iram_text_end = ABSOLUTE(.); + } > iram0_0_seg + + .dram0.data : + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + *(.dram1 .dram1.*) + *libesp32.a:panic.o(.rodata .rodata.*) + *libphy.a:(.rodata .rodata.*) + *libsoc.a:rtc_clk.o(.rodata .rodata.*) + *libapp_trace.a:(.rodata .rodata.*) + *libgcov.a:(.rodata .rodata.*) + *libheap.a:multi_heap.o(.rodata .rodata.*) + *libheap.a:multi_heap_poisoning.o(.rodata .rodata.*) + INCLUDE esp32.spiram.rom-functions-dram.ld + _data_end = ABSOLUTE(.); + . = ALIGN(4); + } >dram0_0_seg + + /* Shared RAM */ + .dram0.bss (NOLOAD) : + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.share.mem) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); + _sheap = ABSOLUTE(.); + } >dram0_0_seg + + /* TODO HEAP handling when BT is used + ETS system memory seems to start at 0x3FFE0000 if BT is not used. + This is the top of the heap for the app */ + . = 0x3FFE0000; + _heap_top = ABSOLUTE(.); + _eheap = ABSOLUTE(.); + + .flash.rodata : + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */ + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE_ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table .gcc_except_table.*) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + . = (. + 3) & ~ 3; + __eh_frame = ABSOLUTE(.); + KEEP(*(.eh_frame)) + . = (. + 7) & ~ 3; + /* C++ constructor and destructor tables, properly ordered: */ + __init_array_start = ABSOLUTE(.); + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __init_array_end = ABSOLUTE(.); + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS_ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + _rodata_end = ABSOLUTE(.); + /* Literals are also RO data. */ + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + . = ALIGN(4); + _thread_local_start = ABSOLUTE(.); + *(.tdata) + *(.tdata.*) + *(.tbss) + *(.tbss.*) + _thread_local_end = ABSOLUTE(.); + . = ALIGN(4); + } >drom0_0_seg + + .flash.text : + { + _stext = .; + _text_start = ABSOLUTE(.); + /* place everything else in iram0_2_seg (cached ROM) */ + *(.literal .text .literal.* .text.* .stub) + *(.gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.irom0.text) /* catch stray ICACHE_RODATA_ATTR */ + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + + /* Similar to _iram_start, this symbol goes here so it is + resolved by addr2line in preference to the first symbol in + the flash.text segment. + */ + _flash_cache_start = ABSOLUTE(0); + } >iram0_2_seg +} diff --git a/cpu/esp32/ld/esp32.ld b/cpu/esp32/ld/esp32.ld new file mode 100644 index 0000000000000..683446cb32951 --- /dev/null +++ b/cpu/esp32/ld/esp32.ld @@ -0,0 +1,68 @@ +/* ESP32 Linker Script Memory Layout + + This file describes the memory layout (memory blocks) as virtual + memory addresses. + + esp32.common.ld contains output sections to link compiler output + into these memory blocks. + + *** + + This linker script is passed through the C preprocessor to include + configuration options. + + Please use preprocessor features sparingly! Restrict + to simple macros with numeric values, and/or #if/#endif blocks. +*/ + +MEMORY +{ + /* All these values assume the flash cache is on, and have the blocks this uses subtracted from the length + of the various regions. The 'data access port' dram/drom regions map to the same iram/irom regions but + are connected to the data port of the CPU and eg allow bytewise access. */ + + /* IRAM for PRO cpu. Not sure if happy with this, this is MMU area... */ + iram0_0_seg (RX) : org = 0x40080000, len = 0x20000 + + /* Even though the segment name is iram, it is actually mapped to flash + */ + iram0_2_seg (RX) : org = 0x400D0018, len = 0x330000-0x18 + + /* + (0x18 offset above is a convenience for the app binary image generation. Flash cache has 64KB pages. The .bin file + which is flashed to the chip has a 0x18 byte file header. Setting this offset makes it simple to meet the flash + cache MMU's constraint that (paddr % 64KB == vaddr % 64KB).) + */ + + + /* Shared data RAM, excluding memory reserved for ROM bss/data/stack. + + Enabling Bluetooth & Trace Memory features in menuconfig will decrease + the amount of RAM available. + + Note: Length of this section *should* be 0x50000, and this extra DRAM is available + in heap at runtime. However due to static ROM memory usage at this 176KB mark, the + additional static memory temporarily cannot be used. + */ + dram0_0_seg (RW) : org = 0x3FFB0000, + len = 0x2c200 + + /* Flash mapped constant data */ + drom0_0_seg (R) : org = 0x3F400018, len = 0x400000-0x18 + + /* (See iram0_2_seg for meaning of 0x18 offset in the above.) */ + + /* RTC fast memory (executable). Persists over deep sleep. + */ + rtc_iram_seg(RWX) : org = 0x400C0000, len = 0x2000 + + /* RTC slow memory (data accessible). Persists over deep sleep. + + Start of RTC slow memory is reserved for ULP co-processor code + data, if enabled. + */ + rtc_slow_seg(RW) : org = 0x50000000, + len = 0x1000 +} + +/* Heap ends at top of dram0_0_seg */ +_heap_end = 0x40000000; diff --git a/cpu/esp32/ld/esp32.peripherals.ld b/cpu/esp32/ld/esp32.peripherals.ld new file mode 100644 index 0000000000000..621fedea13deb --- /dev/null +++ b/cpu/esp32/ld/esp32.peripherals.ld @@ -0,0 +1,28 @@ +PROVIDE ( UART0 = 0x3ff40000 ); +PROVIDE ( SPI1 = 0x3ff42000 ); +PROVIDE ( SPI0 = 0x3ff43000 ); +PROVIDE ( GPIO = 0x3ff44000 ); +PROVIDE ( SIGMADELTA = 0x3ff44f00 ); +PROVIDE ( RTCCNTL = 0x3ff48000 ); +PROVIDE ( RTCIO = 0x3ff48400 ); +PROVIDE ( SENS = 0x3ff48800 ); +PROVIDE ( UHCI1 = 0x3ff4C000 ); +PROVIDE ( I2S0 = 0x3ff4F000 ); +PROVIDE ( UART1 = 0x3ff50000 ); +PROVIDE ( I2C0 = 0x3ff53000 ); +PROVIDE ( UHCI0 = 0x3ff54000 ); +PROVIDE ( RMT = 0x3ff56000 ); +PROVIDE ( RMTMEM = 0x3ff56800 ); +PROVIDE ( PCNT = 0x3ff57000 ); +PROVIDE ( LEDC = 0x3ff59000 ); +PROVIDE ( MCPWM0 = 0x3ff5E000 ); +PROVIDE ( TIMERG0 = 0x3ff5F000 ); +PROVIDE ( TIMERG1 = 0x3ff60000 ); +PROVIDE ( SPI2 = 0x3ff64000 ); +PROVIDE ( SPI3 = 0x3ff65000 ); +PROVIDE ( SYSCON = 0x3ff66000 ); +PROVIDE ( I2C1 = 0x3ff67000 ); +PROVIDE ( SDMMC = 0x3ff68000 ); +PROVIDE ( MCPWM1 = 0x3ff6C000 ); +PROVIDE ( I2S1 = 0x3ff6D000 ); +PROVIDE ( UART2 = 0x3ff6E000 ); diff --git a/cpu/esp32/ld/esp32.rom.ld b/cpu/esp32/ld/esp32.rom.ld new file mode 100644 index 0000000000000..2b34e2a82ee9a --- /dev/null +++ b/cpu/esp32/ld/esp32.rom.ld @@ -0,0 +1,1728 @@ +/* +ESP32 ROM address table +Generated for ROM with MD5sum: +ab8282ae908fe9e7a63fb2a4ac2df013 ../../rom_image/prorom.elf +*/ +PROVIDE ( abort = 0x4000bba4 ); +PROVIDE ( __absvdi2 = 0x4006387c ); +PROVIDE ( __absvsi2 = 0x40063868 ); +PROVIDE ( Add2SelfBigHex256 = 0x40015b7c ); +PROVIDE ( AddBigHex256 = 0x40015b28 ); +PROVIDE ( AddBigHexModP256 = 0x40015c98 ); +PROVIDE ( __adddf3 = 0x40002590 ); +PROVIDE ( AddP256 = 0x40015c74 ); +PROVIDE ( AddPdiv2_256 = 0x40015ce0 ); +PROVIDE ( __addsf3 = 0x400020e8 ); +PROVIDE ( __addvdi3 = 0x40002cbc ); +PROVIDE ( __addvsi3 = 0x40002c98 ); +PROVIDE ( aes_128_cbc_decrypt = 0x4005cc7c ); +PROVIDE ( aes_128_cbc_encrypt = 0x4005cc18 ); +PROVIDE ( aes_unwrap = 0x4005ccf0 ); +PROVIDE ( app_gpio_arg = 0x3ffe003c ); +PROVIDE ( app_gpio_handler = 0x3ffe0040 ); +PROVIDE ( __ashldi3 = 0x4000c818 ); +PROVIDE ( __ashrdi3 = 0x4000c830 ); +PROVIDE ( base64_decode = 0x4005ced8 ); +PROVIDE ( base64_encode = 0x4005cdbc ); +PROVIDE ( BasePoint_x_256 = 0x3ff97488 ); +PROVIDE ( BasePoint_y_256 = 0x3ff97468 ); +PROVIDE ( bigHexInversion256 = 0x400168f0 ); +PROVIDE ( bigHexP256 = 0x3ff973bc ); +PROVIDE ( __bswapdi2 = 0x400649c4 ); +PROVIDE ( __bswapsi2 = 0x4006499c ); +PROVIDE ( btdm_r_ble_bt_handler_tab_p_get = 0x40019b0c ); +PROVIDE ( btdm_r_btdm_option_data_p_get = 0x40010004 ); +PROVIDE ( btdm_r_btdm_rom_version_get = 0x40010078 ); +PROVIDE ( btdm_r_data_init = 0x4001002c ); +PROVIDE ( btdm_r_import_rf_phy_func_p_get = 0x40054298 ); +PROVIDE ( btdm_r_ip_func_p_get = 0x40019af0 ); +PROVIDE ( btdm_r_ip_func_p_set = 0x40019afc ); +PROVIDE ( btdm_r_modules_func_p_get = 0x4005427c ); +PROVIDE ( btdm_r_modules_func_p_set = 0x40054270 ); +PROVIDE ( btdm_r_plf_func_p_set = 0x40054288 ); +PROVIDE ( bt_util_buf_env = 0x3ffb8bd4 ); +PROVIDE ( cache_flash_mmu_set_rom = 0x400095e0 ); +PROVIDE ( Cache_Flush_rom = 0x40009a14 ); +PROVIDE ( Cache_Read_Disable_rom = 0x40009ab8 ); +PROVIDE ( Cache_Read_Enable_rom = 0x40009a84 ); +PROVIDE ( Cache_Read_Init_rom = 0x40009950 ); +PROVIDE ( cache_sram_mmu_set_rom = 0x400097f4 ); +/* This is static function, but can be used, not generated by script*/ +PROVIDE ( calc_rtc_memory_crc = 0x40008170 ); +PROVIDE ( calloc = 0x4000bee4 ); +PROVIDE ( _calloc_r = 0x4000bbf8 ); +PROVIDE ( __clear_cache = 0x40063860 ); +PROVIDE ( _close_r = 0x4000bd3c ); +PROVIDE ( __clrsbdi2 = 0x40064a38 ); +PROVIDE ( __clrsbsi2 = 0x40064a20 ); +PROVIDE ( __clzdi2 = 0x4000ca50 ); +PROVIDE ( __clzsi2 = 0x4000c7e8 ); +PROVIDE ( __cmpdi2 = 0x40063820 ); +PROVIDE ( co_default_bdaddr = 0x3ffae704 ); +PROVIDE ( co_null_bdaddr = 0x3ffb80e0 ); +PROVIDE ( co_sca2ppm = 0x3ff971e8 ); +PROVIDE ( crc16_be = 0x4005d09c ); +PROVIDE ( crc16_le = 0x4005d05c ); +PROVIDE ( crc32_be = 0x4005d024 ); +PROVIDE ( crc32_le = 0x4005cfec ); +PROVIDE ( crc8_be = 0x4005d114 ); +PROVIDE ( crc8_le = 0x4005d0e0 ); +PROVIDE ( _ctype_ = 0x3ff96354 ); +PROVIDE ( __ctype_ptr__ = 0x3ff96350 ); +PROVIDE ( __ctzdi2 = 0x4000ca64 ); +PROVIDE ( __ctzsi2 = 0x4000c7f0 ); +PROVIDE ( _data_end_rom = 0x4000d5c8 ); +PROVIDE ( _data_end_btdm_rom = 0x4000d4f8 ); +PROVIDE ( _data_start_rom = 0x4000d4f8 ); +PROVIDE ( _data_start_btdm_rom = 0x4000d4f4 ); +PROVIDE ( _data_start_btdm = 0x3ffae6e0); +PROVIDE ( _data_end_btdm = 0x3ffaff10); +PROVIDE ( _bss_start_btdm = 0x3ffb8000); +PROVIDE ( _bss_end_btdm = 0x3ffbff70); +PROVIDE ( _daylight = 0x3ffae0a4 ); +PROVIDE ( dbg_default_handler = 0x3ff97218 ); +PROVIDE ( dbg_state = 0x3ffb8d5d ); +PROVIDE ( DebugE256PublicKey_x = 0x3ff97428 ); +PROVIDE ( DebugE256PublicKey_y = 0x3ff97408 ); +PROVIDE ( DebugE256SecretKey = 0x3ff973e8 ); +PROVIDE ( debug_timer = 0x3ffe042c ); +PROVIDE ( debug_timerfn = 0x3ffe0430 ); +PROVIDE ( dh_group14_generator = 0x3ff9ac60 ); +PROVIDE ( dh_group14_prime = 0x3ff9ab60 ); +PROVIDE ( dh_group15_generator = 0x3ff9ab5f ); +PROVIDE ( dh_group15_prime = 0x3ff9a9df ); +PROVIDE ( dh_group16_generator = 0x3ff9a9de ); +PROVIDE ( dh_group16_prime = 0x3ff9a7de ); +PROVIDE ( dh_group17_generator = 0x3ff9a7dd ); +PROVIDE ( dh_group17_prime = 0x3ff9a4dd ); +PROVIDE ( dh_group18_generator = 0x3ff9a4dc ); +PROVIDE ( dh_group18_prime = 0x3ff9a0dc ); +PROVIDE ( dh_group1_generator = 0x3ff9ae03 ); +PROVIDE ( dh_group1_prime = 0x3ff9ada3 ); +PROVIDE ( dh_group2_generator = 0x3ff9ada2 ); +PROVIDE ( dh_group2_prime = 0x3ff9ad22 ); +PROVIDE ( dh_group5_generator = 0x3ff9ad21 ); +PROVIDE ( dh_group5_prime = 0x3ff9ac61 ); +PROVIDE ( __divdc3 = 0x40064460 ); +PROVIDE ( __divdf3 = 0x40002954 ); +PROVIDE ( __divdi3 = 0x4000ca84 ); +PROVIDE ( __divsc3 = 0x40064200 ); +PROVIDE ( __divsf3 = 0x4000234c ); +PROVIDE ( __divsi3 = 0x4000c7b8 ); +PROVIDE ( g_rom_spiflash_dummy_len_plus = 0x3ffae290 ); +PROVIDE ( ecc_env = 0x3ffb8d60 ); +PROVIDE ( ecc_Jacobian_InfinityPoint256 = 0x3ff972e8 ); +PROVIDE ( em_buf_env = 0x3ffb8d74 ); +PROVIDE ( environ = 0x3ffae0b4 ); +PROVIDE ( __eqdf2 = 0x400636a8 ); +PROVIDE ( __eqsf2 = 0x40063374 ); +PROVIDE ( esp_crc8 = 0x4005d144 ); +PROVIDE ( _etext = 0x4000d66c ); +PROVIDE ( ets_readySet_ = 0x3ffe01f0 ); +PROVIDE ( ets_startup_callback = 0x3ffe0404 ); +PROVIDE ( exc_cause_table = 0x3ff991d0 ); +PROVIDE ( _exit_r = 0x4000bd28 ); +PROVIDE ( __extendsfdf2 = 0x40002c34 ); +PROVIDE ( __ffsdi2 = 0x4000ca2c ); +PROVIDE ( __ffssi2 = 0x4000c804 ); +PROVIDE ( __fixdfdi = 0x40002ac4 ); +PROVIDE ( __fixdfsi = 0x40002a78 ); +PROVIDE ( __fixsfdi = 0x4000244c ); +PROVIDE ( __fixsfsi = 0x4000240c ); +PROVIDE ( __fixunsdfsi = 0x40002b30 ); +PROVIDE ( __fixunssfdi = 0x40002504 ); +PROVIDE ( __fixunssfsi = 0x400024ac ); +PROVIDE ( __floatdidf = 0x4000c988 ); +PROVIDE ( __floatdisf = 0x4000c8c0 ); +PROVIDE ( __floatsidf = 0x4000c944 ); +PROVIDE ( __floatsisf = 0x4000c870 ); +PROVIDE ( __floatundidf = 0x4000c978 ); +PROVIDE ( __floatundisf = 0x4000c8b0 ); +PROVIDE ( __floatunsidf = 0x4000c938 ); +PROVIDE ( __floatunsisf = 0x4000c864 ); +PROVIDE ( free = 0x4000beb8 ); +PROVIDE ( _free_r = 0x4000bbcc ); +PROVIDE ( _fstat_r = 0x4000bccc ); +PROVIDE ( __gcc_bcmp = 0x40064a70 ); +PROVIDE ( __gedf2 = 0x40063768 ); +PROVIDE ( __gesf2 = 0x4006340c ); +PROVIDE ( _getpid_r = 0x4000bcfc ); +PROVIDE ( __getreent = 0x4000be8c ); +PROVIDE ( _gettimeofday_r = 0x4000bc58 ); +PROVIDE ( GF_Jacobian_Point_Addition256 = 0x400163a4 ); +PROVIDE ( GF_Jacobian_Point_Double256 = 0x40016260 ); +PROVIDE ( GF_Point_Jacobian_To_Affine256 = 0x40016b0c ); +PROVIDE ( _global_impure_ptr = 0x3ffae0b0 ); +PROVIDE ( g_phyFuns_instance = 0x3ffae0c4 ); +PROVIDE ( g_rom_flashchip = 0x3ffae270 ); +PROVIDE ( __gtdf2 = 0x400636dc ); +PROVIDE ( __gtsf2 = 0x400633a0 ); +PROVIDE ( gTxMsg = 0x3ffe0050 ); +PROVIDE ( hci_cmd_desc_root_tab = 0x3ff976d4 ); +PROVIDE ( hci_cmd_desc_tab_ctrl_bb = 0x3ff97b70 ); +PROVIDE ( hci_cmd_desc_tab_info_par = 0x3ff97b1c ); +PROVIDE ( hci_cmd_desc_tab_le = 0x3ff97870 ); +PROVIDE ( hci_cmd_desc_tab_lk_ctrl = 0x3ff97fc0 ); +PROVIDE ( hci_cmd_desc_tab_lk_pol = 0x3ff97f3c ); +PROVIDE ( hci_cmd_desc_tab_stat_par = 0x3ff97ac8 ); +PROVIDE ( hci_cmd_desc_tab_testing = 0x3ff97a98 ); +PROVIDE ( hci_cmd_desc_tab_vs = 0x3ff97714 ); +PROVIDE ( hci_command_handler = 0x4004c928 ); +PROVIDE ( hci_env = 0x3ffb9350 ); +PROVIDE ( hci_evt_dbg_desc_tab = 0x3ff9750c ); +PROVIDE ( hci_evt_desc_tab = 0x3ff9751c ); +PROVIDE ( hci_evt_le_desc_tab = 0x3ff974b4 ); +PROVIDE ( hci_fc_env = 0x3ffb9340 ); +PROVIDE ( hmac_md5 = 0x4005d264 ); +PROVIDE ( hmac_md5_vector = 0x4005d17c ); +PROVIDE ( hmac_sha1 = 0x40060acc ); +PROVIDE ( hmac_sha1_vector = 0x400609e4 ); +PROVIDE ( hmac_sha256 = 0x40060d58 ); +PROVIDE ( hmac_sha256_vector = 0x40060c84 ); +PROVIDE ( jd_decomp = 0x400613e8 ); +PROVIDE ( jd_prepare = 0x40060fa8 ); +PROVIDE ( ke_env = 0x3ffb93cc ); +PROVIDE ( _kill_r = 0x4000bd10 ); +PROVIDE ( lb_default_handler = 0x3ff982b8 ); +PROVIDE ( lb_default_state_tab_p_get = 0x4001c198 ); +PROVIDE ( lb_env = 0x3ffb9424 ); +PROVIDE ( lb_hci_cmd_handler_tab_p_get = 0x4001c18c ); +PROVIDE ( lb_state = 0x3ffb94e8 ); +PROVIDE ( lc_default_handler = 0x3ff98648 ); +PROVIDE ( lc_default_state_tab_p_get = 0x4002f494 ); +PROVIDE ( lc_env = 0x3ffb94ec ); +PROVIDE ( lc_hci_cmd_handler_tab_p_get = 0x4002f488 ); +PROVIDE ( lc_state = 0x3ffb9508 ); +PROVIDE ( ld_acl_br_sizes = 0x3ff98a2a ); +PROVIDE ( ld_acl_br_types = 0x3ff98a36 ); +PROVIDE ( ld_acl_edr_sizes = 0x3ff98a14 ); +PROVIDE ( ld_acl_edr_types = 0x3ff98a22 ); +PROVIDE ( ld_env = 0x3ffb9510 ); +PROVIDE ( ld_pcm_settings_dft = 0x3ff98a0c ); +PROVIDE ( ld_sched_params = 0x3ffb96c0 ); +PROVIDE ( ld_sync_train_channels = 0x3ff98a3c ); +PROVIDE ( __ledf2 = 0x40063704 ); +PROVIDE ( __lesf2 = 0x400633c0 ); +PROVIDE ( _link_r = 0x4000bc9c ); +PROVIDE ( llc_default_handler = 0x3ff98b3c ); +PROVIDE ( llc_default_state_tab_p_get = 0x40046058 ); +PROVIDE ( llc_env = 0x3ffb96d0 ); +PROVIDE ( llc_hci_acl_data_tx_handler = 0x40042398 ); +PROVIDE ( llc_hci_cmd_handler_tab_p_get = 0x40042358 ); +PROVIDE ( llc_hci_command_handler = 0x40042360 ); +PROVIDE ( llcp_pdu_handler_tab_p_get = 0x40043f64 ); +PROVIDE ( llc_state = 0x3ffb96f8 ); +PROVIDE ( lldesc_build_chain = 0x4000a850 ); +PROVIDE ( lldesc_num2link = 0x4000a948 ); +PROVIDE ( lldesc_set_owner = 0x4000a974 ); +PROVIDE ( lld_evt_deferred_elt_push = 0x400466b4 ); +PROVIDE ( lld_evt_deferred_elt_pop = 0x400466dc ); +PROVIDE ( lld_evt_winsize_change = 0x40046730 ); +PROVIDE ( lld_evt_rxwin_compute = 0x400467c8 ); +PROVIDE ( lld_evt_slave_time_compute = 0x40046818 ); +PROVIDE ( lld_evt_env = 0x3ffb9704 ); +PROVIDE ( lld_evt_elt_wait_get = 0x400468e4 ); +PROVIDE ( lld_evt_get_next_free_slot = 0x4004692c ); +PROVIDE ( lld_pdu_adv_pk_desc_tab = 0x3ff98c70 ); +PROVIDE ( lld_pdu_llcp_pk_desc_tab = 0x3ff98b68 ); +PROVIDE ( lld_pdu_pack = 0x4004ab14 ); +PROVIDE ( LLM_AA_CT1 = 0x3ff98d8a ); +PROVIDE ( LLM_AA_CT2 = 0x3ff98d88 ); +PROVIDE ( llm_default_handler = 0x3ff98d80 ); +PROVIDE ( llm_default_state_tab_p_get = 0x4004e718 ); +PROVIDE ( llm_hci_cmd_handler_tab_p_get = 0x4004c920 ); +PROVIDE ( llm_le_env = 0x3ffb976c ); +PROVIDE ( llm_local_cmds = 0x3ff98d38 ); +PROVIDE ( llm_local_data_len_values = 0x3ff98d1c ); +PROVIDE ( llm_local_le_feats = 0x3ff98d30 ); +PROVIDE ( llm_local_le_states = 0x3ff98d28 ); +PROVIDE ( llm_state = 0x3ffb985c ); +PROVIDE ( lm_default_handler = 0x3ff990e0 ); +PROVIDE ( lm_default_state_tab_p_get = 0x40054268 ); +PROVIDE ( lm_env = 0x3ffb9860 ); +PROVIDE ( lm_hci_cmd_handler_tab_p_get = 0x4005425c ); +PROVIDE ( lm_local_supp_feats = 0x3ff990ee ); +PROVIDE ( lm_n_page_tab = 0x3ff990e8 ); +PROVIDE ( lmp_desc_tab = 0x3ff96e6c ); +PROVIDE ( lmp_ext_desc_tab = 0x3ff96d9c ); +PROVIDE ( lm_state = 0x3ffb9a1c ); +PROVIDE ( _lock_acquire_recursive = 0x4000be28 ); +PROVIDE ( _lock_close = 0x4000bdec ); +PROVIDE ( _lock_close_recursive = 0x4000be00 ); +PROVIDE ( _lock_init = 0x4000bdc4 ); +PROVIDE ( _lock_init_recursive = 0x4000bdd8 ); +PROVIDE ( _lock_release_recursive = 0x4000be78 ); +PROVIDE ( _lock_try_acquire = 0x4000be3c ); +PROVIDE ( _lock_try_acquire_recursive = 0x4000be50 ); +PROVIDE ( _lseek_r = 0x4000bd8c ); +PROVIDE ( __lshrdi3 = 0x4000c84c ); +PROVIDE ( __ltdf2 = 0x40063790 ); +PROVIDE ( __ltsf2 = 0x4006342c ); +PROVIDE ( malloc = 0x4000bea0 ); +PROVIDE ( _malloc_r = 0x4000bbb4 ); +PROVIDE ( maxSecretKey_256 = 0x3ff97448 ); +PROVIDE ( __mb_cur_max = 0x3ff96530 ); +PROVIDE ( MD5Final = 0x4005db1c ); +PROVIDE ( MD5Init = 0x4005da7c ); +PROVIDE ( MD5Update = 0x4005da9c ); +PROVIDE ( md5_vector = 0x4005db80 ); +PROVIDE ( mmu_init = 0x400095a4 ); +PROVIDE ( __moddi3 = 0x4000cd4c ); +PROVIDE ( __modsi3 = 0x4000c7c0 ); +PROVIDE ( __month_lengths = 0x3ff9609c ); +PROVIDE ( __muldc3 = 0x40063bf4 ); +PROVIDE ( __muldf3 = 0x4006358c ); +PROVIDE ( __muldi3 = 0x4000c9fc ); +PROVIDE ( __mulsc3 = 0x40063934 ); +PROVIDE ( __mulsf3 = 0x400632c8 ); +PROVIDE ( __mulsi3 = 0x4000c7b0 ); +PROVIDE ( MultiplyBigHexByUint32_256 = 0x40016214 ); +PROVIDE ( MultiplyBigHexModP256 = 0x400160b8 ); +PROVIDE ( MultiplyByU32ModP256 = 0x40015fdc ); +PROVIDE ( multofup = 0x4000ab8c ); +PROVIDE ( __mulvdi3 = 0x40002d78 ); +PROVIDE ( __mulvsi3 = 0x40002d60 ); +PROVIDE ( mz_adler32 = 0x4005edbc ); +PROVIDE ( mz_crc32 = 0x4005ee88 ); +PROVIDE ( mz_free = 0x4005eed4 ); +PROVIDE ( __nedf2 = 0x400636a8 ); +PROVIDE ( __negdf2 = 0x400634a0 ); +PROVIDE ( __negdi2 = 0x4000ca14 ); +PROVIDE ( __negsf2 = 0x400020c0 ); +PROVIDE ( __negvdi2 = 0x40002e98 ); +PROVIDE ( __negvsi2 = 0x40002e78 ); +PROVIDE ( __nesf2 = 0x40063374 ); +PROVIDE ( notEqual256 = 0x40015b04 ); +PROVIDE ( __nsau_data = 0x3ff96544 ); +PROVIDE ( one_bits = 0x3ff971f8 ); +PROVIDE ( _open_r = 0x4000bd54 ); +PROVIDE ( __paritysi2 = 0x40002f3c ); +PROVIDE ( pbkdf2_sha1 = 0x40060ba4 ); +PROVIDE ( phy_get_romfuncs = 0x40004100 ); +PROVIDE ( __popcountdi2 = 0x40002ef8 ); +PROVIDE ( __popcountsi2 = 0x40002ed0 ); +PROVIDE ( __popcount_tab = 0x3ff96544 ); +PROVIDE ( __powidf2 = 0x400638d4 ); +PROVIDE ( __powisf2 = 0x4006389c ); +PROVIDE ( _Pri_4_HandlerAddress = 0x3ffe0648 ); +PROVIDE ( _Pri_5_HandlerAddress = 0x3ffe064c ); +PROVIDE ( r_btdm_option_data = 0x3ffae6e0 ); +PROVIDE ( r_bt_util_buf_acl_rx_alloc = 0x40010218 ); +PROVIDE ( r_bt_util_buf_acl_rx_free = 0x40010234 ); +PROVIDE ( r_bt_util_buf_acl_tx_alloc = 0x40010268 ); +PROVIDE ( r_bt_util_buf_acl_tx_free = 0x40010280 ); +PROVIDE ( r_bt_util_buf_init = 0x400100e4 ); +PROVIDE ( r_bt_util_buf_lmp_tx_alloc = 0x400101d0 ); +PROVIDE ( r_bt_util_buf_lmp_tx_free = 0x400101ec ); +PROVIDE ( r_bt_util_buf_sync_clear = 0x400103c8 ); +PROVIDE ( r_bt_util_buf_sync_init = 0x400102c4 ); +PROVIDE ( r_bt_util_buf_sync_rx_alloc = 0x40010468 ); +PROVIDE ( r_bt_util_buf_sync_rx_free = 0x4001049c ); +PROVIDE ( r_bt_util_buf_sync_tx_alloc = 0x400103ec ); +PROVIDE ( r_bt_util_buf_sync_tx_free = 0x40010428 ); +PROVIDE ( rc4_skip = 0x40060928 ); +PROVIDE ( r_co_bdaddr_compare = 0x40014324 ); +PROVIDE ( r_co_bytes_to_string = 0x400142e4 ); +PROVIDE ( r_co_list_check_size_available = 0x400142c4 ); +PROVIDE ( r_co_list_extract = 0x4001404c ); +PROVIDE ( r_co_list_extract_after = 0x40014118 ); +PROVIDE ( r_co_list_find = 0x4001419c ); +PROVIDE ( r_co_list_init = 0x40013f14 ); +PROVIDE ( r_co_list_insert_after = 0x40014254 ); +PROVIDE ( r_co_list_insert_before = 0x40014200 ); +PROVIDE ( r_co_list_merge = 0x400141bc ); +PROVIDE ( r_co_list_pool_init = 0x40013f30 ); +PROVIDE ( r_co_list_pop_front = 0x40014028 ); +PROVIDE ( r_co_list_push_back = 0x40013fb8 ); +PROVIDE ( r_co_list_push_front = 0x40013ff4 ); +PROVIDE ( r_co_list_size = 0x400142ac ); +PROVIDE ( r_co_nb_good_channels = 0x40014360 ); +PROVIDE ( r_co_slot_to_duration = 0x40014348 ); +PROVIDE ( r_dbg_init = 0x40014394 ); +PROVIDE ( r_dbg_platform_reset_complete = 0x400143d0 ); +PROVIDE ( r_dbg_swdiag_init = 0x40014470 ); +PROVIDE ( r_dbg_swdiag_read = 0x400144a4 ); +PROVIDE ( r_dbg_swdiag_write = 0x400144d0 ); +PROVIDE ( r_E1 = 0x400108e8 ); +PROVIDE ( r_E21 = 0x40010968 ); +PROVIDE ( r_E22 = 0x400109b4 ); +PROVIDE ( r_E3 = 0x40010a58 ); +PROVIDE ( r_ea_alarm_clear = 0x40015ab4 ); +PROVIDE ( r_ea_alarm_set = 0x40015a10 ); +PROVIDE ( _read_r = 0x4000bda8 ); +PROVIDE ( r_ea_elt_cancel = 0x400150d0 ); +PROVIDE ( r_ea_elt_create = 0x40015264 ); +PROVIDE ( r_ea_elt_insert = 0x400152a8 ); +PROVIDE ( r_ea_elt_remove = 0x400154f0 ); +PROVIDE ( r_ea_finetimer_isr = 0x400155d4 ); +PROVIDE ( r_ea_init = 0x40015228 ); +PROVIDE ( r_ea_interval_create = 0x4001555c ); +PROVIDE ( r_ea_interval_delete = 0x400155a8 ); +PROVIDE ( r_ea_interval_duration_req = 0x4001597c ); +PROVIDE ( r_ea_interval_insert = 0x4001557c ); +PROVIDE ( r_ea_interval_remove = 0x40015590 ); +PROVIDE ( ea_conflict_check = 0x40014e9c ); +PROVIDE ( ea_prog_timer = 0x40014f88 ); +PROVIDE ( realloc = 0x4000becc ); +PROVIDE ( _realloc_r = 0x4000bbe0 ); +PROVIDE ( r_ea_offset_req = 0x40015748 ); +PROVIDE ( r_ea_sleep_check = 0x40015928 ); +PROVIDE ( r_ea_sw_isr = 0x40015724 ); +PROVIDE ( r_ea_time_get_halfslot_rounded = 0x40015894 ); +PROVIDE ( r_ea_time_get_slot_rounded = 0x400158d4 ); +PROVIDE ( r_ecc_abort_key256_generation = 0x40017070 ); +PROVIDE ( r_ecc_generate_key256 = 0x40016e00 ); +PROVIDE ( r_ecc_gen_new_public_key = 0x400170c0 ); +PROVIDE ( r_ecc_gen_new_secret_key = 0x400170e4 ); +PROVIDE ( r_ecc_get_debug_Keys = 0x40017224 ); +PROVIDE ( r_ecc_init = 0x40016dbc ); +PROVIDE ( RecvBuff = 0x3ffe009c ); +PROVIDE ( r_em_buf_init = 0x4001729c ); +PROVIDE ( r_em_buf_rx_buff_addr_get = 0x400173e8 ); +PROVIDE ( r_em_buf_rx_free = 0x400173c4 ); +PROVIDE ( r_em_buf_tx_buff_addr_get = 0x40017404 ); +PROVIDE ( r_em_buf_tx_free = 0x4001741c ); +PROVIDE ( _rename_r = 0x4000bc28 ); +PROVIDE ( r_F1_256 = 0x400133e4 ); +PROVIDE ( r_F2_256 = 0x40013568 ); +PROVIDE ( r_F3_256 = 0x40013664 ); +PROVIDE ( RFPLL_ICP_TABLE = 0x3ffb8b7c ); +PROVIDE ( r_G_256 = 0x40013470 ); +PROVIDE ( r_H3 = 0x40013760 ); +PROVIDE ( r_H4 = 0x40013830 ); +PROVIDE ( r_h4tl_init = 0x40017878 ); +PROVIDE ( r_h4tl_start = 0x40017924 ); +PROVIDE ( r_h4tl_stop = 0x40017934 ); +PROVIDE ( r_h4tl_write = 0x400178d0 ); +PROVIDE ( r_H5 = 0x400138dc ); +PROVIDE ( r_hashConcat = 0x40013a38 ); +PROVIDE ( r_hci_acl_tx_data_alloc = 0x4001951c ); +PROVIDE ( r_hci_acl_tx_data_received = 0x40019654 ); +PROVIDE ( r_hci_bt_acl_bdaddr_register = 0x40018900 ); +PROVIDE ( r_hci_bt_acl_bdaddr_unregister = 0x400189ac ); +PROVIDE ( r_hci_bt_acl_conhdl_register = 0x4001895c ); +PROVIDE ( r_hci_cmd_get_max_param_size = 0x400192d0 ); +PROVIDE ( r_hci_cmd_received = 0x400192f8 ); +PROVIDE ( r_hci_evt_filter_add = 0x40018a64 ); +PROVIDE ( r_hci_evt_mask_set = 0x400189e4 ); +PROVIDE ( r_hci_fc_acl_buf_size_set = 0x40017988 ); +PROVIDE ( r_hci_fc_acl_en = 0x400179d8 ); +PROVIDE ( r_hci_fc_acl_packet_sent = 0x40017a3c ); +PROVIDE ( r_hci_fc_check_host_available_nb_acl_packets = 0x40017aa4 ); +PROVIDE ( r_hci_fc_check_host_available_nb_sync_packets = 0x40017ac8 ); +PROVIDE ( r_hci_fc_host_nb_acl_pkts_complete = 0x40017a6c ); +PROVIDE ( r_hci_fc_host_nb_sync_pkts_complete = 0x40017a88 ); +PROVIDE ( r_hci_fc_init = 0x40017974 ); +PROVIDE ( r_hci_fc_sync_buf_size_set = 0x400179b0 ); +PROVIDE ( r_hci_fc_sync_en = 0x40017a30 ); +PROVIDE ( r_hci_fc_sync_packet_sent = 0x40017a54 ); +PROVIDE ( r_hci_init = 0x40018538 ); +PROVIDE ( r_hci_look_for_cmd_desc = 0x40018454 ); +PROVIDE ( r_hci_look_for_dbg_evt_desc = 0x400184c4 ); +PROVIDE ( r_hci_look_for_evt_desc = 0x400184a0 ); +PROVIDE ( r_hci_look_for_le_evt_desc = 0x400184e0 ); +PROVIDE ( r_hci_reset = 0x4001856c ); +PROVIDE ( r_hci_send_2_host = 0x400185bc ); +PROVIDE ( r_hci_sync_tx_data_alloc = 0x40019754 ); +PROVIDE ( r_hci_sync_tx_data_received = 0x400197c0 ); +PROVIDE ( r_hci_tl_init = 0x40019290 ); +PROVIDE ( r_hci_tl_send = 0x40019228 ); +PROVIDE ( r_hci_util_pack = 0x40019874 ); +PROVIDE ( r_hci_util_unpack = 0x40019998 ); +PROVIDE ( r_hci_voice_settings_get = 0x40018bdc ); +PROVIDE ( r_hci_voice_settings_set = 0x40018be8 ); +PROVIDE ( r_HMAC = 0x40013968 ); +PROVIDE ( r_import_rf_phy_func = 0x3ffb8354 ); +PROVIDE ( r_import_rf_phy_func_p = 0x3ffafd64 ); +PROVIDE ( r_ip_funcs = 0x3ffae710 ); +PROVIDE ( r_ip_funcs_p = 0x3ffae70c ); +PROVIDE ( r_ke_check_malloc = 0x40019de0 ); +PROVIDE ( r_ke_event_callback_set = 0x40019ba8 ); +PROVIDE ( r_ke_event_clear = 0x40019c2c ); +PROVIDE ( r_ke_event_flush = 0x40019ccc ); +PROVIDE ( r_ke_event_get = 0x40019c78 ); +PROVIDE ( r_ke_event_get_all = 0x40019cc0 ); +PROVIDE ( r_ke_event_init = 0x40019b90 ); +PROVIDE ( r_ke_event_schedule = 0x40019cdc ); +PROVIDE ( r_ke_event_set = 0x40019be0 ); +PROVIDE ( r_ke_flush = 0x4001a374 ); +PROVIDE ( r_ke_free = 0x4001a014 ); +PROVIDE ( r_ke_get_max_mem_usage = 0x4001a1c8 ); +PROVIDE ( r_ke_get_mem_usage = 0x4001a1a0 ); +PROVIDE ( r_ke_init = 0x4001a318 ); +PROVIDE ( r_ke_is_free = 0x4001a184 ); +PROVIDE ( r_ke_malloc = 0x40019eb4 ); +PROVIDE ( r_ke_mem_init = 0x40019d3c ); +PROVIDE ( r_ke_mem_is_empty = 0x40019d8c ); +PROVIDE ( r_ke_msg_alloc = 0x4001a1e0 ); +PROVIDE ( r_ke_msg_dest_id_get = 0x4001a2e0 ); +PROVIDE ( r_ke_msg_discard = 0x4001a850 ); +PROVIDE ( r_ke_msg_forward = 0x4001a290 ); +PROVIDE ( r_ke_msg_forward_new_id = 0x4001a2ac ); +PROVIDE ( r_ke_msg_free = 0x4001a2cc ); +PROVIDE ( r_ke_msg_in_queue = 0x4001a2f8 ); +PROVIDE ( r_ke_msg_save = 0x4001a858 ); +PROVIDE ( r_ke_msg_send = 0x4001a234 ); +PROVIDE ( r_ke_msg_send_basic = 0x4001a26c ); +PROVIDE ( r_ke_msg_src_id_get = 0x4001a2ec ); +PROVIDE ( r_ke_queue_extract = 0x40055fd0 ); +PROVIDE ( r_ke_queue_insert = 0x40056020 ); +PROVIDE ( r_ke_sleep_check = 0x4001a3d8 ); +PROVIDE ( r_ke_state_get = 0x4001a7d8 ); +PROVIDE ( r_ke_state_set = 0x4001a6fc ); +PROVIDE ( r_ke_stats_get = 0x4001a3f0 ); +PROVIDE ( r_ke_task_check = 0x4001a8a4 ); +PROVIDE ( r_ke_task_create = 0x4001a674 ); +PROVIDE ( r_ke_task_delete = 0x4001a6c0 ); +PROVIDE ( r_ke_task_init = 0x4001a650 ); +PROVIDE ( r_ke_task_msg_flush = 0x4001a860 ); +PROVIDE ( r_ke_timer_active = 0x4001ac08 ); +PROVIDE ( r_ke_timer_adjust_all = 0x4001ac30 ); +PROVIDE ( r_ke_timer_clear = 0x4001ab90 ); +PROVIDE ( r_ke_timer_init = 0x4001aa9c ); +PROVIDE ( r_ke_timer_set = 0x4001aac0 ); +PROVIDE ( r_ke_timer_sleep_check = 0x4001ac50 ); +PROVIDE ( r_KPrimC = 0x40010ad4 ); +PROVIDE ( r_lb_clk_adj_activate = 0x4001ae70 ); +PROVIDE ( r_lb_clk_adj_id_get = 0x4001af14 ); +PROVIDE ( r_lb_clk_adj_period_update = 0x4001af20 ); +PROVIDE ( r_lb_init = 0x4001acd4 ); +PROVIDE ( r_lb_mst_key = 0x4001afc0 ); +PROVIDE ( r_lb_mst_key_cmp = 0x4001af74 ); +PROVIDE ( r_lb_mst_key_restart_enc = 0x4001b0d4 ); +PROVIDE ( r_lb_mst_start_act_bcst_enc = 0x4001b198 ); +PROVIDE ( r_lb_mst_stop_act_bcst_enc = 0x4001b24c ); +PROVIDE ( r_lb_reset = 0x4001ad38 ); +PROVIDE ( r_lb_send_lmp = 0x4001adbc ); +PROVIDE ( r_lb_send_pdu_clk_adj = 0x4001af3c ); +PROVIDE ( r_lb_util_get_csb_mode = 0x4001ada4 ); +PROVIDE ( r_lb_util_get_nb_broadcast = 0x4001ad80 ); +PROVIDE ( r_lb_util_get_res_lt_addr = 0x4001ad98 ); +PROVIDE ( r_lb_util_set_nb_broadcast = 0x4001ad8c ); +PROVIDE ( r_lc_afh_set = 0x4001cc74 ); +PROVIDE ( r_lc_afh_start = 0x4001d240 ); +PROVIDE ( r_lc_auth_cmp = 0x4001cd54 ); +PROVIDE ( r_lc_calc_link_key = 0x4001ce7c ); +PROVIDE ( r_lc_chg_pkt_type_cmp = 0x4001d038 ); +PROVIDE ( r_lc_chg_pkt_type_cont = 0x4001cfbc ); +PROVIDE ( r_lc_chg_pkt_type_retry = 0x4001d0ac ); +PROVIDE ( r_lc_chk_to = 0x4001d2a8 ); +PROVIDE ( r_lc_cmd_stat_send = 0x4001c914 ); +PROVIDE ( r_lc_comb_key_svr = 0x4001d30c ); +PROVIDE ( r_lc_con_cmp = 0x4001d44c ); +PROVIDE ( r_lc_con_cmp_evt_send = 0x4001d4fc ); +PROVIDE ( r_lc_conn_seq_done = 0x40021334 ); +PROVIDE ( r_lc_detach = 0x4002037c ); +PROVIDE ( r_lc_dhkey = 0x4001d564 ); +PROVIDE ( r_lc_enc_cmp = 0x4001d8bc ); +PROVIDE ( r_lc_enc_key_refresh = 0x4001d720 ); +PROVIDE ( r_lc_end_chk_colli = 0x4001d858 ); +PROVIDE ( r_lc_end_of_sniff_nego = 0x4001d9a4 ); +PROVIDE ( r_lc_enter_sniff_mode = 0x4001ddb8 ); +PROVIDE ( r_lc_epr_change_lk = 0x4001db38 ); +PROVIDE ( r_lc_epr_cmp = 0x4001da88 ); +PROVIDE ( r_lc_epr_resp = 0x4001e0b4 ); +PROVIDE ( r_lc_epr_rsw_cmp = 0x4001dd40 ); +PROVIDE ( r_lc_ext_feat = 0x40020d6c ); +PROVIDE ( r_lc_feat = 0x40020984 ); +PROVIDE ( r_lc_hl_connect = 0x400209e8 ); +PROVIDE ( r_lc_init = 0x4001c948 ); +PROVIDE ( r_lc_init_calc_f3 = 0x4001deb0 ); +PROVIDE ( r_lc_initiator_epr = 0x4001e064 ); +PROVIDE ( r_lc_init_passkey_loop = 0x4001dfc0 ); +PROVIDE ( r_lc_init_start_mutual_auth = 0x4001df60 ); +PROVIDE ( r_lc_key_exch_end = 0x4001e140 ); +PROVIDE ( r_lc_legacy_pair = 0x4001e1c0 ); +PROVIDE ( r_lc_local_switch = 0x4001e22c ); +PROVIDE ( r_lc_local_trans_mode = 0x4001e2e4 ); +PROVIDE ( r_lc_local_untrans_mode = 0x4001e3a0 ); +PROVIDE ( r_lc_loc_auth = 0x40020ecc ); +PROVIDE ( r_lc_locepr_lkref = 0x4001d648 ); +PROVIDE ( r_lc_locepr_rsw = 0x4001d5d0 ); +PROVIDE ( r_lc_loc_sniff = 0x40020a6c ); +PROVIDE ( r_lc_max_slot_mgt = 0x4001e410 ); +PROVIDE ( r_lc_mst_key = 0x4001e7c0 ); +PROVIDE ( r_lc_mst_qos_done = 0x4001ea80 ); +PROVIDE ( r_lc_mst_send_mst_key = 0x4001e8f4 ); +PROVIDE ( r_lc_mutual_auth_end = 0x4001e670 ); +PROVIDE ( r_lc_mutual_auth_end2 = 0x4001e4f4 ); +PROVIDE ( r_lc_packet_type = 0x40021038 ); +PROVIDE ( r_lc_pair = 0x40020ddc ); +PROVIDE ( r_lc_pairing_cont = 0x4001eafc ); +PROVIDE ( r_lc_passkey_comm = 0x4001ed20 ); +PROVIDE ( r_lc_prepare_all_links_for_clk_adj = 0x40021430 ); +PROVIDE ( r_lc_proc_rcv_dhkey = 0x4001edec ); +PROVIDE ( r_lc_ptt = 0x4001ee2c ); +PROVIDE ( r_lc_ptt_cmp = 0x4001eeec ); +PROVIDE ( r_lc_qos_setup = 0x4001ef50 ); +PROVIDE ( r_lc_rd_rem_name = 0x4001efd0 ); +PROVIDE ( r_lc_release = 0x4001f8a8 ); +PROVIDE ( r_lc_rem_enc = 0x4001f124 ); +PROVIDE ( r_lc_rem_name_cont = 0x4001f290 ); +PROVIDE ( r_lc_rem_nego_trans_mode = 0x4001f1b4 ); +PROVIDE ( r_lc_rem_sniff = 0x40020ca4 ); +PROVIDE ( r_lc_rem_sniff_sub_rate = 0x40020b10 ); +PROVIDE ( r_lc_rem_switch = 0x4001f070 ); +PROVIDE ( r_lc_rem_trans_mode = 0x4001f314 ); +PROVIDE ( r_lc_rem_unsniff = 0x400207a0 ); +PROVIDE ( r_lc_rem_untrans_mode = 0x4001f36c ); +PROVIDE ( r_lc_reset = 0x4001c99c ); +PROVIDE ( r_lc_resp_auth = 0x4001f518 ); +PROVIDE ( r_lc_resp_calc_f3 = 0x4001f710 ); +PROVIDE ( r_lc_resp_num_comp = 0x40020074 ); +PROVIDE ( r_lc_resp_oob_nonce = 0x4001f694 ); +PROVIDE ( r_lc_resp_oob_wait_nonce = 0x4001f66c ); +PROVIDE ( r_lc_resp_pair = 0x400208a4 ); +PROVIDE ( r_lc_resp_sec_auth = 0x4001f4a0 ); +PROVIDE ( r_lc_resp_wait_dhkey_cont = 0x4001f86c ); +PROVIDE ( r_lc_restart_enc = 0x4001f8ec ); +PROVIDE ( r_lc_restart_enc_cont = 0x4001f940 ); +PROVIDE ( r_lc_restore_afh_reporting = 0x4001f028 ); +PROVIDE ( r_lc_restore_to = 0x4001f9e0 ); +PROVIDE ( r_lc_ret_sniff_max_slot_chg = 0x4001fa30 ); +PROVIDE ( r_lc_rsw_clean_up = 0x4001dc70 ); +PROVIDE ( r_lc_rsw_done = 0x4001db94 ); +PROVIDE ( r_lc_sco_baseband_ack = 0x40022b00 ); +PROVIDE ( r_lc_sco_detach = 0x40021e40 ); +PROVIDE ( r_lc_sco_host_accept = 0x40022118 ); +PROVIDE ( r_lc_sco_host_reject = 0x400222b8 ); +PROVIDE ( r_lc_sco_host_request = 0x40021f4c ); +PROVIDE ( r_lc_sco_host_request_disc = 0x4002235c ); +PROVIDE ( r_lc_sco_init = 0x40021dc8 ); +PROVIDE ( r_lc_sco_peer_accept = 0x40022780 ); +PROVIDE ( r_lc_sco_peer_accept_disc = 0x40022a08 ); +PROVIDE ( r_lc_sco_peer_reject = 0x40022824 ); +PROVIDE ( r_lc_sco_peer_reject_disc = 0x40022a8c ); +PROVIDE ( r_lc_sco_peer_request = 0x4002240c ); +PROVIDE ( r_lc_sco_peer_request_disc = 0x400228ec ); +PROVIDE ( r_lc_sco_release = 0x40021eec ); +PROVIDE ( r_lc_sco_reset = 0x40021dfc ); +PROVIDE ( r_lc_sco_timeout = 0x40022bd4 ); +PROVIDE ( r_lc_sec_auth_compute_sres = 0x4001f3ec ); +PROVIDE ( r_lc_semi_key_cmp = 0x40020294 ); +PROVIDE ( r_lc_send_enc_chg_evt = 0x4002134c ); +PROVIDE ( r_lc_send_enc_mode = 0x40020220 ); +PROVIDE ( r_lc_send_lmp = 0x4001c1a8 ); +PROVIDE ( r_lc_send_pdu_acc = 0x4001c21c ); +PROVIDE ( r_lc_send_pdu_acc_ext4 = 0x4001c240 ); +PROVIDE ( r_lc_send_pdu_au_rand = 0x4001c308 ); +PROVIDE ( r_lc_send_pdu_auto_rate = 0x4001c5d0 ); +PROVIDE ( r_lc_send_pdu_clk_adj_ack = 0x4001c46c ); +PROVIDE ( r_lc_send_pdu_clk_adj_req = 0x4001c494 ); +PROVIDE ( r_lc_send_pdu_comb_key = 0x4001c368 ); +PROVIDE ( r_lc_send_pdu_dhkey_chk = 0x4001c8e8 ); +PROVIDE ( r_lc_send_pdu_encaps_head = 0x4001c440 ); +PROVIDE ( r_lc_send_pdu_encaps_payl = 0x4001c410 ); +PROVIDE ( r_lc_send_pdu_enc_key_sz_req = 0x4001c670 ); +PROVIDE ( r_lc_send_pdu_esco_lk_rem_req = 0x4001c5a8 ); +PROVIDE ( r_lc_send_pdu_feats_ext_req = 0x4001c6ec ); +PROVIDE ( r_lc_send_pdu_feats_res = 0x4001c694 ); +PROVIDE ( r_lc_send_pdu_in_rand = 0x4001c338 ); +PROVIDE ( r_lc_send_pdu_io_cap_res = 0x4001c72c ); +PROVIDE ( r_lc_send_pdu_lsto = 0x4001c64c ); +PROVIDE ( r_lc_send_pdu_max_slot = 0x4001c3c8 ); +PROVIDE ( r_lc_send_pdu_max_slot_req = 0x4001c3ec ); +PROVIDE ( r_lc_send_pdu_not_acc = 0x4001c26c ); +PROVIDE ( r_lc_send_pdu_not_acc_ext4 = 0x4001c294 ); +PROVIDE ( r_lc_send_pdu_num_comp_fail = 0x4001c770 ); +PROVIDE ( r_lc_send_pdu_pause_enc_aes_req = 0x4001c794 ); +PROVIDE ( r_lc_send_pdu_paus_enc_req = 0x4001c7c0 ); +PROVIDE ( r_lc_send_pdu_ptt_req = 0x4001c4c0 ); +PROVIDE ( r_lc_send_pdu_qos_req = 0x4001c82c ); +PROVIDE ( r_lc_send_pdu_resu_enc_req = 0x4001c7e4 ); +PROVIDE ( r_lc_send_pdu_sco_lk_rem_req = 0x4001c580 ); +PROVIDE ( r_lc_send_pdu_set_afh = 0x4001c2c8 ); +PROVIDE ( r_lc_send_pdu_setup_cmp = 0x4001c808 ); +PROVIDE ( r_lc_send_pdu_slot_off = 0x4001c854 ); +PROVIDE ( r_lc_send_pdu_sniff_req = 0x4001c5f0 ); +PROVIDE ( r_lc_send_pdu_sp_cfm = 0x4001c518 ); +PROVIDE ( r_lc_send_pdu_sp_nb = 0x4001c4e8 ); +PROVIDE ( r_lc_send_pdu_sres = 0x4001c548 ); +PROVIDE ( r_lc_send_pdu_tim_acc = 0x4001c6cc ); +PROVIDE ( r_lc_send_pdu_unit_key = 0x4001c398 ); +PROVIDE ( r_lc_send_pdu_unsniff_req = 0x4001c894 ); +PROVIDE ( r_lc_send_pdu_vers_req = 0x4001c8b4 ); +PROVIDE ( r_lc_skip_hl_oob_req = 0x400201bc ); +PROVIDE ( r_lc_sniff_init = 0x40022cac ); +PROVIDE ( r_lc_sniff_max_slot_chg = 0x40020590 ); +PROVIDE ( r_lc_sniff_reset = 0x40022cc8 ); +PROVIDE ( r_lc_sniff_slot_unchange = 0x40021100 ); +PROVIDE ( r_lc_sniff_sub_mode = 0x400204fc ); +PROVIDE ( r_lc_sp_end = 0x400213a8 ); +PROVIDE ( r_lc_sp_fail = 0x40020470 ); +PROVIDE ( r_lc_sp_oob_tid_fail = 0x400204cc ); +PROVIDE ( r_lc_ssr_nego = 0x4002125c ); +PROVIDE ( r_lc_start = 0x4001ca28 ); +PROVIDE ( r_lc_start_enc = 0x4001fb28 ); +PROVIDE ( r_lc_start_enc_key_size = 0x4001fd9c ); +PROVIDE ( r_lc_start_key_exch = 0x4001fe10 ); +PROVIDE ( r_lc_start_lmp_to = 0x4001fae8 ); +PROVIDE ( r_lc_start_oob = 0x4001fffc ); +PROVIDE ( r_lc_start_passkey = 0x4001feac ); +PROVIDE ( r_lc_start_passkey_loop = 0x4001ff88 ); +PROVIDE ( r_lc_stop_afh_report = 0x40020184 ); +PROVIDE ( r_lc_stop_enc = 0x40020110 ); +PROVIDE ( r_lc_switch_cmp = 0x40020448 ); +PROVIDE ( r_lc_unit_key_svr = 0x400206d8 ); +PROVIDE ( r_lc_unsniff = 0x40020c50 ); +PROVIDE ( r_lc_unsniff_cmp = 0x40020810 ); +PROVIDE ( r_lc_unsniff_cont = 0x40020750 ); +PROVIDE ( r_lc_upd_to = 0x4002065c ); +PROVIDE ( r_lc_util_convert_pref_rate_to_packet_type = 0x4002f9b0 ); +PROVIDE ( r_lc_util_get_max_packet_size = 0x4002f4ac ); +PROVIDE ( r_lc_util_get_offset_clke = 0x4002f538 ); +PROVIDE ( r_lc_util_get_offset_clkn = 0x4002f51c ); +PROVIDE ( r_lc_util_set_loc_trans_coll = 0x4002f500 ); +PROVIDE ( r_lc_version = 0x40020a30 ); +PROVIDE ( lmp_accepted_ext_handler = 0x40027290 ); +PROVIDE ( lmp_not_accepted_ext_handler = 0x40029c54 ); +PROVIDE ( lmp_clk_adj_handler = 0x40027468 ); +PROVIDE ( lmp_clk_adj_ack_handler = 0x400274f4 ); +PROVIDE ( lmp_clk_adj_req_handler = 0x4002751c ); +PROVIDE ( lmp_feats_res_ext_handler = 0x4002cac4 ); +PROVIDE ( lmp_feats_req_ext_handler = 0x4002ccb0 ); +PROVIDE ( lmp_pkt_type_tbl_req_handler = 0x40027574 ); +PROVIDE ( lmp_esco_link_req_handler = 0x40027610 ); +PROVIDE ( lmp_rmv_esco_link_req_handler = 0x400276e8 ); +PROVIDE ( lmp_ch_class_req_handler = 0x40027730 ); +PROVIDE ( lmp_ch_class_handler = 0x4002ca18 ); +PROVIDE ( lmp_ssr_req_handler = 0x4002780c ); +PROVIDE ( lmp_ssr_res_handler = 0x40027900 ); +PROVIDE ( lmp_pause_enc_aes_req_handler = 0x400279a4 ); +PROVIDE ( lmp_pause_enc_req_handler = 0x4002df90 ); +PROVIDE ( lmp_resume_enc_req_handler = 0x4002e084 ); +PROVIDE ( lmp_num_comparison_fail_handler = 0x40027a74 ); +PROVIDE ( lmp_passkey_fail_handler = 0x40027aec ); +PROVIDE ( lmp_keypress_notif_handler = 0x4002c5c8 ); +PROVIDE ( lmp_pwr_ctrl_req_handler = 0x400263bc ); +PROVIDE ( lmp_pwr_ctrl_res_handler = 0x40026480 ); +PROVIDE ( lmp_auto_rate_handler = 0x40026548 ); +PROVIDE ( lmp_pref_rate_handler = 0x4002657c ); +PROVIDE ( lmp_name_req_handler = 0x40025050 ); +PROVIDE ( lmp_name_res_handler = 0x400250bc ); +PROVIDE ( lmp_not_accepted_handler = 0x400251d0 ); +PROVIDE ( lmp_accepted_handler = 0x4002e894 ); +PROVIDE ( lmp_clk_off_req_handler = 0x40025a44 ); +PROVIDE ( lmp_clk_off_res_handler = 0x40025ab8 ); +PROVIDE ( lmp_detach_handler = 0x40025b74 ); +PROVIDE ( lmp_tempkey_handler = 0x4002b6b0 ); +PROVIDE ( lmp_temprand_handler = 0x4002b74c ); +PROVIDE ( lmp_sres_handler = 0x4002b840 ); +PROVIDE ( lmp_aurand_handler = 0x4002bda0 ); +PROVIDE ( lmp_unitkey_handler = 0x4002c13c ); +PROVIDE ( lmp_combkey_handler = 0x4002c234 ); +PROVIDE ( lmp_inrand_handler = 0x4002c414 ); +PROVIDE ( lmp_oob_fail_handler = 0x40027b84 ); +PROVIDE ( lmp_ping_req_handler = 0x40027c08 ); +PROVIDE ( lmp_ping_res_handler = 0x40027c5c ); +PROVIDE ( lmp_enc_mode_req_handler = 0x40025c60 ); +PROVIDE ( lmp_enc_key_size_req_handler = 0x40025e54 ); +PROVIDE ( lmp_switch_req_handler = 0x40025f84 ); +PROVIDE ( lmp_start_enc_req_handler = 0x4002e124 ); +PROVIDE ( lmp_stop_enc_req_handler = 0x4002de30 ); +PROVIDE ( lmp_sniff_req_handler = 0x400260c8 ); +PROVIDE ( lmp_unsniff_req_handler = 0x400261e0 ); +PROVIDE ( lmp_incr_pwr_req_handler = 0x4002629c ); +PROVIDE ( lmp_decr_pwr_req_handler = 0x400262f8 ); +PROVIDE ( lmp_max_pwr_handler = 0x40026354 ); +PROVIDE ( lmp_min_pwr_handler = 0x40026388 ); +PROVIDE ( lmp_ver_req_handler = 0x400265f0 ); +PROVIDE ( lmp_ver_res_handler = 0x40026670 ); +PROVIDE ( lmp_qos_handler = 0x40026790 ); +PROVIDE ( lmp_qos_req_handler = 0x40026844 ); +PROVIDE ( lmp_sco_link_req_handler = 0x40026930 ); +PROVIDE ( lmp_rmv_sco_link_req_handler = 0x40026a10 ); +PROVIDE ( lmp_max_slot_handler = 0x40026a54 ); +PROVIDE ( lmp_max_slot_req_handler = 0x40026aac ); +PROVIDE ( lmp_timing_accu_req_handler = 0x40026b54 ); +PROVIDE ( lmp_timing_accu_res_handler = 0x40026bcc ); +PROVIDE ( lmp_setup_cmp_handler = 0x40026c84 ); +PROVIDE ( lmp_feats_res_handler = 0x4002b548 ); +PROVIDE ( lmp_feats_req_handler = 0x4002b620 ); +PROVIDE ( lmp_host_con_req_handler = 0x4002b3d8 ); +PROVIDE ( lmp_use_semi_perm_key_handler = 0x4002b4c4 ); +PROVIDE ( lmp_slot_off_handler = 0x40026cc8 ); +PROVIDE ( lmp_page_mode_req_handler = 0x40026d0c ); +PROVIDE ( lmp_page_scan_mode_req_handler = 0x40026d4c ); +PROVIDE ( lmp_supv_to_handler = 0x40026d94 ); +PROVIDE ( lmp_test_activate_handler = 0x40026e7c ); +PROVIDE ( lmp_test_ctrl_handler = 0x40026ee4 ); +PROVIDE ( lmp_enc_key_size_mask_req_handler = 0x40027038 ); +PROVIDE ( lmp_enc_key_size_mask_res_handler = 0x400270a4 ); +PROVIDE ( lmp_set_afh_handler = 0x4002b2e4 ); +PROVIDE ( lmp_encaps_hdr_handler = 0x40027120 ); +PROVIDE ( lmp_encaps_payl_handler = 0x4002e590 ); +PROVIDE ( lmp_sp_nb_handler = 0x4002acf0 ); +PROVIDE ( lmp_sp_cfm_handler = 0x4002b170 ); +PROVIDE ( lmp_dhkey_chk_handler = 0x4002ab48 ); +PROVIDE ( lmp_pause_enc_aes_req_handler = 0x400279a4 ); +PROVIDE ( lmp_io_cap_res_handler = 0x4002c670 ); +PROVIDE ( lmp_io_cap_req_handler = 0x4002c7a4 ); +PROVIDE ( ld_acl_tx_packet_type_select = 0x4002fb40 ); +PROVIDE ( ld_acl_sched = 0x40033268 ); +PROVIDE ( ld_acl_sniff_sched = 0x4003340c ); +PROVIDE ( lm_cmd_cmp_send = 0x40051838 ); +PROVIDE ( r_ld_acl_active_hop_types_get = 0x40036e10 ); +PROVIDE ( r_ld_acl_afh_confirm = 0x40036d40 ); +PROVIDE ( r_ld_acl_afh_prepare = 0x40036c84 ); +PROVIDE ( r_ld_acl_afh_set = 0x40036b60 ); +PROVIDE ( r_ld_acl_allowed_tx_packet_types_set = 0x40036810 ); +PROVIDE ( r_ld_acl_bcst_rx_dec = 0x40036394 ); +PROVIDE ( r_ld_acl_bit_off_get = 0x40036b18 ); +PROVIDE ( r_ld_acl_clk_adj_set = 0x40036a00 ); +PROVIDE ( r_ld_acl_clk_off_get = 0x40036b00 ); +PROVIDE ( r_ld_acl_clk_set = 0x40036950 ); +PROVIDE ( r_ld_acl_clock_offset_get = 0x400364c0 ); +PROVIDE ( r_ld_acl_current_tx_power_get = 0x400368f0 ); +PROVIDE ( r_ld_acl_data_flush = 0x400357bc ); +PROVIDE ( r_ld_acl_data_tx = 0x4003544c ); +PROVIDE ( r_ld_acl_edr_set = 0x4003678c ); +PROVIDE ( r_ld_acl_enc_key_load = 0x40036404 ); +PROVIDE ( r_ld_acl_flow_off = 0x40035400 ); +PROVIDE ( r_ld_acl_flow_on = 0x4003541c ); +PROVIDE ( r_ld_acl_flush_timeout_get = 0x40035f9c ); +PROVIDE ( r_ld_acl_flush_timeout_set = 0x40035fe0 ); +PROVIDE ( r_ld_acl_init = 0x40034d08 ); +PROVIDE ( r_ld_acl_lmp_flush = 0x40035d80 ); +PROVIDE ( r_ld_acl_lmp_tx = 0x40035b34 ); +PROVIDE ( r_ld_acl_lsto_get = 0x400366b4 ); +PROVIDE ( r_ld_acl_lsto_set = 0x400366f8 ); +PROVIDE ( r_ld_acl_reset = 0x40034d24 ); +PROVIDE ( r_ld_acl_role_get = 0x40036b30 ); +PROVIDE ( r_ld_acl_rssi_delta_get = 0x40037028 ); +PROVIDE ( r_ld_acl_rsw_req = 0x40035e74 ); +PROVIDE ( r_ld_acl_rx_enc = 0x40036344 ); +PROVIDE ( r_ld_acl_rx_max_slot_get = 0x40036e58 ); +PROVIDE ( r_ld_acl_rx_max_slot_set = 0x40036ea0 ); +PROVIDE ( r_ld_acl_slot_offset_get = 0x4003653c ); +PROVIDE ( r_ld_acl_slot_offset_set = 0x40036658 ); +PROVIDE ( r_ld_acl_sniff = 0x4003617c ); +PROVIDE ( r_ld_acl_sniff_trans = 0x400360a8 ); +PROVIDE ( r_ld_acl_ssr_set = 0x40036274 ); +PROVIDE ( r_ld_acl_start = 0x40034ddc ); +PROVIDE ( r_ld_acl_stop = 0x4003532c ); +PROVIDE ( r_ld_acl_test_mode_set = 0x40036f24 ); +PROVIDE ( r_ld_acl_timing_accuracy_set = 0x4003673c ); +PROVIDE ( r_ld_acl_t_poll_get = 0x40036024 ); +PROVIDE ( r_ld_acl_t_poll_set = 0x40036068 ); +PROVIDE ( r_ld_acl_tx_enc = 0x400362f8 ); +PROVIDE ( r_ld_acl_unsniff = 0x400361e0 ); +PROVIDE ( r_ld_active_check = 0x4003cac4 ); +PROVIDE ( r_ld_afh_ch_assess_data_get = 0x4003caec ); +PROVIDE ( r_ld_bcst_acl_data_tx = 0x40038d3c ); +PROVIDE ( r_ld_bcst_acl_init = 0x40038bd0 ); +PROVIDE ( r_ld_bcst_acl_reset = 0x40038bdc ); +PROVIDE ( r_ld_bcst_acl_start = 0x4003882c ); +PROVIDE ( r_ld_bcst_afh_update = 0x40038f3c ); +PROVIDE ( r_ld_bcst_enc_key_load = 0x4003906c ); +PROVIDE ( r_ld_bcst_lmp_tx = 0x40038bf8 ); +PROVIDE ( r_ld_bcst_tx_enc = 0x40038ff8 ); +PROVIDE ( r_ld_bd_addr_get = 0x4003ca20 ); +PROVIDE ( r_ld_channel_assess = 0x4003c184 ); +PROVIDE ( r_ld_class_of_dev_get = 0x4003ca34 ); +PROVIDE ( r_ld_class_of_dev_set = 0x4003ca50 ); +PROVIDE ( r_ld_csb_rx_afh_update = 0x40039af4 ); +PROVIDE ( r_ld_csb_rx_init = 0x40039690 ); +PROVIDE ( r_ld_csb_rx_reset = 0x4003969c ); +PROVIDE ( r_ld_csb_rx_start = 0x4003972c ); +PROVIDE ( r_ld_csb_rx_stop = 0x40039bb8 ); +PROVIDE ( r_ld_csb_tx_afh_update = 0x4003a5fc ); +PROVIDE ( r_ld_csb_tx_clr_data = 0x4003a71c ); +PROVIDE ( r_ld_csb_tx_dis = 0x4003a5e8 ); +PROVIDE ( r_ld_csb_tx_en = 0x4003a1c0 ); +PROVIDE ( r_ld_csb_tx_init = 0x4003a0e8 ); +PROVIDE ( r_ld_csb_tx_reset = 0x4003a0f8 ); +PROVIDE ( r_ld_csb_tx_set_data = 0x4003a6c0 ); +PROVIDE ( r_ld_fm_clk_isr = 0x4003a7a8 ); +PROVIDE ( r_ld_fm_frame_isr = 0x4003a82c ); +PROVIDE ( r_ld_fm_init = 0x4003a760 ); +PROVIDE ( r_ld_fm_prog_check = 0x4003ab28 ); +PROVIDE ( r_ld_fm_prog_disable = 0x4003a984 ); +PROVIDE ( r_ld_fm_prog_enable = 0x4003a944 ); +PROVIDE ( r_ld_fm_prog_push = 0x4003a9d4 ); +PROVIDE ( r_ld_fm_reset = 0x4003a794 ); +PROVIDE ( r_ld_fm_rx_isr = 0x4003a7f4 ); +PROVIDE ( r_ld_fm_sket_isr = 0x4003a8a4 ); +PROVIDE ( r_ld_init = 0x4003c294 ); +PROVIDE ( r_ld_inq_init = 0x4003b15c ); +PROVIDE ( r_ld_inq_reset = 0x4003b168 ); +PROVIDE ( r_ld_inq_start = 0x4003b1f0 ); +PROVIDE ( r_ld_inq_stop = 0x4003b4f0 ); +PROVIDE ( r_ld_iscan_eir_get = 0x4003c118 ); +PROVIDE ( r_ld_iscan_eir_set = 0x4003bfa0 ); +PROVIDE ( r_ld_iscan_init = 0x4003b9f0 ); +PROVIDE ( r_ld_iscan_reset = 0x4003ba14 ); +PROVIDE ( r_ld_iscan_restart = 0x4003ba44 ); +PROVIDE ( r_ld_iscan_start = 0x4003bb28 ); +PROVIDE ( r_ld_iscan_stop = 0x4003bf1c ); +PROVIDE ( r_ld_iscan_tx_pwr_get = 0x4003c138 ); +PROVIDE ( r_ld_page_init = 0x4003d808 ); +PROVIDE ( r_ld_page_reset = 0x4003d814 ); +PROVIDE ( r_ld_page_start = 0x4003d848 ); +PROVIDE ( r_ld_page_stop = 0x4003da54 ); +PROVIDE ( r_ld_pca_coarse_clock_adjust = 0x4003e324 ); +PROVIDE ( r_ld_pca_init = 0x4003deb4 ); +PROVIDE ( r_ld_pca_initiate_clock_dragging = 0x4003e4ac ); +PROVIDE ( r_ld_pca_local_config = 0x4003df6c ); +PROVIDE ( r_ld_pca_mws_frame_sync = 0x4003e104 ); +PROVIDE ( r_ld_pca_mws_moment_offset_gt = 0x4003e278 ); +PROVIDE ( r_ld_pca_mws_moment_offset_lt = 0x4003e280 ); +PROVIDE ( r_ld_pca_reporting_enable = 0x4003e018 ); +PROVIDE ( r_ld_pca_reset = 0x4003df0c ); +PROVIDE ( r_ld_pca_update_target_offset = 0x4003e050 ); +PROVIDE ( r_ld_pscan_evt_handler = 0x4003f238 ); +PROVIDE ( r_ld_pscan_init = 0x4003f474 ); +PROVIDE ( r_ld_pscan_reset = 0x4003f498 ); +PROVIDE ( r_ld_pscan_restart = 0x4003f4b8 ); +PROVIDE ( r_ld_pscan_start = 0x4003f514 ); +PROVIDE ( r_ld_pscan_stop = 0x4003f618 ); +PROVIDE ( r_ld_read_clock = 0x4003c9e4 ); +PROVIDE ( r_ld_reset = 0x4003c714 ); +PROVIDE ( r_ld_sched_acl_add = 0x4003f978 ); +PROVIDE ( r_ld_sched_acl_remove = 0x4003f99c ); +PROVIDE ( r_ld_sched_compute = 0x4003f6f8 ); +PROVIDE ( r_ld_sched_init = 0x4003f7ac ); +PROVIDE ( r_ld_sched_inq_add = 0x4003f8a8 ); +PROVIDE ( r_ld_sched_inq_remove = 0x4003f8d0 ); +PROVIDE ( r_ld_sched_iscan_add = 0x4003f7e8 ); +PROVIDE ( r_ld_sched_iscan_remove = 0x4003f808 ); +PROVIDE ( r_ld_sched_page_add = 0x4003f910 ); +PROVIDE ( r_ld_sched_page_remove = 0x4003f938 ); +PROVIDE ( r_ld_sched_pscan_add = 0x4003f828 ); +PROVIDE ( r_ld_sched_pscan_remove = 0x4003f848 ); +PROVIDE ( r_ld_sched_reset = 0x4003f7d4 ); +PROVIDE ( r_ld_sched_sco_add = 0x4003fa4c ); +PROVIDE ( r_ld_sched_sco_remove = 0x4003fa9c ); +PROVIDE ( r_ld_sched_sniff_add = 0x4003f9c4 ); +PROVIDE ( r_ld_sched_sniff_remove = 0x4003fa0c ); +PROVIDE ( r_ld_sched_sscan_add = 0x4003f868 ); +PROVIDE ( r_ld_sched_sscan_remove = 0x4003f888 ); +PROVIDE ( r_ld_sco_audio_isr = 0x40037cc8 ); +PROVIDE ( r_ld_sco_data_tx = 0x40037ee8 ); +PROVIDE ( r_ld_sco_start = 0x40037110 ); +PROVIDE ( r_ld_sco_stop = 0x40037c40 ); +PROVIDE ( r_ld_sco_update = 0x40037a74 ); +PROVIDE ( r_ld_sscan_activated = 0x4004031c ); +PROVIDE ( r_ld_sscan_init = 0x400402f0 ); +PROVIDE ( r_ld_sscan_reset = 0x400402fc ); +PROVIDE ( r_ld_sscan_start = 0x40040384 ); +PROVIDE ( r_ld_strain_init = 0x400409f4 ); +PROVIDE ( r_ld_strain_reset = 0x40040a00 ); +PROVIDE ( r_ld_strain_start = 0x40040a8c ); +PROVIDE ( r_ld_strain_stop = 0x40040df0 ); +PROVIDE ( r_ld_timing_accuracy_get = 0x4003caac ); +PROVIDE ( r_ld_util_active_master_afh_map_get = 0x4004131c ); +PROVIDE ( r_ld_util_active_master_afh_map_set = 0x40041308 ); +PROVIDE ( r_ld_util_bch_create = 0x40040fcc ); +PROVIDE ( r_ld_util_fhs_pk = 0x400411c8 ); +PROVIDE ( r_ld_util_fhs_unpk = 0x40040e54 ); +PROVIDE ( r_ld_util_stp_pk = 0x400413f4 ); +PROVIDE ( r_ld_util_stp_unpk = 0x40041324 ); +PROVIDE ( r_ld_version_get = 0x4003ca6c ); +PROVIDE ( r_ld_wlcoex_set = 0x4003caf8 ); +PROVIDE ( r_llc_ch_assess_get_current_ch_map = 0x40041574 ); +PROVIDE ( r_llc_ch_assess_get_local_ch_map = 0x4004150c ); +PROVIDE ( r_llc_ch_assess_local = 0x40041494 ); +PROVIDE ( r_llc_ch_assess_merge_ch = 0x40041588 ); +PROVIDE ( r_llc_ch_assess_reass_ch = 0x400415c0 ); +PROVIDE ( r_llc_common_cmd_complete_send = 0x40044eac ); +PROVIDE ( r_llc_common_cmd_status_send = 0x40044ee0 ); +PROVIDE ( r_llc_common_enc_change_evt_send = 0x40044f6c ); +PROVIDE ( r_llc_common_enc_key_ref_comp_evt_send = 0x40044f38 ); +PROVIDE ( r_llc_common_flush_occurred_send = 0x40044f0c ); +PROVIDE ( r_llc_common_nb_of_pkt_comp_evt_send = 0x40045000 ); +PROVIDE ( r_llc_con_update_complete_send = 0x40044d68 ); +PROVIDE ( r_llc_con_update_finished = 0x4004518c ); +PROVIDE ( r_llc_con_update_ind = 0x40045038 ); +PROVIDE ( r_llc_discon_event_complete_send = 0x40044a30 ); +PROVIDE ( r_llc_end_evt_defer = 0x40046330 ); +PROVIDE ( r_llc_feats_rd_event_send = 0x40044e0c ); +PROVIDE ( r_llc_init = 0x40044778 ); +PROVIDE ( r_llc_le_con_cmp_evt_send = 0x40044a78 ); +PROVIDE ( r_llc_llcp_ch_map_update_pdu_send = 0x40043f94 ); +PROVIDE ( r_llc_llcp_con_param_req_pdu_send = 0x400442fc ); +PROVIDE ( r_llc_llcp_con_param_rsp_pdu_send = 0x40044358 ); +PROVIDE ( r_llc_llcp_con_update_pdu_send = 0x400442c4 ); +PROVIDE ( r_llc_llcp_enc_req_pdu_send = 0x40044064 ); +PROVIDE ( r_llc_llcp_enc_rsp_pdu_send = 0x40044160 ); +PROVIDE ( r_llc_llcp_feats_req_pdu_send = 0x400443b4 ); +PROVIDE ( r_llc_llcp_feats_rsp_pdu_send = 0x400443f0 ); +PROVIDE ( r_llc_llcp_get_autorize = 0x4004475c ); +PROVIDE ( r_llc_llcp_length_req_pdu_send = 0x40044574 ); +PROVIDE ( r_llc_llcp_length_rsp_pdu_send = 0x400445ac ); +PROVIDE ( r_llc_llcp_pause_enc_req_pdu_send = 0x40043fd8 ); +PROVIDE ( r_llc_llcp_pause_enc_rsp_pdu_send = 0x40044010 ); +PROVIDE ( r_llc_llcp_ping_req_pdu_send = 0x4004454c ); +PROVIDE ( r_llc_llcp_ping_rsp_pdu_send = 0x40044560 ); +PROVIDE ( r_llc_llcp_recv_handler = 0x40044678 ); +PROVIDE ( r_llc_llcp_reject_ind_pdu_send = 0x4004425c ); +PROVIDE ( r_llc_llcp_start_enc_req_pdu_send = 0x4004441c ); +PROVIDE ( r_llc_llcp_start_enc_rsp_pdu_send = 0x400441f8 ); +PROVIDE ( r_llc_llcp_terminate_ind_pdu_send = 0x400444b0 ); +PROVIDE ( r_llc_llcp_tester_send = 0x400445e4 ); +PROVIDE ( r_llc_llcp_unknown_rsp_send_pdu = 0x40044534 ); +PROVIDE ( r_llc_llcp_version_ind_pdu_send = 0x40043f6c ); +PROVIDE ( r_llc_lsto_con_update = 0x40045098 ); +PROVIDE ( r_llc_ltk_req_send = 0x40044dc0 ); +PROVIDE ( r_llc_map_update_finished = 0x40045260 ); +PROVIDE ( r_llc_map_update_ind = 0x400450f0 ); +PROVIDE ( r_llc_pdu_acl_tx_ack_defer = 0x400464dc ); +PROVIDE ( r_llc_pdu_defer = 0x40046528 ); +PROVIDE ( r_llc_pdu_llcp_tx_ack_defer = 0x400463ac ); +PROVIDE ( r_llc_reset = 0x400447b8 ); +PROVIDE ( r_llc_start = 0x400447f4 ); +PROVIDE ( r_llc_stop = 0x400449ac ); +PROVIDE ( r_llc_util_bw_mgt = 0x4004629c ); +PROVIDE ( r_llc_util_clear_operation_ptr = 0x40046234 ); +PROVIDE ( r_llc_util_dicon_procedure = 0x40046130 ); +PROVIDE ( r_llc_util_get_free_conhdl = 0x400460c8 ); +PROVIDE ( r_llc_util_get_nb_active_link = 0x40046100 ); +PROVIDE ( r_llc_util_set_auth_payl_to_margin = 0x400461f4 ); +PROVIDE ( r_llc_util_set_llcp_discard_enable = 0x400461c8 ); +PROVIDE ( r_llc_util_update_channel_map = 0x400461ac ); +PROVIDE ( r_llc_version_rd_event_send = 0x40044e60 ); +PROVIDE ( r_lld_adv_start = 0x40048b38 ); +PROVIDE ( r_lld_adv_stop = 0x40048ea0 ); +PROVIDE ( r_lld_ch_map_ind = 0x4004a2f4 ); +PROVIDE ( r_lld_con_param_req = 0x40049f0c ); +PROVIDE ( r_lld_con_param_rsp = 0x40049e00 ); +PROVIDE ( r_lld_con_start = 0x400491f8 ); +PROVIDE ( r_lld_con_stop = 0x40049fdc ); +PROVIDE ( r_lld_con_update_after_param_req = 0x40049bcc ); +PROVIDE ( r_lld_con_update_ind = 0x4004a30c ); +PROVIDE ( r_lld_con_update_req = 0x40049b60 ); +PROVIDE ( r_lld_core_reset = 0x40048a9c ); +PROVIDE ( r_lld_crypt_isr = 0x4004a324 ); +PROVIDE ( r_lld_evt_adv_create = 0x400481f4 ); +PROVIDE ( r_lld_evt_canceled = 0x400485c8 ); +PROVIDE ( r_lld_evt_channel_next = 0x40046aac ); +PROVIDE ( r_lld_evt_deffered_elt_handler = 0x400482bc ); +PROVIDE ( r_lld_evt_delete_elt_handler = 0x40046974 ); +PROVIDE ( r_lld_evt_delete_elt_push = 0x40046a3c ); +PROVIDE ( r_lld_evt_drift_compute = 0x40047670 ); +PROVIDE ( r_lld_evt_elt_delete = 0x40047538 ); +PROVIDE ( r_lld_evt_elt_insert = 0x400474c8 ); +PROVIDE ( r_lld_evt_end = 0x400483e8 ); +PROVIDE ( r_lld_evt_end_isr = 0x4004862c ); +PROVIDE ( r_lld_evt_init = 0x40046b3c ); +PROVIDE ( r_lld_evt_init_evt = 0x40046cd0 ); +PROVIDE ( r_lld_evt_move_to_master = 0x40047ba0 ); +PROVIDE ( r_lld_evt_move_to_slave = 0x40047e18 ); +PROVIDE ( r_lld_evt_prevent_stop = 0x40047adc ); +PROVIDE ( r_lld_evt_restart = 0x40046d50 ); +PROVIDE ( r_lld_evt_rx = 0x40048578 ); +PROVIDE ( r_lld_evt_rx_isr = 0x40048678 ); +PROVIDE ( r_lld_evt_scan_create = 0x40047ae8 ); +PROVIDE ( r_lld_evt_schedule = 0x40047908 ); +PROVIDE ( r_lld_evt_schedule_next = 0x400477dc ); +PROVIDE ( r_lld_evt_schedule_next_instant = 0x400476a8 ); +PROVIDE ( r_lld_evt_slave_update = 0x40048138 ); +PROVIDE ( r_lld_evt_update_create = 0x40047cd8 ); +PROVIDE ( r_lld_get_mode = 0x40049ff8 ); +PROVIDE ( r_lld_init = 0x4004873c ); +PROVIDE ( r_lld_move_to_master = 0x400499e0 ); +PROVIDE ( r_lld_move_to_slave = 0x4004a024 ); +PROVIDE ( r_lld_pdu_adv_pack = 0x4004b488 ); +PROVIDE ( r_lld_pdu_check = 0x4004ac34 ); +PROVIDE ( r_lld_pdu_data_send = 0x4004b018 ); +PROVIDE ( r_lld_pdu_data_tx_push = 0x4004aecc ); +PROVIDE ( r_lld_pdu_rx_handler = 0x4004b4d4 ); +PROVIDE ( r_lld_pdu_send_packet = 0x4004b774 ); +PROVIDE ( r_lld_pdu_tx_flush = 0x4004b414 ); +PROVIDE ( r_lld_pdu_tx_loop = 0x4004ae40 ); +PROVIDE ( r_lld_pdu_tx_prog = 0x4004b120 ); +PROVIDE ( r_lld_pdu_tx_push = 0x4004b080 ); +PROVIDE ( r_lld_ral_renew_req = 0x4004a73c ); +PROVIDE ( r_lld_scan_start = 0x40048ee0 ); +PROVIDE ( r_lld_scan_stop = 0x40049190 ); +PROVIDE ( r_lld_test_mode_rx = 0x4004a540 ); +PROVIDE ( r_lld_test_mode_tx = 0x4004a350 ); +PROVIDE ( r_lld_test_stop = 0x4004a710 ); +PROVIDE ( r_lld_util_anchor_point_move = 0x4004bacc ); +PROVIDE ( r_lld_util_compute_ce_max = 0x4004bc0c ); +PROVIDE ( r_lld_util_connection_param_set = 0x4004ba40 ); +PROVIDE ( r_lld_util_dle_set_cs_fields = 0x4004ba90 ); +PROVIDE ( r_lld_util_eff_tx_time_set = 0x4004bd88 ); +PROVIDE ( r_lld_util_elt_programmed = 0x4004bce0 ); +PROVIDE ( r_lld_util_flush_list = 0x4004bbd8 ); +PROVIDE ( r_lld_util_freq2chnl = 0x4004b9e4 ); +PROVIDE ( r_lld_util_get_bd_address = 0x4004b8ac ); +PROVIDE ( r_lld_util_get_local_offset = 0x4004ba10 ); +PROVIDE ( r_lld_util_get_peer_offset = 0x4004ba24 ); +PROVIDE ( r_lld_util_get_tx_pkt_cnt = 0x4004bd80 ); +PROVIDE ( r_lld_util_instant_get = 0x4004b890 ); +PROVIDE ( r_lld_util_instant_ongoing = 0x4004bbfc ); +PROVIDE ( r_lld_util_priority_set = 0x4004bd10 ); +PROVIDE ( r_lld_util_priority_update = 0x4004bd78 ); +PROVIDE ( r_lld_util_ral_force_rpa_renew = 0x4004b980 ); +PROVIDE ( r_lld_util_set_bd_address = 0x4004b8f8 ); +PROVIDE ( r_lld_wlcoex_set = 0x4004bd98 ); +PROVIDE ( r_llm_ble_ready = 0x4004cc34 ); +PROVIDE ( r_llm_common_cmd_complete_send = 0x4004d288 ); +PROVIDE ( r_llm_common_cmd_status_send = 0x4004d2b4 ); +PROVIDE ( r_llm_con_req_ind = 0x4004cc54 ); +PROVIDE ( r_llm_con_req_tx_cfm = 0x4004d158 ); +PROVIDE ( r_llm_create_con = 0x4004de78 ); +PROVIDE ( r_llm_encryption_done = 0x4004dff8 ); +PROVIDE ( r_llm_encryption_start = 0x4004e128 ); +PROVIDE ( r_llm_end_evt_defer = 0x4004eb6c ); +PROVIDE ( r_llm_init = 0x4004c9f8 ); +PROVIDE ( r_llm_le_adv_report_ind = 0x4004cdf4 ); +PROVIDE ( r_llm_pdu_defer = 0x4004ec48 ); +PROVIDE ( r_llm_ral_clear = 0x4004e1fc ); +PROVIDE ( r_llm_ral_dev_add = 0x4004e23c ); +PROVIDE ( r_llm_ral_dev_rm = 0x4004e3bc ); +PROVIDE ( r_llm_ral_get_rpa = 0x4004e400 ); +PROVIDE ( r_llm_ral_set_timeout = 0x4004e4a0 ); +PROVIDE ( r_llm_ral_update = 0x4004e4f8 ); +PROVIDE ( r_llm_set_adv_data = 0x4004d960 ); +PROVIDE ( r_llm_set_adv_en = 0x4004d7ec ); +PROVIDE ( r_llm_set_adv_param = 0x4004d5f4 ); +PROVIDE ( r_llm_set_scan_en = 0x4004db64 ); +PROVIDE ( r_llm_set_scan_param = 0x4004dac8 ); +PROVIDE ( r_llm_set_scan_rsp_data = 0x4004da14 ); +PROVIDE ( r_llm_test_mode_start_rx = 0x4004d534 ); +PROVIDE ( r_llm_test_mode_start_tx = 0x4004d2fc ); +PROVIDE ( r_llm_util_adv_data_update = 0x4004e8fc ); +PROVIDE ( r_llm_util_apply_bd_addr = 0x4004e868 ); +PROVIDE ( r_llm_util_bd_addr_in_ral = 0x4004eb08 ); +PROVIDE ( r_llm_util_bd_addr_in_wl = 0x4004e788 ); +PROVIDE ( r_llm_util_bd_addr_wl_position = 0x4004e720 ); +PROVIDE ( r_llm_util_bl_add = 0x4004e9ac ); +PROVIDE ( r_llm_util_bl_check = 0x4004e930 ); +PROVIDE ( r_llm_util_bl_rem = 0x4004ea70 ); +PROVIDE ( r_llm_util_check_address_validity = 0x4004e7e4 ); +PROVIDE ( r_llm_util_check_evt_mask = 0x4004e8b0 ); +PROVIDE ( r_llm_util_check_map_validity = 0x4004e800 ); +PROVIDE ( r_llm_util_get_channel_map = 0x4004e8d4 ); +PROVIDE ( r_llm_util_get_supp_features = 0x4004e8e8 ); +PROVIDE ( r_llm_util_set_public_addr = 0x4004e89c ); +PROVIDE ( r_llm_wl_clr = 0x4004dc54 ); +PROVIDE ( r_llm_wl_dev_add = 0x4004dcc0 ); +PROVIDE ( r_llm_wl_dev_add_hdl = 0x4004dd38 ); +PROVIDE ( r_llm_wl_dev_rem = 0x4004dcfc ); +PROVIDE ( r_llm_wl_dev_rem_hdl = 0x4004dde0 ); +PROVIDE ( r_lm_acl_disc = 0x4004f148 ); +PROVIDE ( r_LM_AddSniff = 0x40022d20 ); +PROVIDE ( r_lm_add_sync = 0x40051358 ); +PROVIDE ( r_lm_afh_activate_timer = 0x4004f444 ); +PROVIDE ( r_lm_afh_ch_ass_en_get = 0x4004f3f8 ); +PROVIDE ( r_lm_afh_host_ch_class_get = 0x4004f410 ); +PROVIDE ( r_lm_afh_master_ch_map_get = 0x4004f43c ); +PROVIDE ( r_lm_afh_peer_ch_class_set = 0x4004f418 ); +PROVIDE ( r_lm_check_active_sync = 0x40051334 ); +PROVIDE ( r_LM_CheckEdrFeatureRequest = 0x4002f90c ); +PROVIDE ( r_LM_CheckSwitchInstant = 0x4002f8c0 ); +PROVIDE ( r_lm_check_sync_hl_rsp = 0x4005169c ); +PROVIDE ( r_lm_clk_adj_ack_pending_clear = 0x4004f514 ); +PROVIDE ( r_lm_clk_adj_instant_pending_set = 0x4004f4d8 ); +PROVIDE ( r_LM_ComputePacketType = 0x4002f554 ); +PROVIDE ( r_LM_ComputeSniffSubRate = 0x400233ac ); +PROVIDE ( r_lm_debug_key_compare_192 = 0x4004f3a8 ); +PROVIDE ( r_lm_debug_key_compare_256 = 0x4004f3d0 ); +PROVIDE ( r_lm_dhkey_calc_init = 0x40013234 ); +PROVIDE ( r_lm_dhkey_compare = 0x400132d8 ); +PROVIDE ( r_lm_dut_mode_en_get = 0x4004f3ec ); +PROVIDE ( r_LM_ExtractMaxEncKeySize = 0x4001aca4 ); +PROVIDE ( r_lm_f1 = 0x40012bb8 ); +PROVIDE ( r_lm_f2 = 0x40012cfc ); +PROVIDE ( r_lm_f3 = 0x40013050 ); +PROVIDE ( r_lm_g = 0x40012f90 ); +PROVIDE ( r_LM_GetAFHSwitchInstant = 0x4002f86c ); +PROVIDE ( r_lm_get_auth_en = 0x4004f1ac ); +PROVIDE ( r_lm_get_common_pkt_types = 0x4002fa1c ); +PROVIDE ( r_LM_GetConnectionAcceptTimeout = 0x4004f1f4 ); +PROVIDE ( r_LM_GetFeature = 0x4002f924 ); +PROVIDE ( r_LM_GetLinkTimeout = 0x400233ec ); +PROVIDE ( r_LM_GetLocalNameSeg = 0x4004f200 ); +PROVIDE ( r_lm_get_loopback_mode = 0x4004f248 ); +PROVIDE ( r_LM_GetMasterEncKeySize = 0x4001b29c ); +PROVIDE ( r_LM_GetMasterEncRand = 0x4001b288 ); +PROVIDE ( r_LM_GetMasterKey = 0x4001b260 ); +PROVIDE ( r_LM_GetMasterKeyRand = 0x4001b274 ); +PROVIDE ( r_lm_get_min_sync_intv = 0x400517a8 ); +PROVIDE ( r_lm_get_nb_acl = 0x4004ef9c ); +PROVIDE ( r_lm_get_nb_sync_link = 0x4005179c ); +PROVIDE ( r_lm_get_nonce = 0x400131c4 ); +PROVIDE ( r_lm_get_oob_local_commit = 0x4004f374 ); +PROVIDE ( r_lm_get_oob_local_data_192 = 0x4004f2d4 ); +PROVIDE ( r_lm_get_oob_local_data_256 = 0x4004f318 ); +PROVIDE ( r_LM_GetPINType = 0x4004f1e8 ); +PROVIDE ( r_lm_get_priv_key_192 = 0x4004f278 ); +PROVIDE ( r_lm_get_priv_key_256 = 0x4004f2b8 ); +PROVIDE ( r_lm_get_pub_key_192 = 0x4004f258 ); +PROVIDE ( r_lm_get_pub_key_256 = 0x4004f298 ); +PROVIDE ( r_LM_GetQoSParam = 0x4002f6e0 ); +PROVIDE ( r_lm_get_sec_con_host_supp = 0x4004f1d4 ); +PROVIDE ( r_LM_GetSniffSubratingParam = 0x4002325c ); +PROVIDE ( r_lm_get_sp_en = 0x4004f1c0 ); +PROVIDE ( r_LM_GetSwitchInstant = 0x4002f7f8 ); +PROVIDE ( r_lm_get_synchdl = 0x4005175c ); +PROVIDE ( r_lm_get_sync_param = 0x400503b4 ); +PROVIDE ( r_lm_init = 0x4004ed34 ); +PROVIDE ( r_lm_init_sync = 0x400512d8 ); +PROVIDE ( r_lm_is_acl_con = 0x4004f47c ); +PROVIDE ( r_lm_is_acl_con_role = 0x4004f49c ); +PROVIDE ( r_lm_is_clk_adj_ack_pending = 0x4004f4e8 ); +PROVIDE ( r_lm_is_clk_adj_instant_pending = 0x4004f4c8 ); +PROVIDE ( r_lm_local_ext_fr_configured = 0x4004f540 ); +PROVIDE ( r_lm_look_for_stored_link_key = 0x4002f948 ); +PROVIDE ( r_lm_look_for_sync = 0x40051774 ); +PROVIDE ( r_lm_lt_addr_alloc = 0x4004ef1c ); +PROVIDE ( r_lm_lt_addr_free = 0x4004ef74 ); +PROVIDE ( r_lm_lt_addr_reserve = 0x4004ef48 ); +PROVIDE ( r_LM_MakeCof = 0x4002f84c ); +PROVIDE ( r_LM_MakeRandVec = 0x400112d8 ); +PROVIDE ( r_lm_master_clk_adj_req_handler = 0x40054180 ); +PROVIDE ( r_LM_MaxSlot = 0x4002f694 ); +PROVIDE ( r_lm_modif_sync = 0x40051578 ); +PROVIDE ( r_lm_n_is_zero = 0x40012170 ); +PROVIDE ( r_lm_num_clk_adj_ack_pending_set = 0x4004f500 ); +PROVIDE ( r_lm_oob_f1 = 0x40012e54 ); +PROVIDE ( r_lm_pca_sscan_link_get = 0x4004f560 ); +PROVIDE ( r_lm_pca_sscan_link_set = 0x4004f550 ); +PROVIDE ( nvds_null_read = 0x400542a0 ); +PROVIDE ( nvds_null_write = 0x400542a8 ); +PROVIDE ( nvds_null_erase = 0x400542b0 ); +PROVIDE ( nvds_read = 0x400542c4 ); +PROVIDE ( nvds_write = 0x400542fc ); +PROVIDE ( nvds_erase = 0x40054334 ); +PROVIDE ( nvds_init_memory = 0x40054358 ); +PROVIDE ( r_lmp_pack = 0x4001135c ); +PROVIDE ( r_lmp_unpack = 0x4001149c ); +PROVIDE ( r_lm_read_features = 0x4004f0d8 ); +PROVIDE ( r_LM_RemoveSniff = 0x40023124 ); +PROVIDE ( r_LM_RemoveSniffSubrating = 0x400233c4 ); +PROVIDE ( r_lm_remove_sync = 0x400517c8 ); +PROVIDE ( r_lm_reset_sync = 0x40051304 ); +PROVIDE ( r_lm_role_switch_finished = 0x4004f028 ); +PROVIDE ( r_lm_role_switch_start = 0x4004efe0 ); +PROVIDE ( r_lm_sco_nego_end = 0x40051828 ); +PROVIDE ( r_LM_SniffSubrateNegoRequired = 0x40023334 ); +PROVIDE ( r_LM_SniffSubratingHlReq = 0x40023154 ); +PROVIDE ( r_LM_SniffSubratingPeerReq = 0x400231dc ); +PROVIDE ( r_lm_sp_debug_mode_get = 0x4004f398 ); +PROVIDE ( r_lm_sp_n192_convert_wnaf = 0x400123c0 ); +PROVIDE ( r_lm_sp_n_one = 0x400123a4 ); +PROVIDE ( r_lm_sp_p192_add = 0x40012828 ); +PROVIDE ( r_lm_sp_p192_dbl = 0x4001268c ); +PROVIDE ( r_lm_sp_p192_invert = 0x40012b6c ); +PROVIDE ( r_lm_sp_p192_point_jacobian_to_affine = 0x40012468 ); +PROVIDE ( r_lm_sp_p192_points_jacobian_to_affine = 0x400124e4 ); +PROVIDE ( r_lm_sp_p192_point_to_inf = 0x40012458 ); +PROVIDE ( r_lm_sp_pre_compute_points = 0x40012640 ); +PROVIDE ( r_lm_sp_sha256_calculate = 0x400121a0 ); +PROVIDE ( r_LM_SuppressAclPacket = 0x4002f658 ); +PROVIDE ( r_lm_sync_flow_ctrl_en_get = 0x4004f404 ); +PROVIDE ( r_LM_UpdateAclEdrPacketType = 0x4002f5d8 ); +PROVIDE ( r_LM_UpdateAclPacketType = 0x4002f584 ); +PROVIDE ( r_modules_funcs = 0x3ffafd6c ); +PROVIDE ( r_modules_funcs_p = 0x3ffafd68 ); +PROVIDE ( r_nvds_del = 0x400544c4 ); +PROVIDE ( r_nvds_get = 0x40054488 ); +PROVIDE ( r_nvds_init = 0x40054410 ); +PROVIDE ( r_nvds_lock = 0x400544fc ); +PROVIDE ( r_nvds_put = 0x40054534 ); +PROVIDE ( rom_abs_temp = 0x400054f0 ); +PROVIDE ( rom_bb_bss_bw_40_en = 0x4000401c ); +PROVIDE ( rom_bb_bss_cbw40_dig = 0x40003bac ); +PROVIDE ( rom_bb_rx_ht20_cen_bcov_en = 0x40003734 ); +PROVIDE ( rom_bb_tx_ht20_cen = 0x40003760 ); +PROVIDE ( rom_bb_wdg_test_en = 0x40003b70 ); +PROVIDE ( rom_cbw2040_cfg = 0x400040b0 ); +PROVIDE ( rom_check_noise_floor = 0x40003c78 ); +PROVIDE ( rom_chip_i2c_readReg = 0x40004110 ); +PROVIDE ( rom_chip_i2c_writeReg = 0x40004168 ); +PROVIDE ( rom_chip_v7_bt_init = 0x40004d8c ); +PROVIDE ( rom_chip_v7_rx_init = 0x40004cec ); +PROVIDE ( rom_chip_v7_rx_rifs_en = 0x40003d90 ); +PROVIDE ( rom_chip_v7_tx_init = 0x40004d18 ); +PROVIDE ( rom_clk_force_on_vit = 0x40003710 ); +PROVIDE ( rom_correct_rf_ana_gain = 0x400062a8 ); +PROVIDE ( rom_dc_iq_est = 0x400055c8 ); +PROVIDE ( rom_disable_agc = 0x40002fa4 ); +PROVIDE ( rom_enable_agc = 0x40002fcc ); +PROVIDE ( rom_en_pwdet = 0x4000506c ); +PROVIDE ( rom_gen_rx_gain_table = 0x40003e3c ); +PROVIDE ( rom_get_data_sat = 0x4000312c ); +PROVIDE ( rom_get_fm_sar_dout = 0x40005204 ); +PROVIDE ( rom_get_power_db = 0x40005fc8 ); +PROVIDE ( rom_get_pwctrl_correct = 0x400065d4 ); +PROVIDE ( rom_get_rfcal_rxiq_data = 0x40005bbc ); +PROVIDE ( rom_get_rf_gain_qdb = 0x40006290 ); +PROVIDE ( rom_get_sar_dout = 0x40006564 ); +PROVIDE ( rom_i2c_readReg = 0x40004148 ); +PROVIDE ( rom_i2c_readReg_Mask = 0x400041c0 ); +PROVIDE ( rom_i2c_writeReg = 0x400041a4 ); +PROVIDE ( rom_i2c_writeReg_Mask = 0x400041fc ); +PROVIDE ( rom_index_to_txbbgain = 0x40004df8 ); +PROVIDE ( rom_iq_est_disable = 0x40005590 ); +PROVIDE ( rom_iq_est_enable = 0x40005514 ); +PROVIDE ( rom_linear_to_db = 0x40005f64 ); +PROVIDE ( rom_loopback_mode_en = 0x400030f8 ); +PROVIDE ( rom_meas_tone_pwr_db = 0x40006004 ); +PROVIDE ( rom_mhz2ieee = 0x4000404c ); +PROVIDE ( rom_noise_floor_auto_set = 0x40003bdc ); +PROVIDE ( rom_pbus_debugmode = 0x40004458 ); +PROVIDE ( rom_pbus_force_mode = 0x40004270 ); +PROVIDE ( rom_pbus_force_test = 0x400043c0 ); +PROVIDE ( rom_pbus_rd = 0x40004414 ); +PROVIDE ( rom_pbus_rd_addr = 0x40004334 ); +PROVIDE ( rom_pbus_rd_shift = 0x40004374 ); +PROVIDE ( rom_pbus_rx_dco_cal = 0x40005620 ); +PROVIDE ( rom_pbus_set_dco = 0x40004638 ); +PROVIDE ( rom_pbus_set_rxgain = 0x40004480 ); +PROVIDE ( rom_pbus_workmode = 0x4000446c ); +PROVIDE ( rom_pbus_xpd_rx_off = 0x40004508 ); +PROVIDE ( rom_pbus_xpd_rx_on = 0x4000453c ); +PROVIDE ( rom_pbus_xpd_tx_off = 0x40004590 ); +PROVIDE ( rom_pbus_xpd_tx_on = 0x400045e0 ); +PROVIDE ( rom_phy_disable_agc = 0x40002f6c ); +PROVIDE ( rom_phy_disable_cca = 0x40003000 ); +PROVIDE ( rom_phy_enable_agc = 0x40002f88 ); +PROVIDE ( rom_phy_enable_cca = 0x4000302c ); +PROVIDE ( rom_phy_freq_correct = 0x40004b44 ); +PROVIDE ( rom_phyFuns = 0x3ffae0c0 ); +PROVIDE ( rom_phy_get_noisefloor = 0x40003c2c ); +PROVIDE ( rom_phy_get_vdd33 = 0x4000642c ); +PROVIDE ( rom_pow_usr = 0x40003044 ); +PROVIDE ( rom_read_sar_dout = 0x400051c0 ); +PROVIDE ( rom_restart_cal = 0x400046e0 ); +PROVIDE ( rom_rfcal_pwrctrl = 0x40006058 ); +PROVIDE ( rom_rfcal_rxiq = 0x40005b4c ); +PROVIDE ( rom_rfcal_txcap = 0x40005dec ); +PROVIDE ( rom_rfpll_reset = 0x40004680 ); +PROVIDE ( rom_rfpll_set_freq = 0x400047f8 ); +PROVIDE ( rom_rtc_mem_backup = 0x40003db4 ); +PROVIDE ( rom_rtc_mem_recovery = 0x40003df4 ); +PROVIDE ( rom_rx_gain_force = 0x4000351c ); +PROVIDE ( rom_rxiq_cover_mg_mp = 0x40005a68 ); +PROVIDE ( rom_rxiq_get_mis = 0x400058e4 ); +PROVIDE ( rom_rxiq_set_reg = 0x40005a00 ); +PROVIDE ( rom_set_cal_rxdc = 0x400030b8 ); +PROVIDE ( rom_set_chan_cal_interp = 0x40005ce0 ); +PROVIDE ( rom_set_channel_freq = 0x40004880 ); +PROVIDE ( rom_set_loopback_gain = 0x40003060 ); +PROVIDE ( rom_set_noise_floor = 0x40003d48 ); +PROVIDE ( rom_set_pbus_mem = 0x400031a4 ); +PROVIDE ( rom_set_rf_freq_offset = 0x40004ca8 ); +PROVIDE ( rom_set_rxclk_en = 0x40003594 ); +PROVIDE ( rom_set_txcap_reg = 0x40005d50 ); +PROVIDE ( rom_set_txclk_en = 0x40003564 ); +PROVIDE ( rom_spur_coef_cfg = 0x40003ac8 ); +PROVIDE ( rom_spur_reg_write_one_tone = 0x400037f0 ); +PROVIDE ( rom_start_tx_tone = 0x400036b4 ); +PROVIDE ( rom_start_tx_tone_step = 0x400035d0 ); +PROVIDE ( rom_stop_tx_tone = 0x40003f98 ); +PROVIDE ( _rom_store = 0x4000d66c ); +PROVIDE ( _rom_store_table = 0x4000d4f8 ); +PROVIDE ( rom_target_power_add_backoff = 0x40006268 ); +PROVIDE ( rom_tx_atten_set_interp = 0x400061cc ); +PROVIDE ( rom_txbbgain_to_index = 0x40004dc0 ); +PROVIDE ( rom_txcal_work_mode = 0x4000510c ); +PROVIDE ( rom_txdc_cal_init = 0x40004e10 ); +PROVIDE ( rom_txdc_cal_v70 = 0x40004ea4 ); +PROVIDE ( rom_txiq_cover = 0x4000538c ); +PROVIDE ( rom_txiq_get_mis_pwr = 0x400052dc ); +PROVIDE ( rom_txiq_set_reg = 0x40005154 ); +PROVIDE ( rom_tx_pwctrl_bg_init = 0x4000662c ); +PROVIDE ( rom_txtone_linear_pwr = 0x40005290 ); +PROVIDE ( rom_wait_rfpll_cal_end = 0x400047a8 ); +PROVIDE ( rom_write_gain_mem = 0x4000348c ); +PROVIDE ( rom_write_rfpll_sdm = 0x40004740 ); +PROVIDE ( roundup2 = 0x4000ab7c ); +PROVIDE ( r_plf_funcs_p = 0x3ffb8360 ); +PROVIDE ( r_rf_rw_bt_init = 0x40054868 ); +PROVIDE ( r_rf_rw_init = 0x40054b0c ); +PROVIDE ( r_rf_rw_le_init = 0x400549d0 ); +PROVIDE ( r_rwble_activity_ongoing_check = 0x40054d8c ); +PROVIDE ( r_rwble_init = 0x40054bf4 ); +PROVIDE ( r_rwble_isr = 0x40054e08 ); +PROVIDE ( r_rwble_reset = 0x40054ce8 ); +PROVIDE ( r_rwble_sleep_check = 0x40054d78 ); +PROVIDE ( r_rwble_version = 0x40054dac ); +PROVIDE ( r_rwbt_init = 0x40055160 ); +PROVIDE ( r_rwbt_isr = 0x40055248 ); +PROVIDE ( r_rwbt_reset = 0x400551bc ); +PROVIDE ( r_rwbt_sleep_check = 0x4005577c ); +PROVIDE ( r_rwbt_sleep_enter = 0x400557a4 ); +PROVIDE ( r_rwbt_sleep_wakeup = 0x400557fc ); +PROVIDE ( r_rwbt_sleep_wakeup_end = 0x400558cc ); +PROVIDE ( r_rwbt_version = 0x4005520c ); +PROVIDE ( r_rwip_assert_err = 0x40055f88 ); +PROVIDE ( r_rwip_check_wakeup_boundary = 0x400558fc ); +PROVIDE ( r_rwip_ext_wakeup_enable = 0x40055f3c ); +PROVIDE ( r_rwip_init = 0x4005595c ); +PROVIDE ( r_rwip_pca_clock_dragging_only = 0x40055f48 ); +PROVIDE ( r_rwip_prevent_sleep_clear = 0x40055ec8 ); +PROVIDE ( r_rwip_prevent_sleep_set = 0x40055e64 ); +PROVIDE ( r_rwip_reset = 0x40055ab8 ); +PROVIDE ( r_rwip_schedule = 0x40055b38 ); +PROVIDE ( r_rwip_sleep = 0x40055b5c ); +PROVIDE ( r_rwip_sleep_enable = 0x40055f30 ); +PROVIDE ( r_rwip_version = 0x40055b20 ); +PROVIDE ( r_rwip_wakeup = 0x40055dc4 ); +PROVIDE ( r_rwip_wakeup_delay_set = 0x40055e4c ); +PROVIDE ( r_rwip_wakeup_end = 0x40055e18 ); +PROVIDE ( r_rwip_wlcoex_set = 0x40055f60 ); +PROVIDE ( r_SHA_256 = 0x40013a90 ); +PROVIDE ( rwip_coex_cfg = 0x3ff9914c ); +PROVIDE ( rwip_priority = 0x3ff99159 ); +PROVIDE ( rwip_rf = 0x3ffbdb28 ); +PROVIDE ( rwip_rf_p_get = 0x400558f4 ); +PROVIDE ( r_XorKey = 0x400112c0 ); +PROVIDE ( _sbrk_r = 0x4000bce4 ); +PROVIDE ( __sf_fake_stderr = 0x3ff96458 ); +PROVIDE ( __sf_fake_stdin = 0x3ff96498 ); +PROVIDE ( __sf_fake_stdout = 0x3ff96478 ); +PROVIDE ( sha1_prf = 0x40060ae8 ); +PROVIDE ( sha1_vector = 0x40060b64 ); +PROVIDE ( sha256_prf = 0x40060d70 ); +PROVIDE ( sha256_vector = 0x40060e08 ); +PROVIDE ( sha_blk_bits = 0x3ff99290 ); +PROVIDE ( sha_blk_bits_bytes = 0x3ff99288 ); +PROVIDE ( sha_blk_hash_bytes = 0x3ff9928c ); +PROVIDE ( sig_matrix = 0x3ffae293 ); +PROVIDE ( sip_after_tx_complete = 0x4000b358 ); +PROVIDE ( sip_alloc_to_host_evt = 0x4000ab9c ); +PROVIDE ( sip_get_ptr = 0x4000b34c ); +PROVIDE ( sip_get_state = 0x4000ae2c ); +PROVIDE ( sip_init_attach = 0x4000ae58 ); +PROVIDE ( sip_install_rx_ctrl_cb = 0x4000ae10 ); +PROVIDE ( sip_install_rx_data_cb = 0x4000ae20 ); +PROVIDE ( sip_is_active = 0x4000b3c0 ); +PROVIDE ( sip_post_init = 0x4000aed8 ); +PROVIDE ( sip_reclaim_from_host_cmd = 0x4000adbc ); +PROVIDE ( sip_reclaim_tx_data_pkt = 0x4000ad5c ); +PROVIDE ( sip_send = 0x4000af54 ); +PROVIDE ( sip_to_host_chain_append = 0x4000aef8 ); +PROVIDE ( sip_to_host_evt_send_done = 0x4000ac04 ); +PROVIDE ( slc_add_credits = 0x4000baf4 ); +PROVIDE ( slc_enable = 0x4000b64c ); +PROVIDE ( slc_from_host_chain_fetch = 0x4000b7e8 ); +PROVIDE ( slc_from_host_chain_recycle = 0x4000bb10 ); +PROVIDE ( slc_has_pkt_to_host = 0x4000b5fc ); +PROVIDE ( slc_init_attach = 0x4000b918 ); +PROVIDE ( slc_init_credit = 0x4000badc ); +PROVIDE ( slc_reattach = 0x4000b62c ); +PROVIDE ( slc_send_to_host_chain = 0x4000b6a0 ); +PROVIDE ( slc_set_host_io_max_window = 0x4000b89c ); +PROVIDE ( slc_to_host_chain_recycle = 0x4000b758 ); +PROVIDE ( specialModP256 = 0x4001600c ); +PROVIDE ( __stack = 0x3ffe3f20 ); +PROVIDE ( __stack_app = 0x3ffe7e30 ); +PROVIDE ( _stack_sentry = 0x3ffe1320 ); +PROVIDE ( _stack_sentry_app = 0x3ffe5230 ); +PROVIDE ( _start = 0x40000704 ); +PROVIDE ( start_tb_console = 0x4005a980 ); +PROVIDE ( _stat_r = 0x4000bcb4 ); +PROVIDE ( _stext = 0x40000560 ); +PROVIDE ( __subdf3 = 0x400026e4 ); +PROVIDE ( __subsf3 = 0x400021d0 ); +PROVIDE ( SubtractBigHex256 = 0x40015bcc ); +PROVIDE ( SubtractBigHexMod256 = 0x40015e8c ); +PROVIDE ( SubtractBigHexUint32_256 = 0x40015f8c ); +PROVIDE ( SubtractFromSelfBigHex256 = 0x40015c20 ); +PROVIDE ( SubtractFromSelfBigHexSign256 = 0x40015dc8 ); +PROVIDE ( __subvdi3 = 0x40002d20 ); +PROVIDE ( __subvsi3 = 0x40002cf8 ); +PROVIDE ( sw_to_hw = 0x3ffb8d40 ); +PROVIDE ( syscall_table_ptr_app = 0x3ffae020 ); +PROVIDE ( syscall_table_ptr_pro = 0x3ffae024 ); +PROVIDE ( tdefl_compress = 0x400600bc ); +PROVIDE ( tdefl_compress_buffer = 0x400607f4 ); +PROVIDE ( tdefl_compress_mem_to_mem = 0x40060900 ); +PROVIDE ( tdefl_compress_mem_to_output = 0x400608e0 ); +PROVIDE ( tdefl_get_adler32 = 0x400608d8 ); +PROVIDE ( tdefl_get_prev_return_status = 0x400608d0 ); +PROVIDE ( tdefl_init = 0x40060810 ); +PROVIDE ( tdefl_write_image_to_png_file_in_memory = 0x4006091c ); +PROVIDE ( tdefl_write_image_to_png_file_in_memory_ex = 0x40060910 ); +PROVIDE ( _times_r = 0x4000bc40 ); +PROVIDE ( _timezone = 0x3ffae0a0 ); +PROVIDE ( tinfl_decompress = 0x4005ef30 ); +PROVIDE ( tinfl_decompress_mem_to_callback = 0x40060090 ); +PROVIDE ( tinfl_decompress_mem_to_mem = 0x40060050 ); +PROVIDE ( __truncdfsf2 = 0x40002b90 ); +PROVIDE ( _tzname = 0x3ffae030 ); +PROVIDE ( UartDev = 0x3ffe019c ); +PROVIDE ( __ucmpdi2 = 0x40063840 ); +PROVIDE ( __udivdi3 = 0x4000cff8 ); +PROVIDE ( __udivmoddi4 = 0x40064ab0 ); +PROVIDE ( __udivsi3 = 0x4000c7c8 ); +PROVIDE ( __udiv_w_sdiv = 0x40064aa8 ); +PROVIDE ( __umoddi3 = 0x4000d280 ); +PROVIDE ( __umodsi3 = 0x4000c7d0 ); +PROVIDE ( __umulsidi3 = 0x4000c7d8 ); +PROVIDE ( _unlink_r = 0x4000bc84 ); +PROVIDE ( __unorddf2 = 0x400637f4 ); +PROVIDE ( __unordsf2 = 0x40063478 ); +PROVIDE ( user_code_start = 0x3ffe0400 ); +PROVIDE ( veryBigHexP256 = 0x3ff9736c ); +PROVIDE ( __wctomb = 0x3ff96540 ); +PROVIDE ( _write_r = 0x4000bd70 ); +PROVIDE ( xthal_bcopy = 0x4000c098 ); +PROVIDE ( xthal_copy123 = 0x4000c124 ); +PROVIDE ( xthal_get_ccompare = 0x4000c078 ); +PROVIDE ( xthal_get_ccount = 0x4000c050 ); +PROVIDE ( xthal_get_interrupt = 0x4000c1e4 ); +PROVIDE ( xthal_get_intread = 0x4000c1e4 ); +PROVIDE ( Xthal_intlevel = 0x3ff9c2b4 ); +PROVIDE ( xthal_memcpy = 0x4000c0bc ); +PROVIDE ( xthal_set_ccompare = 0x4000c058 ); +PROVIDE ( xthal_set_intclear = 0x4000c1ec ); +PROVIDE ( _xtos_set_intlevel = 0x4000bfdc ); +PROVIDE ( g_ticks_per_us_pro = 0x3ffe01e0 ); +PROVIDE ( g_ticks_per_us_app = 0x3ffe40f0 ); +PROVIDE ( esp_rom_spiflash_config_param = 0x40063238 ); +PROVIDE ( esp_rom_spiflash_read_user_cmd = 0x400621b0 ); +PROVIDE ( esp_rom_spiflash_write_encrypted_disable = 0x40062e60 ); +PROVIDE ( esp_rom_spiflash_write_encrypted_enable = 0x40062df4 ); +PROVIDE ( esp_rom_spiflash_prepare_encrypted_data = 0x40062e1c ); +PROVIDE ( esp_rom_spiflash_select_qio_pins = 0x40061ddc ); +PROVIDE ( esp_rom_spiflash_attach = 0x40062a6c ); +PROVIDE ( esp_rom_spiflash_config_clk = 0x40062bc8 ); +PROVIDE ( g_rom_spiflash_chip = 0x3ffae270 ); + +/* +These functions are xtos-related (or call xtos-related functions) and do not play well +with multicore FreeRTOS. Where needed, we provide alternatives that are multicore +compatible. These functions also use a chunk of static RAM, by not using them we can +allocate that RAM for general use. +*/ +/* +PROVIDE ( _DebugExceptionVector = 0x40000280 ); +PROVIDE ( _DoubleExceptionVector = 0x400003c0 ); +PROVIDE ( _KernelExceptionVector = 0x40000300 ); +PROVIDE ( _GeneralException = 0x40000e14 ); +PROVIDE ( _ResetHandler = 0x40000450 ); +PROVIDE ( _ResetVector = 0x40000400 ); +PROVIDE ( _UserExceptionVector = 0x40000340 ); +PROVIDE ( _NMIExceptionVector = 0x400002c0 ); +PROVIDE ( _WindowOverflow12 = 0x40000100 ); +PROVIDE ( _WindowOverflow4 = 0x40000000 ); +PROVIDE ( _WindowOverflow8 = 0x40000080 ); +PROVIDE ( _WindowUnderflow12 = 0x40000140 ); +PROVIDE ( _WindowUnderflow4 = 0x40000040 ); +PROVIDE ( _WindowUnderflow8 = 0x400000c0 ); +PROVIDE ( _Level2FromVector = 0x40000954 ); +PROVIDE ( _Level3FromVector = 0x40000a28 ); +PROVIDE ( _Level4FromVector = 0x40000af8 ); +PROVIDE ( _Level5FromVector = 0x40000c68 ); +PROVIDE ( _Level2Vector = 0x40000180 ); +PROVIDE ( _Level3Vector = 0x400001c0 ); +PROVIDE ( _Level4Vector = 0x40000200 ); +PROVIDE ( _Level5Vector = 0x40000240 ); +PROVIDE ( _LevelOneInterrupt = 0x40000835 ); +PROVIDE ( _SyscallException = 0x400007cf ); +PROVIDE ( _xtos_alloca_handler = 0x40000010 ); +PROVIDE ( _xtos_cause3_handler = 0x40000dd8 ); +PROVIDE ( _xtos_c_handler_table = 0x3ffe0548 ); +PROVIDE ( _xtos_c_wrapper_handler = 0x40000de8 ); +PROVIDE ( _xtos_enabled = 0x3ffe0650 ); +PROVIDE ( _xtos_exc_handler_table = 0x3ffe0448 ); +PROVIDE ( _xtos_interrupt_mask_table = 0x3ffe0758 ); +PROVIDE ( _xtos_interrupt_table = 0x3ffe0658 ); +PROVIDE ( _xtos_ints_off = 0x4000bfac ); +PROVIDE ( _xtos_ints_on = 0x4000bf88 ); +PROVIDE ( _xtos_intstruct = 0x3ffe0650 ); +PROVIDE ( _xtos_l1int_handler = 0x40000814 ); +PROVIDE ( _xtos_p_none = 0x4000bfd4 ); +PROVIDE ( _xtos_restore_intlevel = 0x40000928 ); +PROVIDE ( _xtos_return_from_exc = 0x4000c034 ); +PROVIDE ( _xtos_set_exception_handler = 0x4000074c ); +PROVIDE ( _xtos_set_interrupt_handler = 0x4000bf78 ); +PROVIDE ( _xtos_set_interrupt_handler_arg = 0x4000bf34 ); +PROVIDE ( _xtos_set_min_intlevel = 0x4000bff8 ); +PROVIDE ( _xtos_set_vpri = 0x40000934 ); +PROVIDE ( _xtos_syscall_handler = 0x40000790 ); +PROVIDE ( _xtos_unhandled_exception = 0x4000c024 ); +PROVIDE ( _xtos_unhandled_interrupt = 0x4000c01c ); +PROVIDE ( _xtos_vpri_enabled = 0x3ffe0654 ); +PROVIDE ( ets_intr_count = 0x3ffe03fc ); +*/ + +/* These functions are part of the UART downloader but also contain general UART functions. */ +PROVIDE ( FilePacketSendDeflatedReqMsgProc = 0x40008b24 ); +PROVIDE ( FilePacketSendReqMsgProc = 0x40008860 ); +PROVIDE ( FlashDwnLdDeflatedStartMsgProc = 0x40008ad8 ); +PROVIDE ( FlashDwnLdParamCfgMsgProc = 0x4000891c ); +PROVIDE ( FlashDwnLdStartMsgProc = 0x40008820 ); +PROVIDE ( FlashDwnLdStopDeflatedReqMsgProc = 0x40008c18 ); +PROVIDE ( FlashDwnLdStopReqMsgProc = 0x400088ec ); +PROVIDE ( MemDwnLdStartMsgProc = 0x40008948 ); +PROVIDE ( MemDwnLdStopReqMsgProc = 0x400089dc ); +PROVIDE ( MemPacketSendReqMsgProc = 0x40008978 ); +PROVIDE ( uart_baudrate_detect = 0x40009034 ); +PROVIDE ( uart_buff_switch = 0x400093c0 ); +PROVIDE ( UartConnCheck = 0x40008738 ); +PROVIDE ( UartConnectProc = 0x40008a04 ); +PROVIDE ( UartDwnLdProc = 0x40008ce8 ); +PROVIDE ( UartRegReadProc = 0x40008a58 ); +PROVIDE ( UartRegWriteProc = 0x40008a14 ); +PROVIDE ( UartSetBaudProc = 0x40008aac ); +PROVIDE ( UartSpiAttachProc = 0x40008a6c ); +PROVIDE ( UartSpiReadProc = 0x40008a80 ); +PROVIDE ( VerifyFlashMd5Proc = 0x40008c44 ); +PROVIDE ( GetUartDevice = 0x40009598 ); +PROVIDE ( RcvMsg = 0x4000954c ); +PROVIDE ( SendMsg = 0x40009384 ); +PROVIDE ( UartGetCmdLn = 0x40009564 ); +PROVIDE ( UartRxString = 0x400092fc ); +PROVIDE ( Uart_Init = 0x40009120 ); +PROVIDE ( recv_packet = 0x40009424 ); +PROVIDE ( send_packet = 0x40009340 ); +PROVIDE ( uartAttach = 0x40008fd0 ); +PROVIDE ( uart_div_modify = 0x400090cc ); +PROVIDE ( uart_rx_intr_handler = 0x40008f4c ); +PROVIDE ( uart_rx_one_char = 0x400092d0 ); +PROVIDE ( uart_rx_one_char_block = 0x400092a4 ); +PROVIDE ( uart_rx_readbuff = 0x40009394 ); +PROVIDE ( uart_tx_flush = 0x40009258 ); +PROVIDE ( uart_tx_one_char = 0x40009200 ); +PROVIDE ( uart_tx_one_char2 = 0x4000922c ); +PROVIDE ( uart_tx_switch = 0x40009028 ); +PROVIDE ( uart_tx_wait_idle = 0x40009278 ); + + +/* +These functions are part of the ROM GPIO driver. We do not use them; the provided esp-idf functions +replace them and this way we can re-use the fixed RAM addresses these routines need. +*/ +/* <-- So you don't read over it: This comment disables the next lines. +PROVIDE ( gpio_init = 0x40009c20 ); +PROVIDE ( gpio_intr_ack = 0x40009dd4 ); +PROVIDE ( gpio_intr_ack_high = 0x40009e1c ); +PROVIDE ( gpio_intr_handler_register = 0x40009e6c ); +PROVIDE ( gpio_intr_pending = 0x40009cec ); +PROVIDE ( gpio_intr_pending_high = 0x40009cf8 ); +PROVIDE ( gpio_pending_mask = 0x3ffe0038 ); +PROVIDE ( gpio_pending_mask_high = 0x3ffe0044 ); +PROVIDE ( gpio_pin_intr_state_set = 0x40009d04 ); +PROVIDE ( gpio_pin_wakeup_disable = 0x40009eb0 ); +PROVIDE ( gpio_pin_wakeup_enable = 0x40009e7c ); +PROVIDE ( gpio_register_get = 0x40009cbc ); +PROVIDE ( gpio_register_set = 0x40009bbc ); +*/ +/* These are still part of that driver, but have been verified not to use static RAM, so they can be used. */ +PROVIDE ( gpio_output_set = 0x40009b24 ); +PROVIDE ( gpio_output_set_high = 0x40009b5c ); +PROVIDE ( gpio_input_get = 0x40009b88 ); +PROVIDE ( gpio_input_get_high = 0x40009b9c ); +PROVIDE ( gpio_matrix_in = 0x40009edc ); +PROVIDE ( gpio_matrix_out = 0x40009f0c ); +PROVIDE ( gpio_pad_select_gpio = 0x40009fdc ); +PROVIDE ( gpio_pad_set_drv = 0x4000a11c ); +PROVIDE ( gpio_pad_pulldown = 0x4000a348 ); +PROVIDE ( gpio_pad_pullup = 0x4000a22c ); +PROVIDE ( gpio_pad_hold = 0x4000a734 ); +PROVIDE ( gpio_pad_unhold = 0x4000a484 ); + +/* +These functions are part of the non-os kernel (etsc). +*/ +PROVIDE ( ets_aes_crypt = 0x4005c9b8 ); +PROVIDE ( ets_aes_disable = 0x4005c8f8 ); +PROVIDE ( ets_aes_enable = 0x4005c8cc ); +PROVIDE ( ets_aes_set_endian = 0x4005c928 ); +PROVIDE ( ets_aes_setkey_dec = 0x4005c994 ); +PROVIDE ( ets_aes_setkey_enc = 0x4005c97c ); +PROVIDE ( ets_bigint_disable = 0x4005c4e0 ); +PROVIDE ( ets_bigint_enable = 0x4005c498 ); +PROVIDE ( ets_bigint_mod_mult_getz = 0x4005c818 ); +PROVIDE ( ets_bigint_mod_mult_prepare = 0x4005c7b4 ); +PROVIDE ( ets_bigint_mod_power_getz = 0x4005c614 ); +PROVIDE ( ets_bigint_mod_power_prepare = 0x4005c54c ); +PROVIDE ( ets_bigint_montgomery_mult_getz = 0x4005c7a4 ); +PROVIDE ( ets_bigint_montgomery_mult_prepare = 0x4005c6fc ); +PROVIDE ( ets_bigint_mult_getz = 0x4005c6e8 ); +PROVIDE ( ets_bigint_mult_prepare = 0x4005c630 ); +PROVIDE ( ets_bigint_wait_finish = 0x4005c520 ); +PROVIDE ( ets_post = 0x4000673c ); +PROVIDE ( ets_run = 0x400066bc ); +PROVIDE ( ets_set_idle_cb = 0x40006674 ); +PROVIDE ( ets_task = 0x40006688 ); +PROVIDE ( ets_efuse_get_8M_clock = 0x40008710 ); +PROVIDE ( ets_efuse_get_spiconfig = 0x40008658 ); +PROVIDE ( ets_efuse_program_op = 0x40008628 ); +PROVIDE ( ets_efuse_read_op = 0x40008600 ); +PROVIDE ( ets_intr_lock = 0x400067b0 ); +PROVIDE ( ets_intr_unlock = 0x400067c4 ); +PROVIDE ( ets_isr_attach = 0x400067ec ); +PROVIDE ( ets_isr_mask = 0x400067fc ); +PROVIDE ( ets_isr_unmask = 0x40006808 ); +PROVIDE ( ets_waiti0 = 0x400067d8 ); +PROVIDE ( intr_matrix_set = 0x4000681c ); +PROVIDE ( check_pos = 0x400068b8 ); +PROVIDE ( ets_set_appcpu_boot_addr = 0x4000689c ); +PROVIDE ( ets_set_startup_callback = 0x4000688c ); +PROVIDE ( ets_set_user_start = 0x4000687c ); +PROVIDE ( ets_unpack_flash_code = 0x40007018 ); +PROVIDE ( ets_unpack_flash_code_legacy = 0x4000694c ); +PROVIDE ( rom_main = 0x400076c4 ); +PROVIDE ( ets_write_char_uart = 0x40007cf8 ); +PROVIDE ( ets_install_putc1 = 0x40007d18 ); +PROVIDE ( ets_install_putc2 = 0x40007d38 ); +PROVIDE ( ets_install_uart_printf = 0x40007d28 ); +PROVIDE ( ets_printf = 0x40007d54 ); +PROVIDE ( rtc_boot_control = 0x4000821c ); +PROVIDE ( rtc_get_reset_reason = 0x400081d4 ); +PROVIDE ( rtc_get_wakeup_cause = 0x400081f4 ); +PROVIDE ( rtc_select_apb_bridge = 0x40008288 ); +PROVIDE ( set_rtc_memory_crc = 0x40008208 ); +PROVIDE ( software_reset = 0x4000824c ); +PROVIDE ( software_reset_cpu = 0x40008264 ); +PROVIDE ( ets_secure_boot_check = 0x4005cb40 ); +PROVIDE ( ets_secure_boot_check_finish = 0x4005cc04 ); +PROVIDE ( ets_secure_boot_check_start = 0x4005cbcc ); +PROVIDE ( ets_secure_boot_finish = 0x4005ca84 ); +PROVIDE ( ets_secure_boot_hash = 0x4005cad4 ); +PROVIDE ( ets_secure_boot_obtain = 0x4005cb14 ); +PROVIDE ( ets_secure_boot_rd_abstract = 0x4005cba8 ); +PROVIDE ( ets_secure_boot_rd_iv = 0x4005cb84 ); +PROVIDE ( ets_secure_boot_start = 0x4005ca34 ); +PROVIDE ( ets_sha_disable = 0x4005c0a8 ); +PROVIDE ( ets_sha_enable = 0x4005c07c ); +PROVIDE ( ets_sha_finish = 0x4005c104 ); +PROVIDE ( ets_sha_init = 0x4005c0d4 ); +PROVIDE ( ets_sha_update = 0x4005c2a0 ); +PROVIDE ( ets_delay_us = 0x40008534 ); +PROVIDE ( ets_get_cpu_frequency = 0x4000855c ); +PROVIDE ( ets_get_detected_xtal_freq = 0x40008588 ); +PROVIDE ( ets_get_xtal_scale = 0x4000856c ); +PROVIDE ( ets_timer_arm = 0x40008368 ); +PROVIDE ( ets_timer_arm_us = 0x400083ac ); +PROVIDE ( ets_timer_disarm = 0x400083ec ); +PROVIDE ( ets_timer_done = 0x40008428 ); +PROVIDE ( ets_timer_handler_isr = 0x40008454 ); +PROVIDE ( ets_timer_init = 0x400084e8 ); +PROVIDE ( ets_timer_setfn = 0x40008350 ); +PROVIDE ( ets_update_cpu_frequency_rom = 0x40008550 ); /* Updates g_ticks_per_us on the current CPU only; not on the other core */ + +/* Following are static data, but can be used, not generated by script <<<<< btdm data */ +PROVIDE ( hci_tl_env = 0x3ffb8154 ); +PROVIDE ( ld_acl_env = 0x3ffb8258 ); +PROVIDE ( ea_env = 0x3ffb80ec ); +PROVIDE ( ld_active_ch_map = 0x3ffb8334 ); +PROVIDE ( ld_bcst_acl_env = 0x3ffb8274 ); +PROVIDE ( ld_csb_rx_env = 0x3ffb8278 ); +PROVIDE ( ld_csb_tx_env = 0x3ffb827c ); +PROVIDE ( ld_env = 0x3ffb9510 ); +PROVIDE ( ld_fm_env = 0x3ffb8284 ); +PROVIDE ( ld_inq_env = 0x3ffb82e4 ); +PROVIDE ( ld_iscan_env = 0x3ffb82e8 ); +PROVIDE ( ld_page_env = 0x3ffb82f0 ); +PROVIDE ( ld_pca_env = 0x3ffb82f4 ); +PROVIDE ( ld_pscan_env = 0x3ffb8308 ); +PROVIDE ( ld_sched_env = 0x3ffb830c ); +PROVIDE ( ld_sched_params = 0x3ffb96c0 ); +PROVIDE ( ld_sco_env = 0x3ffb824c ); +PROVIDE ( ld_sscan_env = 0x3ffb832c ); +PROVIDE ( ld_strain_env = 0x3ffb8330 ); +PROVIDE ( LM_Sniff = 0x3ffb8230 ); +PROVIDE ( LM_SniffSubRate = 0x3ffb8214 ); +PROVIDE ( prbs_64bytes = 0x3ff98992 ); +PROVIDE ( nvds_env = 0x3ffb8364 ); +PROVIDE ( nvds_magic_number = 0x3ff9912a ); +PROVIDE ( TASK_DESC_LLD = 0x3ff98b58 ); +/* Above are static data, but can be used, not generated by script >>>>> btdm data */ diff --git a/cpu/esp32/ld/esp32.rom.nanofmt.ld b/cpu/esp32/ld/esp32.rom.nanofmt.ld new file mode 100644 index 0000000000000..344ff992b9b4e --- /dev/null +++ b/cpu/esp32/ld/esp32.rom.nanofmt.ld @@ -0,0 +1,98 @@ +/* + Address table for printf/scanf family of functions in ESP32 ROM. + These functions are compiled with newlib "nano" format option. + As such, they don's support 64-bit integer formats. + Floating point formats are supported by setting _printf_float and + _scanf_float entries in syscall table. This is done automatically + by startup code. + + Generated for ROM with MD5sum: + ab8282ae908fe9e7a63fb2a4ac2df013 eagle.pro.rom.out +*/ + +PROVIDE ( asiprintf = 0x40056d9c ); +PROVIDE ( _asiprintf_r = 0x40056d4c ); +PROVIDE ( asniprintf = 0x40056cd8 ); +PROVIDE ( _asniprintf_r = 0x40056c64 ); +PROVIDE ( asnprintf = 0x40056cd8 ); +PROVIDE ( _asnprintf_r = 0x40056c64 ); +PROVIDE ( asprintf = 0x40056d9c ); +PROVIDE ( _asprintf_r = 0x40056d4c ); +PROVIDE ( fiprintf = 0x40056efc ); +PROVIDE ( _fiprintf_r = 0x40056ed8 ); +PROVIDE ( fiscanf = 0x40058884 ); +PROVIDE ( _fiscanf_r = 0x400588b4 ); +PROVIDE ( fprintf = 0x40056efc ); +PROVIDE ( _fprintf_r = 0x40056ed8 ); +PROVIDE ( iprintf = 0x40056978 ); +PROVIDE ( _iprintf_r = 0x40056944 ); +PROVIDE ( printf = 0x40056978 ); +PROVIDE ( _printf_common = 0x40057338 ); +PROVIDE ( _printf_float = 0x4000befc ); +PROVIDE ( _printf_i = 0x40057404 ); +PROVIDE ( _printf_r = 0x40056944 ); +PROVIDE ( siprintf = 0x40056c08 ); +PROVIDE ( _siprintf_r = 0x40056bbc ); +PROVIDE ( sniprintf = 0x40056b4c ); +PROVIDE ( _sniprintf_r = 0x40056ae4 ); +PROVIDE ( snprintf = 0x40056b4c ); +PROVIDE ( _snprintf_r = 0x40056ae4 ); +PROVIDE ( sprintf = 0x40056c08 ); +PROVIDE ( _sprintf_r = 0x40056bbc ); +PROVIDE ( __sprint_r = 0x400577e4 ); +PROVIDE ( _svfiprintf_r = 0x40057100 ); +PROVIDE ( __svfiscanf_r = 0x40057b08 ); +PROVIDE ( _svfprintf_r = 0x40057100 ); +PROVIDE ( __svfscanf = 0x40057f04 ); +PROVIDE ( __svfscanf_r = 0x40057b08 ); +PROVIDE ( vasiprintf = 0x40056eb8 ); +PROVIDE ( _vasiprintf_r = 0x40056e80 ); +PROVIDE ( vasniprintf = 0x40056e58 ); +PROVIDE ( _vasniprintf_r = 0x40056df8 ); +PROVIDE ( vasnprintf = 0x40056e58 ); +PROVIDE ( _vasnprintf_r = 0x40056df8 ); +PROVIDE ( vasprintf = 0x40056eb8 ); +PROVIDE ( _vasprintf_r = 0x40056e80 ); +PROVIDE ( vfiprintf = 0x40057ae8 ); +PROVIDE ( _vfiprintf_r = 0x40057850 ); +PROVIDE ( vfiscanf = 0x40057eb8 ); +PROVIDE ( _vfiscanf_r = 0x40057f24 ); +PROVIDE ( vfprintf = 0x40057ae8 ); +PROVIDE ( _vfprintf_r = 0x40057850 ); +PROVIDE ( vfscanf = 0x40057eb8 ); +PROVIDE ( _vfscanf_r = 0x40057f24 ); +PROVIDE ( viprintf = 0x400569b4 ); +PROVIDE ( _viprintf_r = 0x400569e4 ); +PROVIDE ( viscanf = 0x40058698 ); +PROVIDE ( _viscanf_r = 0x400586c8 ); +PROVIDE ( vprintf = 0x400569b4 ); +PROVIDE ( _vprintf_r = 0x400569e4 ); +PROVIDE ( vscanf = 0x40058698 ); +PROVIDE ( _vscanf_r = 0x400586c8 ); +PROVIDE ( vsiprintf = 0x40056ac4 ); +PROVIDE ( _vsiprintf_r = 0x40056a90 ); +PROVIDE ( vsiscanf = 0x40058740 ); +PROVIDE ( _vsiscanf_r = 0x400586f8 ); +PROVIDE ( vsniprintf = 0x40056a68 ); +PROVIDE ( _vsniprintf_r = 0x40056a14 ); +PROVIDE ( vsnprintf = 0x40056a68 ); +PROVIDE ( _vsnprintf_r = 0x40056a14 ); +PROVIDE ( vsprintf = 0x40056ac4 ); +PROVIDE ( _vsprintf_r = 0x40056a90 ); +PROVIDE ( vsscanf = 0x40058740 ); +PROVIDE ( _vsscanf_r = 0x400586f8 ); +PROVIDE ( fscanf = 0x40058884 ); +PROVIDE ( _fscanf_r = 0x400588b4 ); +PROVIDE ( iscanf = 0x40058760 ); +PROVIDE ( _iscanf_r = 0x4005879c ); +PROVIDE ( scanf = 0x40058760 ); +PROVIDE ( _scanf_chars = 0x40058384 ); +PROVIDE ( _scanf_float = 0x4000bf18 ); +PROVIDE ( _scanf_i = 0x4005845c ); +PROVIDE ( _scanf_r = 0x4005879c ); +PROVIDE ( siscanf = 0x400587d0 ); +PROVIDE ( _siscanf_r = 0x40058830 ); +PROVIDE ( sscanf = 0x400587d0 ); +PROVIDE ( _sscanf_r = 0x40058830 ); +PROVIDE ( __ssvfiscanf_r = 0x4005802c ); +PROVIDE ( __ssvfscanf_r = 0x4005802c ); diff --git a/cpu/esp32/ld/esp32.rom.nosdk.ld b/cpu/esp32/ld/esp32.rom.nosdk.ld new file mode 100644 index 0000000000000..88a6a7cf1130d --- /dev/null +++ b/cpu/esp32/ld/esp32.rom.nosdk.ld @@ -0,0 +1,1725 @@ +/* +ESP32 ROM address table +Generated for ROM with MD5sum: +ab8282ae908fe9e7a63fb2a4ac2df013 ../../rom_image/prorom.elf +*/ +PROVIDE ( abort = 0x4000bba4 ); +PROVIDE ( __absvdi2 = 0x4006387c ); +PROVIDE ( __absvsi2 = 0x40063868 ); +PROVIDE ( Add2SelfBigHex256 = 0x40015b7c ); +PROVIDE ( AddBigHex256 = 0x40015b28 ); +PROVIDE ( AddBigHexModP256 = 0x40015c98 ); +PROVIDE ( __adddf3 = 0x40002590 ); +PROVIDE ( AddP256 = 0x40015c74 ); +PROVIDE ( AddPdiv2_256 = 0x40015ce0 ); +PROVIDE ( __addsf3 = 0x400020e8 ); +PROVIDE ( __addvdi3 = 0x40002cbc ); +PROVIDE ( __addvsi3 = 0x40002c98 ); +PROVIDE ( aes_128_cbc_decrypt = 0x4005cc7c ); +PROVIDE ( aes_128_cbc_encrypt = 0x4005cc18 ); +PROVIDE ( aes_unwrap = 0x4005ccf0 ); +PROVIDE ( app_gpio_arg = 0x3ffe003c ); +PROVIDE ( app_gpio_handler = 0x3ffe0040 ); +PROVIDE ( __ashldi3 = 0x4000c818 ); +PROVIDE ( __ashrdi3 = 0x4000c830 ); +PROVIDE ( base64_decode = 0x4005ced8 ); +PROVIDE ( base64_encode = 0x4005cdbc ); +PROVIDE ( BasePoint_x_256 = 0x3ff97488 ); +PROVIDE ( BasePoint_y_256 = 0x3ff97468 ); +PROVIDE ( bigHexInversion256 = 0x400168f0 ); +PROVIDE ( bigHexP256 = 0x3ff973bc ); +PROVIDE ( __bswapdi2 = 0x400649c4 ); +PROVIDE ( __bswapsi2 = 0x4006499c ); +PROVIDE ( btdm_r_ble_bt_handler_tab_p_get = 0x40019b0c ); +PROVIDE ( btdm_r_btdm_option_data_p_get = 0x40010004 ); +PROVIDE ( btdm_r_btdm_rom_version_get = 0x40010078 ); +PROVIDE ( btdm_r_data_init = 0x4001002c ); +PROVIDE ( btdm_r_import_rf_phy_func_p_get = 0x40054298 ); +PROVIDE ( btdm_r_ip_func_p_get = 0x40019af0 ); +PROVIDE ( btdm_r_ip_func_p_set = 0x40019afc ); +PROVIDE ( btdm_r_modules_func_p_get = 0x4005427c ); +PROVIDE ( btdm_r_modules_func_p_set = 0x40054270 ); +PROVIDE ( btdm_r_plf_func_p_set = 0x40054288 ); +PROVIDE ( bt_util_buf_env = 0x3ffb8bd4 ); +PROVIDE ( cache_flash_mmu_set_rom = 0x400095e0 ); +PROVIDE ( Cache_Flush_rom = 0x40009a14 ); +PROVIDE ( Cache_Read_Disable_rom = 0x40009ab8 ); +PROVIDE ( Cache_Read_Enable_rom = 0x40009a84 ); +PROVIDE ( Cache_Read_Init_rom = 0x40009950 ); +PROVIDE ( cache_sram_mmu_set_rom = 0x400097f4 ); +/* This is static function, but can be used, not generated by script*/ +PROVIDE ( calc_rtc_memory_crc = 0x40008170 ); +PROVIDE ( calloc = 0x4000bee4 ); +PROVIDE ( _calloc_r = 0x4000bbf8 ); +PROVIDE ( __clear_cache = 0x40063860 ); +PROVIDE ( _close_r = 0x4000bd3c ); +PROVIDE ( __clrsbdi2 = 0x40064a38 ); +PROVIDE ( __clrsbsi2 = 0x40064a20 ); +PROVIDE ( __clzdi2 = 0x4000ca50 ); +PROVIDE ( __clzsi2 = 0x4000c7e8 ); +PROVIDE ( __cmpdi2 = 0x40063820 ); +PROVIDE ( co_default_bdaddr = 0x3ffae704 ); +PROVIDE ( co_null_bdaddr = 0x3ffb80e0 ); +PROVIDE ( co_sca2ppm = 0x3ff971e8 ); +PROVIDE ( crc16_be = 0x4005d09c ); +PROVIDE ( crc16_le = 0x4005d05c ); +PROVIDE ( crc32_be = 0x4005d024 ); +PROVIDE ( crc32_le = 0x4005cfec ); +PROVIDE ( crc8_be = 0x4005d114 ); +PROVIDE ( crc8_le = 0x4005d0e0 ); +PROVIDE ( _ctype_ = 0x3ff96354 ); +PROVIDE ( __ctype_ptr__ = 0x3ff96350 ); +PROVIDE ( __ctzdi2 = 0x4000ca64 ); +PROVIDE ( __ctzsi2 = 0x4000c7f0 ); +PROVIDE ( _data_end_rom = 0x4000d5c8 ); +PROVIDE ( _data_end_btdm_rom = 0x4000d4f8 ); +PROVIDE ( _data_start_rom = 0x4000d4f8 ); +PROVIDE ( _data_start_btdm_rom = 0x4000d4f4 ); +PROVIDE ( _data_start_btdm = 0x3ffae6e0); +PROVIDE ( _data_end_btdm = 0x3ffaff10); +PROVIDE ( _bss_start_btdm = 0x3ffb8000); +PROVIDE ( _bss_end_btdm = 0x3ffbff70); +PROVIDE ( _daylight = 0x3ffae0a4 ); +PROVIDE ( dbg_default_handler = 0x3ff97218 ); +PROVIDE ( dbg_state = 0x3ffb8d5d ); +PROVIDE ( DebugE256PublicKey_x = 0x3ff97428 ); +PROVIDE ( DebugE256PublicKey_y = 0x3ff97408 ); +PROVIDE ( DebugE256SecretKey = 0x3ff973e8 ); +PROVIDE ( debug_timer = 0x3ffe042c ); +PROVIDE ( debug_timerfn = 0x3ffe0430 ); +PROVIDE ( dh_group14_generator = 0x3ff9ac60 ); +PROVIDE ( dh_group14_prime = 0x3ff9ab60 ); +PROVIDE ( dh_group15_generator = 0x3ff9ab5f ); +PROVIDE ( dh_group15_prime = 0x3ff9a9df ); +PROVIDE ( dh_group16_generator = 0x3ff9a9de ); +PROVIDE ( dh_group16_prime = 0x3ff9a7de ); +PROVIDE ( dh_group17_generator = 0x3ff9a7dd ); +PROVIDE ( dh_group17_prime = 0x3ff9a4dd ); +PROVIDE ( dh_group18_generator = 0x3ff9a4dc ); +PROVIDE ( dh_group18_prime = 0x3ff9a0dc ); +PROVIDE ( dh_group1_generator = 0x3ff9ae03 ); +PROVIDE ( dh_group1_prime = 0x3ff9ada3 ); +PROVIDE ( dh_group2_generator = 0x3ff9ada2 ); +PROVIDE ( dh_group2_prime = 0x3ff9ad22 ); +PROVIDE ( dh_group5_generator = 0x3ff9ad21 ); +PROVIDE ( dh_group5_prime = 0x3ff9ac61 ); +PROVIDE ( __divdc3 = 0x40064460 ); +PROVIDE ( __divdf3 = 0x40002954 ); +PROVIDE ( __divdi3 = 0x4000ca84 ); +PROVIDE ( __divsc3 = 0x40064200 ); +PROVIDE ( __divsf3 = 0x4000234c ); +PROVIDE ( __divsi3 = 0x4000c7b8 ); +PROVIDE ( g_rom_spiflash_dummy_len_plus = 0x3ffae290 ); +PROVIDE ( ecc_env = 0x3ffb8d60 ); +PROVIDE ( ecc_Jacobian_InfinityPoint256 = 0x3ff972e8 ); +PROVIDE ( em_buf_env = 0x3ffb8d74 ); +PROVIDE ( environ = 0x3ffae0b4 ); +PROVIDE ( __eqdf2 = 0x400636a8 ); +PROVIDE ( __eqsf2 = 0x40063374 ); +PROVIDE ( esp_crc8 = 0x4005d144 ); +PROVIDE ( _etext = 0x4000d66c ); +PROVIDE ( ets_readySet_ = 0x3ffe01f0 ); +PROVIDE ( ets_startup_callback = 0x3ffe0404 ); +PROVIDE ( exc_cause_table = 0x3ff991d0 ); +PROVIDE ( _exit_r = 0x4000bd28 ); +PROVIDE ( __extendsfdf2 = 0x40002c34 ); +PROVIDE ( __ffsdi2 = 0x4000ca2c ); +PROVIDE ( __ffssi2 = 0x4000c804 ); +PROVIDE ( __fixdfdi = 0x40002ac4 ); +PROVIDE ( __fixdfsi = 0x40002a78 ); +PROVIDE ( __fixsfdi = 0x4000244c ); +PROVIDE ( __fixsfsi = 0x4000240c ); +PROVIDE ( __fixunsdfsi = 0x40002b30 ); +PROVIDE ( __fixunssfdi = 0x40002504 ); +PROVIDE ( __fixunssfsi = 0x400024ac ); +PROVIDE ( __floatdidf = 0x4000c988 ); +PROVIDE ( __floatdisf = 0x4000c8c0 ); +PROVIDE ( __floatsidf = 0x4000c944 ); +PROVIDE ( __floatsisf = 0x4000c870 ); +PROVIDE ( __floatundidf = 0x4000c978 ); +PROVIDE ( __floatundisf = 0x4000c8b0 ); +PROVIDE ( __floatunsidf = 0x4000c938 ); +PROVIDE ( __floatunsisf = 0x4000c864 ); +PROVIDE ( free = 0x4000beb8 ); +PROVIDE ( _free_r = 0x4000bbcc ); +PROVIDE ( _fstat_r = 0x4000bccc ); +PROVIDE ( __gcc_bcmp = 0x40064a70 ); +PROVIDE ( __gedf2 = 0x40063768 ); +PROVIDE ( __gesf2 = 0x4006340c ); +PROVIDE ( _getpid_r = 0x4000bcfc ); +PROVIDE ( __getreent = 0x4000be8c ); +PROVIDE ( _gettimeofday_r = 0x4000bc58 ); +PROVIDE ( GF_Jacobian_Point_Addition256 = 0x400163a4 ); +PROVIDE ( GF_Jacobian_Point_Double256 = 0x40016260 ); +PROVIDE ( GF_Point_Jacobian_To_Affine256 = 0x40016b0c ); +PROVIDE ( _global_impure_ptr = 0x3ffae0b0 ); +PROVIDE ( g_phyFuns_instance = 0x3ffae0c4 ); +PROVIDE ( g_rom_flashchip = 0x3ffae270 ); +PROVIDE ( __gtdf2 = 0x400636dc ); +PROVIDE ( __gtsf2 = 0x400633a0 ); +PROVIDE ( gTxMsg = 0x3ffe0050 ); +PROVIDE ( hci_cmd_desc_root_tab = 0x3ff976d4 ); +PROVIDE ( hci_cmd_desc_tab_ctrl_bb = 0x3ff97b70 ); +PROVIDE ( hci_cmd_desc_tab_info_par = 0x3ff97b1c ); +PROVIDE ( hci_cmd_desc_tab_le = 0x3ff97870 ); +PROVIDE ( hci_cmd_desc_tab_lk_ctrl = 0x3ff97fc0 ); +PROVIDE ( hci_cmd_desc_tab_lk_pol = 0x3ff97f3c ); +PROVIDE ( hci_cmd_desc_tab_stat_par = 0x3ff97ac8 ); +PROVIDE ( hci_cmd_desc_tab_testing = 0x3ff97a98 ); +PROVIDE ( hci_cmd_desc_tab_vs = 0x3ff97714 ); +PROVIDE ( hci_command_handler = 0x4004c928 ); +PROVIDE ( hci_env = 0x3ffb9350 ); +PROVIDE ( hci_evt_dbg_desc_tab = 0x3ff9750c ); +PROVIDE ( hci_evt_desc_tab = 0x3ff9751c ); +PROVIDE ( hci_evt_le_desc_tab = 0x3ff974b4 ); +PROVIDE ( hci_fc_env = 0x3ffb9340 ); +PROVIDE ( hmac_md5 = 0x4005d264 ); +PROVIDE ( hmac_md5_vector = 0x4005d17c ); +PROVIDE ( hmac_sha1 = 0x40060acc ); +PROVIDE ( hmac_sha1_vector = 0x400609e4 ); +PROVIDE ( hmac_sha256 = 0x40060d58 ); +PROVIDE ( hmac_sha256_vector = 0x40060c84 ); +PROVIDE ( jd_decomp = 0x400613e8 ); +PROVIDE ( jd_prepare = 0x40060fa8 ); +PROVIDE ( ke_env = 0x3ffb93cc ); +PROVIDE ( _kill_r = 0x4000bd10 ); +PROVIDE ( lb_default_handler = 0x3ff982b8 ); +PROVIDE ( lb_default_state_tab_p_get = 0x4001c198 ); +PROVIDE ( lb_env = 0x3ffb9424 ); +PROVIDE ( lb_hci_cmd_handler_tab_p_get = 0x4001c18c ); +PROVIDE ( lb_state = 0x3ffb94e8 ); +PROVIDE ( lc_default_handler = 0x3ff98648 ); +PROVIDE ( lc_default_state_tab_p_get = 0x4002f494 ); +PROVIDE ( lc_env = 0x3ffb94ec ); +PROVIDE ( lc_hci_cmd_handler_tab_p_get = 0x4002f488 ); +PROVIDE ( lc_state = 0x3ffb9508 ); +PROVIDE ( ld_acl_br_sizes = 0x3ff98a2a ); +PROVIDE ( ld_acl_br_types = 0x3ff98a36 ); +PROVIDE ( ld_acl_edr_sizes = 0x3ff98a14 ); +PROVIDE ( ld_acl_edr_types = 0x3ff98a22 ); +PROVIDE ( ld_env = 0x3ffb9510 ); +PROVIDE ( ld_pcm_settings_dft = 0x3ff98a0c ); +PROVIDE ( ld_sched_params = 0x3ffb96c0 ); +PROVIDE ( ld_sync_train_channels = 0x3ff98a3c ); +PROVIDE ( __ledf2 = 0x40063704 ); +PROVIDE ( __lesf2 = 0x400633c0 ); +PROVIDE ( _link_r = 0x4000bc9c ); +PROVIDE ( llc_default_handler = 0x3ff98b3c ); +PROVIDE ( llc_default_state_tab_p_get = 0x40046058 ); +PROVIDE ( llc_env = 0x3ffb96d0 ); +PROVIDE ( llc_hci_acl_data_tx_handler = 0x40042398 ); +PROVIDE ( llc_hci_cmd_handler_tab_p_get = 0x40042358 ); +PROVIDE ( llc_hci_command_handler = 0x40042360 ); +PROVIDE ( llcp_pdu_handler_tab_p_get = 0x40043f64 ); +PROVIDE ( llc_state = 0x3ffb96f8 ); +PROVIDE ( lldesc_build_chain = 0x4000a850 ); +PROVIDE ( lldesc_num2link = 0x4000a948 ); +PROVIDE ( lldesc_set_owner = 0x4000a974 ); +PROVIDE ( lld_evt_deferred_elt_push = 0x400466b4 ); +PROVIDE ( lld_evt_deferred_elt_pop = 0x400466dc ); +PROVIDE ( lld_evt_winsize_change = 0x40046730 ); +PROVIDE ( lld_evt_rxwin_compute = 0x400467c8 ); +PROVIDE ( lld_evt_slave_time_compute = 0x40046818 ); +PROVIDE ( lld_evt_env = 0x3ffb9704 ); +PROVIDE ( lld_evt_elt_wait_get = 0x400468e4 ); +PROVIDE ( lld_evt_get_next_free_slot = 0x4004692c ); +PROVIDE ( lld_pdu_adv_pk_desc_tab = 0x3ff98c70 ); +PROVIDE ( lld_pdu_llcp_pk_desc_tab = 0x3ff98b68 ); +PROVIDE ( lld_pdu_pack = 0x4004ab14 ); +PROVIDE ( LLM_AA_CT1 = 0x3ff98d8a ); +PROVIDE ( LLM_AA_CT2 = 0x3ff98d88 ); +PROVIDE ( llm_default_handler = 0x3ff98d80 ); +PROVIDE ( llm_default_state_tab_p_get = 0x4004e718 ); +PROVIDE ( llm_hci_cmd_handler_tab_p_get = 0x4004c920 ); +PROVIDE ( llm_le_env = 0x3ffb976c ); +PROVIDE ( llm_local_cmds = 0x3ff98d38 ); +PROVIDE ( llm_local_data_len_values = 0x3ff98d1c ); +PROVIDE ( llm_local_le_feats = 0x3ff98d30 ); +PROVIDE ( llm_local_le_states = 0x3ff98d28 ); +PROVIDE ( llm_state = 0x3ffb985c ); +PROVIDE ( lm_default_handler = 0x3ff990e0 ); +PROVIDE ( lm_default_state_tab_p_get = 0x40054268 ); +PROVIDE ( lm_env = 0x3ffb9860 ); +PROVIDE ( lm_hci_cmd_handler_tab_p_get = 0x4005425c ); +PROVIDE ( lm_local_supp_feats = 0x3ff990ee ); +PROVIDE ( lm_n_page_tab = 0x3ff990e8 ); +PROVIDE ( lmp_desc_tab = 0x3ff96e6c ); +PROVIDE ( lmp_ext_desc_tab = 0x3ff96d9c ); +PROVIDE ( lm_state = 0x3ffb9a1c ); +PROVIDE ( _lock_acquire_recursive = 0x4000be28 ); +PROVIDE ( _lock_close = 0x4000bdec ); +PROVIDE ( _lock_close_recursive = 0x4000be00 ); +PROVIDE ( _lock_init = 0x4000bdc4 ); +PROVIDE ( _lock_init_recursive = 0x4000bdd8 ); +PROVIDE ( _lock_release_recursive = 0x4000be78 ); +PROVIDE ( _lock_try_acquire = 0x4000be3c ); +PROVIDE ( _lock_try_acquire_recursive = 0x4000be50 ); +PROVIDE ( _lseek_r = 0x4000bd8c ); +PROVIDE ( __lshrdi3 = 0x4000c84c ); +PROVIDE ( __ltdf2 = 0x40063790 ); +PROVIDE ( __ltsf2 = 0x4006342c ); +PROVIDE ( malloc = 0x4000bea0 ); +PROVIDE ( _malloc_r = 0x4000bbb4 ); +PROVIDE ( maxSecretKey_256 = 0x3ff97448 ); +PROVIDE ( __mb_cur_max = 0x3ff96530 ); +PROVIDE ( MD5Final = 0x4005db1c ); +PROVIDE ( MD5Init = 0x4005da7c ); +PROVIDE ( MD5Update = 0x4005da9c ); +PROVIDE ( md5_vector = 0x4005db80 ); +PROVIDE ( mmu_init = 0x400095a4 ); +PROVIDE ( __moddi3 = 0x4000cd4c ); +PROVIDE ( __modsi3 = 0x4000c7c0 ); +PROVIDE ( __month_lengths = 0x3ff9609c ); +PROVIDE ( __muldc3 = 0x40063bf4 ); +PROVIDE ( __muldf3 = 0x4006358c ); +PROVIDE ( __muldi3 = 0x4000c9fc ); +PROVIDE ( __mulsc3 = 0x40063934 ); +PROVIDE ( __mulsf3 = 0x400632c8 ); +PROVIDE ( __mulsi3 = 0x4000c7b0 ); +PROVIDE ( MultiplyBigHexByUint32_256 = 0x40016214 ); +PROVIDE ( MultiplyBigHexModP256 = 0x400160b8 ); +PROVIDE ( MultiplyByU32ModP256 = 0x40015fdc ); +PROVIDE ( multofup = 0x4000ab8c ); +PROVIDE ( __mulvdi3 = 0x40002d78 ); +PROVIDE ( __mulvsi3 = 0x40002d60 ); +PROVIDE ( mz_adler32 = 0x4005edbc ); +PROVIDE ( mz_crc32 = 0x4005ee88 ); +PROVIDE ( mz_free = 0x4005eed4 ); +PROVIDE ( __nedf2 = 0x400636a8 ); +PROVIDE ( __negdf2 = 0x400634a0 ); +PROVIDE ( __negdi2 = 0x4000ca14 ); +PROVIDE ( __negsf2 = 0x400020c0 ); +PROVIDE ( __negvdi2 = 0x40002e98 ); +PROVIDE ( __negvsi2 = 0x40002e78 ); +PROVIDE ( __nesf2 = 0x40063374 ); +PROVIDE ( notEqual256 = 0x40015b04 ); +PROVIDE ( __nsau_data = 0x3ff96544 ); +PROVIDE ( one_bits = 0x3ff971f8 ); +PROVIDE ( _open_r = 0x4000bd54 ); +PROVIDE ( __paritysi2 = 0x40002f3c ); +PROVIDE ( pbkdf2_sha1 = 0x40060ba4 ); +PROVIDE ( phy_get_romfuncs = 0x40004100 ); +PROVIDE ( __popcountdi2 = 0x40002ef8 ); +PROVIDE ( __popcountsi2 = 0x40002ed0 ); +PROVIDE ( __popcount_tab = 0x3ff96544 ); +PROVIDE ( __powidf2 = 0x400638d4 ); +PROVIDE ( __powisf2 = 0x4006389c ); +PROVIDE ( _Pri_4_HandlerAddress = 0x3ffe0648 ); +PROVIDE ( _Pri_5_HandlerAddress = 0x3ffe064c ); +PROVIDE ( r_btdm_option_data = 0x3ffae6e0 ); +PROVIDE ( r_bt_util_buf_acl_rx_alloc = 0x40010218 ); +PROVIDE ( r_bt_util_buf_acl_rx_free = 0x40010234 ); +PROVIDE ( r_bt_util_buf_acl_tx_alloc = 0x40010268 ); +PROVIDE ( r_bt_util_buf_acl_tx_free = 0x40010280 ); +PROVIDE ( r_bt_util_buf_init = 0x400100e4 ); +PROVIDE ( r_bt_util_buf_lmp_tx_alloc = 0x400101d0 ); +PROVIDE ( r_bt_util_buf_lmp_tx_free = 0x400101ec ); +PROVIDE ( r_bt_util_buf_sync_clear = 0x400103c8 ); +PROVIDE ( r_bt_util_buf_sync_init = 0x400102c4 ); +PROVIDE ( r_bt_util_buf_sync_rx_alloc = 0x40010468 ); +PROVIDE ( r_bt_util_buf_sync_rx_free = 0x4001049c ); +PROVIDE ( r_bt_util_buf_sync_tx_alloc = 0x400103ec ); +PROVIDE ( r_bt_util_buf_sync_tx_free = 0x40010428 ); +PROVIDE ( rc4_skip = 0x40060928 ); +PROVIDE ( r_co_bdaddr_compare = 0x40014324 ); +PROVIDE ( r_co_bytes_to_string = 0x400142e4 ); +PROVIDE ( r_co_list_check_size_available = 0x400142c4 ); +PROVIDE ( r_co_list_extract = 0x4001404c ); +PROVIDE ( r_co_list_extract_after = 0x40014118 ); +PROVIDE ( r_co_list_find = 0x4001419c ); +PROVIDE ( r_co_list_init = 0x40013f14 ); +PROVIDE ( r_co_list_insert_after = 0x40014254 ); +PROVIDE ( r_co_list_insert_before = 0x40014200 ); +PROVIDE ( r_co_list_merge = 0x400141bc ); +PROVIDE ( r_co_list_pool_init = 0x40013f30 ); +PROVIDE ( r_co_list_pop_front = 0x40014028 ); +PROVIDE ( r_co_list_push_back = 0x40013fb8 ); +PROVIDE ( r_co_list_push_front = 0x40013ff4 ); +PROVIDE ( r_co_list_size = 0x400142ac ); +PROVIDE ( r_co_nb_good_channels = 0x40014360 ); +PROVIDE ( r_co_slot_to_duration = 0x40014348 ); +PROVIDE ( r_dbg_init = 0x40014394 ); +PROVIDE ( r_dbg_platform_reset_complete = 0x400143d0 ); +PROVIDE ( r_dbg_swdiag_init = 0x40014470 ); +PROVIDE ( r_dbg_swdiag_read = 0x400144a4 ); +PROVIDE ( r_dbg_swdiag_write = 0x400144d0 ); +PROVIDE ( r_E1 = 0x400108e8 ); +PROVIDE ( r_E21 = 0x40010968 ); +PROVIDE ( r_E22 = 0x400109b4 ); +PROVIDE ( r_E3 = 0x40010a58 ); +PROVIDE ( r_ea_alarm_clear = 0x40015ab4 ); +PROVIDE ( r_ea_alarm_set = 0x40015a10 ); +PROVIDE ( _read_r = 0x4000bda8 ); +PROVIDE ( r_ea_elt_cancel = 0x400150d0 ); +PROVIDE ( r_ea_elt_create = 0x40015264 ); +PROVIDE ( r_ea_elt_insert = 0x400152a8 ); +PROVIDE ( r_ea_elt_remove = 0x400154f0 ); +PROVIDE ( r_ea_finetimer_isr = 0x400155d4 ); +PROVIDE ( r_ea_init = 0x40015228 ); +PROVIDE ( r_ea_interval_create = 0x4001555c ); +PROVIDE ( r_ea_interval_delete = 0x400155a8 ); +PROVIDE ( r_ea_interval_duration_req = 0x4001597c ); +PROVIDE ( r_ea_interval_insert = 0x4001557c ); +PROVIDE ( r_ea_interval_remove = 0x40015590 ); +PROVIDE ( ea_conflict_check = 0x40014e9c ); +PROVIDE ( ea_prog_timer = 0x40014f88 ); +PROVIDE ( realloc = 0x4000becc ); +PROVIDE ( _realloc_r = 0x4000bbe0 ); +PROVIDE ( r_ea_offset_req = 0x40015748 ); +PROVIDE ( r_ea_sleep_check = 0x40015928 ); +PROVIDE ( r_ea_sw_isr = 0x40015724 ); +PROVIDE ( r_ea_time_get_halfslot_rounded = 0x40015894 ); +PROVIDE ( r_ea_time_get_slot_rounded = 0x400158d4 ); +PROVIDE ( r_ecc_abort_key256_generation = 0x40017070 ); +PROVIDE ( r_ecc_generate_key256 = 0x40016e00 ); +PROVIDE ( r_ecc_gen_new_public_key = 0x400170c0 ); +PROVIDE ( r_ecc_gen_new_secret_key = 0x400170e4 ); +PROVIDE ( r_ecc_get_debug_Keys = 0x40017224 ); +PROVIDE ( r_ecc_init = 0x40016dbc ); +PROVIDE ( RecvBuff = 0x3ffe009c ); +PROVIDE ( r_em_buf_init = 0x4001729c ); +PROVIDE ( r_em_buf_rx_buff_addr_get = 0x400173e8 ); +PROVIDE ( r_em_buf_rx_free = 0x400173c4 ); +PROVIDE ( r_em_buf_tx_buff_addr_get = 0x40017404 ); +PROVIDE ( r_em_buf_tx_free = 0x4001741c ); +PROVIDE ( _rename_r = 0x4000bc28 ); +PROVIDE ( r_F1_256 = 0x400133e4 ); +PROVIDE ( r_F2_256 = 0x40013568 ); +PROVIDE ( r_F3_256 = 0x40013664 ); +PROVIDE ( RFPLL_ICP_TABLE = 0x3ffb8b7c ); +PROVIDE ( r_G_256 = 0x40013470 ); +PROVIDE ( r_H3 = 0x40013760 ); +PROVIDE ( r_H4 = 0x40013830 ); +PROVIDE ( r_h4tl_init = 0x40017878 ); +PROVIDE ( r_h4tl_start = 0x40017924 ); +PROVIDE ( r_h4tl_stop = 0x40017934 ); +PROVIDE ( r_h4tl_write = 0x400178d0 ); +PROVIDE ( r_H5 = 0x400138dc ); +PROVIDE ( r_hashConcat = 0x40013a38 ); +PROVIDE ( r_hci_acl_tx_data_alloc = 0x4001951c ); +PROVIDE ( r_hci_acl_tx_data_received = 0x40019654 ); +PROVIDE ( r_hci_bt_acl_bdaddr_register = 0x40018900 ); +PROVIDE ( r_hci_bt_acl_bdaddr_unregister = 0x400189ac ); +PROVIDE ( r_hci_bt_acl_conhdl_register = 0x4001895c ); +PROVIDE ( r_hci_cmd_get_max_param_size = 0x400192d0 ); +PROVIDE ( r_hci_cmd_received = 0x400192f8 ); +PROVIDE ( r_hci_evt_filter_add = 0x40018a64 ); +PROVIDE ( r_hci_evt_mask_set = 0x400189e4 ); +PROVIDE ( r_hci_fc_acl_buf_size_set = 0x40017988 ); +PROVIDE ( r_hci_fc_acl_en = 0x400179d8 ); +PROVIDE ( r_hci_fc_acl_packet_sent = 0x40017a3c ); +PROVIDE ( r_hci_fc_check_host_available_nb_acl_packets = 0x40017aa4 ); +PROVIDE ( r_hci_fc_check_host_available_nb_sync_packets = 0x40017ac8 ); +PROVIDE ( r_hci_fc_host_nb_acl_pkts_complete = 0x40017a6c ); +PROVIDE ( r_hci_fc_host_nb_sync_pkts_complete = 0x40017a88 ); +PROVIDE ( r_hci_fc_init = 0x40017974 ); +PROVIDE ( r_hci_fc_sync_buf_size_set = 0x400179b0 ); +PROVIDE ( r_hci_fc_sync_en = 0x40017a30 ); +PROVIDE ( r_hci_fc_sync_packet_sent = 0x40017a54 ); +PROVIDE ( r_hci_init = 0x40018538 ); +PROVIDE ( r_hci_look_for_cmd_desc = 0x40018454 ); +PROVIDE ( r_hci_look_for_dbg_evt_desc = 0x400184c4 ); +PROVIDE ( r_hci_look_for_evt_desc = 0x400184a0 ); +PROVIDE ( r_hci_look_for_le_evt_desc = 0x400184e0 ); +PROVIDE ( r_hci_reset = 0x4001856c ); +PROVIDE ( r_hci_send_2_host = 0x400185bc ); +PROVIDE ( r_hci_sync_tx_data_alloc = 0x40019754 ); +PROVIDE ( r_hci_sync_tx_data_received = 0x400197c0 ); +PROVIDE ( r_hci_tl_init = 0x40019290 ); +PROVIDE ( r_hci_tl_send = 0x40019228 ); +PROVIDE ( r_hci_util_pack = 0x40019874 ); +PROVIDE ( r_hci_util_unpack = 0x40019998 ); +PROVIDE ( r_hci_voice_settings_get = 0x40018bdc ); +PROVIDE ( r_hci_voice_settings_set = 0x40018be8 ); +PROVIDE ( r_HMAC = 0x40013968 ); +PROVIDE ( r_import_rf_phy_func = 0x3ffb8354 ); +PROVIDE ( r_import_rf_phy_func_p = 0x3ffafd64 ); +PROVIDE ( r_ip_funcs = 0x3ffae710 ); +PROVIDE ( r_ip_funcs_p = 0x3ffae70c ); +PROVIDE ( r_ke_check_malloc = 0x40019de0 ); +PROVIDE ( r_ke_event_callback_set = 0x40019ba8 ); +PROVIDE ( r_ke_event_clear = 0x40019c2c ); +PROVIDE ( r_ke_event_flush = 0x40019ccc ); +PROVIDE ( r_ke_event_get = 0x40019c78 ); +PROVIDE ( r_ke_event_get_all = 0x40019cc0 ); +PROVIDE ( r_ke_event_init = 0x40019b90 ); +PROVIDE ( r_ke_event_schedule = 0x40019cdc ); +PROVIDE ( r_ke_event_set = 0x40019be0 ); +PROVIDE ( r_ke_flush = 0x4001a374 ); +PROVIDE ( r_ke_free = 0x4001a014 ); +PROVIDE ( r_ke_get_max_mem_usage = 0x4001a1c8 ); +PROVIDE ( r_ke_get_mem_usage = 0x4001a1a0 ); +PROVIDE ( r_ke_init = 0x4001a318 ); +PROVIDE ( r_ke_is_free = 0x4001a184 ); +PROVIDE ( r_ke_malloc = 0x40019eb4 ); +PROVIDE ( r_ke_mem_init = 0x40019d3c ); +PROVIDE ( r_ke_mem_is_empty = 0x40019d8c ); +PROVIDE ( r_ke_msg_alloc = 0x4001a1e0 ); +PROVIDE ( r_ke_msg_dest_id_get = 0x4001a2e0 ); +PROVIDE ( r_ke_msg_discard = 0x4001a850 ); +PROVIDE ( r_ke_msg_forward = 0x4001a290 ); +PROVIDE ( r_ke_msg_forward_new_id = 0x4001a2ac ); +PROVIDE ( r_ke_msg_free = 0x4001a2cc ); +PROVIDE ( r_ke_msg_in_queue = 0x4001a2f8 ); +PROVIDE ( r_ke_msg_save = 0x4001a858 ); +PROVIDE ( r_ke_msg_send = 0x4001a234 ); +PROVIDE ( r_ke_msg_send_basic = 0x4001a26c ); +PROVIDE ( r_ke_msg_src_id_get = 0x4001a2ec ); +PROVIDE ( r_ke_queue_extract = 0x40055fd0 ); +PROVIDE ( r_ke_queue_insert = 0x40056020 ); +PROVIDE ( r_ke_sleep_check = 0x4001a3d8 ); +PROVIDE ( r_ke_state_get = 0x4001a7d8 ); +PROVIDE ( r_ke_state_set = 0x4001a6fc ); +PROVIDE ( r_ke_stats_get = 0x4001a3f0 ); +PROVIDE ( r_ke_task_check = 0x4001a8a4 ); +PROVIDE ( r_ke_task_create = 0x4001a674 ); +PROVIDE ( r_ke_task_delete = 0x4001a6c0 ); +PROVIDE ( r_ke_task_init = 0x4001a650 ); +PROVIDE ( r_ke_task_msg_flush = 0x4001a860 ); +PROVIDE ( r_ke_timer_active = 0x4001ac08 ); +PROVIDE ( r_ke_timer_adjust_all = 0x4001ac30 ); +PROVIDE ( r_ke_timer_clear = 0x4001ab90 ); +PROVIDE ( r_ke_timer_init = 0x4001aa9c ); +PROVIDE ( r_ke_timer_set = 0x4001aac0 ); +PROVIDE ( r_ke_timer_sleep_check = 0x4001ac50 ); +PROVIDE ( r_KPrimC = 0x40010ad4 ); +PROVIDE ( r_lb_clk_adj_activate = 0x4001ae70 ); +PROVIDE ( r_lb_clk_adj_id_get = 0x4001af14 ); +PROVIDE ( r_lb_clk_adj_period_update = 0x4001af20 ); +PROVIDE ( r_lb_init = 0x4001acd4 ); +PROVIDE ( r_lb_mst_key = 0x4001afc0 ); +PROVIDE ( r_lb_mst_key_cmp = 0x4001af74 ); +PROVIDE ( r_lb_mst_key_restart_enc = 0x4001b0d4 ); +PROVIDE ( r_lb_mst_start_act_bcst_enc = 0x4001b198 ); +PROVIDE ( r_lb_mst_stop_act_bcst_enc = 0x4001b24c ); +PROVIDE ( r_lb_reset = 0x4001ad38 ); +PROVIDE ( r_lb_send_lmp = 0x4001adbc ); +PROVIDE ( r_lb_send_pdu_clk_adj = 0x4001af3c ); +PROVIDE ( r_lb_util_get_csb_mode = 0x4001ada4 ); +PROVIDE ( r_lb_util_get_nb_broadcast = 0x4001ad80 ); +PROVIDE ( r_lb_util_get_res_lt_addr = 0x4001ad98 ); +PROVIDE ( r_lb_util_set_nb_broadcast = 0x4001ad8c ); +PROVIDE ( r_lc_afh_set = 0x4001cc74 ); +PROVIDE ( r_lc_afh_start = 0x4001d240 ); +PROVIDE ( r_lc_auth_cmp = 0x4001cd54 ); +PROVIDE ( r_lc_calc_link_key = 0x4001ce7c ); +PROVIDE ( r_lc_chg_pkt_type_cmp = 0x4001d038 ); +PROVIDE ( r_lc_chg_pkt_type_cont = 0x4001cfbc ); +PROVIDE ( r_lc_chg_pkt_type_retry = 0x4001d0ac ); +PROVIDE ( r_lc_chk_to = 0x4001d2a8 ); +PROVIDE ( r_lc_cmd_stat_send = 0x4001c914 ); +PROVIDE ( r_lc_comb_key_svr = 0x4001d30c ); +PROVIDE ( r_lc_con_cmp = 0x4001d44c ); +PROVIDE ( r_lc_con_cmp_evt_send = 0x4001d4fc ); +PROVIDE ( r_lc_conn_seq_done = 0x40021334 ); +PROVIDE ( r_lc_detach = 0x4002037c ); +PROVIDE ( r_lc_dhkey = 0x4001d564 ); +PROVIDE ( r_lc_enc_cmp = 0x4001d8bc ); +PROVIDE ( r_lc_enc_key_refresh = 0x4001d720 ); +PROVIDE ( r_lc_end_chk_colli = 0x4001d858 ); +PROVIDE ( r_lc_end_of_sniff_nego = 0x4001d9a4 ); +PROVIDE ( r_lc_enter_sniff_mode = 0x4001ddb8 ); +PROVIDE ( r_lc_epr_change_lk = 0x4001db38 ); +PROVIDE ( r_lc_epr_cmp = 0x4001da88 ); +PROVIDE ( r_lc_epr_resp = 0x4001e0b4 ); +PROVIDE ( r_lc_epr_rsw_cmp = 0x4001dd40 ); +PROVIDE ( r_lc_ext_feat = 0x40020d6c ); +PROVIDE ( r_lc_feat = 0x40020984 ); +PROVIDE ( r_lc_hl_connect = 0x400209e8 ); +PROVIDE ( r_lc_init = 0x4001c948 ); +PROVIDE ( r_lc_init_calc_f3 = 0x4001deb0 ); +PROVIDE ( r_lc_initiator_epr = 0x4001e064 ); +PROVIDE ( r_lc_init_passkey_loop = 0x4001dfc0 ); +PROVIDE ( r_lc_init_start_mutual_auth = 0x4001df60 ); +PROVIDE ( r_lc_key_exch_end = 0x4001e140 ); +PROVIDE ( r_lc_legacy_pair = 0x4001e1c0 ); +PROVIDE ( r_lc_local_switch = 0x4001e22c ); +PROVIDE ( r_lc_local_trans_mode = 0x4001e2e4 ); +PROVIDE ( r_lc_local_untrans_mode = 0x4001e3a0 ); +PROVIDE ( r_lc_loc_auth = 0x40020ecc ); +PROVIDE ( r_lc_locepr_lkref = 0x4001d648 ); +PROVIDE ( r_lc_locepr_rsw = 0x4001d5d0 ); +PROVIDE ( r_lc_loc_sniff = 0x40020a6c ); +PROVIDE ( r_lc_max_slot_mgt = 0x4001e410 ); +PROVIDE ( r_lc_mst_key = 0x4001e7c0 ); +PROVIDE ( r_lc_mst_qos_done = 0x4001ea80 ); +PROVIDE ( r_lc_mst_send_mst_key = 0x4001e8f4 ); +PROVIDE ( r_lc_mutual_auth_end = 0x4001e670 ); +PROVIDE ( r_lc_mutual_auth_end2 = 0x4001e4f4 ); +PROVIDE ( r_lc_packet_type = 0x40021038 ); +PROVIDE ( r_lc_pair = 0x40020ddc ); +PROVIDE ( r_lc_pairing_cont = 0x4001eafc ); +PROVIDE ( r_lc_passkey_comm = 0x4001ed20 ); +PROVIDE ( r_lc_prepare_all_links_for_clk_adj = 0x40021430 ); +PROVIDE ( r_lc_proc_rcv_dhkey = 0x4001edec ); +PROVIDE ( r_lc_ptt = 0x4001ee2c ); +PROVIDE ( r_lc_ptt_cmp = 0x4001eeec ); +PROVIDE ( r_lc_qos_setup = 0x4001ef50 ); +PROVIDE ( r_lc_rd_rem_name = 0x4001efd0 ); +PROVIDE ( r_lc_release = 0x4001f8a8 ); +PROVIDE ( r_lc_rem_enc = 0x4001f124 ); +PROVIDE ( r_lc_rem_name_cont = 0x4001f290 ); +PROVIDE ( r_lc_rem_nego_trans_mode = 0x4001f1b4 ); +PROVIDE ( r_lc_rem_sniff = 0x40020ca4 ); +PROVIDE ( r_lc_rem_sniff_sub_rate = 0x40020b10 ); +PROVIDE ( r_lc_rem_switch = 0x4001f070 ); +PROVIDE ( r_lc_rem_trans_mode = 0x4001f314 ); +PROVIDE ( r_lc_rem_unsniff = 0x400207a0 ); +PROVIDE ( r_lc_rem_untrans_mode = 0x4001f36c ); +PROVIDE ( r_lc_reset = 0x4001c99c ); +PROVIDE ( r_lc_resp_auth = 0x4001f518 ); +PROVIDE ( r_lc_resp_calc_f3 = 0x4001f710 ); +PROVIDE ( r_lc_resp_num_comp = 0x40020074 ); +PROVIDE ( r_lc_resp_oob_nonce = 0x4001f694 ); +PROVIDE ( r_lc_resp_oob_wait_nonce = 0x4001f66c ); +PROVIDE ( r_lc_resp_pair = 0x400208a4 ); +PROVIDE ( r_lc_resp_sec_auth = 0x4001f4a0 ); +PROVIDE ( r_lc_resp_wait_dhkey_cont = 0x4001f86c ); +PROVIDE ( r_lc_restart_enc = 0x4001f8ec ); +PROVIDE ( r_lc_restart_enc_cont = 0x4001f940 ); +PROVIDE ( r_lc_restore_afh_reporting = 0x4001f028 ); +PROVIDE ( r_lc_restore_to = 0x4001f9e0 ); +PROVIDE ( r_lc_ret_sniff_max_slot_chg = 0x4001fa30 ); +PROVIDE ( r_lc_rsw_clean_up = 0x4001dc70 ); +PROVIDE ( r_lc_rsw_done = 0x4001db94 ); +PROVIDE ( r_lc_sco_baseband_ack = 0x40022b00 ); +PROVIDE ( r_lc_sco_detach = 0x40021e40 ); +PROVIDE ( r_lc_sco_host_accept = 0x40022118 ); +PROVIDE ( r_lc_sco_host_reject = 0x400222b8 ); +PROVIDE ( r_lc_sco_host_request = 0x40021f4c ); +PROVIDE ( r_lc_sco_host_request_disc = 0x4002235c ); +PROVIDE ( r_lc_sco_init = 0x40021dc8 ); +PROVIDE ( r_lc_sco_peer_accept = 0x40022780 ); +PROVIDE ( r_lc_sco_peer_accept_disc = 0x40022a08 ); +PROVIDE ( r_lc_sco_peer_reject = 0x40022824 ); +PROVIDE ( r_lc_sco_peer_reject_disc = 0x40022a8c ); +PROVIDE ( r_lc_sco_peer_request = 0x4002240c ); +PROVIDE ( r_lc_sco_peer_request_disc = 0x400228ec ); +PROVIDE ( r_lc_sco_release = 0x40021eec ); +PROVIDE ( r_lc_sco_reset = 0x40021dfc ); +PROVIDE ( r_lc_sco_timeout = 0x40022bd4 ); +PROVIDE ( r_lc_sec_auth_compute_sres = 0x4001f3ec ); +PROVIDE ( r_lc_semi_key_cmp = 0x40020294 ); +PROVIDE ( r_lc_send_enc_chg_evt = 0x4002134c ); +PROVIDE ( r_lc_send_enc_mode = 0x40020220 ); +PROVIDE ( r_lc_send_lmp = 0x4001c1a8 ); +PROVIDE ( r_lc_send_pdu_acc = 0x4001c21c ); +PROVIDE ( r_lc_send_pdu_acc_ext4 = 0x4001c240 ); +PROVIDE ( r_lc_send_pdu_au_rand = 0x4001c308 ); +PROVIDE ( r_lc_send_pdu_auto_rate = 0x4001c5d0 ); +PROVIDE ( r_lc_send_pdu_clk_adj_ack = 0x4001c46c ); +PROVIDE ( r_lc_send_pdu_clk_adj_req = 0x4001c494 ); +PROVIDE ( r_lc_send_pdu_comb_key = 0x4001c368 ); +PROVIDE ( r_lc_send_pdu_dhkey_chk = 0x4001c8e8 ); +PROVIDE ( r_lc_send_pdu_encaps_head = 0x4001c440 ); +PROVIDE ( r_lc_send_pdu_encaps_payl = 0x4001c410 ); +PROVIDE ( r_lc_send_pdu_enc_key_sz_req = 0x4001c670 ); +PROVIDE ( r_lc_send_pdu_esco_lk_rem_req = 0x4001c5a8 ); +PROVIDE ( r_lc_send_pdu_feats_ext_req = 0x4001c6ec ); +PROVIDE ( r_lc_send_pdu_feats_res = 0x4001c694 ); +PROVIDE ( r_lc_send_pdu_in_rand = 0x4001c338 ); +PROVIDE ( r_lc_send_pdu_io_cap_res = 0x4001c72c ); +PROVIDE ( r_lc_send_pdu_lsto = 0x4001c64c ); +PROVIDE ( r_lc_send_pdu_max_slot = 0x4001c3c8 ); +PROVIDE ( r_lc_send_pdu_max_slot_req = 0x4001c3ec ); +PROVIDE ( r_lc_send_pdu_not_acc = 0x4001c26c ); +PROVIDE ( r_lc_send_pdu_not_acc_ext4 = 0x4001c294 ); +PROVIDE ( r_lc_send_pdu_num_comp_fail = 0x4001c770 ); +PROVIDE ( r_lc_send_pdu_pause_enc_aes_req = 0x4001c794 ); +PROVIDE ( r_lc_send_pdu_paus_enc_req = 0x4001c7c0 ); +PROVIDE ( r_lc_send_pdu_ptt_req = 0x4001c4c0 ); +PROVIDE ( r_lc_send_pdu_qos_req = 0x4001c82c ); +PROVIDE ( r_lc_send_pdu_resu_enc_req = 0x4001c7e4 ); +PROVIDE ( r_lc_send_pdu_sco_lk_rem_req = 0x4001c580 ); +PROVIDE ( r_lc_send_pdu_set_afh = 0x4001c2c8 ); +PROVIDE ( r_lc_send_pdu_setup_cmp = 0x4001c808 ); +PROVIDE ( r_lc_send_pdu_slot_off = 0x4001c854 ); +PROVIDE ( r_lc_send_pdu_sniff_req = 0x4001c5f0 ); +PROVIDE ( r_lc_send_pdu_sp_cfm = 0x4001c518 ); +PROVIDE ( r_lc_send_pdu_sp_nb = 0x4001c4e8 ); +PROVIDE ( r_lc_send_pdu_sres = 0x4001c548 ); +PROVIDE ( r_lc_send_pdu_tim_acc = 0x4001c6cc ); +PROVIDE ( r_lc_send_pdu_unit_key = 0x4001c398 ); +PROVIDE ( r_lc_send_pdu_unsniff_req = 0x4001c894 ); +PROVIDE ( r_lc_send_pdu_vers_req = 0x4001c8b4 ); +PROVIDE ( r_lc_skip_hl_oob_req = 0x400201bc ); +PROVIDE ( r_lc_sniff_init = 0x40022cac ); +PROVIDE ( r_lc_sniff_max_slot_chg = 0x40020590 ); +PROVIDE ( r_lc_sniff_reset = 0x40022cc8 ); +PROVIDE ( r_lc_sniff_slot_unchange = 0x40021100 ); +PROVIDE ( r_lc_sniff_sub_mode = 0x400204fc ); +PROVIDE ( r_lc_sp_end = 0x400213a8 ); +PROVIDE ( r_lc_sp_fail = 0x40020470 ); +PROVIDE ( r_lc_sp_oob_tid_fail = 0x400204cc ); +PROVIDE ( r_lc_ssr_nego = 0x4002125c ); +PROVIDE ( r_lc_start = 0x4001ca28 ); +PROVIDE ( r_lc_start_enc = 0x4001fb28 ); +PROVIDE ( r_lc_start_enc_key_size = 0x4001fd9c ); +PROVIDE ( r_lc_start_key_exch = 0x4001fe10 ); +PROVIDE ( r_lc_start_lmp_to = 0x4001fae8 ); +PROVIDE ( r_lc_start_oob = 0x4001fffc ); +PROVIDE ( r_lc_start_passkey = 0x4001feac ); +PROVIDE ( r_lc_start_passkey_loop = 0x4001ff88 ); +PROVIDE ( r_lc_stop_afh_report = 0x40020184 ); +PROVIDE ( r_lc_stop_enc = 0x40020110 ); +PROVIDE ( r_lc_switch_cmp = 0x40020448 ); +PROVIDE ( r_lc_unit_key_svr = 0x400206d8 ); +PROVIDE ( r_lc_unsniff = 0x40020c50 ); +PROVIDE ( r_lc_unsniff_cmp = 0x40020810 ); +PROVIDE ( r_lc_unsniff_cont = 0x40020750 ); +PROVIDE ( r_lc_upd_to = 0x4002065c ); +PROVIDE ( r_lc_util_convert_pref_rate_to_packet_type = 0x4002f9b0 ); +PROVIDE ( r_lc_util_get_max_packet_size = 0x4002f4ac ); +PROVIDE ( r_lc_util_get_offset_clke = 0x4002f538 ); +PROVIDE ( r_lc_util_get_offset_clkn = 0x4002f51c ); +PROVIDE ( r_lc_util_set_loc_trans_coll = 0x4002f500 ); +PROVIDE ( r_lc_version = 0x40020a30 ); +PROVIDE ( lmp_accepted_ext_handler = 0x40027290 ); +PROVIDE ( lmp_not_accepted_ext_handler = 0x40029c54 ); +PROVIDE ( lmp_clk_adj_handler = 0x40027468 ); +PROVIDE ( lmp_clk_adj_ack_handler = 0x400274f4 ); +PROVIDE ( lmp_clk_adj_req_handler = 0x4002751c ); +PROVIDE ( lmp_feats_res_ext_handler = 0x4002cac4 ); +PROVIDE ( lmp_feats_req_ext_handler = 0x4002ccb0 ); +PROVIDE ( lmp_pkt_type_tbl_req_handler = 0x40027574 ); +PROVIDE ( lmp_esco_link_req_handler = 0x40027610 ); +PROVIDE ( lmp_rmv_esco_link_req_handler = 0x400276e8 ); +PROVIDE ( lmp_ch_class_req_handler = 0x40027730 ); +PROVIDE ( lmp_ch_class_handler = 0x4002ca18 ); +PROVIDE ( lmp_ssr_req_handler = 0x4002780c ); +PROVIDE ( lmp_ssr_res_handler = 0x40027900 ); +PROVIDE ( lmp_pause_enc_aes_req_handler = 0x400279a4 ); +PROVIDE ( lmp_pause_enc_req_handler = 0x4002df90 ); +PROVIDE ( lmp_resume_enc_req_handler = 0x4002e084 ); +PROVIDE ( lmp_num_comparison_fail_handler = 0x40027a74 ); +PROVIDE ( lmp_passkey_fail_handler = 0x40027aec ); +PROVIDE ( lmp_keypress_notif_handler = 0x4002c5c8 ); +PROVIDE ( lmp_pwr_ctrl_req_handler = 0x400263bc ); +PROVIDE ( lmp_pwr_ctrl_res_handler = 0x40026480 ); +PROVIDE ( lmp_auto_rate_handler = 0x40026548 ); +PROVIDE ( lmp_pref_rate_handler = 0x4002657c ); +PROVIDE ( lmp_name_req_handler = 0x40025050 ); +PROVIDE ( lmp_name_res_handler = 0x400250bc ); +PROVIDE ( lmp_not_accepted_handler = 0x400251d0 ); +PROVIDE ( lmp_accepted_handler = 0x4002e894 ); +PROVIDE ( lmp_clk_off_req_handler = 0x40025a44 ); +PROVIDE ( lmp_clk_off_res_handler = 0x40025ab8 ); +PROVIDE ( lmp_detach_handler = 0x40025b74 ); +PROVIDE ( lmp_tempkey_handler = 0x4002b6b0 ); +PROVIDE ( lmp_temprand_handler = 0x4002b74c ); +PROVIDE ( lmp_sres_handler = 0x4002b840 ); +PROVIDE ( lmp_aurand_handler = 0x4002bda0 ); +PROVIDE ( lmp_unitkey_handler = 0x4002c13c ); +PROVIDE ( lmp_combkey_handler = 0x4002c234 ); +PROVIDE ( lmp_inrand_handler = 0x4002c414 ); +PROVIDE ( lmp_oob_fail_handler = 0x40027b84 ); +PROVIDE ( lmp_ping_req_handler = 0x40027c08 ); +PROVIDE ( lmp_ping_res_handler = 0x40027c5c ); +PROVIDE ( lmp_enc_mode_req_handler = 0x40025c60 ); +PROVIDE ( lmp_enc_key_size_req_handler = 0x40025e54 ); +PROVIDE ( lmp_switch_req_handler = 0x40025f84 ); +PROVIDE ( lmp_start_enc_req_handler = 0x4002e124 ); +PROVIDE ( lmp_stop_enc_req_handler = 0x4002de30 ); +PROVIDE ( lmp_sniff_req_handler = 0x400260c8 ); +PROVIDE ( lmp_unsniff_req_handler = 0x400261e0 ); +PROVIDE ( lmp_incr_pwr_req_handler = 0x4002629c ); +PROVIDE ( lmp_decr_pwr_req_handler = 0x400262f8 ); +PROVIDE ( lmp_max_pwr_handler = 0x40026354 ); +PROVIDE ( lmp_min_pwr_handler = 0x40026388 ); +PROVIDE ( lmp_ver_req_handler = 0x400265f0 ); +PROVIDE ( lmp_ver_res_handler = 0x40026670 ); +PROVIDE ( lmp_qos_handler = 0x40026790 ); +PROVIDE ( lmp_qos_req_handler = 0x40026844 ); +PROVIDE ( lmp_sco_link_req_handler = 0x40026930 ); +PROVIDE ( lmp_rmv_sco_link_req_handler = 0x40026a10 ); +PROVIDE ( lmp_max_slot_handler = 0x40026a54 ); +PROVIDE ( lmp_max_slot_req_handler = 0x40026aac ); +PROVIDE ( lmp_timing_accu_req_handler = 0x40026b54 ); +PROVIDE ( lmp_timing_accu_res_handler = 0x40026bcc ); +PROVIDE ( lmp_setup_cmp_handler = 0x40026c84 ); +PROVIDE ( lmp_feats_res_handler = 0x4002b548 ); +PROVIDE ( lmp_feats_req_handler = 0x4002b620 ); +PROVIDE ( lmp_host_con_req_handler = 0x4002b3d8 ); +PROVIDE ( lmp_use_semi_perm_key_handler = 0x4002b4c4 ); +PROVIDE ( lmp_slot_off_handler = 0x40026cc8 ); +PROVIDE ( lmp_page_mode_req_handler = 0x40026d0c ); +PROVIDE ( lmp_page_scan_mode_req_handler = 0x40026d4c ); +PROVIDE ( lmp_supv_to_handler = 0x40026d94 ); +PROVIDE ( lmp_test_activate_handler = 0x40026e7c ); +PROVIDE ( lmp_test_ctrl_handler = 0x40026ee4 ); +PROVIDE ( lmp_enc_key_size_mask_req_handler = 0x40027038 ); +PROVIDE ( lmp_enc_key_size_mask_res_handler = 0x400270a4 ); +PROVIDE ( lmp_set_afh_handler = 0x4002b2e4 ); +PROVIDE ( lmp_encaps_hdr_handler = 0x40027120 ); +PROVIDE ( lmp_encaps_payl_handler = 0x4002e590 ); +PROVIDE ( lmp_sp_nb_handler = 0x4002acf0 ); +PROVIDE ( lmp_sp_cfm_handler = 0x4002b170 ); +PROVIDE ( lmp_dhkey_chk_handler = 0x4002ab48 ); +PROVIDE ( lmp_pause_enc_aes_req_handler = 0x400279a4 ); +PROVIDE ( lmp_io_cap_res_handler = 0x4002c670 ); +PROVIDE ( lmp_io_cap_req_handler = 0x4002c7a4 ); +PROVIDE ( ld_acl_tx_packet_type_select = 0x4002fb40 ); +PROVIDE ( ld_acl_sched = 0x40033268 ); +PROVIDE ( ld_acl_sniff_sched = 0x4003340c ); +PROVIDE ( lm_cmd_cmp_send = 0x40051838 ); +PROVIDE ( r_ld_acl_active_hop_types_get = 0x40036e10 ); +PROVIDE ( r_ld_acl_afh_confirm = 0x40036d40 ); +PROVIDE ( r_ld_acl_afh_prepare = 0x40036c84 ); +PROVIDE ( r_ld_acl_afh_set = 0x40036b60 ); +PROVIDE ( r_ld_acl_allowed_tx_packet_types_set = 0x40036810 ); +PROVIDE ( r_ld_acl_bcst_rx_dec = 0x40036394 ); +PROVIDE ( r_ld_acl_bit_off_get = 0x40036b18 ); +PROVIDE ( r_ld_acl_clk_adj_set = 0x40036a00 ); +PROVIDE ( r_ld_acl_clk_off_get = 0x40036b00 ); +PROVIDE ( r_ld_acl_clk_set = 0x40036950 ); +PROVIDE ( r_ld_acl_clock_offset_get = 0x400364c0 ); +PROVIDE ( r_ld_acl_current_tx_power_get = 0x400368f0 ); +PROVIDE ( r_ld_acl_data_flush = 0x400357bc ); +PROVIDE ( r_ld_acl_data_tx = 0x4003544c ); +PROVIDE ( r_ld_acl_edr_set = 0x4003678c ); +PROVIDE ( r_ld_acl_enc_key_load = 0x40036404 ); +PROVIDE ( r_ld_acl_flow_off = 0x40035400 ); +PROVIDE ( r_ld_acl_flow_on = 0x4003541c ); +PROVIDE ( r_ld_acl_flush_timeout_get = 0x40035f9c ); +PROVIDE ( r_ld_acl_flush_timeout_set = 0x40035fe0 ); +PROVIDE ( r_ld_acl_init = 0x40034d08 ); +PROVIDE ( r_ld_acl_lmp_flush = 0x40035d80 ); +PROVIDE ( r_ld_acl_lmp_tx = 0x40035b34 ); +PROVIDE ( r_ld_acl_lsto_get = 0x400366b4 ); +PROVIDE ( r_ld_acl_lsto_set = 0x400366f8 ); +PROVIDE ( r_ld_acl_reset = 0x40034d24 ); +PROVIDE ( r_ld_acl_role_get = 0x40036b30 ); +PROVIDE ( r_ld_acl_rssi_delta_get = 0x40037028 ); +PROVIDE ( r_ld_acl_rsw_req = 0x40035e74 ); +PROVIDE ( r_ld_acl_rx_enc = 0x40036344 ); +PROVIDE ( r_ld_acl_rx_max_slot_get = 0x40036e58 ); +PROVIDE ( r_ld_acl_rx_max_slot_set = 0x40036ea0 ); +PROVIDE ( r_ld_acl_slot_offset_get = 0x4003653c ); +PROVIDE ( r_ld_acl_slot_offset_set = 0x40036658 ); +PROVIDE ( r_ld_acl_sniff = 0x4003617c ); +PROVIDE ( r_ld_acl_sniff_trans = 0x400360a8 ); +PROVIDE ( r_ld_acl_ssr_set = 0x40036274 ); +PROVIDE ( r_ld_acl_start = 0x40034ddc ); +PROVIDE ( r_ld_acl_stop = 0x4003532c ); +PROVIDE ( r_ld_acl_test_mode_set = 0x40036f24 ); +PROVIDE ( r_ld_acl_timing_accuracy_set = 0x4003673c ); +PROVIDE ( r_ld_acl_t_poll_get = 0x40036024 ); +PROVIDE ( r_ld_acl_t_poll_set = 0x40036068 ); +PROVIDE ( r_ld_acl_tx_enc = 0x400362f8 ); +PROVIDE ( r_ld_acl_unsniff = 0x400361e0 ); +PROVIDE ( r_ld_active_check = 0x4003cac4 ); +PROVIDE ( r_ld_afh_ch_assess_data_get = 0x4003caec ); +PROVIDE ( r_ld_bcst_acl_data_tx = 0x40038d3c ); +PROVIDE ( r_ld_bcst_acl_init = 0x40038bd0 ); +PROVIDE ( r_ld_bcst_acl_reset = 0x40038bdc ); +PROVIDE ( r_ld_bcst_acl_start = 0x4003882c ); +PROVIDE ( r_ld_bcst_afh_update = 0x40038f3c ); +PROVIDE ( r_ld_bcst_enc_key_load = 0x4003906c ); +PROVIDE ( r_ld_bcst_lmp_tx = 0x40038bf8 ); +PROVIDE ( r_ld_bcst_tx_enc = 0x40038ff8 ); +PROVIDE ( r_ld_bd_addr_get = 0x4003ca20 ); +PROVIDE ( r_ld_channel_assess = 0x4003c184 ); +PROVIDE ( r_ld_class_of_dev_get = 0x4003ca34 ); +PROVIDE ( r_ld_class_of_dev_set = 0x4003ca50 ); +PROVIDE ( r_ld_csb_rx_afh_update = 0x40039af4 ); +PROVIDE ( r_ld_csb_rx_init = 0x40039690 ); +PROVIDE ( r_ld_csb_rx_reset = 0x4003969c ); +PROVIDE ( r_ld_csb_rx_start = 0x4003972c ); +PROVIDE ( r_ld_csb_rx_stop = 0x40039bb8 ); +PROVIDE ( r_ld_csb_tx_afh_update = 0x4003a5fc ); +PROVIDE ( r_ld_csb_tx_clr_data = 0x4003a71c ); +PROVIDE ( r_ld_csb_tx_dis = 0x4003a5e8 ); +PROVIDE ( r_ld_csb_tx_en = 0x4003a1c0 ); +PROVIDE ( r_ld_csb_tx_init = 0x4003a0e8 ); +PROVIDE ( r_ld_csb_tx_reset = 0x4003a0f8 ); +PROVIDE ( r_ld_csb_tx_set_data = 0x4003a6c0 ); +PROVIDE ( r_ld_fm_clk_isr = 0x4003a7a8 ); +PROVIDE ( r_ld_fm_frame_isr = 0x4003a82c ); +PROVIDE ( r_ld_fm_init = 0x4003a760 ); +PROVIDE ( r_ld_fm_prog_check = 0x4003ab28 ); +PROVIDE ( r_ld_fm_prog_disable = 0x4003a984 ); +PROVIDE ( r_ld_fm_prog_enable = 0x4003a944 ); +PROVIDE ( r_ld_fm_prog_push = 0x4003a9d4 ); +PROVIDE ( r_ld_fm_reset = 0x4003a794 ); +PROVIDE ( r_ld_fm_rx_isr = 0x4003a7f4 ); +PROVIDE ( r_ld_fm_sket_isr = 0x4003a8a4 ); +PROVIDE ( r_ld_init = 0x4003c294 ); +PROVIDE ( r_ld_inq_init = 0x4003b15c ); +PROVIDE ( r_ld_inq_reset = 0x4003b168 ); +PROVIDE ( r_ld_inq_start = 0x4003b1f0 ); +PROVIDE ( r_ld_inq_stop = 0x4003b4f0 ); +PROVIDE ( r_ld_iscan_eir_get = 0x4003c118 ); +PROVIDE ( r_ld_iscan_eir_set = 0x4003bfa0 ); +PROVIDE ( r_ld_iscan_init = 0x4003b9f0 ); +PROVIDE ( r_ld_iscan_reset = 0x4003ba14 ); +PROVIDE ( r_ld_iscan_restart = 0x4003ba44 ); +PROVIDE ( r_ld_iscan_start = 0x4003bb28 ); +PROVIDE ( r_ld_iscan_stop = 0x4003bf1c ); +PROVIDE ( r_ld_iscan_tx_pwr_get = 0x4003c138 ); +PROVIDE ( r_ld_page_init = 0x4003d808 ); +PROVIDE ( r_ld_page_reset = 0x4003d814 ); +PROVIDE ( r_ld_page_start = 0x4003d848 ); +PROVIDE ( r_ld_page_stop = 0x4003da54 ); +PROVIDE ( r_ld_pca_coarse_clock_adjust = 0x4003e324 ); +PROVIDE ( r_ld_pca_init = 0x4003deb4 ); +PROVIDE ( r_ld_pca_initiate_clock_dragging = 0x4003e4ac ); +PROVIDE ( r_ld_pca_local_config = 0x4003df6c ); +PROVIDE ( r_ld_pca_mws_frame_sync = 0x4003e104 ); +PROVIDE ( r_ld_pca_mws_moment_offset_gt = 0x4003e278 ); +PROVIDE ( r_ld_pca_mws_moment_offset_lt = 0x4003e280 ); +PROVIDE ( r_ld_pca_reporting_enable = 0x4003e018 ); +PROVIDE ( r_ld_pca_reset = 0x4003df0c ); +PROVIDE ( r_ld_pca_update_target_offset = 0x4003e050 ); +PROVIDE ( r_ld_pscan_evt_handler = 0x4003f238 ); +PROVIDE ( r_ld_pscan_init = 0x4003f474 ); +PROVIDE ( r_ld_pscan_reset = 0x4003f498 ); +PROVIDE ( r_ld_pscan_restart = 0x4003f4b8 ); +PROVIDE ( r_ld_pscan_start = 0x4003f514 ); +PROVIDE ( r_ld_pscan_stop = 0x4003f618 ); +PROVIDE ( r_ld_read_clock = 0x4003c9e4 ); +PROVIDE ( r_ld_reset = 0x4003c714 ); +PROVIDE ( r_ld_sched_acl_add = 0x4003f978 ); +PROVIDE ( r_ld_sched_acl_remove = 0x4003f99c ); +PROVIDE ( r_ld_sched_compute = 0x4003f6f8 ); +PROVIDE ( r_ld_sched_init = 0x4003f7ac ); +PROVIDE ( r_ld_sched_inq_add = 0x4003f8a8 ); +PROVIDE ( r_ld_sched_inq_remove = 0x4003f8d0 ); +PROVIDE ( r_ld_sched_iscan_add = 0x4003f7e8 ); +PROVIDE ( r_ld_sched_iscan_remove = 0x4003f808 ); +PROVIDE ( r_ld_sched_page_add = 0x4003f910 ); +PROVIDE ( r_ld_sched_page_remove = 0x4003f938 ); +PROVIDE ( r_ld_sched_pscan_add = 0x4003f828 ); +PROVIDE ( r_ld_sched_pscan_remove = 0x4003f848 ); +PROVIDE ( r_ld_sched_reset = 0x4003f7d4 ); +PROVIDE ( r_ld_sched_sco_add = 0x4003fa4c ); +PROVIDE ( r_ld_sched_sco_remove = 0x4003fa9c ); +PROVIDE ( r_ld_sched_sniff_add = 0x4003f9c4 ); +PROVIDE ( r_ld_sched_sniff_remove = 0x4003fa0c ); +PROVIDE ( r_ld_sched_sscan_add = 0x4003f868 ); +PROVIDE ( r_ld_sched_sscan_remove = 0x4003f888 ); +PROVIDE ( r_ld_sco_audio_isr = 0x40037cc8 ); +PROVIDE ( r_ld_sco_data_tx = 0x40037ee8 ); +PROVIDE ( r_ld_sco_start = 0x40037110 ); +PROVIDE ( r_ld_sco_stop = 0x40037c40 ); +PROVIDE ( r_ld_sco_update = 0x40037a74 ); +PROVIDE ( r_ld_sscan_activated = 0x4004031c ); +PROVIDE ( r_ld_sscan_init = 0x400402f0 ); +PROVIDE ( r_ld_sscan_reset = 0x400402fc ); +PROVIDE ( r_ld_sscan_start = 0x40040384 ); +PROVIDE ( r_ld_strain_init = 0x400409f4 ); +PROVIDE ( r_ld_strain_reset = 0x40040a00 ); +PROVIDE ( r_ld_strain_start = 0x40040a8c ); +PROVIDE ( r_ld_strain_stop = 0x40040df0 ); +PROVIDE ( r_ld_timing_accuracy_get = 0x4003caac ); +PROVIDE ( r_ld_util_active_master_afh_map_get = 0x4004131c ); +PROVIDE ( r_ld_util_active_master_afh_map_set = 0x40041308 ); +PROVIDE ( r_ld_util_bch_create = 0x40040fcc ); +PROVIDE ( r_ld_util_fhs_pk = 0x400411c8 ); +PROVIDE ( r_ld_util_fhs_unpk = 0x40040e54 ); +PROVIDE ( r_ld_util_stp_pk = 0x400413f4 ); +PROVIDE ( r_ld_util_stp_unpk = 0x40041324 ); +PROVIDE ( r_ld_version_get = 0x4003ca6c ); +PROVIDE ( r_ld_wlcoex_set = 0x4003caf8 ); +PROVIDE ( r_llc_ch_assess_get_current_ch_map = 0x40041574 ); +PROVIDE ( r_llc_ch_assess_get_local_ch_map = 0x4004150c ); +PROVIDE ( r_llc_ch_assess_local = 0x40041494 ); +PROVIDE ( r_llc_ch_assess_merge_ch = 0x40041588 ); +PROVIDE ( r_llc_ch_assess_reass_ch = 0x400415c0 ); +PROVIDE ( r_llc_common_cmd_complete_send = 0x40044eac ); +PROVIDE ( r_llc_common_cmd_status_send = 0x40044ee0 ); +PROVIDE ( r_llc_common_enc_change_evt_send = 0x40044f6c ); +PROVIDE ( r_llc_common_enc_key_ref_comp_evt_send = 0x40044f38 ); +PROVIDE ( r_llc_common_flush_occurred_send = 0x40044f0c ); +PROVIDE ( r_llc_common_nb_of_pkt_comp_evt_send = 0x40045000 ); +PROVIDE ( r_llc_con_update_complete_send = 0x40044d68 ); +PROVIDE ( r_llc_con_update_finished = 0x4004518c ); +PROVIDE ( r_llc_con_update_ind = 0x40045038 ); +PROVIDE ( r_llc_discon_event_complete_send = 0x40044a30 ); +PROVIDE ( r_llc_end_evt_defer = 0x40046330 ); +PROVIDE ( r_llc_feats_rd_event_send = 0x40044e0c ); +PROVIDE ( r_llc_init = 0x40044778 ); +PROVIDE ( r_llc_le_con_cmp_evt_send = 0x40044a78 ); +PROVIDE ( r_llc_llcp_ch_map_update_pdu_send = 0x40043f94 ); +PROVIDE ( r_llc_llcp_con_param_req_pdu_send = 0x400442fc ); +PROVIDE ( r_llc_llcp_con_param_rsp_pdu_send = 0x40044358 ); +PROVIDE ( r_llc_llcp_con_update_pdu_send = 0x400442c4 ); +PROVIDE ( r_llc_llcp_enc_req_pdu_send = 0x40044064 ); +PROVIDE ( r_llc_llcp_enc_rsp_pdu_send = 0x40044160 ); +PROVIDE ( r_llc_llcp_feats_req_pdu_send = 0x400443b4 ); +PROVIDE ( r_llc_llcp_feats_rsp_pdu_send = 0x400443f0 ); +PROVIDE ( r_llc_llcp_get_autorize = 0x4004475c ); +PROVIDE ( r_llc_llcp_length_req_pdu_send = 0x40044574 ); +PROVIDE ( r_llc_llcp_length_rsp_pdu_send = 0x400445ac ); +PROVIDE ( r_llc_llcp_pause_enc_req_pdu_send = 0x40043fd8 ); +PROVIDE ( r_llc_llcp_pause_enc_rsp_pdu_send = 0x40044010 ); +PROVIDE ( r_llc_llcp_ping_req_pdu_send = 0x4004454c ); +PROVIDE ( r_llc_llcp_ping_rsp_pdu_send = 0x40044560 ); +PROVIDE ( r_llc_llcp_recv_handler = 0x40044678 ); +PROVIDE ( r_llc_llcp_reject_ind_pdu_send = 0x4004425c ); +PROVIDE ( r_llc_llcp_start_enc_req_pdu_send = 0x4004441c ); +PROVIDE ( r_llc_llcp_start_enc_rsp_pdu_send = 0x400441f8 ); +PROVIDE ( r_llc_llcp_terminate_ind_pdu_send = 0x400444b0 ); +PROVIDE ( r_llc_llcp_tester_send = 0x400445e4 ); +PROVIDE ( r_llc_llcp_unknown_rsp_send_pdu = 0x40044534 ); +PROVIDE ( r_llc_llcp_version_ind_pdu_send = 0x40043f6c ); +PROVIDE ( r_llc_lsto_con_update = 0x40045098 ); +PROVIDE ( r_llc_ltk_req_send = 0x40044dc0 ); +PROVIDE ( r_llc_map_update_finished = 0x40045260 ); +PROVIDE ( r_llc_map_update_ind = 0x400450f0 ); +PROVIDE ( r_llc_pdu_acl_tx_ack_defer = 0x400464dc ); +PROVIDE ( r_llc_pdu_defer = 0x40046528 ); +PROVIDE ( r_llc_pdu_llcp_tx_ack_defer = 0x400463ac ); +PROVIDE ( r_llc_reset = 0x400447b8 ); +PROVIDE ( r_llc_start = 0x400447f4 ); +PROVIDE ( r_llc_stop = 0x400449ac ); +PROVIDE ( r_llc_util_bw_mgt = 0x4004629c ); +PROVIDE ( r_llc_util_clear_operation_ptr = 0x40046234 ); +PROVIDE ( r_llc_util_dicon_procedure = 0x40046130 ); +PROVIDE ( r_llc_util_get_free_conhdl = 0x400460c8 ); +PROVIDE ( r_llc_util_get_nb_active_link = 0x40046100 ); +PROVIDE ( r_llc_util_set_auth_payl_to_margin = 0x400461f4 ); +PROVIDE ( r_llc_util_set_llcp_discard_enable = 0x400461c8 ); +PROVIDE ( r_llc_util_update_channel_map = 0x400461ac ); +PROVIDE ( r_llc_version_rd_event_send = 0x40044e60 ); +PROVIDE ( r_lld_adv_start = 0x40048b38 ); +PROVIDE ( r_lld_adv_stop = 0x40048ea0 ); +PROVIDE ( r_lld_ch_map_ind = 0x4004a2f4 ); +PROVIDE ( r_lld_con_param_req = 0x40049f0c ); +PROVIDE ( r_lld_con_param_rsp = 0x40049e00 ); +PROVIDE ( r_lld_con_start = 0x400491f8 ); +PROVIDE ( r_lld_con_stop = 0x40049fdc ); +PROVIDE ( r_lld_con_update_after_param_req = 0x40049bcc ); +PROVIDE ( r_lld_con_update_ind = 0x4004a30c ); +PROVIDE ( r_lld_con_update_req = 0x40049b60 ); +PROVIDE ( r_lld_core_reset = 0x40048a9c ); +PROVIDE ( r_lld_crypt_isr = 0x4004a324 ); +PROVIDE ( r_lld_evt_adv_create = 0x400481f4 ); +PROVIDE ( r_lld_evt_canceled = 0x400485c8 ); +PROVIDE ( r_lld_evt_channel_next = 0x40046aac ); +PROVIDE ( r_lld_evt_deffered_elt_handler = 0x400482bc ); +PROVIDE ( r_lld_evt_delete_elt_handler = 0x40046974 ); +PROVIDE ( r_lld_evt_delete_elt_push = 0x40046a3c ); +PROVIDE ( r_lld_evt_drift_compute = 0x40047670 ); +PROVIDE ( r_lld_evt_elt_delete = 0x40047538 ); +PROVIDE ( r_lld_evt_elt_insert = 0x400474c8 ); +PROVIDE ( r_lld_evt_end = 0x400483e8 ); +PROVIDE ( r_lld_evt_end_isr = 0x4004862c ); +PROVIDE ( r_lld_evt_init = 0x40046b3c ); +PROVIDE ( r_lld_evt_init_evt = 0x40046cd0 ); +PROVIDE ( r_lld_evt_move_to_master = 0x40047ba0 ); +PROVIDE ( r_lld_evt_move_to_slave = 0x40047e18 ); +PROVIDE ( r_lld_evt_prevent_stop = 0x40047adc ); +PROVIDE ( r_lld_evt_restart = 0x40046d50 ); +PROVIDE ( r_lld_evt_rx = 0x40048578 ); +PROVIDE ( r_lld_evt_rx_isr = 0x40048678 ); +PROVIDE ( r_lld_evt_scan_create = 0x40047ae8 ); +PROVIDE ( r_lld_evt_schedule = 0x40047908 ); +PROVIDE ( r_lld_evt_schedule_next = 0x400477dc ); +PROVIDE ( r_lld_evt_schedule_next_instant = 0x400476a8 ); +PROVIDE ( r_lld_evt_slave_update = 0x40048138 ); +PROVIDE ( r_lld_evt_update_create = 0x40047cd8 ); +PROVIDE ( r_lld_get_mode = 0x40049ff8 ); +PROVIDE ( r_lld_init = 0x4004873c ); +PROVIDE ( r_lld_move_to_master = 0x400499e0 ); +PROVIDE ( r_lld_move_to_slave = 0x4004a024 ); +PROVIDE ( r_lld_pdu_adv_pack = 0x4004b488 ); +PROVIDE ( r_lld_pdu_check = 0x4004ac34 ); +PROVIDE ( r_lld_pdu_data_send = 0x4004b018 ); +PROVIDE ( r_lld_pdu_data_tx_push = 0x4004aecc ); +PROVIDE ( r_lld_pdu_rx_handler = 0x4004b4d4 ); +PROVIDE ( r_lld_pdu_send_packet = 0x4004b774 ); +PROVIDE ( r_lld_pdu_tx_flush = 0x4004b414 ); +PROVIDE ( r_lld_pdu_tx_loop = 0x4004ae40 ); +PROVIDE ( r_lld_pdu_tx_prog = 0x4004b120 ); +PROVIDE ( r_lld_pdu_tx_push = 0x4004b080 ); +PROVIDE ( r_lld_ral_renew_req = 0x4004a73c ); +PROVIDE ( r_lld_scan_start = 0x40048ee0 ); +PROVIDE ( r_lld_scan_stop = 0x40049190 ); +PROVIDE ( r_lld_test_mode_rx = 0x4004a540 ); +PROVIDE ( r_lld_test_mode_tx = 0x4004a350 ); +PROVIDE ( r_lld_test_stop = 0x4004a710 ); +PROVIDE ( r_lld_util_anchor_point_move = 0x4004bacc ); +PROVIDE ( r_lld_util_compute_ce_max = 0x4004bc0c ); +PROVIDE ( r_lld_util_connection_param_set = 0x4004ba40 ); +PROVIDE ( r_lld_util_dle_set_cs_fields = 0x4004ba90 ); +PROVIDE ( r_lld_util_eff_tx_time_set = 0x4004bd88 ); +PROVIDE ( r_lld_util_elt_programmed = 0x4004bce0 ); +PROVIDE ( r_lld_util_flush_list = 0x4004bbd8 ); +PROVIDE ( r_lld_util_freq2chnl = 0x4004b9e4 ); +PROVIDE ( r_lld_util_get_bd_address = 0x4004b8ac ); +PROVIDE ( r_lld_util_get_local_offset = 0x4004ba10 ); +PROVIDE ( r_lld_util_get_peer_offset = 0x4004ba24 ); +PROVIDE ( r_lld_util_get_tx_pkt_cnt = 0x4004bd80 ); +PROVIDE ( r_lld_util_instant_get = 0x4004b890 ); +PROVIDE ( r_lld_util_instant_ongoing = 0x4004bbfc ); +PROVIDE ( r_lld_util_priority_set = 0x4004bd10 ); +PROVIDE ( r_lld_util_priority_update = 0x4004bd78 ); +PROVIDE ( r_lld_util_ral_force_rpa_renew = 0x4004b980 ); +PROVIDE ( r_lld_util_set_bd_address = 0x4004b8f8 ); +PROVIDE ( r_lld_wlcoex_set = 0x4004bd98 ); +PROVIDE ( r_llm_ble_ready = 0x4004cc34 ); +PROVIDE ( r_llm_common_cmd_complete_send = 0x4004d288 ); +PROVIDE ( r_llm_common_cmd_status_send = 0x4004d2b4 ); +PROVIDE ( r_llm_con_req_ind = 0x4004cc54 ); +PROVIDE ( r_llm_con_req_tx_cfm = 0x4004d158 ); +PROVIDE ( r_llm_create_con = 0x4004de78 ); +PROVIDE ( r_llm_encryption_done = 0x4004dff8 ); +PROVIDE ( r_llm_encryption_start = 0x4004e128 ); +PROVIDE ( r_llm_end_evt_defer = 0x4004eb6c ); +PROVIDE ( r_llm_init = 0x4004c9f8 ); +PROVIDE ( r_llm_le_adv_report_ind = 0x4004cdf4 ); +PROVIDE ( r_llm_pdu_defer = 0x4004ec48 ); +PROVIDE ( r_llm_ral_clear = 0x4004e1fc ); +PROVIDE ( r_llm_ral_dev_add = 0x4004e23c ); +PROVIDE ( r_llm_ral_dev_rm = 0x4004e3bc ); +PROVIDE ( r_llm_ral_get_rpa = 0x4004e400 ); +PROVIDE ( r_llm_ral_set_timeout = 0x4004e4a0 ); +PROVIDE ( r_llm_ral_update = 0x4004e4f8 ); +PROVIDE ( r_llm_set_adv_data = 0x4004d960 ); +PROVIDE ( r_llm_set_adv_en = 0x4004d7ec ); +PROVIDE ( r_llm_set_adv_param = 0x4004d5f4 ); +PROVIDE ( r_llm_set_scan_en = 0x4004db64 ); +PROVIDE ( r_llm_set_scan_param = 0x4004dac8 ); +PROVIDE ( r_llm_set_scan_rsp_data = 0x4004da14 ); +PROVIDE ( r_llm_test_mode_start_rx = 0x4004d534 ); +PROVIDE ( r_llm_test_mode_start_tx = 0x4004d2fc ); +PROVIDE ( r_llm_util_adv_data_update = 0x4004e8fc ); +PROVIDE ( r_llm_util_apply_bd_addr = 0x4004e868 ); +PROVIDE ( r_llm_util_bd_addr_in_ral = 0x4004eb08 ); +PROVIDE ( r_llm_util_bd_addr_in_wl = 0x4004e788 ); +PROVIDE ( r_llm_util_bd_addr_wl_position = 0x4004e720 ); +PROVIDE ( r_llm_util_bl_add = 0x4004e9ac ); +PROVIDE ( r_llm_util_bl_check = 0x4004e930 ); +PROVIDE ( r_llm_util_bl_rem = 0x4004ea70 ); +PROVIDE ( r_llm_util_check_address_validity = 0x4004e7e4 ); +PROVIDE ( r_llm_util_check_evt_mask = 0x4004e8b0 ); +PROVIDE ( r_llm_util_check_map_validity = 0x4004e800 ); +PROVIDE ( r_llm_util_get_channel_map = 0x4004e8d4 ); +PROVIDE ( r_llm_util_get_supp_features = 0x4004e8e8 ); +PROVIDE ( r_llm_util_set_public_addr = 0x4004e89c ); +PROVIDE ( r_llm_wl_clr = 0x4004dc54 ); +PROVIDE ( r_llm_wl_dev_add = 0x4004dcc0 ); +PROVIDE ( r_llm_wl_dev_add_hdl = 0x4004dd38 ); +PROVIDE ( r_llm_wl_dev_rem = 0x4004dcfc ); +PROVIDE ( r_llm_wl_dev_rem_hdl = 0x4004dde0 ); +PROVIDE ( r_lm_acl_disc = 0x4004f148 ); +PROVIDE ( r_LM_AddSniff = 0x40022d20 ); +PROVIDE ( r_lm_add_sync = 0x40051358 ); +PROVIDE ( r_lm_afh_activate_timer = 0x4004f444 ); +PROVIDE ( r_lm_afh_ch_ass_en_get = 0x4004f3f8 ); +PROVIDE ( r_lm_afh_host_ch_class_get = 0x4004f410 ); +PROVIDE ( r_lm_afh_master_ch_map_get = 0x4004f43c ); +PROVIDE ( r_lm_afh_peer_ch_class_set = 0x4004f418 ); +PROVIDE ( r_lm_check_active_sync = 0x40051334 ); +PROVIDE ( r_LM_CheckEdrFeatureRequest = 0x4002f90c ); +PROVIDE ( r_LM_CheckSwitchInstant = 0x4002f8c0 ); +PROVIDE ( r_lm_check_sync_hl_rsp = 0x4005169c ); +PROVIDE ( r_lm_clk_adj_ack_pending_clear = 0x4004f514 ); +PROVIDE ( r_lm_clk_adj_instant_pending_set = 0x4004f4d8 ); +PROVIDE ( r_LM_ComputePacketType = 0x4002f554 ); +PROVIDE ( r_LM_ComputeSniffSubRate = 0x400233ac ); +PROVIDE ( r_lm_debug_key_compare_192 = 0x4004f3a8 ); +PROVIDE ( r_lm_debug_key_compare_256 = 0x4004f3d0 ); +PROVIDE ( r_lm_dhkey_calc_init = 0x40013234 ); +PROVIDE ( r_lm_dhkey_compare = 0x400132d8 ); +PROVIDE ( r_lm_dut_mode_en_get = 0x4004f3ec ); +PROVIDE ( r_LM_ExtractMaxEncKeySize = 0x4001aca4 ); +PROVIDE ( r_lm_f1 = 0x40012bb8 ); +PROVIDE ( r_lm_f2 = 0x40012cfc ); +PROVIDE ( r_lm_f3 = 0x40013050 ); +PROVIDE ( r_lm_g = 0x40012f90 ); +PROVIDE ( r_LM_GetAFHSwitchInstant = 0x4002f86c ); +PROVIDE ( r_lm_get_auth_en = 0x4004f1ac ); +PROVIDE ( r_lm_get_common_pkt_types = 0x4002fa1c ); +PROVIDE ( r_LM_GetConnectionAcceptTimeout = 0x4004f1f4 ); +PROVIDE ( r_LM_GetFeature = 0x4002f924 ); +PROVIDE ( r_LM_GetLinkTimeout = 0x400233ec ); +PROVIDE ( r_LM_GetLocalNameSeg = 0x4004f200 ); +PROVIDE ( r_lm_get_loopback_mode = 0x4004f248 ); +PROVIDE ( r_LM_GetMasterEncKeySize = 0x4001b29c ); +PROVIDE ( r_LM_GetMasterEncRand = 0x4001b288 ); +PROVIDE ( r_LM_GetMasterKey = 0x4001b260 ); +PROVIDE ( r_LM_GetMasterKeyRand = 0x4001b274 ); +PROVIDE ( r_lm_get_min_sync_intv = 0x400517a8 ); +PROVIDE ( r_lm_get_nb_acl = 0x4004ef9c ); +PROVIDE ( r_lm_get_nb_sync_link = 0x4005179c ); +PROVIDE ( r_lm_get_nonce = 0x400131c4 ); +PROVIDE ( r_lm_get_oob_local_commit = 0x4004f374 ); +PROVIDE ( r_lm_get_oob_local_data_192 = 0x4004f2d4 ); +PROVIDE ( r_lm_get_oob_local_data_256 = 0x4004f318 ); +PROVIDE ( r_LM_GetPINType = 0x4004f1e8 ); +PROVIDE ( r_lm_get_priv_key_192 = 0x4004f278 ); +PROVIDE ( r_lm_get_priv_key_256 = 0x4004f2b8 ); +PROVIDE ( r_lm_get_pub_key_192 = 0x4004f258 ); +PROVIDE ( r_lm_get_pub_key_256 = 0x4004f298 ); +PROVIDE ( r_LM_GetQoSParam = 0x4002f6e0 ); +PROVIDE ( r_lm_get_sec_con_host_supp = 0x4004f1d4 ); +PROVIDE ( r_LM_GetSniffSubratingParam = 0x4002325c ); +PROVIDE ( r_lm_get_sp_en = 0x4004f1c0 ); +PROVIDE ( r_LM_GetSwitchInstant = 0x4002f7f8 ); +PROVIDE ( r_lm_get_synchdl = 0x4005175c ); +PROVIDE ( r_lm_get_sync_param = 0x400503b4 ); +PROVIDE ( r_lm_init = 0x4004ed34 ); +PROVIDE ( r_lm_init_sync = 0x400512d8 ); +PROVIDE ( r_lm_is_acl_con = 0x4004f47c ); +PROVIDE ( r_lm_is_acl_con_role = 0x4004f49c ); +PROVIDE ( r_lm_is_clk_adj_ack_pending = 0x4004f4e8 ); +PROVIDE ( r_lm_is_clk_adj_instant_pending = 0x4004f4c8 ); +PROVIDE ( r_lm_local_ext_fr_configured = 0x4004f540 ); +PROVIDE ( r_lm_look_for_stored_link_key = 0x4002f948 ); +PROVIDE ( r_lm_look_for_sync = 0x40051774 ); +PROVIDE ( r_lm_lt_addr_alloc = 0x4004ef1c ); +PROVIDE ( r_lm_lt_addr_free = 0x4004ef74 ); +PROVIDE ( r_lm_lt_addr_reserve = 0x4004ef48 ); +PROVIDE ( r_LM_MakeCof = 0x4002f84c ); +PROVIDE ( r_LM_MakeRandVec = 0x400112d8 ); +PROVIDE ( r_lm_master_clk_adj_req_handler = 0x40054180 ); +PROVIDE ( r_LM_MaxSlot = 0x4002f694 ); +PROVIDE ( r_lm_modif_sync = 0x40051578 ); +PROVIDE ( r_lm_n_is_zero = 0x40012170 ); +PROVIDE ( r_lm_num_clk_adj_ack_pending_set = 0x4004f500 ); +PROVIDE ( r_lm_oob_f1 = 0x40012e54 ); +PROVIDE ( r_lm_pca_sscan_link_get = 0x4004f560 ); +PROVIDE ( r_lm_pca_sscan_link_set = 0x4004f550 ); +PROVIDE ( nvds_null_read = 0x400542a0 ); +PROVIDE ( nvds_null_write = 0x400542a8 ); +PROVIDE ( nvds_null_erase = 0x400542b0 ); +PROVIDE ( nvds_read = 0x400542c4 ); +PROVIDE ( nvds_write = 0x400542fc ); +PROVIDE ( nvds_erase = 0x40054334 ); +PROVIDE ( nvds_init_memory = 0x40054358 ); +PROVIDE ( r_lmp_pack = 0x4001135c ); +PROVIDE ( r_lmp_unpack = 0x4001149c ); +PROVIDE ( r_lm_read_features = 0x4004f0d8 ); +PROVIDE ( r_LM_RemoveSniff = 0x40023124 ); +PROVIDE ( r_LM_RemoveSniffSubrating = 0x400233c4 ); +PROVIDE ( r_lm_remove_sync = 0x400517c8 ); +PROVIDE ( r_lm_reset_sync = 0x40051304 ); +PROVIDE ( r_lm_role_switch_finished = 0x4004f028 ); +PROVIDE ( r_lm_role_switch_start = 0x4004efe0 ); +PROVIDE ( r_lm_sco_nego_end = 0x40051828 ); +PROVIDE ( r_LM_SniffSubrateNegoRequired = 0x40023334 ); +PROVIDE ( r_LM_SniffSubratingHlReq = 0x40023154 ); +PROVIDE ( r_LM_SniffSubratingPeerReq = 0x400231dc ); +PROVIDE ( r_lm_sp_debug_mode_get = 0x4004f398 ); +PROVIDE ( r_lm_sp_n192_convert_wnaf = 0x400123c0 ); +PROVIDE ( r_lm_sp_n_one = 0x400123a4 ); +PROVIDE ( r_lm_sp_p192_add = 0x40012828 ); +PROVIDE ( r_lm_sp_p192_dbl = 0x4001268c ); +PROVIDE ( r_lm_sp_p192_invert = 0x40012b6c ); +PROVIDE ( r_lm_sp_p192_point_jacobian_to_affine = 0x40012468 ); +PROVIDE ( r_lm_sp_p192_points_jacobian_to_affine = 0x400124e4 ); +PROVIDE ( r_lm_sp_p192_point_to_inf = 0x40012458 ); +PROVIDE ( r_lm_sp_pre_compute_points = 0x40012640 ); +PROVIDE ( r_lm_sp_sha256_calculate = 0x400121a0 ); +PROVIDE ( r_LM_SuppressAclPacket = 0x4002f658 ); +PROVIDE ( r_lm_sync_flow_ctrl_en_get = 0x4004f404 ); +PROVIDE ( r_LM_UpdateAclEdrPacketType = 0x4002f5d8 ); +PROVIDE ( r_LM_UpdateAclPacketType = 0x4002f584 ); +PROVIDE ( r_modules_funcs = 0x3ffafd6c ); +PROVIDE ( r_modules_funcs_p = 0x3ffafd68 ); +PROVIDE ( r_nvds_del = 0x400544c4 ); +PROVIDE ( r_nvds_get = 0x40054488 ); +PROVIDE ( r_nvds_init = 0x40054410 ); +PROVIDE ( r_nvds_lock = 0x400544fc ); +PROVIDE ( r_nvds_put = 0x40054534 ); +PROVIDE ( rom_abs_temp = 0x400054f0 ); +PROVIDE ( rom_bb_bss_bw_40_en = 0x4000401c ); +PROVIDE ( rom_bb_bss_cbw40_dig = 0x40003bac ); +PROVIDE ( rom_bb_rx_ht20_cen_bcov_en = 0x40003734 ); +PROVIDE ( rom_bb_tx_ht20_cen = 0x40003760 ); +PROVIDE ( rom_bb_wdg_test_en = 0x40003b70 ); +PROVIDE ( rom_cbw2040_cfg = 0x400040b0 ); +PROVIDE ( rom_check_noise_floor = 0x40003c78 ); +PROVIDE ( rom_chip_i2c_readReg = 0x40004110 ); +PROVIDE ( rom_chip_i2c_writeReg = 0x40004168 ); +PROVIDE ( rom_chip_v7_bt_init = 0x40004d8c ); +PROVIDE ( rom_chip_v7_rx_init = 0x40004cec ); +PROVIDE ( rom_chip_v7_rx_rifs_en = 0x40003d90 ); +PROVIDE ( rom_chip_v7_tx_init = 0x40004d18 ); +PROVIDE ( rom_clk_force_on_vit = 0x40003710 ); +PROVIDE ( rom_correct_rf_ana_gain = 0x400062a8 ); +PROVIDE ( rom_dc_iq_est = 0x400055c8 ); +PROVIDE ( rom_disable_agc = 0x40002fa4 ); +PROVIDE ( rom_enable_agc = 0x40002fcc ); +PROVIDE ( rom_en_pwdet = 0x4000506c ); +PROVIDE ( rom_gen_rx_gain_table = 0x40003e3c ); +PROVIDE ( rom_get_data_sat = 0x4000312c ); +PROVIDE ( rom_get_fm_sar_dout = 0x40005204 ); +PROVIDE ( rom_get_power_db = 0x40005fc8 ); +PROVIDE ( rom_get_pwctrl_correct = 0x400065d4 ); +PROVIDE ( rom_get_rfcal_rxiq_data = 0x40005bbc ); +PROVIDE ( rom_get_rf_gain_qdb = 0x40006290 ); +PROVIDE ( rom_get_sar_dout = 0x40006564 ); +PROVIDE ( rom_i2c_readReg = 0x40004148 ); +PROVIDE ( rom_i2c_readReg_Mask = 0x400041c0 ); +PROVIDE ( rom_i2c_writeReg = 0x400041a4 ); +PROVIDE ( rom_i2c_writeReg_Mask = 0x400041fc ); +PROVIDE ( rom_index_to_txbbgain = 0x40004df8 ); +PROVIDE ( rom_iq_est_disable = 0x40005590 ); +PROVIDE ( rom_iq_est_enable = 0x40005514 ); +PROVIDE ( rom_linear_to_db = 0x40005f64 ); +PROVIDE ( rom_loopback_mode_en = 0x400030f8 ); +PROVIDE ( rom_meas_tone_pwr_db = 0x40006004 ); +PROVIDE ( rom_mhz2ieee = 0x4000404c ); +PROVIDE ( rom_noise_floor_auto_set = 0x40003bdc ); +PROVIDE ( rom_pbus_debugmode = 0x40004458 ); +PROVIDE ( rom_pbus_force_mode = 0x40004270 ); +PROVIDE ( rom_pbus_force_test = 0x400043c0 ); +PROVIDE ( rom_pbus_rd = 0x40004414 ); +PROVIDE ( rom_pbus_rd_addr = 0x40004334 ); +PROVIDE ( rom_pbus_rd_shift = 0x40004374 ); +PROVIDE ( rom_pbus_rx_dco_cal = 0x40005620 ); +PROVIDE ( rom_pbus_set_dco = 0x40004638 ); +PROVIDE ( rom_pbus_set_rxgain = 0x40004480 ); +PROVIDE ( rom_pbus_workmode = 0x4000446c ); +PROVIDE ( rom_pbus_xpd_rx_off = 0x40004508 ); +PROVIDE ( rom_pbus_xpd_rx_on = 0x4000453c ); +PROVIDE ( rom_pbus_xpd_tx_off = 0x40004590 ); +PROVIDE ( rom_pbus_xpd_tx_on = 0x400045e0 ); +PROVIDE ( rom_phy_disable_agc = 0x40002f6c ); +PROVIDE ( rom_phy_disable_cca = 0x40003000 ); +PROVIDE ( rom_phy_enable_agc = 0x40002f88 ); +PROVIDE ( rom_phy_enable_cca = 0x4000302c ); +PROVIDE ( rom_phy_freq_correct = 0x40004b44 ); +PROVIDE ( rom_phyFuns = 0x3ffae0c0 ); +PROVIDE ( rom_phy_get_noisefloor = 0x40003c2c ); +PROVIDE ( rom_phy_get_vdd33 = 0x4000642c ); +PROVIDE ( rom_pow_usr = 0x40003044 ); +PROVIDE ( rom_read_sar_dout = 0x400051c0 ); +PROVIDE ( rom_restart_cal = 0x400046e0 ); +PROVIDE ( rom_rfcal_pwrctrl = 0x40006058 ); +PROVIDE ( rom_rfcal_rxiq = 0x40005b4c ); +PROVIDE ( rom_rfcal_txcap = 0x40005dec ); +PROVIDE ( rom_rfpll_reset = 0x40004680 ); +PROVIDE ( rom_rfpll_set_freq = 0x400047f8 ); +PROVIDE ( rom_rtc_mem_backup = 0x40003db4 ); +PROVIDE ( rom_rtc_mem_recovery = 0x40003df4 ); +PROVIDE ( rom_rx_gain_force = 0x4000351c ); +PROVIDE ( rom_rxiq_cover_mg_mp = 0x40005a68 ); +PROVIDE ( rom_rxiq_get_mis = 0x400058e4 ); +PROVIDE ( rom_rxiq_set_reg = 0x40005a00 ); +PROVIDE ( rom_set_cal_rxdc = 0x400030b8 ); +PROVIDE ( rom_set_chan_cal_interp = 0x40005ce0 ); +PROVIDE ( rom_set_channel_freq = 0x40004880 ); +PROVIDE ( rom_set_loopback_gain = 0x40003060 ); +PROVIDE ( rom_set_noise_floor = 0x40003d48 ); +PROVIDE ( rom_set_pbus_mem = 0x400031a4 ); +PROVIDE ( rom_set_rf_freq_offset = 0x40004ca8 ); +PROVIDE ( rom_set_rxclk_en = 0x40003594 ); +PROVIDE ( rom_set_txcap_reg = 0x40005d50 ); +PROVIDE ( rom_set_txclk_en = 0x40003564 ); +PROVIDE ( rom_spur_coef_cfg = 0x40003ac8 ); +PROVIDE ( rom_spur_reg_write_one_tone = 0x400037f0 ); +PROVIDE ( rom_start_tx_tone = 0x400036b4 ); +PROVIDE ( rom_start_tx_tone_step = 0x400035d0 ); +PROVIDE ( rom_stop_tx_tone = 0x40003f98 ); +PROVIDE ( _rom_store = 0x4000d66c ); +PROVIDE ( _rom_store_table = 0x4000d4f8 ); +PROVIDE ( rom_target_power_add_backoff = 0x40006268 ); +PROVIDE ( rom_tx_atten_set_interp = 0x400061cc ); +PROVIDE ( rom_txbbgain_to_index = 0x40004dc0 ); +PROVIDE ( rom_txcal_work_mode = 0x4000510c ); +PROVIDE ( rom_txdc_cal_init = 0x40004e10 ); +PROVIDE ( rom_txdc_cal_v70 = 0x40004ea4 ); +PROVIDE ( rom_txiq_cover = 0x4000538c ); +PROVIDE ( rom_txiq_get_mis_pwr = 0x400052dc ); +PROVIDE ( rom_txiq_set_reg = 0x40005154 ); +PROVIDE ( rom_tx_pwctrl_bg_init = 0x4000662c ); +PROVIDE ( rom_txtone_linear_pwr = 0x40005290 ); +PROVIDE ( rom_wait_rfpll_cal_end = 0x400047a8 ); +PROVIDE ( rom_write_gain_mem = 0x4000348c ); +PROVIDE ( rom_write_rfpll_sdm = 0x40004740 ); +PROVIDE ( roundup2 = 0x4000ab7c ); +PROVIDE ( r_plf_funcs_p = 0x3ffb8360 ); +PROVIDE ( r_rf_rw_bt_init = 0x40054868 ); +PROVIDE ( r_rf_rw_init = 0x40054b0c ); +PROVIDE ( r_rf_rw_le_init = 0x400549d0 ); +PROVIDE ( r_rwble_activity_ongoing_check = 0x40054d8c ); +PROVIDE ( r_rwble_init = 0x40054bf4 ); +PROVIDE ( r_rwble_isr = 0x40054e08 ); +PROVIDE ( r_rwble_reset = 0x40054ce8 ); +PROVIDE ( r_rwble_sleep_check = 0x40054d78 ); +PROVIDE ( r_rwble_version = 0x40054dac ); +PROVIDE ( r_rwbt_init = 0x40055160 ); +PROVIDE ( r_rwbt_isr = 0x40055248 ); +PROVIDE ( r_rwbt_reset = 0x400551bc ); +PROVIDE ( r_rwbt_sleep_check = 0x4005577c ); +PROVIDE ( r_rwbt_sleep_enter = 0x400557a4 ); +PROVIDE ( r_rwbt_sleep_wakeup = 0x400557fc ); +PROVIDE ( r_rwbt_sleep_wakeup_end = 0x400558cc ); +PROVIDE ( r_rwbt_version = 0x4005520c ); +PROVIDE ( r_rwip_assert_err = 0x40055f88 ); +PROVIDE ( r_rwip_check_wakeup_boundary = 0x400558fc ); +PROVIDE ( r_rwip_ext_wakeup_enable = 0x40055f3c ); +PROVIDE ( r_rwip_init = 0x4005595c ); +PROVIDE ( r_rwip_pca_clock_dragging_only = 0x40055f48 ); +PROVIDE ( r_rwip_prevent_sleep_clear = 0x40055ec8 ); +PROVIDE ( r_rwip_prevent_sleep_set = 0x40055e64 ); +PROVIDE ( r_rwip_reset = 0x40055ab8 ); +PROVIDE ( r_rwip_schedule = 0x40055b38 ); +PROVIDE ( r_rwip_sleep = 0x40055b5c ); +PROVIDE ( r_rwip_sleep_enable = 0x40055f30 ); +PROVIDE ( r_rwip_version = 0x40055b20 ); +PROVIDE ( r_rwip_wakeup = 0x40055dc4 ); +PROVIDE ( r_rwip_wakeup_delay_set = 0x40055e4c ); +PROVIDE ( r_rwip_wakeup_end = 0x40055e18 ); +PROVIDE ( r_rwip_wlcoex_set = 0x40055f60 ); +PROVIDE ( r_SHA_256 = 0x40013a90 ); +PROVIDE ( rwip_coex_cfg = 0x3ff9914c ); +PROVIDE ( rwip_priority = 0x3ff99159 ); +PROVIDE ( rwip_rf = 0x3ffbdb28 ); +PROVIDE ( rwip_rf_p_get = 0x400558f4 ); +PROVIDE ( r_XorKey = 0x400112c0 ); +PROVIDE ( _sbrk_r = 0x4000bce4 ); +PROVIDE ( __sf_fake_stderr = 0x3ff96458 ); +PROVIDE ( __sf_fake_stdin = 0x3ff96498 ); +PROVIDE ( __sf_fake_stdout = 0x3ff96478 ); +PROVIDE ( sha1_prf = 0x40060ae8 ); +PROVIDE ( sha1_vector = 0x40060b64 ); +PROVIDE ( sha256_prf = 0x40060d70 ); +PROVIDE ( sha256_vector = 0x40060e08 ); +PROVIDE ( sha_blk_bits = 0x3ff99290 ); +PROVIDE ( sha_blk_bits_bytes = 0x3ff99288 ); +PROVIDE ( sha_blk_hash_bytes = 0x3ff9928c ); +PROVIDE ( sig_matrix = 0x3ffae293 ); +PROVIDE ( sip_after_tx_complete = 0x4000b358 ); +PROVIDE ( sip_alloc_to_host_evt = 0x4000ab9c ); +PROVIDE ( sip_get_ptr = 0x4000b34c ); +PROVIDE ( sip_get_state = 0x4000ae2c ); +PROVIDE ( sip_init_attach = 0x4000ae58 ); +PROVIDE ( sip_install_rx_ctrl_cb = 0x4000ae10 ); +PROVIDE ( sip_install_rx_data_cb = 0x4000ae20 ); +PROVIDE ( sip_is_active = 0x4000b3c0 ); +PROVIDE ( sip_post_init = 0x4000aed8 ); +PROVIDE ( sip_reclaim_from_host_cmd = 0x4000adbc ); +PROVIDE ( sip_reclaim_tx_data_pkt = 0x4000ad5c ); +PROVIDE ( sip_send = 0x4000af54 ); +PROVIDE ( sip_to_host_chain_append = 0x4000aef8 ); +PROVIDE ( sip_to_host_evt_send_done = 0x4000ac04 ); +PROVIDE ( slc_add_credits = 0x4000baf4 ); +PROVIDE ( slc_enable = 0x4000b64c ); +PROVIDE ( slc_from_host_chain_fetch = 0x4000b7e8 ); +PROVIDE ( slc_from_host_chain_recycle = 0x4000bb10 ); +PROVIDE ( slc_has_pkt_to_host = 0x4000b5fc ); +PROVIDE ( slc_init_attach = 0x4000b918 ); +PROVIDE ( slc_init_credit = 0x4000badc ); +PROVIDE ( slc_reattach = 0x4000b62c ); +PROVIDE ( slc_send_to_host_chain = 0x4000b6a0 ); +PROVIDE ( slc_set_host_io_max_window = 0x4000b89c ); +PROVIDE ( slc_to_host_chain_recycle = 0x4000b758 ); +PROVIDE ( specialModP256 = 0x4001600c ); +PROVIDE ( __stack = 0x3ffe3f20 ); +PROVIDE ( __stack_app = 0x3ffe7e30 ); +PROVIDE ( _stack_sentry = 0x3ffe1320 ); +PROVIDE ( _stack_sentry_app = 0x3ffe5230 ); +PROVIDE ( _start = 0x40000704 ); +PROVIDE ( start_tb_console = 0x4005a980 ); +PROVIDE ( _stat_r = 0x4000bcb4 ); +PROVIDE ( _stext = 0x40000560 ); +PROVIDE ( __subdf3 = 0x400026e4 ); +PROVIDE ( __subsf3 = 0x400021d0 ); +PROVIDE ( SubtractBigHex256 = 0x40015bcc ); +PROVIDE ( SubtractBigHexMod256 = 0x40015e8c ); +PROVIDE ( SubtractBigHexUint32_256 = 0x40015f8c ); +PROVIDE ( SubtractFromSelfBigHex256 = 0x40015c20 ); +PROVIDE ( SubtractFromSelfBigHexSign256 = 0x40015dc8 ); +PROVIDE ( __subvdi3 = 0x40002d20 ); +PROVIDE ( __subvsi3 = 0x40002cf8 ); +PROVIDE ( sw_to_hw = 0x3ffb8d40 ); +PROVIDE ( syscall_table_ptr_app = 0x3ffae020 ); +PROVIDE ( syscall_table_ptr_pro = 0x3ffae024 ); +PROVIDE ( tdefl_compress = 0x400600bc ); +PROVIDE ( tdefl_compress_buffer = 0x400607f4 ); +PROVIDE ( tdefl_compress_mem_to_mem = 0x40060900 ); +PROVIDE ( tdefl_compress_mem_to_output = 0x400608e0 ); +PROVIDE ( tdefl_get_adler32 = 0x400608d8 ); +PROVIDE ( tdefl_get_prev_return_status = 0x400608d0 ); +PROVIDE ( tdefl_init = 0x40060810 ); +PROVIDE ( tdefl_write_image_to_png_file_in_memory = 0x4006091c ); +PROVIDE ( tdefl_write_image_to_png_file_in_memory_ex = 0x40060910 ); +PROVIDE ( _times_r = 0x4000bc40 ); +PROVIDE ( _timezone = 0x3ffae0a0 ); +PROVIDE ( tinfl_decompress = 0x4005ef30 ); +PROVIDE ( tinfl_decompress_mem_to_callback = 0x40060090 ); +PROVIDE ( tinfl_decompress_mem_to_mem = 0x40060050 ); +PROVIDE ( __truncdfsf2 = 0x40002b90 ); +PROVIDE ( _tzname = 0x3ffae030 ); +PROVIDE ( UartDev = 0x3ffe019c ); +PROVIDE ( __ucmpdi2 = 0x40063840 ); +PROVIDE ( __udivdi3 = 0x4000cff8 ); +PROVIDE ( __udivmoddi4 = 0x40064ab0 ); +PROVIDE ( __udivsi3 = 0x4000c7c8 ); +PROVIDE ( __udiv_w_sdiv = 0x40064aa8 ); +PROVIDE ( __umoddi3 = 0x4000d280 ); +PROVIDE ( __umodsi3 = 0x4000c7d0 ); +PROVIDE ( __umulsidi3 = 0x4000c7d8 ); +PROVIDE ( _unlink_r = 0x4000bc84 ); +PROVIDE ( __unorddf2 = 0x400637f4 ); +PROVIDE ( __unordsf2 = 0x40063478 ); +PROVIDE ( user_code_start = 0x3ffe0400 ); +PROVIDE ( veryBigHexP256 = 0x3ff9736c ); +PROVIDE ( __wctomb = 0x3ff96540 ); +PROVIDE ( _write_r = 0x4000bd70 ); +PROVIDE ( xthal_bcopy = 0x4000c098 ); +PROVIDE ( xthal_copy123 = 0x4000c124 ); +PROVIDE ( xthal_get_ccompare = 0x4000c078 ); +PROVIDE ( xthal_get_ccount = 0x4000c050 ); +PROVIDE ( xthal_get_interrupt = 0x4000c1e4 ); +PROVIDE ( xthal_get_intread = 0x4000c1e4 ); +PROVIDE ( Xthal_intlevel = 0x3ff9c2b4 ); +PROVIDE ( xthal_memcpy = 0x4000c0bc ); +PROVIDE ( xthal_set_ccompare = 0x4000c058 ); +PROVIDE ( xthal_set_intclear = 0x4000c1ec ); +PROVIDE ( _xtos_set_intlevel = 0x4000bfdc ); +PROVIDE ( g_ticks_per_us_pro = 0x3ffe01e0 ); +PROVIDE ( g_ticks_per_us_app = 0x3ffe40f0 ); +PROVIDE ( esp_rom_spiflash_config_param = 0x40063238 ); +PROVIDE ( esp_rom_spiflash_read_user_cmd = 0x400621b0 ); +PROVIDE ( esp_rom_spiflash_write_encrypted_disable = 0x40062e60 ); +PROVIDE ( esp_rom_spiflash_write_encrypted_enable = 0x40062df4 ); +PROVIDE ( esp_rom_spiflash_prepare_encrypted_data = 0x40062e1c ); +PROVIDE ( esp_rom_spiflash_select_qio_pins = 0x40061ddc ); +PROVIDE ( esp_rom_spiflash_attach = 0x40062a6c ); +PROVIDE ( esp_rom_spiflash_config_clk = 0x40062bc8 ); +PROVIDE ( g_rom_spiflash_chip = 0x3ffae270 ); + +/* +These functions are xtos-related (or call xtos-related functions) and do not play well +with multicore FreeRTOS. Where needed, we provide alternatives that are multicore +compatible. These functions also use a chunk of static RAM, by not using them we can +allocate that RAM for general use. +*/ +PROVIDE ( _DebugExceptionVector = 0x40000280 ); +PROVIDE ( _DoubleExceptionVector = 0x400003c0 ); +PROVIDE ( _KernelExceptionVector = 0x40000300 ); +PROVIDE ( _GeneralException = 0x40000e14 ); +PROVIDE ( _ResetHandler = 0x40000450 ); +PROVIDE ( _ResetVector = 0x40000400 ); +PROVIDE ( _UserExceptionVector = 0x40000340 ); +PROVIDE ( _NMIExceptionVector = 0x400002c0 ); +PROVIDE ( _WindowOverflow12 = 0x40000100 ); +PROVIDE ( _WindowOverflow4 = 0x40000000 ); +PROVIDE ( _WindowOverflow8 = 0x40000080 ); +PROVIDE ( _WindowUnderflow12 = 0x40000140 ); +PROVIDE ( _WindowUnderflow4 = 0x40000040 ); +PROVIDE ( _WindowUnderflow8 = 0x400000c0 ); +PROVIDE ( _Level2FromVector = 0x40000954 ); +PROVIDE ( _Level3FromVector = 0x40000a28 ); +PROVIDE ( _Level4FromVector = 0x40000af8 ); +PROVIDE ( _Level5FromVector = 0x40000c68 ); +PROVIDE ( _Level2Vector = 0x40000180 ); +PROVIDE ( _Level3Vector = 0x400001c0 ); +PROVIDE ( _Level4Vector = 0x40000200 ); +PROVIDE ( _Level5Vector = 0x40000240 ); +PROVIDE ( _LevelOneInterrupt = 0x40000835 ); +PROVIDE ( _SyscallException = 0x400007cf ); +PROVIDE ( _xtos_alloca_handler = 0x40000010 ); +PROVIDE ( _xtos_cause3_handler = 0x40000dd8 ); +PROVIDE ( _xtos_c_handler_table = 0x3ffe0548 ); +PROVIDE ( _xtos_c_wrapper_handler = 0x40000de8 ); +PROVIDE ( _xtos_enabled = 0x3ffe0650 ); +PROVIDE ( _xtos_exc_handler_table = 0x3ffe0448 ); +PROVIDE ( _xtos_interrupt_mask_table = 0x3ffe0758 ); +PROVIDE ( _xtos_interrupt_table = 0x3ffe0658 ); +PROVIDE ( _xtos_ints_off = 0x4000bfac ); +PROVIDE ( _xtos_ints_on = 0x4000bf88 ); +PROVIDE ( _xtos_intstruct = 0x3ffe0650 ); +PROVIDE ( _xtos_l1int_handler = 0x40000814 ); +PROVIDE ( _xtos_p_none = 0x4000bfd4 ); +PROVIDE ( _xtos_restore_intlevel = 0x40000928 ); +PROVIDE ( _xtos_return_from_exc = 0x4000c034 ); +PROVIDE ( _xtos_set_exception_handler = 0x4000074c ); +PROVIDE ( _xtos_set_interrupt_handler = 0x4000bf78 ); +PROVIDE ( _xtos_set_interrupt_handler_arg = 0x4000bf34 ); +PROVIDE ( _xtos_set_min_intlevel = 0x4000bff8 ); +PROVIDE ( _xtos_set_vpri = 0x40000934 ); +PROVIDE ( _xtos_syscall_handler = 0x40000790 ); +PROVIDE ( _xtos_unhandled_exception = 0x4000c024 ); +PROVIDE ( _xtos_unhandled_interrupt = 0x4000c01c ); +PROVIDE ( _xtos_vpri_enabled = 0x3ffe0654 ); +PROVIDE ( ets_intr_count = 0x3ffe03fc ); + +/* These functions are part of the UART downloader but also contain general UART functions. */ +PROVIDE ( FilePacketSendDeflatedReqMsgProc = 0x40008b24 ); +PROVIDE ( FilePacketSendReqMsgProc = 0x40008860 ); +PROVIDE ( FlashDwnLdDeflatedStartMsgProc = 0x40008ad8 ); +PROVIDE ( FlashDwnLdParamCfgMsgProc = 0x4000891c ); +PROVIDE ( FlashDwnLdStartMsgProc = 0x40008820 ); +PROVIDE ( FlashDwnLdStopDeflatedReqMsgProc = 0x40008c18 ); +PROVIDE ( FlashDwnLdStopReqMsgProc = 0x400088ec ); +PROVIDE ( MemDwnLdStartMsgProc = 0x40008948 ); +PROVIDE ( MemDwnLdStopReqMsgProc = 0x400089dc ); +PROVIDE ( MemPacketSendReqMsgProc = 0x40008978 ); +PROVIDE ( uart_baudrate_detect = 0x40009034 ); +PROVIDE ( uart_buff_switch = 0x400093c0 ); +PROVIDE ( UartConnCheck = 0x40008738 ); +PROVIDE ( UartConnectProc = 0x40008a04 ); +PROVIDE ( UartDwnLdProc = 0x40008ce8 ); +PROVIDE ( UartRegReadProc = 0x40008a58 ); +PROVIDE ( UartRegWriteProc = 0x40008a14 ); +PROVIDE ( UartSetBaudProc = 0x40008aac ); +PROVIDE ( UartSpiAttachProc = 0x40008a6c ); +PROVIDE ( UartSpiReadProc = 0x40008a80 ); +PROVIDE ( VerifyFlashMd5Proc = 0x40008c44 ); +PROVIDE ( GetUartDevice = 0x40009598 ); +PROVIDE ( RcvMsg = 0x4000954c ); +PROVIDE ( SendMsg = 0x40009384 ); +PROVIDE ( UartGetCmdLn = 0x40009564 ); +PROVIDE ( UartRxString = 0x400092fc ); +PROVIDE ( Uart_Init = 0x40009120 ); +PROVIDE ( recv_packet = 0x40009424 ); +PROVIDE ( send_packet = 0x40009340 ); +PROVIDE ( uartAttach = 0x40008fd0 ); +PROVIDE ( uart_div_modify = 0x400090cc ); +PROVIDE ( uart_rx_intr_handler = 0x40008f4c ); +PROVIDE ( uart_rx_one_char = 0x400092d0 ); +PROVIDE ( uart_rx_one_char_block = 0x400092a4 ); +PROVIDE ( uart_rx_readbuff = 0x40009394 ); +PROVIDE ( uart_tx_flush = 0x40009258 ); +PROVIDE ( uart_tx_one_char = 0x40009200 ); +PROVIDE ( uart_tx_one_char2 = 0x4000922c ); +PROVIDE ( uart_tx_switch = 0x40009028 ); +PROVIDE ( uart_tx_wait_idle = 0x40009278 ); + + +/* +These functions are part of the ROM GPIO driver. We do not use them; the provided esp-idf functions +replace them and this way we can re-use the fixed RAM addresses these routines need. +*/ +PROVIDE ( gpio_init = 0x40009c20 ); +PROVIDE ( gpio_intr_ack = 0x40009dd4 ); +PROVIDE ( gpio_intr_ack_high = 0x40009e1c ); +PROVIDE ( gpio_intr_handler_register = 0x40009e6c ); +PROVIDE ( gpio_intr_pending = 0x40009cec ); +PROVIDE ( gpio_intr_pending_high = 0x40009cf8 ); +PROVIDE ( gpio_pending_mask = 0x3ffe0038 ); +PROVIDE ( gpio_pending_mask_high = 0x3ffe0044 ); +PROVIDE ( gpio_pin_intr_state_set = 0x40009d04 ); +PROVIDE ( gpio_pin_wakeup_disable = 0x40009eb0 ); +PROVIDE ( gpio_pin_wakeup_enable = 0x40009e7c ); +PROVIDE ( gpio_register_get = 0x40009cbc ); +PROVIDE ( gpio_register_set = 0x40009bbc ); + +/* These are still part of that driver, but have been verified not to use static RAM, so they can be used. */ +PROVIDE ( gpio_output_set = 0x40009b24 ); +PROVIDE ( gpio_output_set_high = 0x40009b5c ); +PROVIDE ( gpio_input_get = 0x40009b88 ); +PROVIDE ( gpio_input_get_high = 0x40009b9c ); +PROVIDE ( gpio_matrix_in = 0x40009edc ); +PROVIDE ( gpio_matrix_out = 0x40009f0c ); +PROVIDE ( gpio_pad_select_gpio = 0x40009fdc ); +PROVIDE ( gpio_pad_set_drv = 0x4000a11c ); +PROVIDE ( gpio_pad_pulldown = 0x4000a348 ); +PROVIDE ( gpio_pad_pullup = 0x4000a22c ); +PROVIDE ( gpio_pad_hold = 0x4000a734 ); +PROVIDE ( gpio_pad_unhold = 0x4000a484 ); + +/* +These functions are part of the non-os kernel (etsc). +*/ +PROVIDE ( ets_aes_crypt = 0x4005c9b8 ); +PROVIDE ( ets_aes_disable = 0x4005c8f8 ); +PROVIDE ( ets_aes_enable = 0x4005c8cc ); +PROVIDE ( ets_aes_set_endian = 0x4005c928 ); +PROVIDE ( ets_aes_setkey_dec = 0x4005c994 ); +PROVIDE ( ets_aes_setkey_enc = 0x4005c97c ); +PROVIDE ( ets_bigint_disable = 0x4005c4e0 ); +PROVIDE ( ets_bigint_enable = 0x4005c498 ); +PROVIDE ( ets_bigint_mod_mult_getz = 0x4005c818 ); +PROVIDE ( ets_bigint_mod_mult_prepare = 0x4005c7b4 ); +PROVIDE ( ets_bigint_mod_power_getz = 0x4005c614 ); +PROVIDE ( ets_bigint_mod_power_prepare = 0x4005c54c ); +PROVIDE ( ets_bigint_montgomery_mult_getz = 0x4005c7a4 ); +PROVIDE ( ets_bigint_montgomery_mult_prepare = 0x4005c6fc ); +PROVIDE ( ets_bigint_mult_getz = 0x4005c6e8 ); +PROVIDE ( ets_bigint_mult_prepare = 0x4005c630 ); +PROVIDE ( ets_bigint_wait_finish = 0x4005c520 ); +PROVIDE ( ets_post = 0x4000673c ); +PROVIDE ( ets_run = 0x400066bc ); +PROVIDE ( ets_set_idle_cb = 0x40006674 ); +PROVIDE ( ets_task = 0x40006688 ); +PROVIDE ( ets_efuse_get_8M_clock = 0x40008710 ); +PROVIDE ( ets_efuse_get_spiconfig = 0x40008658 ); +PROVIDE ( ets_efuse_program_op = 0x40008628 ); +PROVIDE ( ets_efuse_read_op = 0x40008600 ); +PROVIDE ( ets_intr_lock = 0x400067b0 ); +PROVIDE ( ets_intr_unlock = 0x400067c4 ); +PROVIDE ( ets_isr_attach = 0x400067ec ); +PROVIDE ( ets_isr_mask = 0x400067fc ); +PROVIDE ( ets_isr_unmask = 0x40006808 ); +PROVIDE ( ets_waiti0 = 0x400067d8 ); +PROVIDE ( intr_matrix_set = 0x4000681c ); +PROVIDE ( check_pos = 0x400068b8 ); +PROVIDE ( ets_set_appcpu_boot_addr = 0x4000689c ); +PROVIDE ( ets_set_startup_callback = 0x4000688c ); +PROVIDE ( ets_set_user_start = 0x4000687c ); +PROVIDE ( ets_unpack_flash_code = 0x40007018 ); +PROVIDE ( ets_unpack_flash_code_legacy = 0x4000694c ); +PROVIDE ( rom_main = 0x400076c4 ); +PROVIDE ( ets_write_char_uart = 0x40007cf8 ); +PROVIDE ( ets_install_putc1 = 0x40007d18 ); +PROVIDE ( ets_install_putc2 = 0x40007d38 ); +PROVIDE ( ets_install_uart_printf = 0x40007d28 ); +PROVIDE ( ets_printf = 0x40007d54 ); +PROVIDE ( rtc_boot_control = 0x4000821c ); +PROVIDE ( rtc_get_reset_reason = 0x400081d4 ); +PROVIDE ( rtc_get_wakeup_cause = 0x400081f4 ); +PROVIDE ( rtc_select_apb_bridge = 0x40008288 ); +PROVIDE ( set_rtc_memory_crc = 0x40008208 ); +PROVIDE ( software_reset = 0x4000824c ); +PROVIDE ( software_reset_cpu = 0x40008264 ); +PROVIDE ( ets_secure_boot_check = 0x4005cb40 ); +PROVIDE ( ets_secure_boot_check_finish = 0x4005cc04 ); +PROVIDE ( ets_secure_boot_check_start = 0x4005cbcc ); +PROVIDE ( ets_secure_boot_finish = 0x4005ca84 ); +PROVIDE ( ets_secure_boot_hash = 0x4005cad4 ); +PROVIDE ( ets_secure_boot_obtain = 0x4005cb14 ); +PROVIDE ( ets_secure_boot_rd_abstract = 0x4005cba8 ); +PROVIDE ( ets_secure_boot_rd_iv = 0x4005cb84 ); +PROVIDE ( ets_secure_boot_start = 0x4005ca34 ); +PROVIDE ( ets_sha_disable = 0x4005c0a8 ); +PROVIDE ( ets_sha_enable = 0x4005c07c ); +PROVIDE ( ets_sha_finish = 0x4005c104 ); +PROVIDE ( ets_sha_init = 0x4005c0d4 ); +PROVIDE ( ets_sha_update = 0x4005c2a0 ); +PROVIDE ( ets_delay_us = 0x40008534 ); +PROVIDE ( ets_get_cpu_frequency = 0x4000855c ); +PROVIDE ( ets_get_detected_xtal_freq = 0x40008588 ); +PROVIDE ( ets_get_xtal_scale = 0x4000856c ); +PROVIDE ( ets_timer_arm = 0x40008368 ); +PROVIDE ( ets_timer_arm_us = 0x400083ac ); +PROVIDE ( ets_timer_disarm = 0x400083ec ); +PROVIDE ( ets_timer_done = 0x40008428 ); +PROVIDE ( ets_timer_handler_isr = 0x40008454 ); +PROVIDE ( ets_timer_init = 0x400084e8 ); +PROVIDE ( ets_timer_setfn = 0x40008350 ); +PROVIDE ( ets_update_cpu_frequency_rom = 0x40008550 ); /* Updates g_ticks_per_us on the current CPU only; not on the other core */ + +/* Following are static data, but can be used, not generated by script <<<<< btdm data */ +PROVIDE ( hci_tl_env = 0x3ffb8154 ); +PROVIDE ( ld_acl_env = 0x3ffb8258 ); +PROVIDE ( ea_env = 0x3ffb80ec ); +PROVIDE ( ld_active_ch_map = 0x3ffb8334 ); +PROVIDE ( ld_bcst_acl_env = 0x3ffb8274 ); +PROVIDE ( ld_csb_rx_env = 0x3ffb8278 ); +PROVIDE ( ld_csb_tx_env = 0x3ffb827c ); +PROVIDE ( ld_env = 0x3ffb9510 ); +PROVIDE ( ld_fm_env = 0x3ffb8284 ); +PROVIDE ( ld_inq_env = 0x3ffb82e4 ); +PROVIDE ( ld_iscan_env = 0x3ffb82e8 ); +PROVIDE ( ld_page_env = 0x3ffb82f0 ); +PROVIDE ( ld_pca_env = 0x3ffb82f4 ); +PROVIDE ( ld_pscan_env = 0x3ffb8308 ); +PROVIDE ( ld_sched_env = 0x3ffb830c ); +PROVIDE ( ld_sched_params = 0x3ffb96c0 ); +PROVIDE ( ld_sco_env = 0x3ffb824c ); +PROVIDE ( ld_sscan_env = 0x3ffb832c ); +PROVIDE ( ld_strain_env = 0x3ffb8330 ); +PROVIDE ( LM_Sniff = 0x3ffb8230 ); +PROVIDE ( LM_SniffSubRate = 0x3ffb8214 ); +PROVIDE ( prbs_64bytes = 0x3ff98992 ); +PROVIDE ( nvds_env = 0x3ffb8364 ); +PROVIDE ( nvds_magic_number = 0x3ff9912a ); +PROVIDE ( TASK_DESC_LLD = 0x3ff98b58 ); +/* Above are static data, but can be used, not generated by script >>>>> btdm data */ diff --git a/cpu/esp32/ld/esp32.spiram.rom-functions-dram.ld b/cpu/esp32/ld/esp32.spiram.rom-functions-dram.ld new file mode 100644 index 0000000000000..5966204341c7e --- /dev/null +++ b/cpu/esp32/ld/esp32.spiram.rom-functions-dram.ld @@ -0,0 +1,143 @@ +/* + If the Newlib functions in ROM aren't used (eg because the external SPI RAM workaround is active), these functions will + be linked into the application directly instead. Normally, they would end up in flash, which is undesirable because esp-idf + and/or applications may assume that because these functions normally are in ROM, they are accessible even when flash is + inaccessible. To work around this, this ld fragment places these functions in RAM instead. If the ROM functions are used, + these defines do nothing, so they can still be included in that situation. + + This file is responsible for placing the rodata segment in DRAM. +*/ + + *lib_a-utoa.o(.rodata .rodata.*) + *lib_a-longjmp.o(.rodata .rodata.*) + *lib_a-setjmp.o(.rodata .rodata.*) + *lib_a-abs.o(.rodata .rodata.*) + *lib_a-div.o(.rodata .rodata.*) + *lib_a-labs.o(.rodata .rodata.*) + *lib_a-ldiv.o(.rodata .rodata.*) + *lib_a-quorem.o(.rodata .rodata.*) + *lib_a-qsort.o(.rodata .rodata.*) + *lib_a-utoa.o(.rodata .rodata.*) + *lib_a-itoa.o(.rodata .rodata.*) + *lib_a-atoi.o(.rodata .rodata.*) + *lib_a-atol.o(.rodata .rodata.*) + *lib_a-strtol.o(.rodata .rodata.*) + *lib_a-strtoul.o(.rodata .rodata.*) + *lib_a-wcrtomb.o(.rodata .rodata.*) + *lib_a-fvwrite.o(.rodata .rodata.*) + *lib_a-wbuf.o(.rodata .rodata.*) + *lib_a-wsetup.o(.rodata .rodata.*) + *lib_a-fputwc.o(.rodata .rodata.*) + *lib_a-wctomb_r.o(.rodata .rodata.*) + *lib_a-ungetc.o(.rodata .rodata.*) + *lib_a-makebuf.o(.rodata .rodata.*) + *lib_a-fflush.o(.rodata .rodata.*) + *lib_a-refill.o(.rodata .rodata.*) + *lib_a-s_fpclassify.o(.rodata .rodata.*) + *lib_a-locale.o(.rodata .rodata.*) + *lib_a-asctime.o(.rodata .rodata.*) + *lib_a-ctime.o(.rodata .rodata.*) + *lib_a-ctime_r.o(.rodata .rodata.*) + *lib_a-lcltime.o(.rodata .rodata.*) + *lib_a-lcltime_r.o(.rodata .rodata.*) + *lib_a-gmtime.o(.rodata .rodata.*) + *lib_a-gmtime_r.o(.rodata .rodata.*) + *lib_a-strftime.o(.rodata .rodata.*) + *lib_a-mktime.o(.rodata .rodata.*) + *lib_a-syswrite.o(.rodata .rodata.*) + *lib_a-tzset_r.o(.rodata .rodata.*) + *lib_a-tzset.o(.rodata .rodata.*) + *lib_a-toupper.o(.rodata .rodata.*) + *lib_a-tolower.o(.rodata .rodata.*) + *lib_a-toascii.o(.rodata .rodata.*) + *lib_a-systimes.o(.rodata .rodata.*) + *lib_a-time.o(.rodata .rodata.*) + *lib_a-bsd_qsort_r.o(.rodata .rodata.*) + *lib_a-qsort_r.o(.rodata .rodata.*) + *lib_a-gettzinfo.o(.rodata .rodata.*) + *lib_a-strupr.o(.rodata .rodata.*) + *lib_a-asctime_r.o(.rodata .rodata.*) + *lib_a-bzero.o(.rodata .rodata.*) + *lib_a-close.o(.rodata .rodata.*) + *lib_a-creat.o(.rodata .rodata.*) + *lib_a-environ.o(.rodata .rodata.*) + *lib_a-fclose.o(.rodata .rodata.*) + *lib_a-isalnum.o(.rodata .rodata.*) + *lib_a-isalpha.o(.rodata .rodata.*) + *lib_a-isascii.o(.rodata .rodata.*) + *lib_a-isblank.o(.rodata .rodata.*) + *lib_a-iscntrl.o(.rodata .rodata.*) + *lib_a-isdigit.o(.rodata .rodata.*) + *lib_a-isgraph.o(.rodata .rodata.*) + *lib_a-islower.o(.rodata .rodata.*) + *lib_a-isprint.o(.rodata .rodata.*) + *lib_a-ispunct.o(.rodata .rodata.*) + *lib_a-isspace.o(.rodata .rodata.*) + *lib_a-isupper.o(.rodata .rodata.*) + *lib_a-memccpy.o(.rodata .rodata.*) + *lib_a-memchr.o(.rodata .rodata.*) + *lib_a-memcmp.o(.rodata .rodata.*) + *lib_a-memcpy.o(.rodata .rodata.*) + *lib_a-memmove.o(.rodata .rodata.*) + *lib_a-memrchr.o(.rodata .rodata.*) + *lib_a-memset.o(.rodata .rodata.*) + *lib_a-open.o(.rodata .rodata.*) + *lib_a-rand.o(.rodata .rodata.*) + *lib_a-rand_r.o(.rodata .rodata.*) + *lib_a-read.o(.rodata .rodata.*) + *lib_a-rshift.o(.rodata .rodata.*) + *lib_a-sbrk.o(.rodata .rodata.*) + *lib_a-srand.o(.rodata .rodata.*) + *lib_a-strcasecmp.o(.rodata .rodata.*) + *lib_a-strcasestr.o(.rodata .rodata.*) + *lib_a-strcat.o(.rodata .rodata.*) + *lib_a-strchr.o(.rodata .rodata.*) + *lib_a-strcmp.o(.rodata .rodata.*) + *lib_a-strcoll.o(.rodata .rodata.*) + *lib_a-strcpy.o(.rodata .rodata.*) + *lib_a-strcspn.o(.rodata .rodata.*) + *lib_a-strdup.o(.rodata .rodata.*) + *lib_a-strlcat.o(.rodata .rodata.*) + *lib_a-strlcpy.o(.rodata .rodata.*) + *lib_a-strlen.o(.rodata .rodata.*) + *lib_a-strlwr.o(.rodata .rodata.*) + *lib_a-strncasecmp.o(.rodata .rodata.*) + *lib_a-strncat.o(.rodata .rodata.*) + *lib_a-strncmp.o(.rodata .rodata.*) + *lib_a-strncpy.o(.rodata .rodata.*) + *lib_a-strndup.o(.rodata .rodata.*) + *lib_a-strnlen.o(.rodata .rodata.*) + *lib_a-strrchr.o(.rodata .rodata.*) + *lib_a-strsep.o(.rodata .rodata.*) + *lib_a-strspn.o(.rodata .rodata.*) + *lib_a-strstr.o(.rodata .rodata.*) + *lib_a-strtok_r.o(.rodata .rodata.*) + *lib_a-strupr.o(.rodata .rodata.*) + *lib_a-stdio.o(.rodata .rodata.*) + *lib_a-syssbrk.o(.rodata .rodata.*) + *lib_a-sysclose.o(.rodata .rodata.*) + *lib_a-sysopen.o(.rodata .rodata.*) + *creat.o(.rodata .rodata.*) + *lib_a-sysread.o(.rodata .rodata.*) + *lib_a-syswrite.o(.rodata .rodata.*) + *lib_a-impure.o(.rodata .rodata.*) + *lib_a-tzvars.o(.rodata .rodata.*) + *lib_a-sf_nan.o(.rodata .rodata.*) + *lib_a-tzcalc_limits.o(.rodata .rodata.*) + *lib_a-month_lengths.o(.rodata .rodata.*) + *lib_a-timelocal.o(.rodata .rodata.*) + *lib_a-findfp.o(.rodata .rodata.*) + *lock.o(.rodata .rodata.*) + *lib_a-getenv_r.o(.rodata .rodata.*) + *isatty.o(.rodata .rodata.*) + *lib_a-fwalk.o(.rodata .rodata.*) + *lib_a-getenv_r.o(.rodata .rodata.*) + *lib_a-tzlock.o(.rodata .rodata.*) + *lib_a-ctype_.o(.rodata .rodata.*) + *lib_a-sccl.o(.rodata .rodata.*) + *lib_a-strptime.o(.rodata .rodata.*) + *lib_a-envlock.o(.rodata .rodata.*) + *lib_a-raise.o(.rodata .rodata.*) + *lib_a-strdup_r.o(.rodata .rodata.*) + *lib_a-system.o(.rodata .rodata.*) + *lib_a-strndup_r.o(.rodata .rodata.*) diff --git a/cpu/esp32/ld/esp32.spiram.rom-functions-iram.ld b/cpu/esp32/ld/esp32.spiram.rom-functions-iram.ld new file mode 100644 index 0000000000000..a7f6823526fd0 --- /dev/null +++ b/cpu/esp32/ld/esp32.spiram.rom-functions-iram.ld @@ -0,0 +1,144 @@ +/* + If the Newlib functions in ROM aren't used (eg because the external SPI RAM workaround is active), these functions will + be linked into the application directly instead. Normally, they would end up in flash, which is undesirable because esp-idf + and/or applications may assume that because these functions normally are in ROM, they are accessible even when flash is + inaccessible. To work around this, this ld fragment places these functions in RAM instead. If the ROM functions are used, + these defines do nothing, so they can still be included in that situation. + + This file is responsible for placing the literal and text segments in IRAM. +*/ + + + *lib_a-utoa.o(.literal .text .literal.* .text.*) + *lib_a-longjmp.o(.literal .text .literal.* .text.*) + *lib_a-setjmp.o(.literal .text .literal.* .text.*) + *lib_a-abs.o(.literal .text .literal.* .text.*) + *lib_a-div.o(.literal .text .literal.* .text.*) + *lib_a-labs.o(.literal .text .literal.* .text.*) + *lib_a-ldiv.o(.literal .text .literal.* .text.*) + *lib_a-quorem.o(.literal .text .literal.* .text.*) + *lib_a-qsort.o(.literal .text .literal.* .text.*) + *lib_a-utoa.o(.literal .text .literal.* .text.*) + *lib_a-itoa.o(.literal .text .literal.* .text.*) + *lib_a-atoi.o(.literal .text .literal.* .text.*) + *lib_a-atol.o(.literal .text .literal.* .text.*) + *lib_a-strtol.o(.literal .text .literal.* .text.*) + *lib_a-strtoul.o(.literal .text .literal.* .text.*) + *lib_a-wcrtomb.o(.literal .text .literal.* .text.*) + *lib_a-fvwrite.o(.literal .text .literal.* .text.*) + *lib_a-wbuf.o(.literal .text .literal.* .text.*) + *lib_a-wsetup.o(.literal .text .literal.* .text.*) + *lib_a-fputwc.o(.literal .text .literal.* .text.*) + *lib_a-wctomb_r.o(.literal .text .literal.* .text.*) + *lib_a-ungetc.o(.literal .text .literal.* .text.*) + *lib_a-makebuf.o(.literal .text .literal.* .text.*) + *lib_a-fflush.o(.literal .text .literal.* .text.*) + *lib_a-refill.o(.literal .text .literal.* .text.*) + *lib_a-s_fpclassify.o(.literal .text .literal.* .text.*) + *lib_a-locale.o(.literal .text .literal.* .text.*) + *lib_a-asctime.o(.literal .text .literal.* .text.*) + *lib_a-ctime.o(.literal .text .literal.* .text.*) + *lib_a-ctime_r.o(.literal .text .literal.* .text.*) + *lib_a-lcltime.o(.literal .text .literal.* .text.*) + *lib_a-lcltime_r.o(.literal .text .literal.* .text.*) + *lib_a-gmtime.o(.literal .text .literal.* .text.*) + *lib_a-gmtime_r.o(.literal .text .literal.* .text.*) + *lib_a-strftime.o(.literal .text .literal.* .text.*) + *lib_a-mktime.o(.literal .text .literal.* .text.*) + *lib_a-syswrite.o(.literal .text .literal.* .text.*) + *lib_a-tzset_r.o(.literal .text .literal.* .text.*) + *lib_a-tzset.o(.literal .text .literal.* .text.*) + *lib_a-toupper.o(.literal .text .literal.* .text.*) + *lib_a-tolower.o(.literal .text .literal.* .text.*) + *lib_a-toascii.o(.literal .text .literal.* .text.*) + *lib_a-systimes.o(.literal .text .literal.* .text.*) + *lib_a-time.o(.literal .text .literal.* .text.*) + *lib_a-bsd_qsort_r.o(.literal .text .literal.* .text.*) + *lib_a-qsort_r.o(.literal .text .literal.* .text.*) + *lib_a-gettzinfo.o(.literal .text .literal.* .text.*) + *lib_a-strupr.o(.literal .text .literal.* .text.*) + *lib_a-asctime_r.o(.literal .text .literal.* .text.*) + *lib_a-bzero.o(.literal .text .literal.* .text.*) + *lib_a-close.o(.literal .text .literal.* .text.*) + *lib_a-creat.o(.literal .text .literal.* .text.*) + *lib_a-environ.o(.literal .text .literal.* .text.*) + *lib_a-fclose.o(.literal .text .literal.* .text.*) + *lib_a-isalnum.o(.literal .text .literal.* .text.*) + *lib_a-isalpha.o(.literal .text .literal.* .text.*) + *lib_a-isascii.o(.literal .text .literal.* .text.*) + *lib_a-isblank.o(.literal .text .literal.* .text.*) + *lib_a-iscntrl.o(.literal .text .literal.* .text.*) + *lib_a-isdigit.o(.literal .text .literal.* .text.*) + *lib_a-isgraph.o(.literal .text .literal.* .text.*) + *lib_a-islower.o(.literal .text .literal.* .text.*) + *lib_a-isprint.o(.literal .text .literal.* .text.*) + *lib_a-ispunct.o(.literal .text .literal.* .text.*) + *lib_a-isspace.o(.literal .text .literal.* .text.*) + *lib_a-isupper.o(.literal .text .literal.* .text.*) + *lib_a-memccpy.o(.literal .text .literal.* .text.*) + *lib_a-memchr.o(.literal .text .literal.* .text.*) + *lib_a-memcmp.o(.literal .text .literal.* .text.*) + *lib_a-memcpy.o(.literal .text .literal.* .text.*) + *lib_a-memmove.o(.literal .text .literal.* .text.*) + *lib_a-memrchr.o(.literal .text .literal.* .text.*) + *lib_a-memset.o(.literal .text .literal.* .text.*) + *lib_a-open.o(.literal .text .literal.* .text.*) + *lib_a-rand.o(.literal .text .literal.* .text.*) + *lib_a-rand_r.o(.literal .text .literal.* .text.*) + *lib_a-read.o(.literal .text .literal.* .text.*) + *lib_a-rshift.o(.literal .text .literal.* .text.*) + *lib_a-sbrk.o(.literal .text .literal.* .text.*) + *lib_a-srand.o(.literal .text .literal.* .text.*) + *lib_a-strcasecmp.o(.literal .text .literal.* .text.*) + *lib_a-strcasestr.o(.literal .text .literal.* .text.*) + *lib_a-strcat.o(.literal .text .literal.* .text.*) + *lib_a-strchr.o(.literal .text .literal.* .text.*) + *lib_a-strcmp.o(.literal .text .literal.* .text.*) + *lib_a-strcoll.o(.literal .text .literal.* .text.*) + *lib_a-strcpy.o(.literal .text .literal.* .text.*) + *lib_a-strcspn.o(.literal .text .literal.* .text.*) + *lib_a-strdup.o(.literal .text .literal.* .text.*) + *lib_a-strlcat.o(.literal .text .literal.* .text.*) + *lib_a-strlcpy.o(.literal .text .literal.* .text.*) + *lib_a-strlen.o(.literal .text .literal.* .text.*) + *lib_a-strlwr.o(.literal .text .literal.* .text.*) + *lib_a-strncasecmp.o(.literal .text .literal.* .text.*) + *lib_a-strncat.o(.literal .text .literal.* .text.*) + *lib_a-strncmp.o(.literal .text .literal.* .text.*) + *lib_a-strncpy.o(.literal .text .literal.* .text.*) + *lib_a-strndup.o(.literal .text .literal.* .text.*) + *lib_a-strnlen.o(.literal .text .literal.* .text.*) + *lib_a-strrchr.o(.literal .text .literal.* .text.*) + *lib_a-strsep.o(.literal .text .literal.* .text.*) + *lib_a-strspn.o(.literal .text .literal.* .text.*) + *lib_a-strstr.o(.literal .text .literal.* .text.*) + *lib_a-strtok_r.o(.literal .text .literal.* .text.*) + *lib_a-strupr.o(.literal .text .literal.* .text.*) + *lib_a-stdio.o(.literal .text .literal.* .text.*) + *lib_a-syssbrk.o(.literal .text .literal.* .text.*) + *lib_a-sysclose.o(.literal .text .literal.* .text.*) + *lib_a-sysopen.o(.literal .text .literal.* .text.*) + *creat.o(.literal .text .literal.* .text.*) + *lib_a-sysread.o(.literal .text .literal.* .text.*) + *lib_a-syswrite.o(.literal .text .literal.* .text.*) + *lib_a-impure.o(.literal .text .literal.* .text.*) + *lib_a-tzvars.o(.literal .text .literal.* .text.*) + *lib_a-sf_nan.o(.literal .text .literal.* .text.*) + *lib_a-tzcalc_limits.o(.literal .text .literal.* .text.*) + *lib_a-month_lengths.o(.literal .text .literal.* .text.*) + *lib_a-timelocal.o(.literal .text .literal.* .text.*) + *lib_a-findfp.o(.literal .text .literal.* .text.*) + *lock.o(.literal .text .literal.* .text.*) + *lib_a-getenv_r.o(.literal .text .literal.* .text.*) + *isatty.o(.literal .text .literal.* .text.*) + *lib_a-fwalk.o(.literal .text .literal.* .text.*) + *lib_a-getenv_r.o(.literal .text .literal.* .text.*) + *lib_a-tzlock.o(.literal .text .literal.* .text.*) + *lib_a-ctype_.o(.literal .text .literal.* .text.*) + *lib_a-sccl.o(.literal .text .literal.* .text.*) + *lib_a-strptime.o(.literal .text .literal.* .text.*) + *lib_a-envlock.o(.literal .text .literal.* .text.*) + *lib_a-raise.o(.literal .text .literal.* .text.*) + *lib_a-strdup_r.o(.literal .text .literal.* .text.*) + *lib_a-system.o(.literal .text .literal.* .text.*) + *lib_a-strndup_r.o(.literal .text .literal.* .text.*) diff --git a/cpu/esp32/periph/Makefile b/cpu/esp32/periph/Makefile new file mode 100644 index 0000000000000..6d1887b640099 --- /dev/null +++ b/cpu/esp32/periph/Makefile @@ -0,0 +1,3 @@ +MODULE = periph + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/esp32/periph/adc.c b/cpu/esp32/periph/adc.c new file mode 100644 index 0000000000000..c6e56eade634d --- /dev/null +++ b/cpu/esp32/periph/adc.c @@ -0,0 +1,640 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @ingroup cpu_esp32 + * @ingroup drivers_periph_adc + * @{ + * + * @file + * @brief Low-level ADC driver implementation + * + * @author Gunar Schorcht + * + * @} + */ + +#define ENABLE_DEBUG 0 +#include "debug.h" +#include "common.h" + +#include "board.h" +#include "cpu.h" +#include "log.h" +#include "mutex.h" +#include "periph/adc.h" +#include "periph/dac.h" +#include "periph/gpio.h" + +#include "adc_arch.h" +#include "gpio_arch.h" +#include "rom/ets_sys.h" +#include "soc/rtc_io_struct.h" +#include "soc/rtc_cntl_struct.h" +#include "soc/sens_reg.h" +#include "soc/sens_struct.h" + +#define ADC1_CTRL 0 +#define ADC2_CTRL 1 + +/* RTC pin type (does not correspond to RTC gpio num order) */ +typedef enum { + + RTCIO_TOUCH0 = 0, /* touch sensor 0 */ + RTCIO_TOUCH1, /* touch sensor 1 */ + RTCIO_TOUCH2, /* touch sensor 2 */ + RTCIO_TOUCH3, /* touch sensor 3 */ + RTCIO_TOUCH4, /* touch sensor 4 */ + RTCIO_TOUCH5, /* touch sensor 5 */ + RTCIO_TOUCH6, /* touch sensor 6 */ + RTCIO_TOUCH7, /* touch sensor 7 */ + RTCIO_TOUCH8, /* touch sensor 8, 32K_XP */ + RTCIO_TOUCH9, /* touch sensor 9, 32K_XN */ + + RTCIO_ADC_ADC1, /* VDET_1 */ + RTCIO_ADC_ADC2, /* VDET_2 */ + + RTCIO_SENSOR_SENSE1, /* SENSOR_VP */ + RTCIO_SENSOR_SENSE2, /* SENSOR_CAPP */ + RTCIO_SENSOR_SENSE3, /* SENSOR_CAPN */ + RTCIO_SENSOR_SENSE4, /* SENSOR_VN */ + + RTCIO_DAC1, /* DAC output */ + RTCIO_DAC2, /* DAC output */ + + RTCIO_NA, /* RTC pad not available */ +} _rtcio_pin_t; + +/* ADC pin hardware information type (for internal use only) */ +struct _adc_hw_t { + gpio_t gpio; + uint8_t rtc_gpio; + uint8_t adc_ctrl; + uint8_t adc_channel; + char* pad_name; +}; + +/* RTC hardware map, the index corresponds to RTC pin type _rtcio_pin_t + (Table 19 in Technical Reference) */ +const struct _adc_hw_t _adc_hw[] = +{ + /* gpio rtc_gpio adc_ctrl adc_channel, pad_name */ + { GPIO4, 10, ADC2_CTRL, 0, "GPIO4" }, /* RTCIO_TOUCH0 */ + { GPIO0, 11, ADC2_CTRL, 1, "GPIO0" }, /* RTCIO_TOUCH1 */ + { GPIO2, 12, ADC2_CTRL, 2, "GPIO2" }, /* RTCIO_TOUCH2 */ + { GPIO15, 13, ADC2_CTRL, 3, "MTDO" }, /* RTCIO_TOUCH3 */ + { GPIO13, 14, ADC2_CTRL, 4, "MTCK" }, /* RTCIO_TOUCH4 */ + { GPIO12, 15, ADC2_CTRL, 5, "MTDI" }, /* RTCIO_TOUCH5 */ + { GPIO14, 16, ADC2_CTRL, 6, "MTMS" }, /* RTCIO_TOUCH6 */ + { GPIO27, 17, ADC2_CTRL, 7, "GPIO27" }, /* RTCIO_TOUCH7 */ + { GPIO33, 8, ADC1_CTRL, 5, "32K_XN" }, /* RTCIO_TOUCH8 */ + { GPIO32, 9, ADC1_CTRL, 4, "32K_XP" }, /* RTCIO_TOUCH9 */ + { GPIO34, 4, ADC1_CTRL, 6, "VDET_1" }, /* RTCIO_ADC_ADC1 */ + { GPIO35, 5, ADC1_CTRL, 7, "VDET_2" }, /* RTCIO_ADC_ADC2 */ + { GPIO36, 0, ADC1_CTRL, 0, "SENSOR_VP" }, /* RTCIO_SENSOR_SENSE1 */ + { GPIO37, 1, ADC1_CTRL, 1, "SENSOR_CAPP" }, /* RTCIO_SENSOR_SENSE2 */ + { GPIO38, 2, ADC1_CTRL, 2, "SENSOR_CAPN" }, /* RTCIO_SENSOR_SENSE3 */ + { GPIO39, 3, ADC1_CTRL, 3, "SENSOR_VN" }, /* RTCIO_SENSOR_SENSE4 */ + { GPIO25, 6, ADC2_CTRL, 8, "GPIO25" }, /* RTCIO_DAC1 */ + { GPIO26, 7, ADC2_CTRL, 9, "GPIO26" } /* RTCIO_DAC2 */ +}; + +/* maps GPIO pin to RTC pin, this index is used to access ADC hardware table + (Table 19 in Technical Reference) */ +const gpio_t _gpio_rtcio_map[] = { + RTCIO_TOUCH1, /* GPIO0 */ + RTCIO_NA , /* GPIO1 */ + RTCIO_TOUCH2, /* GPIO2 */ + RTCIO_NA, /* GPIO3 */ + RTCIO_TOUCH0, /* GPIO4 */ + RTCIO_NA, /* GPIO5 */ + RTCIO_NA, /* GPIO6 */ + RTCIO_NA, /* GPIO7 */ + RTCIO_NA, /* GPIO8 */ + RTCIO_NA, /* GPIO9 */ + RTCIO_NA, /* GPIO10 */ + RTCIO_NA, /* GPIO11 */ + RTCIO_TOUCH5, /* GPIO12 MTDI */ + RTCIO_TOUCH4, /* GPIO13 MTCK */ + RTCIO_TOUCH6, /* GPIO14 MTMS */ + RTCIO_TOUCH3, /* GPIO15 MTDO */ + RTCIO_NA, /* GPIO16 */ + RTCIO_NA, /* GPIO17 */ + RTCIO_NA, /* GPIO18 */ + RTCIO_NA, /* GPIO19 */ + RTCIO_NA, /* GPIO20 */ + RTCIO_NA, /* GPIO21 */ + RTCIO_NA, /* GPIO22 */ + RTCIO_NA, /* GPIO23 */ + RTCIO_NA, /* GPIO24 */ + RTCIO_DAC1, /* GPIO25 */ + RTCIO_DAC2, /* GPIO26 */ + RTCIO_TOUCH7, /* GPIO27 */ + RTCIO_NA, /* GPIO28 */ + RTCIO_NA, /* GPIO29 */ + RTCIO_NA, /* GPIO30 */ + RTCIO_NA, /* GPIO31 */ + RTCIO_TOUCH9, /* GPIO32 32K_XP */ + RTCIO_TOUCH8, /* GPIO33 32K_XN */ + RTCIO_ADC_ADC1, /* GPIO34 VDET_1 */ + RTCIO_ADC_ADC2, /* GPIO35 VDET_2 */ + RTCIO_SENSOR_SENSE1, /* GPIO36 SENSOR_VP */ + RTCIO_SENSOR_SENSE2, /* GPIO37 SENSOR_CAPP */ + RTCIO_SENSOR_SENSE3, /* GPIO38 SENSOR_CAPN */ + RTCIO_SENSOR_SENSE4, /* GPIO39 SENSOR_VN */ +}; + +#if defined(ADC_GPIOS) || defined(DAC_GPIOS) +/* forward declaration of internal functions */ +static void _adc1_ctrl_init(void); +static void _adc2_ctrl_init(void); + +static bool _adc1_ctrl_initialized = false; +static bool _adc2_ctrl_initialized = false; +#endif /* defined(ADC_GPIOS) || defined(DAC_GPIOS) */ + +#if defined(ADC_GPIOS) +static bool _adc_conf_check(void); +static void _adc_module_init(void); +static bool _adc_module_initialized = false; + +int adc_init(adc_t line) +{ + CHECK_PARAM_RET (line < ADC_NUMOF, -1) + + if (!_adc_module_initialized) { + /* do some configuration checks */ + if (!_adc_conf_check()) { + return -1; + } + _adc_module_init(); + _adc_module_initialized = true; + } + + uint8_t rtcio = _gpio_rtcio_map[ADC_PINS[line]]; + + if (_adc_hw[rtcio].adc_ctrl == ADC1_CTRL && !_adc1_ctrl_initialized) { + _adc1_ctrl_init(); + } + if (_adc_hw[rtcio].adc_ctrl == ADC2_CTRL && !_adc2_ctrl_initialized) { + _adc2_ctrl_init(); + } + + /* try to initialize the pin as ADC input */ + if (_gpio_pin_usage[_adc_hw[rtcio].gpio] != _GPIO) { + LOG_ERROR("GPIO%d is used for %s and cannot be used as ADC input\n", + _adc_hw[rtcio].gpio, + _gpio_pin_usage_str[_gpio_pin_usage[_adc_hw[rtcio].gpio]]); + return -1; + } + + uint8_t idx; + + /* disable the pad output */ + RTCIO.enable_w1tc.val = BIT(_adc_hw[rtcio].rtc_gpio); + + /* route pads to RTC and if possible, disable input, pull-up/pull-down */ + switch (rtcio) { + case RTCIO_SENSOR_SENSE1: /* GPIO36, RTC0 */ + RTCIO.sensor_pads.sense1_mux_sel = 1; /* route to RTC */ + RTCIO.sensor_pads.sense1_fun_sel = 0; /* function ADC1_CH0 */ + break; + case RTCIO_SENSOR_SENSE2: /* GPIO37, RTC1 */ + RTCIO.sensor_pads.sense2_mux_sel = 1; /* route to RTC */ + RTCIO.sensor_pads.sense2_fun_sel = 0; /* function ADC1_CH1 */ + break; + case RTCIO_SENSOR_SENSE3: /* GPIO38, RTC2 */ + RTCIO.sensor_pads.sense3_mux_sel = 1; /* route to RTC */ + RTCIO.sensor_pads.sense3_fun_sel = 0; /* function ADC1_CH2 */ + break; + case RTCIO_SENSOR_SENSE4: /* GPIO39, RTC3 */ + RTCIO.sensor_pads.sense4_mux_sel = 1; /* route to RTC */ + RTCIO.sensor_pads.sense4_fun_sel = 0; /* function ADC1_CH3 */ + break; + + case RTCIO_TOUCH0: /* GPIO4, RTC10 */ + case RTCIO_TOUCH1: /* GPIO0, RTC11 */ + case RTCIO_TOUCH2: /* GPIO2, RTC12 */ + case RTCIO_TOUCH3: /* GPIO15, RTC13 */ + case RTCIO_TOUCH4: /* GPIO13, RTC14 */ + case RTCIO_TOUCH5: /* GPIO12, RTC15 */ + case RTCIO_TOUCH6: /* GPIO14, RTC16 */ + case RTCIO_TOUCH7: /* GPIO27, RTC17 */ + case RTCIO_TOUCH8: /* GPIO33, RTC8 */ + case RTCIO_TOUCH9: /* GPIO32, RTC9 */ + idx = rtcio - RTCIO_TOUCH0; + RTCIO.touch_pad[idx].mux_sel = 1; /* route to RTC */ + RTCIO.touch_pad[idx].fun_sel = 0; /* function ADC2_CH0..ADC2_CH9 */ + RTCIO.touch_pad[idx].fun_ie = 0; /* input disabled */ + RTCIO.touch_pad[idx].rue = 0; /* pull-up disabled */ + RTCIO.touch_pad[idx].rde = 0; /* pull-down disabled */ + RTCIO.touch_pad[idx].xpd = 0; /* touch sensor powered off */ + break; + + case RTCIO_ADC_ADC1: /* GPIO34, RTC4 */ + RTCIO.adc_pad.adc1_mux_sel = 1; /* route to RTC */ + RTCIO.adc_pad.adc1_fun_sel = 0; /* function ADC1_CH6 */ + break; + case RTCIO_ADC_ADC2: /* GPIO35, RTC5 */ + RTCIO.adc_pad.adc2_mux_sel = 1; /* route to RTC */ + RTCIO.adc_pad.adc2_fun_sel = 0; /* function ADC1_CH7 */ + break; + + case RTCIO_DAC1: /* GPIO25, RTC6 */ + case RTCIO_DAC2: /* GPIO26, RTC7 */ + idx = rtcio - RTCIO_DAC1; + RTCIO.pad_dac[idx].mux_sel = 1; /* route to RTC */ + RTCIO.pad_dac[idx].fun_sel = 0; /* function ADC2_CH8, ADC2_CH9 */ + RTCIO.pad_dac[idx].fun_ie = 0; /* input disabled */ + RTCIO.pad_dac[idx].rue = 0; /* pull-up disabled */ + RTCIO.pad_dac[idx].rde = 0; /* pull-down disabled */ + RTCIO.pad_dac[idx].xpd_dac = 0; /* DAC powered off */ + break; + + default: return -1; + } + + /* set pin usage type */ + _gpio_pin_usage[_adc_hw[rtcio].gpio] = _ADC; + + return 0; +} + + +int adc_sample(adc_t line, adc_res_t res) +{ + CHECK_PARAM_RET (line < ADC_NUMOF, -1) + CHECK_PARAM_RET (res <= ADC_RES_12BIT, -1) + + uint8_t rtcio = _gpio_rtcio_map[ADC_PINS[line]]; + + if (_adc_hw[rtcio].adc_ctrl == ADC1_CTRL) { + /* set the resolution for the measurement */ + SENS.sar_start_force.sar1_bit_width = res; + SENS.sar_read_ctrl.sar1_sample_bit = res; + + /* enable the pad in the pad enable bitmap */ + SENS.sar_meas_start1.sar1_en_pad = (1 << _adc_hw[rtcio].adc_channel); + while (SENS.sar_slave_addr1.meas_status != 0) {} + + /* start measurement by toggling the start bit and wait until the + measurement has been finished */ + SENS.sar_meas_start1.meas1_start_sar = 0; + SENS.sar_meas_start1.meas1_start_sar = 1; + while (SENS.sar_meas_start1.meas1_done_sar == 0) {} + + /* read out the result and return */ + return SENS.sar_meas_start1.meas1_data_sar; + } + else { + /* set the resolution for the measurement */ + SENS.sar_start_force.sar2_bit_width = res; + SENS.sar_read_ctrl2.sar2_sample_bit = res; + + /* enable the pad in the pad enable bitmap */ + SENS.sar_meas_start2.sar2_en_pad = (1 << _adc_hw[rtcio].adc_channel); + + /* start measurement by toggling the start bit and wait until the + measurement has been finished */ + SENS.sar_meas_start2.meas2_start_sar = 0; + SENS.sar_meas_start2.meas2_start_sar = 1; + while (SENS.sar_meas_start2.meas2_done_sar == 0) {} + + /* read out the result and return */ + return SENS.sar_meas_start2.meas2_data_sar; + } +} + +int adc_set_attenuation(adc_t line, adc_attenuation_t atten) +{ + CHECK_PARAM_RET (line < ADC_NUMOF, -1) + + uint8_t rtcio = _gpio_rtcio_map[ADC_PINS[line]]; + + if (_adc_hw[rtcio].adc_ctrl == ADC1_CTRL) { + SENS.sar_atten1 &= ~(0x3 << (_adc_hw[rtcio].adc_channel << 1)); + SENS.sar_atten1 |= (atten << (_adc_hw[rtcio].adc_channel << 1)); + } + else { + SENS.sar_atten2 &= ~(0x3 << (_adc_hw[rtcio].adc_channel << 1)); + SENS.sar_atten2 |= (atten << (_adc_hw[rtcio].adc_channel << 1)); + } + return 0; +} + +int adc_vref_to_gpio25 (void) +{ + /* determine ADC line for GPIO25 */ + adc_t line = ADC_UNDEF; + for (unsigned i = 0; i < ADC_NUMOF; i++) { \ + if (ADC_PINS[i] == GPIO25) { \ + line = i; + break; + } + } + + if (line == ADC_UNDEF) { + LOG_ERROR("Have no ADC line for GPIO25\n"); + return -1; + } + + if (adc_init(line) == 0) + { + uint8_t rtcio = _gpio_rtcio_map[ADC_PINS[line]]; + RTCCNTL.bias_conf.dbg_atten = 0; + RTCCNTL.test_mux.dtest_rtc = 1; + RTCCNTL.test_mux.ent_rtc = 1; + SENS.sar_start_force.sar2_en_test = 1; + SENS.sar_meas_start2.sar2_en_pad = (1 << _adc_hw[rtcio].adc_channel); + LOG_INFO("You can now measure Vref at GPIO25\n"); + return 0; + } + else { + LOG_ERROR("Could not init GPIO25 as Vref output\n"); + return -1; + } +} + +static bool _adc_conf_check(void) +{ + for (unsigned i = 0; i < ADC_NUMOF; i++) { + if (_gpio_rtcio_map[ADC_PINS[i]] == RTCIO_NA) { + LOG_ERROR("GPIO%d cannot be used as ADC line\n", ADC_PINS[i]); + return false; + } + } + + return true; +} + +static void _adc_module_init(void) +{ + RTCIO.enable_w1tc.val = ~0x0; + + /* always power on */ + SENS.sar_meas_wait2.force_xpd_sar = SENS_FORCE_XPD_SAR_PU; + + /* disable temperature sensor */ + SENS.sar_tctrl.tsens_power_up_force = 1; /* controlled by SW */ + SENS.sar_tctrl.tsens_power_up = 0; /* power down */ +} + +#endif /* defined(ADC_GPIOS) */ + +#if defined(DAC_GPIOS) + +static bool _dac_conf_check(void); +static bool _dac_module_initialized = false; + +int8_t dac_init (dac_t line) +{ + CHECK_PARAM_RET (line < DAC_NUMOF, DAC_NOLINE) + + if (!_dac_module_initialized) { + /* do some configuration checks */ + if (!_dac_conf_check()) { + return -1; + } + _dac_module_initialized = true; + } + + if (!_adc2_ctrl_initialized) { + _adc2_ctrl_init(); + } + + uint8_t rtcio = _gpio_rtcio_map[DAC_PINS[line]]; + uint8_t idx; + + /* try to initialize the pin as DAC ouput */ + if (_gpio_pin_usage[_adc_hw[rtcio].gpio] != _GPIO) { + LOG_ERROR("GPIO%d is used for %s and cannot be used as DAC output\n", + _adc_hw[rtcio].gpio, + _gpio_pin_usage_str[_gpio_pin_usage[_adc_hw[rtcio].gpio]]); + return DAC_NOLINE; + } + + /* disable the output of the pad */ + RTCIO.enable_w1tc.val = BIT(_adc_hw[rtcio].rtc_gpio); + + switch (rtcio) { + case RTCIO_DAC1: /* GPIO25, RTC6 */ + case RTCIO_DAC2: /* GPIO26, RTC7 */ + idx = rtcio - RTCIO_DAC1; + RTCIO.pad_dac[idx].mux_sel = 1; /* route to RTC */ + RTCIO.pad_dac[idx].fun_sel = 0; /* function ADC2_CH8, ADC2_CH9 */ + RTCIO.pad_dac[idx].fun_ie = 0; /* input disabled */ + RTCIO.pad_dac[idx].rue = 0; /* pull-up disabled */ + RTCIO.pad_dac[idx].rde = 0; /* pull-down disabled */ + + RTCIO.pad_dac[idx].dac_xpd_force = 1; /* use RTC pad not the FSM*/ + RTCIO.pad_dac[idx].xpd_dac = 1; /* DAC powered on */ + break; + + default: return DAC_NOLINE; + } + + /* set pin usage type */ + _gpio_pin_usage[_adc_hw[rtcio].gpio] = _DAC; + + /* don't use DMA */ + SENS.sar_dac_ctrl1.dac_dig_force = 0; + + /* disable CW generators and invert DAC signal */ + SENS.sar_dac_ctrl1.sw_tone_en = 0; + SENS.sar_dac_ctrl2.dac_cw_en1 = 0; + SENS.sar_dac_ctrl2.dac_cw_en2 = 0; + + return DAC_OK; +} + +void dac_set (dac_t line, uint16_t value) +{ + CHECK_PARAM (line < DAC_NUMOF); + RTCIO.pad_dac[_gpio_rtcio_map[DAC_PINS[line]] - RTCIO_DAC1].dac = value >> 8; +} + +void dac_poweroff (dac_t line) +{ + CHECK_PARAM (line < DAC_NUMOF); +} + +void dac_poweron (dac_t line) +{ + CHECK_PARAM (line < DAC_NUMOF); +} + +static bool _dac_conf_check(void) +{ + for (unsigned i = 0; i < DAC_NUMOF; i++) { + if (_gpio_rtcio_map[DAC_PINS[i]] != RTCIO_DAC1 && + _gpio_rtcio_map[DAC_PINS[i]] != RTCIO_DAC2) { + LOG_ERROR("GPIO%d cannot be used as DAC line\n", DAC_PINS[i]); + return false; + } + } + + return true; +} + +#endif /* defined(DAC_GPIOS) */ + +#if defined(ADC_GPIOS) || defined(DAC_GPIOS) + +static void _adc1_ctrl_init(void) +{ + /* always power on */ + SENS.sar_meas_wait2.force_xpd_sar = SENS_FORCE_XPD_SAR_PU; + + /* power off LN amp */ + SENS.sar_meas_wait2.sar2_rstb_wait = 2; + SENS.sar_meas_ctrl.amp_rst_fb_fsm = 0; + SENS.sar_meas_ctrl.amp_short_ref_fsm = 0; + SENS.sar_meas_ctrl.amp_short_ref_gnd_fsm = 0; + SENS.sar_meas_wait1.sar_amp_wait1 = 1; + SENS.sar_meas_wait1.sar_amp_wait2 = 1; + SENS.sar_meas_wait2.sar_amp_wait3 = 1; + SENS.sar_meas_wait2.force_xpd_amp = SENS_FORCE_XPD_AMP_PD; + + /* SAR ADC1 controller configuration */ + SENS.sar_read_ctrl.sar1_dig_force = 0; /* SAR ADC1 controlled by RTC */ + SENS.sar_meas_start1.meas1_start_force = 1; /* SAR ADC1 started by SW */ + SENS.sar_meas_start1.sar1_en_pad_force = 1; /* pad enable bitmap controlled by SW */ + SENS.sar_touch_ctrl1.xpd_hall_force = 1; /* XPD HALL is controlled by SW */ + SENS.sar_touch_ctrl1.hall_phase_force = 1; /* HALL PHASE is controlled by SW */ + SENS.sar_read_ctrl.sar1_data_inv = 1; /* invert data */ + SENS.sar_atten1 = 0xffffffff; /* set attenuation to 11 dB for all pads + (input range 0 ... 3,3 V) */ + /* power off built-in hall sensor */ + RTCIO.hall_sens.xpd_hall = 0; + + /* set default resolution */ + SENS.sar_start_force.sar1_bit_width = ADC_RES_12BIT; + SENS.sar_read_ctrl.sar1_sample_bit = ADC_RES_12BIT; + + _adc1_ctrl_initialized = true; +} + +static void _adc2_ctrl_init(void) +{ + /* SAR ADC2 controller configuration */ + SENS.sar_read_ctrl2.sar2_dig_force = 0; /* SAR ADC2 controlled by RTC not DIG*/ + SENS.sar_meas_start2.meas2_start_force = 1; /* SAR ADC2 started by SW */ + SENS.sar_meas_start2.sar2_en_pad_force = 1; /* pad enable bitmap controlled by SW */ + SENS.sar_read_ctrl2.sar2_data_inv = 1; /* invert data */ + SENS.sar_atten2 = 0xffffffff; /* set attenuation to 11 dB for all pads + (input range 0 ... 3,3 V) */ + /* set default resolution */ + SENS.sar_start_force.sar2_bit_width = ADC_RES_12BIT; + SENS.sar_read_ctrl2.sar2_sample_bit = ADC_RES_12BIT; + + _adc2_ctrl_initialized = true; +} + +#endif /* defined(ADC_GPIOS) || defined(DAC_GPIOS) */ + +extern const gpio_t _gpio_rtcio_map[]; + +int rtcio_config_sleep_mode (gpio_t pin, bool mode, bool input) +{ + CHECK_PARAM_RET(pin < GPIO_PIN_NUMOF, -1); + + uint8_t rtcio = _gpio_rtcio_map[pin]; + uint8_t idx; + + /* route pads to RTC and if possible, disable input, pull-up/pull-down */ + switch (rtcio) { + case RTCIO_SENSOR_SENSE1: /* GPIO36, RTC0 */ + RTCIO.sensor_pads.sense1_mux_sel = 1; /* route to RTC */ + RTCIO.sensor_pads.sense1_fun_sel = 0; /* RTC mux function 0 */ + RTCIO.sensor_pads.sense1_slp_sel = mode; /* sleep mode */ + RTCIO.sensor_pads.sense1_slp_ie = input; /* input enabled */ + break; + case RTCIO_SENSOR_SENSE2: /* GPIO37, RTC1 */ + RTCIO.sensor_pads.sense2_mux_sel = 1; /* route to RTC */ + RTCIO.sensor_pads.sense2_fun_sel = 0; /* RTC mux function 0 */ + RTCIO.sensor_pads.sense2_slp_sel = mode; /* sleep mode */ + RTCIO.sensor_pads.sense2_slp_ie = input; /* input enabled */ + break; + case RTCIO_SENSOR_SENSE3: /* GPIO38, RTC2 */ + RTCIO.sensor_pads.sense3_mux_sel = 1; /* route to RTC */ + RTCIO.sensor_pads.sense3_fun_sel = 0; /* RTC mux function 0 */ + RTCIO.sensor_pads.sense3_slp_sel = mode; /* sleep mode */ + RTCIO.sensor_pads.sense3_slp_ie = input; /* input enabled */ + break; + case RTCIO_SENSOR_SENSE4: /* GPIO39, RTC3 */ + RTCIO.sensor_pads.sense4_mux_sel = 1; /* route to RTC */ + RTCIO.sensor_pads.sense4_fun_sel = 0; /* RTC mux function 0 */ + RTCIO.sensor_pads.sense4_slp_sel = mode; /* sleep mode */ + RTCIO.sensor_pads.sense4_slp_ie = input; /* input enabled */ + break; + + case RTCIO_TOUCH0: /* GPIO4, RTC10 */ + case RTCIO_TOUCH1: /* GPIO0, RTC11 */ + case RTCIO_TOUCH2: /* GPIO2, RTC12 */ + case RTCIO_TOUCH3: /* GPIO15, RTC13 */ + case RTCIO_TOUCH4: /* GPIO13, RTC14 */ + case RTCIO_TOUCH5: /* GPIO12, RTC15 */ + case RTCIO_TOUCH6: /* GPIO14, RTC16 */ + case RTCIO_TOUCH7: /* GPIO27, RTC17 */ + case RTCIO_TOUCH8: /* GPIO33, RTC8 */ + case RTCIO_TOUCH9: /* GPIO32, RTC9 */ + idx = rtcio - RTCIO_TOUCH0; + RTCIO.touch_pad[idx].mux_sel = 1; /* route to RTC */ + RTCIO.touch_pad[idx].fun_sel = 0; /* RTC mux function 0 */ + RTCIO.touch_pad[idx].slp_sel = mode; /* sleep mode */ + RTCIO.touch_pad[idx].slp_ie = input; /* input enabled */ + RTCIO.touch_pad[idx].slp_oe = ~input; /* output enabled*/ + break; + + case RTCIO_ADC_ADC1: /* GPIO34, RTC4 */ + RTCIO.adc_pad.adc1_mux_sel = 1; /* route to RTC */ + RTCIO.adc_pad.adc1_fun_sel = 0; /* RTC mux function 0 */ + RTCIO.adc_pad.adc1_slp_sel = mode; /* sleep mode */ + RTCIO.adc_pad.adc1_slp_ie = input; /* input enabled */ + break; + case RTCIO_ADC_ADC2: /* GPIO35, RTC5 */ + RTCIO.adc_pad.adc2_mux_sel = 1; /* route to RTC */ + RTCIO.adc_pad.adc2_fun_sel = 0; /* RTC mux function 0 */ + RTCIO.adc_pad.adc2_slp_sel = mode; /* sleep mode */ + RTCIO.adc_pad.adc2_slp_ie = input; /* input enabled */ + break; + + case RTCIO_DAC1: /* GPIO25, RTC6 */ + case RTCIO_DAC2: /* GPIO26, RTC7 */ + idx = rtcio - RTCIO_DAC1; + RTCIO.pad_dac[idx].mux_sel = 1; /* route to RTC */ + RTCIO.pad_dac[idx].fun_sel = 0; /* RTC mux function 0 */ + RTCIO.pad_dac[idx].slp_sel = mode; /* sleep mode */ + RTCIO.pad_dac[idx].slp_ie = input; /* input enabled */ + RTCIO.pad_dac[idx].slp_oe = ~input; /* output enabled*/ + break; + default: + LOG_ERROR("GPIO %d is not an RTCIO pin and cannot be used in " + "sleep mode\n", pin); + return -1; + } + return 0; +} + +void adc_print_config(void) { + LOG_INFO("ADC: pins=[ "); + #if defined(ADC_GPIOS) + for (unsigned i = 0; i < ADC_NUMOF; i++) { + LOG_INFO("%d ", ADC_PINS[i]); + } + #endif /* defined(ADC_GPIOS) */ + LOG_INFO("]\n"); + + LOG_INFO("DAC: pins=[ "); + #if defined(DAC_GPIOS) + for (unsigned i = 0; i < DAC_NUMOF; i++) { + LOG_INFO("%d ", DAC_PINS[i]); + } + #endif /* defined(DAC_GPIOS) */ + LOG_INFO("]\n"); +} diff --git a/cpu/esp32/periph/cpuid.c b/cpu/esp32/periph/cpuid.c new file mode 100644 index 0000000000000..3ddf9a8a2d742 --- /dev/null +++ b/cpu/esp32/periph/cpuid.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @ingroup drivers_periph_cpuid + * @{ + * + * @file + * @brief Implementation + * + * @author Gunar Schorcht + * + * @} + */ + +#include +#include + +#include "periph/cpuid.h" +#include "soc/efuse_reg.h" + +void cpuid_get(void *id) +{ + /* since ESP32 has two cores, the default MAC address is used as CPU id */ + uint32_t rdata1 = REG_READ(EFUSE_BLK0_RDATA1_REG); + uint32_t rdata2 = REG_READ(EFUSE_BLK0_RDATA2_REG); + + uint8_t *tmp = id; + + tmp[0] = rdata2 >> 16; + tmp[1] = rdata2 >> 8; + tmp[2] = rdata2; + tmp[3] = rdata1 >> 24; + tmp[4] = rdata1 >> 16; + tmp[5] = rdata1 >> 8; + tmp[6] = rdata1; +} diff --git a/cpu/esp32/periph/flash.c b/cpu/esp32/periph/flash.c new file mode 100644 index 0000000000000..ee09183182061 --- /dev/null +++ b/cpu/esp32/periph/flash.c @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief Low-level SPI flash and MTD drive implementation + * + * @author Gunar Schorcht + * + * @} + */ + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include +#include +#include + +#include "common.h" +#include "irq_arch.h" +#include "log.h" +#include "mtd.h" + +#include "rom/cache.h" +#include "rom/spi_flash.h" +#include "esp_flash_data_types.h" +#include "esp_partition.h" +#include "esp_spi_flash.h" + +#define ESP_PART_TABLE_ADDR ESP_PARTITION_TABLE_ADDR +#define ESP_PART_TABLE_SIZE 0xC00 +#define ESP_PART_ENTRY_SIZE 0x20 +#define ESP_PART_ENTRY_MAGIC ESP_PARTITION_MAGIC + +/* the external pointer to the system MTD device */ +mtd_dev_t* mtd0 = 0; + +mtd_dev_t _flash_dev; +mtd_desc_t _flash_driver; + +/* forward declaration of mtd functions */ +static int _flash_init (mtd_dev_t *dev); +static int _flash_read (mtd_dev_t *dev, void *buff, uint32_t addr, uint32_t size); +static int _flash_write (mtd_dev_t *dev, const void *buff, uint32_t addr, uint32_t size); +static int _flash_erase (mtd_dev_t *dev, uint32_t addr, uint32_t size); +static int _flash_power (mtd_dev_t *dev, enum mtd_power_state power); + +static uint32_t _flash_beg; /* first byte addr of the flash drive in SPI flash */ +static uint32_t _flash_end; /* first byte addr after the flash drive in SPI flash */ +static uint32_t _flash_size; /* resulting size of the flash drive in SPI flash */ + +static esp_rom_spiflash_chip_t* _flashchip = NULL; + +void spi_flash_drive_init (void) +{ + DEBUG("%s\n", __func__); + + _flashchip = &g_rom_flashchip; + + _flash_driver.init = &_flash_init; + _flash_driver.read = &_flash_read; + _flash_driver.write = &_flash_write; + _flash_driver.erase = &_flash_erase; + _flash_driver.power = &_flash_power; + + /* first, set the beginning of flash to 0x0 to read partition table */ + _flash_beg = 0x0; + _flash_end = _flashchip->chip_size - 5 * _flashchip->sector_size; + _flash_size = _flash_end - _flash_beg; + + /* read in partition table an determine the top of all partitions */ + uint32_t part_addr = ESP_PART_TABLE_ADDR; + uint8_t part_buf[ESP_PART_ENTRY_SIZE]; + bool part_read = true; + uint32_t part_top = 0; + esp_partition_info_t* part = (esp_partition_info_t*)part_buf; + + while (part_read && part_addr < ESP_PART_TABLE_ADDR + ESP_PART_TABLE_SIZE) { + spi_flash_read (part_addr, (void*)part_buf, ESP_PART_ENTRY_SIZE); + + if (part->magic == ESP_PART_ENTRY_MAGIC) { + DEBUG("%s partition @%08x size=%08lx label=%s\n", __func__, + part->pos.offset, part->pos.size, part->label); + if (part->pos.offset + part->pos.size > part_top) { + part_top = part->pos.offset + part->pos.size; + } + part_addr += ESP_PART_ENTRY_SIZE; + } + else { + part_read = false; + } + } + + /* map the partition top address to next higher multiple of 0x10000 */ + part_top = (part_top + 0x10000) & ~0xffff; + DEBUG("%s flash mtd begins at %08lx\n", __func__, part_top); + + /* second, change flash parameters according to partition table */ + _flash_beg = part_top; + _flash_end = _flashchip->chip_size - 5 * _flashchip->sector_size; + _flash_size = _flash_end - _flash_beg; /* MUST be at least 3 sectors (0x3000) */ + + _flash_dev.driver = &_flash_driver; + _flash_dev.sector_count = _flash_size / _flashchip->sector_size; + + mtd0 = &_flash_dev; + + _flash_dev.pages_per_sector = _flashchip->sector_size / _flashchip->page_size; + _flash_dev.page_size = _flashchip->page_size; + + DEBUG("%s flashchip chip_size=%d block_size=%d sector_size=%d page_size=%d\n", __func__, + _flashchip->chip_size, _flashchip->block_size, + _flashchip->sector_size, _flashchip->page_size); + DEBUG("%s flash_dev sector_count=%d pages_per_sector=%d page_size=%d\n", __func__, + _flash_dev.sector_count, _flash_dev.pages_per_sector, _flash_dev.page_size); + DEBUG("\n"); +} + + +#define RETURN_WITH_ESP_ERR_CODE(err) do { \ + switch (err) { \ + case ESP_ROM_SPIFLASH_RESULT_OK : return ESP_OK; \ + case ESP_ROM_SPIFLASH_RESULT_ERR : return ESP_ERR_FLASH_OP_FAIL; \ + case ESP_ROM_SPIFLASH_RESULT_TIMEOUT: return ESP_ERR_FLASH_OP_TIMEOUT; \ + } \ + return ESP_FAIL; \ +} while(0) + +uint8_t _flash_buf[ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM]; + +esp_err_t IRAM_ATTR spi_flash_read(size_t addr, void *buff, size_t size) +{ + DEBUG("%s addr=%08lx size=%lu buf=%p\n", __func__, addr, size, buff); + + CHECK_PARAM_RET (buff != NULL, -ENOTSUP); + + /* size must be within the flash address space */ + CHECK_PARAM_RET (addr + size <= _flash_end, -EOVERFLOW); + + /* prepare for write access */ + critical_enter(); + Cache_Read_Disable(PRO_CPU_NUM); + int result = ESP_ROM_SPIFLASH_RESULT_OK; + uint32_t len = size; + + /* if addr is not 4 byte aligned, we need to read the first full word */ + if (addr & 0x3) { + uint32_t word_addr = addr & ~0x3; + uint32_t pos_in_word = addr & 0x3; + uint32_t len_in_word = 4 - pos_in_word; + len_in_word = (len_in_word < len) ? len_in_word : len; + + result = esp_rom_spiflash_read (word_addr, (uint32_t*)_flash_buf, 4); + memcpy(buff, _flash_buf + pos_in_word, len_in_word); + + buff = (uint8_t*)buff + len_in_word; + addr += len_in_word; + len -= len_in_word; + } + + /* read all full words, maximum ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM + in one read operation */ + while (len > 4 && result == ESP_ROM_SPIFLASH_RESULT_OK) { + uint32_t len_full_words = len & ~0x3; + if (len_full_words > ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM) { + len_full_words = ESP_ROM_SPIFLASH_BUFF_BYTE_READ_NUM; + } + + result |= esp_rom_spiflash_read (addr, (uint32_t*)_flash_buf, len_full_words); + memcpy(buff, _flash_buf, len_full_words); + + buff = (uint8_t*)buff + len_full_words; + addr += len_full_words; + len -= len_full_words; + } + + /* if there is some remaining, we need to prepare last word */ + if (len && result == ESP_ROM_SPIFLASH_RESULT_OK) { + result |= esp_rom_spiflash_read (addr, (uint32_t*)_flash_buf, 4); + memcpy(buff, _flash_buf, len); + } + + /* reset read access */ + Cache_Read_Enable(PRO_CPU_NUM); + critical_exit(); + + /* return with the ESP-IDF error code that is mapped from ROM error code */ + RETURN_WITH_ESP_ERR_CODE(result); +} + +esp_err_t IRAM_ATTR spi_flash_write(size_t addr, const void *buff, size_t size) +{ + DEBUG("%s addr=%08lx size=%lu buf=%p\n", __func__, addr, size, buff); + + CHECK_PARAM_RET (buff != NULL, -ENOTSUP); + + /* size must be within the flash address space */ + CHECK_PARAM_RET (addr + size <= _flash_end, -EOVERFLOW); + + /* prepare for write access */ + critical_enter(); + Cache_Read_Disable(PRO_CPU_NUM); + int result = esp_rom_spiflash_unlock(); + uint32_t len = size; + + /* if addr is not 4 byte aligned, we need to prepare first full word */ + if (addr & 0x3 && result == ESP_ROM_SPIFLASH_RESULT_OK) { + uint32_t word_addr = addr & ~0x3; + uint32_t pos_in_word = addr & 0x3; + uint32_t len_in_word = 4 - pos_in_word; + len_in_word = (len_in_word < len) ? len_in_word : len; + + result |= esp_rom_spiflash_read (word_addr, (uint32_t*)_flash_buf, 4); + memcpy(_flash_buf + pos_in_word, buff, len_in_word); + result |= esp_rom_spiflash_write (word_addr, (uint32_t*)_flash_buf, 4); + + buff = (uint8_t*)buff + len_in_word; + addr += len_in_word; + len -= len_in_word; + } + + /* write all full words, maximum ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM + in one write operation */ + while (len > 4 && result == ESP_ROM_SPIFLASH_RESULT_OK) { + uint32_t len_full_words = len & ~0x3; + if (len_full_words > ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM) { + len_full_words = ESP_ROM_SPIFLASH_BUFF_BYTE_WRITE_NUM; + } + + memcpy(_flash_buf, buff, len_full_words); + result |= esp_rom_spiflash_write (addr, (uint32_t*)_flash_buf, len_full_words); + + buff = (uint8_t*)buff + len_full_words; + addr += len_full_words; + len -= len_full_words; + } + + /* if there is some remaining, we need to prepare last word */ + if (len && result == ESP_ROM_SPIFLASH_RESULT_OK) { + result |= esp_rom_spiflash_read (addr, (uint32_t*)_flash_buf, 4); + memcpy(_flash_buf, buff, len); + result |= esp_rom_spiflash_write (addr, (uint32_t*)_flash_buf, 4); + } + + /* reset write access */ + esp_rom_spiflash_lock(); + Cache_Read_Enable(PRO_CPU_NUM); + critical_exit(); + + /* return with the ESP-IDF error code that is mapped from ROM error code */ + RETURN_WITH_ESP_ERR_CODE(result); +} + +esp_err_t IRAM_ATTR spi_flash_erase_sector(size_t sector) +{ + return spi_flash_erase_range(sector * _flashchip->sector_size, 1); +} + +esp_err_t IRAM_ATTR spi_flash_erase_range(size_t addr, size_t size) +{ + /* size must be within the flash address space */ + CHECK_PARAM_RET (addr + size <= _flash_end, -EOVERFLOW); + + /* size must be a multiple of sector_size && at least one sector */ + CHECK_PARAM_RET (size >= _flashchip->sector_size, -ENOTSUP); + CHECK_PARAM_RET (size % _flashchip->sector_size == 0, -ENOTSUP) + + /* prepare for write access */ + critical_enter(); + Cache_Read_Disable(PRO_CPU_NUM); + uint32_t result = esp_rom_spiflash_unlock(); + + /* erase as many sectors as necessary */ + uint32_t sec = addr / _flashchip->sector_size; + uint32_t cnt = size / _flashchip->sector_size; + while (cnt-- && result == ESP_ROM_SPIFLASH_RESULT_OK) { + result = esp_rom_spiflash_erase_sector (sec++); + } + + /* reset write access */ + esp_rom_spiflash_lock(); + Cache_Read_Enable(PRO_CPU_NUM); + critical_exit(); + + /* return with the ESP-IDF error code that is mapped from ROM error code */ + RETURN_WITH_ESP_ERR_CODE(result); +} + +const esp_partition_t* esp_partition_find_first(esp_partition_type_t type, + esp_partition_subtype_t subtype, + const char* label) +{ + uint32_t info_addr = ESP_PART_TABLE_ADDR; + uint8_t info_buf[ESP_PART_ENTRY_SIZE]; + bool info_read = true; + + esp_partition_info_t* info = (esp_partition_info_t*)info_buf; + esp_partition_t* part; + + while (info_read && info_addr < ESP_PART_TABLE_ADDR + ESP_PART_TABLE_SIZE) { + spi_flash_read (info_addr, (void*)info_buf, ESP_PART_ENTRY_SIZE); + + if (info->magic == ESP_PART_ENTRY_MAGIC) { + DEBUG("%s partition @%08x size=%08lx label=%s\n", __func__, + info->pos.offset, info->pos.size, info->label); + if ((info->type == type) && + (info->subtype == subtype || subtype == ESP_PARTITION_SUBTYPE_ANY) && + (label == NULL || strcmp((const char*)info->label, label) == 0)) { + part = malloc(sizeof(esp_partition_t)); + part->type = info->type; + part->subtype = info->subtype; + part->address = info->pos.offset; + part->size = info->pos.size; + part->encrypted = info->flags & PART_FLAG_ENCRYPTED; + strncpy(part->label, (const char*)info->label, sizeof(info->label)); + part->label[sizeof(part->label) - 1] = 0x0; + + return part; + } + info_addr += ESP_PART_ENTRY_SIZE; + } + else { + info_read = false; + } + } + return NULL; +} + +esp_err_t esp_partition_erase_range(const esp_partition_t* part, + size_t addr, size_t size) +{ + CHECK_PARAM_RET(part != NULL, ESP_ERR_INVALID_ARG); + + /* start addr and size must be inside the partition */ + CHECK_PARAM_RET(addr <= part->size, ESP_ERR_INVALID_ARG); + CHECK_PARAM_RET(addr + size <= part->size, ESP_ERR_INVALID_SIZE); + /* start addr and size must be a multiple of sector size */ + CHECK_PARAM_RET(addr % SPI_FLASH_SEC_SIZE == 0, ESP_ERR_INVALID_ARG); + CHECK_PARAM_RET(size % SPI_FLASH_SEC_SIZE == 0, ESP_ERR_INVALID_SIZE); + + return spi_flash_erase_range(part->address + addr, size); +} + + +static int _flash_init (mtd_dev_t *dev) +{ + DEBUG("%s dev=%p driver=%p\n", __func__, dev, &_flash_driver); + + CHECK_PARAM_RET (dev == &_flash_dev, -ENODEV); + + return 0; +} + +static int _flash_read (mtd_dev_t *dev, void *buff, uint32_t addr, uint32_t size) +{ + DEBUG("%s dev=%p addr=%08lx size=%lu buf=%p\n", __func__, dev, addr, size, buff); + + CHECK_PARAM_RET (dev == &_flash_dev, -ENODEV); + CHECK_PARAM_RET (buff != NULL, -ENOTSUP); + + /* size must be within the flash address space */ + CHECK_PARAM_RET (_flash_beg + addr + size <= _flash_end, -EOVERFLOW); + + return (spi_flash_read(_flash_beg + addr, buff, size) == ESP_OK) ?(int)size : -EIO; +} + +static int _flash_write (mtd_dev_t *dev, const void *buff, uint32_t addr, uint32_t size) +{ + DEBUG("%s dev=%p addr=%08lx size=%lu buf=%p\n", __func__, dev, addr, size, buff); + + CHECK_PARAM_RET (dev == &_flash_dev, -ENODEV); + CHECK_PARAM_RET (buff != NULL, -ENOTSUP); + + /* size must be within the flash address space */ + CHECK_PARAM_RET (_flash_beg + addr + size <= _flash_end, -EOVERFLOW); + + return (spi_flash_write(_flash_beg + addr, buff, size) == ESP_OK) ?(int)size : -EIO; +} + +static int _flash_erase (mtd_dev_t *dev, uint32_t addr, uint32_t size) +{ + DEBUG("%s dev=%p addr=%08lx size=%lu\n", __func__, dev, addr, size); + + CHECK_PARAM_RET (dev == &_flash_dev, -ENODEV); + + /* size must be within the flash address space */ + CHECK_PARAM_RET (_flash_beg + addr + size <= _flash_end, -EOVERFLOW); + + /* size must be a multiple of sector_size && at least one sector */ + CHECK_PARAM_RET (size >= _flashchip->sector_size, -ENOTSUP); + CHECK_PARAM_RET (size % _flashchip->sector_size == 0, -ENOTSUP) + + return (spi_flash_erase_range(_flash_beg + addr, size) == ESP_OK) ? 0 : -EIO; +} + +static int _flash_power (mtd_dev_t *dev, enum mtd_power_state power) +{ + DEBUG("%s\n", __func__); + + return -ENOTSUP; +} diff --git a/cpu/esp32/periph/gpio.c b/cpu/esp32/periph/gpio.c new file mode 100644 index 0000000000000..cafc795439f05 --- /dev/null +++ b/cpu/esp32/periph/gpio.c @@ -0,0 +1,506 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @ingroup drivers_periph_gpio + * @{ + * + * @file + * @brief Low-level GPIO driver implementation for ESP32 + * + * @author Gunar Schorcht + * @} + */ + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include + +#include "log.h" +#include "periph/gpio.h" /* RIOT gpio.h */ + +#include "esp/common_macros.h" +#include "rom/ets_sys.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/gpio_struct.h" +#include "soc/io_mux_reg.h" +#include "soc/rtc_io_reg.h" +#include "soc/rtc_io_struct.h" +#include "xtensa/xtensa_api.h" + +#include "common.h" +#include "adc_arch.h" +#include "gpio_arch.h" +#include "irq_arch.h" +#include "syscalls.h" + +#define GPIO_PRO_CPU_INTR_ENA (BIT(2)) + +/* GPIO to IOMUX register mapping (see Technical Reference, Section 4.12 Register Summary) + https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf */ + +const uint32_t _gpio_to_iomux_reg[GPIO_PIN_NUMOF] = +{ + PERIPHS_IO_MUX_GPIO0_U, /* GPIO0 */ + PERIPHS_IO_MUX_U0TXD_U, /* GPIO1 */ + PERIPHS_IO_MUX_GPIO2_U, /* GPIO2 */ + PERIPHS_IO_MUX_U0RXD_U, /* GPIO3 */ + PERIPHS_IO_MUX_GPIO4_U, /* GPIO4 */ + PERIPHS_IO_MUX_GPIO5_U, /* GPIO5 */ + PERIPHS_IO_MUX_SD_CLK_U, /* GPIO6 used for FLASH */ + PERIPHS_IO_MUX_SD_DATA0_U, /* GPIO7 used for FLASH */ + PERIPHS_IO_MUX_SD_DATA1_U, /* GPIO8 used for FLASH */ + PERIPHS_IO_MUX_SD_DATA2_U, /* GPIO9 used for FLASH in qio or qout mode */ + PERIPHS_IO_MUX_SD_DATA3_U, /* GPIO10 used for FLASH in qio or qout mode */ + PERIPHS_IO_MUX_SD_CMD_U, /* GPIO11 used for FLASH */ + PERIPHS_IO_MUX_MTDI_U, /* GPIO12 used as JTAG for OCD */ + PERIPHS_IO_MUX_MTCK_U, /* GPIO13 used as JTAG for OCD */ + PERIPHS_IO_MUX_MTMS_U, /* GPIO14 used as JTAG for OCD */ + PERIPHS_IO_MUX_MTDO_U, /* GPIO15 used as JTAG for OCD */ + PERIPHS_IO_MUX_GPIO16_U, /* GPIO16 used as CS for PSRAM TODO */ + PERIPHS_IO_MUX_GPIO17_U, /* GPIO17 */ + PERIPHS_IO_MUX_GPIO18_U, /* GPIO18 */ + PERIPHS_IO_MUX_GPIO19_U, /* GPIO19 */ + 0, /* GPIO20 not available */ + PERIPHS_IO_MUX_GPIO21_U, /* GPIO21 */ + PERIPHS_IO_MUX_GPIO22_U, /* GPIO22 */ + PERIPHS_IO_MUX_GPIO23_U, /* GPIO23 */ + 0, /* GPIO24 not available */ + PERIPHS_IO_MUX_GPIO25_U, /* GPIO25 */ + PERIPHS_IO_MUX_GPIO26_U, /* GPIO26 */ + PERIPHS_IO_MUX_GPIO27_U, /* GPIO27 */ + 0, /* GPIO28 not available */ + 0, /* GPIO29 not available */ + 0, /* GPIO30 not available */ + 0, /* GPIO31 not available */ + PERIPHS_IO_MUX_GPIO32_U, /* GPIO32 */ + PERIPHS_IO_MUX_GPIO33_U, /* GPIO33 */ + PERIPHS_IO_MUX_GPIO34_U, /* GPIO34 */ + PERIPHS_IO_MUX_GPIO35_U, /* GPIO35 */ + PERIPHS_IO_MUX_GPIO36_U, /* GPIO36 */ + PERIPHS_IO_MUX_GPIO37_U, /* GPIO37 */ + PERIPHS_IO_MUX_GPIO38_U, /* GPIO38 */ + PERIPHS_IO_MUX_GPIO39_U, /* GPIO39 */ +}; + +/* + * Following table defines which GPIOs have to be handled using RTC_GPIO + * registers since pull-up and pull-down resistors for pads with both GPIO + * and RTC_GPIO functionality can only be controlled via RTC_GPIO registers + * https://www.espressif.com/sites/default/files/documentation/eco_and_workarounds_for_bugs_in_esp32_en.pdf + * + * The table contains the RTC_GPIO num or -1 if it is not an RTC_GPIO pin. + */ + +static const int8_t _gpio_to_rtc[GPIO_PIN_NUMOF] = { + 11, /* gpio0 */ + -1, /* gpio1 */ + 12, /* gpio2 */ + -1, /* gpio3 */ + 10, /* gpio4 */ + -1, /* gpio5 */ + -1, /* gpio6 */ + -1, /* gpio7 */ + -1, /* gpio8 */ + -1, /* gpio9 */ + -1, /* gpio10 */ + -1, /* gpio11 */ + 15, /* gpio12 */ + 14, /* gpio13 */ + 16, /* gpio14 */ + 13, /* gpio15 */ + -1, /* gpio16 */ + -1, /* gpio17 */ + -1, /* gpio18 */ + -1, /* gpio19 */ + -1, /* gpio20 */ + -1, /* gpio21 */ + -1, /* gpio22 */ + -1, /* gpio23 */ + -1, /* gpio24 */ + 6, /* gpio25 */ + 7, /* gpio26 */ + 17, /* gpio27 */ + -1, /* gpio28 */ + -1, /* gpio29 */ + -1, /* gpio30 */ + -1, /* gpio31 */ + 9, /* gpio32 */ + 8, /* gpio33 */ + 4, /* gpio34 */ + 5, /* gpio35 */ + 0, /* gpio36 */ + 1, /* gpio37 */ + 2, /* gpio38 */ + 9 /* gpio39 */ +}; + +/** + * @brief Register information type for RTCIO GPIO pins (for internal use only) + */ +struct _rtc_gpio_t { + uint8_t num; /**< RTC_GPIO pin number */ + uint32_t reg; /**< register of the RTC_GPIO pin */ + uint8_t mux; /**< mux io/rtc bit [0..31] in the register, 32 - no mux */ + uint8_t pullup; /**< pullup bit [0..31] in the register, 32 - no pullup */ + uint8_t pulldown; /**< pulldown bit [0..31] in the register, 32 - no pulldown */ +}; + +/* Table of RTCIO GPIO pins information */ +static const struct _rtc_gpio_t _rtc_gpios[] = { + { 0, RTC_IO_SENSOR_PADS_REG, 27, 32, 32 }, /* rtc0 (gpio36) - no pullup/pulldown */ + { 1, RTC_IO_SENSOR_PADS_REG, 26, 32, 32 }, /* rtc1 (gpio37) - no pullup/pulldown */ + { 2, RTC_IO_SENSOR_PADS_REG, 25, 32, 32 }, /* rtc2 (gpio38) - no pullup/pulldown */ + { 3, RTC_IO_SENSOR_PADS_REG, 24, 32, 32 }, /* rtc3 (gpio39) - no pullup/pulldown */ + { 4, RTC_IO_ADC_PAD_REG, 29, 32, 32 }, /* rtc4 (gpio34) - no pullup/pulldown */ + { 5, RTC_IO_ADC_PAD_REG, 28, 32, 32 }, /* rtc5 (gpio35) - no pullup/pulldown */ + { 6, RTC_IO_PAD_DAC1_REG, 17, 27, 28 }, /* rtc6 (gpio25) */ + { 7, RTC_IO_PAD_DAC2_REG, 17, 27, 28 }, /* rtc7 (gpio26) */ + { 8, RTC_IO_XTAL_32K_PAD_REG, 18, 27, 28 },/* rtc8 (gpio33) */ + { 9, RTC_IO_XTAL_32K_PAD_REG, 17, 22, 23 },/* rtc9 (gpio32) */ + { 10, RTC_IO_TOUCH_PAD0_REG, 19, 27, 28 }, /* rtc10 (gpio4) */ + { 11, RTC_IO_TOUCH_PAD1_REG, 19, 27, 28 }, /* rtc11 (gpio0) */ + { 12, RTC_IO_TOUCH_PAD2_REG, 19, 27, 28 }, /* rtc12 (gpio2) */ + { 13, RTC_IO_TOUCH_PAD3_REG, 19, 27, 28 }, /* rtc13 (gpio15) */ + { 14, RTC_IO_TOUCH_PAD4_REG, 19, 27, 28 }, /* rtc14 (gpio13) */ + { 15, RTC_IO_TOUCH_PAD5_REG, 19, 27, 28 }, /* rtc15 (gpio12) */ + { 16, RTC_IO_TOUCH_PAD6_REG, 19, 27, 28 }, /* rtc16 (gpio14) */ + { 17, RTC_IO_TOUCH_PAD7_REG, 19, 27, 28 } /* rtc17 (gpio27) */ +}; + +/* Table of the usage type of each GPIO pin */ +_gpio_pin_usage_t _gpio_pin_usage [GPIO_PIN_NUMOF] = { + _GPIO, /* gpio0 */ + _UART, /* gpio1 configured as direct I/O UART0 RxD */ + _GPIO, /* gpio2 */ + _UART, /* gpio3 configured as direct I/O UART0 TxD */ + _GPIO, /* gpio4 */ + _GPIO, /* gpio5 configurable as direct I/O VSPI CS0 */ + _SPIF, /* gpio6 not configurable, used as SPI SCK */ + _SPIF, /* gpio7 not configurable, used as SPI MISO */ + _SPIF, /* gpio8 not configurable, used as SPI MOSI */ + #if defined(FLASH_MODE_QIO) || defined(FLASH_MODE_QOUT) + /* in qio and qout mode thes pins are used for quad SPI */ + _SPIF, /* gpio9 not configurable, used as SPI HD */ + _SPIF, /* gpio10 not configurable, used as SPI WP */ + #else + /* otherwise these pins can be used as GPIO */ + _GPIO, /* gpio9 */ + _GPIO, /* gpio10 */ + #endif + _SPIF, /* gpio11 not configurable, used as SPI CS0 */ + _GPIO, /* gpio12 configurable as direct I/O HSPI MISO */ + _GPIO, /* gpio13 configurable as direct I/O HSPI MOSI */ + _GPIO, /* gpio14 configurable as direct I/O HSPI SCK */ + _GPIO, /* gpio15 configurable as direct I/O HSPI CS0 */ + _GPIO, /* gpio16 */ + _GPIO, /* gpio17 */ + _GPIO, /* gpio18 configurable as direct I/O VSPI SCK */ + _GPIO, /* gpio19 configurable as direct I/O VSPI MISO */ + _NOT_EXIST, /* gpio20 */ + _GPIO, /* gpio21 */ + _GPIO, /* gpio22 */ + _GPIO, /* gpio23 configurable as direct I/O VSPI MOSI */ + _NOT_EXIST, /* gpio24 */ + _GPIO, /* gpio25 */ + _GPIO, /* gpio26 */ + _GPIO, /* gpio27 */ + _NOT_EXIST, /* gpio28 */ + _NOT_EXIST, /* gpio29 */ + _NOT_EXIST, /* gpio30 */ + _NOT_EXIST, /* gpio31 */ + _GPIO, /* gpio32 */ + _GPIO, /* gpio33 */ + _GPIO, /* gpio34 */ + _GPIO, /* gpio35 */ + _GPIO, /* gpio36 */ + _GPIO, /* gpio37 */ + _GPIO, /* gpio38 */ + _GPIO /* gpio39 */ +}; + +/* String representation of usage types */ +const char* _gpio_pin_usage_str[] = +{ + "GPIO", "ADC", "DAC", "I2C", "PWM", "SPI", "SPI Flash", "UART", "N/A" +}; + + +#define FUN_GPIO 2 /* the function number for all GPIOs */ + +#define GPIO_PIN_SET(b) if (b < 32) GPIO.out_w1ts = BIT(b); else GPIO.out1_w1ts.val = BIT(b-32) +#define GPIO_PIN_CLR(b) if (b < 32) GPIO.out_w1tc = BIT(b); else GPIO.out1_w1tc.val = BIT(b-32) + +#define GPIO_REG_BIT_GET(l,h,b) ((b < 32) ? GPIO.l & BIT(b) : GPIO.h.val & BIT(b-32)) +#define GPIO_REG_BIT_SET(l,h,b) if (b < 32) GPIO.l |= BIT(b); else GPIO.h.val |= BIT(b-32) +#define GPIO_REG_BIT_CLR(l,h,b) if (b < 32) GPIO.l &= ~BIT(b); else GPIO.h.val &= ~BIT(b-32) +#define GPIO_REG_BIT_XOR(l,h,b) if (b < 32) GPIO.l ^= BIT(b); else GPIO.h.val ^= BIT(b-32) +#define REG_SET_CLR_BIT(c,r,f) if (c) REG_SET_BIT(r,f); else REG_CLR_BIT(r,f) + +int gpio_init(gpio_t pin, gpio_mode_t mode) +{ + CHECK_PARAM_RET(pin < GPIO_PIN_NUMOF, -1); + + /* check if the pin can be used as GPIO or if it is used for something else */ + if (_gpio_pin_usage[pin] != _GPIO) { + LOG_ERROR("GPIO%d is already used as %s signal\n", pin, + _gpio_pin_usage_str[_gpio_pin_usage[pin]]); + return -1; + } + + /* check additional limitations for GPIOs 34 ... 39 */ + if (pin > GPIO33) { + switch (mode) { + case GPIO_OUT: + case GPIO_OD: + case GPIO_OD_PU: + case GPIO_IN_OUT: + case GPIO_IN_OD: + case GPIO_IN_OD_PU: + /* GPIOs 34 ... 39 cannot be used as output */ + LOG_ERROR("GPIO%d can only be used as input\n", pin); + return -1; + + case GPIO_IN_PD: + case GPIO_IN_PU: + /* GPIOs 34 ... 39 have no software controlable pullups/pulldowns */ + LOG_ERROR("GPIO%d has no pullups/pulldowns\n", pin); + return -1; + default: break; + } + } + + const struct _rtc_gpio_t* rtc = (_gpio_to_rtc[pin] != -1) ? + &_rtc_gpios[_gpio_to_rtc[pin]] : NULL; + + /* if pin is a RTC_GPIO, reset RTC function to route RTC pin to GPIO pin */ + if (rtc) { + REG_CLR_BIT(rtc->reg, BIT(rtc->mux)); + } + + switch (mode) { + + case GPIO_IN: + case GPIO_IN_PD: + case GPIO_IN_PU: + /* according to Technical Reference it is not necessary + to configure the GPIO matrix to read GPIOs from + GPIO_IN_REG/GPIO_IN1_REG */ + #if 0 + /* configure the GPIO matrix for the inputs */ + GPIO.func_in_sel_cfg[signal].sig_in_sel = 0; /* route through GPIO matrix */ + GPIO.func_in_sel_cfg[signal].sig_in_inv = 0; /* do not invert input */ + GPIO.func_in_sel_cfg[signal].func_sel = pin; /* connect signal to GPIOx */ + #endif + + /* disable the output for input-only signals */ + GPIO.func_out_sel_cfg[pin].oen_sel = 1; + GPIO_REG_BIT_SET(enable_w1tc, enable1_w1tc, pin); + + /* set the FUN_IE bit for input */ + REG_SET_BIT(_gpio_to_iomux_reg[pin], FUN_IE); + + /* FUN_GPIO / FUN_WPU / FUN_WPD are set later */ + break; + + case GPIO_OUT: + case GPIO_OD: + case GPIO_OD_PU: + /* configure GPIO signal in the GPIO matrix */ + GPIO.func_out_sel_cfg[pin].func_sel = SIG_GPIO_OUT_IDX; + + /* enable the output */ + GPIO.func_out_sel_cfg[pin].oen_sel = 1; + GPIO_REG_BIT_SET(enable_w1ts, enable1_w1ts, pin); + + /* set pad driver to one for open drain outputs */ + GPIO.pin[pin].pad_driver = (mode == GPIO_OD || mode == GPIO_OD_PU) ? 1 : 0; + + /* set pad driver also for RTC pin if it is a RTC_GPIO */ + if (rtc) { + RTCIO.pin[rtc->num].pad_driver = GPIO.pin[pin].pad_driver; + } + + /* clear the FUN_IE bit */ + REG_CLR_BIT(_gpio_to_iomux_reg[pin], FUN_IE); + + /* FUN_GPIO / FUN_WPU / FUN_WPD are set later */ + break; + + case GPIO_IN_OUT: + case GPIO_IN_OD: + case GPIO_IN_OD_PU: + /* configure GPIO signal in the GPIO matrix */ + GPIO.func_out_sel_cfg[pin].func_sel = SIG_GPIO_OUT_IDX; + + /* enable the output */ + GPIO.func_out_sel_cfg[pin].oen_sel = 1; + GPIO_REG_BIT_SET(enable_w1ts, enable1_w1ts, pin); + + /* set pad driver to one for open drain outputs */ + GPIO.pin[pin].pad_driver = (mode == GPIO_IN_OD || mode == GPIO_IN_OD_PU) ? 1 : 0; + + /* set pad driver also for RTC pin if it is a RTC_GPIO */ + if (rtc) { + RTCIO.pin[rtc->num].pad_driver = GPIO.pin[pin].pad_driver; + } + + /* enable the input */ + REG_SET_BIT(_gpio_to_iomux_reg[pin], FUN_IE); + + /* FUN_GPIO / FUN_WPU / FUN_WPD are set later */ + break; + } +ets_printf("G1 %08lx %p\n", *((uint32_t*)_gpio_to_iomux_reg[pin]), _gpio_to_iomux_reg[pin]); + /* select GPIO as IO_MUX function (FUN_GPIO) */ + REG_SET_FIELD(_gpio_to_iomux_reg[pin], MCU_SEL, FUN_GPIO); + + /* enable/disable the pull-up resistor (FUN_WPU) */ + REG_SET_CLR_BIT(mode == GPIO_IN_PU || mode == GPIO_OD_PU || mode == GPIO_IN_OD_PU, + _gpio_to_iomux_reg[pin], FUN_PU); + + /* enable/disable the pull-down resistor (FUN_WPD) */ + REG_SET_CLR_BIT(mode == GPIO_IN_PD, _gpio_to_iomux_reg[pin], FUN_PD); +ets_printf("G2 %08lx\n", *((uint32_t*)_gpio_to_iomux_reg[pin])); + + /* handle pull-up/pull-down resistors for RTC_GPIOs */ + if (rtc) { +ets_printf("R1 %08lx %p %d\n", *((uint32_t*)rtc->reg), rtc->reg, mode == GPIO_IN_PU || mode == GPIO_OD_PU || mode == GPIO_IN_OD_PU); + /* enable/disable the pull-up resistor (FUN_WPU) */ + REG_SET_CLR_BIT(mode == GPIO_IN_PU || mode == GPIO_OD_PU || mode == GPIO_IN_OD_PU, + rtc->reg, BIT(rtc->pullup)); + /* enable/disable the pull-down resistor (FUN_WPD) */ + REG_SET_CLR_BIT(mode == GPIO_IN_PD, rtc->reg, BIT(rtc->pulldown)); +ets_printf("R2 %08lx %p\n", *((uint32_t*)rtc->reg), rtc->reg); + } + + return 0; +} + +static gpio_isr_ctx_t gpio_isr_ctx_table [GPIO_PIN_NUMOF] = { }; +static bool gpio_int_enabled_table [GPIO_PIN_NUMOF] = { }; + +void IRAM gpio_int_handler (void* arg) +{ + irq_isr_enter(); + (void)arg; + + for (unsigned i = 0; i < GPIO_PIN_NUMOF; i++) { + if (GPIO_REG_BIT_GET(status, status1, i)) { + GPIO_REG_BIT_SET(status_w1tc, status1_w1tc, i); + if (gpio_int_enabled_table[i] && GPIO.pin[i].int_type) { + gpio_isr_ctx_table[i].cb (gpio_isr_ctx_table[i].arg); + } + } + } + irq_isr_exit(); +} + +int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank, + gpio_cb_t cb, void *arg) +{ + if (gpio_init(pin, mode)) { + return -1; + } + + gpio_isr_ctx_table[pin].cb = cb; + gpio_isr_ctx_table[pin].arg = arg; + + GPIO.pin[pin].int_type = flank; + if (flank != GPIO_NONE) { + gpio_int_enabled_table [pin] = (gpio_isr_ctx_table[pin].cb != NULL); + GPIO.pin[pin].int_ena = GPIO_PRO_CPU_INTR_ENA; + + intr_matrix_set(PRO_CPU_NUM, ETS_GPIO_INTR_SOURCE, CPU_INUM_GPIO); + xt_set_interrupt_handler(CPU_INUM_GPIO, gpio_int_handler, NULL); + xt_ints_on(BIT(CPU_INUM_GPIO)); + } + + return 0; +} + +void gpio_irq_enable (gpio_t pin) +{ + CHECK_PARAM(pin < GPIO_PIN_NUMOF); + + gpio_int_enabled_table [pin] = true; +} + +void gpio_irq_disable (gpio_t pin) +{ + CHECK_PARAM(pin < GPIO_PIN_NUMOF); + + gpio_int_enabled_table [pin] = false; +} + +int gpio_read (gpio_t pin) +{ + CHECK_PARAM_RET(pin < GPIO_PIN_NUMOF, -1); +ets_printf("RR %08lx %08lx\n", GPIO.in, GPIO.in1.val); + return GPIO_REG_BIT_GET(in, in1, pin) ? 1 : 0; +} + +void gpio_write (gpio_t pin, int value) +{ + DEBUG("%s gpio=%lu val=%d\n", __func__, pin, value); + CHECK_PARAM(pin < GPIO_PIN_NUMOF); + if (value) { + GPIO_PIN_SET(pin); + } + else { + GPIO_PIN_CLR(pin); + } +} + +void gpio_set (gpio_t pin) +{ + DEBUG("%s gpio=%lu\n", __func__, pin); + CHECK_PARAM(pin < GPIO_PIN_NUMOF); + GPIO_PIN_SET(pin); +} + +void gpio_clear (gpio_t pin) +{ + DEBUG("%s gpio=%lu\n", __func__, pin); + CHECK_PARAM(pin < GPIO_PIN_NUMOF); + GPIO_PIN_CLR(pin); + +} + +void gpio_toggle (gpio_t pin) +{ + DEBUG("%s gpio=%lu\n", __func__, pin); + CHECK_PARAM(pin < GPIO_PIN_NUMOF); + GPIO_REG_BIT_XOR(out, out1, pin); +} + +void gpio_pullup_dis (gpio_t pin) +{ + CHECK_PARAM(pin < GPIO_PIN_NUMOF); + + const struct _rtc_gpio_t* rtc = (_gpio_to_rtc[pin] != -1) ? + &_rtc_gpios[_gpio_to_rtc[pin]] : NULL; + + REG_CLR_BIT(_gpio_to_iomux_reg[pin], FUN_PU); + if (rtc) { + REG_CLR_BIT(rtc->reg, BIT(rtc->pullup)); + } +} + +int8_t gpio_is_rtcio (gpio_t pin) +{ + return _gpio_to_rtc[pin]; +} + +int gpio_config_sleep_mode (gpio_t pin, bool mode, bool input) +{ + return rtcio_config_sleep_mode (pin, mode, input); +} diff --git a/cpu/esp32/periph/hwrng.c b/cpu/esp32/periph/hwrng.c new file mode 100644 index 0000000000000..270f15def57db --- /dev/null +++ b/cpu/esp32/periph/hwrng.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @ingroup drivers_periph_hwrng + * @{ + * + * @file + * @brief Low-level random number generator driver implementation + * + * @author Gunar Schorcht + * + * @} + */ + +#include "cpu.h" +#include "periph_conf.h" +#include "periph/hwrng.h" + +static const uint32_t* RNG_DATA_REG = (uint32_t*)0x3ff75144; + +void hwrng_init(void) +{ + /* no need for initialization */ +} +#include "rom/ets_sys.h" +void hwrng_read(void *buf, unsigned int num) +{ + unsigned int count = 0; + uint8_t *b = (uint8_t *)buf; + + while (count < num) { + /* read next 4 bytes of random data */ + uint32_t tmp = *RNG_DATA_REG; + + /* copy data into result vector */ + for (int i = 0; i < 4 && count < num; i++) { + b[count++] = (uint8_t)tmp; + tmp = tmp >> 8; + } + } +} + +uint32_t hwrand (void) +{ + uint32_t _tmp; + hwrng_read(&_tmp, sizeof(uint32_t)); + return _tmp; +} diff --git a/cpu/esp32/periph/i2c_hw.c b/cpu/esp32/periph/i2c_hw.c new file mode 100644 index 0000000000000..fd73d8df2f24b --- /dev/null +++ b/cpu/esp32/periph/i2c_hw.c @@ -0,0 +1,916 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @ingroup drivers_periph_i2c + * @{ + * + * @file + * @brief Low-level I2C driver implementation for ESP32 SDK + * + * @note This implementation only implements the 7-bit addressing mode. + * + * @author Gunar Schorcht + * + * @} + */ + +#ifdef I2C_HW_USED /* hardware implementation used */ + +/* + PLEASE NOTE: + + Some part of the implementation were inspired by the Espressif IoT Development + Framework [ESP-IDF](https://github.com/espressif/esp-idf.git) implementation + of I2C. These partes are marked with an according copyright notice. +*/ + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include +#include + +#include "cpu.h" +#include "log.h" +#include "mutex.h" +#include "periph_conf.h" +#include "periph/gpio.h" +#include "periph/i2c.h" +#include "thread_flags.h" + +#include "common.h" +#include "gpio_arch.h" +#include "driver/periph_ctrl.h" +#include "irq_arch.h" +#include "rom/ets_sys.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/gpio_struct.h" +#include "soc/i2c_reg.h" +#include "soc/i2c_struct.h" +#include "soc/rtc.h" +#include "soc/soc.h" +#include "syscalls.h" +#include "xtensa/xtensa_api.h" + +#if defined(I2C_NUMOF) && I2C_NUMOF > 0 + +#undef I2C_CLK_FREQ +#define I2C_CLK_FREQ rtc_clk_apb_freq_get() /* APB_CLK is used */ + +/* operation codes used for commands */ +#define I2C_CMD_RSTART 0 +#define I2C_CMD_WRITE 1 +#define I2C_CMD_READ 2 +#define I2C_CMD_STOP 3 +#define I2C_CMD_END 4 + +/* maximum number of data that can be written / read in one transfer */ +#define I2C_MAX_DATA 30 + +#define I2C_FIFO_USED 1 + +struct i2c_hw_t { + i2c_dev_t* regs; /* pointer to register data struct of the I2C device */ + uint8_t mod; /* peripheral hardware module of the I2C interface */ + uint8_t int_src; /* peripheral interrupt source used by the I2C device */ + uint8_t pin_scl; /* SCL pin */ + uint8_t pin_sda; /* SDA pin */ + uint8_t signal_scl_in; /* SCL signal to the controller */ + uint8_t signal_scl_out; /* SCL signal from the controller */ + uint8_t signal_sda_in; /* SDA signal to the controller */ + uint8_t signal_sda_out; /* SDA signal from the controller */ +}; + +static const struct i2c_hw_t _i2c_hw[] = { + #if defined(I2C0_SCL) && defined(I2C0_SDA) && !defined(I2C0_NOT_AVAILABLE) + { + .regs = &I2C0, + .mod = PERIPH_I2C0_MODULE, + .int_src = ETS_I2C_EXT0_INTR_SOURCE, + .pin_scl = I2C0_SCL, + .pin_sda = I2C0_SDA, + .signal_scl_in = I2CEXT0_SCL_IN_IDX, + .signal_scl_out = I2CEXT0_SCL_OUT_IDX, + .signal_sda_in = I2CEXT0_SDA_IN_IDX, + .signal_sda_out = I2CEXT0_SDA_OUT_IDX + }, + #endif + #if defined(I2C1_SCL) && defined(I2C1_SDA) && !defined(I2C1_NOT_AVAILABLE) + { + .regs = &I2C1, + .mod = PERIPH_I2C1_MODULE, + .int_src = ETS_I2C_EXT1_INTR_SOURCE, + .pin_scl = I2C1_SCL, + .pin_sda = I2C1_SDA, + .signal_scl_in = I2CEXT1_SCL_IN_IDX, + .signal_scl_out = I2CEXT1_SCL_OUT_IDX, + .signal_sda_in = I2CEXT1_SDA_IN_IDX, + .signal_sda_out = I2CEXT1_SDA_OUT_IDX + } + #endif +}; + +struct _i2c_bus_t +{ + i2c_speed_t speed; /* bus speed */ + uint8_t cmd; /* command index */ + uint8_t data; /* index in RAM for data */ + mutex_t lock; /* mutex lock */ + kernel_pid_t pid; /* PID of thread that triggered a transfer */ + uint32_t results; /* results of a transfer */ +}; + +static struct _i2c_bus_t _i2c_bus[] = +{ + #if defined(I2C0_SDA) && defined(I2C0_SCL) && !defined(I2C0_NOT_AVAILABLE) + { + .speed = I2C_SPEED_NORMAL, + .cmd = 0, + .data = 0, + .lock = MUTEX_INIT + }, + #endif + #if defined(I2C1_SDA) && defined(I2C1_SCL) && !defined(I2C1_NOT_AVAILABLE) + { + .speed = I2C_SPEED_NORMAL, + .cmd = 0, + .data = 0, + .lock = MUTEX_INIT + }, + #endif +}; + +/* forward declaration of internal functions */ + +static bool _i2c_init_pins(i2c_t dev); +static bool _i2c_start_cmd (i2c_t dev); +static bool _i2c_stop_cmd (i2c_t dev); +static bool _i2c_end_cmd (i2c_t dev); +static bool _i2c_write_cmd(i2c_t dev, uint8_t* data, uint8_t len); +static bool _i2c_read_cmd(i2c_t dev, uint8_t* data, uint8_t len, bool last); +static int _i2c_transfer (i2c_t dev); +static bool _i2c_reset_hw (i2c_t dev); +static inline void _i2c_delay (uint32_t delay); +static void _i2c_intr_handler (void *arg); + +/* implementation of i2c interface */ + +int i2c_init_master(i2c_t dev, i2c_speed_t speed) +{ + if (I2C_NUMOF != sizeof(_i2c_bus)/sizeof(struct _i2c_bus_t)) { + LOG_INFO("I2C_NUMOF does not match number of the I2C_SDA_x/I2C_SCL_x definitions\n"); + LOG_INFO("Please check your configuration in file board.h\n"); + assert(I2C_NUMOF < sizeof(_i2c_bus)/sizeof(struct _i2c_bus_t)); + + return -1; + } + + CHECK_PARAM_RET (dev < I2C_NUMOF, -1) + + if (speed == I2C_SPEED_FAST_PLUS || speed == I2C_SPEED_HIGH) { + LOG_INFO("I2C_SPEED_FAST_PLUS and I2C_SPEED_HIGH are not supported\n"); + return -2; + } + + _i2c_bus[dev].speed = speed; + _i2c_bus[dev].cmd = 0; + _i2c_bus[dev].data = 0; + + DEBUG ("%s scl=%d sda=%d speed=%d\n", __func__, + _i2c_hw[dev].pin_scl, _i2c_hw[dev].pin_sda, _i2c_bus[dev].speed); + + /* enable (power on) the according I2C module */ + periph_module_enable(_i2c_hw[dev].mod); + + /* initialize pins */ + if (!_i2c_init_pins(dev)) { + return -1; + } + + /* set master mode */ + _i2c_hw[dev].regs->ctr.ms_mode = 1; + + /* set bit order to MSB first */ + _i2c_hw[dev].regs->ctr.tx_lsb_first = 0; + _i2c_hw[dev].regs->ctr.rx_lsb_first = 0; + + /* determine the half period of clock in APB clock cycles */ + uint32_t half_period = 0; + + switch (speed) { + case I2C_SPEED_LOW: + /* 10 kbps (period 100 us) */ + half_period = (I2C_CLK_FREQ >> 1) / 10000; + break; + + case I2C_SPEED_NORMAL: + /* 100 kbps (period 10 us) */ + half_period = ((I2C_CLK_FREQ >> 1) / 100000); + half_period = half_period * 95 / 100; /* correction factor */ + break; + + case I2C_SPEED_FAST: + /* 400 kbps (period 2.5 us) */ + half_period = ((I2C_CLK_FREQ >> 1) / 400000); + half_period = half_period * 82 / 100; /* correction factor */ + break; + + case I2C_SPEED_FAST_PLUS: + /* 1 Mbps (period 1 us) not working */ + half_period = (I2C_CLK_FREQ >> 1) / 1000000; + break; + + case I2C_SPEED_HIGH: + /* 3.4 Mbps (period 0.3 us) not working */ + half_period = (I2C_CLK_FREQ >> 1) / 3400000; + break; + + default: + LOG_ERROR("Invalid speed value in %s\n", __func__); + return -2; + } + + /* set an timeout which is at least 16 times of half cycle */ + _i2c_hw[dev].regs->timeout.tout = half_period << 4; + + /* timing for SCL (low and high time in APB clock cycles) */ + _i2c_hw[dev].regs->scl_low_period.period = half_period; + _i2c_hw[dev].regs->scl_high_period.period = half_period; + + /* timing for SDA (sample time after rising edge and hold time after falling edge) */ + _i2c_hw[dev].regs->sda_sample.time = half_period >> 1; + _i2c_hw[dev].regs->sda_hold.time = half_period >> 1; + + /* timing for START condition (STAR hold and repeated START setup time) */ + _i2c_hw[dev].regs->scl_start_hold.time = half_period >> 1; + _i2c_hw[dev].regs->scl_rstart_setup.time = half_period >> 1; + + /* timing for STOP condition (STOP hold and STOP setup time) */ + _i2c_hw[dev].regs->scl_stop_hold.time = half_period >> 1; + _i2c_hw[dev].regs->scl_stop_setup.time = half_period >> 1; + + /* configure open drain outputs */ + _i2c_hw[dev].regs->ctr.scl_force_out = 1; + _i2c_hw[dev].regs->ctr.sda_force_out = 1; + + /* sample data during high level */ + _i2c_hw[dev].regs->ctr.sample_scl_level = 0; + + /* enable non FIFO access and disable slave FIFO address offset */ + #if I2C_FIFO_USED + _i2c_hw[dev].regs->fifo_conf.nonfifo_en = 0; + #else + _i2c_hw[dev].regs->fifo_conf.nonfifo_en = 1; + _i2c_hw[dev].regs->fifo_conf.nonfifo_rx_thres = 0; + _i2c_hw[dev].regs->fifo_conf.nonfifo_tx_thres = 0; + _i2c_hw[dev].regs->fifo_conf.rx_fifo_full_thrhd = 0; + _i2c_hw[dev].regs->fifo_conf.tx_fifo_empty_thrhd = 0; + + #endif + _i2c_hw[dev].regs->fifo_conf.fifo_addr_cfg_en = 0; + + /* route all I2C interrupt sources to same the CPU interrupt */ + intr_matrix_set(PRO_CPU_NUM, _i2c_hw[dev].int_src, CPU_INUM_I2C); + + /* set the interrupt handler and enable the interrupt */ + xt_set_interrupt_handler(CPU_INUM_I2C, _i2c_intr_handler, NULL); + xt_ints_on(BIT(CPU_INUM_I2C)); + + return 0; +} + +int i2c_acquire(i2c_t dev) +{ + CHECK_PARAM_RET (dev < I2C_NUMOF, -1) + + mutex_lock(&_i2c_bus[dev].lock); + return 0; +} + +int i2c_release(i2c_t dev) +{ + CHECK_PARAM_RET (dev < I2C_NUMOF, -1) + + mutex_unlock(&_i2c_bus[dev].lock); + return 0; +} + +int i2c_read_byte(i2c_t dev, uint8_t address, void *data) +{ + return i2c_read_bytes(dev, address, data, 1); +} + +static int _i2c_read_data (i2c_t dev, uint8_t address, uint8_t *data, int length) +{ + bool result = true; + int ret = -1; + + /* send START or repeated START condition to switch to read phase */ + result &= _i2c_start_cmd (dev); + + /* send address byte with read flag */ + uint8_t addr = (address << 1) | I2C_FLAG_READ; + result &= _i2c_write_cmd (dev, &addr, 1); + + uint32_t len = length; + uint32_t off = 0; + + /* if length > I2C_MAX_DATA read blocks I2C_MAX_DATA bytes at a time */ + while (len > I2C_MAX_DATA) + { + /* read data bytes */ + result &= _i2c_read_cmd (dev, data, I2C_MAX_DATA, false); + + /* send end command */ + result &= _i2c_end_cmd (dev); + + /* execute commands */ + if (result) { + ret = _i2c_transfer (dev); + } + + /* if transfer was successful, fetch data from I2C ram */ + if (ret == 0) { + for (unsigned i = 0; i < I2C_MAX_DATA; i++) { + #if I2C_FIFO_USED + data[i + off] = _i2c_hw[dev].regs->fifo_data.data; + #else + data[i + off] = _i2c_hw[dev].regs->ram_data[i]; + #endif + } + } + + len -= I2C_MAX_DATA; + off += I2C_MAX_DATA; + } + + /* read remaining data bytes */ + result &= _i2c_read_cmd (dev, data, len, true); + + /* send STOP condition */ + result &= _i2c_stop_cmd (dev); + + /* execute commands */ + if (result) { + ret = _i2c_transfer (dev); + } + + /* if transfer was successful, fetch data from I2C ram */ + if (ret == 0) { + for (unsigned i = 0; i < len; i++) { + #if I2C_FIFO_USED + ((uint8_t*)data)[i + off] = _i2c_hw[dev].regs->fifo_data.data; + #else + ((uint8_t*)data)[i + off] = _i2c_hw[dev].regs->ram_data[i]; + #endif + } + } + + return ret == 0 ? length : -1; +} + +int i2c_read_bytes(i2c_t dev, uint8_t address, void *data, int length) +{ + DEBUG ("%s dev=%lu addr=%02x data=%p len=%d\n", __func__, dev, address, data, length); + + CHECK_PARAM_RET (dev < I2C_NUMOF, -1); + CHECK_PARAM_RET (length > 0, 0); + CHECK_PARAM_RET (data != NULL, 0); + + /* reset hardware module to bring the FSMs in definite state */ + _i2c_reset_hw(dev); + + /* call read phase */ + return _i2c_read_data(dev, address, (uint8_t*)data, length); +} + +int i2c_read_reg(i2c_t dev, uint8_t address, uint8_t reg, void *data) +{ + return i2c_read_regs(dev, address, reg, data, 1); +} + +int i2c_read_regs(i2c_t dev, uint8_t address, uint8_t reg, void *data, int length) +{ + DEBUG ("%s dev=%lu addr=%02x reg=%02x data=%p len=%d\n", __func__, + dev, address, reg, data, length); + + CHECK_PARAM_RET (dev < I2C_NUMOF, -1) + CHECK_PARAM_RET (length > 0, 0) + CHECK_PARAM_RET (data != NULL, 0); + + bool result = true; + int ret = -1; + + /* reset hardware module to bring the FSMs in definite state */ + _i2c_reset_hw(dev); + + /* send START condition */ + result &= _i2c_start_cmd (dev); + + /* send address byte with write flag */ + uint8_t addr = (address << 1) | I2C_FLAG_WRITE; + result &= _i2c_write_cmd (dev, &addr, 1); + + /* send register address */ + result &= _i2c_write_cmd (dev, ®, 1); + + if (result) { + /* call read phase */ + ret = _i2c_read_data(dev, address, (uint8_t*)data, length); + } + + return ret; +} + +static int _i2c_write_data (i2c_t dev, uint8_t address, uint8_t *data, int length) +{ + bool result = true; + int ret = -1; + + uint32_t len = length; + uint32_t off = 0; + + /* if length > I2C_MAX_DATA write blocks I2C_MAX_DATA bytes at a time */ + while (len > I2C_MAX_DATA) + { + /* write data bytes */ + result &= _i2c_write_cmd (dev, data + off, I2C_MAX_DATA); + + /* send end command */ + result &= _i2c_end_cmd (dev); + + /* execute commands */ + if (result) { + ret = _i2c_transfer (dev); + } + + len -= I2C_MAX_DATA; + off += I2C_MAX_DATA; + } + + /* write remaining data bytes */ + result &= _i2c_write_cmd (dev, data + off, len); + + /* send STOP condition */ + result &= _i2c_stop_cmd (dev); + + /* execute commands */ + if (result) { + ret = _i2c_transfer (dev); + } + + return ret == 0 ? length : -1; +} + +int i2c_write_byte(i2c_t dev, uint8_t address, uint8_t data) +{ + return i2c_write_bytes(dev, address, &data, 1); +} + +int i2c_write_bytes(i2c_t dev, uint8_t address, const void *data, int length) +{ + DEBUG ("%s dev=%lu addr=%02x data=%p len=%d\n", __func__, dev, address, data, length); + + CHECK_PARAM_RET (dev < I2C_NUMOF, -1); + CHECK_PARAM_RET (length > 0, 0); + CHECK_PARAM_RET (data != NULL, 0); + + bool result = true; + int ret = -1; + + /* reset hardware module to bring the FSMs in definite state */ + _i2c_reset_hw(dev); + + /* send START condition */ + result &= _i2c_start_cmd (dev); + + /* send address byte with write flag */ + uint8_t addr = (address << 1) | I2C_FLAG_WRITE; + result &= _i2c_write_cmd (dev, &addr, 1); + + /* call write phase */ + if (result) { + ret = _i2c_write_data(dev, address, (uint8_t*)data, length); + } + + return ret; +} + +int i2c_write_reg(i2c_t dev, uint8_t address, uint8_t reg, uint8_t data) +{ + return i2c_write_regs(dev, address, reg, &data, 1); +} + +int i2c_write_regs(i2c_t dev, uint8_t address, uint8_t reg, + const void *data, int length) +{ + DEBUG ("%s dev=%lu addr=%02x reg=%02x data=%p len=%d\n", __func__, + dev, address, reg, data, length); + + CHECK_PARAM_RET (dev < I2C_NUMOF, -1) + CHECK_PARAM_RET (length > 0, 0) + CHECK_PARAM_RET (data != NULL, 0); + + bool result = true; + int ret = -1; + + /* reset hardware module to bring the FSMs in definite state */ + _i2c_reset_hw(dev); + + /* send START condition */ + result &= _i2c_start_cmd(dev); + + /* send address byte with write flag */ + uint8_t addr = (address << 1) | I2C_FLAG_WRITE; + result &= _i2c_write_cmd (dev, &addr, 1); + + /* send register address */ + result &= _i2c_write_cmd (dev, ®, 1); + + /* call write phase */ + if (result) { + ret = _i2c_write_data(dev, address, (uint8_t*)data, length); + } + + return ret; +} + +void i2c_poweron(i2c_t dev) +{ + CHECK_PARAM (dev < I2C_NUMOF); + + /* enable (power on) the according I2C module */ + periph_module_enable(_i2c_hw[dev].mod); +} + +void i2c_poweroff(i2c_t dev) +{ + CHECK_PARAM (dev < I2C_NUMOF); + + /* enable (power on) the according I2C module */ + periph_module_disable(_i2c_hw[dev].mod); +} + +/* internal functions */ + +static bool _i2c_init_pins(i2c_t dev) +{ + /* reset GPIO usage type if the pins were used already for I2C before to + make it possible to reinitialize I2C */ + if (_gpio_pin_usage[_i2c_hw[dev].pin_scl] == _I2C) { + _gpio_pin_usage[_i2c_hw[dev].pin_scl] = _GPIO; + } + if (_gpio_pin_usage[_i2c_hw[dev].pin_sda] == _I2C) { + _gpio_pin_usage[_i2c_hw[dev].pin_sda] = _GPIO; + } + + /* try to configure SDA and SCL pin as GPIO in open-drain mode with enabled pull-ups */ + if (gpio_init (_i2c_hw[dev].pin_scl, GPIO_IN_OD_PU) || + gpio_init (_i2c_hw[dev].pin_sda, GPIO_IN_OD_PU)) { + return false; + } + + /* bring signals to high */ + gpio_set(_i2c_hw[dev].pin_scl); + gpio_set(_i2c_hw[dev].pin_sda); + + /* store the usage type in GPIO table */ + _gpio_pin_usage[_i2c_hw[dev].pin_scl] = _I2C; + _gpio_pin_usage[_i2c_hw[dev].pin_sda] = _I2C; + + /* connect SCL and SDA pins to output signals through the GPIO matrix */ + GPIO.func_out_sel_cfg[_i2c_hw[dev].pin_scl].func_sel = _i2c_hw[dev].signal_scl_out; + GPIO.func_out_sel_cfg[_i2c_hw[dev].pin_sda].func_sel = _i2c_hw[dev].signal_sda_out; + + /* connect SCL and SDA input signals to pins through the GPIO matrix */ + GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].sig_in_sel = 1; + GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].sig_in_inv = 0; + GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].func_sel = _i2c_hw[dev].pin_scl; + GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].sig_in_sel = 1; + GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].sig_in_inv = 0; + GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].func_sel = _i2c_hw[dev].pin_sda; + + return true; +} + +static bool _i2c_start_cmd(i2c_t dev) +{ + DEBUG ("%s\n", __func__); + + /* place START condition command in command queue */ + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_RSTART; + + /* increment the command counter */ + _i2c_bus[dev].cmd++; + + return true; +} + +static bool _i2c_stop_cmd (i2c_t dev) +{ + DEBUG ("%s\n", __func__); + + /* place STOP condition command in command queue */ + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_STOP; + + /* increment the command counter */ + _i2c_bus[dev].cmd++; + + return true; +} + +static bool _i2c_end_cmd (i2c_t dev) +{ + DEBUG ("%s\n", __func__); + + /* place STOP condition command in command queue */ + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_END; + + /* increment the command counter */ + _i2c_bus[dev].cmd++; + + return true; +} + +static bool _i2c_write_cmd (i2c_t dev, uint8_t* data, uint8_t len) +{ + DEBUG ("%s dev=%lu data=%p len=%d\n", __func__, dev, data, len); + + if (_i2c_bus[dev].data + len > I2C_MAX_DATA) { + LOG_ERROR("Maximum number of bytes (32 bytes) that can be sent with " + "on transfer reached\n"); + return false; + } + + /* store the byte in RAM of I2C controller and increment the data counter */ + for (int i = 0; i < len; i++) { + #if I2C_FIFO_USED + WRITE_PERI_REG(I2C_DATA_APB_REG(dev), data[i]); + #else + _i2c_hw[dev].regs->ram_data[_i2c_bus[dev].data++] = (uint32_t)data[i]; + #endif + + } + + /* place WRITE command for multiple bytes in command queue */ + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].byte_num = len; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_en = 1; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_exp = 0; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_val = 0; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_WRITE; + + /* increment the command counter */ + _i2c_bus[dev].cmd++; + + return true; +} + +static bool _i2c_read_cmd (i2c_t dev, uint8_t* data, uint8_t len, bool last) +{ + DEBUG ("%s dev=%lu data=%p len=%d\n", __func__, dev, data, len); + + if (len < 1 || len > I2C_MAX_DATA) { + /* at least one byte has to be read */ + LOG_ERROR("At least one byte has to be read\n"); + return false; + } + + if (len > 1) + { + /* place READ command for len-1 bytes with positive ack in command queue*/ + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].byte_num = len-1; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_en = 0; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_exp = 0; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_val = 0; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_READ; + + /* increment the command counter */ + _i2c_bus[dev].cmd++; + } + + /* place READ command for last byte with negative ack in last segment in command queue*/ + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].val = 0; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].byte_num = 1; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_en = 0; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_exp = 0; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].ack_val = last ? 1 : 0; + _i2c_hw[dev].regs->command[_i2c_bus[dev].cmd].op_code = I2C_CMD_READ; + + /* increment the command counter */ + _i2c_bus[dev].cmd++; + + return true; +} + +#if 0 +static bool _i2c_clear_bus(i2c_t dev) +{ + /* reset the usage type in GPIO table */ + _gpio_pin_usage[_i2c_hw[dev].pin_scl] = _GPIO; + _gpio_pin_usage[_i2c_hw[dev].pin_sda] = _GPIO; + + /* configure SDA and SCL pin as GPIO in open-drain mode temporarily */ + gpio_init (_i2c_hw[dev].pin_scl, GPIO_IN_OD_PU); + gpio_init (_i2c_hw[dev].pin_sda, GPIO_IN_OD_PU); + + /* master send some clock pulses to make the slave release the bus */ + gpio_set (_i2c_hw[dev].pin_scl); + gpio_set (_i2c_hw[dev].pin_sda); + gpio_clear (_i2c_hw[dev].pin_sda); + for (int i = 0; i < 20; i++) { + gpio_toggle(_i2c_hw[dev].pin_scl); + } + gpio_set(_i2c_hw[dev].pin_sda); + + /* store the usage type in GPIO table */ + _gpio_pin_usage[_i2c_hw[dev].pin_scl] = _I2C; + _gpio_pin_usage[_i2c_hw[dev].pin_sda] = _I2C; + + /* connect SCL and SDA pins to output signals through the GPIO matrix */ + GPIO.func_out_sel_cfg[_i2c_hw[dev].pin_scl].func_sel = _i2c_hw[dev].signal_scl_out; + GPIO.func_out_sel_cfg[_i2c_hw[dev].pin_sda].func_sel = _i2c_hw[dev].signal_sda_out; + + /* connect SCL and SDA input signals to pins through the GPIO matrix */ + GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].sig_in_sel = 1; + GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].sig_in_inv = 0; + GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_scl_in].func_sel = _i2c_hw[dev].pin_scl; + GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].sig_in_sel = 1; + GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].sig_in_inv = 0; + GPIO.func_in_sel_cfg[_i2c_hw[dev].signal_sda_in].func_sel = _i2c_hw[dev].pin_sda; + + return true; +} +#endif + +static inline void _i2c_delay (uint32_t cycles) +{ + /* produces a delay of 0,0625 us per cycle for -O2 compile option */ + /* 1 us = ca. 16 cycles (80 MHz) / 1 us = 32 cycles (160 MHz) */ + + if (cycles) { + __asm__ volatile ("1: _addi.n %0, %0, -1 \n" + " bnez %0, 1b \n" : "=r" (cycles) : "0" (cycles)); + } +} + +/* transfer related interrupts */ +static const uint32_t transfer_int_mask = I2C_TRANS_COMPLETE_INT_ENA + | I2C_ACK_ERR_INT_ENA + | I2C_TIME_OUT_INT_ENA + | I2C_ARBITRATION_LOST_INT_ENA + | I2C_END_DETECT_INT_ENA; + +#define I2C_THREAD_FLAG BIT(0) + +/* Transfer of commands in I2C controller command pipeline */ +static int _i2c_transfer (i2c_t dev) +{ + DEBUG("%s\n", __func__); + + #if FIFO_USED + /* reset RX FIFO queue */ + _i2c_hw[dev].regs->fifo_conf.rx_fifo_rst = 1; + _i2c_hw[dev].regs->fifo_conf.rx_fifo_rst = 0; + #endif + + /* disable and enable all transmission interrupts and clear current status */ + _i2c_hw[dev].regs->int_ena.val &= ~transfer_int_mask; + _i2c_hw[dev].regs->int_ena.val |= transfer_int_mask; + _i2c_hw[dev].regs->int_clr.val = transfer_int_mask; + + /* start execution of commands in command pipeline registers */ + _i2c_bus[dev].results = 0; + _i2c_hw[dev].regs->ctr.trans_start = 0; + _i2c_hw[dev].regs->ctr.trans_start = 1; + + /* wait for transfer results */ + _i2c_bus[dev].pid = thread_getpid(); + thread_flags_wait_one(I2C_THREAD_FLAG); + + DEBUG("%s results=%08lx\n", __func__, _i2c_bus[dev].results); + + /* tranmission complete and end results are considered as success */ + _i2c_bus[dev].results &= ~(I2C_TRANS_COMPLETE_INT_ENA | I2C_END_DETECT_INT_ENA); + + #if FIFO_USED + /* reset TX FIFO queue */ + _i2c_hw[dev].regs->fifo_conf.tx_fifo_rst = 1; + _i2c_hw[dev].regs->fifo_conf.tx_fifo_rst = 0; + #endif + + /* reset command and data index */ + _i2c_bus[dev].cmd = 0; + _i2c_bus[dev].data = 0; + + return _i2c_bus[dev].results; +} + +static void /* IRAM */ _i2c_intr_handler (void *arg) +{ + /* to satisfy the compiler */ + (void)arg; + + irq_isr_enter (); + + /* all I2C peripheral interrupt sources are routed to the same interrupt, + so we have to use the status register to distinguish interruptees */ + for (unsigned dev = 0; dev < I2C_NUMOF; dev++) { + /* test for transmission complete or end interrupt */ + if (_i2c_hw[dev].regs->int_status.trans_complete || + _i2c_hw[dev].regs->int_status.end_detect) { + /* set transfer result */ + _i2c_bus[dev].results |= _i2c_hw[dev].regs->int_status.val; + /* wake up the thread that is waiting for the results */ + thread_flags_set((thread_t*)thread_get(_i2c_bus[dev].pid), I2C_THREAD_FLAG); + } + else if (_i2c_hw[dev].regs->int_status.val) { + /* set transfer result */ + _i2c_bus[dev].results |= _i2c_hw[dev].regs->int_status.val; + } + /* clear all interrupts */ + _i2c_hw[dev].regs->int_clr.val = ~0x0; + } + + irq_isr_exit (); +} + +#endif /* defined(I2C_NUMOF) && I2C_NUMOF > 0 */ + +void i2c_print_config(void) +{ + #if defined(I2C_NUMOF) && I2C_NUMOF + for (unsigned bus = 0; bus < I2C_NUMOF; bus++) { + LOG_INFO("I2C_DEV(%d): scl=%d sda=%d\n", bus, + _i2c_hw[bus].pin_scl, _i2c_hw[bus].pin_sda); + } + #else + LOG_INFO("I2C: no devices\n"); + #endif /* defined(I2C_NUMOF) && I2C_NUMOF > 0 */ +} + +/* + * PLEASE NOTE: The following function is from the ESP-IDF and is licensed + * under the Apache License, Version 2.0 (the "License"). + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD + */ +static bool _i2c_reset_hw (i2c_t dev) +{ + /* save current configuration */ + uint32_t ctr = _i2c_hw[dev].regs->ctr.val; + uint32_t fifo_conf = _i2c_hw[dev].regs->fifo_conf.val; + uint32_t scl_low_period = _i2c_hw[dev].regs->scl_low_period.val; + uint32_t scl_high_period = _i2c_hw[dev].regs->scl_high_period.val; + uint32_t scl_start_hold = _i2c_hw[dev].regs->scl_start_hold.val; + uint32_t scl_rstart_setup = _i2c_hw[dev].regs->scl_rstart_setup.val; + uint32_t scl_stop_hold = _i2c_hw[dev].regs->scl_stop_hold.val; + uint32_t scl_stop_setup = _i2c_hw[dev].regs->scl_stop_setup.val; + uint32_t sda_hold = _i2c_hw[dev].regs->sda_hold.val; + uint32_t sda_sample = _i2c_hw[dev].regs->sda_sample.val; + uint32_t timeout = _i2c_hw[dev].regs->timeout.val; + uint32_t scl_filter_cfg = _i2c_hw[dev].regs->scl_filter_cfg.val; + uint32_t sda_filter_cfg = _i2c_hw[dev].regs->sda_filter_cfg.val; + + /* reset hardware mpdule */ + i2c_poweroff(dev); + /* TODO Improvement _i2c_clear_bus(dev); */ + i2c_poweron(dev); + + /* restore configuration */ + _i2c_hw[dev].regs->int_ena.val = 0; + _i2c_hw[dev].regs->ctr.val = ctr & (~I2C_TRANS_START_M); + _i2c_hw[dev].regs->fifo_conf.val = fifo_conf; + _i2c_hw[dev].regs->scl_low_period.val = scl_low_period; + _i2c_hw[dev].regs->scl_high_period.val = scl_high_period; + _i2c_hw[dev].regs->scl_start_hold.val = scl_start_hold; + _i2c_hw[dev].regs->scl_rstart_setup.val = scl_rstart_setup; + _i2c_hw[dev].regs->scl_stop_hold.val = scl_stop_hold; + _i2c_hw[dev].regs->scl_stop_setup.val = scl_stop_setup; + _i2c_hw[dev].regs->sda_hold.val = sda_hold; + _i2c_hw[dev].regs->sda_sample.val = sda_sample; + _i2c_hw[dev].regs->timeout.val = timeout; + _i2c_hw[dev].regs->scl_filter_cfg.val = scl_filter_cfg; + _i2c_hw[dev].regs->sda_filter_cfg.val = sda_filter_cfg; + + /* disable and clear all interrupt sources */ + _i2c_hw[dev].regs->int_ena.val = 0; + _i2c_hw[dev].regs->int_clr.val = ~0x0; + + return true; +} + +#endif /* I2C_HW_USED */ diff --git a/cpu/esp32/periph/i2c_sw.c b/cpu/esp32/periph/i2c_sw.c new file mode 100644 index 0000000000000..a1712fbd961e6 --- /dev/null +++ b/cpu/esp32/periph/i2c_sw.c @@ -0,0 +1,626 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @ingroup drivers_periph_i2c + * @{ + * + * @file + * @brief Low-level I2C driver implementation for ESP32 SDK + * + * @note This implementation only implements the 7-bit addressing mode. + * + * @author Gunar Schorcht + * + * @} + */ + +/* + PLEASE NOTE: + + Some parts of the implementation bases on the bit-banging implementation as + described in [wikipedia](https://en.wikipedia.org/wiki/I%C2%B2C) as well as + its implementation in [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos.git). + These parts are under the copyright of their respective owners. +*/ + +#ifdef I2C_SW_USED /* software implementation used */ + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include + +#include "cpu.h" +#include "log.h" +#include "mutex.h" +#include "periph_conf.h" +#include "periph/gpio.h" +#include "periph/i2c.h" + +#include "common.h" +#include "gpio_arch.h" +#include "rom/ets_sys.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_struct.h" + +#if defined(I2C_NUMOF) && I2C_NUMOF > 0 + +/* max clock stretching counter */ +#define I2C_CLOCK_STRETCH 200 + +/* gpio access macros */ +#define GPIO_SET(l,h,b) if (b < 32) GPIO.l = BIT(b); else GPIO.h.val = BIT(32-b) +#define GPIO_GET(l,h,b) ((b < 32) ? GPIO.l & BIT(b) : GPIO.h.val & BIT(32-b)) + +typedef struct +{ + i2c_speed_t speed; + + bool started; + + gpio_t scl; + gpio_t sda; + + uint32_t scl_bit; /* gpio bit mask for faster access */ + uint32_t sda_bit; /* gpio bit mask for faster access */ + + uint32_t delay; + +} _i2c_bus_t; + +static _i2c_bus_t _i2c_bus[] = +{ + #if defined(I2C0_SDA) && defined(I2C0_SCL) && !defined(I2C0_NOT_AVAILABLE) + { + .sda = I2C0_SDA, + .scl = I2C0_SCL + }, + #endif + #if defined(I2C1_SDA) && defined(I2C1_SCL) && !defined(I2C1_NOT_AVAILABLE) + { + .sda = I2C1_SDA, + .scl = I2C1_SCL + }, + #endif +}; + +static const uint32_t _i2c_delays[][3] = +{ + /* values specify one half-period and are only valid for -O2 option */ + /* value = [period - 0.25 us (240 MHz) / 0.5us(160MHz) / 1.0us(80MHz)] */ + /* * cycles per second / 2 */ + /* 1 us = 48 cycles (240) / 32 cycles (160 MHz) / 16 cycles (80 MHz) */ + /* values for 240, 160, 80 MHz */ + [I2C_SPEED_LOW] = {2380, 1590, 785}, /* 10 kbps (period 100 us) */ + [I2C_SPEED_NORMAL] = { 220, 148, 68}, /* 100 kbps (period 10 us) */ + [I2C_SPEED_FAST] = { 40, 25, 8}, /* 400 kbps (period 2.5 us) */ + [I2C_SPEED_FAST_PLUS] = { 5, 0, 0}, /* 1 Mbps (period 1 us) */ + [I2C_SPEED_HIGH] = { 0, 0, 0} /* 3.4 Mbps (period 0.3 us) not working */ +}; + +static mutex_t i2c_bus_lock[I2C_NUMOF] = { MUTEX_INIT }; + +/* forward declaration of internal functions */ + +static inline void _i2c_delay (_i2c_bus_t* bus); +static inline bool _i2c_read_scl (_i2c_bus_t* bus); +static inline bool _i2c_read_sda (_i2c_bus_t* bus); +static inline void _i2c_set_scl (_i2c_bus_t* bus); +static inline void _i2c_clear_scl (_i2c_bus_t* bus); +static inline void _i2c_set_sda (_i2c_bus_t* bus); +static inline void _i2c_clear_sda (_i2c_bus_t* bus); +static void _i2c_start_cond (_i2c_bus_t* bus); +static void _i2c_stop_cond (_i2c_bus_t* bus); +static void _i2c_write_bit (_i2c_bus_t* bus, bool bit); +static bool _i2c_read_bit (_i2c_bus_t* bus); +static bool _i2c_write_byte (_i2c_bus_t* bus, uint8_t byte); +static uint8_t _i2c_read_byte (_i2c_bus_t* bus, bool ack); + +/* implementation of i2c interface */ + +int i2c_init_master(i2c_t dev, i2c_speed_t speed) +{ + if (I2C_NUMOF != sizeof(_i2c_bus)/sizeof(_i2c_bus_t)) { + LOG_INFO("I2C_NUMOF does not match number of the I2C_SDA_x/I2C_SCL_x definitions\n"); + LOG_INFO("Please check your configuration in file board.h\n"); + assert(I2C_NUMOF < sizeof(_i2c_bus)/sizeof(_i2c_bus_t)); + + return -1; + } + + CHECK_PARAM_RET (dev < I2C_NUMOF, -1) + + if (speed == I2C_SPEED_HIGH) { + LOG_INFO("I2C_SPEED_HIGH is not supported\n"); + return -2; + } + + _i2c_bus[dev].speed = speed; + _i2c_bus[dev].scl_bit = BIT(_i2c_bus[dev].scl); /* store bit mask for faster access */ + _i2c_bus[dev].sda_bit = BIT(_i2c_bus[dev].sda); /* store bit mask for faster access */ + _i2c_bus[dev].started = false; /* for handling of repeated start condition */ + + switch (ets_get_cpu_frequency()) { + case 240: _i2c_bus[dev].delay = _i2c_delays[speed][0]; break; + case 160: _i2c_bus[dev].delay = _i2c_delays[speed][1]; break; + case 80: _i2c_bus[dev].delay = _i2c_delays[speed][2]; break; + default : LOG_INFO("I2C software implementation is not supported " + "for this CPU frequency: %d MHz\n", + ets_get_cpu_frequency()); + return -1; + } + + DEBUG ("%s scl=%d sda=%d speed=%d\n", __func__, + _i2c_bus[dev].scl, _i2c_bus[dev].sda, _i2c_bus[dev].speed); + + /* reset the GPIO usage if the pins were used for I2C befor */ + if (_gpio_pin_usage[_i2c_bus[dev].scl] == _I2C) { + _gpio_pin_usage[_i2c_bus[dev].scl] = _GPIO; + } + if (_gpio_pin_usage[_i2c_bus[dev].sda] == _I2C) { + _gpio_pin_usage[_i2c_bus[dev].sda] = _GPIO; + } + + /* try to configure SDA and SCL pin as GPIO in open-drain mode with enabled pull-ups */ + if (gpio_init (_i2c_bus[dev].scl, GPIO_IN_OD_PU) || + gpio_init (_i2c_bus[dev].sda, GPIO_IN_OD_PU)) { + return -1; + } + + /* store the usage type in GPIO table */ + _gpio_pin_usage[_i2c_bus[dev].scl] = _I2C; + _gpio_pin_usage[_i2c_bus[dev].sda] = _I2C; + + /* set SDA and SCL to be floating and pulled-up to high */ + _i2c_set_sda (&_i2c_bus[dev]); + _i2c_set_scl (&_i2c_bus[dev]); + + return 0; +} + +int i2c_acquire(i2c_t dev) +{ + CHECK_PARAM_RET (dev < I2C_NUMOF, -1) + + mutex_lock(&i2c_bus_lock[dev]); + return 0; +} + +int i2c_release(i2c_t dev) +{ + CHECK_PARAM_RET (dev < I2C_NUMOF, -1) + + mutex_unlock(&i2c_bus_lock[dev]); + return 0; +} + +int /* IRAM */ i2c_read_byte(i2c_t dev, uint8_t address, void *data) +{ + return i2c_read_bytes(dev, address, data, 1); +} + +int /* IRAM */ i2c_read_bytes(i2c_t dev, uint8_t address, void *data, int length) +{ + DEBUG ("%s dev=%lu addr=%02x data=%p len=%d\n", __func__, dev, address, data, length); + + CHECK_PARAM_RET (dev < I2C_NUMOF, -1); + CHECK_PARAM_RET (length > 0, 0); + CHECK_PARAM_RET (data != NULL, 0); + + _i2c_bus_t* bus = &_i2c_bus[dev]; + int i = 0; + + /* send START condition */ + _i2c_start_cond (bus); + + /* send address byte with read flag */ + if (_i2c_write_byte (bus, (address << 1) | I2C_FLAG_READ)) { + /* receive bytes if send address was successful */ + for ( ; i < length; i++) { + ((uint8_t*)data)[i] = _i2c_read_byte (bus, i < length-1); + } + } + /* send STOP condition */ + _i2c_stop_cond (bus); + + return i; +} + +int /* IRAM */ i2c_read_reg(i2c_t dev, uint8_t address, uint8_t reg, void *data) +{ + return i2c_read_regs(dev, address, reg, data, 1); +} + +int /* IRAM */ i2c_read_regs(i2c_t dev, uint8_t address, uint8_t reg, void *data, int length) +{ + CHECK_PARAM_RET (dev < I2C_NUMOF, -1) + CHECK_PARAM_RET (length > 0, 0) + CHECK_PARAM_RET (data != NULL, 0); + + _i2c_bus_t* bus = &_i2c_bus[dev]; + + /* send START condition */ + _i2c_start_cond (bus); + + /* send address with read flag and if successful */ + /* send register address */ + if (!_i2c_write_byte (bus, (address << 1) | I2C_FLAG_WRITE) || + !_i2c_write_byte (bus, reg)) { + /* if not successful send STOP condition and return 0 */ + _i2c_stop_cond (bus); + return 0; + } + + /* receive data with repeated START condition if send address and register was successful */ + return i2c_read_bytes(dev, address, data, length); +} + +int /* IRAM */ i2c_write_byte(i2c_t dev, uint8_t address, uint8_t data) +{ + return i2c_write_bytes(dev, address, &data, 1); +} + +int /* IRAM */ i2c_write_bytes(i2c_t dev, uint8_t address, const void *data, int length) +{ + DEBUG ("%s dev=%lu addr=%02x data=%p len=%d\n", __func__, dev, address, data, length); + + CHECK_PARAM_RET (dev < I2C_NUMOF, -1); + CHECK_PARAM_RET (length > 0, 0); + CHECK_PARAM_RET (data != NULL, 0); + + _i2c_bus_t* bus = &_i2c_bus[dev]; + int i = 0; + + /* send START condition */ + _i2c_start_cond (bus); + + /* send address with read flag */ + if (!_i2c_write_byte (bus, (address << 1) | I2C_FLAG_WRITE)) { + length = 0; + } + + /* send bytes if send address was successful */ + for ( ; i < length; i++) { + if (!_i2c_write_byte (bus, ((uint8_t*)data)[i])) { + break; + } + } + + /* send STOP condition */ + _i2c_stop_cond (bus); + + return i; +} + +int /* IRAM */ i2c_write_reg(i2c_t dev, uint8_t address, uint8_t reg, uint8_t data) +{ + return i2c_write_regs(dev, address, reg, &data, 1); +} + +int /* IRAM */ i2c_write_regs(i2c_t dev, uint8_t address, uint8_t reg, const void *data, int length) +{ + CHECK_PARAM_RET (dev < I2C_NUMOF, -1) + CHECK_PARAM_RET (length > 0, 0) + CHECK_PARAM_RET (data != NULL, 0); + + _i2c_bus_t* bus = &_i2c_bus[dev]; + int i = 0; + + /* send START condition */ + _i2c_start_cond (bus); + + /* send address with read flag and if successful */ + /* send register address */ + if (!_i2c_write_byte (bus, (address << 1) | I2C_FLAG_WRITE) || + !_i2c_write_byte (bus, reg)) { + length = 0; + } + + /* send bytes if send address and register was successful */ + for ( ; i < length; i++) { + if (!_i2c_write_byte (bus, ((uint8_t*)data)[i])) { + break; + } + } + + /* send STOP condition */ + _i2c_stop_cond (bus); + + return i; +} + +void i2c_poweron(i2c_t dev) +{ + /* since I2C is realized in software there is no device to power on */ + /* just return */ +} + +void i2c_poweroff(i2c_t dev) +{ + /* since I2C is realized in software there is no device to power off */ + /* just return */ +} + +/* --- internal functions --- */ + +static inline void _i2c_delay (_i2c_bus_t* bus) +{ + /* produces a delay */ + /* ca. 16 cycles = 1 us (80 MHz) or ca. 32 cycles = 1 us (160 MHz) */ + + uint32_t cycles = bus->delay; + if (cycles) { + __asm__ volatile ("1: _addi.n %0, %0, -1 \n" + " bnez %0, 1b \n" : "=r" (cycles) : "0" (cycles)); + } +} + +/* + * Please note: SDA and SDL pins are used in GPIO_OD_PU mode + * (open-drain with pull-ups). + * + * Setting a pin which is in open-drain mode leaves the pin floating and + * the signal is pulled up to high. The signal can then be actively driven + * to low by a slave. A read operation returns the current signal at the pin. + * + * Clearing a pin which is in open-drain mode actively drives the signal to + * low. + */ + +static inline bool _i2c_read_scl(_i2c_bus_t* bus) +{ + /* read SCL status (pin is in open-drain mode and set) */ + return GPIO_GET(in, in1, bus->scl); +} + +static inline bool _i2c_read_sda(_i2c_bus_t* bus) +{ + /* read SDA status (pin is in open-drain mode and set) */ + return GPIO_GET(in, in1, bus->sda); +} + +static inline void _i2c_set_scl(_i2c_bus_t* bus) +{ + /* set SCL signal high (pin is in open-drain mode and pulled-up) */ + GPIO_SET(out_w1ts, out1_w1ts, bus->scl); +} + +static inline void _i2c_clear_scl(_i2c_bus_t* bus) +{ + /* set SCL signal low (actively driven to low) */ + GPIO_SET(out_w1tc, out1_w1tc, bus->scl); +} + +static inline void _i2c_set_sda(_i2c_bus_t* bus) +{ + /* set SDA signal high (pin is in open-drain mode and pulled-up) */ + GPIO_SET(out_w1ts, out1_w1ts, bus->sda); +} + +static inline void _i2c_clear_sda(_i2c_bus_t* bus) +{ + /* set SDA signal low (actively driven to low) */ + GPIO_SET(out_w1tc, out1_w1tc, bus->sda); +} + +static /* IRAM */ void _i2c_start_cond(_i2c_bus_t* bus) +{ + /* + * send start condition + * on entry: SDA and SCL are set to be floating and pulled-up to high + * on exit : SDA and SCL are actively driven to low + */ + + if (bus->started) { + /* prepare the repeated start condition */ + + /* SDA = passive HIGH (floating and pulled-up) */ + _i2c_set_sda (bus); + + /* t_VD;DAT not neccessary */ + /* _i2c_delay (bus); */ + + /* SCL = passive HIGH (floating and pulled-up) */ + _i2c_set_scl (bus); + + /* clock stretching wait as long as clock is driven to low by the slave */ + uint32_t stretch = I2C_CLOCK_STRETCH; + while (!_i2c_read_scl (bus) && stretch--) {} + + /* wait t_SU;STA - set-up time for a repeated START condition */ + /* min. in us: 4.7 (SM), 0.6 (FM), 0.26 (FPM), 0.16 (HSM); no max. */ + _i2c_delay (bus); + } + + /* if SDA is low, arbitration is lost and someone else is driving the bus */ + if (_i2c_read_sda (bus) == 0) { + DEBUG("%s arbitration lost dev\n", __func__); + } + + /* begin the START condition: SDA = active LOW */ + _i2c_clear_sda (bus); + + /* wait t_HD;STA - hold time (repeated) START condition, */ + /* max none */ + /* min 4.0 us (SM), 0.6 us (FM), 0.26 us (FPM), 0.16 us (HSM) */ + _i2c_delay (bus); + + /* complete the START condition: SCL = active LOW */ + _i2c_clear_scl (bus); + + /* needed for repeated start condition */ + bus->started = true; +} + +static /* IRAM */ void _i2c_stop_cond(_i2c_bus_t* bus) +{ + /* + * send stop condition + * on entry: SCL is active low and SDA can be changed + * on exit : SCL and SDA are set to be floating and pulled-up to high + */ + + /* begin the STOP condition: SDA = active LOW */ + _i2c_clear_sda (bus); + + /* wait t_LOW - LOW period of SCL clock */ + /* min. in us: 4.7 (SM), 1.3 (FM), 0.5 (FPM), 0.16 (HSM); no max. */ + _i2c_delay (bus); + + /* SCL = passive HIGH (floating and pulled up) while SDA = active LOW */ + _i2c_set_scl (bus); + + /* clock stretching wait as long as clock is driven to low by the slave */ + uint32_t stretch = I2C_CLOCK_STRETCH; + while (!_i2c_read_scl (bus) && stretch--) {} + + /* wait t_SU;STO - hold time (repeated) START condition, */ + /* min. in us: 4.0 (SM), 0.6 (FM), 0.26 (FPM), 0.16 (HSM); no max. */ + _i2c_delay (bus); + + /* complete the STOP condition: SDA = passive HIGH (floating and pulled up) */ + _i2c_set_sda (bus); + + /* wait t_BUF - bus free time between a STOP and a START condition */ + /* min. in us: 4.7 (SM), 1.3 (FM), 0.5 (FPM), 0.16 (HSM); no max. */ + _i2c_delay (bus); + + /* if SDA is low, arbitration is lost and someone else is driving the bus */ + if (_i2c_read_sda (bus) == 0) { + DEBUG("%s arbitration lost dev\n", __func__); + } + + bus->started = false; +} + +static /* IRAM */ void _i2c_write_bit (_i2c_bus_t* bus, bool bit) +{ + /* + * send one bit + * on entry: SCL is active low, SDA can be changed + * on exit : SCL is active low, SDA can be changed + */ + + /* SDA = bit */ + if (bit) { + _i2c_set_sda (bus); + } + else { + _i2c_clear_sda (bus); + } + + /* wait t_VD;DAT - data valid time (time until data are valid) */ + /* max. in us: 3.45 (SM), 0.9 (FM), 0.45 (FPM); no min */ + _i2c_delay (bus); + + /* SCL = passive HIGH (floating and pulled-up), SDA value is available */ + _i2c_set_scl (bus); + + /* wait t_HIGH - time for the slave to read SDA */ + /* min. in us: 4 (SM), 0.6 (FM), 0.26 (FPM), 0.09 (HSM); no max. */ + _i2c_delay (bus); + + /* clock stretching wait as long as clock is driven low by the slave */ + uint32_t stretch = I2C_CLOCK_STRETCH; + while (!_i2c_read_scl (bus) && stretch--) {} + + /* if SCL is high, now data is valid */ + /* if SDA is high, check that nobody else is driving SDA low */ + if (bit && !_i2c_read_sda(bus)) { + DEBUG("%s arbitration lost dev\n", __func__); + } + + /* SCL = active LOW to allow next SDA change */ + _i2c_clear_scl(bus); +} + +static /* IRAM */ bool _i2c_read_bit (_i2c_bus_t* bus) +{ + /* read one bit + * on entry: SCL is active low, SDA can be changed + * on exit : SCL is active low, SDA can be changed + */ + + bool bit; + + /* SDA = passive HIGH (floating and pulled-up) to let the slave drive data */ + _i2c_set_sda (bus); + + /* wait t_VD;DAT - data valid time (time until data are valid) */ + /* max. in us: 3.45 (SM), 0.9 (FM), 0.45 (FPM); no min */ + _i2c_delay (bus); + + /* SCL = passive HIGH (floating and pulled-up), SDA value is available */ + _i2c_set_scl (bus); + + /* clock stretching wait as long as clock is driven to low by the slave */ + uint32_t stretch = I2C_CLOCK_STRETCH; + while (!_i2c_read_scl (bus) && stretch--) {} + + /* wait t_HIGH - time for the slave to read SDA */ + /* min. in us: 4 (SM), 0.6 (FM), 0.26 (FPM), 0.09 (HSM); no max. */ + _i2c_delay (bus); + + /* SCL is high, read out bit */ + bit = _i2c_read_sda (bus); + + /* SCL = active LOW to allow next SDA change */ + _i2c_clear_scl(bus); + + return bit; +} + +static /* IRAM */ bool _i2c_write_byte (_i2c_bus_t* bus, uint8_t byte) +{ + /* send one byte and returns true in case of ACK from slave */ + + uint8_t bit; + + /* send the byte */ + for (bit = 0; bit < 8; ++bit) { + _i2c_write_bit(bus, (byte & 0x80) != 0); + byte <<= 1; + } + + /* read acknowledge bit from slave */ + return !_i2c_read_bit (bus); +} + + +static /* IRAM */ uint8_t _i2c_read_byte(_i2c_bus_t* bus, bool ack) +{ + uint8_t byte = 0; + uint8_t bit; + + /* read the byte */ + for (bit = 0; bit < 8; ++bit) { + byte = (byte << 1) | _i2c_read_bit (bus); + } + + /* write acknowledgement flag */ + _i2c_write_bit(bus, !ack); + + return byte; +} +#endif /* defined(I2C_NUMOF) && I2C_NUMOF > 0 */ + +void i2c_print_config(void) +{ + #if defined(I2C_NUMOF) && I2C_NUMOF + for (unsigned bus = 0; bus < I2C_NUMOF; bus++) { + LOG_INFO("I2C_DEV(%d): scl=%d sda=%d\n", bus, + _i2c_bus[bus].scl, _i2c_bus[bus].sda); + } + #else + LOG_INFO("I2C: no devices\n"); + #endif /* defined(I2C_NUMOF) && I2C_NUMOF > 0 */ +} + +#endif /* I2C_SW_USED */ diff --git a/cpu/esp32/periph/pm.c b/cpu/esp32/periph/pm.c new file mode 100644 index 0000000000000..201918be1fb66 --- /dev/null +++ b/cpu/esp32/periph/pm.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_32 + * @ingroup drivers_periph_pm + * @{ + * + * @file + * @brief Implementation of power management functions + * + * @author Gunar Schorcht + * @} + */ + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include "esp_attr.h" +#include "syscalls.h" + +#include "rom/rtc.h" +#include "rom/uart.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" + +void pm_set_lowest(void) +{ + DEBUG ("%s enter to sleep @%lu\n", __func__, system_get_time()); + + #if !defined(QEMU) + /* passive wait for interrupt to leave lowest power mode */ + __asm__ volatile ("waiti 0"); + + /* reset system watchdog timer */ + system_wdt_feed(); + #endif + + DEBUG ("%s exit from sleep @%lu\n", __func__, system_get_time()); +} + +void IRAM_ATTR pm_off(void) +{ + DEBUG ("%s\n", __func__); + + /* suspend UARTs */ + for (int i = 0; i < 3; ++i) { + REG_SET_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XOFF); + uart_tx_wait_idle(i); + } + + /* set all power down flags */ + uint32_t pd_flags = RTC_SLEEP_PD_DIG | + RTC_SLEEP_PD_RTC_PERIPH | + RTC_SLEEP_PD_RTC_SLOW_MEM | + RTC_SLEEP_PD_RTC_FAST_MEM | + RTC_SLEEP_PD_RTC_MEM_FOLLOW_CPU | + RTC_SLEEP_PD_VDDSDIO; + + rtc_sleep_config_t config = RTC_SLEEP_CONFIG_DEFAULT(pd_flags); + config.wifi_pd_en = 1; + config.rom_mem_pd_en = 1; + config.lslp_meminf_pd = 1; + + /* Save current frequency and switch to XTAL */ + rtc_cpu_freq_t cpu_freq = rtc_clk_cpu_freq_get(); + rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL); + + /* set deep sleep duration to forever */ + rtc_sleep_set_wakeup_time(rtc_time_get() + ~0x0UL); + + /* configure deep sleep */ + rtc_sleep_init(config); + rtc_sleep_start(RTC_TIMER_TRIG_EN, 0); + + /* Restore CPU frequency */ + rtc_clk_cpu_freq_set(cpu_freq); + + /* resume UARTs */ + for (int i = 0; i < 3; ++i) { + REG_CLR_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XOFF); + REG_SET_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XON); + REG_CLR_BIT(UART_FLOW_CONF_REG(i), UART_FORCE_XON); + } +} + +extern void esp_restart_noos(void) __attribute__ ((noreturn)); + +void pm_reboot(void) +{ + DEBUG ("%s\n", __func__); + software_reset(); +} diff --git a/cpu/esp32/periph/pwm.c b/cpu/esp32/periph/pwm.c new file mode 100644 index 0000000000000..84a747cd522cf --- /dev/null +++ b/cpu/esp32/periph/pwm.c @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @ingroup drivers_periph_pwm + * @{ + * + * @file + * @brief Low-level PWM driver implementation + * + * @author Gunar Schorcht + * @} + */ + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#include "cpu.h" +#include "log.h" +#include "irq_arch.h" +#include "periph/pwm.h" +#include "periph/gpio.h" + +#include "common.h" +#include "gpio_arch.h" + +#include "driver/periph_ctrl.h" +#include "rom/ets_sys.h" +#include "soc/gpio_struct.h" +#include "soc/gpio_sig_map.h" +#include "soc/mcpwm_reg.h" +#include "soc/mcpwm_struct.h" + +#if PWM_NUMOF + +#define PWM_NUMOF_MAX (2) /* maximum number of PWM devices */ +#define PWM_CLK (160000000UL) /* base clock of PWM devices */ +#define PWM_CPS_MAX (10000000UL) /* maximum cycles per second supported */ +#define PWM_CPS_MIN (2500UL) /* minumum cycles per second supported */ + +#define PWM_TIMER_MOD_FREEZE 0 /* timer is disabled */ +#define PWM_TIMER_MOD_UP 1 /* timer counts up */ +#define PWM_TIMER_MOD_DOWN 2 /* timer counts down */ +#define PWM_TIMER_MOD_UP_DOWN 3 /* timer counts up and then down */ + +#define PWM_TIMER_STOPS_AT_TEZ 0 /* PWM starts, then stops at next TEZ */ +#define PWM_TIMER_STOPS_AT_TEP 1 /* PWM starts, then stops at next TEP */ +#define PWM_TIMER_RUNS_ON 2 /* PWM runs on */ +#define PWM_TIMER_STARTS_STOPS_AT_TEZ 3 /* PWM starts and stops at next TEZ */ +#define PWM_TIMER_STARTS_STOPS_AT_TEP 4 /* PWM starts and stops at next TEP */ + +#define PWM_TIMER_UPDATE_IMMIDIATE 0 /* update period immediatly */ +#define PWM_TIMER_UPDATE_AT_TEZ 1 /* update period at TEZ */ +#define PWM_TIMER_UPDATE_AT_SYNC 2 /* update period at sync */ +#define PWM_TIMER_UPDATE_AT_TEZ_SYNC 3 /* update period at TEZ and sync */ + +#define PWM_OP_ACTION_NO_CHANGE 0 /* do not change output */ +#define PWM_OP_ACTION_LOW 1 /* set the output to high */ +#define PWM_OP_ACTION_HIGH 2 /* set the output to low */ +#define PWM_OP_ACTION_TOGGLE 3 /* toggle the output */ + +#define PWM_OP_CHANNEL_A 0 /* operator channel A */ +#define PWM_OP_CHANNEL_B 0 /* operator channel B */ + +/* forward declaration of internal functions */ +static void _pwm_start(pwm_t pwm); +static void _pwm_stop(pwm_t pwm); +static bool _pwm_configuration(void); + +/* data structure for static configuration of PWM devices */ +struct _pwm_hw_t { + mcpwm_dev_t* regs; /* PWM's registers set address */ + uint8_t mod; /* PWM's hardware module */ + uint8_t int_src; /* PWM's peripheral interrupt source */ + uint32_t signal_group; /* PWM's base peripheral signal index */ + uint8_t gpio_num; /* number of GPIOs used as channels outputs */ + const gpio_t* gpios; /* GPIOs used as channel outputs */ +}; + +#ifdef PWM0_GPIOS +static const gpio_t _pwm_channel_gpios_0[] = PWM0_GPIOS; +#endif + +#ifdef PWM1_GPIOS +static const gpio_t _pwm_channel_gpios_1[] = PWM1_GPIOS; +#endif + +/* static configuration of PWM devices */ +static const struct _pwm_hw_t _pwm_hw[] = +{ + #ifdef PWM0_GPIOS + { + .regs = &MCPWM0, + .mod = PERIPH_PWM0_MODULE, + .int_src = ETS_PWM0_INTR_SOURCE, + .signal_group = PWM0_OUT0A_IDX, + .gpio_num = sizeof(_pwm_channel_gpios_0) >> 2, + .gpios = _pwm_channel_gpios_0, + }, + #endif + #ifdef PWM1_GPIOS + { + .regs = &MCPWM1, + .mod = PERIPH_PWM1_MODULE, + .int_src = ETS_PWM1_INTR_SOURCE, + .signal_group = PWM1_OUT0A_IDX, + .gpio_num = sizeof(_pwm_channel_gpios_1) >> 2, + .gpios = _pwm_channel_gpios_1, + }, + #endif +}; + +/* data structure dynamic channel configuration */ +typedef struct { + bool used; + uint32_t duty; +} _pwm_chn_t; + +/* data structure for dynamic configuration of PWM devices */ +struct _pwm_dev_t { + uint16_t res; + uint32_t freq; + pwm_mode_t mode; + uint8_t chn_num; + _pwm_chn_t chn[PWM_CHANNEL_NUM_DEV_MAX]; +}; + +/* dynamic configuration of PWM devices */ +static struct _pwm_dev_t _pwm_dev[PWM_NUMOF_MAX] = {}; + +/* if pwm_init is called first time, it checks the overall pwm configuration */ +static bool _pwm_init_first_time = true; + +/* Initialize PWM device */ +uint32_t pwm_init(pwm_t pwm, pwm_mode_t mode, uint32_t freq, uint16_t res) +{ + DEBUG ("%s pwm=%lu mode=%lu freq=%lu, res=%u\n", __func__, pwm, mode, freq, res); + + CHECK_PARAM_RET (pwm < PWM_NUMOF, 0); + CHECK_PARAM_RET (freq > 0, 0); + + if (_pwm_init_first_time) { + if (!_pwm_configuration()) + return 0; + } + + if (_pwm_hw[pwm].gpio_num == 0) { + LOG_ERROR("PWM device %d has no assigned pins\n", pwm); + return 0; + } + + /* reset by disabling and enable the PWM module */ + periph_module_disable(_pwm_hw[pwm].mod); + periph_module_enable(_pwm_hw[pwm].mod); + + _pwm_dev[pwm].res = res; + _pwm_dev[pwm].freq = freq; + _pwm_dev[pwm].mode = mode; + _pwm_dev[pwm].chn_num = _pwm_hw[pwm].gpio_num; + + for (int i = 0; i < _pwm_dev[pwm].chn_num; i++) { + /* initialize channel data */ + _pwm_dev[pwm].chn[i].used = false; + _pwm_dev[pwm].chn[i].duty = 0; + + /* reset GPIO usage type if the pins were used already for PWM before + to make it possible to reinitialize PWM with new parameters */ + if (_gpio_pin_usage[_pwm_hw[pwm].gpios[i]] == _PWM) { + _gpio_pin_usage[_pwm_hw[pwm].gpios[i]] = _GPIO; + } + + if (_gpio_pin_usage[_pwm_hw[pwm].gpios[i]] != _GPIO) { + LOG_ERROR("GPIO%d is used for %s and cannot be used as PWM output\n", i, + _gpio_pin_usage_str[_gpio_pin_usage[_pwm_hw[pwm].gpios[i]]]); + return 0; + } + + if (gpio_init(_pwm_hw[pwm].gpios[i], GPIO_OUT) < 0) { + return 0; + } + + /* initialize the GPIO and route the PWM signal output to the GPIO */ + _gpio_pin_usage[_pwm_hw[pwm].gpios[i]] = _PWM; + gpio_clear (_pwm_hw[pwm].gpios[i]); + GPIO.func_out_sel_cfg[_pwm_hw[pwm].gpios[i]].func_sel = _pwm_hw[pwm].signal_group + i; + } + + /* start the PWM device */ + _pwm_start(pwm); + + return freq; +} + +uint8_t pwm_channels(pwm_t pwm) +{ + CHECK_PARAM_RET (pwm < PWM_NUMOF, 0); + + return _pwm_hw[pwm].gpio_num; +} + +void pwm_set(pwm_t pwm, uint8_t channel, uint16_t value) +{ + DEBUG("%s pwm=%lu channel=%u value=%u\n", __func__, pwm, channel, value); + + CHECK_PARAM (pwm < PWM_NUMOF); + CHECK_PARAM (channel < _pwm_dev[pwm].chn_num); + CHECK_PARAM (value <= _pwm_dev[pwm].res); + + uint32_t state = irq_disable(); + + _pwm_dev[pwm].chn[channel].duty = value; + _pwm_dev[pwm].chn[channel].used = true; + + /* determine used operator and operator output */ + uint8_t op_idx = channel >> 1; + uint8_t op_out = channel & 0x01; + + /* compute and set shadow register (compare) )value of according channel */ + uint16_t cmp = 0; + switch (_pwm_dev[pwm].mode) { + case PWM_LEFT: cmp = value; + break; + case PWM_RIGHT: cmp = value - 1; + break; + case PWM_CENTER: cmp = _pwm_hw[pwm].regs->timer[0].period.period - value; + break; + } + _pwm_hw[pwm].regs->channel[op_idx].cmpr_value[op_out].cmpr_val = cmp; + + /* set actions for timing events (reset all first) */ + _pwm_hw[pwm].regs->channel[op_idx].generator[op_out].val = 0; + + if (op_out == 0) + { + /* channel/output A is used -> set actions for channel A */ + switch (_pwm_dev[pwm].mode) + { + case PWM_LEFT: + _pwm_hw[pwm].regs->channel[op_idx].generator[op_out].utez = PWM_OP_ACTION_HIGH; + _pwm_hw[pwm].regs->channel[op_idx].generator[op_out].utea = PWM_OP_ACTION_LOW; + break; + + case PWM_RIGHT: + _pwm_hw[pwm].regs->channel[op_idx].generator[op_out].dtea = PWM_OP_ACTION_HIGH; + _pwm_hw[pwm].regs->channel[op_idx].generator[op_out].dtep = PWM_OP_ACTION_LOW; + break; + + case PWM_CENTER: + _pwm_hw[pwm].regs->channel[op_idx].generator[op_out].utea = PWM_OP_ACTION_HIGH; + _pwm_hw[pwm].regs->channel[op_idx].generator[op_out].dtea = PWM_OP_ACTION_LOW; + break; + } + } + else { + /* channel/output B is used -> set actions for channel B */ + switch (_pwm_dev[pwm].mode) + { + case PWM_LEFT: + _pwm_hw[pwm].regs->channel[op_idx].generator[op_out].utez = PWM_OP_ACTION_HIGH; + _pwm_hw[pwm].regs->channel[op_idx].generator[op_out].uteb = PWM_OP_ACTION_LOW; + break; + + case PWM_RIGHT: + _pwm_hw[pwm].regs->channel[op_idx].generator[op_out].dteb = PWM_OP_ACTION_HIGH; + _pwm_hw[pwm].regs->channel[op_idx].generator[op_out].dtep = PWM_OP_ACTION_LOW; + break; + + case PWM_CENTER: + _pwm_hw[pwm].regs->channel[op_idx].generator[op_out].uteb = PWM_OP_ACTION_HIGH; + _pwm_hw[pwm].regs->channel[op_idx].generator[op_out].dteb = PWM_OP_ACTION_LOW; + break; + } + } + + irq_restore(state); +} + +void pwm_poweron(pwm_t pwm) +{ + CHECK_PARAM (pwm < PWM_NUMOF); + periph_module_enable(_pwm_hw[pwm].mod); + _pwm_start(pwm); +} + +void pwm_poweroff(pwm_t pwm) +{ + CHECK_PARAM (pwm < PWM_NUMOF); + _pwm_stop (pwm); + periph_module_disable(_pwm_hw[pwm].mod); +} + +static void _pwm_start(pwm_t pwm) +{ + pwm_mode_t mode = _pwm_dev[pwm].mode; + uint16_t res = _pwm_dev[pwm].res; + uint32_t freq = _pwm_dev[pwm].freq; + uint32_t period = 0; + + /* set timer mode */ + switch (mode) { + case PWM_LEFT: + period = res; + _pwm_hw[pwm].regs->timer[0].mode.mode = PWM_TIMER_MOD_UP; + break; + case PWM_RIGHT: + period = res; + _pwm_hw[pwm].regs->timer[0].mode.mode = PWM_TIMER_MOD_DOWN; + break; + case PWM_CENTER: + period = res * 2; + _pwm_hw[pwm].regs->timer[0].mode.mode = PWM_TIMER_MOD_UP_DOWN; + break; + } + + uint32_t cps = period * freq; + /* maximum number of timer clock cycles per second (freq*period) must not + be greater than PWM_CPS_MAX, reduce the freq if neccessary and keep + the resolution */ + if (cps > PWM_CPS_MAX) { + freq = PWM_CPS_MAX / period; + _pwm_dev[pwm].freq = freq; + DEBUG("%s freq*res was to high, freq was reduced to %d Hz\n", + __func__, freq); + } + /* minimum number of timer clock cycles per second (freq*period) must not + be less than PWM_CPS_MIN, increase the freq if neccessary and keep + the resolution */ + else if (cps < PWM_CPS_MIN) { + freq = PWM_CPS_MIN / period; + _pwm_dev[pwm].freq = freq; + DEBUG("%s freq*res was to low, freq was increased to %d Hz\n", + __func__, freq); + } + + /* determine a suitable pwm clock prescale */ + uint32_t prescale; + if (cps > 1000000) { + /* pwm clock is not scaled, + 8 bit timer prescaler can scale down timer clock to 625 kHz */ + prescale = 1; + } + else if (cps > 100000) { + /* pwm clock is scaled down to 10 MHz, + 8 bit timer prescaler can scale down timer clock to 39,0625 kHz */ + prescale = 16; + } + else if (cps > 10000) { + /* pwm clock is scaled down to 1 MHz + 8 bit timer prescaler can scale down timer clock to 3,90625 kHz */ + prescale = 160; + } + else { + /* pwm clock is scaled down to 640 kHz + 8 bit timer prescaler can scale down timer clock to 2,5 kHz */ + prescale = 250; + } + _pwm_hw[pwm].regs->clk_cfg.prescale = prescale - 1; + + /* set timing parameters (only timer0 is used) */ + _pwm_hw[pwm].regs->timer[0].period.prescale = (PWM_CLK / prescale / cps) - 1; + _pwm_hw[pwm].regs->timer[0].period.period = (mode == PWM_CENTER) ? res : res - 1; + _pwm_hw[pwm].regs->timer[0].period.upmethod = PWM_TIMER_UPDATE_IMMIDIATE; + + /* start the timer */ + _pwm_hw[pwm].regs->timer[0].mode.start = PWM_TIMER_RUNS_ON; + + /* set timer sync phase and enable timer sync input */ + _pwm_hw[pwm].regs->timer[0].sync.timer_phase = 0; + _pwm_hw[pwm].regs->timer[0].sync.in_en = 1; + + /* set the duty for all channels to start them */ + for (int i = 0; i < _pwm_dev[pwm].chn_num; i++) { + if (_pwm_dev[pwm].chn[i].used) + pwm_set(pwm, i, _pwm_dev[pwm].chn[i].duty); + } + + /* sync all timers */ + for (unsigned i = 0; i < PWM_NUMOF; i++) { + _pwm_hw[i].regs->timer[0].sync.sync_sw = ~_pwm_hw[i].regs->timer[0].sync.sync_sw; + } +} + +static void _pwm_stop(pwm_t pwm) +{ + /* disable the timer */ + _pwm_hw[pwm].regs->timer[0].mode.mode = PWM_TIMER_MOD_FREEZE; +} + +/* do some static initialization and configuration checks */ +static bool _pwm_configuration(void) +{ + if (PWM_NUMOF > PWM_NUMOF_MAX) { + LOG_ERROR("%d PWM devices were defined, only %d PWM are " + "supported\n", __func__, PWM_NUMOF, PWM_NUMOF_MAX); + return false; + } + + for (unsigned i = 0; i < PWM_NUMOF; i++) { + if (_pwm_hw[i].gpio_num > PWM_CHANNEL_NUM_DEV_MAX) { + LOG_ERROR("Number of PWM channels of device % d is %d, " + "at maximum only %d channels per PWM device %d are " + "supported\n", __func__, + i, _pwm_hw[i].gpio_num, PWM_CHANNEL_NUM_DEV_MAX); + return false; + } + } + bool multiple_used = false; + for (unsigned i = 0; i < PWM_NUMOF; i++) { + for (unsigned j = 0; j < PWM_NUMOF; j++) { + if (i != j) { + for (unsigned k = 0; k < _pwm_hw[i].gpio_num >> 2; k++) { + for (unsigned l = 0; l < _pwm_hw[i].gpio_num >> 2; l++) { + if (_pwm_hw[i].gpios[k] == _pwm_hw[j].gpios[l]) { + LOG_ERROR("GPIO%d is used multiple times in " + "PWM devices %d and %d\n", __func__, + _pwm_hw[i].gpios[k], i, j); + multiple_used = true; + } + } + } + } + } + } + if (multiple_used) { + return false; + } + + return true; +} + +#endif /* PWM_NUMOF */ + +void pwm_print_config(void) +{ + #if PWM_NUMOF + for (unsigned pwm = 0; pwm < PWM_NUMOF; pwm++) { + LOG_INFO("PWM_DEV(%d): channels=[ ", pwm); + for (int i = 0; i < _pwm_hw[pwm].gpio_num; i++) { + LOG_INFO("%d ", _pwm_hw[pwm].gpios[i]); + } + LOG_INFO("]\n"); + } + #else + LOG_INFO("PWM: no devices\n"); + #endif /* PWM_NUMOF */ +} diff --git a/cpu/esp32/periph/spi.c b/cpu/esp32/periph/spi.c new file mode 100644 index 0000000000000..599fd02dc996b --- /dev/null +++ b/cpu/esp32/periph/spi.c @@ -0,0 +1,492 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @ingroup drivers_periph_spi + * @{ + * + * @file + * @brief Low-level SPI driver implementation + * + * @author Gunar Schorcht + * + * @} + */ +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "common.h" +#include "log.h" + +#include + +#include "cpu.h" +#include "mutex.h" +#include "periph/spi.h" + +#include "driver/periph_ctrl.h" +#include "rom/ets_sys.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/gpio_struct.h" +#include "soc/io_mux_reg.h" +#include "soc/spi_reg.h" +#include "soc/spi_struct.h" + +#include "gpio_arch.h" + +#define SPI_BUS_NUM SPI_NUMOF +#define SPI_BLOCK_SIZE 64 /* number of bytes per SPI transfer */ + +struct spi_hw_t { + spi_dev_t* regs; /* pointer to register data struct of the SPI device */ + uint8_t mod; /* peripheral hardware module of the SPI interface */ + uint8_t int_src; /* peripheral interrupt source used by the SPI device */ + uint8_t pin_sck; /* SCK pin */ + uint8_t pin_mosi; /* MOSI pin */ + uint8_t pin_miso; /* MISO pin */ + uint8_t pin_cs; /* CS pin */ + uint8_t signal_sck; /* SCK signal from the controller */ + uint8_t signal_mosi; /* MOSI signal from the controller */ + uint8_t signal_miso; /* MISO signal to the controller */ +}; + +static const struct spi_hw_t _spi[SPI_BUS_NUM] = { + { + .regs = &SPI2, /* registers of controller SPI2 (HSPI) */ + .mod = PERIPH_HSPI_MODULE, + .int_src = ETS_SPI2_INTR_SOURCE, + .pin_sck = SPI0_SCK, + .pin_mosi = SPI0_MOSI, + .pin_miso = SPI0_MISO, + .pin_cs = SPI0_CS0, + .signal_sck = HSPICLK_OUT_IDX, + .signal_mosi = HSPID_OUT_IDX, + .signal_miso = HSPIQ_IN_IDX + }, + { + .regs = &SPI3, /* registers of controller (VSPI) */ + .mod = PERIPH_VSPI_MODULE, + .int_src = ETS_SPI3_INTR_SOURCE, + .pin_sck = SPI1_SCK, + .pin_mosi = SPI1_MOSI, + .pin_miso = SPI1_MISO, + .pin_cs = SPI1_CS0, + .signal_sck = VSPICLK_OUT_IDX, + .signal_mosi = VSPID_OUT_IDX, + .signal_miso = VSPIQ_IN_IDX + }, + { + .regs = &SPI1, /* registers of controller SPI1 (FSPI) */ + .mod = PERIPH_SPI_MODULE, + .int_src = ETS_SPI1_INTR_SOURCE, + .pin_sck = SPI2_SCK, + .pin_mosi = SPI2_MOSI, + .pin_miso = SPI2_MISO, + .pin_cs = SPI2_CS0, + .signal_sck = SPICLK_OUT_IDX, + .signal_mosi = SPID_OUT_IDX, + .signal_miso = SPIQ_IN_IDX + } +}; + +/* allocate a mutex for each possible SPI interface */ +static mutex_t _spi_lock[SPI_BUS_NUM] = { MUTEX_INIT }; + +/* indicate whether SPI interface were already initilized */ +static bool _spi_initialized[SPI_BUS_NUM] = { false }; + +/* indicate whether pins of the SPI interface were already initilized */ +static bool _spi_pins_initialized[SPI_BUS_NUM] = { false }; + +/* + * GPIOs that were once initialized as SPI interface pins can not be used + * afterwards for anything else. Therefore, SPI interfaces are not initialized + * until they are used for the first time. The *spi_init* function is just a + * dummy for source code compatibility. The initialization of an SPI interface + * is performed by the *_spi_init_internal* function, which is called either by + * the *spi_init_cs* function or the *spi_acquire* function when the interface + * is used for the first time. + */ +void IRAM_ATTR spi_init (spi_t bus) +{ + return; +} + +#define CHECK_SPI_DEV(bus) { \ + CHECK_PARAM(bus < SPI_BUS_NUM); \ + if (_spi[bus].pin_sck == GPIO_UNDEF) { \ + LOG_ERROR("SPI_DEV(%s) is declared as not available\n", bus); \ + return; \ + } \ +} + +#define CHECK_SPI_DEV_RET(bus,error) { \ + CHECK_PARAM_RET(bus < SPI_BUS_NUM, error); \ + if (_spi[bus].pin_sck == GPIO_UNDEF) { \ + LOG_ERROR("SPI_DEV(%s) is declared as not available\n", bus); \ + return error; \ + } \ +} +/* Internal initialization function when the interface is used the first time */ +static void _spi_init_internal (spi_t bus) +{ + CHECK_SPI_DEV(bus); + + /* avoid multiple initializations */ + if (_spi_initialized[bus]) { + return; + } + _spi_initialized[bus] = true; + + DEBUG("%s bus=%lu\n", __func__, bus); + + /* initialize pins */ + spi_init_pins(bus); + + /* check whether pins could be initialized, otherwise return */ + if (_gpio_pin_usage[_spi[bus].pin_sck] != _SPI && + _gpio_pin_usage[_spi[bus].pin_miso] != _SPI && + _gpio_pin_usage[_spi[bus].pin_mosi] != _SPI && + _gpio_pin_usage[_spi[bus].pin_cs] != _SPI) { + return; + } + + /* enable (power on) the according SPI module */ + periph_module_enable(_spi[bus].mod); + + /* bring the bus into a defined state */ + _spi[bus].regs->user.val = SPI_USR_MOSI | SPI_CK_I_EDGE | SPI_DOUTDIN | + SPI_CS_SETUP | SPI_CS_HOLD; + + /* set byte order to little endian for read and write operations */ + _spi[bus].regs->user.wr_byte_order = 0; + _spi[bus].regs->user.rd_byte_order = 0; + + /* set bit order to most significant first for read and write operations */ + _spi[bus].regs->ctrl.wr_bit_order = 0; + _spi[bus].regs->ctrl.rd_bit_order = 0; + + /* reset all DIO or QIO flags */ + _spi[bus].regs->ctrl.fread_qio = 0; + _spi[bus].regs->ctrl.fread_dio = 0; + _spi[bus].regs->ctrl.fread_quad = 0; + _spi[bus].regs->ctrl.fread_dual = 0; + + /* disable fast read mode and write protection */ + _spi[bus].regs->ctrl.fastrd_mode = 0; + _spi[bus].regs->ctrl.wp = 0; + + /* aquire and release to set default parameters */ + spi_acquire(bus, GPIO_UNDEF, SPI_MODE_0, SPI_CLK_1MHZ); + spi_release(bus); +} + +void spi_init_pins(spi_t bus) +{ + CHECK_SPI_DEV(bus); + + /* call initialization of the SPI interface if it is not initialized yet */ + if (!_spi_initialized[bus]) { + _spi_init_internal(bus); + } + + /* avoid multiple pin initializations */ + if (_spi_pins_initialized[bus]) { + return; + } + _spi_pins_initialized[bus] = true; + + DEBUG("%s bus=%lu\n", __func__, bus); + + /* in case of SPI_DEV(2) all pins are already initialized + as SPI pins */ + if (bus != SPI_DEV(2)) { + /* if not already initialized as SPI, try to initialize the pins */ + if (gpio_init (_spi[bus].pin_sck, GPIO_OUT) || + gpio_init (_spi[bus].pin_mosi, GPIO_OUT) || + gpio_init (_spi[bus].pin_miso, GPIO_IN)) { + LOG_ERROR("SPI_DEV(%s) pins could not be initialized\n", bus); + return; + } + if (spi_init_cs(bus, _spi[bus].pin_cs) != SPI_OK) { + LOG_ERROR("SPI_DEV(%s) CS signal could not be initialized\n", bus); + return; + } + /* store the usage type in GPIO table */ + _gpio_pin_usage[_spi[bus].pin_sck] = _SPI; + _gpio_pin_usage[_spi[bus].pin_mosi] = _SPI; + _gpio_pin_usage[_spi[bus].pin_miso] = _SPI; + + /* connect SCK and MOSI pins to the output signal through the GPIO matrix */ + GPIO.func_out_sel_cfg[_spi[bus].pin_sck].func_sel = _spi[bus].signal_sck; + GPIO.func_out_sel_cfg[_spi[bus].pin_mosi].func_sel = _spi[bus].signal_mosi; + /* connect MISO input signal to the MISO pin through the GPIO matrix */ + GPIO.func_in_sel_cfg[_spi[bus].signal_miso].sig_in_sel = 1; + GPIO.func_in_sel_cfg[_spi[bus].signal_miso].sig_in_inv = 0; + GPIO.func_in_sel_cfg[_spi[bus].signal_miso].func_sel = _spi[bus].pin_miso; + } + else { + LOG_WARNING("Using SPI_DEV(2) is dangerous\n"); + } +} + +int spi_init_cs(spi_t bus, spi_cs_t cs) +{ + DEBUG("%s bus=%lu cs=%lu\n", __func__, bus, cs); + + CHECK_SPI_DEV_RET(bus, SPI_NODEV); + + /* call initialization of the SPI interface if it is not initialized yet */ + if (!_spi_initialized[bus]) { + _spi_init_internal(bus); + } + + /* return if pin is already initialized as SPI CS signal */ + if (_gpio_pin_usage [cs] == _SPI) { + return SPI_OK; + } + + /* check whether CS pin is used otherwise */ + if (_gpio_pin_usage [cs] != _GPIO) { + return SPI_NOCS; + } + + /* initialize the pin */ + gpio_init(cs, GPIO_OUT); + gpio_set (cs); + + /* pin cannot be used for anything else */ + _gpio_pin_usage [cs] = _SPI; + + return SPI_OK; +} + +int IRAM_ATTR spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk) +{ + DEBUG("%s bus=%lu cs=%lu mode=%lu clk=%lu\n", __func__, bus, cs, mode, clk); + + CHECK_SPI_DEV_RET(bus, SPI_NODEV); + + /* call initialization of the SPI interface if it is not initialized yet */ + if (!_spi_initialized[bus]) { + _spi_init_internal(bus); + } + + /* if parameter cs is GPIO_UNDEF, the default CS pin is used */ + cs = (cs == GPIO_UNDEF) ? _spi[bus].pin_cs : cs; + + /* if the CS pin used is not yet initialized, we do it now */ + if (_gpio_pin_usage[cs] != _SPI && spi_init_cs(bus, cs) != SPI_OK) { + LOG_ERROR("SPI_DEV(%s) CS signal could not be initialized\n", bus); + return SPI_NOCS; + } + + /* lock the bus */ + mutex_lock(&_spi_lock[bus]); + + /* set SPI mode, see Table 25 and Section 7.4.2 in Technical Reference */ + _spi[bus].regs->pin.ck_idle_edge = (mode == SPI_MODE_2 || mode == SPI_MODE_3); + _spi[bus].regs->user.ck_out_edge = (mode == SPI_MODE_1 || mode == SPI_MODE_2); + _spi[bus].regs->ctrl2.miso_delay_mode = (mode == SPI_MODE_0 || mode == SPI_MODE_3) ? 2 : 1; + _spi[bus].regs->ctrl2.miso_delay_num = 0; + _spi[bus].regs->ctrl2.mosi_delay_mode = 0; + _spi[bus].regs->ctrl2.mosi_delay_num = 0; + + /* set SPI clock, see Technical Reference */ + + uint32_t spi_clkdiv_pre; + uint32_t spi_clkcnt_N; + + switch (clk) { + case SPI_CLK_10MHZ: spi_clkdiv_pre = 2; /* predivides 80 MHz to 40 MHz */ + spi_clkcnt_N = 4; /* 4 cycles results into 10 MHz */ + break; + case SPI_CLK_5MHZ: spi_clkdiv_pre = 2; /* predivides 80 MHz to 40 MHz */ + spi_clkcnt_N = 8; /* 8 cycles results into 5 MHz */ + break; + case SPI_CLK_1MHZ: spi_clkdiv_pre = 2; /* predivides 80 MHz to 40 MHz */ + spi_clkcnt_N = 40; /* 40 cycles results into 1 MHz */ + break; + case SPI_CLK_400KHZ: spi_clkdiv_pre = 20; /* predivides 80 MHz to 4 MHz */ + spi_clkcnt_N = 10; /* 10 cycles results into 400 kHz */ + break; + case SPI_CLK_100KHZ: spi_clkdiv_pre = 20; /* predivides 80 MHz to 4 MHz */ + spi_clkcnt_N = 40; /* 20 cycles results into 100 kHz */ + break; + default: spi_clkdiv_pre = 20; /* predivides 80 MHz to 4 MHz */ + spi_clkcnt_N = 40; /* 20 cycles results into 100 kHz */ + } + + /* register values are set to deviders-1 */ + spi_clkdiv_pre--; + spi_clkcnt_N--; + + DEBUG("%s spi_clkdiv_prev=%lu spi_clkcnt_N=%lu\n", + __func__, spi_clkdiv_pre, spi_clkcnt_N); + + /* SPI clock is derived from APB clock by dividers */ + _spi[bus].regs->clock.clk_equ_sysclk = 0; + + /* set SPI clock deviders */ + _spi[bus].regs->clock.clkdiv_pre = spi_clkdiv_pre; + _spi[bus].regs->clock.clkcnt_n = spi_clkcnt_N; + _spi[bus].regs->clock.clkcnt_h = (spi_clkcnt_N+1)/2-1; + _spi[bus].regs->clock.clkcnt_l = spi_clkcnt_N; + + DEBUG("%s bus %d: SPI_CLOCK_REG=%08lx\n", + __func__, bus, _spi[bus].regs->clock.val); + + return SPI_OK; +} + +void IRAM_ATTR spi_release(spi_t bus) +{ + CHECK_SPI_DEV(bus); + + /* release the bus */ + mutex_unlock(&_spi_lock[bus]); +} + +static const char* _spi_names[] = { "VSPI", "HSPI", "FSPI" }; + +void spi_print_config(void) +{ + for (unsigned bus = 0; bus < SPI_NUMOF; bus++) { + LOG_INFO("SPI_DEV(%d) / %s: ", bus, _spi_names[bus]); + if (_spi[bus].pin_sck == GPIO_UNDEF) { + LOG_INFO("not available\n"); + } + else { + LOG_INFO("sck=%d ", _spi[bus].pin_sck); + LOG_INFO("miso=%d ", _spi[bus].pin_miso); + LOG_INFO("mosi=%d ", _spi[bus].pin_mosi); + LOG_INFO("cs=%d\n", _spi[bus].pin_cs); + } + } +} + +/* + * Following functions are from the hardware SPI driver of the esp-open-rtos + * project. + * + * Copyright (c) Ruslan V. Uss, 2016 + * BSD Licensed as described in the file LICENSE + * https://github.com/SuperHouse/esp-open-rtos/blob/master/LICENSE + */ + +inline static void IRAM_ATTR _set_size(uint8_t bus, uint8_t bytes) +{ + uint32_t bits = ((uint32_t)bytes << 3) - 1; + + _spi[bus].regs->mosi_dlen.val = bits; + _spi[bus].regs->miso_dlen.val = bits; +} + +inline static void IRAM_ATTR _wait(uint8_t bus) +{ + /* SPI_CMD_REG.SPI_USR is cleared when operation has been finished */ + while (_spi[bus].regs->cmd.usr) {} +} + +inline static void IRAM_ATTR _start(uint8_t bus) +{ + /* set SPI_CMD_REG.SPI_USR to start an operation */ + _spi[bus].regs->cmd.usr = 1; +} + +inline static void IRAM_ATTR _store_data(uint8_t bus, const void *data, size_t len) +{ + uint8_t words = len / 4; + uint8_t tail = len % 4; + + memcpy((void *)_spi[bus].regs->data_buf, data, len - tail); + + if (!tail) { + return; + } + + uint32_t last = 0; + uint8_t *offs = (uint8_t *)data + len - tail; + for (uint8_t i = 0; i < tail; i++) { + last = last | (offs[i] << (i * 8)); + } + _spi[bus].regs->data_buf[words] = last; +} + +static const uint8_t spi_empty_out[SPI_BLOCK_SIZE] = { 0 }; + +static void IRAM_ATTR _spi_buf_transfer(uint8_t bus, const void *out, void *in, size_t len) +{ + DEBUG("%s bus=%lu out=%p in=%p len=%lu\n", __func__, bus, out, in, len); + + /* transfer one block data */ + _wait(bus); + _set_size(bus, len); + _store_data(bus, out ? out : spi_empty_out, len); + _start(bus); + _wait(bus); + if (in) { + memcpy(in, (void *)_spi[bus].regs->data_buf, len); + } +} + +void IRAM_ATTR spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont, + const void *out, void *in, size_t len) +{ + CHECK_SPI_DEV(bus); + + DEBUG("%s bus=%lu cs=%lu cont=%d out=%p in=%p len=%lu\n", + __func__, bus, cs, cont, out, in, len); + + if (!len) { + return; + } + + #if ENABLE_DEBUG + if (out) { + DEBUG("out = "); + for (size_t i = 0; i < len; i++) { + DEBUG("%02x ", ((const uint8_t *)out)[i]); + } + DEBUG("\n"); + } + #endif + + gpio_clear (cs != SPI_CS_UNDEF ? cs : _spi[bus].pin_cs); + + size_t blocks = len / SPI_BLOCK_SIZE; + uint8_t tail = len % SPI_BLOCK_SIZE; + + DEBUG("%s bus=%lu cs=%lu blocks=%d tail=%d\n", + __func__, bus, cs, blocks, tail); + + for (size_t i = 0; i < blocks; i++) { + _spi_buf_transfer(bus, + out ? (const uint8_t *)out + i * SPI_BLOCK_SIZE : NULL, + in ? (uint8_t *)in + i * SPI_BLOCK_SIZE : NULL, SPI_BLOCK_SIZE); + } + if (tail) { + _spi_buf_transfer(bus, + out ? (const uint8_t *)out + blocks * SPI_BLOCK_SIZE : 0, + in ? (uint8_t *)in + blocks * SPI_BLOCK_SIZE : NULL, tail); + } + if (!cont) { + gpio_set (cs != SPI_CS_UNDEF ? cs : _spi[bus].pin_cs); + } + + #if ENABLE_DEBUG + if (in) { + DEBUG("in = "); + for (size_t i = 0; i < len; i++) { + DEBUG("%02x ", ((const uint8_t *)in)[i]); + } + DEBUG("\n"); + } + #endif +} diff --git a/cpu/esp32/periph/timer.c b/cpu/esp32/periph/timer.c new file mode 100644 index 0000000000000..4b7f43c65dceb --- /dev/null +++ b/cpu/esp32/periph/timer.c @@ -0,0 +1,623 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @ingroup drivers_periph_timer + * @{ + * + * @file + * @brief Low-level timer driver implementation for ESP32 SDK + * + * @author Gunar Schorcht + * @} + */ + +/* + * WARNING! enable debugging will have timing side effects and can lead + * to timer underflows, system crashes or system dead locks in worst case. + */ +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include "periph/timer.h" + +#include "driver/periph_ctrl.h" +#include "esp/common_macros.h" +#include "rom/ets_sys.h" +#include "soc/rtc.h" +#include "soc/timer_group_struct.h" +#include "xtensa/hal.h" +#include "xtensa/xtensa_api.h" + +#include "common.h" +#include "irq_arch.h" +#include "syscalls.h" +#include "xtimer.h" + +#define RTC_PLL_480M 480 /* PLL with 480 MHz at maximum */ +#define RTC_PLL_320M 320 /* PLL with 480 MHz at maximum */ + +#ifndef HW_COUNTER_USED + +/* hardware timer modules used */ + +/** + * ESP32 has four 64 bit hardware timers: + * two timer groups TMG0 and TMG1 with 2 timers each + * + * TMG0, timer 0 is used for system time in us and is therefore not + * available as low level timer. Timers have only one channel. Timer device + * are mapped to hardware timer as following: + * + * 0 -> TMG0 timer 1 + * 1 -> TMG1 timer 0 + * 2 -> TMG1 timer 1 + * + * The reason for this mapping is, that if only one timer is needed, + * TMG1 is left disabled. TMG1 is only enabled when more than one + * timer device is needed. + * + * PLEASE NOTE: Don't use ETS timer functions ets_timer_* in and this hardware + * timer implementation together! + */ + +#define HW_TIMER_NUMOF 3 +#define HW_TIMER_CHANNELS 1 +#define HW_TIMER_CLK_DIV (rtc_clk_apb_freq_get() / 1000000) +#define HW_TIMER_CORRECTION (RTC_PLL_320M / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ) +#define HW_TIMER_DELTA_MIN (MAX(HW_TIMER_CORRECTION << 1, 5)) + +struct hw_timer_regs_t { + /* see Technical Reference, section 17.4 */ + struct { + uint32_t unused : 10; + uint32_t ALARM_EN : 1; /* alarms are enabled */ + uint32_t LEVEL_INT_EN: 1; /* alarms will generate level type interrupt */ + uint32_t EDGE_INT_EN : 1; /* alarms will generate egde type interrupt */ + uint32_t DIVIDER : 16; /* timer clock prescale value (basis is ABP) */ + uint32_t AUTORELOAD : 1; /* auto-reload on alarms */ + uint32_t INCREASE : 1; /* count up */ + uint32_t EN : 1; /* timer is enabled */ + } CONFIG_REG; + uint32_t LO_REG; /* time-base counter value low 32 bits */ + uint32_t HI_REG; /* time-base counter value high 32 bits */ + uint32_t UPDATE_REG; /* time-base counter value update trigger */ + uint32_t ALARMLO_REG; /* alarm trigger time-base counter value, low 32 bits */ + uint32_t ALARMHI_REG; /* alarm trigger time-base counter value, high 32 bits */ + uint32_t LOADLO_REG; /* reload value, low 32 bits */ + uint32_t LOADHI_REG; /* reload value, high 32 bits */ + uint32_t LOAD_REG; /* reload trigger */ +}; + +struct hw_timer_ints_t { + /* see Technical Reference, section 17.4 */ + uint32_t INT_ENA_REG; /* interrupt enable bits */ + uint32_t INT_RAW_REG; /* raw interrupt status */ + uint32_t INT_STA_REG; /* masked interrupt status */ + uint32_t INT_CLR_REG; /* interrupt clear bits */ +}; + +struct hw_timer_t { + bool initialized; /* indicates whether timer is already initialized */ + bool started; /* indicates whether timer is already started */ + + timer_isr_ctx_t isr_ctx; +}; + +struct hw_timer_hw_t { + struct hw_timer_regs_t* regs; /* timer configuration regs */ + struct hw_timer_ints_t* int_regs; /* timer interrupt regs */ + uint8_t int_mask; /* timer interrupt bit mask in interrupt regs */ + uint8_t int_src; /* timer interrupt source */ +}; + +static struct hw_timer_t timers[HW_TIMER_NUMOF] = { }; +static const struct hw_timer_hw_t timers_hw[HW_TIMER_NUMOF] = +{ + { + .regs = (struct hw_timer_regs_t*)&TIMERG0.hw_timer[1], + .int_regs = (struct hw_timer_ints_t*)&TIMERG0.int_ena, + .int_mask = BIT(1), + .int_src = ETS_TG0_T1_LEVEL_INTR_SOURCE + }, + { + .regs = (struct hw_timer_regs_t*)&TIMERG1.hw_timer[0], + .int_regs = (struct hw_timer_ints_t*)&TIMERG1.int_ena, + .int_mask = BIT(0), + .int_src = ETS_TG1_T0_LEVEL_INTR_SOURCE + }, + { + .regs = (struct hw_timer_regs_t*)&TIMERG1.hw_timer[1], + .int_regs = (struct hw_timer_ints_t*)&TIMERG1.int_ena, + .int_mask = BIT(1), + .int_src = ETS_TG1_T1_LEVEL_INTR_SOURCE + } +}; + + +/** Latches the current counter value and return only the low part */ +static inline uint32_t timer_get_counter_lo(tim_t dev) +{ + /* we have to latch the current timer value */ + timers_hw[dev].regs->UPDATE_REG = 0; + /* wait until instructions have been finished */ + __asm__ volatile ("isync"); + /* read high and low part of counter */ + return timers_hw[dev].regs->LO_REG; +} + +/** Latches the current counter value and return the high and the low part */ +static inline void timer_get_counter(tim_t dev, uint32_t* hi, uint32_t* lo) +{ + /* parameter check */ + if (!hi || !lo) { + return; + } + /* we have to latch the current timer value */ + timers_hw[dev].regs->UPDATE_REG = 0; + /* wait until instructions have been finished */ + __asm__ volatile ("isync"); + /* read high and low part of counter */ + *hi = timers_hw[dev].regs->HI_REG; + *lo = timers_hw[dev].regs->LO_REG; +} + +void IRAM hw_timer_handler(void* arg) +{ + (void)arg; + + /* since all timer interrupt sources are routed to the same cpu interrupt */ + /* signal, we can't use arg to identify the timer wich caused the it */ + + irq_isr_enter(); + + for (unsigned dev = 0; dev < HW_TIMER_NUMOF; dev++) { + /* iterate over all devices and check what interrupt flags are set */ + if (timers_hw[dev].int_regs->INT_STA_REG & timers_hw[dev].int_mask) { + DEBUG("%s dev=%d\n", __func__, dev); + /* disable alarms */ + timers_hw[dev].regs->CONFIG_REG.LEVEL_INT_EN = 0; + timers_hw[dev].regs->CONFIG_REG.ALARM_EN = 0; + /* clear the bit in interrupt enable and status register */ + timers_hw[dev].int_regs->INT_ENA_REG &= ~timers_hw[dev].int_mask; + timers_hw[dev].int_regs->INT_CLR_REG |= timers_hw[dev].int_mask; + /* execute the callback function */ + timers[dev].isr_ctx.cb(timers[dev].isr_ctx.arg, 0); + } + } + + irq_isr_exit(); +} + +int timer_init (tim_t dev, unsigned long freq, timer_cb_t cb, void *arg) +{ + DEBUG("%s dev=%lu freq=%lu cb=%p arg=%p\n", __func__, dev, freq, cb, arg); + + CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1); + CHECK_PARAM_RET (freq == XTIMER_HZ_BASE, -1); + CHECK_PARAM_RET (cb != NULL, -1); + + if (timers[dev].initialized) { + DEBUG("%s timer dev=%lu is already initialized (used)\n", __func__, dev); + return -1; + } + + /* initialize timer data structure */ + timers[dev].initialized = true; + timers[dev].started = false; + timers[dev].isr_ctx.cb = cb; + timers[dev].isr_ctx.arg = arg; + + /* route all timer interrupt sources to the same level type interrupt */ + intr_matrix_set(PRO_CPU_NUM, timers_hw[dev].int_src, CPU_INUM_TIMER); + + /* we have to enable therefore the interrupt here */ + xt_set_interrupt_handler(CPU_INUM_TIMER, hw_timer_handler, NULL); + xt_ints_on(BIT(CPU_INUM_TIMER)); + + if (dev) { + /* if dev > 0 we have to enable TMG1 module */ + periph_module_enable(PERIPH_TIMG1_MODULE); + } + + /* hardware timer configuration */ + timers_hw[dev].regs->CONFIG_REG.EN = 0; + timers_hw[dev].regs->CONFIG_REG.AUTORELOAD = 0; + timers_hw[dev].regs->CONFIG_REG.INCREASE = 1; + timers_hw[dev].regs->CONFIG_REG.DIVIDER = HW_TIMER_CLK_DIV; + timers_hw[dev].regs->CONFIG_REG.EDGE_INT_EN = 0; + timers_hw[dev].regs->CONFIG_REG.LEVEL_INT_EN = 0; + timers_hw[dev].regs->CONFIG_REG.ALARM_EN = 0; + + /* start the timer */ + timer_start(dev); + + return 0; +} + +int IRAM timer_set(tim_t dev, int chn, unsigned int delta) +{ + DEBUG("%s dev=%lu channel=%ld delta=%lu\n", __func__, dev, chn, delta); + + CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1); + CHECK_PARAM_RET (chn < HW_TIMER_CHANNELS, -1); + + /* disable interrupts */ + int state = irq_disable (); + + /* disable alarms */ + timers_hw[dev].regs->CONFIG_REG.LEVEL_INT_EN = 0; + timers_hw[dev].regs->CONFIG_REG.ALARM_EN = 0; + + delta = (delta > HW_TIMER_DELTA_MIN) ? delta : HW_TIMER_DELTA_MIN; + delta = (delta > HW_TIMER_CORRECTION) ? delta - HW_TIMER_CORRECTION : HW_TIMER_CORRECTION; + + /* read the current value */ + uint32_t count_lo; + uint32_t count_hi; + timer_get_counter(dev, &count_hi, &count_lo); + + /* determine the alarm time */ + uint64_t alarm; + alarm = count_lo; + alarm += ((uint64_t)count_hi) << 32; + alarm += delta; + + timers_hw[dev].regs->ALARMHI_REG = (uint32_t)(alarm >> 32); + timers_hw[dev].regs->ALARMLO_REG = (uint32_t)(alarm & 0xffffffff); + + /* enable alarms */ + timers_hw[dev].regs->CONFIG_REG.LEVEL_INT_EN = 1; + timers_hw[dev].regs->CONFIG_REG.ALARM_EN = 1; + + /* wait until instructions have been finished */ + timers_hw[dev].regs->CONFIG_REG.EN = 1; + __asm__ volatile ("isync"); + + /* clear the bit in status and set the bit in interrupt enable */ + timers_hw[dev].int_regs->INT_CLR_REG |= timers_hw[dev].int_mask; + timers_hw[dev].int_regs->INT_ENA_REG |= timers_hw[dev].int_mask; + + /* restore interrupts enabled state */ + irq_restore (state); + + return 0; +} + +int IRAM timer_set_absolute(tim_t dev, int chn, unsigned int value) +{ + DEBUG("%s dev=%lu channel=%ld value=%lu\n", __func__, dev, chn, value); + + return timer_set (dev, chn, value - timer_read(dev)); +} + +int timer_clear(tim_t dev, int chn) +{ + DEBUG("%s dev=%lu channel=%ld\n", __func__, dev, chn); + + CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1); + CHECK_PARAM_RET (chn < HW_TIMER_CHANNELS, -1); + + /* disable alarms */ + timers_hw[dev].regs->CONFIG_REG.LEVEL_INT_EN = 0; + timers_hw[dev].regs->CONFIG_REG.ALARM_EN = 0; + /* clear the bit in interrupt enable and status register */ + timers_hw[dev].int_regs->INT_ENA_REG &= ~timers_hw[dev].int_mask; + timers_hw[dev].int_regs->INT_CLR_REG |= timers_hw[dev].int_mask; + + return 0; +} + +unsigned int IRAM timer_read(tim_t dev) +{ + CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1); + + #if ENABLE_DEBUG + uint32_t count_lo = timer_get_counter_lo(dev); + DEBUG("%s %lu\n", __func__, count_lo); + return count_lo; + #else + return timer_get_counter_lo(dev); + #endif +} + +void IRAM timer_start(tim_t dev) +{ + DEBUG("%s dev=%lu @%lu\n", __func__, dev, system_get_time()); + + CHECK_PARAM (dev < HW_TIMER_NUMOF); + + timers_hw[dev].regs->CONFIG_REG.EN = 1; +} + +void IRAM timer_stop(tim_t dev) +{ + DEBUG("%s dev=%lu\n", __func__, dev); + + CHECK_PARAM (dev < HW_TIMER_NUMOF); + + timers_hw[dev].regs->CONFIG_REG.EN = 0; +} + +#else /* HW_COUNTER_USED */ + +/* hardware counter used as timer */ + +/** + * ESP32 has 3 ccompare registers. Each of them can generate an interrupt + * at different levels: + * + * CCOMPARE INT Level Priority + * 0 6 XCHAL_TIMER0_INTERRUPT 1 low + * 1 15 XCHAL_TIMER1_INTERRUPT 3 medium + * 2 16 XCHAL_TIMER2_INTERRUPT 5 high + * + * PLEASE NOTE: High level interrupts are not disabled in any case. So be + * careful to to use CCOMPARE register 2 and timer num 2, respectively. + * By default, TIMER_NUMOF is therefore set to only 2 in periph_conf.h. + */ +#define HW_TIMER_NUMOF XCHAL_NUM_TIMERS +#define HW_TIMER_CHANNELS 1 + +#define HW_TIMER_MASK 0xffffffff +#define HW_TIMER_DELTA_MAX 0x00ffffff /* in us */ +#define HW_TIMER_DELTA_MASK 0x00ffffff +#define HW_TIMER_DELTA_RSHIFT 24 + +#define HW_TIMER_CORRECTION (RTC_PLL_480M / CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ) +#define HW_TIMER_DELTA_MIN (MAX(HW_TIMER_CORRECTION, 5)) + +#define US_TO_HW_TIMER_TICKS(t) (t * system_get_cpu_freq()) +#define HW_TIMER_TICKS_TO_US(t) (t / system_get_cpu_freq()) + +struct hw_channel_t { + bool used; /* indicates whether the channel is used */ + uint32_t start_time; /* physical time when the timer channel has been started */ + uint32_t delta_time; /* timer delta value (delta = cycles * timer_max + remainder) */ + uint32_t cycles; /* number of complete max timer cycles */ + uint32_t remainder; /* remainder timer value */ +}; + +struct hw_timer_t { + tim_t dev; /* the timer device num */ + bool initialized; /* indicates whether timer is already initialized */ + bool started; /* indicates whether timer is already started */ + timer_isr_ctx_t isr_ctx; + struct hw_channel_t channels[HW_TIMER_CHANNELS]; +}; + +static struct hw_timer_t timers[HW_TIMER_NUMOF] = { }; +static const uint8_t timers_int[HW_TIMER_NUMOF] = { XCHAL_TIMER0_INTERRUPT, + XCHAL_TIMER1_INTERRUPT, + XCHAL_TIMER2_INTERRUPT }; + +static void __timer_channel_start (struct hw_timer_t* timer, struct hw_channel_t* channel); +static void __timer_channel_stop (struct hw_timer_t* timer, struct hw_channel_t* channel); + +static uint32_t __hw_timer_ticks_max; +static uint32_t __hw_timer_ticks_min; + +void IRAM hw_timer_handler(void* arg) +{ + uint32_t dev = (uint32_t)arg; + uint32_t chn = 0; + + if (dev >= HW_TIMER_NUMOF && chn >= HW_TIMER_CHANNELS) { + return; + } + + irq_isr_enter(); + + DEBUG("%s arg=%p\n", __func__, arg); + + struct hw_timer_t* timer = &timers[dev]; + struct hw_channel_t* channel = &timer->channels[chn]; + + if (channel->cycles) { + channel->cycles--; + xthal_set_ccompare(dev, xthal_get_ccount() + __hw_timer_ticks_max); + } + else if (channel->remainder >= HW_TIMER_DELTA_MIN) { + xthal_set_ccompare (dev, xthal_get_ccount() + + US_TO_HW_TIMER_TICKS(channel->remainder)); + channel->remainder = 0; + } + else { + channel->remainder = 0; + channel->used = false; + xt_ints_off(BIT(timers_int[dev])); + xthal_set_ccompare (dev, 0); + timer->isr_ctx.cb(timer->isr_ctx.arg, chn); + } + + irq_isr_exit(); +} + +int timer_init (tim_t dev, unsigned long freq, timer_cb_t cb, void *arg) +{ + DEBUG("%s dev=%lu freq=%lu cb=%p arg=%p\n", __func__, dev, freq, cb, arg); + + CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1); + CHECK_PARAM_RET (freq == XTIMER_HZ_BASE, -1); + CHECK_PARAM_RET (cb != NULL, -1); + + if (timers[dev].initialized) { + DEBUG("%s timer dev=%lu is already initialized (used)\n", __func__, dev); + return -1; + } + + timers[dev].dev = dev; + timers[dev].initialized = true; + timers[dev].started = false; + timers[dev].isr_ctx.cb = cb; + timers[dev].isr_ctx.arg = arg; + + xt_set_interrupt_handler(timers_int[dev], hw_timer_handler, (void *)dev); + + for (int i = 0; i < HW_TIMER_CHANNELS; i++) { + timers[dev].channels[i].used = false; + timers[dev].channels[i].cycles = 0; + timers[dev].channels[i].remainder = 0; + } + + timer_start(dev); + + return 0; +} + +int IRAM timer_set(tim_t dev, int chn, unsigned int delta) +{ + DEBUG("%s dev=%lu channel=%ld delta=%lu\n", __func__, dev, chn, delta); + + CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1); + CHECK_PARAM_RET (chn < HW_TIMER_CHANNELS, -1); + + int state = irq_disable (); + + struct hw_timer_t* timer = &timers[dev]; + struct hw_channel_t* channel = &timer->channels[chn]; + + /* set delta time and channel used flag */ + channel->delta_time = delta > HW_TIMER_CORRECTION ? delta - HW_TIMER_CORRECTION : 0; + channel->used = true; + + /* start channel with new delta time */ + __timer_channel_start (timer, channel); + + irq_restore (state); + + return 0; +} + +int IRAM timer_set_absolute(tim_t dev, int chn, unsigned int value) +{ + DEBUG("%s dev=%lu channel=%ld value=%lu\n", __func__, dev, chn, value); + return timer_set (dev, chn, value - timer_read(dev)); +} + +int timer_clear(tim_t dev, int chn) +{ + DEBUG("%s dev=%lu channel=%ld\n", __func__, dev, chn); + + CHECK_PARAM_RET (dev < HW_TIMER_NUMOF, -1); + CHECK_PARAM_RET (chn < HW_TIMER_CHANNELS, -1); + + int state = irq_disable (); + + /* stop running timer channel */ + __timer_channel_stop (&timers[dev], &timers[dev].channels[chn]); + + irq_restore (state); + + return 0; +} + +unsigned int IRAM timer_read(tim_t dev) +{ + (void)dev; + + return system_get_time (); +} + +void IRAM timer_start(tim_t dev) +{ + DEBUG("%s dev=%lu @%lu\n", __func__, dev, system_get_time()); + + CHECK_PARAM (dev < HW_TIMER_NUMOF); + CHECK_PARAM (!timers[dev].started); + + int state = irq_disable (); + + __hw_timer_ticks_max = US_TO_HW_TIMER_TICKS(HW_TIMER_DELTA_MAX); + __hw_timer_ticks_min = US_TO_HW_TIMER_TICKS(HW_TIMER_DELTA_MIN); + + struct hw_timer_t* timer = &timers[dev]; + + timer->started = true; + + for (int i = 0; i < HW_TIMER_CHANNELS; i++) { + __timer_channel_start (timer, &timer->channels[i]); + } + + irq_restore (state); +} + +void IRAM timer_stop(tim_t dev) +{ + DEBUG("%s dev=%lu\n", __func__, dev); + + CHECK_PARAM (dev < HW_TIMER_NUMOF); + + int state = irq_disable (); + + struct hw_timer_t* timer = &timers[dev]; + + timer->started = false; + + for (int i = 0; i < HW_TIMER_CHANNELS; i++) { + __timer_channel_stop (timer, &timer->channels[i]); + } + + irq_restore (state); +} + + +static void IRAM __timer_channel_start (struct hw_timer_t* timer, struct hw_channel_t* channel) +{ + if (!timer->started || !channel->used) { + return; + } + + /* save channel starting time */ + channel->start_time = timer_read (0); + channel->cycles = channel->delta_time >> HW_TIMER_DELTA_RSHIFT; + channel->remainder = channel->delta_time & HW_TIMER_DELTA_MASK; + + DEBUG("%s cycles=%u remainder=%lu @%lu\n", + __func__, channel->cycles, channel->remainder, system_get_time()); + + /* start timer either with full cycles, remaining or minimum time */ + if (channel->cycles) { + channel->cycles--; + xthal_set_ccompare(timer->dev, xthal_get_ccount() + __hw_timer_ticks_max); + } + else if (channel->remainder > HW_TIMER_DELTA_MIN) { + xthal_set_ccompare(timer->dev, xthal_get_ccount() + + US_TO_HW_TIMER_TICKS(channel->remainder)); + channel->remainder = 0; + } + else { + channel->remainder = 0; + xthal_set_ccompare(timer->dev, xthal_get_ccount() + __hw_timer_ticks_min); + } + + xt_ints_on(BIT(timers_int[timer->dev])); +} + +static void IRAM __timer_channel_stop (struct hw_timer_t* timer, struct hw_channel_t* channel) +{ + if (!channel->used) { + return; + } + + xt_ints_off(BIT(timers_int[timer->dev])); + + /* compute elapsed time */ + uint32_t elapsed_time = timer_read (0) - channel->start_time; + + if (channel->delta_time > elapsed_time) { + /* compute new delta time if the timer has no been expired */ + channel->delta_time -= elapsed_time; + } + else { + /* otherwise deactivate the channel */ + channel->used = false; + } +} +#endif /* HW_COUNTER_USED */ diff --git a/cpu/esp32/periph/uart.c b/cpu/esp32/periph/uart.c new file mode 100644 index 0000000000000..31c7549d37955 --- /dev/null +++ b/cpu/esp32/periph/uart.c @@ -0,0 +1,277 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @ingroup drivers_periph_uart + * @{ + * + * @file + * @brief Low-level UART driver implementation + * + * @author Gunar Schorcht + * + * @} + */ + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include "common.h" + +#include "cpu.h" +#include "irq_arch.h" +#include "log.h" +#include "sched.h" +#include "thread.h" + +#include "periph/gpio.h" +#include "periph/uart.h" + +#include "gpio_arch.h" +#include "driver/periph_ctrl.h" +#include "esp/common_macros.h" +#include "rom/ets_sys.h" +#include "soc/gpio_reg.h" +#include "soc/gpio_sig_map.h" +#include "soc/gpio_struct.h" +#include "soc/rtc.h" +#include "soc/uart_reg.h" +#include "soc/uart_struct.h" +#include "xtensa/xtensa_api.h" + +#undef UART_CLK_FREQ +#define UART_CLK_FREQ rtc_clk_apb_freq_get() /* APB_CLK is used */ + +struct uart_hw_t { + uart_dev_t* regs; /* pointer to register data struct of the UART device */ + uint8_t pin_txd; /* TxD pin */ + uint8_t pin_rxd; /* RxD pin */ + uint8_t signal_txd; /* TxD signal from the controller */ + uint8_t signal_rxd; /* RxD signal to the controller */ + bool used; /* indicates whether UART is used */ + uint8_t int_src; /* peripheral interrupt source used by the UART device */ +}; + +/* allocate memory to store the callback functions. */ +static uart_isr_ctx_t isr_ctx[UART_NUMOF]; + +/* hardware ressources */ +static struct uart_hw_t __uarts[] = { + { + .regs = &UART0, + .pin_txd = GPIO1, + .pin_rxd = GPIO3, + .signal_txd = U0TXD_OUT_IDX, + .signal_rxd = U0RXD_IN_IDX, + .used = false, + .int_src = ETS_UART0_INTR_SOURCE + }, + #if !defined(UART1_NOT_AVAILABLE) + { .regs = &UART1, + .pin_txd = UART1_TXD, + .pin_rxd = UART1_RXD, + .signal_txd = U1TXD_OUT_IDX, + .signal_rxd = U1RXD_IN_IDX, + .used = false, + .int_src = ETS_UART1_INTR_SOURCE + }, + #endif + #if !defined(UART2_NOT_AVAILABLE) + { .regs = &UART2, + .pin_txd = UART2_TXD, + .pin_rxd = UART2_RXD, + .signal_txd = U2TXD_OUT_IDX, + .signal_rxd = U2RXD_IN_IDX, + .used = false, + .int_src = ETS_UART2_INTR_SOURCE + } + #endif +}; + +/* declaration of external functions */ +extern void uart_div_modify(uint8_t uart_no, uint32_t div); + +/* forward declaration of internal functions */ +static uint8_t IRAM __uart_rx_one_char (uart_t uart); +static void __uart_tx_one_char(uart_t uart, uint8_t data); +static void __uart_intr_enable (uart_t uart); +static void IRAM __uart_intr_handler (void *para); + +int uart_init(uart_t uart, uint32_t baudrate, uart_rx_cb_t rx_cb, void *arg) +{ + DEBUG("%s uart=%d, rate=%d, rx_cb=%p, arg=%p\n", __func__, uart, baudrate, rx_cb, arg); + + CHECK_PARAM_RET (uart < UART_NUMOF, -1); + + /* UART1 and UART2 have configurable pins */ + if (uart == UART_DEV(1) || uart == UART_DEV(2)) { + + /* try to initialize the pins as GPIOs first */ + if (gpio_init (__uarts[uart].pin_txd, GPIO_OUT) || + gpio_init (__uarts[uart].pin_rxd, GPIO_IN)) { + return -1; + } + + /* store the usage type in GPIO table */ + _gpio_pin_usage[__uarts[uart].pin_txd] = _UART; + _gpio_pin_usage[__uarts[uart].pin_rxd] = _UART; + + /* connect TxD pin to the TxD output signal through the GPIO matrix */ + GPIO.func_out_sel_cfg[__uarts[uart].pin_txd].func_sel = __uarts[uart].signal_txd; + + /* connect RxD input signal to the RxD pin through the GPIO matrix */ + GPIO.func_in_sel_cfg[__uarts[uart].signal_rxd].sig_in_sel = 1; + GPIO.func_in_sel_cfg[__uarts[uart].signal_rxd].sig_in_inv = 0; + GPIO.func_in_sel_cfg[__uarts[uart].signal_rxd].func_sel = __uarts[uart].pin_rxd; + } + + /* enable the according UART module */ + uart_poweron(uart); + + /* setup the baudrate */ + if (uart == UART_DEV(0) || uart == UART_DEV(1)) { + /* for UART0 and UART1, we can us the ROM function */ + uart_div_modify(uart, (UART_CLK_FREQ << 4) / baudrate); + } + else { + /* for UART2, we have to control it by registers */ + __uarts[uart].regs->conf0.tick_ref_always_on = 1; /* use APB_CLK */ + /* compute and set the integral and the decimal part */ + uint32_t clk = (UART_CLK_FREQ << 4) / baudrate; + __uarts[uart].regs->clk_div.div_int = clk >> 4; + __uarts[uart].regs->clk_div.div_frag = clk & 0xf; + } + + /* register interrupt context */ + isr_ctx[uart].rx_cb = rx_cb; + isr_ctx[uart].arg = arg; + + if (rx_cb) { + /* since reading can only be done byte by byte, we set + UART_RXFIFO_FULL_THRHD interrupt level to 1 byte */ + __uarts[uart].regs->conf1.rxfifo_full_thrhd = 1; + + /* enable the RX FIFO FULL interrupt */ + __uart_intr_enable (uart); + + /* route all UART interrupt sources to same the CPU interrupt */ + intr_matrix_set(PRO_CPU_NUM, __uarts[uart].int_src, CPU_INUM_UART); + + /* we have to enable therefore the CPU interrupt here */ + xt_set_interrupt_handler(CPU_INUM_UART, __uart_intr_handler, NULL); + xt_ints_on(BIT(CPU_INUM_UART)); + } + + return UART_OK; +} + +void uart_write(uart_t uart, const uint8_t *data, size_t len) +{ + CHECK_PARAM (uart < UART_NUMOF); + + for (size_t i = 0; i < len; i++) { + __uart_tx_one_char(uart, data[i]); + } +} + +void uart_poweron (uart_t uart) +{ + switch (uart) { + case 0: periph_module_enable(PERIPH_UART0_MODULE); break; + case 1: periph_module_enable(PERIPH_UART1_MODULE); break; + case 2: periph_module_enable(PERIPH_UART2_MODULE); break; + } +} + +void uart_poweroff (uart_t uart) +{ + switch (uart) { + case 0: periph_module_disable(PERIPH_UART0_MODULE); break; + case 1: periph_module_disable(PERIPH_UART1_MODULE); break; + case 2: periph_module_disable(PERIPH_UART2_MODULE); break; + } +} + +void IRAM __uart_intr_handler (void *arg) +{ + /* to satisfy the compiler */ + (void)arg; + + irq_isr_enter (); + + DEBUG("%s \n", __func__); + + /* UART0, UART1, UART2 peripheral interrupt sources are routed to the same + interrupt, so we have to use the status to distinguish interruptees */ + for (unsigned uart = 0; uart < UART_NUMOF; uart++) { + if (__uarts[uart].used && __uarts[uart].regs->int_st.rxfifo_full) { + /* read one byte of data */ + uint8_t data = __uart_rx_one_char (uart); + /* call registered RX callback function */ + isr_ctx[uart].rx_cb(isr_ctx[uart].arg, data); + /* clear interrupt flag */ + __uarts[uart].regs->int_clr.rxfifo_full = 1; + } + else { + /* TODO handle other types of interrupts, for the moment just clear them */ + __uarts[uart].regs->int_clr.val = ~0x0; + } + } + + irq_isr_exit (); +} + +/* RX/TX FIFO capacity is 128 byte */ +#define UART_FIFO_MAX 127 + +/* receive one data byte with wait */ +static uint8_t IRAM __uart_rx_one_char (uart_t uart) +{ + /* wait until at least von byte is in RX FIFO */ + while (!__uarts[uart].regs->status.rxfifo_cnt) {} + + /* read the lowest byte from RX FIFO register */ + return __uarts[uart].regs->fifo.rw_byte; +} + +/* send one data byte with wait */ +static void __uart_tx_one_char(uart_t uart, uint8_t data) +{ + /* wait until at least one byte is avaiable in the TX FIFO */ + while (__uarts[uart].regs->status.txfifo_cnt >= UART_FIFO_MAX) {} + + /* send the byte by placing it in the TX FIFO using MPU */ + WRITE_PERI_REG(UART_FIFO_AHB_REG(uart), data); +} + +static void __uart_intr_enable(uart_t uart) +{ + __uarts[uart].regs->int_ena.rxfifo_full = 1; + __uarts[uart].regs->int_clr.rxfifo_full = 1; + __uarts[uart].used = true; + + DEBUG("%s %08lx\n", __func__, __uarts[uart].regs->int_ena.val); +} + +/* systemwide UART initializations */ +void uart_system_init (void) +{ + for (unsigned uart = 0; uart < UART_NUMOF; uart++) { + /* reset all UART interrupt status registers */ + __uarts[uart].regs->int_clr.val = ~0; + } +} + +void uart_print_config(void) +{ + for (unsigned uart = 0; uart < UART_NUMOF; uart++) { + LOG_INFO("UART_DEV(%d): txd=%d rxd=%d\n", uart, + __uarts[uart].pin_txd, __uarts[uart].pin_rxd); + } +} diff --git a/cpu/esp32/periph_cpu.c b/cpu/esp32/periph_cpu.c new file mode 100644 index 0000000000000..30c3c3088bee9 --- /dev/null +++ b/cpu/esp32/periph_cpu.c @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief CPU specific definitions and functions for peripheral handling + * + * @author Gunar Schorcht + */ + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#include "periph_cpu.h" diff --git a/cpu/esp32/startup.c b/cpu/esp32/startup.c new file mode 100644 index 0000000000000..9109191c3098c --- /dev/null +++ b/cpu/esp32/startup.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief Implementation of the CPU initialization + * + * @author Gunar Schorcht + * @} + */ + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "common.h" + +#include +#include +#include + +#include "board.h" +#include "exceptions.h" +#include "kernel_defines.h" +#include "kernel_init.h" +#include "log.h" +#include "syscalls.h" +#include "thread_arch.h" + +#include "periph/cpuid.h" +#include "periph/init.h" + +#include "driver/periph_ctrl.h" +#include "esp/common_macros.h" +#include "heap/esp_heap_caps_init.h" +#include "rom/cache.h" +#include "rom/ets_sys.h" +#include "rom/rtc.h" +#include "rom/uart.h" +#include "soc/apb_ctrl_reg.h" +#include "soc/cpu.h" +#include "soc/dport_reg.h" +#include "soc/dport_access.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/rtc_cntl_struct.h" +#include "soc/timer_group_struct.h" +#include "xtensa/core-macros.h" + +#include "periph_cpu.h" + +#ifdef MODULE_UART_STDIO +#include "uart_stdio.h" +#endif + +#define MHZ 1000000UL +#define STRINGIFY(s) STRINGIFY2(s) +#define STRINGIFY2(s) #s + +/* following variables are defined in linker script */ +extern uint8_t _bss_start; +extern uint8_t _bss_end; +extern uint8_t _sheap; +extern uint8_t _eheap; + +extern uint8_t _rtc_bss_start; +extern uint8_t _rtc_bss_end; +extern uint8_t _init_start; +extern void (*__init_array_start)(void); +extern void (*__init_array_end)(void); + +/* external esp function declarations */ +extern void esp_clk_init(void); +extern void esp_perip_clk_init(void); +extern void esp_reent_init(struct _reent* r); +extern void esp_panic_wdt_stop (void); +extern void spi_ram_init(void); +extern void spi_ram_heap_init(void); +extern uint32_t hwrand (void); + +/* forward declarations */ +static void system_init(void); +static void do_global_ctors(void); +static void intr_matrix_clear(void); + +typedef int32_t esp_err_t; + +/** + * @brief CPU startup function + * + * This function is the entry point in the user application. It is called + * after a system reset to startup the system. + */ +NORETURN void IRAM call_start_cpu0 (void) +{ + register uint32_t *sp __asm__ ("a1"); (void)sp; + + RESET_REASON reset_reason; + + cpu_configure_region_protection(); + + /* move exception vectors to IRAM */ + asm volatile ("wsr %0, vecbase\n" ::"r"(&_init_start)); + + reset_reason = rtc_get_reset_reason(PRO_CPU_NUM); + + /* reset from panic handler we can by RWDT or TG0WDT */ + if (reset_reason == RTCWDT_SYS_RESET || reset_reason == TG0WDT_SYS_RESET) { + esp_panic_wdt_stop(); + } + + /* Clear BSS. Please do not attempt to do any complex stuff */ + /* (like early logging) before this. */ + memset(&_bss_start, 0, (&_bss_end - &_bss_start) * sizeof(_bss_start)); + + /* Unless waking from deep sleep (implying RTC memory is intact), clear RTC bss */ + if (reset_reason != DEEPSLEEP_RESET) { + memset(&_rtc_bss_start, 0, + (&_rtc_bss_end - &_rtc_bss_start) * sizeof(_rtc_bss_start)); + } + + ets_printf("Current clocks in Hz: CPU=%d APB=%d XTAL=%d SLOW=%d\n", + rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()), + rtc_clk_apb_freq_get(), rtc_clk_xtal_freq_get()*MHZ, + rtc_clk_slow_freq_get_hz()); + + /* init SPI RAM if enabled */ + #if CONFIG_SPIRAM_SUPPORT && CONFIG_SPIRAM_BOOT_INIT + spi_ram_init(); + #endif + + #if ENABLE_DEBUG + ets_printf("reset reason: %d\n", reset_reason); + ets_printf("_stack %p\n", sp); + ets_printf("_bss_start %p\n", &_bss_start); + ets_printf("_bss_end %p\n", &_bss_end); + #ifndef ESP_IDF_HEAP_USED + ets_printf("_heap_start %p\n", &_sheap); + ets_printf("_heap_end %p\n", &_eheap); + ets_printf("_heap_free %lu\n", get_free_heap_size()); + #endif /* ESP_IDF_HEAP_USED */ + #endif /* ENABLE_DEBUG */ + + uint8_t cpu_id[CPUID_LEN]; + cpuid_get ((void*)cpu_id); + + ets_printf("\nStarting ESP32 with ID: "); + for (unsigned i = 0; i < CPUID_LEN; i++) { + ets_printf("%02x", cpu_id[i]); + } + ets_printf("\n\nPRO cpu is up "); + + /* disable APP cpu */ + ets_printf("(single core mode, only PRO cpu is used)\n"); + DPORT_CLEAR_PERI_REG_MASK(DPORT_APPCPU_CTRL_B_REG, DPORT_APPCPU_CLKGATE_EN); + + #ifdef ESP_IDF_HEAP_USED + /* init heap */ + heap_caps_init(); + #ifdef ENABLE_DEBUG + ets_printf("heap_free(info): %lu\n", get_free_heap_size()); + #endif /* ENABLE_DEBUG */ + #endif /* ESP_IDF_HEAP_USED */ + + ets_printf("PRO cpu starts user code\n"); + system_init(); + + UNREACHABLE(); +} + +#define RTC_FAST_FREQ_8M_MHZ 8000000 +#define rtc_select_slow_clk select_rtc_slow_clk + +extern uint32_t esp_clk_slowclk_cal_get(void); +extern void IRAM_ATTR rtc_select_slow_clk(rtc_slow_freq_t slow_clk); + +static void IRAM system_clk_init (void) +{ + /* first initialize RTC with default configuration */ + rtc_config_t rtc_cfg = RTC_CONFIG_DEFAULT(); + rtc_init(rtc_cfg); + + /* set FAST_CLK to internal low power clock of 8 MHz */ + rtc_clk_fast_freq_set(RTC_FAST_FREQ_8M); + + /* set SLOW_CLK to internal low power clock of 150 kHz */ + rtc_select_slow_clk(RTC_SLOW_FREQ_RTC); + + /* wait until UART is idle to avoid loosing output */ + uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); + + /* determine configured CPU clock frequency from sdkconfig.h */ + rtc_cpu_freq_t freq; + switch (CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ) { + case 40: freq = RTC_CPU_FREQ_XTAL; /* derived from external cristal */ + break; /* normally 40 MHz */ + case 80: freq = RTC_CPU_FREQ_80M; /* derived from PLL */ + break; + case 160: freq = RTC_CPU_FREQ_160M; /* derived from PLL */ + break; + case 240: freq = RTC_CPU_FREQ_240M; /* derived from PLL */ + break; + default: freq = RTC_CPU_FREQ_2M; /* frequencies <= 8 MHz are + set to 2 MHz and handled later */ + } + + uint32_t freq_before = rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()) / MHZ ; + + /* set configured CPU frequency */ + rtc_clk_cpu_freq_set(freq); + + /* Recalculate the ccount to make time calculation correct. */ + uint32_t freq_after = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; + XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before ); + + /* disable clocks of peripherals that are not needed at startup */ + esp_perip_clk_init(); +} + +static NORETURN void IRAM system_init (void) +{ + /* initialize the ISR stack for usage measurements */ + thread_isr_stack_init(); + + /* initialize clocks (CPU_CLK, APB_CLK, SLOW and FAST) */ + system_clk_init(); + + const int uart_clk_freq = rtc_clk_apb_freq_get(); + uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM); + uart_div_modify(CONFIG_CONSOLE_UART_NUM, + (uart_clk_freq << 4) / CONFIG_CONSOLE_UART_BAUDRATE); + + /* initialize system call tables of ESP32 rom and newlib */ + syscalls_init(); + + /* enable cached read from flash */ + Cache_Read_Enable(PRO_CPU_NUM); + + /* add SPI RAM to heap if enabled */ + #if CONFIG_SPIRAM_SUPPORT && CONFIG_SPIRAM_BOOT_INIT + spi_ram_heap_init(); + #endif + + /* install execption handlers */ + init_exceptions(); + + /* clear interrupt matrix */ + intr_matrix_clear(); + + /* systemwide UART initialization */ + extern void uart_system_init (void); + uart_system_init(); + + /* Disable the hold flag of alll RTC GPIO pins */ + RTCCNTL.hold_force.val = 0; + + /* initialize newlib data structure */ + esp_reent_init(_GLOBAL_REENT); + _GLOBAL_REENT->_stdin = (FILE*) &__sf_fake_stdin; + _GLOBAL_REENT->_stdout = (FILE*) &__sf_fake_stdout; + _GLOBAL_REENT->_stderr = (FILE*) &__sf_fake_stderr; + + /* execute constructors */ + do_global_ctors(); + + /* init watchdogs */ + system_wdt_init(); + + /* init random number generator */ + srand(hwrand()); + + /* init flash drive */ + extern void spi_flash_drive_init (void); + spi_flash_drive_init(); + + #ifdef MODULE_NEWLIB_SYSCALLS_DEFAULT + /* initialization as it should be called from newlibc */ + extern void _init(void); + _init(); + #endif + + #ifdef MODULE_UART_STDIO + uart_stdio_init(); + #endif + + ets_printf("Used clocks in Hz: CPU=%d APB=%d XTAL=%d FAST=%d SLOW=%d\n", + rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()), + rtc_clk_apb_freq_get(), rtc_clk_xtal_freq_get()*MHZ, + RTC_FAST_FREQ_8M_MHZ, rtc_clk_slow_freq_get_hz()); + ets_printf("XTAL calibration value: %d\n", esp_clk_slowclk_cal_get()); + ets_printf("Heap: %lu bytes\n", get_free_heap_size()); + + /* init watchdogs */ + system_wdt_init(); + + /* initialize the board */ + board_init(); + + /* trigger static peripheral initialization */ + periph_init(); + + #if ENABLE_DEBUG + print_board_config(); + #endif + + /* starting RIOT */ + ets_printf("Starting RIOT kernel on PRO cpu\n"); + kernel_init(); + + UNREACHABLE(); +} + +static void do_global_ctors(void) +{ + void (**p)(void); + for (p = &__init_array_end - 1; p >= &__init_array_start; --p) { + (*p)(); + } +} + +static void intr_matrix_clear(void) +{ + /* attach all peripheral interrupt sources (Technical Reference, Table 7) */ + /* to an arbitrary CPU interrupt number (Technical Reference, Table 8) */ + for (int i = ETS_WIFI_MAC_INTR_SOURCE; i <= ETS_CACHE_IA_INTR_SOURCE; i++) { + intr_matrix_set(PRO_CPU_NUM, i, ETS_INVALID_INUM); + } +} diff --git a/cpu/esp32/syscalls.c b/cpu/esp32/syscalls.c new file mode 100644 index 0000000000000..c55e962572006 --- /dev/null +++ b/cpu/esp32/syscalls.c @@ -0,0 +1,554 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief Implementation of required system calls + * + * @author Gunar Schorcht + * + * @} + */ + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "cpu_conf.h" +#include "irq.h" +#include "irq_arch.h" +#include "kernel_defines.h" +#include "log.h" +#include "mutex.h" +#include "rmutex.h" +#include "sched.h" +#include "periph/pm.h" + +#include "timex.h" + +#include "esp_attr.h" +#include "esp/xtensa_ops.h" +#include "esp/common_macros.h" +#include "rom/ets_sys.h" +#include "rom/libc_stubs.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_struct.h" +#include "soc/timer_group_reg.h" +#include "soc/timer_group_struct.h" +#include "xtensa/xtensa_api.h" + +#include "periph_cpu.h" +#include "syscalls.h" + +#ifdef ESP_IDF_HEAP_USED +#include "heap/esp_heap_caps.h" +#endif + +#define MHZ 1000000UL + +#ifdef MODULE_UART_STDIO +#include "uart_stdio.h" + +int IRAM putchar(int c) +{ + char tmp = c; + if (uart_stdio_write(&tmp, 1) > 0) { + return c; + } + return -EOF; +} + +int IRAM getchar(void) +{ + char tmp; + if (uart_stdio_read(&tmp, 1) > 0) { + return tmp; + } + return -EOF; +} +#endif /* MODULE_UART_STDIO */ + +int IRAM puts(const char *s) +{ + if (!s) { + return EOF; + } + ets_printf("%s\n", s); + return strlen(s); +} + +char _printf_buf[PRINTF_BUFSIZ]; + +int IRAM printf(const char* format, ...) +{ + va_list arglist; + va_start(arglist, format); + + int ret = vsnprintf(_printf_buf, PRINTF_BUFSIZ, format, arglist); + + if (ret > 0) { + ets_printf (_printf_buf); + } + + va_end(arglist); + + return ret; +} + +/** + * @name Locking functions + * + * Following function implements the lock mechanism in newlib. The only static + * mutex defined here is the _malloc_rmtx to avoid that memory management + * functions try to lock before RIOT's threads are running. All other mutexes + * are allocated dynamically. + */ + +static rmutex_t _malloc_rmtx = RMUTEX_INIT; + +void IRAM _lock_init(_lock_t *lock) +{ + CHECK_PARAM (sched_active_thread != 0); + CHECK_PARAM (lock != NULL); + CHECK_PARAM (*lock != (_lock_t)&_malloc_rmtx); + + mutex_t* mtx = malloc (sizeof(mutex_t)); + + if (mtx) { + memset (mtx, 0, sizeof(mutex_t)); + *lock = (_lock_t)mtx; + } +} + +void IRAM _lock_init_recursive(_lock_t *lock) +{ + CHECK_PARAM (sched_active_thread != 0); + CHECK_PARAM (lock != NULL); + CHECK_PARAM (*lock != (_lock_t)&_malloc_rmtx); + + rmutex_t* rmtx = malloc (sizeof(rmutex_t)); + + if (rmtx) { + memset (rmtx, 0, sizeof(rmutex_t)); + *lock = (_lock_t)rmtx; + } +} + +void IRAM _lock_close(_lock_t *lock) +{ + CHECK_PARAM (lock != NULL); + CHECK_PARAM (*lock != (_lock_t)&_malloc_rmtx); + + free ((void*)*lock); + *lock = 0; +} + +void IRAM _lock_close_recursive(_lock_t *lock) +{ + CHECK_PARAM (lock != NULL); + CHECK_PARAM (*lock != (_lock_t)&_malloc_rmtx); + + free ((void*)*lock); + *lock = 0; +} + +void IRAM _lock_acquire(_lock_t *lock) +{ + CHECK_PARAM (sched_active_thread != 0); + CHECK_PARAM (lock != NULL && *lock != 0); + + mutex_lock ((mutex_t*)*lock); +} + +void IRAM _lock_acquire_recursive(_lock_t *lock) +{ + CHECK_PARAM (sched_active_thread != 0); + CHECK_PARAM (lock != NULL && *lock != 0); + + rmutex_lock ((rmutex_t*)*lock); +} + +int IRAM _lock_try_acquire(_lock_t *lock) +{ + CHECK_PARAM_RET (sched_active_thread != 0, 0); + CHECK_PARAM_RET (lock != NULL && *lock != 0, 0); + + return rmutex_trylock ((rmutex_t*)*lock); +} + +int IRAM _lock_try_acquire_recursive(_lock_t *lock) +{ + CHECK_PARAM_RET (sched_active_thread != 0, 0); + CHECK_PARAM_RET (lock != NULL && *lock != 0, 0); + + return mutex_trylock ((mutex_t*)*lock); +} + +void IRAM _lock_release(_lock_t *lock) +{ + CHECK_PARAM (sched_active_thread != 0); + CHECK_PARAM (lock != NULL && *lock != 0); + + mutex_unlock ((mutex_t*)*lock); +} + +void IRAM _lock_release_recursive(_lock_t *lock) +{ + CHECK_PARAM (sched_active_thread != 0); + CHECK_PARAM (lock != NULL && *lock != 0); + + rmutex_unlock ((rmutex_t*)*lock); +} + +/** + * @name Memory allocation functions + */ + +#ifdef ESP_IDF_HEAP_USED + +extern void *heap_caps_malloc_default( size_t size ); +extern void *heap_caps_realloc_default( void *ptr, size_t size ); + +void* IRAM_ATTR _malloc_r(struct _reent *r, size_t size) +{ + return heap_caps_malloc_default( size ); +} + +void IRAM_ATTR _free_r(struct _reent *r, void* ptr) +{ + heap_caps_free( ptr ); +} + +void* IRAM_ATTR _realloc_r(struct _reent *r, void* ptr, size_t size) +{ + return heap_caps_realloc_default( ptr, size ); +} + +void* IRAM_ATTR _calloc_r(struct _reent *r, size_t count, size_t size) +{ + void* result = heap_caps_malloc_default(count * size); + if (result) { + bzero(result, count * size); + } + return result; +} + +#ifndef MODULE_NEWLIB_SYSCALLS_DEFAULT +/* this should not happen when ESP_IDF_HEAP_USED is activated since heap_caps + doesn't use _sbrk_r to allocate memory blocks */ +void* _sbrk_r (struct _reent *r, ptrdiff_t sz) +{ + _exit(ENOSYS); +} +#endif /* MODULE_NEWLIB_SYSCALLS_DEFAULT */ + +#else /* ESP_IDF_HEAP_USED */ + +extern uint8_t _eheap; /* end of heap (defined in esp32.common.ld) */ +extern uint8_t _sheap; /* start of heap (defined in esp32.common.ld) */ + +#ifdef MODULE_NEWLIB_SYSCALLS_DEFAULT + +extern uint8_t *heap_top; +#define _cheap heap_top + +#else /* MODULE_NEWLIB_SYSCALLS_DEFAULT */ + +static uint8_t* _cheap = 0; /* last allocated chunk of heap */ + +void* IRAM _sbrk_r (struct _reent *r, ptrdiff_t incr) +{ + uint8_t* _cheap_old; + + /* initial _cheap */ + if (_cheap == NULL) { + _cheap = &_sheap; + } + + /* save old _cheap */ + _cheap_old = _cheap; + + /* check whether _cheap + incr overflows the heap */ + if (_cheap + incr >= &_eheap) { + r->_errno = ENOMEM; + return (caddr_t)-1; + } + + /* set new _cheap */ + _cheap += incr; + + #if ENABLE_DEBUG + uint32_t remaining = &_eheap - _cheap; + printf ("%s %lu byte allocated in %p .. %p, remaining %u\n", + __func__, incr, _cheap_old, _cheap, remaining); + #endif + + /* return allocated memory */ + return (caddr_t) _cheap_old; +} + +#endif /* MODULE_NEWLIB_SYSCALLS_DEFAULT */ +#endif /* ESP_IDF_HEAP_USED */ + +unsigned int IRAM get_free_heap_size (void) +{ + #if ESP_IDF_HEAP_USED + return heap_caps_get_free_size( MALLOC_CAP_DEFAULT ); + #else + return &_eheap - ((_cheap) ? _cheap : &_sheap); + #endif +} + +/* alias for compatibility with espressif/wifi_libs */ +uint32_t esp_get_free_heap_size( void ) __attribute__((alias("get_free_heap_size"))); + + +/** + * @name Other system functions + */ + +#ifndef MODULE_NEWLIB_SYSCALLS_DEFAULT + +int _getpid_r(struct _reent *r) +{ + return sched_active_pid; +} + +int _kill_r(struct _reent *r, int pid, int sig) +{ + DEBUG("%s: system function not yet implemented\n", __func__); + r->_errno = ESRCH; /* no such process */ + return -1; +} + +void _exit(int __status) +{ + LOG_INFO("#! exit %d: powering off\n", __status); + pm_off(); + while(1); +} + +#endif /* MODULE_NEWLIB_SYSCALLS_DEFAULT */ + +void _abort(void) +{ + LOG_INFO("#! abort called: powering off\n"); + pm_off(); + while(1); +} + +void _exit_r(struct _reent *r, int status) +{ + _exit(status); +} + +clock_t IRAM_ATTR _times_r(struct _reent *r, struct tms *ptms) +{ + ptms->tms_cstime = 0; + ptms->tms_cutime = 0; + ptms->tms_stime = system_get_time() / (US_PER_SEC / CLK_TCK); + ptms->tms_utime = 0; + + return ptms->tms_stime / MHZ; +} + +struct _reent* __getreent(void) { + return _GLOBAL_REENT; +} + +static int _no_sys_func (struct _reent *r) +{ + DEBUG("%s: system function does not exist\n", __func__); + r->_errno = ENOSYS; + return -1; +} + +static struct _reent s_reent; + +static struct syscall_stub_table s_stub_table = +{ + .__getreent = &__getreent, + + ._malloc_r = &_malloc_r, + ._free_r = &_free_r, + ._realloc_r = &_realloc_r, + ._calloc_r = &_calloc_r, + ._sbrk_r = &_sbrk_r, + + ._system_r = (int (*)(struct _reent *, const char*))&_no_sys_func, + ._raise_r = (void (*)(struct _reent *))&_no_sys_func, + ._abort = &_abort, + ._exit_r = &_exit_r, + ._getpid_r = &_getpid_r, + ._kill_r = &_kill_r, + + ._times_r = &_times_r, + #ifdef MODULE_NEWLIB_SYSCALLS_DEFAULT + ._gettimeofday_r = _gettimeofday_r, + ._open_r = &_open_r, + ._close_r = &_close_r, + ._lseek_r = (int (*)(struct _reent *r, int, int, int))&_lseek_r, + ._fstat_r = &_fstat_r, + ._stat_r = &_stat_r, + ._write_r = (int (*)(struct _reent *r, int, const void *, int))&_write_r, + ._read_r = (int (*)(struct _reent *r, int, void *, int))&_read_r, + ._unlink_r = &_unlink_r, + #else /* MODULE_NEWLIB_SYSCALLS_DEFAULT */ + ._gettimeofday_r = (int (*)(struct _reent *r, struct timeval *, void *))&_no_sys_func, + ._open_r = (int (*)(struct _reent *r, const char *, int, int))&_no_sys_func, + ._close_r = (int (*)(struct _reent *r, int))&_no_sys_func, + ._lseek_r = (int (*)(struct _reent *r, int, int, int))&_no_sys_func, + ._fstat_r = (int (*)(struct _reent *r, int, struct stat *))&_no_sys_func, + ._stat_r = (int (*)(struct _reent *r, const char*, struct stat *))&_no_sys_func, + ._write_r = (int (*)(struct _reent *r, int, const void *, int))&_no_sys_func, + ._read_r = (int (*)(struct _reent *r, int, void *, int))&_no_sys_func, + ._unlink_r = (int (*)(struct _reent *r, const char*))&_no_sys_func, + #endif /* MODULE_NEWLIB_SYSCALLS_DEFAULT */ + ._link_r = (int (*)(struct _reent *r, const char*, const char*))&_no_sys_func, + ._rename_r = (int (*)(struct _reent *r, const char*, const char*))&_no_sys_func, + + ._lock_init = &_lock_init, + ._lock_init_recursive = &_lock_init_recursive, + ._lock_close = &_lock_close, + ._lock_close_recursive = &_lock_close_recursive, + ._lock_acquire = &_lock_acquire, + ._lock_acquire_recursive = &_lock_acquire_recursive, + ._lock_try_acquire = &_lock_try_acquire, + ._lock_try_acquire_recursive = &_lock_try_acquire_recursive, + ._lock_release = &_lock_release, + ._lock_release_recursive = &_lock_release_recursive, + #ifdef CONFIG_NEWLIB_NANO_FORMAT + ._printf_float = &_printf_float, + ._scanf_float = &_scanf_float, + #else /* CONFIG_NEWLIB_NANO_FORMAT */ + ._printf_float = NULL, + ._scanf_float = NULL, + #endif /* CONFIG_NEWLIB_NANO_FORMAT */ +}; + +void IRAM syscalls_init (void) +{ + /* enable the system timer in us (TMG0 is enabled by default) */ + TIMER_SYSTEM.config.divider = rtc_clk_apb_freq_get()/MHZ; + TIMER_SYSTEM.config.autoreload = 0; + TIMER_SYSTEM.config.enable = 1; + syscall_table_ptr_pro = &s_stub_table; + syscall_table_ptr_app = &s_stub_table; + + _GLOBAL_REENT = &s_reent; + + environ = malloc(sizeof(char*)); + environ[0] = NULL; +} + +uint32_t system_get_time (void) +{ + /* latch 64 bit timer value before read */ + TIMER_SYSTEM.update = 0; + /* wait until instructions have been finished */ + __asm__ volatile ("isync"); + return TIMER_SYSTEM.cnt_low; +} + +uint64_t system_get_time_64 (void) +{ + uint64_t ret; + uint32_t* tmp = (uint32_t*)&ret; + /* latch 64 bit timer value before read */ + TIMER_SYSTEM.update = 0; + /* wait until instructions have been finished */ + __asm__ volatile ("isync"); + /* read the current timer value */ + tmp[0] = TIMER_SYSTEM.cnt_high; + tmp[1] = TIMER_SYSTEM.cnt_low; + return ret; +} + +/* alias for compatibility with espressif/wifi_libs */ +int64_t esp_timer_get_time(void) __attribute__((alias("system_get_time_64"))); + +static IRAM void system_wdt_int_handler(void *arg) +{ + TIMERG0.int_clr_timers.wdt=1; /* clear interrupt */ + system_wdt_feed(); +} + +void IRAM system_wdt_feed (void) +{ + DEBUG("%s\n", __func__); + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */ + TIMERG0.wdt_feed=1; /* reset MWDT */ + TIMERG0.wdt_wprotect=0; /* enable write protection */ +} + +void system_wdt_init (void) +{ + /* disable boot watchdogs */ + TIMERG0.wdt_config0.flashboot_mod_en = 0; + RTCCNTL.wdt_config0.flashboot_mod_en = 0; + + /* enable system watchdog */ + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */ + TIMERG0.wdt_config0.stg0 = TIMG_WDT_STG_SEL_INT; /* stage0 timeout: interrupt */ + TIMERG0.wdt_config0.stg1 = TIMG_WDT_STG_SEL_RESET_SYSTEM; /* stage1 timeout: sys reset */ + TIMERG0.wdt_config0.sys_reset_length = 7; /* sys reset signal lenght: 3.2 us */ + TIMERG0.wdt_config0.cpu_reset_length = 7; /* sys reset signal lenght: 3.2 us */ + TIMERG0.wdt_config0.edge_int_en = 0; + TIMERG0.wdt_config0.level_int_en = 1; + + /* MWDT clock = 80 * 12,5 ns = 1 us */ + TIMERG0.wdt_config1.clk_prescale = 80; + + /* define stage timeouts */ + TIMERG0.wdt_config2 = 2 * US_PER_SEC; /* stage 0: 2 s (interrupt) */ + TIMERG0.wdt_config3 = 4 * US_PER_SEC; /* stage 1: 4 s (sys reset) */ + + TIMERG0.wdt_config0.en = 1; /* enable MWDT */ + TIMERG0.wdt_feed = 1; /* reset MWDT */ + TIMERG0.wdt_wprotect = 0; /* enable write protection */ + + DEBUG("%s TIMERG0 wdt_config0=%08lx wdt_config1=%08lx wdt_config2=%08lx\n", + __func__, TIMERG0.wdt_config0, TIMERG0.wdt_config1, TIMERG0.wdt_config2); + + /* route WDT peripheral interrupt source to CPU_INUM_WDT */ + intr_matrix_set(PRO_CPU_NUM, ETS_TG0_WDT_LEVEL_INTR_SOURCE, CPU_INUM_WDT); + /* set the interrupt handler and activate the interrupt */ + xt_set_interrupt_handler(CPU_INUM_WDT, system_wdt_int_handler, NULL); + xt_ints_on(BIT(CPU_INUM_WDT)); +} + +void system_wdt_stop (void) +{ + xt_ints_off(BIT(CPU_INUM_WDT)); + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */ + TIMERG0.wdt_config0.en = 0; /* disable MWDT */ + TIMERG0.wdt_feed = 1; /* reset MWDT */ + TIMERG0.wdt_wprotect = 0; /* enable write protection */ +} + +void system_wdt_start (void) +{ + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; /* disable write protection */ + TIMERG0.wdt_config0.en = 1; /* disable MWDT */ + TIMERG0.wdt_feed = 1; /* reset MWDT */ + TIMERG0.wdt_wprotect = 0; /* enable write protection */ + xt_ints_on(BIT(CPU_INUM_WDT)); +} diff --git a/cpu/esp32/thread_arch.c b/cpu/esp32/thread_arch.c new file mode 100644 index 0000000000000..7260018f7fb5d --- /dev/null +++ b/cpu/esp32/thread_arch.c @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief Implementation of the kernel's architecture dependent thread interface + * + * @author Gunar Schorcht + * + * @} + */ + +/* + * PLEASE NOTE: Some parts of the code are taken from the FreeRTOS port for + * Xtensa processors from Cadence Design Systems. These parts are marked + * accordingly. For these parts, the following license is valid: + * + * Copyright (c) 2003-2015 Cadence Design Systems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#include +#include + +#include "board.h" +#include "cpu.h" +#include "irq.h" +#include "log.h" +#include "thread.h" +#include "sched.h" + +#include "common.h" +#include "esp/xtensa_ops.h" +#include "rom/ets_sys.h" +#include "syscalls.h" +#include "tools.h" +#include "xtensa/xtensa_context.h" + +/* User exception dispatcher when exiting */ +extern void _xt_user_exit(void); + +/* Switch context to the highest priority ready task without context save */ +extern void _frxt_dispatch(void); + +/* Set an flag indicating that a task switch is required on return from interrupt */ +extern void _frxt_setup_switch(void); + +/* Switch context to the highest priority ready task with context save */ +extern void vPortYield(void); +extern void vPortYieldFromInt(void); + +char* thread_stack_init(thread_task_func_t task_func, void *arg, void *stack_start, int stack_size) +{ + /* Stack layout after task stack initialization + * + * +------------------------+ + * | | TOP + * | thread_control_block | + * stack_start + stack_size ==> | | top_of_stack+1 + * +------------------------+ + * top_of_stack ==> | | + * | XT_CP_SA | + * | (optional) | + * | ... | ... + * | cpstored | XT_CPSTORED + * top_of_stack + 1 - XT_CP_SIZE ==> | cpenable | XT_CPENABLE + * (cp_state) +------------------------+ + * | | + * | XT_STK_FRAME | + * | | XT_STK_... + * | a2 = arg | XT_STK_A2 + * | a1 = sp + XT_STK_FRMSZ | XT_STK_A1 + * | a0 = sched_task_exit | XT_STK_A0 + * | ps = PS_UM | PS_EXCM | XT_STK_PS + * | pc = task_func | XT_STK_PC + * sp = top_of_stack + 1 - XT_CP_SIZE ==> | exit = _xt_user_exit | XT_STK_EXIT + * - XT_STK_FRMSZ +------------------------+ + * | | + * | remaining stack space | + * | available for data | + * stack_start (preallocated var) ==> | | BOTTOM + * +------------------------+ + * + * Initialized stack frame represents the registers as set when the + * the task function would have been called. + * + * Registers in a called function + * + * pc - PC at the beginning in the function + * a0 - return address from the function (return address to caller) + * a1 - current stack pointer at the beginning in the function + * a2 - first argument of the function + */ + + /* stack is [stack_start+0 ... stack_start+stack_size-1] */ + uint8_t *top_of_stack; + uint8_t *sp; + + top_of_stack = (uint8_t*)((uint32_t)stack_start + stack_size-1); + + /* BEGIN - code from FreeRTOS port for Xtensa from Cadence */ + + /* Create interrupt stack frame aligned to 16 byte boundary */ + sp = (uint8_t*)(((uint32_t)(top_of_stack+1) - XT_STK_FRMSZ - XT_CP_SIZE) & ~0xf); + + /* Clear whole stack with a known value to assist debugging */ + #if !defined(DEVELHELP) && !defined(SCHED_TEST_STACK) + /* Unfortunatly, this affects thread_measure_stack_free function */ + memset(stack_start, 0, stack_size); + #else + memset(sp, 0, XT_STK_FRMSZ + XT_CP_SIZE); + #endif + + /* ensure that stack is big enough */ + assert (sp > (uint8_t*)stack_start); + + XtExcFrame* exc_frame = (XtExcFrame*)sp; + + /* Explicitly initialize certain saved registers for call0 ABI */ + exc_frame->pc = (uint32_t)task_func; /* task entry point */ + exc_frame->a0 = (uint32_t)sched_task_exit; /* task exit point*/ + exc_frame->a1 = (uint32_t)sp + XT_STK_FRMSZ; /* physical top of stack frame */ + exc_frame->exit = (uint32_t)_xt_user_exit; /* user exception exit dispatcher */ + + /* Set initial PS to int level 0, EXCM disabled ('rfe' will enable), user mode. */ + /* Also set entry point argument parameter. */ + #ifdef __XTENSA_CALL0_ABI__ + /* for CALL0 ABI set in parameter a2 to task argument */ + exc_frame->ps = PS_UM | PS_EXCM; + exc_frame->a2 = (uint32_t)arg; /* parameters for task_func */ + #else + /* for Windowed Register ABI set PS.CALLINC=01 to handle task entry as + call4 return address in a4 and parameter in a6 and */ + exc_frame->ps = PS_UM | PS_EXCM | PS_WOE | PS_CALLINC(1); + exc_frame->a4 = (uint32_t)sched_task_exit; /* task exit point*/ + exc_frame->a6 = (uint32_t)arg; /* parameters for task_func */ + #endif + + #ifdef XT_USE_SWPRI + /* Set the initial virtual priority mask value to all 1's. */ + exc_frame->vpri = 0xFFFFFFFF; + #endif + + #if XCHAL_CP_NUM > 0 + /* Init the coprocessor save area (see xtensa_context.h) */ + /* No access to TCB here, so derive indirectly. Stack growth is top to bottom. */ + /* p = (uint32_t *) xMPUSettings->coproc_area; */ + + uint32_t *p; + + p = (uint32_t *)(((uint32_t) top_of_stack+1 - XT_CP_SIZE)); + p[0] = 0; + p[1] = 0; + p[2] = (((uint32_t) p) + 12 + XCHAL_TOTAL_SA_ALIGN - 1) & -XCHAL_TOTAL_SA_ALIGN; + #endif + + /* END - code from FreeRTOS port for Xtensa from Cadence */ + DEBUG("%s start=%p size=%ld top=%p sp=%p free=%lu\n", + __func__, stack_start, stack_size, top_of_stack, sp, sp-(uint8_t*)stack_start); + + return (char*)sp; +} + + +unsigned sched_interrupt_nesting = 0; /* Interrupt nesting level */ + +void thread_yield_higher(void) +{ + /* reset hardware watchdog */ + system_wdt_feed(); + + /* yield next task */ + #if defined(ENABLE_DEBUG) && defined(DEVELHELP) + if (sched_active_thread) { + DEBUG("%lu old task %lu %s %lu\n", system_get_time(), + sched_active_thread->pid, sched_active_thread->name, + sched_active_thread->sp - sched_active_thread-> stack_start); + } + #endif + + if (!irq_is_in()) { + vPortYield(); + } + else { + _frxt_setup_switch(); + } + + #if defined(ENABLE_DEBUG) && defined(DEVELHELP) + if (sched_active_thread) { + DEBUG("%lu new task %lu %s %lu\n", system_get_time(), + sched_active_thread->pid, sched_active_thread->name, + sched_active_thread->sp - sched_active_thread-> stack_start); + } + #endif + + return; +} + +void thread_stack_print(void) +{ + /* Print the current stack to stdout. */ + + #if defined(DEVELHELP) + volatile thread_t* task = thread_get(sched_active_pid); + if (task) { + + char* stack_top = task->stack_start + task->stack_size; + int size = stack_top - task->sp; + printf("Printing current stack of thread %" PRIkernel_pid "\n", thread_getpid()); + hexdump((void*)(task->sp), size >> 2, 'w', 8); + } + #else + NOT_SUPPORTED(); + #endif +} + +void thread_print_stack(void) +{ + /* Prints human readable, ps-like thread information for debugging purposes. */ + /* because of Xtensa stack structure and call ABI, it is not possible to implement */ + NOT_YET_IMPLEMENTED(); + return; +} + +#ifdef DEVELHELP + +extern uint8_t port_IntStack; +extern uint8_t port_IntStackTop; + +void thread_isr_stack_init(void) +{ + /* code from thread.c, please see the copyright notice there */ + + /* assign each int of the stack the value of it's address */ + uintptr_t *stackmax = (uintptr_t *)&port_IntStackTop; + uintptr_t *stackp = (uintptr_t *)&port_IntStack; + + while (stackp < stackmax) { + *stackp = (uintptr_t) stackp; + stackp++; + } +} + +int thread_isr_stack_usage(void) +{ + return &port_IntStackTop - &port_IntStack - + thread_measure_stack_free((char*)&port_IntStack); +} + +void *thread_isr_stack_pointer(void) +{ + /* Get the current ISR stack pointer. */ + return &port_IntStackTop; +} + +void *thread_isr_stack_start(void) +{ + /* Get the start of the ISR stack. */ + return &port_IntStack; +} + +void thread_isr_stack_print(void) +{ + printf("Printing current ISR\n"); + hexdump(&port_IntStack, &port_IntStackTop-&port_IntStack, 'w', 8); +} + +#else /* DEVELHELP */ + +void thread_isr_stack_init(void) {} + +#endif /* DEVELHELP */ + +NORETURN void cpu_switch_context_exit(void) +{ + /* Switch context to the highest priority ready task without context save */ + __asm__ volatile ("call0 _frxt_dispatch"); + + UNREACHABLE(); +} diff --git a/cpu/esp32/tools.c b/cpu/esp32/tools.c new file mode 100644 index 0000000000000..d038a50c0becf --- /dev/null +++ b/cpu/esp32/tools.c @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_esp32 + * @{ + * + * @file + * @brief Implementation of some tools + * + * @author Gunar Schorcht + * + * @} + */ + +#include +#include "esp/common_macros.h" +#include "rom/ets_sys.h" +#include "tools.h" + +void hexdump (const void* addr, uint32_t num, char width, uint8_t per_line) +{ + uint32_t count = 0; + uint32_t size; + + uint8_t* addr8 = (uint8_t*) addr; + uint16_t* addr16 = (uint16_t*)addr; + uint32_t* addr32 = (uint32_t*)addr; + uint64_t* addr64 = (uint64_t*)addr; + + switch (width) { + case 'b': size = 1; break; + case 'h': size = 2; break; + case 'w': size = 4; break; + case 'g': size = 8; break; + default : size = 1; break; + } + + while (count < num) { + if (count % per_line == 0) { + ets_printf ("%08lx: ", (uint32_t)((uint8_t*)addr+count*size)); + } + switch (width) { + case 'b': ets_printf("%02x ", addr8[count++]); break; + case 'h': ets_printf("%04x ", addr16[count++]); break; + case 'w': ets_printf("%08x ", addr32[count++]); break; + case 'g': ets_printf("%016x ",addr64[count++]); break; + default : ets_printf("."); count++; break; + } + if (count % per_line == 0) { + ets_printf ("\n"); + } + } + ets_printf ("\n"); +} diff --git a/cpu/esp32/xtensa/Makefile b/cpu/esp32/xtensa/Makefile new file mode 100644 index 0000000000000..350d5aaf0c833 --- /dev/null +++ b/cpu/esp32/xtensa/Makefile @@ -0,0 +1,3 @@ +MODULE=xtensa + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/esp32/xtensa/README.md b/cpu/esp32/xtensa/README.md new file mode 100644 index 0000000000000..6cce4e5f16ffb --- /dev/null +++ b/cpu/esp32/xtensa/README.md @@ -0,0 +1,30 @@ +All files in this directory are from the [FreeRTOS port for Xtensa](https://github.com/tensilica/freertos) configurable processors and Diamond processors. All of these files are copyright of Cadence Design Systems Inc., see below. + +Some of the files are slightly modified for the RIOT OS port. + +Please note the following copyright notices for all these files: + +``` +/******************************************************************************* +Copyright (c) 2006-2015 Cadence Design Systems Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ +``` diff --git a/cpu/esp32/xtensa/portasm.S b/cpu/esp32/xtensa/portasm.S new file mode 100644 index 0000000000000..db0da672f1520 --- /dev/null +++ b/cpu/esp32/xtensa/portasm.S @@ -0,0 +1,636 @@ +/* +//----------------------------------------------------------------------------- +// Copyright (c) 2003-2015 Cadence Design Systems, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +//----------------------------------------------------------------------------- +*/ + +#ifdef RIOT_OS +#include "xtensa_conf.h" + +#define pxCurrentTCB sched_active_thread +#define port_xSchedulerRunning sched_num_threads +#define port_switch_flag sched_context_switch_request +#define port_interruptNesting irq_interrupt_nesting +#define vTaskSwitchContext sched_run +#define configISR_STACK_SIZE ISR_STACKSIZE + +.extern sched_active_thread +.extern sched_num_threads +.extern sched_context_switch_request +.extern irq_interrupt_nesting +#endif + +#include "xtensa_context.h" + +#define TOPOFSTACK_OFFS 0x00 /* StackType_t *pxTopOfStack */ +#define CP_TOPOFSTACK_OFFS 0x04 /* xMPU_SETTINGS.coproc_area */ + +/* +******************************************************************************* +* Interrupt stack. The size of the interrupt stack is determined by the config +* parameter "configISR_STACK_SIZE" in FreeRTOSConfig.h +******************************************************************************* +*/ + .data + .align 16 + .global port_IntStack +port_IntStack: + .space configISR_STACK_SIZE + .global port_IntStackTop +port_IntStackTop: + .word 0 + +#ifndef RIOT_OS +port_switch_flag: + .word 0 +#endif + + .text + .literal_position + +/* +******************************************************************************* +* _frxt_setup_switch +* void _frxt_setup_switch(void); +* +* Sets an internal flag indicating that a task switch is required on return +* from interrupt handling. +* +******************************************************************************* +*/ + .global _frxt_setup_switch + .type _frxt_setup_switch,@function + .align 4 +_frxt_setup_switch: + + ENTRY(16) + + movi a2, port_switch_flag + movi a3, 1 + s32i a3, a2, 0 + + RET(16) + +/* +******************************************************************************* +* _frxt_int_enter +* void _frxt_int_enter(void) +* +* Implements the Xtensa RTOS porting layer's XT_RTOS_INT_ENTER function for +* freeRTOS. Saves the rest of the interrupt context (not already saved). +* May only be called from assembly code by the 'call0' instruction, with +* interrupts disabled. +* See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h. +* +******************************************************************************* +*/ + .globl _frxt_int_enter + .type _frxt_int_enter,@function + .align 4 +_frxt_int_enter: + + /* Save a12-13 in the stack frame as required by _xt_context_save. */ + s32i a12, a1, XT_STK_A12 + s32i a13, a1, XT_STK_A13 + + /* Save return address in a safe place (free a0). */ + mov a12, a0 + + /* Save the rest of the interrupted context (preserves A12-13). */ + call0 _xt_context_save + + /* + Save interrupted task's SP in TCB only if not nesting. + Manage nesting directly rather than call the generic IntEnter() + (in windowed ABI we can't call a C function here anyway because PS.EXCM is still set). + */ + + movi a2, port_xSchedulerRunning + movi a3, port_interruptNesting + l32i a2, a2, 0 /* a2 = port_xSchedulerRunning */ + beqz a2, 1f /* scheduler not running, no tasks */ + l32i a2, a3, 0 /* a2 = port_interruptNesting */ + addi a2, a2, 1 /* increment nesting count */ + s32i a2, a3, 0 /* save nesting count */ + bnei a2, 1, .Lnested /* !=0 before incr, so nested */ + + movi a2, pxCurrentTCB + l32i a2, a2, 0 /* a2 = current TCB */ + beqz a2, 1f + s32i a1, a2, TOPOFSTACK_OFFS /* pxCurrentTCB->pxTopOfStack = SP */ + movi a1, port_IntStackTop /* a1 = top of intr stack */ + +.Lnested: +1: + mov a0, a12 /* restore return addr and return */ + ret + +/* +******************************************************************************* +* _frxt_int_exit +* void _frxt_int_exit(void) +* +* Implements the Xtensa RTOS porting layer's XT_RTOS_INT_EXIT function for +* FreeRTOS. If required, calls vPortYieldFromInt() to perform task context +* switching, restore the (possibly) new task's context, and return to the +* exit dispatcher saved in the task's stack frame at XT_STK_EXIT. +* May only be called from assembly code by the 'call0' instruction. Does not +* return to caller. +* See the description of the XT_RTOS_ENTER macro in xtensa_rtos.h. +* +******************************************************************************* +*/ + .globl _frxt_int_exit + .type _frxt_int_exit,@function + .align 4 +_frxt_int_exit: + + movi a2, port_xSchedulerRunning + movi a3, port_interruptNesting + rsil a0, XCHAL_EXCM_LEVEL /* lock out interrupts */ + l32i a2, a2, 0 /* a2 = port_xSchedulerRunning */ + beqz a2, .Lnoswitch /* scheduler not running, no tasks */ + l32i a2, a3, 0 /* a2 = port_interruptNesting */ + addi a2, a2, -1 /* decrement nesting count */ + s32i a2, a3, 0 /* save nesting count */ + bnez a2, .Lnesting /* !=0 after decr so still nested */ + + movi a2, pxCurrentTCB + l32i a2, a2, 0 /* a2 = current TCB */ + beqz a2, 1f /* no task ? go to dispatcher */ + l32i a1, a2, TOPOFSTACK_OFFS /* SP = pxCurrentTCB->pxTopOfStack */ + + movi a2, port_switch_flag /* address of switch flag */ + l32i a3, a2, 0 /* a3 = port_switch_flag */ + beqz a3, .Lnoswitch /* flag = 0 means no switch reqd */ + movi a3, 0 + s32i a3, a2, 0 /* zero out the flag for next time */ + +1: + /* + Call0 ABI callee-saved regs a12-15 need to be saved before possible preemption. + However a12-13 were already saved by _frxt_int_enter(). + */ + #ifdef __XTENSA_CALL0_ABI__ + s32i a14, a1, XT_STK_A14 + s32i a15, a1, XT_STK_A15 + #endif + + #ifdef __XTENSA_CALL0_ABI__ + call0 vPortYieldFromInt /* call dispatch inside the function; never returns */ + #else + call4 vPortYieldFromInt /* this one returns */ + call0 _frxt_dispatch /* tail-call dispatcher */ + /* Never returns here. */ + #endif + +.Lnoswitch: + /* + If we came here then about to resume the interrupted task. + */ + +.Lnesting: + /* + We come here only if there was no context switch, that is if this + is a nested interrupt, or the interrupted task was not preempted. + In either case there's no need to load the SP. + */ + + /* Restore full context from interrupt stack frame */ + call0 _xt_context_restore + + /* + Must return via the exit dispatcher corresponding to the entrypoint from which + this was called. Interruptee's A0, A1, PS, PC are restored and the interrupt + stack frame is deallocated in the exit dispatcher. + */ + l32i a0, a1, XT_STK_EXIT + ret + +/* +********************************************************************************************************** +* _frxt_timer_int +* void _frxt_timer_int(void) +* +* Implements the Xtensa RTOS porting layer's XT_RTOS_TIMER_INT function for FreeRTOS. +* Called every timer interrupt. +* Manages the tick timer and calls xPortSysTickHandler() every tick. +* See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h. +* +* Callable from C (obeys ABI conventions). Implemented in assmebly code for performance. +* +********************************************************************************************************** +*/ + .globl _frxt_timer_int + .type _frxt_timer_int,@function + .align 4 +_frxt_timer_int: + + /* + Xtensa timers work by comparing a cycle counter with a preset value. Once the match occurs + an interrupt is generated, and the handler has to set a new cycle count into the comparator. + To avoid clock drift due to interrupt latency, the new cycle count is computed from the old, + not the time the interrupt was serviced. However if a timer interrupt is ever serviced more + than one tick late, it is necessary to process multiple ticks until the new cycle count is + in the future, otherwise the next timer interrupt would not occur until after the cycle + counter had wrapped (2^32 cycles later). + + do { + ticks++; + old_ccompare = read_ccompare_i(); + write_ccompare_i( old_ccompare + divisor ); + service one tick; + diff = read_ccount() - old_ccompare; + } while ( diff > divisor ); + */ + + ENTRY(16) + /* In RIOT-OS the timer is used in a different way and is realized in C functions. */ + #if 0 + +.L_xt_timer_int_catchup: + + /* Update the timer comparator for the next tick. */ + #ifdef XT_CLOCK_FREQ + movi a2, XT_TICK_DIVISOR /* a2 = comparator increment */ + #else + movi a3, _xt_tick_divisor + l32i a2, a3, 0 /* a2 = comparator increment */ + #endif + rsr a3, XT_CCOMPARE /* a3 = old comparator value */ + add a4, a3, a2 /* a4 = new comparator value */ + wsr a4, XT_CCOMPARE /* update comp. and clear interrupt */ + esync + + #ifdef __XTENSA_CALL0_ABI__ + /* Preserve a2 and a3 across C calls. */ + s32i a2, sp, 4 + s32i a3, sp, 8 + #endif + + /* Call the FreeRTOS tick handler (see port.c). */ + #ifdef __XTENSA_CALL0_ABI__ + call0 xPortSysTickHandler + #else + call4 xPortSysTickHandler + #endif + + #ifdef __XTENSA_CALL0_ABI__ + /* Restore a2 and a3. */ + l32i a2, sp, 4 + l32i a3, sp, 8 + #endif + + /* Check if we need to process more ticks to catch up. */ + esync /* ensure comparator update complete */ + rsr a4, CCOUNT /* a4 = cycle count */ + sub a4, a4, a3 /* diff = ccount - old comparator */ + blt a2, a4, .L_xt_timer_int_catchup /* repeat while diff > divisor */ + + #endif + RET(16) + + /* +********************************************************************************************************** +* _frxt_tick_timer_init +* void _frxt_tick_timer_init(void) +* +* Initialize timer and timer interrrupt handler (_xt_tick_divisor_init() has already been been called). +* Callable from C (obeys ABI conventions on entry). +* +********************************************************************************************************** +*/ +#if 0 + .globl _frxt_tick_timer_init + .type _frxt_tick_timer_init,@function + .align 4 +_frxt_tick_timer_init: + + ENTRY(16) + + /* Set up the periodic tick timer (assume enough time to complete init). */ + #ifdef XT_CLOCK_FREQ + movi a3, XT_TICK_DIVISOR + #else + movi a2, _xt_tick_divisor + l32i a3, a2, 0 + #endif + rsr a2, CCOUNT /* current cycle count */ + add a2, a2, a3 /* time of first timer interrupt */ + wsr a2, XT_CCOMPARE /* set the comparator */ + + /* + Enable the timer interrupt at the device level. Don't write directly + to the INTENABLE register because it may be virtualized. + */ + #ifdef __XTENSA_CALL0_ABI__ + movi a2, XT_TIMER_INTEN + call0 xt_ints_on + #else + movi a6, XT_TIMER_INTEN + call4 xt_ints_on + #endif + + RET(16) +#endif + +/* +********************************************************************************************************** +* DISPATCH THE HIGH READY TASK +* void _frxt_dispatch(void) +* +* Switch context to the highest priority ready task, restore its state and dispatch control to it. +* +* This is a common dispatcher that acts as a shared exit path for all the context switch functions +* including vPortYield() and vPortYieldFromInt(), all of which tail-call this dispatcher +* (for windowed ABI vPortYieldFromInt() calls it indirectly via _frxt_int_exit() ). +* +* The Xtensa port uses different stack frames for solicited and unsolicited task suspension (see +* comments on stack frames in xtensa_context.h). This function restores the state accordingly. +* If restoring a task that solicited entry, restores the minimal state and leaves CPENABLE clear. +* If restoring a task that was preempted, restores all state including the task's CPENABLE. +* +* Entry: +* pxCurrentTCB points to the TCB of the task to suspend, +* Because it is tail-called without a true function entrypoint, it needs no 'entry' instruction. +* +* Exit: +* If incoming task called vPortYield() (solicited), this function returns as if from vPortYield(). +* If incoming task was preempted by an interrupt, this function jumps to exit dispatcher. +* +********************************************************************************************************** +*/ + .globl _frxt_dispatch + .type _frxt_dispatch,@function + .align 4 +_frxt_dispatch: + + #ifdef __XTENSA_CALL0_ABI__ + call0 vTaskSwitchContext // Get next TCB to resume + movi a2, pxCurrentTCB + #else + movi a2, pxCurrentTCB + call4 vTaskSwitchContext // Get next TCB to resume + #endif + l32i a3, a2, 0 + l32i sp, a3, TOPOFSTACK_OFFS /* SP = next_TCB->pxTopOfStack; */ + s32i a3, a2, 0 + + /* Determine the type of stack frame. */ + l32i a2, sp, XT_STK_EXIT /* exit dispatcher or solicited flag */ + bnez a2, .L_frxt_dispatch_stk + +.L_frxt_dispatch_sol: + + /* Solicited stack frame. Restore minimal context and return from vPortYield(). */ + l32i a3, sp, XT_SOL_PS + #ifdef __XTENSA_CALL0_ABI__ + l32i a12, sp, XT_SOL_A12 + l32i a13, sp, XT_SOL_A13 + l32i a14, sp, XT_SOL_A14 + l32i a15, sp, XT_SOL_A15 + #endif + l32i a0, sp, XT_SOL_PC + #if XCHAL_CP_NUM > 0 + /* Ensure wsr.CPENABLE is complete (should be, it was cleared on entry). */ + rsync + #endif + /* As soons as PS is restored, interrupts can happen. No need to sync PS. */ + wsr a3, PS + #ifdef __XTENSA_CALL0_ABI__ + addi sp, sp, XT_SOL_FRMSZ + ret + #else + retw + #endif + +.L_frxt_dispatch_stk: + + #if XCHAL_CP_NUM > 0 + /* Restore CPENABLE from task's co-processor save area. */ + movi a3, pxCurrentTCB + l32i a3, a3, 0 /* a3 = pxCurrentTCB */ + #if 0 /* TODO when architecture dependent thread data are possible */ + l32i a2, a3, CP_TOPOFSTACK_OFFS /* StackType_t *pxStack; */ + #else + addi a2, a3, -XT_CP_SIZE /* a2 = cp_state */ + #endif + l16ui a3, a2, XT_CPENABLE /* CPENABLE = cpenable; */ + wsr a3, CPENABLE + #endif + + /* Interrupt stack frame. Restore full context and return to exit dispatcher. */ + call0 _xt_context_restore + + /* In Call0 ABI, restore callee-saved regs (A12, A13 already restored). */ + #ifdef __XTENSA_CALL0_ABI__ + l32i a14, sp, XT_STK_A14 + l32i a15, sp, XT_STK_A15 + #endif + + #if XCHAL_CP_NUM > 0 + /* Ensure wsr.CPENABLE has completed. */ + rsync + #endif + + /* + Must return via the exit dispatcher corresponding to the entrypoint from which + this was called. Interruptee's A0, A1, PS, PC are restored and the interrupt + stack frame is deallocated in the exit dispatcher. + */ + l32i a0, sp, XT_STK_EXIT + ret + + +/* +********************************************************************************************************** +* PERFORM A SOLICTED CONTEXT SWITCH (from a task) +* void vPortYield(void) +* +* This function saves the minimal state needed for a solicited task suspension, clears CPENABLE, +* then tail-calls the dispatcher _frxt_dispatch() to perform the actual context switch +* +* At Entry: +* pxCurrentTCB points to the TCB of the task to suspend +* Callable from C (obeys ABI conventions on entry). +* +* Does not return to caller. +* +********************************************************************************************************** +*/ + .globl vPortYield + .type vPortYield,@function + .align 4 +vPortYield: + + #ifdef __XTENSA_CALL0_ABI__ + addi sp, sp, -XT_SOL_FRMSZ + #else + entry sp, XT_SOL_FRMSZ + #endif + + rsr a2, PS + s32i a0, sp, XT_SOL_PC + s32i a2, sp, XT_SOL_PS + #ifdef __XTENSA_CALL0_ABI__ + s32i a12, sp, XT_SOL_A12 /* save callee-saved registers */ + s32i a13, sp, XT_SOL_A13 + s32i a14, sp, XT_SOL_A14 + s32i a15, sp, XT_SOL_A15 + #else + /* Spill register windows. Calling xthal_window_spill() causes extra */ + /* spills and reloads, so we will set things up to call the _nw version */ + /* instead to save cycles. */ + movi a6, ~(PS_WOE_MASK|PS_INTLEVEL_MASK) /* spills a4-a7 if needed */ + and a2, a2, a6 /* clear WOE, INTLEVEL */ + addi a2, a2, XCHAL_EXCM_LEVEL /* set INTLEVEL */ + wsr a2, PS + rsync + call0 xthal_window_spill_nw + l32i a2, sp, XT_SOL_PS /* restore PS */ + wsr a2, PS + #endif + + rsil a2, XCHAL_EXCM_LEVEL /* disable low/med interrupts */ + + #if XCHAL_CP_NUM > 0 + /* Save coprocessor callee-saved state (if any). At this point CPENABLE */ + /* should still reflect which CPs were in use (enabled). */ + call0 _xt_coproc_savecs + #endif + + movi a2, pxCurrentTCB + movi a3, 0 + l32i a2, a2, 0 /* a2 = pxCurrentTCB */ + s32i a3, sp, XT_SOL_EXIT /* 0 to flag as solicited frame */ + s32i sp, a2, TOPOFSTACK_OFFS /* pxCurrentTCB->pxTopOfStack = SP */ + + #if XCHAL_CP_NUM > 0 + /* Clear CPENABLE, also in task's co-processor state save area. */ + #if 0 /* TODO when architecture dependent thread data are possible */ + l32i a2, a2, CP_TOPOFSTACK_OFFS /* a2 = pxCurrentTCB->cp_state */ + #else + addi a2, a2, -XT_CP_SIZE /* a2 = cp_state */ + #endif + movi a3, 0 + wsr a3, CPENABLE + beqz a2, 1f + s16i a3, a2, XT_CPENABLE /* clear saved cpenable */ +1: + #endif + + /* Tail-call dispatcher. */ + call0 _frxt_dispatch + /* Never reaches here. */ + + +/* +********************************************************************************************************** +* PERFORM AN UNSOLICITED CONTEXT SWITCH (from an interrupt) +* void vPortYieldFromInt(void) +* +* This calls the context switch hook (removed), saves and clears CPENABLE, then tail-calls the dispatcher +* _frxt_dispatch() to perform the actual context switch. +* +* At Entry: +* Interrupted task context has been saved in an interrupt stack frame at pxCurrentTCB->pxTopOfStack. +* pxCurrentTCB points to the TCB of the task to suspend, +* Callable from C (obeys ABI conventions on entry). +* +* At Exit: +* Windowed ABI defers the actual context switch until the stack is unwound to interrupt entry. +* Call0 ABI tail-calls the dispatcher directly (no need to unwind) so does not return to caller. +* +********************************************************************************************************** +*/ + .globl vPortYieldFromInt + .type vPortYieldFromInt,@function + .align 4 +vPortYieldFromInt: + + ENTRY(16) + + #if XCHAL_CP_NUM > 0 + /* Save CPENABLE in task's co-processor save area, and clear CPENABLE. */ + movi a3, pxCurrentTCB + l32i a3, a3, 0 /* a3 = pxCurrentTCB */ + #if 0 /* TODO when architecture dependent thread data are possible */ + l32i a2, a3, CP_TOPOFSTACK_OFFS + #else + addi a2, a3, -XT_CP_SIZE /* a2 = cp_state */ + #endif + + rsr a3, CPENABLE + s16i a3, a2, XT_CPENABLE /* cp_state->cpenable = CPENABLE; */ + movi a3, 0 + wsr a3, CPENABLE /* disable all co-processors */ + #endif + + #ifdef __XTENSA_CALL0_ABI__ + /* Tail-call dispatcher. */ + call0 _frxt_dispatch + /* Never reaches here. */ + #else + RET(16) + #endif + +/* +********************************************************************************************************** +* _frxt_task_coproc_state +* void _frxt_task_coproc_state(void) +* +* Implements the Xtensa RTOS porting layer's XT_RTOS_CP_STATE function for FreeRTOS. +* +* May only be called when a task is running, not within an interrupt handler (returns 0 in that case). +* May only be called from assembly code by the 'call0' instruction. Does NOT obey ABI conventions. +* Returns in A15 a pointer to the base of the co-processor state save area for the current task. +* See the detailed description of the XT_RTOS_ENTER macro in xtensa_rtos.h. +* +********************************************************************************************************** +*/ +#if XCHAL_CP_NUM > 0 + + .globl _frxt_task_coproc_state + .type _frxt_task_coproc_state,@function + .align 4 +_frxt_task_coproc_state: + + movi a15, port_xSchedulerRunning /* if (port_xSchedulerRunning */ + l32i a15, a15, 0 + beqz a15, 1f + movi a15, port_interruptNesting /* && port_interruptNesting == 0 */ + l32i a15, a15, 0 + bnez a15, 1f + movi a15, pxCurrentTCB + l32i a15, a15, 0 /* && pxCurrentTCB != 0) { */ + beqz a15, 2f + #if 0 /* TODO when architecture dependent thread data are possible */ + l32i a15, a15, CP_TOPOFSTACK_OFFS + #else + addi a15, a15, -XT_CP_SIZE /* cp_state */ + #endif + ret + +1: movi a15, 0 +2: ret + +#endif /* XCHAL_CP_NUM > 0 */ diff --git a/cpu/esp32/xtensa/xtensa_api.h b/cpu/esp32/xtensa/xtensa_api.h new file mode 100644 index 0000000000000..025b3d1676ce0 --- /dev/null +++ b/cpu/esp32/xtensa/xtensa_api.h @@ -0,0 +1,127 @@ +/******************************************************************************* +Copyright (c) 2006-2015 Cadence Design Systems Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + +/****************************************************************************** + Xtensa-specific API for RTOS ports. +******************************************************************************/ + +#ifndef XTENSA_API_H +#define XTENSA_API_H + +#include + +#include "xtensa_context.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Typedef for C-callable interrupt handler function */ +typedef void (*xt_handler)(void *); + +/* Typedef for C-callable exception handler function */ +typedef void (*xt_exc_handler)(XtExcFrame *); + + +/* +------------------------------------------------------------------------------- + Call this function to set a handler for the specified exception. + + n - Exception number (type) + f - Handler function address, NULL to uninstall handler. + + The handler will be passed a pointer to the exception frame, which is created + on the stack of the thread that caused the exception. + + If the handler returns, the thread context will be restored and the faulting + instruction will be retried. Any values in the exception frame that are + modified by the handler will be restored as part of the context. For details + of the exception frame structure see xtensa_context.h. +------------------------------------------------------------------------------- +*/ +extern xt_exc_handler xt_set_exception_handler(int n, xt_exc_handler f); + + +/* +------------------------------------------------------------------------------- + Call this function to set a handler for the specified interrupt. + + n - Interrupt number. + f - Handler function address, NULL to uninstall handler. + arg - Argument to be passed to handler. +------------------------------------------------------------------------------- +*/ +extern xt_handler xt_set_interrupt_handler(int n, xt_handler f, void * arg); + + +/* +------------------------------------------------------------------------------- + Call this function to enable the specified interrupts. + + mask - Bit mask of interrupts to be enabled. + + Returns the previous state of the interrupt enables. +------------------------------------------------------------------------------- +*/ +extern unsigned int xt_ints_on(unsigned int mask); + + +/* +------------------------------------------------------------------------------- + Call this function to disable the specified interrupts. + + mask - Bit mask of interrupts to be disabled. + + Returns the previous state of the interrupt enables. +------------------------------------------------------------------------------- +*/ +extern unsigned int xt_ints_off(unsigned int mask); + + +/* +------------------------------------------------------------------------------- + Call this function to set the specified (s/w) interrupt. +------------------------------------------------------------------------------- +*/ +static inline void xt_set_intset(unsigned int arg) +{ + xthal_set_intset(arg); +} + + +/* +------------------------------------------------------------------------------- + Call this function to clear the specified (s/w or edge-triggered) + interrupt. +------------------------------------------------------------------------------- +*/ +static inline void xt_set_intclear(unsigned int arg) +{ + xthal_set_intclear(arg); +} + +#ifdef __cplusplus +} +#endif + +#endif /* XTENSA_API_H */ diff --git a/cpu/esp32/xtensa/xtensa_context.S b/cpu/esp32/xtensa/xtensa_context.S new file mode 100644 index 0000000000000..3a56258eed713 --- /dev/null +++ b/cpu/esp32/xtensa/xtensa_context.S @@ -0,0 +1,624 @@ +/******************************************************************************* +Copyright (c) 2006-2015 Cadence Design Systems Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------- + + XTENSA CONTEXT SAVE AND RESTORE ROUTINES + +Low-level Call0 functions for handling generic context save and restore of +registers not specifically addressed by the interrupt vectors and handlers. +Those registers (not handled by these functions) are PC, PS, A0, A1 (SP). +Except for the calls to RTOS functions, this code is generic to Xtensa. + +Note that in Call0 ABI, interrupt handlers are expected to preserve the callee- +save regs (A12-A15), which is always the case if the handlers are coded in C. +However A12, A13 are made available as scratch registers for interrupt dispatch +code, so are presumed saved anyway, and are always restored even in Call0 ABI. +Only A14, A15 are truly handled as callee-save regs. + +Because Xtensa is a configurable architecture, this port supports all user +generated configurations (except restrictions stated in the release notes). +This is accomplished by conditional compilation using macros and functions +defined in the Xtensa HAL (hardware adaptation layer) for your configuration. +Only the processor state included in your configuration is saved and restored, +including any processor state added by user configuration options or TIE. + +*******************************************************************************/ + +/* Warn nicely if this file gets named with a lowercase .s instead of .S: */ +#define NOERROR # +NOERROR: .error "C preprocessor needed for this file: make sure its filename\ + ends in uppercase .S, or use xt-xcc's -x assembler-with-cpp option." + +#include "xtensa_rtos.h" +#include "xtensa_context.h" + +#ifdef XT_USE_OVLY +#include +#endif + + .text + .literal_position + +/******************************************************************************* + +_xt_context_save + + !! MUST BE CALLED ONLY BY 'CALL0' INSTRUCTION !! + +Saves all Xtensa processor state except PC, PS, A0, A1 (SP), A12, A13, in the +interrupt stack frame defined in xtensa_rtos.h. +Its counterpart is _xt_context_restore (which also restores A12, A13). + +Caller is expected to have saved PC, PS, A0, A1 (SP), A12, A13 in the frame. +This function preserves A12 & A13 in order to provide the caller with 2 scratch +regs that need not be saved over the call to this function. The choice of which +2 regs to provide is governed by xthal_window_spill_nw and xthal_save_extra_nw, +to avoid moving data more than necessary. Caller can assign regs accordingly. + +Entry Conditions: + A0 = Return address in caller. + A1 = Stack pointer of interrupted thread or handler ("interruptee"). + Original A12, A13 have already been saved in the interrupt stack frame. + Other processor state except PC, PS, A0, A1 (SP), A12, A13, is as at the + point of interruption. + If windowed ABI, PS.EXCM = 1 (exceptions disabled). + +Exit conditions: + A0 = Return address in caller. + A1 = Stack pointer of interrupted thread or handler ("interruptee"). + A12, A13 as at entry (preserved). + If windowed ABI, PS.EXCM = 1 (exceptions disabled). + +*******************************************************************************/ + + .global _xt_context_save + .type _xt_context_save,@function + .align 4 +_xt_context_save: + + s32i a2, sp, XT_STK_A2 + s32i a3, sp, XT_STK_A3 + s32i a4, sp, XT_STK_A4 + s32i a5, sp, XT_STK_A5 + s32i a6, sp, XT_STK_A6 + s32i a7, sp, XT_STK_A7 + s32i a8, sp, XT_STK_A8 + s32i a9, sp, XT_STK_A9 + s32i a10, sp, XT_STK_A10 + s32i a11, sp, XT_STK_A11 + + /* + Call0 ABI callee-saved regs a12-15 do not need to be saved here. + a12-13 are the caller's responsibility so it can use them as scratch. + So only need to save a14-a15 here for Windowed ABI (not Call0). + */ + #ifndef __XTENSA_CALL0_ABI__ + s32i a14, sp, XT_STK_A14 + s32i a15, sp, XT_STK_A15 + #endif + + rsr a3, SAR + s32i a3, sp, XT_STK_SAR + + #if XCHAL_HAVE_LOOPS + rsr a3, LBEG + s32i a3, sp, XT_STK_LBEG + rsr a3, LEND + s32i a3, sp, XT_STK_LEND + rsr a3, LCOUNT + s32i a3, sp, XT_STK_LCOUNT + #endif + + #if XT_USE_SWPRI + /* Save virtual priority mask */ + movi a3, _xt_vpri_mask + l32i a3, a3, 0 + s32i a3, sp, XT_STK_VPRI + #endif + + #if XCHAL_EXTRA_SA_SIZE > 0 || !defined(__XTENSA_CALL0_ABI__) + mov a9, a0 /* preserve ret addr */ + #endif + + #ifndef __XTENSA_CALL0_ABI__ + /* + To spill the reg windows, temp. need pre-interrupt stack ptr and a4-15. + Need to save a9,12,13 temporarily (in frame temps) and recover originals. + Interrupts need to be disabled below XCHAL_EXCM_LEVEL and window overflow + and underflow exceptions disabled (assured by PS.EXCM == 1). + */ + s32i a12, sp, XT_STK_TMP0 /* temp. save stuff in stack frame */ + s32i a13, sp, XT_STK_TMP1 + s32i a9, sp, XT_STK_TMP2 + + /* + Save the overlay state if we are supporting overlays. Since we just saved + three registers, we can conveniently use them here. Note that as of now, + overlays only work for windowed calling ABI. + */ + #ifdef XT_USE_OVLY + l32i a9, sp, XT_STK_PC /* recover saved PC */ + _xt_overlay_get_state a9, a12, a13 + s32i a9, sp, XT_STK_OVLY /* save overlay state */ + #endif + + l32i a12, sp, XT_STK_A12 /* recover original a9,12,13 */ + l32i a13, sp, XT_STK_A13 + l32i a9, sp, XT_STK_A9 + addi sp, sp, XT_STK_FRMSZ /* restore the interruptee's SP */ + call0 xthal_window_spill_nw /* preserves only a4,5,8,9,12,13 */ + addi sp, sp, -XT_STK_FRMSZ + l32i a12, sp, XT_STK_TMP0 /* recover stuff from stack frame */ + l32i a13, sp, XT_STK_TMP1 + l32i a9, sp, XT_STK_TMP2 + #endif + + #if XCHAL_EXTRA_SA_SIZE > 0 + /* + NOTE: Normally the xthal_save_extra_nw macro only affects address + registers a2-a5. It is theoretically possible for Xtensa processor + designers to write TIE that causes more address registers to be + affected, but it is generally unlikely. If that ever happens, + more registers need to be saved/restored around this macro invocation. + Here we assume a9,12,13 are preserved. + Future Xtensa tools releases might limit the regs that can be affected. + */ + addi a2, sp, XT_STK_EXTRA /* where to save it */ + # if XCHAL_EXTRA_SA_ALIGN > 16 + movi a3, -XCHAL_EXTRA_SA_ALIGN + and a2, a2, a3 /* align dynamically >16 bytes */ + # endif + call0 xthal_save_extra_nw /* destroys a0,2,3,4,5 */ + #endif + + #if XCHAL_EXTRA_SA_SIZE > 0 || !defined(__XTENSA_CALL0_ABI__) + mov a0, a9 /* retrieve ret addr */ + #endif + + ret + +/******************************************************************************* + +_xt_context_restore + + !! MUST BE CALLED ONLY BY 'CALL0' INSTRUCTION !! + +Restores all Xtensa processor state except PC, PS, A0, A1 (SP) (and in Call0 +ABI, A14, A15 which are preserved by all interrupt handlers) from an interrupt +stack frame defined in xtensa_rtos.h . +Its counterpart is _xt_context_save (whose caller saved A12, A13). + +Caller is responsible to restore PC, PS, A0, A1 (SP). + +Entry Conditions: + A0 = Return address in caller. + A1 = Stack pointer of interrupted thread or handler ("interruptee"). + +Exit conditions: + A0 = Return address in caller. + A1 = Stack pointer of interrupted thread or handler ("interruptee"). + Other processor state except PC, PS, A0, A1 (SP), is as at the point + of interruption. + +*******************************************************************************/ + + .global _xt_context_restore + .type _xt_context_restore,@function + .align 4 +_xt_context_restore: + + #if XCHAL_EXTRA_SA_SIZE > 0 + /* + NOTE: Normally the xthal_restore_extra_nw macro only affects address + registers a2-a5. It is theoretically possible for Xtensa processor + designers to write TIE that causes more address registers to be + affected, but it is generally unlikely. If that ever happens, + more registers need to be saved/restored around this macro invocation. + Here we only assume a13 is preserved. + Future Xtensa tools releases might limit the regs that can be affected. + */ + mov a13, a0 /* preserve ret addr */ + addi a2, sp, XT_STK_EXTRA /* where to find it */ + # if XCHAL_EXTRA_SA_ALIGN > 16 + movi a3, -XCHAL_EXTRA_SA_ALIGN + and a2, a2, a3 /* align dynamically >16 bytes */ + # endif + call0 xthal_restore_extra_nw /* destroys a0,2,3,4,5 */ + mov a0, a13 /* retrieve ret addr */ + #endif + + #if XCHAL_HAVE_LOOPS + l32i a2, sp, XT_STK_LBEG + l32i a3, sp, XT_STK_LEND + wsr a2, LBEG + l32i a2, sp, XT_STK_LCOUNT + wsr a3, LEND + wsr a2, LCOUNT + #endif + + #ifdef XT_USE_OVLY + /* + If we are using overlays, this is a good spot to check if we need + to restore an overlay for the incoming task. Here we have a bunch + of registers to spare. Note that this step is going to use a few + bytes of storage below SP (SP-20 to SP-32) if an overlay is going + to be restored. + */ + l32i a2, sp, XT_STK_PC /* retrieve PC */ + l32i a3, sp, XT_STK_PS /* retrieve PS */ + l32i a4, sp, XT_STK_OVLY /* retrieve overlay state */ + l32i a5, sp, XT_STK_A1 /* retrieve stack ptr */ + _xt_overlay_check_map a2, a3, a4, a5, a6 + s32i a2, sp, XT_STK_PC /* save updated PC */ + s32i a3, sp, XT_STK_PS /* save updated PS */ + #endif + + #ifdef XT_USE_SWPRI + /* Restore virtual interrupt priority and interrupt enable */ + movi a3, _xt_intdata + l32i a4, a3, 0 /* a4 = _xt_intenable */ + l32i a5, sp, XT_STK_VPRI /* a5 = saved _xt_vpri_mask */ + and a4, a4, a5 + wsr a4, INTENABLE /* update INTENABLE */ + s32i a5, a3, 4 /* restore _xt_vpri_mask */ + #endif + + l32i a3, sp, XT_STK_SAR + l32i a2, sp, XT_STK_A2 + wsr a3, SAR + l32i a3, sp, XT_STK_A3 + l32i a4, sp, XT_STK_A4 + l32i a5, sp, XT_STK_A5 + l32i a6, sp, XT_STK_A6 + l32i a7, sp, XT_STK_A7 + l32i a8, sp, XT_STK_A8 + l32i a9, sp, XT_STK_A9 + l32i a10, sp, XT_STK_A10 + l32i a11, sp, XT_STK_A11 + + /* + Call0 ABI callee-saved regs a12-15 do not need to be restored here. + However a12-13 were saved for scratch before XT_RTOS_INT_ENTER(), + so need to be restored anyway, despite being callee-saved in Call0. + */ + l32i a12, sp, XT_STK_A12 + l32i a13, sp, XT_STK_A13 + #ifndef __XTENSA_CALL0_ABI__ + l32i a14, sp, XT_STK_A14 + l32i a15, sp, XT_STK_A15 + #endif + + ret + + +/******************************************************************************* + +_xt_coproc_init + +Initializes global co-processor management data, setting all co-processors +to "unowned". Leaves CPENABLE as it found it (does NOT clear it). + +Called during initialization of the RTOS, before any threads run. + +This may be called from normal Xtensa single-threaded application code which +might use co-processors. The Xtensa run-time initialization enables all +co-processors. They must remain enabled here, else a co-processor exception +might occur outside of a thread, which the exception handler doesn't expect. + +Entry Conditions: + Xtensa single-threaded run-time environment is in effect. + No thread is yet running. + +Exit conditions: + None. + +Obeys ABI conventions per prototype: + void _xt_coproc_init(void) + +*******************************************************************************/ + +#if XCHAL_CP_NUM > 0 + + .global _xt_coproc_init + .type _xt_coproc_init,@function + .align 4 +_xt_coproc_init: + ENTRY0 + + /* Initialize thread co-processor ownerships to 0 (unowned). */ + movi a2, _xt_coproc_owner_sa /* a2 = base of owner array */ + addi a3, a2, XCHAL_CP_MAX << 2 /* a3 = top+1 of owner array */ + movi a4, 0 /* a4 = 0 (unowned) */ +1: s32i a4, a2, 0 + addi a2, a2, 4 + bltu a2, a3, 1b + + RET0 + +#endif + + +/******************************************************************************* + +_xt_coproc_release + +Releases any and all co-processors owned by a given thread. The thread is +identified by it's co-processor state save area defined in xtensa_context.h . + +Must be called before a thread's co-proc save area is deleted to avoid +memory corruption when the exception handler tries to save the state. +May be called when a thread terminates or completes but does not delete +the co-proc save area, to avoid the exception handler having to save the +thread's co-proc state before another thread can use it (optimization). + +Entry Conditions: + A2 = Pointer to base of co-processor state save area. + +Exit conditions: + None. + +Obeys ABI conventions per prototype: + void _xt_coproc_release(void * coproc_sa_base) + +*******************************************************************************/ + +#if XCHAL_CP_NUM > 0 + + .global _xt_coproc_release + .type _xt_coproc_release,@function + .align 4 +_xt_coproc_release: + ENTRY0 /* a2 = base of save area */ + + movi a3, _xt_coproc_owner_sa /* a3 = base of owner array */ + addi a4, a3, XCHAL_CP_MAX << 2 /* a4 = top+1 of owner array */ + movi a5, 0 /* a5 = 0 (unowned) */ + + rsil a6, XCHAL_EXCM_LEVEL /* lock interrupts */ + +1: l32i a7, a3, 0 /* a7 = owner at a3 */ + bne a2, a7, 2f /* if (coproc_sa_base == owner) */ + s32i a5, a3, 0 /* owner = unowned */ +2: addi a3, a3, 1<<2 /* a3 = next entry in owner array */ + bltu a3, a4, 1b /* repeat until end of array */ + +3: wsr a6, PS /* restore interrupts */ + + RET0 + +#endif + + +/******************************************************************************* +_xt_coproc_savecs + +If there is a current thread and it has a coprocessor state save area, then +save all callee-saved state into this area. This function is called from the +solicited context switch handler. It calls a system-specific function to get +the coprocessor save area base address. + +Entry conditions: + - The thread being switched out is still the current thread. + - CPENABLE state reflects which coprocessors are active. + - Registers have been saved/spilled already. + +Exit conditions: + - All necessary CP callee-saved state has been saved. + - Registers a2-a7, a13-a15 have been trashed. + +Must be called from assembly code only, using CALL0. +*******************************************************************************/ +#if XCHAL_CP_NUM > 0 + + .extern _xt_coproc_sa_offset /* external reference */ + + .global _xt_coproc_savecs + .type _xt_coproc_savecs,@function + .align 4 +_xt_coproc_savecs: + + /* At entry, CPENABLE should be showing which CPs are enabled. */ + + rsr a2, CPENABLE /* a2 = which CPs are enabled */ + beqz a2, .Ldone /* quick exit if none */ + mov a14, a0 /* save return address */ + call0 XT_RTOS_CP_STATE /* get address of CP save area */ + mov a0, a14 /* restore return address */ + beqz a15, .Ldone /* if none then nothing to do */ + s16i a2, a15, XT_CP_CS_ST /* save mask of CPs being stored */ + movi a13, _xt_coproc_sa_offset /* array of CP save offsets */ + l32i a15, a15, XT_CP_ASA /* a15 = base of aligned save area */ + +#if XCHAL_CP0_SA_SIZE + bbci.l a2, 0, 2f /* CP 0 not enabled */ + l32i a14, a13, 0 /* a14 = _xt_coproc_sa_offset[0] */ + add a3, a14, a15 /* a3 = save area for CP 0 */ + xchal_cp0_store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XCHAL_CP1_SA_SIZE + bbci.l a2, 1, 2f /* CP 1 not enabled */ + l32i a14, a13, 4 /* a14 = _xt_coproc_sa_offset[1] */ + add a3, a14, a15 /* a3 = save area for CP 1 */ + xchal_cp1_store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XCHAL_CP2_SA_SIZE + bbci.l a2, 2, 2f + l32i a14, a13, 8 + add a3, a14, a15 + xchal_cp2_store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XCHAL_CP3_SA_SIZE + bbci.l a2, 3, 2f + l32i a14, a13, 12 + add a3, a14, a15 + xchal_cp3_store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XCHAL_CP4_SA_SIZE + bbci.l a2, 4, 2f + l32i a14, a13, 16 + add a3, a14, a15 + xchal_cp4_store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XCHAL_CP5_SA_SIZE + bbci.l a2, 5, 2f + l32i a14, a13, 20 + add a3, a14, a15 + xchal_cp5_store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XCHAL_CP6_SA_SIZE + bbci.l a2, 6, 2f + l32i a14, a13, 24 + add a3, a14, a15 + xchal_cp6_store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XCHAL_CP7_SA_SIZE + bbci.l a2, 7, 2f + l32i a14, a13, 28 + add a3, a14, a15 + xchal_cp7_store a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +.Ldone: + ret +#endif + + +/******************************************************************************* +_xt_coproc_restorecs + +Restore any callee-saved coprocessor state for the incoming thread. +This function is called from coprocessor exception handling, when giving +ownership to a thread that solicited a context switch earlier. It calls a +system-specific function to get the coprocessor save area base address. + +Entry conditions: + - The incoming thread is set as the current thread. + - CPENABLE is set up correctly for all required coprocessors. + - a2 = mask of coprocessors to be restored. + +Exit conditions: + - All necessary CP callee-saved state has been restored. + - CPENABLE - unchanged. + - Registers a2-a7, a13-a15 have been trashed. + +Must be called from assembly code only, using CALL0. +*******************************************************************************/ +#if XCHAL_CP_NUM > 0 + + .global _xt_coproc_restorecs + .type _xt_coproc_restorecs,@function + .align 4 +_xt_coproc_restorecs: + + mov a14, a0 /* save return address */ + call0 XT_RTOS_CP_STATE /* get address of CP save area */ + mov a0, a14 /* restore return address */ + beqz a15, .Ldone2 /* if none then nothing to do */ + l16ui a3, a15, XT_CP_CS_ST /* a3 = which CPs have been saved */ + xor a3, a3, a2 /* clear the ones being restored */ + s32i a3, a15, XT_CP_CS_ST /* update saved CP mask */ + movi a13, _xt_coproc_sa_offset /* array of CP save offsets */ + l32i a15, a15, XT_CP_ASA /* a15 = base of aligned save area */ + +#if XCHAL_CP0_SA_SIZE + bbci.l a2, 0, 2f /* CP 0 not enabled */ + l32i a14, a13, 0 /* a14 = _xt_coproc_sa_offset[0] */ + add a3, a14, a15 /* a3 = save area for CP 0 */ + xchal_cp0_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XCHAL_CP1_SA_SIZE + bbci.l a2, 1, 2f /* CP 1 not enabled */ + l32i a14, a13, 4 /* a14 = _xt_coproc_sa_offset[1] */ + add a3, a14, a15 /* a3 = save area for CP 1 */ + xchal_cp1_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XCHAL_CP2_SA_SIZE + bbci.l a2, 2, 2f + l32i a14, a13, 8 + add a3, a14, a15 + xchal_cp2_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XCHAL_CP3_SA_SIZE + bbci.l a2, 3, 2f + l32i a14, a13, 12 + add a3, a14, a15 + xchal_cp3_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XCHAL_CP4_SA_SIZE + bbci.l a2, 4, 2f + l32i a14, a13, 16 + add a3, a14, a15 + xchal_cp4_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XCHAL_CP5_SA_SIZE + bbci.l a2, 5, 2f + l32i a14, a13, 20 + add a3, a14, a15 + xchal_cp5_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XCHAL_CP6_SA_SIZE + bbci.l a2, 6, 2f + l32i a14, a13, 24 + add a3, a14, a15 + xchal_cp6_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +#if XCHAL_CP7_SA_SIZE + bbci.l a2, 7, 2f + l32i a14, a13, 28 + add a3, a14, a15 + xchal_cp7_load a3, a4, a5, a6, a7 continue=0 ofs=-1 select=XTHAL_SAS_TIE|XTHAL_SAS_NOCC|XTHAL_SAS_CALE alloc=XTHAL_SAS_ALL +2: +#endif + +.Ldone2: + ret + +#endif diff --git a/cpu/esp32/xtensa/xtensa_context.h b/cpu/esp32/xtensa/xtensa_context.h new file mode 100644 index 0000000000000..e331d62482845 --- /dev/null +++ b/cpu/esp32/xtensa/xtensa_context.h @@ -0,0 +1,355 @@ +/******************************************************************************* +Copyright (c) 2006-2015 Cadence Design Systems Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------- + + XTENSA CONTEXT FRAMES AND MACROS FOR RTOS ASSEMBLER SOURCES + +This header contains definitions and macros for use primarily by Xtensa +RTOS assembly coded source files. It includes and uses the Xtensa hardware +abstraction layer (HAL) to deal with config specifics. It may also be +included in C source files. + +!! Supports only Xtensa Exception Architecture 2 (XEA2). XEA1 not supported. !! + +NOTE: The Xtensa architecture requires stack pointer alignment to 16 bytes. + +*******************************************************************************/ + +#ifndef XTENSA_CONTEXT_H +#define XTENSA_CONTEXT_H + +#ifdef __ASSEMBLER__ +#include +#endif + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Align a value up to nearest n-byte boundary, where n is a power of 2. */ +#define ALIGNUP(n, val) (((val) + (n)-1) & -(n)) + + +/* +------------------------------------------------------------------------------- + Macros that help define structures for both C and assembler. +------------------------------------------------------------------------------- +*/ +#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) + +#define STRUCT_BEGIN .pushsection .text; .struct 0 +#define STRUCT_FIELD(ctype,size,asname,name) asname: .space size +#define STRUCT_AFIELD(ctype,size,asname,name,n) asname: .space (size)*(n) +#define STRUCT_END(sname) sname##Size:; .popsection + +#else + +#define STRUCT_BEGIN typedef struct { +#define STRUCT_FIELD(ctype,size,asname,name) ctype name; +#define STRUCT_AFIELD(ctype,size,asname,name,n) ctype name[n]; +#define STRUCT_END(sname) } sname; + +#endif //_ASMLANGUAGE || __ASSEMBLER__ + + +/* +------------------------------------------------------------------------------- + INTERRUPT/EXCEPTION STACK FRAME FOR A THREAD OR NESTED INTERRUPT + + A stack frame of this structure is allocated for any interrupt or exception. + It goes on the current stack. If the RTOS has a system stack for handling + interrupts, every thread stack must allow space for just one interrupt stack + frame, then nested interrupt stack frames go on the system stack. + + The frame includes basic registers (explicit) and "extra" registers introduced + by user TIE or the use of the MAC16 option in the user's Xtensa config. + The frame size is minimized by omitting regs not applicable to user's config. + + For Windowed ABI, this stack frame includes the interruptee's base save area, + another base save area to manage gcc nested functions, and a little temporary + space to help manage the spilling of the register windows. +------------------------------------------------------------------------------- +*/ + +STRUCT_BEGIN +STRUCT_FIELD (long, 4, XT_STK_EXIT, exit) /* exit point for dispatch */ +STRUCT_FIELD (long, 4, XT_STK_PC, pc) /* return PC */ +STRUCT_FIELD (long, 4, XT_STK_PS, ps) /* return PS */ +STRUCT_FIELD (long, 4, XT_STK_A0, a0) +STRUCT_FIELD (long, 4, XT_STK_A1, a1) /* stack pointer before interrupt */ +STRUCT_FIELD (long, 4, XT_STK_A2, a2) +STRUCT_FIELD (long, 4, XT_STK_A3, a3) +STRUCT_FIELD (long, 4, XT_STK_A4, a4) +STRUCT_FIELD (long, 4, XT_STK_A5, a5) +STRUCT_FIELD (long, 4, XT_STK_A6, a6) +STRUCT_FIELD (long, 4, XT_STK_A7, a7) +STRUCT_FIELD (long, 4, XT_STK_A8, a8) +STRUCT_FIELD (long, 4, XT_STK_A9, a9) +STRUCT_FIELD (long, 4, XT_STK_A10, a10) +STRUCT_FIELD (long, 4, XT_STK_A11, a11) +STRUCT_FIELD (long, 4, XT_STK_A12, a12) +STRUCT_FIELD (long, 4, XT_STK_A13, a13) +STRUCT_FIELD (long, 4, XT_STK_A14, a14) +STRUCT_FIELD (long, 4, XT_STK_A15, a15) +STRUCT_FIELD (long, 4, XT_STK_SAR, sar) +STRUCT_FIELD (long, 4, XT_STK_EXCCAUSE, exccause) +STRUCT_FIELD (long, 4, XT_STK_EXCVADDR, excvaddr) +#if XCHAL_HAVE_LOOPS +STRUCT_FIELD (long, 4, XT_STK_LBEG, lbeg) +STRUCT_FIELD (long, 4, XT_STK_LEND, lend) +STRUCT_FIELD (long, 4, XT_STK_LCOUNT, lcount) +#endif +#ifndef __XTENSA_CALL0_ABI__ +/* Temporary space for saving stuff during window spill */ +STRUCT_FIELD (long, 4, XT_STK_TMP0, tmp0) +STRUCT_FIELD (long, 4, XT_STK_TMP1, tmp1) +STRUCT_FIELD (long, 4, XT_STK_TMP2, tmp2) +#endif +#ifdef XT_USE_SWPRI +/* Storage for virtual priority mask */ +STRUCT_FIELD (long, 4, XT_STK_VPRI, vpri) +#endif +#ifdef XT_USE_OVLY +/* Storage for overlay state */ +STRUCT_FIELD (long, 4, XT_STK_OVLY, ovly) +#endif +STRUCT_END(XtExcFrame) + +#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) +#define XT_STK_NEXT1 XtExcFrameSize +#else +#define XT_STK_NEXT1 sizeof(XtExcFrame) +#endif + +/* Allocate extra storage if needed */ +#if XCHAL_EXTRA_SA_SIZE != 0 + +#if XCHAL_EXTRA_SA_ALIGN <= 16 +#define XT_STK_EXTRA ALIGNUP(XCHAL_EXTRA_SA_ALIGN, XT_STK_NEXT1) +#else +/* If need more alignment than stack, add space for dynamic alignment */ +#define XT_STK_EXTRA (ALIGNUP(XCHAL_EXTRA_SA_ALIGN, XT_STK_NEXT1) + XCHAL_EXTRA_SA_ALIGN) +#endif +#define XT_STK_NEXT2 (XT_STK_EXTRA + XCHAL_EXTRA_SA_SIZE) + +#else + +#define XT_STK_NEXT2 XT_STK_NEXT1 + +#endif + +/* +------------------------------------------------------------------------------- + This is the frame size. Add space for 4 registers (interruptee's base save + area) and some space for gcc nested functions if any. +------------------------------------------------------------------------------- +*/ +#define XT_STK_FRMSZ (ALIGNUP(0x10, XT_STK_NEXT2) + 0x20) + + +/* +------------------------------------------------------------------------------- + SOLICITED STACK FRAME FOR A THREAD + + A stack frame of this structure is allocated whenever a thread enters the + RTOS kernel intentionally (and synchronously) to submit to thread scheduling. + It goes on the current thread's stack. + + The solicited frame only includes registers that are required to be preserved + by the callee according to the compiler's ABI conventions, some space to save + the return address for returning to the caller, and the caller's PS register. + + For Windowed ABI, this stack frame includes the caller's base save area. + + Note on XT_SOL_EXIT field: + It is necessary to distinguish a solicited from an interrupt stack frame. + This field corresponds to XT_STK_EXIT in the interrupt stack frame and is + always at the same offset (0). It can be written with a code (usually 0) + to distinguish a solicted frame from an interrupt frame. An RTOS port may + opt to ignore this field if it has another way of distinguishing frames. +------------------------------------------------------------------------------- +*/ + +STRUCT_BEGIN +#ifdef __XTENSA_CALL0_ABI__ +STRUCT_FIELD (long, 4, XT_SOL_EXIT, exit) +STRUCT_FIELD (long, 4, XT_SOL_PC, pc) +STRUCT_FIELD (long, 4, XT_SOL_PS, ps) +STRUCT_FIELD (long, 4, XT_SOL_NEXT, next) +STRUCT_FIELD (long, 4, XT_SOL_A12, a12) /* should be on 16-byte alignment */ +STRUCT_FIELD (long, 4, XT_SOL_A13, a13) +STRUCT_FIELD (long, 4, XT_SOL_A14, a14) +STRUCT_FIELD (long, 4, XT_SOL_A15, a15) +#else +STRUCT_FIELD (long, 4, XT_SOL_EXIT, exit) +STRUCT_FIELD (long, 4, XT_SOL_PC, pc) +STRUCT_FIELD (long, 4, XT_SOL_PS, ps) +STRUCT_FIELD (long, 4, XT_SOL_NEXT, next) +STRUCT_FIELD (long, 4, XT_SOL_A0, a0) /* should be on 16-byte alignment */ +STRUCT_FIELD (long, 4, XT_SOL_A1, a1) +STRUCT_FIELD (long, 4, XT_SOL_A2, a2) +STRUCT_FIELD (long, 4, XT_SOL_A3, a3) +#endif +STRUCT_END(XtSolFrame) + +/* Size of solicited stack frame */ +#define XT_SOL_FRMSZ ALIGNUP(0x10, XtSolFrameSize) + + +/* +------------------------------------------------------------------------------- + CO-PROCESSOR STATE SAVE AREA FOR A THREAD + + The RTOS must provide an area per thread to save the state of co-processors + when that thread does not have control. Co-processors are context-switched + lazily (on demand) only when a new thread uses a co-processor instruction, + otherwise a thread retains ownership of the co-processor even when it loses + control of the processor. An Xtensa co-processor exception is triggered when + any co-processor instruction is executed by a thread that is not the owner, + and the context switch of that co-processor is then peformed by the handler. + Ownership represents which thread's state is currently in the co-processor. + + Co-processors may not be used by interrupt or exception handlers. If an + co-processor instruction is executed by an interrupt or exception handler, + the co-processor exception handler will trigger a kernel panic and freeze. + This restriction is introduced to reduce the overhead of saving and restoring + co-processor state (which can be quite large) and in particular remove that + overhead from interrupt handlers. + + The co-processor state save area may be in any convenient per-thread location + such as in the thread control block or above the thread stack area. It need + not be in the interrupt stack frame since interrupts don't use co-processors. + + Along with the save area for each co-processor, two bitmasks with flags per + co-processor (laid out as in the CPENABLE reg) help manage context-switching + co-processors as efficiently as possible: + + XT_CPENABLE + The contents of a non-running thread's CPENABLE register. + It represents the co-processors owned (and whose state is still needed) + by the thread. When a thread is preempted, its CPENABLE is saved here. + When a thread solicits a context-swtich, its CPENABLE is cleared - the + compiler has saved the (caller-saved) co-proc state if it needs to. + When a non-running thread loses ownership of a CP, its bit is cleared. + When a thread runs, it's XT_CPENABLE is loaded into the CPENABLE reg. + Avoids co-processor exceptions when no change of ownership is needed. + + XT_CPSTORED + A bitmask with the same layout as CPENABLE, a bit per co-processor. + Indicates whether the state of each co-processor is saved in the state + save area. When a thread enters the kernel, only the state of co-procs + still enabled in CPENABLE is saved. When the co-processor exception + handler assigns ownership of a co-processor to a thread, it restores + the saved state only if this bit is set, and clears this bit. + + XT_CP_CS_ST + A bitmask with the same layout as CPENABLE, a bit per co-processor. + Indicates whether callee-saved state is saved in the state save area. + Callee-saved state is saved by itself on a solicited context switch, + and restored when needed by the coprocessor exception handler. + Unsolicited switches will cause the entire coprocessor to be saved + when necessary. + + XT_CP_ASA + Pointer to the aligned save area. Allows it to be aligned more than + the overall save area (which might only be stack-aligned or TCB-aligned). + Especially relevant for Xtensa cores configured with a very large data + path that requires alignment greater than 16 bytes (ABI stack alignment). +------------------------------------------------------------------------------- +*/ + +#if XCHAL_CP_NUM > 0 + +/* Offsets of each coprocessor save area within the 'aligned save area': */ +#define XT_CP0_SA 0 +#define XT_CP1_SA ALIGNUP(XCHAL_CP1_SA_ALIGN, XT_CP0_SA + XCHAL_CP0_SA_SIZE) +#define XT_CP2_SA ALIGNUP(XCHAL_CP2_SA_ALIGN, XT_CP1_SA + XCHAL_CP1_SA_SIZE) +#define XT_CP3_SA ALIGNUP(XCHAL_CP3_SA_ALIGN, XT_CP2_SA + XCHAL_CP2_SA_SIZE) +#define XT_CP4_SA ALIGNUP(XCHAL_CP4_SA_ALIGN, XT_CP3_SA + XCHAL_CP3_SA_SIZE) +#define XT_CP5_SA ALIGNUP(XCHAL_CP5_SA_ALIGN, XT_CP4_SA + XCHAL_CP4_SA_SIZE) +#define XT_CP6_SA ALIGNUP(XCHAL_CP6_SA_ALIGN, XT_CP5_SA + XCHAL_CP5_SA_SIZE) +#define XT_CP7_SA ALIGNUP(XCHAL_CP7_SA_ALIGN, XT_CP6_SA + XCHAL_CP6_SA_SIZE) +#define XT_CP_SA_SIZE ALIGNUP(16, XT_CP7_SA + XCHAL_CP7_SA_SIZE) + +/* Offsets within the overall save area: */ +#define XT_CPENABLE 0 /* (2 bytes) coprocessors active for this thread */ +#define XT_CPSTORED 2 /* (2 bytes) coprocessors saved for this thread */ +#define XT_CP_CS_ST 4 /* (2 bytes) coprocessor callee-saved regs stored for this thread */ +#define XT_CP_ASA 8 /* (4 bytes) ptr to aligned save area */ +/* Overall size allows for dynamic alignment: */ +#define XT_CP_SIZE (12 + XT_CP_SA_SIZE + XCHAL_TOTAL_SA_ALIGN) +#else +#define XT_CP_SIZE 0 +#endif + + +/* +------------------------------------------------------------------------------- + MACROS TO HANDLE ABI SPECIFICS OF FUNCTION ENTRY AND RETURN + + Convenient where the frame size requirements are the same for both ABIs. + ENTRY(sz), RET(sz) are for framed functions (have locals or make calls). + ENTRY0, RET0 are for frameless functions (no locals, no calls). + + where size = size of stack frame in bytes (must be >0 and aligned to 16). + For framed functions the frame is created and the return address saved at + base of frame (Call0 ABI) or as determined by hardware (Windowed ABI). + For frameless functions, there is no frame and return address remains in a0. + Note: Because CPP macros expand to a single line, macros requiring multi-line + expansions are implemented as assembler macros. +------------------------------------------------------------------------------- +*/ + +#ifdef __ASSEMBLER__ +#ifdef __XTENSA_CALL0_ABI__ + /* Call0 */ + #define ENTRY(sz) entry1 sz + .macro entry1 size=0x10 + addi sp, sp, -\size + s32i a0, sp, 0 + .endm + #define ENTRY0 + #define RET(sz) ret1 sz + .macro ret1 size=0x10 + l32i a0, sp, 0 + addi sp, sp, \size + ret + .endm + #define RET0 ret +#else + /* Windowed */ + #define ENTRY(sz) entry sp, sz + #define ENTRY0 entry sp, 0x10 + #define RET(sz) retw + #define RET0 retw +#endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* XTENSA_CONTEXT_H */ diff --git a/cpu/esp32/xtensa/xtensa_intr.c b/cpu/esp32/xtensa/xtensa_intr.c new file mode 100644 index 0000000000000..f33b8d36f00ba --- /dev/null +++ b/cpu/esp32/xtensa/xtensa_intr.c @@ -0,0 +1,141 @@ +/******************************************************************************* +Copyright (c) 2006-2015 Cadence Design Systems Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + +/****************************************************************************** + Xtensa-specific interrupt and exception functions for RTOS ports. + Also see xtensa_intr_asm.S. +******************************************************************************/ + +#ifndef SDK_INT_HANDLING /* not needed in SDK task handling version of RIOT */ + +#include + +#include + +#include "xtensa_api.h" + + +#if XCHAL_HAVE_EXCEPTIONS + +/* Handler table is in xtensa_intr_asm.S */ + +extern xt_exc_handler _xt_exception_table[XCHAL_EXCCAUSE_NUM]; + + +/* + Default handler for unhandled exceptions. +*/ +void xt_unhandled_exception(XtExcFrame *frame) +{ + exit(-1); +} + + +/* + This function registers a handler for the specified exception. + The function returns the address of the previous handler. + On error, it returns 0. +*/ +xt_exc_handler xt_set_exception_handler(int n, xt_exc_handler f) +{ + xt_exc_handler old; + + if( n < 0 || n >= XCHAL_EXCCAUSE_NUM ) + return 0; /* invalid exception number */ + + old = _xt_exception_table[n]; + + if (f) { + _xt_exception_table[n] = f; + } + else { + _xt_exception_table[n] = &xt_unhandled_exception; + } + + return ((old == &xt_unhandled_exception) ? 0 : old); +} + +#endif + +#if XCHAL_HAVE_INTERRUPTS + +/* Handler table is in xtensa_intr_asm.S */ + +typedef struct xt_handler_table_entry { + void * handler; + void * arg; +} xt_handler_table_entry; + +extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS]; + + +/* + Default handler for unhandled interrupts. +*/ +void xt_unhandled_interrupt(void * arg) +{ + exit(-1); +} + + +/* + This function registers a handler for the specified interrupt. The "arg" + parameter specifies the argument to be passed to the handler when it is + invoked. The function returns the address of the previous handler. + On error, it returns 0. +*/ +xt_handler xt_set_interrupt_handler(int n, xt_handler f, void * arg) +{ + xt_handler_table_entry * entry; + xt_handler old; + + if( n < 0 || n >= XCHAL_NUM_INTERRUPTS ) + return 0; /* invalid interrupt number */ + if( Xthal_intlevel[n] > XCHAL_EXCM_LEVEL ) + return 0; /* priority level too high to safely handle in C */ + + #ifdef SDK_USED + // for compatibility reasons with SDK, we use _xtos_interrupt_table + // in reverse order + entry = _xt_interrupt_table + (XCHAL_NUM_INTERRUPTS - n); + #else + entry = _xt_interrupt_table + n; + #endif + old = entry->handler; + + if (f) { + entry->handler = f; + entry->arg = arg; + } + else { + entry->handler = &xt_unhandled_interrupt; + entry->arg = (void*)n; + } + + return ((old == &xt_unhandled_interrupt) ? 0 : old); +} + + +#endif /* XCHAL_HAVE_INTERRUPTS */ + +#endif /* SDK_INT_HANDLING */ diff --git a/cpu/esp32/xtensa/xtensa_intr_asm.S b/cpu/esp32/xtensa/xtensa_intr_asm.S new file mode 100644 index 0000000000000..5c89b558dc3db --- /dev/null +++ b/cpu/esp32/xtensa/xtensa_intr_asm.S @@ -0,0 +1,187 @@ +/******************************************************************************* +Copyright (c) 2006-2015 Cadence Design Systems Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + +/****************************************************************************** + Xtensa interrupt handling data and assembly routines. + Also see xtensa_intr.c and xtensa_vectors.S. +******************************************************************************/ + +#ifndef SDK_INT_HANDLING /* not needed in SDK version of RIOT */ + +#include +#include + +#include "xtensa_context.h" + +#if XCHAL_HAVE_INTERRUPTS + + .literal_position + +/* +------------------------------------------------------------------------------- + INTENABLE virtualization information. +------------------------------------------------------------------------------- +*/ + .data + .global _xt_intdata + .align 8 +_xt_intdata: + .global _xt_intenable + .type _xt_intenable,@object + .size _xt_intenable,4 + .global _xt_vpri_mask + .type _xt_vpri_mask,@object + .size _xt_vpri_mask,4 + +_xt_intenable: .word 0 /* Virtual INTENABLE */ +_xt_vpri_mask: .word 0xFFFFFFFF /* Virtual priority mask */ + + +/* +------------------------------------------------------------------------------- + Table of C-callable interrupt handlers for each interrupt. Note that not all + slots can be filled, because interrupts at level > EXCM_LEVEL will not be + dispatched to a C handler by default. +------------------------------------------------------------------------------- +*/ +/* + in SDK we use _xtos_interrupt_table_ which is provided as symbol + _xt_interrupt_table_ by ld script +*/ +#ifdef SDK_NOT_USED + .data + .global _xt_interrupt_table + .align 8 + +_xt_interrupt_table: + + .set i, 0 + .rept XCHAL_NUM_INTERRUPTS + .word xt_unhandled_interrupt /* handler address */ + .word i /* handler arg (default: intnum) */ + .set i, i+1 + .endr +#endif + +#endif /* XCHAL_HAVE_INTERRUPTS */ + + +#if XCHAL_HAVE_EXCEPTIONS + +/* +------------------------------------------------------------------------------- + Table of C-callable exception handlers for each exception. Note that not all + slots will be active, because some exceptions (e.g. coprocessor exceptions) + are always handled by the OS and cannot be hooked by user handlers. +------------------------------------------------------------------------------- +*/ + + .data + .global _xt_exception_table + .align 4 + +_xt_exception_table: + .rept XCHAL_EXCCAUSE_NUM + .word xt_unhandled_exception /* handler address */ + .endr + +#endif + + +/* +------------------------------------------------------------------------------- + unsigned int xt_ints_on ( unsigned int mask ) + + Enables a set of interrupts. Does not simply set INTENABLE directly, but + computes it as a function of the current virtual priority. + Can be called from interrupt handlers. +------------------------------------------------------------------------------- +*/ + + .text + .align 4 + .global xt_ints_on + .type xt_ints_on,@function + +xt_ints_on: + + ENTRY0 +#if XCHAL_HAVE_INTERRUPTS + movi a3, 0 + movi a4, _xt_intdata + xsr a3, INTENABLE /* Disables all interrupts */ + rsync + l32i a3, a4, 0 /* a3 = _xt_intenable */ + l32i a6, a4, 4 /* a6 = _xt_vpri_mask */ + or a5, a3, a2 /* a5 = _xt_intenable | mask */ + s32i a5, a4, 0 /* _xt_intenable |= mask */ + and a5, a5, a6 /* a5 = _xt_intenable & _xt_vpri_mask */ + wsr a5, INTENABLE /* Reenable interrupts */ + mov a2, a3 /* Previous mask */ +#else + movi a2, 0 /* Return zero */ +#endif + RET0 + + .size xt_ints_on, . - xt_ints_on + + +/* +------------------------------------------------------------------------------- + unsigned int xt_ints_off ( unsigned int mask ) + + Disables a set of interrupts. Does not simply set INTENABLE directly, + but computes it as a function of the current virtual priority. + Can be called from interrupt handlers. +------------------------------------------------------------------------------- +*/ + + .text + .align 4 + .global xt_ints_off + .type xt_ints_off,@function + +xt_ints_off: + + ENTRY0 +#if XCHAL_HAVE_INTERRUPTS + movi a3, 0 + movi a4, _xt_intdata + xsr a3, INTENABLE /* Disables all interrupts */ + rsync + l32i a3, a4, 0 /* a3 = _xt_intenable */ + l32i a6, a4, 4 /* a6 = _xt_vpri_mask */ + or a5, a3, a2 /* a5 = _xt_intenable | mask */ + xor a5, a5, a2 /* a5 = _xt_intenable & ~mask */ + s32i a5, a4, 0 /* _xt_intenable &= ~mask */ + and a5, a5, a6 /* a5 = _xt_intenable & _xt_vpri_mask */ + wsr a5, INTENABLE /* Reenable interrupts */ + mov a2, a3 /* Previous mask */ +#else + movi a2, 0 /* return zero */ +#endif + RET0 + + .size xt_ints_off, . - xt_ints_off + +#endif /* SDK_INT_HANDLING */ diff --git a/cpu/esp32/xtensa/xtensa_rtos.h b/cpu/esp32/xtensa/xtensa_rtos.h new file mode 100644 index 0000000000000..7d27df21c8464 --- /dev/null +++ b/cpu/esp32/xtensa/xtensa_rtos.h @@ -0,0 +1,247 @@ +/******************************************************************************* +// Copyright (c) 2003-2015 Cadence Design Systems, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------- + + RTOS-SPECIFIC INFORMATION FOR XTENSA RTOS ASSEMBLER SOURCES + (FreeRTOS Port) + +This header is the primary glue between generic Xtensa RTOS support +sources and a specific RTOS port for Xtensa. It contains definitions +and macros for use primarily by Xtensa assembly coded source files. + +Macros in this header map callouts from generic Xtensa files to specific +RTOS functions. It may also be included in C source files. + +Xtensa RTOS ports support all RTOS-compatible configurations of the Xtensa +architecture, using the Xtensa hardware abstraction layer (HAL) to deal +with configuration specifics. + +Should be included by all Xtensa generic and RTOS port-specific sources. + +*******************************************************************************/ + +#ifndef XTENSA_RTOS_H +#define XTENSA_RTOS_H + +#ifdef __ASSEMBLER__ +#include +#else +#include +#endif + +#include +#include +#ifndef RIOT_OS +#include +#endif +#define XT_BOARD 1 + +/* +Include any RTOS specific definitions that are needed by this header. +*/ +#ifndef RIOT_OS +#include +#endif + +/* +Convert FreeRTOSConfig definitions to XTENSA definitions. +However these can still be overridden from the command line. +*/ + +#ifndef XT_SIMULATOR + #if configXT_SIMULATOR + #define XT_SIMULATOR 1 /* Simulator mode */ + #endif +#endif + +#ifndef XT_BOARD + #if configXT_BOARD + #define XT_BOARD 1 /* Board mode */ + #endif +#endif + +#ifndef XT_TIMER_INDEX + #if defined configXT_TIMER_INDEX + #define XT_TIMER_INDEX configXT_TIMER_INDEX /* Index of hardware timer to be used */ + #endif +#endif + +#ifndef XT_INTEXC_HOOKS + #if configXT_INTEXC_HOOKS + #define XT_INTEXC_HOOKS 1 /* Enables exception hooks */ + #endif +#endif + +#if (!XT_SIMULATOR) && (!XT_BOARD) + #error Either XT_SIMULATOR or XT_BOARD must be defined. +#endif + + +/* +Name of RTOS (for messages). +*/ +#define XT_RTOS_NAME RIOT-OS + +/* +Check some Xtensa configuration requirements and report error if not met. +Error messages can be customize to the RTOS port. +*/ + +#if !XCHAL_HAVE_XEA2 +#error "RIOT-OS/Xtensa requires XEA2 (exception architecture 2)." +#endif + + +/******************************************************************************* + +RTOS CALLOUT MACROS MAPPED TO RTOS PORT-SPECIFIC FUNCTIONS. + +Define callout macros used in generic Xtensa code to interact with the RTOS. +The macros are simply the function names for use in calls from assembler code. +Some of these functions may call back to generic functions in xtensa_context.h . + +*******************************************************************************/ + +/* +Inform RTOS of entry into an interrupt handler that will affect it. +Allows RTOS to manage switch to any system stack and count nesting level. +Called after minimal context has been saved, with interrupts disabled. +RTOS port can call0 _xt_context_save to save the rest of the context. +May only be called from assembly code by the 'call0' instruction. +*/ +// void XT_RTOS_INT_ENTER(void) +#define XT_RTOS_INT_ENTER _frxt_int_enter + +/* +Inform RTOS of completion of an interrupt handler, and give control to +RTOS to perform thread/task scheduling, switch back from any system stack +and restore the context, and return to the exit dispatcher saved in the +stack frame at XT_STK_EXIT. RTOS port can call0 _xt_context_restore +to save the context saved in XT_RTOS_INT_ENTER via _xt_context_save, +leaving only a minimal part of the context to be restored by the exit +dispatcher. This function does not return to the place it was called from. +May only be called from assembly code by the 'call0' instruction. +*/ +// void XT_RTOS_INT_EXIT(void) +#define XT_RTOS_INT_EXIT _frxt_int_exit + +/* +Inform RTOS of the occurrence of a tick timer interrupt. +If RTOS has no tick timer, leave XT_RTOS_TIMER_INT undefined. +May be coded in or called from C or assembly, per ABI conventions. +RTOS may optionally define XT_TICK_PER_SEC in its own way (eg. macro). +*/ +// void XT_RTOS_TIMER_INT(void) +#define XT_RTOS_TIMER_INT _frxt_timer_int +#ifndef RIOT_OS + #define XT_TICK_PER_SEC configTICK_RATE_HZ +#endif + +/* +Return in a15 the base address of the co-processor state save area for the +thread that triggered a co-processor exception, or 0 if no thread was running. +The state save area is structured as defined in xtensa_context.h and has size +XT_CP_SIZE. Co-processor instructions should only be used in thread code, never +in interrupt handlers or the RTOS kernel. May only be called from assembly code +and by the 'call0' instruction. A result of 0 indicates an unrecoverable error. +The implementation may use only a2-4, a15 (all other regs must be preserved). +*/ +// void* XT_RTOS_CP_STATE(void) +#define XT_RTOS_CP_STATE _frxt_task_coproc_state + + +/******************************************************************************* + +HOOKS TO DYNAMICALLY INSTALL INTERRUPT AND EXCEPTION HANDLERS PER LEVEL. + +This Xtensa RTOS port provides hooks for dynamically installing exception +and interrupt handlers to facilitate automated testing where each test +case can install its own handler for user exceptions and each interrupt +priority (level). This consists of an array of function pointers indexed +by interrupt priority, with index 0 being the user exception handler hook. +Each entry in the array is initially 0, and may be replaced by a function +pointer of type XT_INTEXC_HOOK. A handler may be uninstalled by installing 0. + +The handler for low and medium priority obeys ABI conventions so may be coded +in C. For the exception handler, the cause is the contents of the EXCCAUSE +reg, and the result is -1 if handled, else the cause (still needs handling). +For interrupt handlers, the cause is a mask of pending enabled interrupts at +that level, and the result is the same mask with the bits for the handled +interrupts cleared (those not cleared still need handling). This allows a test +case to either pre-handle or override the default handling for the exception +or interrupt level (see xtensa_vectors.S). + +High priority handlers (including NMI) must be coded in assembly, are always +called by 'call0' regardless of ABI, must preserve all registers except a0, +and must not use or modify the interrupted stack. The hook argument 'cause' +is not passed and the result is ignored, so as not to burden the caller with +saving and restoring a2 (it assumes only one interrupt per level - see the +discussion in high priority interrupts in xtensa_vectors.S). The handler +therefore should be coded to prototype 'void h(void)' even though it plugs +into an array of handlers of prototype 'unsigned h(unsigned)'. + +To enable interrupt/exception hooks, compile the RTOS with '-DXT_INTEXC_HOOKS'. + +*******************************************************************************/ + +#define XT_INTEXC_HOOK_NUM (1 + XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI) + +#ifndef __ASSEMBLER__ +typedef unsigned (*XT_INTEXC_HOOK)(unsigned cause); +extern volatile XT_INTEXC_HOOK _xt_intexc_hooks[XT_INTEXC_HOOK_NUM]; +#endif + + +/******************************************************************************* + +CONVENIENCE INCLUSIONS. + +Ensures RTOS specific files need only include this one Xtensa-generic header. +These headers are included last so they can use the RTOS definitions above. + +*******************************************************************************/ + +#include "xtensa_context.h" + +#ifdef XT_RTOS_TIMER_INT +#include "xtensa_timer.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + + +/******************************************************************************* + +Xtensa Port Version. + +*******************************************************************************/ + +#define XTENSA_PORT_VERSION 1.5 +#define XTENSA_PORT_VERSION_STRING "1.5" + +#ifdef __cplusplus +} +#endif + +#endif /* XTENSA_RTOS_H */ diff --git a/cpu/esp32/xtensa/xtensa_timer.h b/cpu/esp32/xtensa/xtensa_timer.h new file mode 100644 index 0000000000000..28141a49d1ab0 --- /dev/null +++ b/cpu/esp32/xtensa/xtensa_timer.h @@ -0,0 +1,170 @@ +/******************************************************************************* +// Copyright (c) 2003-2015 Cadence Design Systems, Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------- + + XTENSA INFORMATION FOR RTOS TICK TIMER AND CLOCK FREQUENCY + +This header contains definitions and macros for use primarily by Xtensa +RTOS assembly coded source files. It includes and uses the Xtensa hardware +abstraction layer (HAL) to deal with config specifics. It may also be +included in C source files. + +User may edit to modify timer selection and to specify clock frequency and +tick duration to match timer interrupt to the real-time tick duration. + +If the RTOS has no timer interrupt, then there is no tick timer and the +clock frequency is irrelevant, so all of these macros are left undefined +and the Xtensa core configuration need not have a timer. + +*******************************************************************************/ + +#ifndef XTENSA_TIMER_H +#define XTENSA_TIMER_H + +#ifdef __ASSEMBLER__ +#include +#endif + +#include +#include + +#ifndef RIOT_OS +#include "xtensa_rtos.h" /* in case this wasn't included directly */ +#include +#endif /* ifndef RIOT_OS */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* +Select timer to use for periodic tick, and determine its interrupt number +and priority. User may specify a timer by defining XT_TIMER_INDEX with -D, +in which case its validity is checked (it must exist in this core and must +not be on a high priority interrupt - an error will be reported in invalid). +Otherwise select the first low or medium priority interrupt timer available. +*/ +#if XCHAL_NUM_TIMERS == 0 + + #error "This Xtensa configuration is unsupported, it has no timers." + +#else + +#ifndef XT_TIMER_INDEX + #if XCHAL_TIMER3_INTERRUPT != XTHAL_TIMER_UNCONFIGURED + #if XCHAL_INT_LEVEL(XCHAL_TIMER3_INTERRUPT) <= XCHAL_EXCM_LEVEL + #undef XT_TIMER_INDEX + #define XT_TIMER_INDEX 3 + #endif + #endif + #if XCHAL_TIMER2_INTERRUPT != XTHAL_TIMER_UNCONFIGURED + #if XCHAL_INT_LEVEL(XCHAL_TIMER2_INTERRUPT) <= XCHAL_EXCM_LEVEL + #undef XT_TIMER_INDEX + #define XT_TIMER_INDEX 2 + #endif + #endif + #if XCHAL_TIMER1_INTERRUPT != XTHAL_TIMER_UNCONFIGURED + #if XCHAL_INT_LEVEL(XCHAL_TIMER1_INTERRUPT) <= XCHAL_EXCM_LEVEL + #undef XT_TIMER_INDEX + #define XT_TIMER_INDEX 1 + #endif + #endif + #if XCHAL_TIMER0_INTERRUPT != XTHAL_TIMER_UNCONFIGURED + #if XCHAL_INT_LEVEL(XCHAL_TIMER0_INTERRUPT) <= XCHAL_EXCM_LEVEL + #undef XT_TIMER_INDEX + #define XT_TIMER_INDEX 0 + #endif + #endif +#endif +#ifndef XT_TIMER_INDEX + #error "There is no suitable timer in this Xtensa configuration." +#endif + +#define XT_CCOMPARE (CCOMPARE + XT_TIMER_INDEX) +#define XT_TIMER_INTNUM XCHAL_TIMER_INTERRUPT(XT_TIMER_INDEX) +#define XT_TIMER_INTPRI XCHAL_INT_LEVEL(XT_TIMER_INTNUM) +#define XT_TIMER_INTEN (1 << XT_TIMER_INTNUM) + +#if XT_TIMER_INTNUM == XTHAL_TIMER_UNCONFIGURED + #error "The timer selected by XT_TIMER_INDEX does not exist in this core." +#elif XT_TIMER_INTPRI > XCHAL_EXCM_LEVEL + #error "The timer interrupt cannot be high priority (use medium or low)." +#endif + +#endif /* XCHAL_NUM_TIMERS */ + +#ifndef RIOT_OS +/* +Set processor clock frequency, used to determine clock divisor for timer tick. +User should BE SURE TO ADJUST THIS for the Xtensa platform being used. +If using a supported board via the board-independent API defined in xtbsp.h, +this may be left undefined and frequency and tick divisor will be computed +and cached during run-time initialization. + +NOTE ON SIMULATOR: +Under the Xtensa instruction set simulator, the frequency can only be estimated +because it depends on the speed of the host and the version of the simulator. +Also because it runs much slower than hardware, it is not possible to achieve +real-time performance for most applications under the simulator. A frequency +too low does not allow enough time between timer interrupts, starving threads. +To obtain a more convenient but non-real-time tick duration on the simulator, +compile with xt-xcc option "-DXT_SIMULATOR". +Adjust this frequency to taste (it's not real-time anyway!). +*/ +#if defined(XT_SIMULATOR) && !defined(XT_CLOCK_FREQ) +#define XT_CLOCK_FREQ configCPU_CLOCK_HZ +#endif + +#if !defined(XT_CLOCK_FREQ) && !defined(XT_BOARD) + #error "XT_CLOCK_FREQ must be defined for the target platform." +#endif + +/* +Default number of timer "ticks" per second (default 100 for 10ms tick). +RTOS may define this in its own way (if applicable) in xtensa_rtos.h. +User may redefine this to an optimal value for the application, either by +editing this here or in xtensa_rtos.h, or compiling with xt-xcc option +"-DXT_TICK_PER_SEC=" where is a suitable number. +*/ + +#ifndef XT_TICK_PER_SEC +#define XT_TICK_PER_SEC configTICK_RATE_HZ /* 10 ms tick = 100 ticks per second */ +#endif + +/* +Derivation of clock divisor for timer tick and interrupt (one per tick). +*/ +#ifdef XT_CLOCK_FREQ +#define XT_TICK_DIVISOR (XT_CLOCK_FREQ / XT_TICK_PER_SEC) +#endif +#endif /* ifndef RIOT_OS */ + +#ifndef __ASSEMBLER__ +extern unsigned _xt_tick_divisor; +extern void _xt_tick_divisor_init(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* XTENSA_TIMER_H */ diff --git a/cpu/esp32/xtensa/xtensa_vectors.S b/cpu/esp32/xtensa/xtensa_vectors.S new file mode 100644 index 0000000000000..39ea5b00e0210 --- /dev/null +++ b/cpu/esp32/xtensa/xtensa_vectors.S @@ -0,0 +1,1945 @@ +/******************************************************************************* +Copyright (c) 2006-2015 Cadence Design Systems Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------- + + XTENSA VECTORS AND LOW LEVEL HANDLERS FOR AN RTOS + + Xtensa low level exception and interrupt vectors and handlers for an RTOS. + + Interrupt handlers and user exception handlers support interaction with + the RTOS by calling XT_RTOS_INT_ENTER and XT_RTOS_INT_EXIT before and + after user's specific interrupt handlers. These macros are defined in + xtensa_.h to call suitable functions in a specific RTOS. + + Users can install application-specific interrupt handlers for low and + medium level interrupts, by calling xt_set_interrupt_handler(). These + handlers can be written in C, and must obey C calling convention. The + handler table is indexed by the interrupt number. Each handler may be + provided with an argument. + + Note that the system timer interrupt is handled specially, and is + dispatched to the RTOS-specific handler. This timer cannot be hooked + by application code. + + Optional hooks are also provided to install a handler per level at + run-time, made available by compiling this source file with + '-DXT_INTEXC_HOOKS' (useful for automated testing). + +!! This file is a template that usually needs to be modified to handle !! +!! application specific interrupts. Search USER_EDIT for helpful comments !! +!! on where to insert handlers and how to write them. !! + + Users can also install application-specific exception handlers in the + same way, by calling xt_set_exception_handler(). One handler slot is + provided for each exception type. Note that some exceptions are handled + by the porting layer itself, and cannot be taken over by application + code in this manner. These are the alloca, syscall, and coprocessor + exceptions. + + The exception handlers can be written in C, and must follow C calling + convention. Each handler is passed a pointer to an exception frame as + its single argument. The exception frame is created on the stack, and + holds the saved context of the thread that took the exception. If the + handler returns, the context will be restored and the instruction that + caused the exception will be retried. If the handler makes any changes + to the saved state in the exception frame, the changes will be applied + when restoring the context. + + Because Xtensa is a configurable architecture, this port supports all user + generated configurations (except restrictions stated in the release notes). + This is accomplished by conditional compilation using macros and functions + defined in the Xtensa HAL (hardware adaptation layer) for your configuration. + Only the relevant parts of this file will be included in your RTOS build. + For example, this file provides interrupt vector templates for all types and + all priority levels, but only the ones in your configuration are built. + + NOTES on the use of 'call0' for long jumps instead of 'j': + 1. This file should be assembled with the -mlongcalls option to xt-xcc. + 2. The -mlongcalls compiler option causes 'call0 dest' to be expanded to + a sequence 'l32r a0, dest' 'callx0 a0' which works regardless of the + distance from the call to the destination. The linker then relaxes + it back to 'call0 dest' if it determines that dest is within range. + This allows more flexibility in locating code without the performance + overhead of the 'l32r' literal data load in cases where the destination + is in range of 'call0'. There is an additional benefit in that 'call0' + has a longer range than 'j' due to the target being word-aligned, so + the 'l32r' sequence is less likely needed. + 3. The use of 'call0' with -mlongcalls requires that register a0 not be + live at the time of the call, which is always the case for a function + call but needs to be ensured if 'call0' is used as a jump in lieu of 'j'. + 4. This use of 'call0' is independent of the C function call ABI. + +*******************************************************************************/ + +#include "xtensa_context.h" + +#ifndef SDK_INT_HANDLING + +#include "xtensa_rtos.h" + +/* Enable stack backtrace across exception/interrupt - see below */ +#define XT_DEBUG_BACKTRACE 1 + + +/* +-------------------------------------------------------------------------------- + Defines used to access _xtos_interrupt_table. +-------------------------------------------------------------------------------- +*/ +#define XIE_HANDLER 0 +#define XIE_ARG 4 +#define XIE_SIZE 8 + +/* +-------------------------------------------------------------------------------- + Macro extract_msb - return the input with only the highest bit set. + + Input : "ain" - Input value, clobbered. + Output : "aout" - Output value, has only one bit set, MSB of "ain". + The two arguments must be different AR registers. +-------------------------------------------------------------------------------- +*/ + + .macro extract_msb aout ain +1: + addi \aout, \ain, -1 /* aout = ain - 1 */ + and \ain, \ain, \aout /* ain = ain & aout */ + bnez \ain, 1b /* repeat until ain == 0 */ + addi \aout, \aout, 1 /* return aout + 1 */ + .endm + +/* +-------------------------------------------------------------------------------- + Macro dispatch_c_isr - dispatch interrupts to user ISRs. + This will dispatch to user handlers (if any) that are registered in the + XTOS dispatch table (_xtos_interrupt_table). These handlers would have + been registered by calling _xtos_set_interrupt_handler(). There is one + exception - the timer interrupt used by the OS will not be dispatched + to a user handler - this must be handled by the caller of this macro. + + Level triggered and software interrupts are automatically deasserted by + this code. + + ASSUMPTIONS: + -- PS.INTLEVEL is set to "level" at entry + -- PS.EXCM = 0, C calling enabled + + NOTE: For CALL0 ABI, a12-a15 have not yet been saved. + + NOTE: This macro will use registers a0 and a2-a6. The arguments are: + level -- interrupt level + mask -- interrupt bitmask for this level +-------------------------------------------------------------------------------- +*/ + + .macro dispatch_c_isr level mask + + /* Get mask of pending, enabled interrupts at this level into a2. */ + +.L_xt_user_int_&level&: + rsr a2, INTENABLE + rsr a3, INTERRUPT + movi a4, \mask + and a2, a2, a3 + and a2, a2, a4 + beqz a2, 9f /* nothing to do */ + + /* This bit of code provides a nice debug backtrace in the debugger. + It does take a few more instructions, so undef XT_DEBUG_BACKTRACE + if you want to save the cycles. + */ + #if XT_DEBUG_BACKTRACE + #ifndef __XTENSA_CALL0_ABI__ + rsr a0, EPC_1 + \level - 1 /* return address */ + movi a4, 0xC0000000 /* constant with top 2 bits set (call size) */ + or a0, a0, a4 /* set top 2 bits */ + addx2 a0, a4, a0 /* clear top bit -- simulating call4 size */ + #endif + #endif + + #ifdef XT_INTEXC_HOOKS + /* Call interrupt hook if present to (pre)handle interrupts. */ + movi a4, _xt_intexc_hooks + l32i a4, a4, \level << 2 + beqz a4, 2f + #ifdef __XTENSA_CALL0_ABI__ + callx0 a4 + beqz a2, 9f + #else + mov a6, a2 + callx4 a4 + beqz a6, 9f + mov a2, a6 + #endif +2: + #endif + + /* Now look up in the dispatch table and call user ISR if any. */ + /* If multiple bits are set then MSB has highest priority. */ + + extract_msb a4, a2 /* a4 = MSB of a2, a2 trashed */ + + #ifdef XT_USE_SWPRI + /* Enable all interrupts at this level that are numerically higher + than the one we just selected, since they are treated as higher + priority. + */ + movi a3, \mask /* a3 = all interrupts at this level */ + add a2, a4, a4 /* a2 = a4 << 1 */ + addi a2, a2, -1 /* a2 = mask of 1's <= a4 bit */ + and a2, a2, a3 /* a2 = mask of all bits <= a4 at this level */ + movi a3, _xt_intdata + l32i a6, a3, 4 /* a6 = _xt_vpri_mask */ + neg a2, a2 + addi a2, a2, -1 /* a2 = mask to apply */ + and a5, a6, a2 /* mask off all bits <= a4 bit */ + s32i a5, a3, 4 /* update _xt_vpri_mask */ + rsr a3, INTENABLE + and a3, a3, a2 /* mask off all bits <= a4 bit */ + wsr a3, INTENABLE + rsil a3, \level - 1 /* lower interrupt level by 1 */ + #endif + + movi a3, XT_TIMER_INTEN /* a3 = timer interrupt bit */ + wsr a4, INTCLEAR /* clear sw or edge-triggered interrupt */ + + #ifndef RIOT_OS /* we use it as hardware timer in RIOT OS */ + beq a3, a4, 7f /* if timer interrupt then skip table */ + #endif + + find_ms_setbit a3, a4, a3, 0 /* a3 = interrupt number */ + +#if SDK_USED /* _xtos_interrupt_table is in reverse order */ + movi a4, XCHAL_NUM_INTERRUPTS /* intnum = XCHAL_NUM_INTERRUPTS - intnum */ + sub a3, a4, a3 +#endif + movi a4, _xt_interrupt_table + addx8 a3, a3, a4 /* a3 = address of interrupt table entry */ + l32i a4, a3, XIE_HANDLER /* a4 = handler address */ + #ifdef __XTENSA_CALL0_ABI__ + mov a12, a6 /* save in callee-saved reg */ + l32i a2, a3, XIE_ARG /* a2 = handler arg */ + callx0 a4 /* call handler */ + mov a2, a12 + #else + mov a2, a6 /* save in windowed reg */ + l32i a6, a3, XIE_ARG /* a6 = handler arg */ + callx4 a4 /* call handler */ + #endif + + #ifdef XT_USE_SWPRI + j 8f + #else + j .L_xt_user_int_&level& /* check for more interrupts */ + #endif + +7: + + .ifeq XT_TIMER_INTPRI - \level +.L_xt_user_int_timer_&level&: + /* + Interrupt handler for the RTOS tick timer if at this level. + We'll be reading the interrupt state again after this call + so no need to preserve any registers except a6 (vpri_mask). + */ + #ifdef __XTENSA_CALL0_ABI__ + mov a12, a6 + call0 XT_RTOS_TIMER_INT + mov a2, a12 + #else + mov a2, a6 + call4 XT_RTOS_TIMER_INT + #endif + .endif + + #ifdef XT_USE_SWPRI + j 8f + #else + j .L_xt_user_int_&level& /* check for more interrupts */ + #endif + + #ifdef XT_USE_SWPRI +8: + /* Restore old value of _xt_vpri_mask from a2. Also update INTENABLE from + virtual _xt_intenable which _could_ have changed during interrupt + processing. */ + + movi a3, _xt_intdata + l32i a4, a3, 0 /* a4 = _xt_intenable */ + s32i a2, a3, 4 /* update _xt_vpri_mask */ + and a4, a4, a2 /* a4 = masked intenable */ + wsr a4, INTENABLE /* update INTENABLE */ + #endif + +9: + /* done */ + + .endm + + +/* +-------------------------------------------------------------------------------- + Panic handler. + Should be reached by call0 (preferable) or jump only. If call0, a0 says where + from. If on simulator, display panic message and abort, else loop indefinitely. +-------------------------------------------------------------------------------- +*/ + + .text + .global _xt_panic + .type _xt_panic,@function + .align 4 + +_xt_panic: + #ifdef XT_SIMULATOR + addi a4, a0, -3 /* point to call0 */ + movi a3, _xt_panic_message + movi a2, SYS_log_msg + simcall + movi a2, SYS_gdb_abort + simcall + #else + rsil a2, XCHAL_EXCM_LEVEL /* disable all low & med ints */ +1: j 1b /* loop infinitely */ + #endif + + .section .rodata, "a" + .align 4 + +_xt_panic_message: + .string "\n*** _xt_panic() was called from 0x%08x or jumped to. ***\n" + + +/* +-------------------------------------------------------------------------------- + Hooks to dynamically install handlers for exceptions and interrupts. + Allows automated regression frameworks to install handlers per test. + Consists of an array of function pointers indexed by interrupt level, + with index 0 containing the entry for user exceptions. + Initialized with all 0s, meaning no handler is installed at each level. + See comment in xtensa_rtos.h for more details. +-------------------------------------------------------------------------------- +*/ + + #ifdef XT_INTEXC_HOOKS + .data + .global _xt_intexc_hooks + .type _xt_intexc_hooks,@object + .align 4 + +_xt_intexc_hooks: + .fill XT_INTEXC_HOOK_NUM, 4, 0 + #endif + + +/* +-------------------------------------------------------------------------------- + EXCEPTION AND LEVEL 1 INTERRUPT VECTORS AND LOW LEVEL HANDLERS + (except window exception vectors). + + Each vector goes at a predetermined location according to the Xtensa + hardware configuration, which is ensured by its placement in a special + section known to the Xtensa linker support package (LSP). It performs + the minimum necessary before jumping to the handler in the .text section. + + The corresponding handler goes in the normal .text section. It sets up + the appropriate stack frame, saves a few vector-specific registers and + calls XT_RTOS_INT_ENTER to save the rest of the interrupted context + and enter the RTOS, then sets up a C environment. It then calls the + user's interrupt handler code (which may be coded in C) and finally + calls XT_RTOS_INT_EXIT to transfer control to the RTOS for scheduling. + + While XT_RTOS_INT_EXIT does not return directly to the interruptee, + eventually the RTOS scheduler will want to dispatch the interrupted + task or handler. The scheduler will return to the exit point that was + saved in the interrupt stack frame at XT_STK_EXIT. +-------------------------------------------------------------------------------- +*/ + + +/* +-------------------------------------------------------------------------------- +Debug Exception. +-------------------------------------------------------------------------------- +*/ + +#if XCHAL_HAVE_DEBUG + + .begin literal_prefix .DebugExceptionVector + .section .DebugExceptionVector.text, "ax" + .global _DebugExceptionVector + .literal_position + .align 4 + +_DebugExceptionVector: + + #ifdef XT_SIMULATOR + /* + In the simulator, let the debugger (if any) handle the debug exception, + or simply stop the simulation: + */ + wsr a2, EXCSAVE+XCHAL_DEBUGLEVEL /* save a2 where sim expects it */ + movi a2, SYS_gdb_enter_sktloop + simcall /* have ISS handle debug exc. */ + #elif 0 /* change condition to 1 to use the HAL minimal debug handler */ + wsr a3, EXCSAVE+XCHAL_DEBUGLEVEL + movi a3, xthal_debugexc_defhndlr_nw /* use default debug handler */ + jx a3 + #else + wsr a0, EXCSAVE+XCHAL_DEBUGLEVEL /* save original a0 somewhere */ + call0 _xt_panic /* does not return */ + rfi XCHAL_DEBUGLEVEL /* make a0 point here not later */ + #endif + + .end literal_prefix + +#endif + +/* +-------------------------------------------------------------------------------- +Double Exception. +Double exceptions are not a normal occurrence. They indicate a bug of some kind. +-------------------------------------------------------------------------------- +*/ + +#ifdef XCHAL_DOUBLEEXC_VECTOR_VADDR + + .begin literal_prefix .DoubleExceptionVector + .section .DoubleExceptionVector.text, "ax" + .global _DoubleExceptionVector + .literal_position + .align 4 + +_DoubleExceptionVector: + + #if XCHAL_HAVE_DEBUG + break 1, 4 /* unhandled double exception */ + #endif + call0 _xt_panic /* does not return */ + rfde /* make a0 point here not later */ + + .end literal_prefix + +#endif /* XCHAL_DOUBLEEXC_VECTOR_VADDR */ + +/* +-------------------------------------------------------------------------------- +Kernel Exception (including Level 1 Interrupt from kernel mode). +-------------------------------------------------------------------------------- +*/ + + .begin literal_prefix .KernelExceptionVector + .section .KernelExceptionVector.text, "ax" + .global _KernelExceptionVector + .literal_position + .align 4 + +_KernelExceptionVector: + + wsr a0, EXCSAVE_1 /* preserve a0 */ + call0 _xt_kernel_exc /* kernel exception handler */ + /* never returns here - call0 is used as a jump (see note at top) */ + + .end literal_prefix + + .text + .literal_position + .align 4 + +_xt_kernel_exc: + #if XCHAL_HAVE_DEBUG + break 1, 0 /* unhandled kernel exception */ + #endif + call0 _xt_panic /* does not return */ + rfe /* make a0 point here not there */ + + +/* +-------------------------------------------------------------------------------- +User Exception (including Level 1 Interrupt from user mode). +-------------------------------------------------------------------------------- +*/ + + .begin literal_prefix .UserExceptionVector + .section .UserExceptionVector.text, "ax" + .global _UserExceptionVector + .type _UserExceptionVector,@function + .literal_position + .align 4 + +_UserExceptionVector: + + wsr a0, EXCSAVE_1 /* preserve a0 */ + call0 _xt_user_exc /* user exception handler */ + /* never returns here - call0 is used as a jump (see note at top) */ + + .end literal_prefix + +/* +-------------------------------------------------------------------------------- + Insert some waypoints for jumping beyond the signed 8-bit range of + conditional branch instructions, so the conditional branchces to specific + exception handlers are not taken in the mainline. Saves some cycles in the + mainline. +-------------------------------------------------------------------------------- +*/ + + .text + + #if XCHAL_HAVE_WINDOWED + .align 4 +_xt_to_alloca_exc: + call0 _xt_alloca_exc /* in window vectors section */ + /* never returns here - call0 is used as a jump (see note at top) */ + #endif + + .align 4 +_xt_to_syscall_exc: + call0 _xt_syscall_exc + /* never returns here - call0 is used as a jump (see note at top) */ + + #if XCHAL_CP_NUM > 0 + .align 4 +_xt_to_coproc_exc: + call0 _xt_coproc_exc + /* never returns here - call0 is used as a jump (see note at top) */ + #endif + + +/* +-------------------------------------------------------------------------------- + User exception handler. +-------------------------------------------------------------------------------- +*/ + + .type _xt_user_exc,@function + .align 4 + +_xt_user_exc: + + /* If level 1 interrupt then jump to the dispatcher */ + rsr a0, EXCCAUSE + beqi a0, EXCCAUSE_LEVEL1INTERRUPT, _xt_lowint1 + + /* Handle any coprocessor exceptions. Rely on the fact that exception + numbers above EXCCAUSE_CP0_DISABLED all relate to the coprocessors. + */ + #if XCHAL_CP_NUM > 0 + bgeui a0, EXCCAUSE_CP0_DISABLED, _xt_to_coproc_exc + #endif + + /* Handle alloca and syscall exceptions */ + #if XCHAL_HAVE_WINDOWED + beqi a0, EXCCAUSE_ALLOCA, _xt_to_alloca_exc + #endif + beqi a0, EXCCAUSE_SYSCALL, _xt_to_syscall_exc + + /* Handle all other exceptions. All can have user-defined handlers. */ + /* NOTE: we'll stay on the user stack for exception handling. */ + + /* Allocate exception frame and save minimal context. */ + mov a0, sp + addi sp, sp, -XT_STK_FRMSZ + s32i a0, sp, XT_STK_A1 + #if XCHAL_HAVE_WINDOWED + s32e a0, sp, -12 /* for debug backtrace */ + #endif + rsr a0, PS /* save interruptee's PS */ + s32i a0, sp, XT_STK_PS + rsr a0, EPC_1 /* save interruptee's PC */ + s32i a0, sp, XT_STK_PC + rsr a0, EXCSAVE_1 /* save interruptee's a0 */ + s32i a0, sp, XT_STK_A0 + #if XCHAL_HAVE_WINDOWED + s32e a0, sp, -16 /* for debug backtrace */ + #endif + s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */ + s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */ + call0 _xt_context_save + + /* Save exc cause and vaddr into exception frame */ + rsr a0, EXCCAUSE + s32i a0, sp, XT_STK_EXCCAUSE + rsr a0, EXCVADDR + s32i a0, sp, XT_STK_EXCVADDR + + /* Set up PS for C, reenable hi-pri interrupts, and clear EXCM. */ + #ifdef __XTENSA_CALL0_ABI__ + movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM + #else + movi a0, PS_INTLEVEL(XCHAL_EXCM_LEVEL) | PS_UM | PS_WOE + #endif + wsr a0, PS + + #ifdef XT_DEBUG_BACKTRACE + #ifndef __XTENSA_CALL0_ABI__ + rsr a0, EPC_1 /* return address for debug backtrace */ + movi a5, 0xC0000000 /* constant with top 2 bits set (call size) */ + rsync /* wait for WSR.PS to complete */ + or a0, a0, a5 /* set top 2 bits */ + addx2 a0, a5, a0 /* clear top bit -- thus simulating call4 size */ + #else + rsync /* wait for WSR.PS to complete */ + #endif + #endif + + rsr a2, EXCCAUSE /* recover exc cause */ + + #ifdef XT_INTEXC_HOOKS + /* + Call exception hook to pre-handle exceptions (if installed). + Pass EXCCAUSE in a2, and check result in a2 (if -1, skip default handling). + */ + movi a4, _xt_intexc_hooks + l32i a4, a4, 0 /* user exception hook index 0 */ + beqz a4, 1f +.Ln_xt_user_exc_call_hook: + #ifdef __XTENSA_CALL0_ABI__ + callx0 a4 + beqi a2, -1, .L_xt_user_done + #else + mov a6, a2 + callx4 a4 + beqi a6, -1, .L_xt_user_done + mov a2, a6 + #endif +1: + #endif + + rsr a2, EXCCAUSE /* recover exc cause */ + movi a3, _xt_exception_table + addx4 a4, a2, a3 /* a4 = address of exception table entry */ + l32i a4, a4, 0 /* a4 = handler address */ + #ifdef __XTENSA_CALL0_ABI__ + mov a2, sp /* a2 = pointer to exc frame */ + callx0 a4 /* call handler */ + #else + mov a6, sp /* a6 = pointer to exc frame */ + callx4 a4 /* call handler */ + #endif + +.L_xt_user_done: + + /* Restore context and return */ + call0 _xt_context_restore + l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ + wsr a0, PS + l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ + wsr a0, EPC_1 + l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ + l32i sp, sp, XT_STK_A1 /* remove exception frame */ + rsync /* ensure PS and EPC written */ + rfe /* PS.EXCM is cleared */ + +#else + + .text + +#endif /* SDK_INT_HANDLING */ + +/* +-------------------------------------------------------------------------------- + Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT + on entry and used to return to a thread or interrupted interrupt handler. +-------------------------------------------------------------------------------- +*/ + + .global _xt_user_exit + .type _xt_user_exit,@function + .align 4 +_xt_user_exit: + l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ + wsr a0, PS + l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ + wsr a0, EPC_1 + l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ + l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ + rsync /* ensure PS and EPC written */ + rfe /* PS.EXCM is cleared */ + +#ifndef SDK_INT_HANDLING +/* +-------------------------------------------------------------------------------- +Syscall Exception Handler (jumped to from User Exception Handler). +Syscall 0 is required to spill the register windows (no-op in Call 0 ABI). +Only syscall 0 is handled here. Other syscalls return -1 to caller in a2. +-------------------------------------------------------------------------------- +*/ + + .text + .type _xt_syscall_exc,@function + .align 4 +_xt_syscall_exc: + + #ifdef __XTENSA_CALL0_ABI__ + /* + Save minimal regs for scratch. Syscall 0 does nothing in Call0 ABI. + Use a minimal stack frame (16B) to save A2 & A3 for scratch. + PS.EXCM could be cleared here, but unlikely to improve worst-case latency. + rsr a0, PS + addi a0, a0, -PS_EXCM_MASK + wsr a0, PS + */ + addi sp, sp, -16 + s32i a2, sp, 8 + s32i a3, sp, 12 + #else /* Windowed ABI */ + /* + Save necessary context and spill the register windows. + PS.EXCM is still set and must remain set until after the spill. + Reuse context save function though it saves more than necessary. + For this reason, a full interrupt stack frame is allocated. + */ + addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ + s32i a12, sp, XT_STK_A12 /* _xt_context_save requires A12- */ + s32i a13, sp, XT_STK_A13 /* A13 to have already been saved */ + call0 _xt_context_save + #endif + + /* + Grab the interruptee's PC and skip over the 'syscall' instruction. + If it's at the end of a zero-overhead loop and it's not on the last + iteration, decrement loop counter and skip to beginning of loop. + */ + rsr a2, EPC_1 /* a2 = PC of 'syscall' */ + addi a3, a2, 3 /* ++PC */ + #if XCHAL_HAVE_LOOPS + rsr a0, LEND /* if (PC == LEND */ + bne a3, a0, 1f + rsr a0, LCOUNT /* && LCOUNT != 0) */ + beqz a0, 1f /* { */ + addi a0, a0, -1 /* --LCOUNT */ + rsr a3, LBEG /* PC = LBEG */ + wsr a0, LCOUNT /* } */ + #endif +1: wsr a3, EPC_1 /* update PC */ + + /* Restore interruptee's context and return from exception. */ + #ifdef __XTENSA_CALL0_ABI__ + l32i a2, sp, 8 + l32i a3, sp, 12 + addi sp, sp, 16 + #else + call0 _xt_context_restore + addi sp, sp, XT_STK_FRMSZ + #endif + movi a0, -1 + movnez a2, a0, a2 /* return -1 if not syscall 0 */ + rsr a0, EXCSAVE_1 + rfe + +/* +-------------------------------------------------------------------------------- +Co-Processor Exception Handler (jumped to from User Exception Handler). +These exceptions are generated by co-processor instructions, which are only +allowed in thread code (not in interrupts or kernel code). This restriction is +deliberately imposed to reduce the burden of state-save/restore in interrupts. +-------------------------------------------------------------------------------- +*/ +#if XCHAL_CP_NUM > 0 + + .section .rodata, "a" + +/* Offset to CP n save area in thread's CP save area. */ + .global _xt_coproc_sa_offset + .type _xt_coproc_sa_offset,@object + .align 16 /* minimize crossing cache boundaries */ +_xt_coproc_sa_offset: + .word XT_CP0_SA, XT_CP1_SA, XT_CP2_SA, XT_CP3_SA + .word XT_CP4_SA, XT_CP5_SA, XT_CP6_SA, XT_CP7_SA + +/* Bitmask for CP n's CPENABLE bit. */ + .type _xt_coproc_mask,@object + .align 16,,8 /* try to keep it all in one cache line */ + .set i, 0 +_xt_coproc_mask: + .rept XCHAL_CP_MAX + .long (i<<16) | (1<= 2 + + .begin literal_prefix .Level2InterruptVector + .section .Level2InterruptVector.text, "ax" + .global _Level2Vector + .type _Level2Vector,@function + .literal_position + + .align 4 +_Level2Vector: + wsr a0, EXCSAVE_2 /* preserve a0 */ + call0 _xt_medint2 /* load interrupt handler */ + /* never returns here - call0 is used as a jump (see note at top) */ + + .end literal_prefix + + .text + .type _xt_medint2,@function + .align 4 +_xt_medint2: + mov a0, sp /* sp == a1 */ + addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ + s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ + rsr a0, EPS_2 /* save interruptee's PS */ + s32i a0, sp, XT_STK_PS + rsr a0, EPC_2 /* save interruptee's PC */ + s32i a0, sp, XT_STK_PC + rsr a0, EXCSAVE_2 /* save interruptee's a0 */ + s32i a0, sp, XT_STK_A0 + movi a0, _xt_medint2_exit /* save exit point for dispatch */ + s32i a0, sp, XT_STK_EXIT + + /* Save rest of interrupt context and enter RTOS. */ + call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ + + /* !! We are now on the RTOS system stack !! */ + + /* Set up PS for C, enable interrupts above this level and clear EXCM. */ + #ifdef __XTENSA_CALL0_ABI__ + movi a0, PS_INTLEVEL(2) | PS_UM + #else + movi a0, PS_INTLEVEL(2) | PS_UM | PS_WOE + #endif + wsr a0, PS + rsync + + /* OK to call C code at this point, dispatch user ISRs */ + + dispatch_c_isr 2 XCHAL_INTLEVEL2_MASK + + /* Done handling interrupts, transfer control to OS */ + call0 XT_RTOS_INT_EXIT /* does not return directly here */ + + /* + Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT + on entry and used to return to a thread or interrupted interrupt handler. + */ + .global _xt_medint2_exit + .type _xt_medint2_exit,@function + .align 4 +_xt_medint2_exit: + /* Restore only level-specific regs (the rest were already restored) */ + l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ + wsr a0, EPS_2 + l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ + wsr a0, EPC_2 + l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ + l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ + rsync /* ensure EPS and EPC written */ + rfi 2 + +#endif /* Level 2 */ + +#if XCHAL_EXCM_LEVEL >= 3 + + .begin literal_prefix .Level3InterruptVector + .section .Level3InterruptVector.text, "ax" + .global _Level3Vector + .type _Level3Vector,@function + .literal_position + + .align 4 +_Level3Vector: + wsr a0, EXCSAVE_3 /* preserve a0 */ + call0 _xt_medint3 /* load interrupt handler */ + /* never returns here - call0 is used as a jump (see note at top) */ + + .end literal_prefix + + .text + .type _xt_medint3,@function + .align 4 +_xt_medint3: + mov a0, sp /* sp == a1 */ + addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ + s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ + rsr a0, EPS_3 /* save interruptee's PS */ + s32i a0, sp, XT_STK_PS + rsr a0, EPC_3 /* save interruptee's PC */ + s32i a0, sp, XT_STK_PC + rsr a0, EXCSAVE_3 /* save interruptee's a0 */ + s32i a0, sp, XT_STK_A0 + movi a0, _xt_medint3_exit /* save exit point for dispatch */ + s32i a0, sp, XT_STK_EXIT + + /* Save rest of interrupt context and enter RTOS. */ + call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ + + /* !! We are now on the RTOS system stack !! */ + + /* Set up PS for C, enable interrupts above this level and clear EXCM. */ + #ifdef __XTENSA_CALL0_ABI__ + movi a0, PS_INTLEVEL(3) | PS_UM + #else + movi a0, PS_INTLEVEL(3) | PS_UM | PS_WOE + #endif + wsr a0, PS + rsync + + /* OK to call C code at this point, dispatch user ISRs */ + + dispatch_c_isr 3 XCHAL_INTLEVEL3_MASK + + /* Done handling interrupts, transfer control to OS */ + call0 XT_RTOS_INT_EXIT /* does not return directly here */ + + /* + Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT + on entry and used to return to a thread or interrupted interrupt handler. + */ + .global _xt_medint3_exit + .type _xt_medint3_exit,@function + .align 4 +_xt_medint3_exit: + /* Restore only level-specific regs (the rest were already restored) */ + l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ + wsr a0, EPS_3 + l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ + wsr a0, EPC_3 + l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ + l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ + rsync /* ensure EPS and EPC written */ + rfi 3 + +#endif /* Level 3 */ + +#if XCHAL_EXCM_LEVEL >= 4 + + .begin literal_prefix .Level4InterruptVector + .section .Level4InterruptVector.text, "ax" + .global _Level4Vector + .type _Level4Vector,@function + .literal_position + + .align 4 +_Level4Vector: + wsr a0, EXCSAVE_4 /* preserve a0 */ + call0 _xt_medint4 /* load interrupt handler */ + + .end literal_prefix + + .text + .type _xt_medint4,@function + .align 4 +_xt_medint4: + mov a0, sp /* sp == a1 */ + addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ + s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ + rsr a0, EPS_4 /* save interruptee's PS */ + s32i a0, sp, XT_STK_PS + rsr a0, EPC_4 /* save interruptee's PC */ + s32i a0, sp, XT_STK_PC + rsr a0, EXCSAVE_4 /* save interruptee's a0 */ + s32i a0, sp, XT_STK_A0 + movi a0, _xt_medint4_exit /* save exit point for dispatch */ + s32i a0, sp, XT_STK_EXIT + + /* Save rest of interrupt context and enter RTOS. */ + call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ + + /* !! We are now on the RTOS system stack !! */ + + /* Set up PS for C, enable interrupts above this level and clear EXCM. */ + #ifdef __XTENSA_CALL0_ABI__ + movi a0, PS_INTLEVEL(4) | PS_UM + #else + movi a0, PS_INTLEVEL(4) | PS_UM | PS_WOE + #endif + wsr a0, PS + rsync + + /* OK to call C code at this point, dispatch user ISRs */ + + dispatch_c_isr 4 XCHAL_INTLEVEL4_MASK + + /* Done handling interrupts, transfer control to OS */ + call0 XT_RTOS_INT_EXIT /* does not return directly here */ + + /* + Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT + on entry and used to return to a thread or interrupted interrupt handler. + */ + .global _xt_medint4_exit + .type _xt_medint4_exit,@function + .align 4 +_xt_medint4_exit: + /* Restore only level-specific regs (the rest were already restored) */ + l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ + wsr a0, EPS_4 + l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ + wsr a0, EPC_4 + l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ + l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ + rsync /* ensure EPS and EPC written */ + rfi 4 + +#endif /* Level 4 */ + +#if XCHAL_EXCM_LEVEL >= 5 + + .begin literal_prefix .Level5InterruptVector + .section .Level5InterruptVector.text, "ax" + .global _Level5Vector + .type _Level5Vector,@function + .literal_position + + .align 4 +_Level5Vector: + wsr a0, EXCSAVE_5 /* preserve a0 */ + call0 _xt_medint5 /* load interrupt handler */ + + .end literal_prefix + + .text + .type _xt_medint5,@function + .align 4 +_xt_medint5: + mov a0, sp /* sp == a1 */ + addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ + s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ + rsr a0, EPS_5 /* save interruptee's PS */ + s32i a0, sp, XT_STK_PS + rsr a0, EPC_5 /* save interruptee's PC */ + s32i a0, sp, XT_STK_PC + rsr a0, EXCSAVE_5 /* save interruptee's a0 */ + s32i a0, sp, XT_STK_A0 + movi a0, _xt_medint5_exit /* save exit point for dispatch */ + s32i a0, sp, XT_STK_EXIT + + /* Save rest of interrupt context and enter RTOS. */ + call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ + + /* !! We are now on the RTOS system stack !! */ + + /* Set up PS for C, enable interrupts above this level and clear EXCM. */ + #ifdef __XTENSA_CALL0_ABI__ + movi a0, PS_INTLEVEL(5) | PS_UM + #else + movi a0, PS_INTLEVEL(5) | PS_UM | PS_WOE + #endif + wsr a0, PS + rsync + + /* OK to call C code at this point, dispatch user ISRs */ + + dispatch_c_isr 5 XCHAL_INTLEVEL5_MASK + + /* Done handling interrupts, transfer control to OS */ + call0 XT_RTOS_INT_EXIT /* does not return directly here */ + + /* + Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT + on entry and used to return to a thread or interrupted interrupt handler. + */ + .global _xt_medint5_exit + .type _xt_medint5_exit,@function + .align 4 +_xt_medint5_exit: + /* Restore only level-specific regs (the rest were already restored) */ + l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ + wsr a0, EPS_5 + l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ + wsr a0, EPC_5 + l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ + l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ + rsync /* ensure EPS and EPC written */ + rfi 5 + +#endif /* Level 5 */ + +#if XCHAL_EXCM_LEVEL >= 6 + + .begin literal_prefix .Level6InterruptVector + .section .Level6InterruptVector.text, "ax" + .global _Level6Vector + .type _Level6Vector,@function + .literal_position + + .align 4 +_Level6Vector: + wsr a0, EXCSAVE_6 /* preserve a0 */ + call0 _xt_medint6 /* load interrupt handler */ + + .end literal_prefix + + .text + .type _xt_medint6,@function + .align 4 +_xt_medint6: + mov a0, sp /* sp == a1 */ + addi sp, sp, -XT_STK_FRMSZ /* allocate interrupt stack frame */ + s32i a0, sp, XT_STK_A1 /* save pre-interrupt SP */ + rsr a0, EPS_6 /* save interruptee's PS */ + s32i a0, sp, XT_STK_PS + rsr a0, EPC_6 /* save interruptee's PC */ + s32i a0, sp, XT_STK_PC + rsr a0, EXCSAVE_6 /* save interruptee's a0 */ + s32i a0, sp, XT_STK_A0 + movi a0, _xt_medint6_exit /* save exit point for dispatch */ + s32i a0, sp, XT_STK_EXIT + + /* Save rest of interrupt context and enter RTOS. */ + call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ + + /* !! We are now on the RTOS system stack !! */ + + /* Set up PS for C, enable interrupts above this level and clear EXCM. */ + #ifdef __XTENSA_CALL0_ABI__ + movi a0, PS_INTLEVEL(6) | PS_UM + #else + movi a0, PS_INTLEVEL(6) | PS_UM | PS_WOE + #endif + wsr a0, PS + rsync + + /* OK to call C code at this point, dispatch user ISRs */ + + dispatch_c_isr 6 XCHAL_INTLEVEL6_MASK + + /* Done handling interrupts, transfer control to OS */ + call0 XT_RTOS_INT_EXIT /* does not return directly here */ + + /* + Exit point for dispatch. Saved in interrupt stack frame at XT_STK_EXIT + on entry and used to return to a thread or interrupted interrupt handler. + */ + .global _xt_medint6_exit + .type _xt_medint6_exit,@function + .align 4 +_xt_medint6_exit: + /* Restore only level-specific regs (the rest were already restored) */ + l32i a0, sp, XT_STK_PS /* retrieve interruptee's PS */ + wsr a0, EPS_6 + l32i a0, sp, XT_STK_PC /* retrieve interruptee's PC */ + wsr a0, EPC_6 + l32i a0, sp, XT_STK_A0 /* retrieve interruptee's A0 */ + l32i sp, sp, XT_STK_A1 /* remove interrupt stack frame */ + rsync /* ensure EPS and EPC written */ + rfi 6 + +#endif /* Level 6 */ + + +/******************************************************************************* + +HIGH PRIORITY (LEVEL > XCHAL_EXCM_LEVEL) INTERRUPT VECTORS AND HANDLERS + +High priority interrupts are by definition those with priorities greater +than XCHAL_EXCM_LEVEL. This includes non-maskable (NMI). High priority +interrupts cannot interact with the RTOS, that is they must save all regs +they use and not call any RTOS function. + +A further restriction imposed by the Xtensa windowed architecture is that +high priority interrupts must not modify the stack area even logically +"above" the top of the interrupted stack (they need to provide their +own stack or static save area). + +Cadence Design Systems recommends high priority interrupt handlers be coded in assembly +and used for purposes requiring very short service times. + +Here are templates for high priority (level 2+) interrupt vectors. +They assume only one interrupt per level to avoid the burden of identifying +which interrupts at this level are pending and enabled. This allows for +minimum latency and avoids having to save/restore a2 in addition to a0. +If more than one interrupt per high priority level is configured, this burden +is on the handler which in any case must provide a way to save and restore +registers it uses without touching the interrupted stack. + +Each vector goes at a predetermined location according to the Xtensa +hardware configuration, which is ensured by its placement in a special +section known to the Xtensa linker support package (LSP). It performs +the minimum necessary before jumping to the handler in the .text section. + +*******************************************************************************/ + +/* +Currently only shells for high priority interrupt handlers are provided +here. However a template and example can be found in the Cadence Design Systems tools +documentation: "Microprocessor Programmer's Guide". +*/ + +#if XCHAL_NUM_INTLEVELS >=2 && XCHAL_EXCM_LEVEL <2 && XCHAL_DEBUGLEVEL !=2 + + .begin literal_prefix .Level2InterruptVector + .section .Level2InterruptVector.text, "ax" + .global _Level2Vector + .type _Level2Vector,@function + .literal_position + + .align 4 +_Level2Vector: + wsr a0, EXCSAVE_2 /* preserve a0 */ + call0 _xt_highint2 /* load interrupt handler */ + + .end literal_prefix + + .text + .type _xt_highint2,@function + .align 4 +_xt_highint2: + + #ifdef XT_INTEXC_HOOKS + /* Call interrupt hook if present to (pre)handle interrupts. */ + movi a0, _xt_intexc_hooks + l32i a0, a0, 2<<2 + beqz a0, 1f +.Ln_xt_highint2_call_hook: + callx0 a0 /* must NOT disturb stack! */ +1: + #endif + + /* USER_EDIT: + ADD HIGH PRIORITY LEVEL 2 INTERRUPT HANDLER CODE HERE. + */ + + .align 4 +.L_xt_highint2_exit: + rsr a0, EXCSAVE_2 /* restore a0 */ + rfi 2 + +#endif /* Level 2 */ + +#if XCHAL_NUM_INTLEVELS >=3 && XCHAL_EXCM_LEVEL <3 && XCHAL_DEBUGLEVEL !=3 + + .begin literal_prefix .Level3InterruptVector + .section .Level3InterruptVector.text, "ax" + .global _Level3Vector + .type _Level3Vector,@function + .literal_position + + .align 4 +_Level3Vector: + wsr a0, EXCSAVE_3 /* preserve a0 */ + call0 _xt_highint3 /* load interrupt handler */ + /* never returns here - call0 is used as a jump (see note at top) */ + + .end literal_prefix + + .text + .type _xt_highint3,@function + .align 4 +_xt_highint3: + + #ifdef XT_INTEXC_HOOKS + /* Call interrupt hook if present to (pre)handle interrupts. */ + movi a0, _xt_intexc_hooks + l32i a0, a0, 3<<2 + beqz a0, 1f +.Ln_xt_highint3_call_hook: + callx0 a0 /* must NOT disturb stack! */ +1: + #endif + + /* USER_EDIT: + ADD HIGH PRIORITY LEVEL 3 INTERRUPT HANDLER CODE HERE. + */ + + .align 4 +.L_xt_highint3_exit: + rsr a0, EXCSAVE_3 /* restore a0 */ + rfi 3 + +#endif /* Level 3 */ + +#if XCHAL_NUM_INTLEVELS >=4 && XCHAL_EXCM_LEVEL <4 && XCHAL_DEBUGLEVEL !=4 + + .begin literal_prefix .Level4InterruptVector + .section .Level4InterruptVector.text, "ax" + .global _Level4Vector + .type _Level4Vector,@function + .literal_position + + .align 4 +_Level4Vector: + wsr a0, EXCSAVE_4 /* preserve a0 */ + call0 _xt_highint4 /* load interrupt handler */ + /* never returns here - call0 is used as a jump (see note at top) */ + + .end literal_prefix + + .text + .type _xt_highint4,@function + .align 4 +_xt_highint4: + + #ifdef XT_INTEXC_HOOKS + /* Call interrupt hook if present to (pre)handle interrupts. */ + movi a0, _xt_intexc_hooks + l32i a0, a0, 4<<2 + beqz a0, 1f +.Ln_xt_highint4_call_hook: + callx0 a0 /* must NOT disturb stack! */ +1: + #endif + + /* USER_EDIT: + ADD HIGH PRIORITY LEVEL 4 INTERRUPT HANDLER CODE HERE. + */ + + .align 4 +.L_xt_highint4_exit: + rsr a0, EXCSAVE_4 /* restore a0 */ + rfi 4 + +#endif /* Level 4 */ + +#if XCHAL_NUM_INTLEVELS >=5 && XCHAL_EXCM_LEVEL <5 && XCHAL_DEBUGLEVEL !=5 + + .begin literal_prefix .Level5InterruptVector + .section .Level5InterruptVector.text, "ax" + .global _Level5Vector + .type _Level5Vector,@function + .literal_position + + .align 4 +_Level5Vector: + wsr a0, EXCSAVE_5 /* preserve a0 */ + call0 _xt_highint5 /* load interrupt handler */ + /* never returns here - call0 is used as a jump (see note at top) */ + + .end literal_prefix + + .text + .type _xt_highint5,@function + .align 4 +_xt_highint5: + + #ifdef XT_INTEXC_HOOKS + /* Call interrupt hook if present to (pre)handle interrupts. */ + movi a0, _xt_intexc_hooks + l32i a0, a0, 5<<2 + beqz a0, 1f +.Ln_xt_highint5_call_hook: + callx0 a0 /* must NOT disturb stack! */ +1: + #endif + + /* USER_EDIT: + ADD HIGH PRIORITY LEVEL 5 INTERRUPT HANDLER CODE HERE. + */ + + .align 4 +.L_xt_highint5_exit: + rsr a0, EXCSAVE_5 /* restore a0 */ + rfi 5 + +#endif /* Level 5 */ + +#if XCHAL_NUM_INTLEVELS >=6 && XCHAL_EXCM_LEVEL <6 && XCHAL_DEBUGLEVEL !=6 + + .begin literal_prefix .Level6InterruptVector + .section .Level6InterruptVector.text, "ax" + .global _Level6Vector + .type _Level6Vector,@function + .literal_position + + .align 4 +_Level6Vector: + wsr a0, EXCSAVE_6 /* preserve a0 */ + call0 _xt_highint6 /* load interrupt handler */ + /* never returns here - call0 is used as a jump (see note at top) */ + + .end literal_prefix + + .text + .type _xt_highint6,@function + .align 4 +_xt_highint6: + + #ifdef XT_INTEXC_HOOKS + /* Call interrupt hook if present to (pre)handle interrupts. */ + movi a0, _xt_intexc_hooks + l32i a0, a0, 6<<2 + beqz a0, 1f +.Ln_xt_highint6_call_hook: + callx0 a0 /* must NOT disturb stack! */ +1: + #endif + + /* USER_EDIT: + ADD HIGH PRIORITY LEVEL 6 INTERRUPT HANDLER CODE HERE. + */ + + .align 4 +.L_xt_highint6_exit: + rsr a0, EXCSAVE_6 /* restore a0 */ + rfi 6 + +#endif /* Level 6 */ + +#if XCHAL_HAVE_NMI + + .begin literal_prefix .NMIExceptionVector + .section .NMIExceptionVector.text, "ax" + .global _NMIExceptionVector + .type _NMIExceptionVector,@function + .literal_position + + .align 4 +_NMIExceptionVector: + wsr a0, EXCSAVE + XCHAL_NMILEVEL _ /* preserve a0 */ + call0 _xt_nmi /* load interrupt handler */ + /* never returns here - call0 is used as a jump (see note at top) */ + + .end literal_prefix + + .text + .type _xt_nmi,@function + .align 4 +_xt_nmi: + + #ifdef XT_INTEXC_HOOKS + /* Call interrupt hook if present to (pre)handle interrupts. */ + movi a0, _xt_intexc_hooks + l32i a0, a0, XCHAL_NMILEVEL<<2 + beqz a0, 1f +.Ln_xt_nmi_call_hook: + callx0 a0 /* must NOT disturb stack! */ +1: + #endif + + /* USER_EDIT: + ADD HIGH PRIORITY NON-MASKABLE INTERRUPT (NMI) HANDLER CODE HERE. + */ + + .align 4 +.L_xt_nmi_exit: + rsr a0, EXCSAVE + XCHAL_NMILEVEL /* restore a0 */ + rfi XCHAL_NMILEVEL + +#endif /* NMI */ + + +/******************************************************************************* + +WINDOW OVERFLOW AND UNDERFLOW EXCEPTION VECTORS AND ALLOCA EXCEPTION HANDLER + +Here is the code for each window overflow/underflow exception vector and +(interspersed) efficient code for handling the alloca exception cause. +Window exceptions are handled entirely in the vector area and are very +tight for performance. The alloca exception is also handled entirely in +the window vector area so comes at essentially no cost in code size. +Users should never need to modify them and Cadence Design Systems recommends +they do not. + +Window handlers go at predetermined vector locations according to the +Xtensa hardware configuration, which is ensured by their placement in a +special section known to the Xtensa linker support package (LSP). Since +their offsets in that section are always the same, the LSPs do not define +a section per vector. + +These things are coded for XEA2 only (XEA1 is not supported). + +Note on Underflow Handlers: +The underflow handler for returning from call[i+1] to call[i] +must preserve all the registers from call[i+1]'s window. +In particular, a0 and a1 must be preserved because the RETW instruction +will be reexecuted (and may even underflow if an intervening exception +has flushed call[i]'s registers). +Registers a2 and up may contain return values. + +*******************************************************************************/ + +#if XCHAL_HAVE_WINDOWED + + .section .WindowVectors.text, "ax" + +/* +-------------------------------------------------------------------------------- +Window Overflow Exception for Call4. + +Invoked if a call[i] referenced a register (a4-a15) +that contains data from ancestor call[j]; +call[j] had done a call4 to call[j+1]. +On entry here: + window rotated to call[j] start point; + a0-a3 are registers to be saved; + a4-a15 must be preserved; + a5 is call[j+1]'s stack pointer. +-------------------------------------------------------------------------------- +*/ + + .org 0x0 + .global _WindowOverflow4 +_WindowOverflow4: + + s32e a0, a5, -16 /* save a0 to call[j+1]'s stack frame */ + s32e a1, a5, -12 /* save a1 to call[j+1]'s stack frame */ + s32e a2, a5, -8 /* save a2 to call[j+1]'s stack frame */ + s32e a3, a5, -4 /* save a3 to call[j+1]'s stack frame */ + rfwo /* rotates back to call[i] position */ + +/* +-------------------------------------------------------------------------------- +Window Underflow Exception for Call4 + +Invoked by RETW returning from call[i+1] to call[i] +where call[i]'s registers must be reloaded (not live in ARs); +where call[i] had done a call4 to call[i+1]. +On entry here: + window rotated to call[i] start point; + a0-a3 are undefined, must be reloaded with call[i].reg[0..3]; + a4-a15 must be preserved (they are call[i+1].reg[0..11]); + a5 is call[i+1]'s stack pointer. +-------------------------------------------------------------------------------- +*/ + + .org 0x40 + .global _WindowUnderflow4 +_WindowUnderflow4: + + l32e a0, a5, -16 /* restore a0 from call[i+1]'s stack frame */ + l32e a1, a5, -12 /* restore a1 from call[i+1]'s stack frame */ + l32e a2, a5, -8 /* restore a2 from call[i+1]'s stack frame */ + l32e a3, a5, -4 /* restore a3 from call[i+1]'s stack frame */ + rfwu + +/* +-------------------------------------------------------------------------------- +Handle alloca exception generated by interruptee executing 'movsp'. +This uses space between the window vectors, so is essentially "free". +All interruptee's regs are intact except a0 which is saved in EXCSAVE_1, +and PS.EXCM has been set by the exception hardware (can't be interrupted). +The fact the alloca exception was taken means the registers associated with +the base-save area have been spilled and will be restored by the underflow +handler, so those 4 registers are available for scratch. +The code is optimized to avoid unaligned branches and minimize cache misses. +-------------------------------------------------------------------------------- +*/ + + .align 4 + .global _xt_alloca_exc +_xt_alloca_exc: + + rsr a0, WINDOWBASE /* grab WINDOWBASE before rotw changes it */ + rotw -1 /* WINDOWBASE goes to a4, new a0-a3 are scratch */ + rsr a2, PS + extui a3, a2, XCHAL_PS_OWB_SHIFT, XCHAL_PS_OWB_BITS + xor a3, a3, a4 /* bits changed from old to current windowbase */ + rsr a4, EXCSAVE_1 /* restore original a0 (now in a4) */ + slli a3, a3, XCHAL_PS_OWB_SHIFT + xor a2, a2, a3 /* flip changed bits in old window base */ + wsr a2, PS /* update PS.OWB to new window base */ + rsync + + _bbci.l a4, 31, _WindowUnderflow4 + rotw -1 /* original a0 goes to a8 */ + _bbci.l a8, 30, _WindowUnderflow8 + rotw -1 + j _WindowUnderflow12 + +/* +-------------------------------------------------------------------------------- +Window Overflow Exception for Call8 + +Invoked if a call[i] referenced a register (a4-a15) +that contains data from ancestor call[j]; +call[j] had done a call8 to call[j+1]. +On entry here: + window rotated to call[j] start point; + a0-a7 are registers to be saved; + a8-a15 must be preserved; + a9 is call[j+1]'s stack pointer. +-------------------------------------------------------------------------------- +*/ + + .org 0x80 + .global _WindowOverflow8 +_WindowOverflow8: + + s32e a0, a9, -16 /* save a0 to call[j+1]'s stack frame */ + l32e a0, a1, -12 /* a0 <- call[j-1]'s sp + (used to find end of call[j]'s frame) */ + s32e a1, a9, -12 /* save a1 to call[j+1]'s stack frame */ + s32e a2, a9, -8 /* save a2 to call[j+1]'s stack frame */ + s32e a3, a9, -4 /* save a3 to call[j+1]'s stack frame */ + s32e a4, a0, -32 /* save a4 to call[j]'s stack frame */ + s32e a5, a0, -28 /* save a5 to call[j]'s stack frame */ + s32e a6, a0, -24 /* save a6 to call[j]'s stack frame */ + s32e a7, a0, -20 /* save a7 to call[j]'s stack frame */ + rfwo /* rotates back to call[i] position */ + +/* +-------------------------------------------------------------------------------- +Window Underflow Exception for Call8 + +Invoked by RETW returning from call[i+1] to call[i] +where call[i]'s registers must be reloaded (not live in ARs); +where call[i] had done a call8 to call[i+1]. +On entry here: + window rotated to call[i] start point; + a0-a7 are undefined, must be reloaded with call[i].reg[0..7]; + a8-a15 must be preserved (they are call[i+1].reg[0..7]); + a9 is call[i+1]'s stack pointer. +-------------------------------------------------------------------------------- +*/ + + .org 0xC0 + .global _WindowUnderflow8 +_WindowUnderflow8: + + l32e a0, a9, -16 /* restore a0 from call[i+1]'s stack frame */ + l32e a1, a9, -12 /* restore a1 from call[i+1]'s stack frame */ + l32e a2, a9, -8 /* restore a2 from call[i+1]'s stack frame */ + l32e a7, a1, -12 /* a7 <- call[i-1]'s sp + (used to find end of call[i]'s frame) */ + l32e a3, a9, -4 /* restore a3 from call[i+1]'s stack frame */ + l32e a4, a7, -32 /* restore a4 from call[i]'s stack frame */ + l32e a5, a7, -28 /* restore a5 from call[i]'s stack frame */ + l32e a6, a7, -24 /* restore a6 from call[i]'s stack frame */ + l32e a7, a7, -20 /* restore a7 from call[i]'s stack frame */ + rfwu + +/* +-------------------------------------------------------------------------------- +Window Overflow Exception for Call12 + +Invoked if a call[i] referenced a register (a4-a15) +that contains data from ancestor call[j]; +call[j] had done a call12 to call[j+1]. +On entry here: + window rotated to call[j] start point; + a0-a11 are registers to be saved; + a12-a15 must be preserved; + a13 is call[j+1]'s stack pointer. +-------------------------------------------------------------------------------- +*/ + + .org 0x100 + .global _WindowOverflow12 +_WindowOverflow12: + + s32e a0, a13, -16 /* save a0 to call[j+1]'s stack frame */ + l32e a0, a1, -12 /* a0 <- call[j-1]'s sp + (used to find end of call[j]'s frame) */ + s32e a1, a13, -12 /* save a1 to call[j+1]'s stack frame */ + s32e a2, a13, -8 /* save a2 to call[j+1]'s stack frame */ + s32e a3, a13, -4 /* save a3 to call[j+1]'s stack frame */ + s32e a4, a0, -48 /* save a4 to end of call[j]'s stack frame */ + s32e a5, a0, -44 /* save a5 to end of call[j]'s stack frame */ + s32e a6, a0, -40 /* save a6 to end of call[j]'s stack frame */ + s32e a7, a0, -36 /* save a7 to end of call[j]'s stack frame */ + s32e a8, a0, -32 /* save a8 to end of call[j]'s stack frame */ + s32e a9, a0, -28 /* save a9 to end of call[j]'s stack frame */ + s32e a10, a0, -24 /* save a10 to end of call[j]'s stack frame */ + s32e a11, a0, -20 /* save a11 to end of call[j]'s stack frame */ + rfwo /* rotates back to call[i] position */ + +/* +-------------------------------------------------------------------------------- +Window Underflow Exception for Call12 + +Invoked by RETW returning from call[i+1] to call[i] +where call[i]'s registers must be reloaded (not live in ARs); +where call[i] had done a call12 to call[i+1]. +On entry here: + window rotated to call[i] start point; + a0-a11 are undefined, must be reloaded with call[i].reg[0..11]; + a12-a15 must be preserved (they are call[i+1].reg[0..3]); + a13 is call[i+1]'s stack pointer. +-------------------------------------------------------------------------------- +*/ + + .org 0x140 + .global _WindowUnderflow12 +_WindowUnderflow12: + + l32e a0, a13, -16 /* restore a0 from call[i+1]'s stack frame */ + l32e a1, a13, -12 /* restore a1 from call[i+1]'s stack frame */ + l32e a2, a13, -8 /* restore a2 from call[i+1]'s stack frame */ + l32e a11, a1, -12 /* a11 <- call[i-1]'s sp + (used to find end of call[i]'s frame) */ + l32e a3, a13, -4 /* restore a3 from call[i+1]'s stack frame */ + l32e a4, a11, -48 /* restore a4 from end of call[i]'s stack frame */ + l32e a5, a11, -44 /* restore a5 from end of call[i]'s stack frame */ + l32e a6, a11, -40 /* restore a6 from end of call[i]'s stack frame */ + l32e a7, a11, -36 /* restore a7 from end of call[i]'s stack frame */ + l32e a8, a11, -32 /* restore a8 from end of call[i]'s stack frame */ + l32e a9, a11, -28 /* restore a9 from end of call[i]'s stack frame */ + l32e a10, a11, -24 /* restore a10 from end of call[i]'s stack frame */ + l32e a11, a11, -20 /* restore a11 from end of call[i]'s stack frame */ + rfwu + +#endif /* XCHAL_HAVE_WINDOWED */ + +#endif /* SDK_INT_HANDLING */ diff --git a/dist/tools/licenses/patterns/Apache2.0 b/dist/tools/licenses/patterns/Apache2.0 new file mode 100644 index 0000000000000..51fca54c2a05e --- /dev/null +++ b/dist/tools/licenses/patterns/Apache2.0 @@ -0,0 +1,11 @@ +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.