Files
zephyr/drivers/input/input_esp32_touch_sensor.c
Sylvio Alves b7b32944fc drivers: espressif: move ISRs into IRAM area
Most of Espressif drivers ISRs are already running in IRAM area, except
those in this PR. Move ISRs accordingly so we avoid any
interrupt miss when cache is disabled.

Signed-off-by: Sylvio Alves <sylvio.alves@espressif.com>
2025-12-15 07:27:10 -05:00

340 lines
11 KiB
C

/*
* Copyright (c) 2023 Espressif Systems (Shanghai) Co., Ltd.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT espressif_esp32_touch
#include <zephyr/device.h>
#include <zephyr/input/input.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/interrupt_controller/intc_esp32.h>
#include <esp_err.h>
#include <soc/soc_pins.h>
#include <soc/periph_defs.h>
#include <hal/touch_sensor_types.h>
#include <hal/touch_sensor_hal.h>
#include <driver/rtc_io.h>
#include <esp_intr_alloc.h>
LOG_MODULE_REGISTER(espressif_esp32_touch, CONFIG_INPUT_LOG_LEVEL);
#define ESP32_SCAN_DONE_MAX_COUNT 5
#if defined(CONFIG_SOC_SERIES_ESP32)
#define ESP32_RTC_INTR_MSK RTC_CNTL_TOUCH_INT_ST_M
#elif defined(CONFIG_SOC_SERIES_ESP32S2) || defined(CONFIG_SOC_SERIES_ESP32S3)
#define ESP32_RTC_INTR_MSK (RTC_CNTL_TOUCH_DONE_INT_ST_M | \
RTC_CNTL_TOUCH_ACTIVE_INT_ST_M | \
RTC_CNTL_TOUCH_INACTIVE_INT_ST_M | \
RTC_CNTL_TOUCH_SCAN_DONE_INT_ST_M | \
RTC_CNTL_TOUCH_TIMEOUT_INT_ST_M)
#define ESP32_TOUCH_PAD_INTR_MASK (TOUCH_PAD_INTR_MASK_ACTIVE | \
TOUCH_PAD_INTR_MASK_INACTIVE | \
TOUCH_PAD_INTR_MASK_TIMEOUT | \
TOUCH_PAD_INTR_MASK_SCAN_DONE)
#endif /* defined(CONFIG_SOC_SERIES_ESP32) */
struct esp32_touch_sensor_channel_config {
int32_t channel_num;
int32_t channel_sens;
uint32_t zephyr_code;
};
struct esp32_touch_sensor_config {
uint32_t debounce_interval_ms;
int num_channels;
int href_microvolt_enum_idx;
int lref_microvolt_enum_idx;
int href_atten_microvolt_enum_idx;
int filter_mode;
int filter_debounce_cnt;
int filter_noise_thr;
int filter_jitter_step;
int filter_smooth_level;
const struct esp32_touch_sensor_channel_config *channel_cfg;
struct esp32_touch_sensor_channel_data *channel_data;
};
struct esp32_touch_sensor_channel_data {
const struct device *dev;
struct k_work_delayable work;
uint32_t status;
#if defined(CONFIG_SOC_SERIES_ESP32S2) || defined(CONFIG_SOC_SERIES_ESP32S3)
uint32_t last_status;
#endif /* defined(CONFIG_SOC_SERIES_ESP32S2) || defined(CONFIG_SOC_SERIES_ESP32S3) */
};
struct esp32_touch_sensor_data {
};
static void esp32_touch_sensor_interrupt_cb(void *arg)
{
const struct device *dev = arg;
const struct esp32_touch_sensor_config *dev_cfg = dev->config;
const struct esp32_touch_sensor_channel_config *channel_cfg;
const int num_channels = dev_cfg->num_channels;
uint32_t pad_status;
#if defined(CONFIG_SOC_SERIES_ESP32)
touch_hal_intr_clear();
#elif defined(CONFIG_SOC_SERIES_ESP32S2) || defined(CONFIG_SOC_SERIES_ESP32S3)
static uint8_t scan_done_counter;
touch_pad_intr_mask_t intr_mask = touch_hal_read_intr_status_mask();
if (intr_mask & TOUCH_PAD_INTR_MASK_SCAN_DONE) {
if (++scan_done_counter == ESP32_SCAN_DONE_MAX_COUNT) {
touch_hal_intr_disable(TOUCH_PAD_INTR_MASK_SCAN_DONE);
for (int i = 0; i < num_channels; i++) {
channel_cfg = &dev_cfg->channel_cfg[i];
/* Set interrupt threshold */
uint32_t benchmark_value;
touch_hal_read_benchmark(channel_cfg->channel_num,
&benchmark_value);
touch_hal_set_threshold(channel_cfg->channel_num,
channel_cfg->channel_sens * benchmark_value / 100);
}
}
return;
}
#endif /* defined(CONFIG_SOC_SERIES_ESP32) */
touch_hal_read_trigger_status_mask(&pad_status);
#if defined(CONFIG_SOC_SERIES_ESP32)
touch_hal_clear_trigger_status_mask();
#endif /* defined(CONFIG_SOC_SERIES_ESP32) */
for (int i = 0; i < num_channels; i++) {
uint32_t channel_status;
channel_cfg = &dev_cfg->channel_cfg[i];
channel_status = (pad_status >> channel_cfg->channel_num) & 0x01;
#if defined(CONFIG_SOC_SERIES_ESP32)
if (channel_status != 0) {
#elif defined(CONFIG_SOC_SERIES_ESP32S2) || defined(CONFIG_SOC_SERIES_ESP32S3)
uint32_t channel_num = (uint32_t)touch_hal_get_current_meas_channel();
if (channel_cfg->channel_num == channel_num) {
#endif /* CONFIG_SOC_SERIES_ESP32 */
struct esp32_touch_sensor_channel_data
*channel_data = &dev_cfg->channel_data[i];
channel_data->status = channel_status;
(void)k_work_reschedule(&channel_data->work,
K_MSEC(dev_cfg->debounce_interval_ms));
}
}
}
static void IRAM_ATTR esp32_touch_rtc_isr(void *arg)
{
uint32_t status = REG_READ(RTC_CNTL_INT_ST_REG);
if (!(status & ESP32_RTC_INTR_MSK)) {
return;
}
esp32_touch_sensor_interrupt_cb(arg);
REG_WRITE(RTC_CNTL_INT_CLR_REG, status);
}
/**
* Handle debounced touch sensor touch state.
*/
static void esp32_touch_sensor_change_deferred(struct k_work *work)
{
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
struct esp32_touch_sensor_channel_data *channel_data =
CONTAINER_OF(dwork, struct esp32_touch_sensor_channel_data, work);
const struct device *dev = channel_data->dev;
const struct esp32_touch_sensor_config *dev_cfg = dev->config;
int key_index = channel_data - &dev_cfg->channel_data[0];
const struct esp32_touch_sensor_channel_config
*channel_cfg = &dev_cfg->channel_cfg[key_index];
#if defined(CONFIG_SOC_SERIES_ESP32S2) || defined(CONFIG_SOC_SERIES_ESP32S3)
if (channel_data->last_status != channel_data->status) {
#endif /* defined(CONFIG_SOC_SERIES_ESP32S2) || defined(CONFIG_SOC_SERIES_ESP32S3) */
input_report_key(dev, channel_cfg->zephyr_code,
channel_data->status, true, K_FOREVER);
#if defined(CONFIG_SOC_SERIES_ESP32S2) || defined(CONFIG_SOC_SERIES_ESP32S3)
channel_data->last_status = channel_data->status;
}
#endif /* defined(CONFIG_SOC_SERIES_ESP32S2) || defined(CONFIG_SOC_SERIES_ESP32S3) */
}
static int esp32_touch_sensor_init(const struct device *dev)
{
esp_err_t err, flags;
const struct esp32_touch_sensor_config *dev_cfg = dev->config;
const int num_channels = dev_cfg->num_channels;
touch_hal_init();
#if defined(CONFIG_SOC_SERIES_ESP32)
touch_hal_volt_t volt = {
.refh = dev_cfg->href_microvolt_enum_idx,
.refl = dev_cfg->lref_microvolt_enum_idx,
.atten = dev_cfg->href_atten_microvolt_enum_idx
};
touch_hal_set_voltage(&volt);
touch_hal_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
#endif /* defined(CONFIG_SOC_SERIES_ESP32) */
for (int i = 0; i < num_channels; i++) {
struct esp32_touch_sensor_channel_data *channel_data = &dev_cfg->channel_data[i];
const struct esp32_touch_sensor_channel_config *channel_cfg =
&dev_cfg->channel_cfg[i];
if (!(channel_cfg->channel_num > 0 &&
channel_cfg->channel_num < SOC_TOUCH_SENSOR_NUM)) {
LOG_ERR("Touch %d configuration failed: "
"Touch channel error", i);
return -EINVAL;
}
#if defined(CONFIG_SOC_SERIES_ESP32S2) || defined(CONFIG_SOC_SERIES_ESP32S3)
if (channel_cfg->channel_num == SOC_TOUCH_DENOISE_CHANNEL) {
LOG_ERR("Touch %d configuration failed: "
"TOUCH0 is internal denoise channel", i);
return -EINVAL;
}
#endif /* defined(CONFIG_SOC_SERIES_ESP32S2) || defined(CONFIG_SOC_SERIES_ESP32S3) */
gpio_num_t gpio_num = touch_sensor_channel_io_map[channel_cfg->channel_num];
rtc_gpio_init(gpio_num);
rtc_gpio_set_direction(gpio_num, RTC_GPIO_MODE_DISABLED);
rtc_gpio_pulldown_dis(gpio_num);
rtc_gpio_pullup_dis(gpio_num);
touch_hal_config(channel_cfg->channel_num);
#if defined(CONFIG_SOC_SERIES_ESP32)
touch_hal_set_threshold(channel_cfg->channel_num, 0);
touch_hal_set_group_mask(BIT(channel_cfg->channel_num),
BIT(channel_cfg->channel_num));
#endif /* defined(CONFIG_SOC_SERIES_ESP32) */
touch_hal_set_channel_mask(BIT(channel_cfg->channel_num));
channel_data->status = 0;
#if defined(CONFIG_SOC_SERIES_ESP32S2) || defined(CONFIG_SOC_SERIES_ESP32S3)
channel_data->last_status = 0;
#endif /* defined(CONFIG_SOC_SERIES_ESP32S2) || defined(CONFIG_SOC_SERIES_ESP32S3) */
channel_data->dev = dev;
k_work_init_delayable(&channel_data->work, esp32_touch_sensor_change_deferred);
}
#if defined(CONFIG_SOC_SERIES_ESP32)
for (int i = 0; i < num_channels; i++) {
const struct esp32_touch_sensor_channel_config *channel_cfg =
&dev_cfg->channel_cfg[i];
uint32_t ref_time;
ref_time = k_uptime_get_32();
while (!touch_hal_meas_is_done()) {
if (k_uptime_get_32() - ref_time > 500) {
return -ETIMEDOUT;
}
k_busy_wait(1000);
}
uint16_t touch_value = touch_hal_read_raw_data(channel_cfg->channel_num);
touch_hal_set_threshold(channel_cfg->channel_num,
touch_value * (100 - channel_cfg->channel_sens) / 100);
}
#elif defined(CONFIG_SOC_SERIES_ESP32S2) || defined(CONFIG_SOC_SERIES_ESP32S3)
touch_filter_config_t filter_info = {
.mode = dev_cfg->filter_mode,
.debounce_cnt = dev_cfg->filter_debounce_cnt,
.noise_thr = dev_cfg->filter_noise_thr,
.jitter_step = dev_cfg->filter_jitter_step,
.smh_lvl = dev_cfg->filter_smooth_level,
};
touch_hal_filter_set_config(&filter_info);
touch_hal_filter_enable();
touch_hal_timeout_enable();
touch_hal_timeout_set_threshold(SOC_TOUCH_PAD_THRESHOLD_MAX);
#endif /* defined(CONFIG_SOC_SERIES_ESP32) */
flags = ESP_PRIO_TO_FLAGS(DT_IRQ_BY_IDX(DT_NODELABEL(touch), 0, priority)) |
ESP_INT_FLAGS_CHECK(DT_IRQ_BY_IDX(DT_NODELABEL(touch), 0, flags)) |
ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_IRAM;
err = esp_intr_alloc(DT_IRQ_BY_IDX(DT_NODELABEL(touch), 0, irq), flags, esp32_touch_rtc_isr,
(void *)dev, NULL);
if (err) {
LOG_ERR("Failed to register ISR\n");
return -EFAULT;
}
#if defined(CONFIG_SOC_SERIES_ESP32)
touch_hal_intr_enable();
#elif defined(CONFIG_SOC_SERIES_ESP32S2) || defined(CONFIG_SOC_SERIES_ESP32S3)
touch_hal_intr_enable(ESP32_TOUCH_PAD_INTR_MASK);
touch_hal_set_fsm_mode(TOUCH_FSM_MODE_TIMER);
#endif /* defined(CONFIG_SOC_SERIES_ESP32) */
touch_hal_start_fsm();
return 0;
}
#define ESP32_TOUCH_SENSOR_CHANNEL_CFG_INIT(node_id) \
{ \
.channel_num = DT_PROP(node_id, channel_num), \
.channel_sens = DT_PROP(node_id, channel_sens), \
.zephyr_code = DT_PROP(node_id, zephyr_code), \
}
#define ESP32_TOUCH_SENSOR_INIT(inst) \
static const struct esp32_touch_sensor_channel_config \
esp32_touch_sensor_channel_config_##inst[] = { \
DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(inst, \
ESP32_TOUCH_SENSOR_CHANNEL_CFG_INIT, (,)) \
}; \
\
static struct esp32_touch_sensor_channel_data esp32_touch_sensor_channel_data_##inst \
[ARRAY_SIZE(esp32_touch_sensor_channel_config_##inst)]; \
\
static const struct esp32_touch_sensor_config esp32_touch_sensor_config_##inst = { \
.debounce_interval_ms = DT_INST_PROP(inst, debounce_interval_ms), \
.num_channels = ARRAY_SIZE(esp32_touch_sensor_channel_config_##inst), \
.href_microvolt_enum_idx = DT_INST_ENUM_IDX(inst, href_microvolt), \
.lref_microvolt_enum_idx = DT_INST_ENUM_IDX(inst, lref_microvolt), \
.href_atten_microvolt_enum_idx = DT_INST_ENUM_IDX(inst, href_atten_microvolt), \
.filter_mode = DT_INST_PROP(inst, filter_mode), \
.filter_debounce_cnt = DT_INST_PROP(inst, filter_debounce_cnt), \
.filter_noise_thr = DT_INST_PROP(inst, filter_noise_thr), \
.filter_jitter_step = DT_INST_PROP(inst, filter_jitter_step), \
.filter_smooth_level = DT_INST_PROP(inst, filter_smooth_level), \
.channel_cfg = esp32_touch_sensor_channel_config_##inst, \
.channel_data = esp32_touch_sensor_channel_data_##inst, \
}; \
\
static struct esp32_touch_sensor_data esp32_touch_sensor_data_##inst; \
\
DEVICE_DT_INST_DEFINE(inst, \
&esp32_touch_sensor_init, \
NULL, \
&esp32_touch_sensor_data_##inst, \
&esp32_touch_sensor_config_##inst, \
POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, \
NULL);
DT_INST_FOREACH_STATUS_OKAY(ESP32_TOUCH_SENSOR_INIT)