zephyr/drivers/sensor/pcnt_esp32/pcnt_esp32.c

424 lines
13 KiB
C

/*
* 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_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_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;
#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;
struct sensor_trigger trigger;
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_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_ESP32 */
} else {
return;
}
if (!(pcnt_unit_status & PCNT_INTR_THRES_0) && !(pcnt_unit_status & PCNT_INTR_THRES_1)) {
return;
}
if (!data->trigger_handler) {
return;
}
trigger.type = SENSOR_TRIG_THRESHOLD;
trigger.chan = SENSOR_CHAN_ROTATION;
data->trigger_handler(dev, &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;
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 */
};
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);