Files
linux/drivers/iio/accel/adxl355_core.c
Valek Andrej c92c1bc408 iio: accel: fix ADXL355 startup race condition
There is an race-condition where device is not full working after SW reset.
Therefore it's necessary to wait some time after reset and verify shadow
registers values by reading and comparing the values before/after reset.
This mechanism is described in datasheet at least from revision D.

Fixes: 12ed27863e ("iio: accel: Add driver support for ADXL355")
Signed-off-by: Valek Andrej <andrej.v@skyrain.eu>
Signed-off-by: Kessler Markus <markus.kessler@hilti.com>
Cc: <Stable@vger.kernel.org>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
2025-10-18 14:07:16 +01:00

843 lines
21 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* ADXL355 3-Axis Digital Accelerometer IIO core driver
*
* Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com>
*
* Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/adxl354_adxl355.pdf
*/
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/limits.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/units.h>
#include <linux/unaligned.h>
#include "adxl355.h"
/* ADXL355 Register Definitions */
#define ADXL355_DEVID_AD_REG 0x00
#define ADXL355_DEVID_MST_REG 0x01
#define ADXL355_PARTID_REG 0x02
#define ADXL355_STATUS_REG 0x04
#define ADXL355_FIFO_ENTRIES_REG 0x05
#define ADXL355_TEMP2_REG 0x06
#define ADXL355_XDATA3_REG 0x08
#define ADXL355_YDATA3_REG 0x0B
#define ADXL355_ZDATA3_REG 0x0E
#define ADXL355_FIFO_DATA_REG 0x11
#define ADXL355_OFFSET_X_H_REG 0x1E
#define ADXL355_OFFSET_Y_H_REG 0x20
#define ADXL355_OFFSET_Z_H_REG 0x22
#define ADXL355_ACT_EN_REG 0x24
#define ADXL355_ACT_THRESH_H_REG 0x25
#define ADXL355_ACT_THRESH_L_REG 0x26
#define ADXL355_ACT_COUNT_REG 0x27
#define ADXL355_FILTER_REG 0x28
#define ADXL355_FILTER_ODR_MSK GENMASK(3, 0)
#define ADXL355_FILTER_HPF_MSK GENMASK(6, 4)
#define ADXL355_FIFO_SAMPLES_REG 0x29
#define ADXL355_INT_MAP_REG 0x2A
#define ADXL355_SYNC_REG 0x2B
#define ADXL355_RANGE_REG 0x2C
#define ADXL355_POWER_CTL_REG 0x2D
#define ADXL355_POWER_CTL_MODE_MSK GENMASK(1, 0)
#define ADXL355_POWER_CTL_DRDY_MSK BIT(2)
#define ADXL355_SELF_TEST_REG 0x2E
#define ADXL355_RESET_REG 0x2F
#define ADXL355_BASE_ADDR_SHADOW_REG 0x50
#define ADXL355_SHADOW_REG_COUNT 5
#define ADXL355_DEVID_AD_VAL 0xAD
#define ADXL355_DEVID_MST_VAL 0x1D
#define ADXL355_PARTID_VAL 0xED
#define ADXL359_PARTID_VAL 0xE9
#define ADXL355_RESET_CODE 0x52
static const struct regmap_range adxl355_read_reg_range[] = {
regmap_reg_range(ADXL355_DEVID_AD_REG, ADXL355_FIFO_DATA_REG),
regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_SELF_TEST_REG),
};
const struct regmap_access_table adxl355_readable_regs_tbl = {
.yes_ranges = adxl355_read_reg_range,
.n_yes_ranges = ARRAY_SIZE(adxl355_read_reg_range),
};
EXPORT_SYMBOL_NS_GPL(adxl355_readable_regs_tbl, "IIO_ADXL355");
static const struct regmap_range adxl355_write_reg_range[] = {
regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_RESET_REG),
};
const struct regmap_access_table adxl355_writeable_regs_tbl = {
.yes_ranges = adxl355_write_reg_range,
.n_yes_ranges = ARRAY_SIZE(adxl355_write_reg_range),
};
EXPORT_SYMBOL_NS_GPL(adxl355_writeable_regs_tbl, "IIO_ADXL355");
const struct adxl355_chip_info adxl35x_chip_info[] = {
[ADXL355] = {
.name = "adxl355",
.part_id = ADXL355_PARTID_VAL,
/*
* At +/- 2g with 20-bit resolution, scale is given in datasheet
* as 3.9ug/LSB = 0.0000039 * 9.80665 = 0.00003824593 m/s^2.
*/
.accel_scale = {
.integer = 0,
.decimal = 38245,
},
/*
* The datasheet defines an intercept of 1885 LSB at 25 degC
* and a slope of -9.05 LSB/C. The following formula can be used
* to find the temperature:
* Temp = ((RAW - 1885)/(-9.05)) + 25 but this doesn't follow
* the format of the IIO which is Temp = (RAW + OFFSET) * SCALE.
* Hence using some rearranging we get the scale as -110.497238
* and offset as -2111.25.
*/
.temp_offset = {
.integer = -2111,
.decimal = 250000,
},
},
[ADXL359] = {
.name = "adxl359",
.part_id = ADXL359_PARTID_VAL,
/*
* At +/- 10g with 20-bit resolution, scale is given in datasheet
* as 19.5ug/LSB = 0.0000195 * 9.80665 = 0.0.00019122967 m/s^2.
*/
.accel_scale = {
.integer = 0,
.decimal = 191229,
},
/*
* The datasheet defines an intercept of 1852 LSB at 25 degC
* and a slope of -9.05 LSB/C. The following formula can be used
* to find the temperature:
* Temp = ((RAW - 1852)/(-9.05)) + 25 but this doesn't follow
* the format of the IIO which is Temp = (RAW + OFFSET) * SCALE.
* Hence using some rearranging we get the scale as -110.497238
* and offset as -2079.25.
*/
.temp_offset = {
.integer = -2079,
.decimal = 250000,
},
},
};
EXPORT_SYMBOL_NS_GPL(adxl35x_chip_info, "IIO_ADXL355");
enum adxl355_op_mode {
ADXL355_MEASUREMENT,
ADXL355_STANDBY,
ADXL355_TEMP_OFF,
};
enum adxl355_odr {
ADXL355_ODR_4000HZ,
ADXL355_ODR_2000HZ,
ADXL355_ODR_1000HZ,
ADXL355_ODR_500HZ,
ADXL355_ODR_250HZ,
ADXL355_ODR_125HZ,
ADXL355_ODR_62_5HZ,
ADXL355_ODR_31_25HZ,
ADXL355_ODR_15_625HZ,
ADXL355_ODR_7_813HZ,
ADXL355_ODR_3_906HZ,
};
enum adxl355_hpf_3db {
ADXL355_HPF_OFF,
ADXL355_HPF_24_7,
ADXL355_HPF_6_2084,
ADXL355_HPF_1_5545,
ADXL355_HPF_0_3862,
ADXL355_HPF_0_0954,
ADXL355_HPF_0_0238,
};
static const int adxl355_odr_table[][2] = {
[0] = {4000, 0},
[1] = {2000, 0},
[2] = {1000, 0},
[3] = {500, 0},
[4] = {250, 0},
[5] = {125, 0},
[6] = {62, 500000},
[7] = {31, 250000},
[8] = {15, 625000},
[9] = {7, 813000},
[10] = {3, 906000},
};
static const int adxl355_hpf_3db_multipliers[] = {
0,
247000,
62084,
15545,
3862,
954,
238,
};
enum adxl355_chans {
chan_x, chan_y, chan_z,
};
struct adxl355_chan_info {
u8 data_reg;
u8 offset_reg;
};
static const struct adxl355_chan_info adxl355_chans[] = {
[chan_x] = {
.data_reg = ADXL355_XDATA3_REG,
.offset_reg = ADXL355_OFFSET_X_H_REG
},
[chan_y] = {
.data_reg = ADXL355_YDATA3_REG,
.offset_reg = ADXL355_OFFSET_Y_H_REG
},
[chan_z] = {
.data_reg = ADXL355_ZDATA3_REG,
.offset_reg = ADXL355_OFFSET_Z_H_REG
},
};
struct adxl355_data {
const struct adxl355_chip_info *chip_info;
struct regmap *regmap;
struct device *dev;
struct mutex lock; /* lock to protect op_mode */
enum adxl355_op_mode op_mode;
enum adxl355_odr odr;
enum adxl355_hpf_3db hpf_3db;
int calibbias[3];
int adxl355_hpf_3db_table[7][2];
struct iio_trigger *dready_trig;
union {
u8 transf_buf[3];
struct {
u8 buf[14];
aligned_s64 ts;
} buffer;
} __aligned(IIO_DMA_MINALIGN);
};
static int adxl355_set_op_mode(struct adxl355_data *data,
enum adxl355_op_mode op_mode)
{
int ret;
if (data->op_mode == op_mode)
return 0;
ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG,
ADXL355_POWER_CTL_MODE_MSK, op_mode);
if (ret)
return ret;
data->op_mode = op_mode;
return ret;
}
static int adxl355_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool state)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct adxl355_data *data = iio_priv(indio_dev);
int ret;
mutex_lock(&data->lock);
ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG,
ADXL355_POWER_CTL_DRDY_MSK,
FIELD_PREP(ADXL355_POWER_CTL_DRDY_MSK,
state ? 0 : 1));
mutex_unlock(&data->lock);
return ret;
}
static void adxl355_fill_3db_frequency_table(struct adxl355_data *data)
{
u32 multiplier;
u64 div, rem;
u64 odr;
int i;
odr = mul_u64_u32_shr(adxl355_odr_table[data->odr][0], MEGA, 0) +
adxl355_odr_table[data->odr][1];
for (i = 0; i < ARRAY_SIZE(adxl355_hpf_3db_multipliers); i++) {
multiplier = adxl355_hpf_3db_multipliers[i];
div = div64_u64_rem(mul_u64_u32_shr(odr, multiplier, 0),
TERA * 100, &rem);
data->adxl355_hpf_3db_table[i][0] = div;
data->adxl355_hpf_3db_table[i][1] = div_u64(rem, MEGA * 100);
}
}
static int adxl355_setup(struct adxl355_data *data)
{
unsigned int regval;
int retries = 5; /* the number is chosen based on empirical reasons */
int ret;
u8 *shadow_regs __free(kfree) = kzalloc(ADXL355_SHADOW_REG_COUNT, GFP_KERNEL);
if (!shadow_regs)
return -ENOMEM;
ret = regmap_read(data->regmap, ADXL355_DEVID_AD_REG, &regval);
if (ret)
return ret;
if (regval != ADXL355_DEVID_AD_VAL) {
dev_err(data->dev, "Invalid ADI ID 0x%02x\n", regval);
return -ENODEV;
}
ret = regmap_read(data->regmap, ADXL355_DEVID_MST_REG, &regval);
if (ret)
return ret;
if (regval != ADXL355_DEVID_MST_VAL) {
dev_err(data->dev, "Invalid MEMS ID 0x%02x\n", regval);
return -ENODEV;
}
ret = regmap_read(data->regmap, ADXL355_PARTID_REG, &regval);
if (ret)
return ret;
if (regval != ADXL355_PARTID_VAL)
dev_warn(data->dev, "Invalid DEV ID 0x%02x\n", regval);
/* Read shadow registers to be compared after reset */
ret = regmap_bulk_read(data->regmap,
ADXL355_BASE_ADDR_SHADOW_REG,
shadow_regs, ADXL355_SHADOW_REG_COUNT);
if (ret)
return ret;
do {
if (--retries == 0) {
dev_err(data->dev, "Shadow registers mismatch\n");
return -EIO;
}
/*
* Perform a software reset to make sure the device is in a consistent
* state after start-up.
*/
ret = regmap_write(data->regmap, ADXL355_RESET_REG,
ADXL355_RESET_CODE);
if (ret)
return ret;
/* Wait at least 5ms after software reset */
usleep_range(5000, 10000);
/* Read shadow registers for comparison */
ret = regmap_bulk_read(data->regmap,
ADXL355_BASE_ADDR_SHADOW_REG,
data->buffer.buf,
ADXL355_SHADOW_REG_COUNT);
if (ret)
return ret;
} while (memcmp(shadow_regs, data->buffer.buf,
ADXL355_SHADOW_REG_COUNT));
ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG,
ADXL355_POWER_CTL_DRDY_MSK,
FIELD_PREP(ADXL355_POWER_CTL_DRDY_MSK, 1));
if (ret)
return ret;
adxl355_fill_3db_frequency_table(data);
return adxl355_set_op_mode(data, ADXL355_MEASUREMENT);
}
static int adxl355_get_temp_data(struct adxl355_data *data, u8 addr)
{
return regmap_bulk_read(data->regmap, addr, data->transf_buf, 2);
}
static int adxl355_read_axis(struct adxl355_data *data, u8 addr)
{
int ret;
ret = regmap_bulk_read(data->regmap, addr, data->transf_buf,
ARRAY_SIZE(data->transf_buf));
if (ret)
return ret;
return get_unaligned_be24(data->transf_buf);
}
static int adxl355_find_match(const int (*freq_tbl)[2], const int n,
const int val, const int val2)
{
int i;
for (i = 0; i < n; i++) {
if (freq_tbl[i][0] == val && freq_tbl[i][1] == val2)
return i;
}
return -EINVAL;
}
static int adxl355_set_odr(struct adxl355_data *data,
enum adxl355_odr odr)
{
int ret;
mutex_lock(&data->lock);
if (data->odr == odr) {
mutex_unlock(&data->lock);
return 0;
}
ret = adxl355_set_op_mode(data, ADXL355_STANDBY);
if (ret)
goto err_unlock;
ret = regmap_update_bits(data->regmap, ADXL355_FILTER_REG,
ADXL355_FILTER_ODR_MSK,
FIELD_PREP(ADXL355_FILTER_ODR_MSK, odr));
if (ret)
goto err_set_opmode;
data->odr = odr;
adxl355_fill_3db_frequency_table(data);
ret = adxl355_set_op_mode(data, ADXL355_MEASUREMENT);
if (ret)
goto err_set_opmode;
mutex_unlock(&data->lock);
return 0;
err_set_opmode:
adxl355_set_op_mode(data, ADXL355_MEASUREMENT);
err_unlock:
mutex_unlock(&data->lock);
return ret;
}
static int adxl355_set_hpf_3db(struct adxl355_data *data,
enum adxl355_hpf_3db hpf)
{
int ret;
mutex_lock(&data->lock);
if (data->hpf_3db == hpf) {
mutex_unlock(&data->lock);
return 0;
}
ret = adxl355_set_op_mode(data, ADXL355_STANDBY);
if (ret)
goto err_unlock;
ret = regmap_update_bits(data->regmap, ADXL355_FILTER_REG,
ADXL355_FILTER_HPF_MSK,
FIELD_PREP(ADXL355_FILTER_HPF_MSK, hpf));
if (ret)
goto err_set_opmode;
data->hpf_3db = hpf;
ret = adxl355_set_op_mode(data, ADXL355_MEASUREMENT);
if (ret)
goto err_set_opmode;
mutex_unlock(&data->lock);
return 0;
err_set_opmode:
adxl355_set_op_mode(data, ADXL355_MEASUREMENT);
err_unlock:
mutex_unlock(&data->lock);
return ret;
}
static int adxl355_set_calibbias(struct adxl355_data *data,
enum adxl355_chans chan, int calibbias)
{
int ret;
mutex_lock(&data->lock);
ret = adxl355_set_op_mode(data, ADXL355_STANDBY);
if (ret)
goto err_unlock;
put_unaligned_be16(calibbias, data->transf_buf);
ret = regmap_bulk_write(data->regmap,
adxl355_chans[chan].offset_reg,
data->transf_buf, 2);
if (ret)
goto err_set_opmode;
data->calibbias[chan] = calibbias;
ret = adxl355_set_op_mode(data, ADXL355_MEASUREMENT);
if (ret)
goto err_set_opmode;
mutex_unlock(&data->lock);
return 0;
err_set_opmode:
adxl355_set_op_mode(data, ADXL355_MEASUREMENT);
err_unlock:
mutex_unlock(&data->lock);
return ret;
}
static int adxl355_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct adxl355_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_TEMP:
ret = adxl355_get_temp_data(data, chan->address);
if (ret < 0)
return ret;
*val = get_unaligned_be16(data->transf_buf);
return IIO_VAL_INT;
case IIO_ACCEL:
ret = adxl355_read_axis(data, adxl355_chans[
chan->address].data_reg);
if (ret < 0)
return ret;
*val = sign_extend32(ret >> chan->scan_type.shift,
chan->scan_type.realbits - 1);
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_TEMP:
/*
* Temperature scale is -110.497238.
* See the detailed explanation in adxl35x_chip_info
* definition above.
*/
*val = -110;
*val2 = 497238;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_ACCEL:
*val = data->chip_info->accel_scale.integer;
*val2 = data->chip_info->accel_scale.decimal;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_OFFSET:
*val = data->chip_info->temp_offset.integer;
*val2 = data->chip_info->temp_offset.decimal;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_CALIBBIAS:
*val = sign_extend32(data->calibbias[chan->address], 15);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = adxl355_odr_table[data->odr][0];
*val2 = adxl355_odr_table[data->odr][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
*val = data->adxl355_hpf_3db_table[data->hpf_3db][0];
*val2 = data->adxl355_hpf_3db_table[data->hpf_3db][1];
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static int adxl355_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct adxl355_data *data = iio_priv(indio_dev);
int odr_idx, hpf_idx, calibbias;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
odr_idx = adxl355_find_match(adxl355_odr_table,
ARRAY_SIZE(adxl355_odr_table),
val, val2);
if (odr_idx < 0)
return odr_idx;
return adxl355_set_odr(data, odr_idx);
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
hpf_idx = adxl355_find_match(data->adxl355_hpf_3db_table,
ARRAY_SIZE(data->adxl355_hpf_3db_table),
val, val2);
if (hpf_idx < 0)
return hpf_idx;
return adxl355_set_hpf_3db(data, hpf_idx);
case IIO_CHAN_INFO_CALIBBIAS:
calibbias = clamp_t(int, val, S16_MIN, S16_MAX);
return adxl355_set_calibbias(data, chan->address, calibbias);
default:
return -EINVAL;
}
}
static int adxl355_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
struct adxl355_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
*vals = (const int *)adxl355_odr_table;
*type = IIO_VAL_INT_PLUS_MICRO;
/* Values are stored in a 2D matrix */
*length = ARRAY_SIZE(adxl355_odr_table) * 2;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
*vals = (const int *)data->adxl355_hpf_3db_table;
*type = IIO_VAL_INT_PLUS_MICRO;
/* Values are stored in a 2D matrix */
*length = ARRAY_SIZE(data->adxl355_hpf_3db_table) * 2;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static const unsigned long adxl355_avail_scan_masks[] = {
GENMASK(3, 0),
0
};
static const struct iio_info adxl355_info = {
.read_raw = adxl355_read_raw,
.write_raw = adxl355_write_raw,
.read_avail = &adxl355_read_avail,
};
static const struct iio_trigger_ops adxl355_trigger_ops = {
.set_trigger_state = &adxl355_data_rdy_trigger_set_state,
.validate_device = &iio_trigger_validate_own_device,
};
static irqreturn_t adxl355_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct adxl355_data *data = iio_priv(indio_dev);
int ret;
mutex_lock(&data->lock);
/*
* data->buffer is used both for triggered buffer support
* and read/write_raw(), hence, it has to be zeroed here before usage.
*/
data->buffer.buf[0] = 0;
/*
* The acceleration data is 24 bits and big endian. It has to be saved
* in 32 bits, hence, it is saved in the 2nd byte of the 4 byte buffer.
* The buf array is 14 bytes as it includes 3x4=12 bytes for
* acceleration data of x, y, and z axis. It also includes 2 bytes for
* temperature data.
*/
ret = regmap_bulk_read(data->regmap, ADXL355_XDATA3_REG,
&data->buffer.buf[1], 3);
if (ret)
goto out_unlock_notify;
ret = regmap_bulk_read(data->regmap, ADXL355_YDATA3_REG,
&data->buffer.buf[5], 3);
if (ret)
goto out_unlock_notify;
ret = regmap_bulk_read(data->regmap, ADXL355_ZDATA3_REG,
&data->buffer.buf[9], 3);
if (ret)
goto out_unlock_notify;
ret = regmap_bulk_read(data->regmap, ADXL355_TEMP2_REG,
&data->buffer.buf[12], 2);
if (ret)
goto out_unlock_notify;
iio_push_to_buffers_with_ts(indio_dev, &data->buffer,
sizeof(data->buffer), pf->timestamp);
out_unlock_notify:
mutex_unlock(&data->lock);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
#define ADXL355_ACCEL_CHANNEL(index, reg, axis) { \
.type = IIO_ACCEL, \
.address = reg, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_CALIBBIAS), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \
.info_mask_shared_by_type_available = \
BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \
.scan_index = index, \
.scan_type = { \
.sign = 's', \
.realbits = 20, \
.storagebits = 32, \
.shift = 4, \
.endianness = IIO_BE, \
} \
}
static const struct iio_chan_spec adxl355_channels[] = {
ADXL355_ACCEL_CHANNEL(0, chan_x, X),
ADXL355_ACCEL_CHANNEL(1, chan_y, Y),
ADXL355_ACCEL_CHANNEL(2, chan_z, Z),
{
.type = IIO_TEMP,
.address = ADXL355_TEMP2_REG,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.scan_index = 3,
.scan_type = {
.sign = 's',
.realbits = 12,
.storagebits = 16,
.endianness = IIO_BE,
},
},
IIO_CHAN_SOFT_TIMESTAMP(4),
};
static int adxl355_probe_trigger(struct iio_dev *indio_dev, int irq)
{
struct adxl355_data *data = iio_priv(indio_dev);
int ret;
data->dready_trig = devm_iio_trigger_alloc(data->dev, "%s-dev%d",
indio_dev->name,
iio_device_id(indio_dev));
if (!data->dready_trig)
return -ENOMEM;
data->dready_trig->ops = &adxl355_trigger_ops;
iio_trigger_set_drvdata(data->dready_trig, indio_dev);
ret = devm_request_irq(data->dev, irq,
&iio_trigger_generic_data_rdy_poll,
IRQF_ONESHOT, "adxl355_irq", data->dready_trig);
if (ret)
return dev_err_probe(data->dev, ret, "request irq %d failed\n",
irq);
ret = devm_iio_trigger_register(data->dev, data->dready_trig);
if (ret) {
dev_err(data->dev, "iio trigger register failed\n");
return ret;
}
indio_dev->trig = iio_trigger_get(data->dready_trig);
return 0;
}
int adxl355_core_probe(struct device *dev, struct regmap *regmap,
const struct adxl355_chip_info *chip_info)
{
struct adxl355_data *data;
struct iio_dev *indio_dev;
int ret;
int irq;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->regmap = regmap;
data->dev = dev;
data->op_mode = ADXL355_STANDBY;
data->chip_info = chip_info;
mutex_init(&data->lock);
indio_dev->name = chip_info->name;
indio_dev->info = &adxl355_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = adxl355_channels;
indio_dev->num_channels = ARRAY_SIZE(adxl355_channels);
indio_dev->available_scan_masks = adxl355_avail_scan_masks;
ret = adxl355_setup(data);
if (ret) {
dev_err(dev, "ADXL355 setup failed\n");
return ret;
}
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
&iio_pollfunc_store_time,
&adxl355_trigger_handler, NULL);
if (ret) {
dev_err(dev, "iio triggered buffer setup failed\n");
return ret;
}
irq = fwnode_irq_get_byname(dev_fwnode(dev), "DRDY");
if (irq > 0) {
ret = adxl355_probe_trigger(indio_dev, irq);
if (ret)
return ret;
}
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS_GPL(adxl355_core_probe, "IIO_ADXL355");
MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>");
MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer core driver");
MODULE_LICENSE("GPL v2");