/* * 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 #include #include #include #include #include #include #include #include #include #ifdef CONFIG_PCNT_ESP32_TRIGGER #include #endif /* CONFIG_PCNT_ESP32_TRIGGER */ #include 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_source; const int irq_priority; const int irq_flags; 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) { 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_source, ESP_PRIO_TO_FLAGS(config->irq_priority) | ESP_INT_FLAGS_CHECK(config->irq_flags) | ESP_INTR_FLAG_IRAM, (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 DEVICE_API(sensor, 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_source = DT_INST_IRQ_BY_IDX(0, 0, irq), .irq_priority = DT_INST_IRQ_BY_IDX(0, 0, priority), .irq_flags = DT_INST_IRQ_BY_IDX(0, 0, flags), .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);