blob: 8fec0c0dc1f9216000775653bc0bd9005ea8a720 [file] [log] [blame]
/* 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); \
\
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)