| /* |
| * Copyright (c) 2022 Espressif Systems (Shanghai) Co., Ltd. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT espressif_esp32_pcnt |
| |
| /* Include esp-idf headers first to avoid redefining BIT() macro */ |
| #include <hal/pcnt_hal.h> |
| #include <hal/pcnt_ll.h> |
| #include <hal/pcnt_types.h> |
| |
| #include <soc.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <zephyr/drivers/sensor.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/drivers/pinctrl.h> |
| #include <zephyr/drivers/clock_control.h> |
| #ifdef CONFIG_PCNT_ESP32_TRIGGER |
| #include <zephyr/drivers/interrupt_controller/intc_esp32.h> |
| #endif /* CONFIG_PCNT_ESP32_TRIGGER */ |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(pcnt_esp32, CONFIG_SENSOR_LOG_LEVEL); |
| |
| #define PCNT_INTR_UNIT_0 BIT(0) |
| #define PCNT_INTR_UNIT_1 BIT(1) |
| #define PCNT_INTR_UNIT_2 BIT(2) |
| #define PCNT_INTR_UNIT_3 BIT(3) |
| #ifdef CONFIG_SOC_SERIES_ESP32 |
| #define PCNT_INTR_UNIT_4 BIT(4) |
| #define PCNT_INTR_UNIT_5 BIT(5) |
| #define PCNT_INTR_UNIT_6 BIT(6) |
| #define PCNT_INTR_UNIT_7 BIT(7) |
| #endif /* CONFIG_SOC_SERIES_ESP32 */ |
| |
| #ifdef CONFIG_PCNT_ESP32_TRIGGER |
| #define PCNT_INTR_THRES_1 BIT(2) |
| #define PCNT_INTR_THRES_0 BIT(3) |
| #endif /* CONFIG_PCNT_ESP32_TRIGGER */ |
| |
| struct pcnt_esp32_data { |
| pcnt_hal_context_t hal; |
| struct k_mutex cmd_mux; |
| #ifdef CONFIG_PCNT_ESP32_TRIGGER |
| sensor_trigger_handler_t trigger_handler; |
| const struct sensor_trigger *trigger; |
| #endif /* CONFIG_PCNT_ESP32_TRIGGER */ |
| }; |
| |
| struct pcnt_esp32_channel_config { |
| uint8_t sig_pos_mode; |
| uint8_t sig_neg_mode; |
| uint8_t ctrl_h_mode; |
| uint8_t ctrl_l_mode; |
| }; |
| |
| struct pcnt_esp32_unit_config { |
| const uint8_t idx; |
| int16_t filter; |
| int16_t count_val_acc; |
| struct pcnt_esp32_channel_config channel_config[2]; |
| int32_t h_thr; |
| int32_t l_thr; |
| int32_t offset; |
| }; |
| |
| struct pcnt_esp32_config { |
| const struct pinctrl_dev_config *pincfg; |
| const struct device *clock_dev; |
| const clock_control_subsys_t clock_subsys; |
| const int irq_src; |
| struct pcnt_esp32_unit_config *unit_config; |
| const int unit_len; |
| }; |
| |
| static int pcnt_esp32_sample_fetch(const struct device *dev, enum sensor_channel chan) |
| { |
| const struct pcnt_esp32_config *config = dev->config; |
| struct pcnt_esp32_data *data = (struct pcnt_esp32_data *const)(dev)->data; |
| |
| if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_ROTATION) { |
| return -ENOTSUP; |
| } |
| |
| k_mutex_lock(&data->cmd_mux, K_FOREVER); |
| |
| for (uint8_t i = 0; i < config->unit_len; i++) { |
| struct pcnt_esp32_unit_config *unit_config = &config->unit_config[i]; |
| |
| unit_config->count_val_acc = pcnt_ll_get_count(data->hal.dev, i); |
| } |
| |
| k_mutex_unlock(&data->cmd_mux); |
| |
| return 0; |
| } |
| |
| static int pcnt_esp32_channel_get(const struct device *dev, enum sensor_channel chan, |
| struct sensor_value *val) |
| { |
| int ret = 0; |
| const struct pcnt_esp32_config *config = dev->config; |
| struct pcnt_esp32_data *data = (struct pcnt_esp32_data *const)(dev)->data; |
| |
| k_mutex_lock(&data->cmd_mux, K_FOREVER); |
| |
| if (chan == SENSOR_CHAN_ROTATION) { |
| val->val1 = config->unit_config[0].count_val_acc + config->unit_config[0].offset; |
| val->val2 = 0; |
| } else { |
| ret = -ENOTSUP; |
| } |
| |
| k_mutex_unlock(&data->cmd_mux); |
| |
| return ret; |
| } |
| |
| static int pcnt_esp32_configure_pinctrl(const struct device *dev) |
| { |
| int ret; |
| struct pcnt_esp32_config *config = (struct pcnt_esp32_config *)dev->config; |
| |
| return pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); |
| } |
| |
| int pcnt_esp32_init(const struct device *dev) |
| { |
| int ret; |
| const struct pcnt_esp32_config *config = dev->config; |
| struct pcnt_esp32_data *data = (struct pcnt_esp32_data *const)(dev)->data; |
| |
| ret = clock_control_on(config->clock_dev, config->clock_subsys); |
| if (ret < 0) { |
| LOG_ERR("Could not initialize clock (%d)", ret); |
| return ret; |
| } |
| |
| ret = pcnt_esp32_configure_pinctrl(dev); |
| if (ret < 0) { |
| LOG_ERR("PWM pinctrl setup failed (%d)", ret); |
| return ret; |
| } |
| |
| pcnt_hal_init(&data->hal, 0); |
| |
| for (uint8_t i = 0; i < config->unit_len; i++) { |
| struct pcnt_esp32_unit_config *unit_config = &config->unit_config[i]; |
| |
| unit_config->h_thr = 0; |
| unit_config->l_thr = 0; |
| unit_config->offset = 0; |
| |
| pcnt_ll_enable_thres_event(data->hal.dev, i, 0, false); |
| pcnt_ll_enable_thres_event(data->hal.dev, i, 1, false); |
| pcnt_ll_enable_low_limit_event(data->hal.dev, i, false); |
| pcnt_ll_enable_high_limit_event(data->hal.dev, i, false); |
| pcnt_ll_enable_zero_cross_event(data->hal.dev, i, false); |
| pcnt_ll_set_edge_action(data->hal.dev, i, 0, |
| unit_config->channel_config[0].sig_pos_mode, |
| unit_config->channel_config[0].sig_neg_mode); |
| pcnt_ll_set_edge_action(data->hal.dev, i, 1, |
| unit_config->channel_config[1].sig_pos_mode, |
| unit_config->channel_config[1].sig_neg_mode); |
| pcnt_ll_set_level_action(data->hal.dev, i, 0, |
| unit_config->channel_config[0].ctrl_h_mode, |
| unit_config->channel_config[0].ctrl_l_mode); |
| pcnt_ll_set_level_action(data->hal.dev, i, 1, |
| unit_config->channel_config[1].ctrl_h_mode, |
| unit_config->channel_config[1].ctrl_l_mode); |
| pcnt_ll_clear_count(data->hal.dev, i); |
| |
| pcnt_ll_set_glitch_filter_thres(data->hal.dev, i, unit_config->filter); |
| pcnt_ll_enable_glitch_filter(data->hal.dev, i, (bool)unit_config->filter); |
| |
| pcnt_ll_start_count(data->hal.dev, i); |
| } |
| |
| return 0; |
| } |
| |
| static int pcnt_esp32_attr_set_thresh(const struct device *dev, enum sensor_attribute attr, |
| const struct sensor_value *val) |
| { |
| const struct pcnt_esp32_config *config = dev->config; |
| struct pcnt_esp32_data *data = (struct pcnt_esp32_data *const)(dev)->data; |
| |
| for (uint8_t i = 0; i < config->unit_len; i++) { |
| struct pcnt_esp32_unit_config *unit_config = &config->unit_config[i]; |
| |
| switch (attr) { |
| case SENSOR_ATTR_LOWER_THRESH: |
| unit_config->l_thr = val->val1; |
| pcnt_ll_set_thres_value(data->hal.dev, i, 0, unit_config->l_thr); |
| pcnt_ll_enable_thres_event(data->hal.dev, i, 0, true); |
| break; |
| case SENSOR_ATTR_UPPER_THRESH: |
| unit_config->h_thr = val->val1; |
| pcnt_ll_set_thres_value(data->hal.dev, i, 1, unit_config->h_thr); |
| pcnt_ll_enable_thres_event(data->hal.dev, i, 1, true); |
| break; |
| default: |
| return -ENOTSUP; |
| } |
| pcnt_ll_stop_count(data->hal.dev, i); |
| pcnt_ll_clear_count(data->hal.dev, i); |
| pcnt_ll_start_count(data->hal.dev, i); |
| } |
| |
| return 0; |
| } |
| |
| static int pcnt_esp32_attr_set_offset(const struct device *dev, const struct sensor_value *val) |
| { |
| const struct pcnt_esp32_config *config = dev->config; |
| |
| for (uint8_t i = 0; i < config->unit_len; i++) { |
| struct pcnt_esp32_unit_config *unit_config = &config->unit_config[i]; |
| |
| unit_config->offset = val->val1; |
| } |
| |
| return 0; |
| } |
| |
| static int pcnt_esp32_attr_set(const struct device *dev, enum sensor_channel chan, |
| enum sensor_attribute attr, const struct sensor_value *val) |
| { |
| if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_ROTATION) { |
| return -ENOTSUP; |
| } |
| |
| switch (attr) { |
| case SENSOR_ATTR_LOWER_THRESH: |
| case SENSOR_ATTR_UPPER_THRESH: |
| return pcnt_esp32_attr_set_thresh(dev, attr, val); |
| case SENSOR_ATTR_OFFSET: |
| return pcnt_esp32_attr_set_offset(dev, val); |
| default: |
| return -ENOTSUP; |
| } |
| |
| return 0; |
| } |
| |
| static int pcnt_esp32_attr_get(const struct device *dev, enum sensor_channel chan, |
| enum sensor_attribute attr, struct sensor_value *val) |
| { |
| const struct pcnt_esp32_config *config = dev->config; |
| |
| if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_ROTATION) { |
| return -ENOTSUP; |
| } |
| |
| switch (attr) { |
| case SENSOR_ATTR_LOWER_THRESH: |
| val->val1 = config->unit_config[0].l_thr; |
| break; |
| case SENSOR_ATTR_UPPER_THRESH: |
| val->val1 = config->unit_config[0].h_thr; |
| break; |
| case SENSOR_ATTR_OFFSET: |
| val->val1 = config->unit_config[0].offset; |
| break; |
| default: |
| return -ENOTSUP; |
| } |
| |
| val->val2 = 0; |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_PCNT_ESP32_TRIGGER |
| static void IRAM_ATTR pcnt_esp32_isr(const struct device *dev) |
| { |
| struct pcnt_esp32_data *data = (struct pcnt_esp32_data *const)(dev)->data; |
| |
| uint32_t pcnt_intr_status; |
| uint32_t pcnt_unit_status; |
| |
| pcnt_intr_status = pcnt_ll_get_intr_status(data->hal.dev); |
| pcnt_ll_clear_intr_status(data->hal.dev, pcnt_intr_status); |
| |
| if (pcnt_intr_status & PCNT_INTR_UNIT_0) { |
| pcnt_unit_status = pcnt_ll_get_unit_status(data->hal.dev, 0); |
| } else if (pcnt_intr_status & PCNT_INTR_UNIT_1) { |
| pcnt_unit_status = pcnt_ll_get_unit_status(data->hal.dev, 1); |
| } else if (pcnt_intr_status & PCNT_INTR_UNIT_2) { |
| pcnt_unit_status = pcnt_ll_get_unit_status(data->hal.dev, 2); |
| } else if (pcnt_intr_status & PCNT_INTR_UNIT_3) { |
| pcnt_unit_status = pcnt_ll_get_unit_status(data->hal.dev, 3); |
| #ifdef CONFIG_SOC_SERIES_ESP32 |
| } else if (pcnt_intr_status & PCNT_INTR_UNIT_4) { |
| pcnt_unit_status = pcnt_ll_get_unit_status(data->hal.dev, 4); |
| } else if (pcnt_intr_status & PCNT_INTR_UNIT_5) { |
| pcnt_unit_status = pcnt_ll_get_unit_status(data->hal.dev, 5); |
| } else if (pcnt_intr_status & PCNT_INTR_UNIT_6) { |
| pcnt_unit_status = pcnt_ll_get_unit_status(data->hal.dev, 6); |
| } else if (pcnt_intr_status & PCNT_INTR_UNIT_7) { |
| pcnt_unit_status = pcnt_ll_get_unit_status(data->hal.dev, 7); |
| #endif /* CONFIG_SOC_SERIES_ESP32 */ |
| } else { |
| return; |
| } |
| |
| if (!(pcnt_unit_status & PCNT_INTR_THRES_0) && !(pcnt_unit_status & PCNT_INTR_THRES_1)) { |
| return; |
| } |
| |
| if (!data->trigger_handler) { |
| return; |
| } |
| |
| data->trigger_handler(dev, data->trigger); |
| } |
| |
| static int pcnt_esp32_trigger_set(const struct device *dev, const struct sensor_trigger *trig, |
| sensor_trigger_handler_t handler) |
| { |
| int ret; |
| const struct pcnt_esp32_config *config = dev->config; |
| struct pcnt_esp32_data *data = (struct pcnt_esp32_data *const)(dev)->data; |
| |
| if (trig->type != SENSOR_TRIG_THRESHOLD) { |
| return -ENOTSUP; |
| } |
| |
| if ((trig->chan != SENSOR_CHAN_ALL) && (trig->chan != SENSOR_CHAN_ROTATION)) { |
| return -ENOTSUP; |
| } |
| |
| if (!handler) { |
| return -EINVAL; |
| } |
| |
| data->trigger_handler = handler; |
| data->trigger = trig; |
| |
| ret = esp_intr_alloc(config->irq_src, 0, (intr_handler_t)pcnt_esp32_isr, (void *)dev, NULL); |
| if (ret != 0) { |
| LOG_ERR("pcnt isr registration failed (%d)", ret); |
| return ret; |
| } |
| |
| pcnt_ll_enable_intr(data->hal.dev, 1, true); |
| |
| return 0; |
| } |
| #endif /* CONFIG_PCNT_ESP32_TRIGGER */ |
| |
| static const struct sensor_driver_api pcnt_esp32_api = { |
| .sample_fetch = pcnt_esp32_sample_fetch, |
| .channel_get = pcnt_esp32_channel_get, |
| .attr_set = pcnt_esp32_attr_set, |
| .attr_get = pcnt_esp32_attr_get, |
| #ifdef CONFIG_PCNT_ESP32_TRIGGER |
| .trigger_set = pcnt_esp32_trigger_set, |
| #endif /* CONFIG_PCNT_ESP32_TRIGGER */ |
| }; |
| |
| PINCTRL_DT_INST_DEFINE(0); |
| |
| #define UNIT_CONFIG(node_id) \ |
| { \ |
| .idx = DT_REG_ADDR(node_id), \ |
| .filter = DT_PROP_OR(node_id, filter, 0) > 1024 ? 1024 \ |
| : DT_PROP_OR(node_id, filter, 0), \ |
| .channel_config[0] = \ |
| { \ |
| .sig_pos_mode = DT_PROP_OR(DT_CHILD(node_id, channela_0), \ |
| sig_pos_mode, 0), \ |
| .sig_neg_mode = DT_PROP_OR(DT_CHILD(node_id, channela_0), \ |
| sig_neg_mode, 0), \ |
| .ctrl_l_mode = \ |
| DT_PROP_OR(DT_CHILD(node_id, channela_0), ctrl_l_mode, 0), \ |
| .ctrl_h_mode = \ |
| DT_PROP_OR(DT_CHILD(node_id, channela_0), ctrl_h_mode, 0), \ |
| }, \ |
| .channel_config[1] = \ |
| { \ |
| .sig_pos_mode = DT_PROP_OR(DT_CHILD(node_id, channelb_0), \ |
| sig_pos_mode, 0), \ |
| .sig_neg_mode = DT_PROP_OR(DT_CHILD(node_id, channelb_0), \ |
| sig_neg_mode, 0), \ |
| .ctrl_l_mode = \ |
| DT_PROP_OR(DT_CHILD(node_id, channelb_0), ctrl_l_mode, 0), \ |
| .ctrl_h_mode = \ |
| DT_PROP_OR(DT_CHILD(node_id, channelb_0), ctrl_h_mode, 0), \ |
| }, \ |
| }, |
| |
| static struct pcnt_esp32_unit_config unit_config[] = {DT_INST_FOREACH_CHILD(0, UNIT_CONFIG)}; |
| |
| static struct pcnt_esp32_config pcnt_esp32_config = { |
| .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0), |
| .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(0)), |
| .clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(0, offset), |
| .irq_src = DT_INST_IRQN(0), |
| .unit_config = unit_config, |
| .unit_len = ARRAY_SIZE(unit_config), |
| }; |
| |
| static struct pcnt_esp32_data pcnt_esp32_data = { |
| .hal = { |
| .dev = (pcnt_dev_t *)DT_INST_REG_ADDR(0), |
| }, |
| .cmd_mux = Z_MUTEX_INITIALIZER(pcnt_esp32_data.cmd_mux), |
| #ifdef CONFIG_PCNT_ESP32_TRIGGER |
| .trigger_handler = NULL, |
| #endif /* CONFIG_PCNT_ESP32_TRIGGER */ |
| }; |
| |
| SENSOR_DEVICE_DT_INST_DEFINE(0, &pcnt_esp32_init, NULL, |
| &pcnt_esp32_data, |
| &pcnt_esp32_config, |
| POST_KERNEL, |
| CONFIG_KERNEL_INIT_PRIORITY_DEVICE, |
| &pcnt_esp32_api); |