1031 lines
26 KiB
C
1031 lines
26 KiB
C
/* fdc2x1x.c - Driver for the Texas Instruments FDC2X1X */
|
|
|
|
/*
|
|
* Copyright (c) 2020 arithmetics.io
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT ti_fdc2x1x
|
|
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/pm/device.h>
|
|
#include <zephyr/sys/util.h>
|
|
#include <zephyr/logging/log.h>
|
|
#include <math.h>
|
|
|
|
#include "fdc2x1x.h"
|
|
#include <zephyr/drivers/sensor/fdc2x1x.h>
|
|
|
|
LOG_MODULE_REGISTER(FDC2X1X, CONFIG_SENSOR_LOG_LEVEL);
|
|
|
|
static int fdc2x1x_init_config(const struct device *dev);
|
|
|
|
/**
|
|
* Convert raw data to frequency (MHz).
|
|
* @param dev - The device structure.
|
|
* @param ch - Channel to convert the data from.
|
|
* @param freq - Calculated frequency value .
|
|
*/
|
|
static void fdc2x1x_raw_to_freq(const struct device *dev,
|
|
uint8_t ch, double *freq)
|
|
{
|
|
struct fdc2x1x_data *data = dev->data;
|
|
const struct fdc2x1x_config *cfg = dev->config;
|
|
|
|
if (data->fdc221x) {
|
|
*freq = (cfg->ch_cfg->fin_sel * (cfg->fref / 1000.0) *
|
|
data->channel_buf[ch]) / pow(2, 28);
|
|
} else {
|
|
*freq = cfg->ch_cfg->fin_sel * (cfg->fref / 1000.0) *
|
|
((data->channel_buf[ch] / pow(2, 12 + cfg->output_gain)) +
|
|
(cfg->ch_cfg[ch].offset / pow(2, 16)));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert raw data to capacitance in picofarad (pF).
|
|
* Requires the previous conversion from raw to frequency.
|
|
* @param dev - The device structure.
|
|
* @param ch - Channel to convert the data from .
|
|
* @param freq - Frequency value
|
|
* @param capacitance - Calculated capacitance value
|
|
*/
|
|
static void fdc2x1x_raw_to_capacitance(const struct device *dev,
|
|
uint8_t ch, double freq, double *capacitance)
|
|
{
|
|
const struct fdc2x1x_config *cfg = dev->config;
|
|
|
|
*capacitance = 1 / ((cfg->ch_cfg->inductance / 1000000.0) *
|
|
pow((2 * PI * freq), 2));
|
|
}
|
|
|
|
/**
|
|
* Read/Write from device.
|
|
* @param dev - The device structure.
|
|
* @param reg - The register address. Use FDC2X1X_REG_READ(x) or
|
|
* FDC2X1X_REG_WRITE(x).
|
|
* @param data - The register data.
|
|
* @param length - Number of bytes being read
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_bus_access(const struct device *dev, uint8_t reg,
|
|
uint8_t *data, size_t length)
|
|
{
|
|
const struct fdc2x1x_config *cfg = dev->config;
|
|
|
|
if (reg & FDC2X1X_READ) {
|
|
return i2c_burst_read_dt(&cfg->i2c, FDC2X1X_TO_I2C_REG(reg), data, length);
|
|
} else {
|
|
if (length != 2) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
uint8_t buf[3];
|
|
|
|
buf[0] = FDC2X1X_TO_I2C_REG(reg);
|
|
memcpy(buf + 1, data, sizeof(uint16_t));
|
|
|
|
return i2c_write_dt(&cfg->i2c, buf, sizeof(buf));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read (16 Bit) from device.
|
|
* @param dev - The device structure.
|
|
* @param reg_addr - The register address.
|
|
* @param reg_data - The register data.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_reg_read(const struct device *dev,
|
|
uint8_t reg_addr,
|
|
uint16_t *reg_data)
|
|
{
|
|
uint8_t buf[2];
|
|
int ret;
|
|
|
|
ret = fdc2x1x_bus_access(dev, FDC2X1X_REG_READ(reg_addr), buf, 2);
|
|
*reg_data = ((uint16_t)buf[0] << 8) | buf[1];
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Write (16 Bit) to device.
|
|
* @param dev - The device structure.
|
|
* @param reg_addr - The register address.
|
|
* @param reg_data - The register data.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_reg_write(const struct device *dev,
|
|
uint8_t reg_addr,
|
|
uint16_t reg_data)
|
|
{
|
|
LOG_DBG("[0x%x] = 0x%x", reg_addr, reg_data);
|
|
|
|
uint8_t buf[2];
|
|
|
|
buf[0] = (uint8_t)(reg_data >> 8);
|
|
buf[1] = (uint8_t)reg_data;
|
|
|
|
return fdc2x1x_bus_access(dev, FDC2X1X_REG_WRITE(reg_addr), buf, 2);
|
|
}
|
|
|
|
/**
|
|
* I2C write (16 Bit) to device using a mask.
|
|
* @param dev - The device structure.
|
|
* @param reg_addr - The register address.
|
|
* @param mask - The mask.
|
|
* @param data - The register data.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
int fdc2x1x_reg_write_mask(const struct device *dev,
|
|
uint8_t reg_addr,
|
|
uint16_t mask,
|
|
uint16_t data)
|
|
{
|
|
int ret;
|
|
uint16_t tmp;
|
|
|
|
ret = fdc2x1x_reg_read(dev, reg_addr, &tmp);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
LOG_DBG("read [0x%x] = 0x%x", reg_addr, tmp);
|
|
LOG_DBG("mask: 0x%x", mask);
|
|
|
|
tmp &= ~mask;
|
|
tmp |= data;
|
|
|
|
return fdc2x1x_reg_write(dev, reg_addr, tmp);
|
|
}
|
|
|
|
/**
|
|
* Set the Frequency Selection value of a specific channel.
|
|
* @param dev - The device structure.
|
|
* @param chx - Channel number.
|
|
* @param fin_sel - Frequency selection value.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_set_fin_sel(const struct device *dev, uint8_t chx,
|
|
uint8_t fin_sel)
|
|
{
|
|
return fdc2x1x_reg_write_mask(dev,
|
|
FDC2X1X_CLOCK_DIVIDERS_CH0 + chx,
|
|
FDC2X1X_CLK_DIV_CHX_FIN_SEL_MSK,
|
|
FDC2X1X_CLK_DIV_CHX_FIN_SEL_SET(fin_sel));
|
|
}
|
|
|
|
/**
|
|
* Set the Reference Divider value of a specific channel.
|
|
* @param dev - The device structure.
|
|
* @param chx - Channel number.
|
|
* @param fref_div - Reference divider value.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_set_fref_divider(const struct device *dev, uint8_t chx,
|
|
uint16_t fref_div)
|
|
{
|
|
return fdc2x1x_reg_write_mask(dev,
|
|
FDC2X1X_CLOCK_DIVIDERS_CH0 + chx,
|
|
FDC2X1X_CLK_DIV_CHX_FREF_DIV_MSK,
|
|
FDC2X1X_CLK_DIV_CHX_FREF_DIV_SET(fref_div));
|
|
}
|
|
|
|
/**
|
|
* Set the Drive Current value of a specific channel.
|
|
* @param dev - The device structure.
|
|
* @param chx - Channel number.
|
|
* @param idrv - Sensor driver current.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_set_idrive(const struct device *dev, uint8_t chx,
|
|
uint8_t idrv)
|
|
{
|
|
return fdc2x1x_reg_write_mask(dev,
|
|
FDC2X1X_DRIVE_CURRENT_CH0 + chx,
|
|
FDC2X1X_DRV_CURRENT_CHX_IDRIVE_MSK,
|
|
FDC2X1X_DRV_CURRENT_CHX_IDRIVE_SET(idrv));
|
|
}
|
|
|
|
/**
|
|
* Set the Conversion Settling value of a specific channel.
|
|
* @param dev - The device structure.
|
|
* @param chx - Channel number.
|
|
* @param settle_count - Settling time value.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_set_settle_count(const struct device *dev, uint8_t chx,
|
|
uint16_t settle_count)
|
|
{
|
|
return fdc2x1x_reg_write(dev,
|
|
FDC2X1X_SETTLECOUNT_CH0 + chx, settle_count);
|
|
}
|
|
|
|
/**
|
|
* Set the Reference Count value of a specific channel.
|
|
* @param dev - The device structure.
|
|
* @param chx - Channel number.
|
|
* @param rcount - Reference count value.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_set_rcount(const struct device *dev,
|
|
uint8_t chx, uint16_t rcount)
|
|
{
|
|
return fdc2x1x_reg_write(dev, FDC2X1X_RCOUNT_CH0 + chx, rcount);
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the Offset value of a specific channel.
|
|
* @param dev - The device structure.
|
|
* @param chx - Channel number.
|
|
* @param offset - Offset value.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_set_offset(const struct device *dev,
|
|
uint8_t chx, uint16_t offset)
|
|
{
|
|
return fdc2x1x_reg_write(dev, FDC2X1X_OFFSET_CH0 + chx, offset);
|
|
}
|
|
|
|
/**
|
|
* Set the Auto-Scan Mode.
|
|
* @param dev - The device structure.
|
|
* @param en - Enable/disable auto-scan mode.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_set_autoscan_mode(const struct device *dev, bool en)
|
|
{
|
|
return fdc2x1x_reg_write_mask(dev,
|
|
FDC2X1X_MUX_CONFIG,
|
|
FDC2X1X_MUX_CFG_AUTOSCAN_EN_MSK,
|
|
FDC2X1X_MUX_CFG_AUTOSCAN_EN_SET(en));
|
|
}
|
|
|
|
/**
|
|
* Set the Auto-Scan Sequence Configuration.
|
|
* @param dev - The device structure.
|
|
* @param rr_seq - Auto-Scan sequence value.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_set_rr_sequence(const struct device *dev, uint8_t rr_seq)
|
|
{
|
|
return fdc2x1x_reg_write_mask(dev,
|
|
FDC2X1X_MUX_CONFIG,
|
|
FDC2X1X_MUX_CFG_RR_SEQUENCE_MSK,
|
|
FDC2X1X_MUX_CFG_RR_SEQUENCE_SET(rr_seq));
|
|
}
|
|
|
|
/**
|
|
* Set the Input deglitch filter bandwidth.
|
|
* @param dev - The device structure.
|
|
* @param deglitch - Deglitch selection.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_set_deglitch(const struct device *dev, uint8_t deglitch)
|
|
{
|
|
return fdc2x1x_reg_write_mask(dev,
|
|
FDC2X1X_MUX_CONFIG,
|
|
FDC2X1X_MUX_CFG_DEGLITCH_MSK,
|
|
FDC2X1X_MUX_CFG_DEGLITCH_SET(deglitch));
|
|
}
|
|
|
|
/**
|
|
* Set the Output gain control.
|
|
* @param dev - The device structure.
|
|
* @param gain - Output gain.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_set_output_gain(const struct device *dev, uint8_t gain)
|
|
{
|
|
return fdc2x1x_reg_write_mask(dev,
|
|
FDC2X1X_RESET_DEV,
|
|
FDC2X1X_RESET_DEV_OUTPUT_GAIN_MSK,
|
|
FDC2X1X_RESET_DEV_OUTPUT_GAIN_SET(gain));
|
|
}
|
|
|
|
/**
|
|
* Set the Active Channel for single channel
|
|
* conversion if Auto-Scan Mode is disabled.
|
|
* @param dev - The device structure.
|
|
* @param ch - Active channel.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_set_active_channel(const struct device *dev, uint8_t ch)
|
|
{
|
|
return fdc2x1x_reg_write_mask(dev,
|
|
FDC2X1X_CONFIG,
|
|
FDC2X1X_CFG_ACTIVE_CHAN_MSK,
|
|
FDC2X1X_CFG_ACTIVE_CHAN_SET(ch));
|
|
}
|
|
|
|
/**
|
|
* Set the Sensor Activation Mode Selection.
|
|
* @param dev - The device structure.
|
|
* @param act_sel - Sensor Activation Mode Selection.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_set_sensor_activate_sel(const struct device *dev,
|
|
uint8_t act_sel)
|
|
{
|
|
return fdc2x1x_reg_write_mask(dev,
|
|
FDC2X1X_CONFIG,
|
|
FDC2X1X_CFG_SENSOR_ACTIVATE_SEL_MSK,
|
|
FDC2X1X_CFG_SENSOR_ACTIVATE_SEL_SET(act_sel));
|
|
}
|
|
|
|
/**
|
|
* Set the Reference Frequency Source.
|
|
* @param dev - The device structure.
|
|
* @param clk_src - Clock source.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_set_ref_clk_src(const struct device *dev, uint8_t clk_src)
|
|
{
|
|
return fdc2x1x_reg_write_mask(dev,
|
|
FDC2X1X_CONFIG,
|
|
FDC2X1X_CFG_REF_CLK_SRC_MSK,
|
|
FDC2X1X_CFG_REF_CLK_SRC_SET(clk_src));
|
|
}
|
|
|
|
/**
|
|
* Set the Current Sensor Drive.
|
|
* @param dev - The device structure.
|
|
* @param cur_drv - Current Sensor Drive.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_set_current_drv(const struct device *dev, uint8_t cur_drv)
|
|
{
|
|
return fdc2x1x_reg_write_mask(dev,
|
|
FDC2X1X_CONFIG,
|
|
FDC2X1X_CFG_HIGH_CURRENT_DRV_MSK,
|
|
FDC2X1X_CFG_HIGH_CURRENT_DRV_SET(cur_drv));
|
|
}
|
|
|
|
/**
|
|
* Enable/disable the INTB-Pin interrupt assertion.
|
|
* @param dev - The device structure.
|
|
* @param enable - True = enable int assertion, false = disable int assertion.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
int fdc2x1x_set_interrupt_pin(const struct device *dev, bool enable)
|
|
{
|
|
return fdc2x1x_reg_write_mask(dev,
|
|
FDC2X1X_CONFIG,
|
|
FDC2X1X_CFG_INTB_DIS_MSK,
|
|
FDC2X1X_CFG_INTB_DIS_SET(!enable));
|
|
}
|
|
|
|
/**
|
|
* Set the Operation Mode
|
|
* @param dev - The device structure.
|
|
* @param op_mode - Operation mode
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
int fdc2x1x_set_op_mode(const struct device *dev,
|
|
enum fdc2x1x_op_mode op_mode)
|
|
{
|
|
return fdc2x1x_reg_write_mask(dev,
|
|
FDC2X1X_CONFIG,
|
|
FDC2X1X_CFG_SLEEP_SET_EN_MSK,
|
|
FDC2X1X_CFG_SLEEP_SET_EN_SET(op_mode));
|
|
}
|
|
|
|
/**
|
|
* Get the STATUS register data
|
|
* @param dev - The device structure.
|
|
* @param status - Data stored in the STATUS register
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
int fdc2x1x_get_status(const struct device *dev, uint16_t *status)
|
|
{
|
|
return fdc2x1x_reg_read(dev, FDC2X1X_STATUS, status);
|
|
}
|
|
|
|
/**
|
|
* Reset the device.
|
|
* @param dev - The device structure.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_reset(const struct device *dev)
|
|
{
|
|
int ret;
|
|
|
|
ret = fdc2x1x_reg_write_mask(dev,
|
|
FDC2X1X_RESET_DEV,
|
|
FDC2X1X_RESET_DEV_MSK,
|
|
FDC2X1X_RESET_DEV_SET(1));
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_PM_DEVICE
|
|
/**
|
|
* Reinitialize device after exiting shutdown mode
|
|
* @param dev - The device structure.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_restart(const struct device *dev)
|
|
{
|
|
int ret;
|
|
|
|
k_sleep(K_MSEC(100));
|
|
|
|
ret = fdc2x1x_init_config(dev);
|
|
if (ret) {
|
|
LOG_ERR("Reinitializing failed");
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_FDC2X1X_TRIGGER
|
|
struct fdc2x1x_data *data = dev->data;
|
|
|
|
ret = fdc2x1x_reg_write_mask(dev, FDC2X1X_ERROR_CONFIG,
|
|
data->int_config, data->int_config);
|
|
if (ret) {
|
|
LOG_ERR("Reinitializing trigger failed");
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Enable/disable Shutdown Mode
|
|
* @param dev - The device structure.
|
|
* @param enable - True = enable shutdown, false = disable shutdown
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_set_shutdown(const struct device *dev, bool enable)
|
|
{
|
|
const struct fdc2x1x_config *cfg = dev->config;
|
|
int ret = 0;
|
|
|
|
gpio_pin_set_dt(&cfg->sd_gpio, enable);
|
|
|
|
if (!enable) {
|
|
ret = fdc2x1x_restart(dev);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Set the Device Power Management State.
|
|
* @param dev - The device structure.
|
|
* @param pm_state - power management state
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_device_pm_action(const struct device *dev,
|
|
enum pm_device_action action)
|
|
{
|
|
int ret;
|
|
const struct fdc2x1x_config *cfg = dev->config;
|
|
enum pm_device_state curr_state;
|
|
|
|
(void)pm_device_state_get(dev, &curr_state);
|
|
|
|
switch (action) {
|
|
case PM_DEVICE_ACTION_RESUME:
|
|
if (curr_state == PM_DEVICE_STATE_OFF) {
|
|
ret = fdc2x1x_set_shutdown(dev, false);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = fdc2x1x_set_op_mode(dev, FDC2X1X_ACTIVE_MODE);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
break;
|
|
case PM_DEVICE_ACTION_SUSPEND:
|
|
if (curr_state == PM_DEVICE_STATE_OFF) {
|
|
ret = fdc2x1x_set_shutdown(dev, false);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
ret = fdc2x1x_set_op_mode(dev, FDC2X1X_SLEEP_MODE);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
break;
|
|
case PM_DEVICE_ACTION_TURN_OFF:
|
|
if (cfg->sd_gpio->port.name) {
|
|
ret = fdc2x1x_set_shutdown(dev, true);
|
|
} else {
|
|
LOG_ERR("SD pin not defined");
|
|
ret = -ENOTSUP;
|
|
}
|
|
break;
|
|
default:
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Set attributes for the device.
|
|
* @param dev - The device structure.
|
|
* @param chan - The sensor channel type.
|
|
* @param attr - The sensor attribute.
|
|
* @param value - The sensor attribute value.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_attr_set(const struct device *dev,
|
|
enum sensor_channel chan,
|
|
enum sensor_attribute attr,
|
|
const struct sensor_value *val)
|
|
{
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/**
|
|
* Read sensor data from the device.
|
|
* @param dev - The device structure.
|
|
* @param cap_data - The sensor value data.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_get_cap_data(const struct device *dev)
|
|
{
|
|
uint8_t increment_steps;
|
|
int i;
|
|
|
|
const struct fdc2x1x_config *cfg = dev->config;
|
|
struct fdc2x1x_data *data = dev->data;
|
|
uint8_t reg_addr = FDC2X1X_DATA_CH0;
|
|
uint8_t buf_size = cfg->num_channels;
|
|
|
|
if (data->fdc221x) {
|
|
buf_size *= 2;
|
|
increment_steps = 1;
|
|
} else {
|
|
increment_steps = 2;
|
|
}
|
|
|
|
uint16_t buf[buf_size];
|
|
|
|
#ifdef CONFIG_FDC2X1X_TRIGGER_NONE
|
|
uint16_t status;
|
|
|
|
do {
|
|
fdc2x1x_get_status(dev, &status);
|
|
} while (!(FDC2X1X_STATUS_DRDY(status)));
|
|
#endif
|
|
|
|
for (i = 0; i < buf_size; i++) {
|
|
if (fdc2x1x_reg_read(dev, reg_addr, &buf[i]) < 0) {
|
|
LOG_ERR("Failed to read reg 0x%x", reg_addr);
|
|
return -EIO;
|
|
}
|
|
reg_addr += increment_steps;
|
|
}
|
|
|
|
for (i = 0; i < cfg->num_channels; i++) {
|
|
if (data->fdc221x) {
|
|
data->channel_buf[i] = buf[i * 2] << 16 | buf[i * 2 + 1];
|
|
} else {
|
|
data->channel_buf[i] = buf[i];
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Fetch sensor data from the device.
|
|
* @param dev - The device structure.
|
|
* @param chan - The sensor channel type.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_sample_fetch(const struct device *dev,
|
|
enum sensor_channel chan)
|
|
{
|
|
#ifdef CONFIG_PM_DEVICE
|
|
enum pm_device_state state;
|
|
|
|
(void)pm_device_state_get(dev, &state);
|
|
if (state != PM_DEVICE_STATE_ACTIVE) {
|
|
LOG_ERR("Sample fetch failed, device is not in active mode");
|
|
return -ENXIO;
|
|
}
|
|
#endif
|
|
|
|
return fdc2x1x_get_cap_data(dev);
|
|
}
|
|
|
|
/**
|
|
* Get sensor channel value from the device.
|
|
* @param dev - The device structure.
|
|
* @param chan - The sensor channel type.
|
|
* @param val - The sensor channel value.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_channel_get(const struct device *dev,
|
|
enum sensor_channel chan,
|
|
struct sensor_value *val)
|
|
{
|
|
const struct fdc2x1x_config *cfg = dev->config;
|
|
double ch_data;
|
|
|
|
switch ((int16_t)chan) {
|
|
case SENSOR_CHAN_FDC2X1X_FREQ_CH0:
|
|
fdc2x1x_raw_to_freq(dev, 0, &ch_data);
|
|
val->val1 = (uint32_t)ch_data;
|
|
val->val2 = ((uint32_t)(ch_data * 1000000)) % 1000000;
|
|
break;
|
|
case SENSOR_CHAN_FDC2X1X_FREQ_CH1:
|
|
if (cfg->num_channels >= 2) {
|
|
fdc2x1x_raw_to_freq(dev, 1, &ch_data);
|
|
val->val1 = (uint32_t)ch_data;
|
|
val->val2 = ((uint32_t)(ch_data * 1000000)) % 1000000;
|
|
} else {
|
|
LOG_ERR("CH1 not defined.");
|
|
return -ENOTSUP;
|
|
}
|
|
break;
|
|
case SENSOR_CHAN_FDC2X1X_FREQ_CH2:
|
|
if (cfg->num_channels >= 3) {
|
|
fdc2x1x_raw_to_freq(dev, 2, &ch_data);
|
|
val->val1 = (uint32_t)ch_data;
|
|
val->val2 = ((uint32_t)(ch_data * 1000000)) % 1000000;
|
|
} else {
|
|
LOG_ERR("CH2 not selected or not supported by device.");
|
|
return -ENOTSUP;
|
|
}
|
|
break;
|
|
case SENSOR_CHAN_FDC2X1X_FREQ_CH3:
|
|
if (cfg->num_channels == 4) {
|
|
fdc2x1x_raw_to_freq(dev, 3, &ch_data);
|
|
val->val1 = (uint32_t)ch_data;
|
|
val->val2 = ((uint32_t)(ch_data * 1000000)) % 1000000;
|
|
} else {
|
|
LOG_ERR("CH3 not selected or not supported by device.");
|
|
return -ENOTSUP;
|
|
}
|
|
break;
|
|
case SENSOR_CHAN_FDC2X1X_CAPACITANCE_CH0:
|
|
fdc2x1x_raw_to_freq(dev, 0, &ch_data);
|
|
fdc2x1x_raw_to_capacitance(dev, 0, ch_data, &ch_data);
|
|
val->val1 = (uint32_t)ch_data;
|
|
val->val2 = ((uint32_t)(ch_data * 1000000)) % 1000000;
|
|
break;
|
|
case SENSOR_CHAN_FDC2X1X_CAPACITANCE_CH1:
|
|
if (cfg->num_channels >= 2) {
|
|
fdc2x1x_raw_to_freq(dev, 1, &ch_data);
|
|
fdc2x1x_raw_to_capacitance(dev, 1, ch_data, &ch_data);
|
|
val->val1 = (uint32_t)ch_data;
|
|
val->val2 = ((uint32_t)(ch_data * 1000000)) % 1000000;
|
|
} else {
|
|
LOG_ERR("CH1 not selected or not supported by device.");
|
|
return -ENOTSUP;
|
|
}
|
|
break;
|
|
case SENSOR_CHAN_FDC2X1X_CAPACITANCE_CH2:
|
|
if (cfg->num_channels >= 3) {
|
|
fdc2x1x_raw_to_freq(dev, 2, &ch_data);
|
|
fdc2x1x_raw_to_capacitance(dev, 2, ch_data, &ch_data);
|
|
val->val1 = (uint32_t)ch_data;
|
|
val->val2 = ((uint32_t)(ch_data * 1000000)) % 1000000;
|
|
} else {
|
|
LOG_ERR("CH3 not selected or not supported by device.");
|
|
return -ENOTSUP;
|
|
}
|
|
break;
|
|
case SENSOR_CHAN_FDC2X1X_CAPACITANCE_CH3:
|
|
if (cfg->num_channels >= 4) {
|
|
fdc2x1x_raw_to_freq(dev, 3, &ch_data);
|
|
fdc2x1x_raw_to_capacitance(dev, 3, ch_data, &ch_data);
|
|
val->val1 = (uint32_t)ch_data;
|
|
val->val2 = ((uint32_t)(ch_data * 1000000)) % 1000000;
|
|
} else {
|
|
LOG_ERR("CH3 not selected or not supported by device.");
|
|
return -ENOTSUP;
|
|
}
|
|
break;
|
|
default:
|
|
LOG_ERR("Channel type not supported.");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct sensor_driver_api fdc2x1x_api_funcs = {
|
|
.attr_set = fdc2x1x_attr_set,
|
|
.sample_fetch = fdc2x1x_sample_fetch,
|
|
.channel_get = fdc2x1x_channel_get,
|
|
#ifdef CONFIG_FDC2X1X_TRIGGER
|
|
.trigger_set = fdc2x1x_trigger_set,
|
|
#endif
|
|
};
|
|
|
|
static int fdc2x1x_init_config(const struct device *dev)
|
|
{
|
|
int ret;
|
|
int ch;
|
|
const struct fdc2x1x_config *cfg = dev->config;
|
|
struct fdc2x1x_data *data = dev->data;
|
|
|
|
/* Channel specific settings */
|
|
for (ch = 0; ch < cfg->num_channels; ch++) {
|
|
ret = fdc2x1x_set_fin_sel(dev, ch, cfg->ch_cfg[ch].fin_sel);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = fdc2x1x_set_fref_divider(dev, ch,
|
|
cfg->ch_cfg[ch].fref_divider);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = fdc2x1x_set_idrive(dev, ch, cfg->ch_cfg[ch].idrive);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = fdc2x1x_set_settle_count(dev, ch,
|
|
cfg->ch_cfg[ch].settle_count);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = fdc2x1x_set_rcount(dev, ch, cfg->ch_cfg[ch].rcount);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
if (!data->fdc221x) {
|
|
ret = fdc2x1x_set_offset(dev, ch,
|
|
cfg->ch_cfg[ch].offset);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
ret = fdc2x1x_set_autoscan_mode(dev, cfg->autoscan_en);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = fdc2x1x_set_rr_sequence(dev, cfg->rr_sequence);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = fdc2x1x_set_deglitch(dev, cfg->deglitch);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
if (!data->fdc221x) {
|
|
ret = fdc2x1x_set_output_gain(dev, cfg->output_gain);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = fdc2x1x_set_active_channel(dev, cfg->active_channel);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = fdc2x1x_set_sensor_activate_sel(dev, cfg->sensor_activate_sel);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
ret = fdc2x1x_set_ref_clk_src(dev, cfg->clk_src);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_FDC2X1X_TRIGGER_NONE
|
|
/* Enable Data Ready Flag to poll for new measurement */
|
|
ret = fdc2x1x_reg_write_mask(dev, FDC2X1X_ERROR_CONFIG,
|
|
FDC2X1X_ERROR_CONFIG_DRDY_2INT_MSK,
|
|
FDC2X1X_ERROR_CONFIG_DRDY_2INT_SET(1));
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
/* INTB asserts by default, so disable it here */
|
|
ret = fdc2x1x_set_interrupt_pin(dev, false);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
ret = fdc2x1x_set_current_drv(dev, cfg->current_drv);
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Probe device (Check if it is the correct device).
|
|
* @param dev - The device structure.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_probe(const struct device *dev)
|
|
{
|
|
struct fdc2x1x_data *data = dev->data;
|
|
uint16_t dev_id, man_id;
|
|
|
|
if (fdc2x1x_reg_read(dev, FDC2X1X_DEVICE_ID, &dev_id) < 0) {
|
|
LOG_ERR("Failed to read device id");
|
|
return -EIO;
|
|
}
|
|
|
|
|
|
if (dev_id == FDC2X1X_DEVICE_ID_VAL_28BIT) {
|
|
data->fdc221x = true;
|
|
} else if (dev_id == FDC2X1X_DEVICE_ID_VAL) {
|
|
data->fdc221x = false;
|
|
} else {
|
|
LOG_ERR("Wrong device id");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (data->fdc221x) {
|
|
printk("is 28bit\n");
|
|
} else {
|
|
printk("is 12bit\n");
|
|
}
|
|
|
|
if (fdc2x1x_reg_read(dev, FDC2X1X_MANUFACTURER_ID, &man_id) < 0) {
|
|
LOG_ERR("Failed to read manufacturer id");
|
|
return -EIO;
|
|
}
|
|
|
|
if (man_id != FDC2X1X_MANUFACTURER_ID_VAL) {
|
|
LOG_ERR("Wrong manufacturer id");
|
|
return -ENODEV;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Initialize the SD-Pin.
|
|
* @param dev - The device structure.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_init_sd_pin(const struct device *dev)
|
|
{
|
|
const struct fdc2x1x_config *cfg = dev->config;
|
|
|
|
if (!device_is_ready(cfg->sd_gpio.port)) {
|
|
LOG_ERR("%s: sd_gpio device not ready", cfg->sd_gpio.port->name);
|
|
return -ENODEV;
|
|
}
|
|
|
|
gpio_pin_configure_dt(&cfg->sd_gpio, GPIO_OUTPUT_INACTIVE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Initialization of the device.
|
|
* @param dev - The device structure.
|
|
* @return 0 in case of success, negative error code otherwise.
|
|
*/
|
|
static int fdc2x1x_init(const struct device *dev)
|
|
{
|
|
const struct fdc2x1x_config *cfg = dev->config;
|
|
uint8_t ch_supported;
|
|
|
|
if (cfg->fdc2x14) {
|
|
ch_supported = 4;
|
|
} else {
|
|
ch_supported = 2;
|
|
}
|
|
|
|
if (cfg->num_channels == 0) {
|
|
LOG_ERR("No channel nodes found");
|
|
return -EINVAL;
|
|
} else if (cfg->num_channels > ch_supported) {
|
|
LOG_ERR("Amount of channels not supported by this device");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (cfg->sd_gpio.port->name) {
|
|
if (fdc2x1x_init_sd_pin(dev) < 0) {
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
if (!device_is_ready(cfg->i2c.bus)) {
|
|
LOG_ERR("I2C bus device not ready");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (fdc2x1x_probe(dev) < 0) {
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (fdc2x1x_reset(dev) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
if (fdc2x1x_init_config(dev) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
if (fdc2x1x_set_op_mode(dev, FDC2X1X_ACTIVE_MODE) < 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
#ifdef CONFIG_FDC2X1X_TRIGGER
|
|
if (fdc2x1x_init_interrupt(dev) < 0) {
|
|
LOG_ERR("Failed to initialize interrupt!");
|
|
return -EIO;
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define FDC2X1X_SD_PROPS(n) \
|
|
.sd_gpio = GPIO_DT_SPEC_INST_GET(n, sd_gpios), \
|
|
|
|
#define FDC2X1X_SD(n) \
|
|
IF_ENABLED(DT_INST_NODE_HAS_PROP(n, sd_gpios), \
|
|
(FDC2X1X_SD_PROPS(n)))
|
|
|
|
#define FDC2X1X_INTB_PROPS(n) \
|
|
.intb_gpio = GPIO_DT_SPEC_INST_GET(n, intb_gpios), \
|
|
|
|
#define FDC2X1X_INTB(n) \
|
|
IF_ENABLED(CONFIG_FDC2X1X_TRIGGER, \
|
|
(FDC2X1X_INTB_PROPS(n)))
|
|
|
|
#define FDC2X1X_CH_CFG_INIT(ch) \
|
|
{ \
|
|
.rcount = DT_PROP(ch, rcount), \
|
|
.offset = DT_PROP(ch, offset), \
|
|
.settle_count = DT_PROP(ch, settlecount), \
|
|
.fref_divider = DT_PROP(ch, fref_divider), \
|
|
.idrive = DT_PROP(ch, idrive), \
|
|
.fin_sel = DT_PROP(ch, fin_sel), \
|
|
.inductance = DT_PROP(ch, inductance), \
|
|
},
|
|
|
|
#define FDC2X1X_CHANNEL_BUF_INIT(ch) 0,
|
|
|
|
#define FDC2X1X_INIT(n) \
|
|
static uint32_t fdc2x1x_sample_buf_##n[] = { \
|
|
DT_INST_FOREACH_CHILD(n, FDC2X1X_CHANNEL_BUF_INIT) \
|
|
}; \
|
|
\
|
|
static struct fdc2x1x_data fdc2x1x_data_##n = { \
|
|
.channel_buf = fdc2x1x_sample_buf_##n, \
|
|
}; \
|
|
\
|
|
const struct fdc2x1x_chx_config ch_cfg_##n[] = { \
|
|
DT_INST_FOREACH_CHILD(n, FDC2X1X_CH_CFG_INIT) \
|
|
}; \
|
|
\
|
|
static const struct fdc2x1x_config fdc2x1x_config_##n = { \
|
|
.i2c = I2C_DT_SPEC_INST_GET(n), \
|
|
.fdc2x14 = DT_INST_PROP(n, fdc2x14), \
|
|
.autoscan_en = DT_INST_PROP(n, autoscan), \
|
|
.rr_sequence = DT_INST_PROP(n, rr_sequence), \
|
|
.active_channel = DT_INST_PROP(n, active_channel), \
|
|
.deglitch = DT_INST_PROP(n, deglitch), \
|
|
.sensor_activate_sel = \
|
|
DT_INST_ENUM_IDX(n, sensor_activate_sel), \
|
|
.clk_src = DT_INST_ENUM_IDX(n, ref_clk_src), \
|
|
.current_drv = DT_INST_ENUM_IDX(n, current_drive), \
|
|
.output_gain = DT_INST_PROP(n, output_gain), \
|
|
.ch_cfg = ch_cfg_##n, \
|
|
.num_channels = ARRAY_SIZE(fdc2x1x_sample_buf_##n), \
|
|
.fref = DT_INST_PROP(n, fref), \
|
|
FDC2X1X_SD(n) \
|
|
FDC2X1X_INTB(n) \
|
|
}; \
|
|
\
|
|
PM_DEVICE_DT_INST_DEFINE(n, fdc2x1x_device_pm_action); \
|
|
\
|
|
SENSOR_DEVICE_DT_INST_DEFINE(n, \
|
|
fdc2x1x_init, \
|
|
PM_DEVICE_DT_INST_GET(n), \
|
|
&fdc2x1x_data_##n, \
|
|
&fdc2x1x_config_##n, \
|
|
POST_KERNEL, \
|
|
CONFIG_SENSOR_INIT_PRIORITY, \
|
|
&fdc2x1x_api_funcs);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(FDC2X1X_INIT)
|