From c344d0d74df35fcdc0d474e45b920d390e0f6554 Mon Sep 17 00:00:00 2001 From: Glauber Maroto Ferreira Date: Mon, 15 Feb 2021 16:40:38 -0300 Subject: [PATCH] esp32: drivers: counter: add support for general-purpose counters Adds support for ESP32 general-purpose Counters Signed-off-by: Glauber Maroto Ferreira --- boards/xtensa/esp32/esp32.dts | 16 + drivers/counter/CMakeLists.txt | 1 + drivers/counter/Kconfig | 2 + drivers/counter/Kconfig.esp32 | 47 +++ drivers/counter/counter_esp32.c | 286 ++++++++++++++++++ dts/xtensa/espressif/esp32.dtsi | 31 ++ samples/drivers/counter/alarm/Kconfig | 4 + .../drivers/counter/alarm/boards/esp32.conf | 1 + samples/drivers/counter/alarm/src/main.c | 2 + soc/xtensa/esp32/linker.ld | 2 + .../counter_basic_api/boards/esp32.conf | 1 + 11 files changed, 393 insertions(+) create mode 100644 drivers/counter/Kconfig.esp32 create mode 100644 drivers/counter/counter_esp32.c create mode 100644 samples/drivers/counter/alarm/boards/esp32.conf create mode 100644 tests/drivers/counter/counter_basic_api/boards/esp32.conf diff --git a/boards/xtensa/esp32/esp32.dts b/boards/xtensa/esp32/esp32.dts index 87d5405dc41..7981f7a88e8 100644 --- a/boards/xtensa/esp32/esp32.dts +++ b/boards/xtensa/esp32/esp32.dts @@ -91,6 +91,22 @@ csel-pin = <5>; }; +&timer0 { + status = "okay"; +}; + +&timer1 { + status = "okay"; +}; + +&timer2 { + status = "okay"; +}; + +&timer3 { + status = "okay"; +}; + &trng0 { status = "okay"; }; diff --git a/drivers/counter/CMakeLists.txt b/drivers/counter/CMakeLists.txt index ea686557bc1..4e700ea77e4 100644 --- a/drivers/counter/CMakeLists.txt +++ b/drivers/counter/CMakeLists.txt @@ -20,3 +20,4 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_NATIVE_POSIX counter_native_p zephyr_library_sources_ifdef(CONFIG_USERSPACE counter_handlers.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_PIT counter_mcux_pit.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_XLNX_AXI_TIMER counter_xlnx_axi_timer.c) +zephyr_library_sources_ifdef(CONFIG_COUNTER_ESP32 counter_esp32.c) diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 2f5b5f4776d..4878d45bccd 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -46,4 +46,6 @@ source "drivers/counter/Kconfig.mcux_pit" source "drivers/counter/Kconfig.xlnx" +source "drivers/counter/Kconfig.esp32" + endif # COUNTER diff --git a/drivers/counter/Kconfig.esp32 b/drivers/counter/Kconfig.esp32 new file mode 100644 index 00000000000..60f2de97b0c --- /dev/null +++ b/drivers/counter/Kconfig.esp32 @@ -0,0 +1,47 @@ +# ESP32 Timer configuration + +# Copyright (c) 2020 Espressif Systems (Shanghai) Co., Ltd. +# SPDX-License-Identifier: Apache-2.0 + +config COUNTER_ESP32 + bool "ESP32 Counter Driver" + depends on SOC_ESP32 + default y + help + Enable Counter driver for ESP32. + +config COUNTER_ESP32_IRQ_0 + int "IRQ line for TG0_T0 interrupt" + depends on COUNTER_ESP32 + default 13 + help + Set the IRQ line used by the TG0_T0 device. + +config COUNTER_ESP32_IRQ_1 + int "IRQ line for TG0_T1 interrupt" + depends on COUNTER_ESP32 + default 17 + help + Set the IRQ line used by the TG0_T1 device. + +config COUNTER_ESP32_IRQ_2 + int "IRQ line for TG1_T0 interrupt" + depends on COUNTER_ESP32 + default 18 + help + Set the IRQ line used by the TG1_T0 device. + +config COUNTER_ESP32_IRQ_3 + int "IRQ line for TG1_T1 interrupt" + depends on COUNTER_ESP32 + default 20 + help + Set the IRQ line used by the TG1_T1 device. + +config COUNTER_ESP32_PRESCALER + int "Prescaling value for counter device" + depends on COUNTER_ESP32 + range 2 65336 + default 2 + help + Sets prescaler value for Timer clock. diff --git a/drivers/counter/counter_esp32.c b/drivers/counter/counter_esp32.c new file mode 100644 index 00000000000..09614c5685d --- /dev/null +++ b/drivers/counter/counter_esp32.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2020 Espressif Systems (Shanghai) Co., Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT espressif_esp32_timer + +/* Include esp-idf headers first to avoid redefining BIT() macro */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +LOG_MODULE_REGISTER(esp32_counter, CONFIG_COUNTER_LOG_LEVEL); + +#define INITIAL_COUNT (0x00000000ULL) + +#define INTR_SRC_0 ETS_TG0_T0_LEVEL_INTR_SOURCE +#define INTR_SRC_1 ETS_TG0_T1_LEVEL_INTR_SOURCE +#define INTR_SRC_2 ETS_TG1_T0_LEVEL_INTR_SOURCE +#define INTR_SRC_3 ETS_TG1_T1_LEVEL_INTR_SOURCE + +#define INST_0_INDEX TIMER_0 +#define INST_1_INDEX TIMER_1 +#define INST_2_INDEX TIMER_0 +#define INST_3_INDEX TIMER_1 + +#define INST_0_GROUP TIMER_GROUP_0 +#define INST_1_GROUP TIMER_GROUP_0 +#define INST_2_GROUP TIMER_GROUP_1 +#define INST_3_GROUP TIMER_GROUP_1 + +#define TIMX p_timer_obj[TIMG(dev)][TIDX(dev)] +#define DEV_CFG(dev) ((const struct counter_esp32_config *const)(dev)->config) +#define DEV_DATA(dev) ((struct counter_esp32_data *)(dev)->data) +#define TIMG(dev) (DEV_CFG(dev)->group) +#define TIDX(dev) (DEV_CFG(dev)->idx) + +typedef void (*counter_irq_config_func_t)(const struct device *dev); + +struct timer_isr_func_t { + timer_isr_t fn; + void *args; + timer_isr_handle_t timer_isr_handle; + timer_group_t isr_timer_group; +}; + +struct counter_obj_t { + timer_hal_context_t hal; + struct timer_isr_func_t timer_isr_fun; +}; + +struct counter_esp32_config { + struct counter_config_info counter_info; + timer_config_t config; + timer_group_t group; + timer_idx_t idx; + + const struct { + int source; + int line; + } irq; + + counter_irq_config_func_t irq_config_fn; +}; + +struct counter_esp32_data { + struct counter_alarm_cfg alarm_cfg; + uint32_t ticks; +}; + +static struct counter_obj_t *p_timer_obj[TIMER_GROUP_MAX][TIMER_MAX] = { 0 }; +static struct k_spinlock lock; + +static int counter_esp32_init(const struct device *dev) +{ + const struct counter_esp32_config *cfg = DEV_CFG(dev); + + if (TIMG(dev) == TIMER_GROUP_0) { + periph_module_enable(PERIPH_TIMG0_MODULE); + } else if (TIMG(dev) == TIMER_GROUP_1) { + periph_module_enable(PERIPH_TIMG1_MODULE); + } else { + return -ENOTSUP; + } + + if (TIMX == NULL) { + TIMX = (struct counter_obj_t *)k_calloc(1, sizeof(struct counter_obj_t)); + if (TIMX == NULL) { + LOG_ERR("TIMER driver malloc error"); + return -ENOMEM; + } + } + + k_spinlock_key_t key = k_spin_lock(&lock); + + timer_hal_init(&TIMX->hal, TIMG(dev), TIDX(dev)); + DEV_DATA(dev)->alarm_cfg.callback = NULL; + timer_hal_intr_disable(&TIMX->hal); + timer_hal_clear_intr_status(&TIMX->hal); + timer_hal_set_auto_reload(&TIMX->hal, cfg->config.auto_reload); + timer_hal_set_divider(&TIMX->hal, cfg->config.divider); + timer_hal_set_counter_increase(&TIMX->hal, cfg->config.counter_dir); + timer_hal_set_alarm_enable(&TIMX->hal, cfg->config.alarm_en); + if (cfg->config.intr_type == TIMER_INTR_LEVEL) { + timer_hal_set_level_int_enable(&TIMX->hal, true); + } + timer_hal_set_counter_value(&TIMX->hal, INITIAL_COUNT); + timer_hal_set_counter_enable(&TIMX->hal, cfg->config.counter_en); + DEV_CFG(dev)->irq_config_fn(dev); + k_spin_unlock(&lock, key); + + return 0; +} + +static int counter_esp32_start(const struct device *dev) +{ + k_spinlock_key_t key = k_spin_lock(&lock); + + timer_hal_set_counter_enable(&TIMX->hal, TIMER_START); + k_spin_unlock(&lock, key); + + return 0; +} + +static int counter_esp32_stop(const struct device *dev) +{ + k_spinlock_key_t key = k_spin_lock(&lock); + + timer_hal_set_counter_enable(&TIMX->hal, TIMER_PAUSE); + k_spin_unlock(&lock, key); + + return 0; +} + +static int counter_esp32_get_value(const struct device *dev, uint32_t *ticks) +{ + k_spinlock_key_t key = k_spin_lock(&lock); + + timer_hal_get_counter_value(&TIMX->hal, (uint64_t *)ticks); + k_spin_unlock(&lock, key); + + return 0; +} + +static int counter_esp32_set_alarm(const struct device *dev, uint8_t chan_id, + const struct counter_alarm_cfg *alarm_cfg) +{ + ARG_UNUSED(chan_id); + uint32_t now; + + counter_esp32_get_value(dev, &now); + k_spinlock_key_t key = k_spin_lock(&lock); + + timer_hal_set_alarm_value(&TIMX->hal, (now + alarm_cfg->ticks)); + timer_hal_intr_enable(&TIMX->hal); + timer_hal_set_alarm_enable(&TIMX->hal, TIMER_ALARM_EN); + DEV_DATA(dev)->alarm_cfg.callback = alarm_cfg->callback; + DEV_DATA(dev)->alarm_cfg.user_data = alarm_cfg->user_data; + k_spin_unlock(&lock, key); + + return 0; +} + +static int counter_esp32_cancel_alarm(const struct device *dev, uint8_t chan_id) +{ + ARG_UNUSED(chan_id); + + k_spinlock_key_t key = k_spin_lock(&lock); + + timer_hal_intr_disable(&TIMX->hal); + timer_hal_set_alarm_enable(&TIMX->hal, TIMER_ALARM_DIS); + k_spin_unlock(&lock, key); + + return 0; +} + +static int counter_esp32_set_top_value(const struct device *dev, + const struct counter_top_cfg *cfg) +{ + if (cfg->ticks != (DEV_CFG(dev))->counter_info.max_top_value) { + return -ENOTSUP; + } else { + return 0; + } +} + +static uint32_t counter_esp32_get_pending_int(const struct device *dev) +{ + timer_hal_get_intr_status_reg(&TIMX->hal); + + return 0; +} + +static uint32_t counter_esp32_get_top_value(const struct device *dev) +{ + return DEV_CFG(dev)->counter_info.max_top_value; +} + +static uint32_t counter_esp32_get_max_relative_alarm(const struct device *dev) +{ + return counter_esp32_get_top_value(dev); +} + +static const struct counter_driver_api counter_api = { + .start = counter_esp32_start, + .stop = counter_esp32_stop, + .get_value = counter_esp32_get_value, + .set_alarm = counter_esp32_set_alarm, + .cancel_alarm = counter_esp32_cancel_alarm, + .set_top_value = counter_esp32_set_top_value, + .get_pending_int = counter_esp32_get_pending_int, + .get_top_value = counter_esp32_get_top_value, + .get_max_relative_alarm = counter_esp32_get_max_relative_alarm +}; + +static void counter_esp32_isr(struct device *dev) +{ + counter_esp32_cancel_alarm(dev, 0); + uint32_t now; + + counter_esp32_get_value(dev, &now); + + struct counter_alarm_cfg *alarm_cfg = &DEV_DATA(dev)->alarm_cfg; + + if (alarm_cfg->callback) { + alarm_cfg->callback(dev, 0, now, alarm_cfg->user_data); + } + + timer_hal_clear_intr_status(&TIMX->hal); +} + +#define ESP32_COUNTER_INIT(n) \ + \ + static struct counter_esp32_data counter_data_##n; \ + static void counter_esp32_irq_config_##n(const struct device *dev); \ + \ + static const struct counter_esp32_config counter_config_##n = { \ + .counter_info = { \ + .max_top_value = UINT32_MAX, \ + .freq = (APB_CLK_FREQ / CONFIG_COUNTER_ESP32_PRESCALER), \ + .flags = COUNTER_CONFIG_INFO_COUNT_UP, \ + .channels = 1 \ + }, \ + .config = { \ + .alarm_en = TIMER_ALARM_DIS, \ + .counter_en = TIMER_START, \ + .intr_type = TIMER_INTR_LEVEL, \ + .counter_dir = TIMER_COUNT_UP, \ + .auto_reload = TIMER_AUTORELOAD_DIS, \ + .divider = CONFIG_COUNTER_ESP32_PRESCALER, \ + }, \ + .group = INST_##n##_GROUP, \ + .idx = INST_##n##_INDEX, \ + .irq = { \ + .source = INTR_SRC_##n, \ + .line = CONFIG_COUNTER_ESP32_IRQ_##n, \ + }, \ + .irq_config_fn = counter_esp32_irq_config_##n \ + }; \ + \ + static void counter_esp32_irq_config_##n(const struct device *dev) \ + { \ + intr_matrix_set(0, INTR_SRC_##n, \ + CONFIG_COUNTER_ESP32_IRQ_##n); \ + IRQ_CONNECT(CONFIG_COUNTER_ESP32_IRQ_##n, \ + DT_INST_IRQ(n, priority), counter_esp32_isr, \ + DEVICE_DT_INST_GET(n), 0); \ + irq_enable(CONFIG_COUNTER_ESP32_IRQ_##n); \ + } \ + \ + DEVICE_DT_INST_DEFINE(n, \ + counter_esp32_init, \ + device_pm_control_nop, &counter_data_##n, \ + &counter_config_##n, PRE_KERNEL_1, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &counter_api); + +DT_INST_FOREACH_STATUS_OKAY(ESP32_COUNTER_INIT) diff --git a/dts/xtensa/espressif/esp32.dtsi b/dts/xtensa/espressif/esp32.dtsi index 492f2a00c4a..8a9ac87adc0 100644 --- a/dts/xtensa/espressif/esp32.dtsi +++ b/dts/xtensa/espressif/esp32.dtsi @@ -167,6 +167,37 @@ status = "disabled"; }; + timer0: counter@3ff5f000 { + compatible = "espressif,esp32-timer"; + reg = <0x3ff5f000 DT_SIZE_K(4)>; + /* interrupts = <13>; - FIXME: Enable when irq controller is supported */ + label = "TIMG0_T0"; + status = "disabled"; + }; + + timer1: counter@3ff5f024 { + compatible = "espressif,esp32-timer"; + reg = <0x3ff5f024 DT_SIZE_K(4)>; + /* interrupts = <17>; - FIXME: Enable when irq controller is supported */ + label = "TIMG0_T1"; + status = "disabled"; + }; + + timer2: counter@3ff60000 { + compatible = "espressif,esp32-timer"; + reg = <0x3ff60000 DT_SIZE_K(4)>; + /* interrupts = <18>; - FIXME: Enable when irq controller is supported */ + label = "TIMG1_T0"; + status = "disabled"; + }; + + timer3: counter@3ff60024 { + compatible = "espressif,esp32-timer"; + reg = <0x3ff60024 DT_SIZE_K(4)>; + /* interrupts = <20>; - FIXME: Enable when irq controller is supported */ + label = "TIMG1_T1"; + status = "disabled"; + }; }; }; diff --git a/samples/drivers/counter/alarm/Kconfig b/samples/drivers/counter/alarm/Kconfig index 030a80b7709..cb118661789 100644 --- a/samples/drivers/counter/alarm/Kconfig +++ b/samples/drivers/counter/alarm/Kconfig @@ -11,4 +11,8 @@ config COUNTER_SAM0_TC32 bool default y if BOARD_ATSAMD20_XPRO +config COUNTER_ESP32 + bool + default y if SOC_ESP32 + source "Kconfig.zephyr" diff --git a/samples/drivers/counter/alarm/boards/esp32.conf b/samples/drivers/counter/alarm/boards/esp32.conf new file mode 100644 index 00000000000..26d52701286 --- /dev/null +++ b/samples/drivers/counter/alarm/boards/esp32.conf @@ -0,0 +1 @@ +CONFIG_HEAP_MEM_POOL_SIZE=256 diff --git a/samples/drivers/counter/alarm/src/main.c b/samples/drivers/counter/alarm/src/main.c index 5d455ced421..0146eea6b8f 100644 --- a/samples/drivers/counter/alarm/src/main.c +++ b/samples/drivers/counter/alarm/src/main.c @@ -25,6 +25,8 @@ struct counter_alarm_cfg alarm_cfg; #define TIMER DT_LABEL(DT_NODELABEL(counter0)) #elif defined(CONFIG_COUNTER_XLNX_AXI_TIMER) #define TIMER DT_LABEL(DT_INST(0, xlnx_xps_timer_1_00_a)) +#elif defined(CONFIG_COUNTER_ESP32) +#define TIMER DT_LABEL(DT_NODELABEL(timer0)) #endif static void test_counter_interrupt_fn(const struct device *counter_dev, diff --git a/soc/xtensa/esp32/linker.ld b/soc/xtensa/esp32/linker.ld index a4f3169ab00..2e4f52825a2 100644 --- a/soc/xtensa/esp32/linker.ld +++ b/soc/xtensa/esp32/linker.ld @@ -60,6 +60,8 @@ PROVIDE ( esp32_rom_ets_printf = ets_printf ); PROVIDE ( esp32_rom_g_ticks_per_us_app = g_ticks_per_us_app ); PROVIDE ( esp32_rom_g_ticks_per_us_pro = g_ticks_per_us_app ); PROVIDE ( esp32_rom_ets_delay_us = ets_delay_us ); +PROVIDE ( TIMERG0 = 0x3ff5F000 ); +PROVIDE ( TIMERG1 = 0x3ff60000 ); /* __udivdi3 is exported using assignment, which declares strong symbols */ __udivdi3 = 0x4000cff8; diff --git a/tests/drivers/counter/counter_basic_api/boards/esp32.conf b/tests/drivers/counter/counter_basic_api/boards/esp32.conf new file mode 100644 index 00000000000..26d52701286 --- /dev/null +++ b/tests/drivers/counter/counter_basic_api/boards/esp32.conf @@ -0,0 +1 @@ +CONFIG_HEAP_MEM_POOL_SIZE=256