drivers: sensor: qdec_nrfx: Add multi-instance support for QDEC SHIM
Reworked QDEC SHIM to suppor multi-instance peripheral. Patch includes Kconfig alignment for proper instance handling. Signed-off-by: Adam Wojasinski <adam.wojasinski@nordicsemi.no>
This commit is contained in:
parent
af36179859
commit
09fa46ee4e
4 changed files with 149 additions and 108 deletions
|
@ -5,7 +5,7 @@ config QDEC_NRFX
|
|||
bool "Nordic QDEC nrfx driver"
|
||||
default y
|
||||
depends on DT_HAS_NORDIC_NRF_QDEC_ENABLED
|
||||
depends on HAS_HW_NRF_QDEC0
|
||||
select NRFX_QDEC
|
||||
select NRFX_QDEC0 if HAS_HW_NRF_QDEC0
|
||||
select NRFX_QDEC1 if HAS_HW_NRF_QDEC1
|
||||
help
|
||||
Enable support for nrfx QDEC driver for nRF MCU series.
|
||||
|
|
|
@ -26,18 +26,21 @@ LOG_MODULE_REGISTER(qdec_nrfx, CONFIG_SENSOR_LOG_LEVEL);
|
|||
|
||||
|
||||
struct qdec_nrfx_data {
|
||||
int32_t acc;
|
||||
int32_t acc;
|
||||
sensor_trigger_handler_t data_ready_handler;
|
||||
const struct sensor_trigger *data_ready_trigger;
|
||||
};
|
||||
|
||||
static struct qdec_nrfx_data qdec_nrfx_data;
|
||||
struct qdec_nrfx_config {
|
||||
nrfx_qdec_t qdec;
|
||||
nrfx_qdec_config_t config;
|
||||
void (*irq_connect)(void);
|
||||
const struct pinctrl_dev_config *pcfg;
|
||||
uint32_t enable_pin;
|
||||
int32_t steps;
|
||||
};
|
||||
|
||||
PINCTRL_DT_DEFINE(DT_DRV_INST(0));
|
||||
static const struct pinctrl_dev_config *qdec_nrfx_pcfg =
|
||||
PINCTRL_DT_DEV_CONFIG_GET(DT_DRV_INST(0));
|
||||
|
||||
static void accumulate(struct qdec_nrfx_data *data, int16_t acc)
|
||||
static void accumulate(struct qdec_nrfx_data *data, int32_t acc)
|
||||
{
|
||||
unsigned int key = irq_lock();
|
||||
|
||||
|
@ -54,39 +57,30 @@ static void accumulate(struct qdec_nrfx_data *data, int16_t acc)
|
|||
static int qdec_nrfx_sample_fetch(const struct device *dev,
|
||||
enum sensor_channel chan)
|
||||
{
|
||||
struct qdec_nrfx_data *data = &qdec_nrfx_data;
|
||||
|
||||
int16_t acc;
|
||||
int16_t accdbl;
|
||||
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
LOG_DBG("");
|
||||
const struct qdec_nrfx_config *config = dev->config;
|
||||
struct qdec_nrfx_data *data = dev->data;
|
||||
int32_t acc;
|
||||
uint32_t accdbl;
|
||||
|
||||
if ((chan != SENSOR_CHAN_ALL) && (chan != SENSOR_CHAN_ROTATION)) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
nrfx_qdec_accumulators_read(&acc, &accdbl);
|
||||
nrfx_qdec_accumulators_read(&config->qdec, &acc, &accdbl);
|
||||
|
||||
accumulate(data, acc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define QDEC_STEPS DT_INST_PROP(0, steps)
|
||||
|
||||
static int qdec_nrfx_channel_get(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
struct qdec_nrfx_data *data = &qdec_nrfx_data;
|
||||
struct qdec_nrfx_data *data = dev->data;
|
||||
const struct qdec_nrfx_config *config = dev->config;
|
||||
unsigned int key;
|
||||
int32_t acc;
|
||||
const int32_t steps = QDEC_STEPS;
|
||||
|
||||
ARG_UNUSED(dev);
|
||||
LOG_DBG("");
|
||||
|
||||
if (chan != SENSOR_CHAN_ROTATION) {
|
||||
return -ENOTSUP;
|
||||
|
@ -97,14 +91,11 @@ static int qdec_nrfx_channel_get(const struct device *dev,
|
|||
data->acc = 0;
|
||||
irq_unlock(key);
|
||||
|
||||
BUILD_ASSERT(QDEC_STEPS > 0, "only positive number valid");
|
||||
BUILD_ASSERT(QDEC_STEPS <= 2048, "overflow possible");
|
||||
|
||||
val->val1 = (acc * FULL_ANGLE) / steps;
|
||||
val->val2 = (acc * FULL_ANGLE) - (val->val1 * steps);
|
||||
val->val1 = (acc * FULL_ANGLE) / config->steps;
|
||||
val->val2 = (acc * FULL_ANGLE) - (val->val1 * config->steps);
|
||||
if (val->val2 != 0) {
|
||||
val->val2 *= 1000000;
|
||||
val->val2 /= steps;
|
||||
val->val2 /= config->steps;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -112,14 +103,11 @@ static int qdec_nrfx_channel_get(const struct device *dev,
|
|||
|
||||
static int qdec_nrfx_trigger_set(const struct device *dev,
|
||||
const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler)
|
||||
sensor_trigger_handler_t handler)
|
||||
{
|
||||
struct qdec_nrfx_data *data = &qdec_nrfx_data;
|
||||
struct qdec_nrfx_data *data = dev->data;
|
||||
unsigned int key;
|
||||
|
||||
ARG_UNUSED(dev);
|
||||
LOG_DBG("");
|
||||
|
||||
if (trig->type != SENSOR_TRIG_DATA_READY) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
@ -137,23 +125,26 @@ static int qdec_nrfx_trigger_set(const struct device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void qdec_nrfx_event_handler(nrfx_qdec_event_t event)
|
||||
static void qdec_nrfx_event_handler(nrfx_qdec_event_t event, void *p_context)
|
||||
{
|
||||
const struct device *dev = p_context;
|
||||
struct qdec_nrfx_data *dev_data = dev->data;
|
||||
|
||||
sensor_trigger_handler_t handler;
|
||||
const struct sensor_trigger *trig;
|
||||
unsigned int key;
|
||||
|
||||
switch (event.type) {
|
||||
case NRF_QDEC_EVENT_REPORTRDY:
|
||||
accumulate(&qdec_nrfx_data, event.data.report.acc);
|
||||
accumulate(dev_data, event.data.report.acc);
|
||||
|
||||
key = irq_lock();
|
||||
handler = qdec_nrfx_data.data_ready_handler;
|
||||
trig = qdec_nrfx_data.data_ready_trigger;
|
||||
handler = dev_data->data_ready_handler;
|
||||
trig = dev_data->data_ready_trigger;
|
||||
irq_unlock(key);
|
||||
|
||||
if (handler) {
|
||||
handler(DEVICE_DT_INST_GET(0), trig);
|
||||
handler(dev, trig);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -163,79 +154,47 @@ static void qdec_nrfx_event_handler(nrfx_qdec_event_t event)
|
|||
}
|
||||
}
|
||||
|
||||
static void qdec_nrfx_gpio_ctrl(bool enable)
|
||||
static void qdec_nrfx_gpio_ctrl(const struct device *dev, bool enable)
|
||||
{
|
||||
#if DT_INST_NODE_HAS_PROP(0, enable_pin)
|
||||
uint32_t val = (enable)?(0):(1);
|
||||
const struct qdec_nrfx_config *config = dev->config;
|
||||
|
||||
nrf_gpio_pin_write(DT_INST_PROP(0, enable_pin), val);
|
||||
nrf_gpio_cfg_output(DT_INST_PROP(0, enable_pin));
|
||||
#endif
|
||||
if (config->enable_pin != NRF_QDEC_PIN_NOT_CONNECTED) {
|
||||
|
||||
uint32_t val = (enable)?(0):(1);
|
||||
|
||||
nrf_gpio_pin_write(config->enable_pin, val);
|
||||
nrf_gpio_cfg_output(config->enable_pin);
|
||||
}
|
||||
}
|
||||
|
||||
NRF_DT_CHECK_NODE_HAS_PINCTRL_SLEEP(DT_DRV_INST(0));
|
||||
|
||||
static int qdec_nrfx_init(const struct device *dev)
|
||||
{
|
||||
static const nrfx_qdec_config_t config = {
|
||||
.reportper = NRF_QDEC_REPORTPER_40,
|
||||
.sampleper = NRF_QDEC_SAMPLEPER_2048us,
|
||||
.skip_gpio_cfg = true,
|
||||
.skip_psel_cfg = true,
|
||||
.ledpre = DT_INST_PROP(0, led_pre),
|
||||
.ledpol = NRF_QDEC_LEPOL_ACTIVE_HIGH,
|
||||
};
|
||||
|
||||
nrfx_err_t nerr;
|
||||
|
||||
LOG_DBG("");
|
||||
|
||||
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority),
|
||||
nrfx_isr, nrfx_qdec_irq_handler, 0);
|
||||
|
||||
int ret = pinctrl_apply_state(qdec_nrfx_pcfg, PINCTRL_STATE_DEFAULT);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
nerr = nrfx_qdec_init(&config, qdec_nrfx_event_handler);
|
||||
if (nerr == NRFX_ERROR_INVALID_STATE) {
|
||||
LOG_ERR("qdec already in use");
|
||||
return -EBUSY;
|
||||
} else if (nerr != NRFX_SUCCESS) {
|
||||
LOG_ERR("failed to initialize qdec");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
qdec_nrfx_gpio_ctrl(true);
|
||||
nrfx_qdec_enable();
|
||||
|
||||
return 0;
|
||||
}
|
||||
static const struct sensor_driver_api qdec_nrfx_driver_api = {
|
||||
.sample_fetch = qdec_nrfx_sample_fetch,
|
||||
.channel_get = qdec_nrfx_channel_get,
|
||||
.trigger_set = qdec_nrfx_trigger_set,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_DEVICE
|
||||
static int qdec_nrfx_pm_action(const struct device *dev,
|
||||
enum pm_device_action action)
|
||||
{
|
||||
const struct qdec_nrfx_config *config = dev->config;
|
||||
int ret = 0;
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
ret = pinctrl_apply_state(qdec_nrfx_pcfg,
|
||||
ret = pinctrl_apply_state(config->pcfg,
|
||||
PINCTRL_STATE_DEFAULT);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
qdec_nrfx_gpio_ctrl(true);
|
||||
nrfx_qdec_enable();
|
||||
qdec_nrfx_gpio_ctrl(dev, true);
|
||||
nrfx_qdec_enable(&config->qdec);
|
||||
break;
|
||||
|
||||
case PM_DEVICE_ACTION_TURN_OFF:
|
||||
/* device must be uninitialized */
|
||||
nrfx_qdec_uninit();
|
||||
ret = pinctrl_apply_state(qdec_nrfx_pcfg,
|
||||
nrfx_qdec_uninit(&config->qdec);
|
||||
ret = pinctrl_apply_state(config->pcfg,
|
||||
PINCTRL_STATE_SLEEP);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
|
@ -244,9 +203,9 @@ static int qdec_nrfx_pm_action(const struct device *dev,
|
|||
|
||||
case PM_DEVICE_ACTION_SUSPEND:
|
||||
/* device must be suspended */
|
||||
nrfx_qdec_disable();
|
||||
qdec_nrfx_gpio_ctrl(false);
|
||||
ret = pinctrl_apply_state(qdec_nrfx_pcfg,
|
||||
nrfx_qdec_disable(&config->qdec);
|
||||
qdec_nrfx_gpio_ctrl(dev, false);
|
||||
ret = pinctrl_apply_state(config->pcfg,
|
||||
PINCTRL_STATE_SLEEP);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
|
@ -260,15 +219,82 @@ static int qdec_nrfx_pm_action(const struct device *dev,
|
|||
}
|
||||
#endif /* CONFIG_PM_DEVICE */
|
||||
|
||||
static int qdec_nrfx_init(const struct device *dev)
|
||||
{
|
||||
const struct qdec_nrfx_config *dev_config = dev->config;
|
||||
|
||||
static const struct sensor_driver_api qdec_nrfx_driver_api = {
|
||||
.sample_fetch = qdec_nrfx_sample_fetch,
|
||||
.channel_get = qdec_nrfx_channel_get,
|
||||
.trigger_set = qdec_nrfx_trigger_set,
|
||||
};
|
||||
dev_config->irq_connect();
|
||||
|
||||
PM_DEVICE_DT_INST_DEFINE(0, qdec_nrfx_pm_action);
|
||||
int err = pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_DEFAULT);
|
||||
|
||||
SENSOR_DEVICE_DT_INST_DEFINE(0, qdec_nrfx_init,
|
||||
PM_DEVICE_DT_INST_GET(0), NULL, NULL, POST_KERNEL,
|
||||
CONFIG_SENSOR_INIT_PRIORITY, &qdec_nrfx_driver_api);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
nrfx_err_t nerr = nrfx_qdec_init(&dev_config->qdec,
|
||||
&dev_config->config,
|
||||
qdec_nrfx_event_handler,
|
||||
(void *)dev);
|
||||
|
||||
if (nerr == NRFX_ERROR_INVALID_STATE) {
|
||||
LOG_ERR("qdec already in use");
|
||||
return -EBUSY;
|
||||
} else if (nerr != NRFX_SUCCESS) {
|
||||
LOG_ERR("failed to initialize qdec");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
qdec_nrfx_gpio_ctrl(dev, true);
|
||||
nrfx_qdec_enable(&dev_config->qdec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define QDEC(idx) DT_NODELABEL(qdec##idx)
|
||||
#define QDEC_PROP(idx, prop) DT_PROP(QDEC(idx), prop)
|
||||
|
||||
#define SENSOR_NRFX_QDEC_DEVICE(idx) \
|
||||
NRF_DT_CHECK_NODE_HAS_PINCTRL_SLEEP(QDEC(idx)); \
|
||||
BUILD_ASSERT(QDEC_PROP(idx, steps) > 0, \
|
||||
"Wrong QDEC"#idx" steps setting in dts. Only positive number valid"); \
|
||||
BUILD_ASSERT(QDEC_PROP(idx, steps) <= 2048, \
|
||||
"Wrong QDEC"#idx" steps setting in dts. Overflow possible"); \
|
||||
static void irq_connect##idx(void) \
|
||||
{ \
|
||||
IRQ_CONNECT(DT_IRQN(QDEC(idx)), DT_IRQ(QDEC(idx), priority), \
|
||||
nrfx_isr, nrfx_qdec_##idx##_irq_handler, 0); \
|
||||
} \
|
||||
static struct qdec_nrfx_data qdec_##idx##_data; \
|
||||
PINCTRL_DT_DEFINE(QDEC(idx)); \
|
||||
static struct qdec_nrfx_config qdec_##idx##_config = { \
|
||||
.qdec = NRFX_QDEC_INSTANCE(idx), \
|
||||
.config = { \
|
||||
.reportper = NRF_QDEC_REPORTPER_40, \
|
||||
.sampleper = NRF_QDEC_SAMPLEPER_2048US, \
|
||||
.skip_gpio_cfg = true, \
|
||||
.skip_psel_cfg = true, \
|
||||
.ledpre = QDEC_PROP(idx, led_pre), \
|
||||
.ledpol = NRF_QDEC_LEPOL_ACTIVE_HIGH, \
|
||||
}, \
|
||||
.irq_connect = irq_connect##idx, \
|
||||
.pcfg = PINCTRL_DT_DEV_CONFIG_GET(QDEC(idx)), \
|
||||
.enable_pin = DT_PROP_OR(QDEC(idx), enable_pin, NRF_QDEC_PIN_NOT_CONNECTED), \
|
||||
.steps = QDEC_PROP(idx, steps), \
|
||||
}; \
|
||||
PM_DEVICE_DT_DEFINE(QDEC(idx), qdec_nrfx_pm_action); \
|
||||
SENSOR_DEVICE_DT_DEFINE(QDEC(idx), \
|
||||
qdec_nrfx_init, \
|
||||
PM_DEVICE_DT_GET(QDEC(idx)), \
|
||||
&qdec_##idx##_data, \
|
||||
&qdec_##idx##_config, \
|
||||
POST_KERNEL, \
|
||||
CONFIG_SENSOR_INIT_PRIORITY, \
|
||||
&qdec_nrfx_driver_api)
|
||||
|
||||
#if DT_NODE_HAS_STATUS(DT_NODELABEL(qdec0), okay)
|
||||
SENSOR_NRFX_QDEC_DEVICE(0);
|
||||
#endif
|
||||
|
||||
#if DT_NODE_HAS_STATUS(DT_NODELABEL(qdec1), okay)
|
||||
SENSOR_NRFX_QDEC_DEVICE(1);
|
||||
#endif
|
||||
|
|
|
@ -140,8 +140,17 @@ config NRFX_PWM3
|
|||
select NRFX_PWM
|
||||
|
||||
config NRFX_QDEC
|
||||
bool "QDEC driver"
|
||||
depends on $(dt_has_compat,$(DT_COMPAT_NORDIC_NRF_QDEC))
|
||||
bool
|
||||
|
||||
config NRFX_QDEC0
|
||||
bool "QDEC0 driver instance"
|
||||
depends on $(dt_nodelabel_has_compat,qdec0,$(DT_COMPAT_NORDIC_NRF_QDEC))
|
||||
select NRFX_QDEC
|
||||
|
||||
config NRFX_QDEC1
|
||||
bool "QDEC1 driver instance"
|
||||
depends on $(dt_nodelabel_has_compat,qdec1,$(DT_COMPAT_NORDIC_NRF_QDEC))
|
||||
select NRFX_QDEC
|
||||
|
||||
config NRFX_QSPI
|
||||
bool "QSPI driver"
|
||||
|
|
|
@ -181,6 +181,12 @@
|
|||
#ifdef CONFIG_NRFX_QDEC
|
||||
#define NRFX_QDEC_ENABLED 1
|
||||
#endif
|
||||
#ifdef CONFIG_NRFX_QDEC0
|
||||
#define NRFX_QDEC0_ENABLED 1
|
||||
#endif
|
||||
#ifdef CONFIG_NRFX_QDEC1
|
||||
#define NRFX_QDEC1_ENABLED 1
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NRFX_QSPI
|
||||
#define NRFX_QSPI_ENABLED 1
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue