drivers: sensor: Add adc-comparator binding and implementation for NPCX
Comparator will monitor signal though ADC channel, based on user configuration, callback will be triggered. This will enable comparator functionality for nuvoton MCU utilizing its ADC threshold detection feature. Implementation is exported through sensor trigger API. Use of CONFIG_ADC_CMP_NPCX is required. Signed-off-by: Bernardo Perez Priego <bernardo.perez.priego@intel.com>
This commit is contained in:
parent
1b07c9ed47
commit
cfe4d51b4f
12 changed files with 811 additions and 5 deletions
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <assert.h>
|
||||
#include <drivers/adc.h>
|
||||
#include <drivers/adc/adc_npcx_threshold.h>
|
||||
#include <drivers/clock_control.h>
|
||||
#include <kernel.h>
|
||||
#include <soc.h>
|
||||
|
@ -37,6 +38,12 @@ LOG_MODULE_REGISTER(adc_npcx, CONFIG_ADC_LOG_LEVEL);
|
|||
#define NPCX_ADC_CHN_CONVERSION_MODE 0
|
||||
#define NPCX_ADC_SCAN_CONVERSION_MODE 1
|
||||
|
||||
/* ADC threshold detector number */
|
||||
#define NPCX_ADC_THRESHOLD_COUNT DT_INST_PROP(0, threshold_count)
|
||||
|
||||
#define ADC_NPCX_THRVAL_RESOLUTION 10
|
||||
#define ADC_NPCX_THRVAL_MAX BIT_MASK(ADC_NPCX_THRVAL_RESOLUTION)
|
||||
|
||||
/* Device config */
|
||||
struct adc_npcx_config {
|
||||
/* adc controller base address */
|
||||
|
@ -47,6 +54,46 @@ struct adc_npcx_config {
|
|||
const struct npcx_alt *alts_list;
|
||||
};
|
||||
|
||||
struct adc_npcx_threshold_control {
|
||||
/*
|
||||
* Selects ADC channel number, for which the measured data is compared
|
||||
* for threshold detection.
|
||||
*/
|
||||
uint8_t chnsel;
|
||||
/*
|
||||
* Sets relation between measured value and assetion threshold value.
|
||||
* in thrval:
|
||||
* 0: Threshold event is generated if Measured data > thrval.
|
||||
* 1: Threshold event is generated if Measured data <= thrval.
|
||||
*/
|
||||
bool l_h;
|
||||
/* Sets the threshold value to which measured data is compared. */
|
||||
uint16_t thrval;
|
||||
/*
|
||||
* Pointer of work queue thread to be notified when threshold assertion
|
||||
* occurs.
|
||||
*/
|
||||
struct k_work *work;
|
||||
};
|
||||
|
||||
struct adc_npcx_threshold_data {
|
||||
/*
|
||||
* While threshold interruption is enabled we need to resume to repetitive
|
||||
* sampling mode after adc_npcx_read is called. This variable records
|
||||
* channels being used in repetitive mode in order to set ADC registers
|
||||
* back to threshold detection when adc_npcx_read is completed.
|
||||
*/
|
||||
uint16_t repetitive_channels;
|
||||
/*
|
||||
* While threshold interruption is enabled, adc_npcx_read must disable
|
||||
* all active threshold running to avoid race condition, this variable
|
||||
* helps restore active threshods after adc_npcs_read has finnished.
|
||||
*/
|
||||
uint8_t active_thresholds;
|
||||
/* This array holds current configuration for each threshold. */
|
||||
struct adc_npcx_threshold_control control[NPCX_ADC_THRESHOLD_COUNT];
|
||||
};
|
||||
|
||||
/* Driver data */
|
||||
struct adc_npcx_data {
|
||||
/* Input clock for ADC converter */
|
||||
|
@ -64,6 +111,8 @@ struct adc_npcx_data {
|
|||
uint16_t *repeat_buffer;
|
||||
/* end pointer of buffer to ensure enough space for storing ADC data. */
|
||||
uint16_t *buf_end;
|
||||
/* Threshold comparator data pointer */
|
||||
struct adc_npcx_threshold_data *threshold_data;
|
||||
};
|
||||
|
||||
/* Driver convenience defines */
|
||||
|
@ -75,6 +124,7 @@ static void adc_npcx_isr(const struct device *dev)
|
|||
const struct adc_npcx_config *config = dev->config;
|
||||
struct adc_npcx_data *const data = dev->data;
|
||||
struct adc_reg *const inst = HAL_INSTANCE(dev);
|
||||
struct adc_npcx_threshold_data *const t_data = data->threshold_data;
|
||||
uint16_t status = inst->ADCSTS;
|
||||
uint16_t result, channel;
|
||||
|
||||
|
@ -83,14 +133,16 @@ static void adc_npcx_isr(const struct device *dev)
|
|||
LOG_DBG("%s: status is %04X\n", __func__, status);
|
||||
|
||||
/* Is end of conversion cycle event? ie. Scan conversion is done. */
|
||||
if (IS_BIT_SET(status, NPCX_ADCSTS_EOCCEV)) {
|
||||
if (IS_BIT_SET(status, NPCX_ADCSTS_EOCCEV) &&
|
||||
IS_BIT_SET(inst->ADCCNF, NPCX_ADCCNF_INTECCEN)) {
|
||||
/* Stop conversion for scan conversion mode */
|
||||
inst->ADCCNF |= BIT(NPCX_ADCCNF_STOP);
|
||||
|
||||
/* Get result for each ADC selected channel */
|
||||
while (data->channels) {
|
||||
channel = find_lsb_set(data->channels) - 1;
|
||||
result = GET_FIELD(CHNDAT(config->base, channel), NPCX_CHNDAT_CHDAT_FIELD);
|
||||
result = GET_FIELD(CHNDAT(config->base, channel),
|
||||
NPCX_CHNDAT_CHDAT_FIELD);
|
||||
/*
|
||||
* Save ADC result and adc_npcx_validate_buffer_size()
|
||||
* already ensures that the buffer has enough space for
|
||||
|
@ -101,11 +153,45 @@ static void adc_npcx_isr(const struct device *dev)
|
|||
}
|
||||
data->channels &= ~BIT(channel);
|
||||
}
|
||||
/* Disable End of cyclic conversion interruption */
|
||||
inst->ADCCNF &= ~BIT(NPCX_ADCCNF_INTECCEN);
|
||||
|
||||
/* Turn off ADC and inform sampling is done */
|
||||
inst->ADCCNF &= ~(BIT(NPCX_ADCCNF_ADCEN));
|
||||
if (IS_ENABLED(CONFIG_ADC_CMP_NPCX) &&
|
||||
t_data->active_thresholds) {
|
||||
/* Set repetitive channels back */
|
||||
inst->ADCCS = t_data->repetitive_channels;
|
||||
/* Start conversion */
|
||||
inst->ADCCNF |= BIT(NPCX_ADCCNF_START);
|
||||
} else {
|
||||
/* Disable all channels */
|
||||
inst->ADCCS = 0;
|
||||
/* Turn off ADC */
|
||||
inst->ADCCNF &= ~(BIT(NPCX_ADCCNF_ADCEN));
|
||||
}
|
||||
/* Inform sampling is done */
|
||||
adc_context_on_sampling_done(&data->ctx, data->adc_dev);
|
||||
}
|
||||
|
||||
if (!(IS_ENABLED(CONFIG_ADC_CMP_NPCX) && t_data->active_thresholds)) {
|
||||
return;
|
||||
}
|
||||
uint16_t thrcts;
|
||||
|
||||
for (uint8_t i = 0; i < NPCX_ADC_THRESHOLD_COUNT; i++) {
|
||||
if (IS_BIT_SET(inst->THRCTS, i) && IS_BIT_SET(inst->THRCTS,
|
||||
(NPCX_THRCTS_THR1_IEN + i))) {
|
||||
/* Avoid clearing other threshold status */
|
||||
thrcts = inst->THRCTS &
|
||||
~GENMASK(NPCX_ADC_THRESHOLD_COUNT - 1, 0);
|
||||
/* Clear threshold status */
|
||||
thrcts |= BIT(i);
|
||||
inst->THRCTS = thrcts;
|
||||
/* Notify work thread */
|
||||
if (t_data->control[i].work) {
|
||||
k_work_submit(t_data->control[i].work);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -145,8 +231,14 @@ static void adc_npcx_start_scan(const struct device *dev)
|
|||
/* Turn on ADC first */
|
||||
inst->ADCCNF |= BIT(NPCX_ADCCNF_ADCEN);
|
||||
|
||||
/* Stop conversion for scan conversion mode */
|
||||
inst->ADCCNF |= BIT(NPCX_ADCCNF_STOP);
|
||||
|
||||
/* Clear end of cyclic conversion event status flag */
|
||||
inst->ADCSTS |= BIT(NPCX_ADCSTS_EOCCEV);
|
||||
|
||||
/* Update selected channels in scan mode by channels mask */
|
||||
inst->ADCCS = data->channels;
|
||||
inst->ADCCS |= data->channels;
|
||||
|
||||
/* Select 'Scan' Conversion mode. */
|
||||
SET_FIELD(inst->ADCCNF, NPCX_ADCCNF_ADCMD_FIELD,
|
||||
|
@ -291,6 +383,277 @@ static int adc_npcx_read_async(const struct device *dev,
|
|||
}
|
||||
#endif /* CONFIG_ADC_ASYNC */
|
||||
|
||||
static void adc_npcx_set_repetitive(const struct device *dev, int chnsel,
|
||||
uint8_t enable)
|
||||
{
|
||||
struct adc_reg *const inst = HAL_INSTANCE(dev);
|
||||
struct adc_npcx_data *const data = dev->data;
|
||||
struct adc_npcx_threshold_data *const t_data = data->threshold_data;
|
||||
|
||||
/* Stop ADC conversion */
|
||||
inst->ADCCNF |= BIT(NPCX_ADCCNF_STOP);
|
||||
|
||||
if (enable) {
|
||||
/* Turn on ADC */
|
||||
inst->ADCCNF |= BIT(NPCX_ADCCNF_ADCEN);
|
||||
/* Set ADC conversion code to SW conversion mode */
|
||||
SET_FIELD(inst->ADCCNF, NPCX_ADCCNF_ADCMD_FIELD,
|
||||
NPCX_ADC_SCAN_CONVERSION_MODE);
|
||||
/* Update number of channel to be converted */
|
||||
inst->ADCCS |= BIT(chnsel);
|
||||
/* Set conversion type to repetitive (runs continuously) */
|
||||
inst->ADCCNF |= BIT(NPCX_ADCCNF_ADCRPTC);
|
||||
|
||||
t_data->repetitive_channels |= BIT(chnsel);
|
||||
/* Start conversion */
|
||||
inst->ADCCNF |= BIT(NPCX_ADCCNF_START);
|
||||
} else {
|
||||
inst->ADCCS &= ~BIT(chnsel);
|
||||
|
||||
t_data->repetitive_channels &= ~BIT(chnsel);
|
||||
if (!t_data->repetitive_channels) {
|
||||
/* No thesholdd active left, disable repetitive mode */
|
||||
inst->ADCCNF &= ~BIT(NPCX_ADCCNF_ADCRPTC);
|
||||
/* Turn off ADC */
|
||||
inst->ADCCNF &= ~BIT(NPCX_ADCCNF_ADCEN);
|
||||
} else {
|
||||
/* Start conversion again */
|
||||
inst->ADCCNF |= BIT(NPCX_ADCCNF_START);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int adc_npcx_threshold_ctrl_set_param(const struct device *dev,
|
||||
const uint8_t th_sel,
|
||||
const struct adc_npcx_threshold_param
|
||||
*param)
|
||||
{
|
||||
struct adc_npcx_data *const data = dev->data;
|
||||
struct adc_npcx_threshold_data *const t_data = data->threshold_data;
|
||||
struct adc_npcx_threshold_control *const t_ctrl =
|
||||
&t_data->control[th_sel];
|
||||
int ret = 0;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_ADC_CMP_NPCX)) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (!param || th_sel >= NPCX_ADC_THRESHOLD_COUNT) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
adc_context_lock(&data->ctx, false, NULL);
|
||||
switch (param->type) {
|
||||
case ADC_NPCX_THRESHOLD_PARAM_CHNSEL:
|
||||
if (param->val >= NPCX_ADC_CH_COUNT) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
t_ctrl->chnsel = (uint8_t)param->val;
|
||||
break;
|
||||
|
||||
case ADC_NPCX_THRESHOLD_PARAM_L_H:
|
||||
t_ctrl->l_h = !!param->val;
|
||||
break;
|
||||
|
||||
case ADC_NPCX_THRESHOLD_PARAM_THVAL:
|
||||
if (param->val == 0 || param->val >= ADC_NPCX_THRVAL_MAX) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
t_ctrl->thrval = (uint16_t)param->val;
|
||||
break;
|
||||
|
||||
case ADC_NPCX_THRESHOLD_PARAM_WORK:
|
||||
if (param->val == 0) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
t_ctrl->work = (struct k_work *)param->val;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
adc_context_release(&data->ctx, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc_npcx_threshold_ctrl_setup(const struct device *dev,
|
||||
const uint8_t th_sel)
|
||||
{
|
||||
struct adc_npcx_data *const data = dev->data;
|
||||
struct adc_npcx_threshold_data *const t_data = data->threshold_data;
|
||||
const struct adc_npcx_config *config = dev->config;
|
||||
struct adc_npcx_threshold_control *const t_ctrl =
|
||||
&t_data->control[th_sel];
|
||||
|
||||
if (th_sel >= NPCX_ADC_THRESHOLD_COUNT) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
adc_context_lock(&data->ctx, false, NULL);
|
||||
|
||||
if (t_data->active_thresholds & BIT(th_sel)) {
|
||||
/* Unable to setup threshold parameters while active */
|
||||
adc_context_release(&data->ctx, 0);
|
||||
LOG_ERR("Threshold selected (%d) is active!", th_sel);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (t_ctrl->chnsel >= NPCX_ADC_CH_COUNT ||
|
||||
t_ctrl->thrval >= NPCX_ADC_VREF_VOL ||
|
||||
t_ctrl->thrval == 0 || t_ctrl->work == 0) {
|
||||
adc_context_release(&data->ctx, 0);
|
||||
LOG_ERR("Threshold selected (%d) is not configured!", th_sel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
SET_FIELD(THRCTL(config->base, (th_sel + 1)),
|
||||
NPCX_THRCTL_CHNSEL, t_ctrl->chnsel);
|
||||
|
||||
if (t_ctrl->l_h) {
|
||||
THRCTL(config->base, (th_sel + 1)) |= BIT(NPCX_THRCTL_L_H);
|
||||
} else {
|
||||
THRCTL(config->base, (th_sel + 1)) &= ~BIT(NPCX_THRCTL_L_H);
|
||||
}
|
||||
/* Set the threshold value. */
|
||||
SET_FIELD(THRCTL(config->base, (th_sel + 1)), NPCX_THRCTL_THRVAL,
|
||||
t_ctrl->thrval);
|
||||
|
||||
adc_context_release(&data->ctx, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_npcx_threshold_enable_irq(const struct device *dev,
|
||||
const uint8_t th_sel)
|
||||
{
|
||||
struct adc_reg *const inst = HAL_INSTANCE(dev);
|
||||
struct adc_npcx_data *const data = dev->data;
|
||||
const struct adc_npcx_config *config = dev->config;
|
||||
struct adc_npcx_threshold_data *const t_data = data->threshold_data;
|
||||
struct adc_npcx_threshold_control *const t_ctrl =
|
||||
&t_data->control[th_sel];
|
||||
uint16_t thrcts;
|
||||
|
||||
if (th_sel >= NPCX_ADC_THRESHOLD_COUNT) {
|
||||
LOG_ERR("Invalid ADC threshold selection! (%d)", th_sel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
adc_context_lock(&data->ctx, false, NULL);
|
||||
if (t_ctrl->chnsel >= NPCX_ADC_CH_COUNT ||
|
||||
t_ctrl->thrval >= NPCX_ADC_VREF_VOL ||
|
||||
t_ctrl->thrval == 0 || t_ctrl->work == 0) {
|
||||
adc_context_release(&data->ctx, 0);
|
||||
LOG_ERR("Threshold selected (%d) is not configured!", th_sel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Record new active threshold */
|
||||
t_data->active_thresholds |= BIT(th_sel);
|
||||
|
||||
/* avoid clearing other threshold status */
|
||||
thrcts = inst->THRCTS & ~GENMASK(NPCX_ADC_THRESHOLD_COUNT - 1, 0);
|
||||
|
||||
/* Enable threshold detection */
|
||||
THRCTL(config->base, (th_sel + 1)) |= BIT(NPCX_THRCTL_THEN);
|
||||
|
||||
/* clear threshold status */
|
||||
thrcts |= BIT(th_sel);
|
||||
|
||||
/* set enable threshold status */
|
||||
thrcts |= BIT(NPCX_THRCTS_THR1_IEN + th_sel);
|
||||
|
||||
inst->THRCTS = thrcts;
|
||||
|
||||
adc_npcx_set_repetitive(dev, t_data->control[th_sel].chnsel, true);
|
||||
|
||||
adc_context_release(&data->ctx, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_npcx_threshold_disable_irq(const struct device *dev,
|
||||
const uint8_t th_sel)
|
||||
{
|
||||
struct adc_reg *const inst = HAL_INSTANCE(dev);
|
||||
const struct adc_npcx_config *config = dev->config;
|
||||
struct adc_npcx_data *const data = dev->data;
|
||||
struct adc_npcx_threshold_data *const t_data = data->threshold_data;
|
||||
uint16_t thrcts;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_ADC_CMP_NPCX)) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (th_sel >= NPCX_ADC_THRESHOLD_COUNT) {
|
||||
LOG_ERR("Invalid ADC threshold selection! (%d)", th_sel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
adc_context_lock(&data->ctx, false, NULL);
|
||||
if (!(t_data->active_thresholds & BIT(th_sel))) {
|
||||
adc_context_release(&data->ctx, 0);
|
||||
LOG_ERR("Threshold selection (%d) is not enabled", th_sel);
|
||||
return -ENODEV;
|
||||
}
|
||||
/* avoid clearing other threshold status */
|
||||
thrcts = inst->THRCTS & ~GENMASK(NPCX_ADC_THRESHOLD_COUNT - 1, 0);
|
||||
|
||||
/* set enable threshold status */
|
||||
thrcts &= ~BIT(NPCX_THRCTS_THR1_IEN + th_sel);
|
||||
inst->THRCTS = thrcts;
|
||||
|
||||
/* Disable threshold detection */
|
||||
THRCTL(config->base, (th_sel + 1)) &= ~BIT(NPCX_THRCTL_THEN);
|
||||
|
||||
/* Update active threshold */
|
||||
t_data->active_thresholds &= ~BIT(th_sel);
|
||||
|
||||
adc_npcx_set_repetitive(dev, t_data->control[th_sel].chnsel, false);
|
||||
|
||||
adc_context_release(&data->ctx, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_npcx_threshold_ctrl_enable(const struct device *dev, uint8_t th_sel,
|
||||
const bool enable)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_ADC_CMP_NPCX)) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/* Enable/Disable threshold IRQ */
|
||||
if (enable) {
|
||||
/* Set control threshold registers */
|
||||
ret = adc_npcx_threshold_ctrl_setup(dev, th_sel);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
ret = adc_npcx_threshold_enable_irq(dev, th_sel);
|
||||
} else {
|
||||
ret = adc_npcx_threshold_disable_irq(dev, th_sel);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adc_npcx_threshold_mv_to_thrval(uint32_t val_mv, uint32_t *thrval)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_ADC_CMP_NPCX)) {
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (val_mv >= NPCX_ADC_VREF_VOL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*thrval = (val_mv << ADC_NPCX_THRVAL_RESOLUTION) /
|
||||
NPCX_ADC_VREF_VOL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ADC driver registration */
|
||||
static const struct adc_driver_api adc_npcx_driver_api = {
|
||||
.channel_setup = adc_npcx_channel_setup,
|
||||
|
@ -311,6 +674,8 @@ static const struct adc_npcx_config adc_npcx_cfg_0 = {
|
|||
.alts_list = adc_alts,
|
||||
};
|
||||
|
||||
static struct adc_npcx_threshold_data threshold_data_0;
|
||||
|
||||
static struct adc_npcx_data adc_npcx_data_0 = {
|
||||
ADC_CONTEXT_INIT_TIMER(adc_npcx_data_0, ctx),
|
||||
ADC_CONTEXT_INIT_LOCK(adc_npcx_data_0, ctx),
|
||||
|
@ -366,6 +731,10 @@ static int adc_npcx_init(const struct device *dev)
|
|||
inst->GENDLY = ADC_REGULAR_GENDLY_VAL;
|
||||
inst->MEAST = ADC_REGULAR_MEAST_VAL;
|
||||
|
||||
if (IS_ENABLED(CONFIG_ADC_CMP_NPCX)) {
|
||||
data->threshold_data = &threshold_data_0;
|
||||
}
|
||||
|
||||
/* Configure ADC interrupt and enable it */
|
||||
IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), adc_npcx_isr,
|
||||
DEVICE_DT_INST_GET(0), 0);
|
||||
|
|
|
@ -103,6 +103,7 @@ add_subdirectory_ifdef(CONFIG_TACH_XEC mchp_tach_xec)
|
|||
add_subdirectory_ifdef(CONFIG_ITDS wsen_itds)
|
||||
add_subdirectory_ifdef(CONFIG_MCUX_ACMP mcux_acmp)
|
||||
add_subdirectory_ifdef(CONFIG_TACH_NPCX nuvoton_tach_npcx)
|
||||
add_subdirectory_ifdef(CONFIG_ADC_CMP_NPCX nuvoton_adc_cmp_npcx)
|
||||
add_subdirectory_ifdef(CONFIG_TACH_IT8XXX2 ite_tach_it8xxx2)
|
||||
add_subdirectory_ifdef(CONFIG_VCMP_IT8XXX2 ite_vcmp_it8xxx2)
|
||||
|
||||
|
|
|
@ -242,6 +242,8 @@ source "drivers/sensor/mcux_acmp/Kconfig"
|
|||
|
||||
source "drivers/sensor/nuvoton_tach_npcx/Kconfig"
|
||||
|
||||
source "drivers/sensor/nuvoton_adc_cmp_npcx/Kconfig"
|
||||
|
||||
source "drivers/sensor/ite_tach_it8xxx2/Kconfig"
|
||||
|
||||
source "drivers/sensor/ite_vcmp_it8xxx2/Kconfig"
|
||||
|
|
5
drivers/sensor/nuvoton_adc_cmp_npcx/CMakeLists.txt
Normal file
5
drivers/sensor/nuvoton_adc_cmp_npcx/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources(adc_cmp_npcx.c)
|
14
drivers/sensor/nuvoton_adc_cmp_npcx/Kconfig
Normal file
14
drivers/sensor/nuvoton_adc_cmp_npcx/Kconfig
Normal file
|
@ -0,0 +1,14 @@
|
|||
# ADC CMP NPCX driver configuration options
|
||||
|
||||
# Copyright (c) 2022 Intel Corporation.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
if ADC_NPCX
|
||||
|
||||
config ADC_CMP_NPCX
|
||||
bool "Nuvoton NPCX ADC threshold detection interruption"
|
||||
help
|
||||
This option enables threshold interruption using sensor
|
||||
trigger API.
|
||||
|
||||
endif # ADC_NPCX
|
271
drivers/sensor/nuvoton_adc_cmp_npcx/adc_cmp_npcx.c
Normal file
271
drivers/sensor/nuvoton_adc_cmp_npcx/adc_cmp_npcx.c
Normal file
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <drivers/sensor.h>
|
||||
#include <drivers/adc/adc_npcx_threshold.h>
|
||||
#include <drivers/sensor/adc_cmp_npcx.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(adc_cmp_npcx, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
struct adc_cmp_npcx_data {
|
||||
/* Work queue to be notified when threshold assertion happens */
|
||||
struct k_work work;
|
||||
/* Sensor trigger hanlder to notify user of assetion */
|
||||
sensor_trigger_handler_t handler;
|
||||
/* ADC NPCX driver reference */
|
||||
const struct device *dev;
|
||||
};
|
||||
|
||||
struct adc_cmp_npcx_config {
|
||||
/*
|
||||
* Pointer of ADC device that will be performing measurement, this
|
||||
* must be provied by device tree.
|
||||
*/
|
||||
const struct device *adc;
|
||||
/*
|
||||
* ADC channel that will be used to measure signal, this must be
|
||||
* provided by device tree.
|
||||
*/
|
||||
uint8_t chnsel;
|
||||
/* Threshold selection number assigned during initialization */
|
||||
uint8_t th_sel;
|
||||
/* Threshold assert value in millivolts */
|
||||
uint32_t thr_mv;
|
||||
/*
|
||||
* Condition to be met between measured signal and threshold assert
|
||||
* value that will trigger event
|
||||
*/
|
||||
enum adc_cmp_npcx_comparison comparison;
|
||||
};
|
||||
|
||||
#define DT_DRV_COMPAT nuvoton_adc_cmp
|
||||
|
||||
#define ADC_CMP_NPCX_UNDEFINED (-1)
|
||||
static void adc_cmp_npcx_trigger_work_handler(struct k_work *item)
|
||||
{
|
||||
struct adc_cmp_npcx_data *data =
|
||||
CONTAINER_OF(item, struct adc_cmp_npcx_data, work);
|
||||
struct sensor_trigger trigger = {
|
||||
.type = SENSOR_TRIG_THRESHOLD,
|
||||
.chan = SENSOR_CHAN_VOLTAGE
|
||||
};
|
||||
|
||||
if (data->handler) {
|
||||
data->handler(data->dev, &trigger);
|
||||
}
|
||||
}
|
||||
|
||||
static int adc_cmp_npcx_init(const struct device *dev)
|
||||
{
|
||||
const struct adc_cmp_npcx_config *const config = dev->config;
|
||||
struct adc_cmp_npcx_data *data = dev->data;
|
||||
struct adc_npcx_threshold_param param;
|
||||
int ret;
|
||||
|
||||
LOG_DBG("Initialize ADC CMP threshold selection (%d)", config->th_sel);
|
||||
/* Data must keep device reference for worker handler*/
|
||||
data->dev = dev;
|
||||
|
||||
/* Set ADC channel selection */
|
||||
param.type = ADC_NPCX_THRESHOLD_PARAM_CHNSEL;
|
||||
param.val = (uint32_t)config->chnsel;
|
||||
ret = adc_npcx_threshold_ctrl_set_param(config->adc, config->th_sel,
|
||||
¶m);
|
||||
if (ret) {
|
||||
goto init_error;
|
||||
}
|
||||
|
||||
/* Init and set Worker queue to enable notifications */
|
||||
k_work_init(&data->work, adc_cmp_npcx_trigger_work_handler);
|
||||
param.type = ADC_NPCX_THRESHOLD_PARAM_WORK;
|
||||
param.val = (uint32_t)&data->work;
|
||||
ret = adc_npcx_threshold_ctrl_set_param(config->adc, config->th_sel,
|
||||
¶m);
|
||||
if (ret) {
|
||||
goto init_error;
|
||||
}
|
||||
|
||||
/* Set threshold value if set on device tree */
|
||||
if (config->thr_mv != ADC_CMP_NPCX_UNDEFINED) {
|
||||
param.type = ADC_NPCX_THRESHOLD_PARAM_THVAL;
|
||||
/* Convert from millivolts to ADC raw register value */
|
||||
ret = adc_npcx_threshold_mv_to_thrval(config->thr_mv,
|
||||
¶m.val);
|
||||
if (ret) {
|
||||
goto init_error;
|
||||
}
|
||||
|
||||
ret = adc_npcx_threshold_ctrl_set_param(config->adc,
|
||||
config->th_sel, ¶m);
|
||||
if (ret) {
|
||||
goto init_error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set threshold comparison if set on device tree */
|
||||
if (config->comparison == ADC_CMP_NPCX_GREATER ||
|
||||
config->comparison == ADC_CMP_NPCX_LESS_OR_EQUAL) {
|
||||
param.type = ADC_NPCX_THRESHOLD_PARAM_L_H;
|
||||
param.val =
|
||||
config->comparison == ADC_CMP_NPCX_GREATER ?
|
||||
ADC_NPCX_THRESHOLD_PARAM_L_H_HIGHER :
|
||||
ADC_NPCX_THRESHOLD_PARAM_L_H_LOWER;
|
||||
ret = adc_npcx_threshold_ctrl_set_param(config->adc,
|
||||
config->th_sel, ¶m);
|
||||
}
|
||||
|
||||
init_error:
|
||||
if (ret) {
|
||||
LOG_ERR("Error setting parameter %d - value %d",
|
||||
(uint32_t)param.type, param.val);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc_cmp_npcx_set_threshold(const struct device *dev, bool is_upper,
|
||||
bool is_mv, uint32_t value)
|
||||
{
|
||||
const struct adc_cmp_npcx_config *const config = dev->config;
|
||||
struct adc_npcx_threshold_param param;
|
||||
int ret;
|
||||
|
||||
param.type = ADC_NPCX_THRESHOLD_PARAM_THVAL;
|
||||
if (is_mv) {
|
||||
ret = adc_npcx_threshold_mv_to_thrval(value, ¶m.val);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = adc_npcx_threshold_ctrl_set_param(config->adc,
|
||||
config->th_sel, ¶m);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
param.type = ADC_NPCX_THRESHOLD_PARAM_L_H;
|
||||
param.val = is_upper ? ADC_NPCX_THRESHOLD_PARAM_L_H_HIGHER :
|
||||
ADC_NPCX_THRESHOLD_PARAM_L_H_LOWER;
|
||||
|
||||
ret = adc_npcx_threshold_ctrl_set_param(config->adc,
|
||||
config->th_sel, ¶m);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc_cmp_npcx_attr_set(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
enum sensor_attribute attr,
|
||||
const struct sensor_value *val)
|
||||
{
|
||||
const struct adc_cmp_npcx_config *const config = dev->config;
|
||||
int ret;
|
||||
|
||||
if (chan != SENSOR_CHAN_VOLTAGE) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
switch ((uint16_t)attr) {
|
||||
case SENSOR_ATTR_LOWER_THRESH:
|
||||
__fallthrough;
|
||||
case SENSOR_ATTR_UPPER_THRESH:
|
||||
__fallthrough;
|
||||
case SENSOR_ATTR_LOWER_VOLTAGE_THRESH:
|
||||
__fallthrough;
|
||||
case SENSOR_ATTR_UPPER_VOLTAGE_THRESH:
|
||||
ret = adc_cmp_npcx_set_threshold(dev,
|
||||
/* Is upper? */
|
||||
attr == SENSOR_ATTR_UPPER_THRESH ||
|
||||
(uint16_t)attr == SENSOR_ATTR_UPPER_VOLTAGE_THRESH,
|
||||
/* Is mV? */
|
||||
(uint16_t)attr == SENSOR_ATTR_LOWER_VOLTAGE_THRESH ||
|
||||
(uint16_t)attr == SENSOR_ATTR_UPPER_VOLTAGE_THRESH,
|
||||
val->val1);
|
||||
break;
|
||||
case SENSOR_ATTR_ALERT:
|
||||
ret = adc_npcx_threshold_ctrl_enable(config->adc,
|
||||
config->th_sel, !!val->val1);
|
||||
break;
|
||||
default:
|
||||
ret = -ENOTSUP;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc_cmp_npcx_attr_get(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
enum sensor_attribute attr,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int adc_cmp_npcx_trigger_set(const struct device *dev,
|
||||
const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler)
|
||||
{
|
||||
const struct adc_cmp_npcx_config *const config = dev->config;
|
||||
struct adc_cmp_npcx_data *data = dev->data;
|
||||
struct adc_npcx_threshold_param param;
|
||||
|
||||
if (trig == NULL || handler == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (trig->type != SENSOR_TRIG_THRESHOLD ||
|
||||
trig->chan != SENSOR_CHAN_VOLTAGE) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
data->handler = handler;
|
||||
|
||||
param.type = ADC_NPCX_THRESHOLD_PARAM_WORK;
|
||||
param.val = (uint32_t)&data->work;
|
||||
return adc_npcx_threshold_ctrl_set_param(config->adc, config->th_sel,
|
||||
¶m);
|
||||
}
|
||||
|
||||
static int adc_cmp_npcx_sample_fetch(const struct device *dev,
|
||||
enum sensor_channel chan)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static int adc_cmp_npcx_channel_get(const struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
static const struct sensor_driver_api adc_cmp_npcx_api = {
|
||||
.attr_set = adc_cmp_npcx_attr_set,
|
||||
.attr_get = adc_cmp_npcx_attr_get,
|
||||
.trigger_set = adc_cmp_npcx_trigger_set,
|
||||
.sample_fetch = adc_cmp_npcx_sample_fetch,
|
||||
.channel_get = adc_cmp_npcx_channel_get,
|
||||
};
|
||||
|
||||
#define NPCX_ADC_CMP_INIT(inst) \
|
||||
static struct adc_cmp_npcx_data adc_cmp_npcx_data_##inst; \
|
||||
static const struct adc_cmp_npcx_config adc_cmp_npcx_config_##inst = {\
|
||||
.adc = DEVICE_DT_GET(DT_INST_IO_CHANNELS_CTLR(inst)), \
|
||||
.chnsel = DT_INST_IO_CHANNELS_INPUT(inst), \
|
||||
.th_sel = inst, \
|
||||
.thr_mv = DT_INST_PROP_OR(inst, threshold_mv, \
|
||||
ADC_CMP_NPCX_UNDEFINED), \
|
||||
.comparison = DT_INST_STRING_TOKEN_OR(inst, \
|
||||
comparison, ADC_CMP_NPCX_UNDEFINED) \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(inst, adc_cmp_npcx_init, NULL, \
|
||||
&adc_cmp_npcx_data_##inst, \
|
||||
&adc_cmp_npcx_config_##inst, PRE_KERNEL_2, \
|
||||
CONFIG_SENSOR_INIT_PRIORITY, \
|
||||
&adc_cmp_npcx_api);
|
||||
DT_INST_FOREACH_STATUS_OKAY(NPCX_ADC_CMP_INIT)
|
Loading…
Add table
Add a link
Reference in a new issue