linux/drivers/iio/accel/adxl367.c
Al Viro 5f60d5f6bb move asm/unaligned.h to linux/unaligned.h
asm/unaligned.h is always an include of asm-generic/unaligned.h;
might as well move that thing to linux/unaligned.h and include
that - there's nothing arch-specific in that header.

auto-generated by the following:

for i in `git grep -l -w asm/unaligned.h`; do
	sed -i -e "s/asm\/unaligned.h/linux\/unaligned.h/" $i
done
for i in `git grep -l -w asm-generic/unaligned.h`; do
	sed -i -e "s/asm-generic\/unaligned.h/linux\/unaligned.h/" $i
done
git mv include/asm-generic/unaligned.h include/linux/unaligned.h
git mv tools/include/asm-generic/unaligned.h tools/include/linux/unaligned.h
sed -i -e "/unaligned.h/d" include/asm-generic/Kbuild
sed -i -e "s/__ASM_GENERIC/__LINUX/" include/linux/unaligned.h tools/include/linux/unaligned.h
2024-10-02 17:23:23 -04:00

1483 lines
36 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2021 Analog Devices, Inc.
* Author: Cosmin Tanislav <cosmin.tanislav@analog.com>
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/sysfs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/mod_devicetable.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/unaligned.h>
#include "adxl367.h"
#define ADXL367_REG_DEVID 0x00
#define ADXL367_DEVID_AD 0xAD
#define ADXL367_REG_STATUS 0x0B
#define ADXL367_STATUS_INACT_MASK BIT(5)
#define ADXL367_STATUS_ACT_MASK BIT(4)
#define ADXL367_STATUS_FIFO_FULL_MASK BIT(2)
#define ADXL367_FIFO_ENT_H_MASK GENMASK(1, 0)
#define ADXL367_REG_X_DATA_H 0x0E
#define ADXL367_REG_Y_DATA_H 0x10
#define ADXL367_REG_Z_DATA_H 0x12
#define ADXL367_REG_TEMP_DATA_H 0x14
#define ADXL367_REG_EX_ADC_DATA_H 0x16
#define ADXL367_DATA_MASK GENMASK(15, 2)
#define ADXL367_TEMP_25C 165
#define ADXL367_TEMP_PER_C 54
#define ADXL367_VOLTAGE_OFFSET 8192
#define ADXL367_VOLTAGE_MAX_MV 1000
#define ADXL367_VOLTAGE_MAX_RAW GENMASK(13, 0)
#define ADXL367_REG_RESET 0x1F
#define ADXL367_RESET_CODE 0x52
#define ADXL367_REG_THRESH_ACT_H 0x20
#define ADXL367_REG_THRESH_INACT_H 0x23
#define ADXL367_THRESH_MAX GENMASK(12, 0)
#define ADXL367_THRESH_VAL_H_MASK GENMASK(12, 6)
#define ADXL367_THRESH_H_MASK GENMASK(6, 0)
#define ADXL367_THRESH_VAL_L_MASK GENMASK(5, 0)
#define ADXL367_THRESH_L_MASK GENMASK(7, 2)
#define ADXL367_REG_TIME_ACT 0x22
#define ADXL367_REG_TIME_INACT_H 0x25
#define ADXL367_TIME_ACT_MAX GENMASK(7, 0)
#define ADXL367_TIME_INACT_MAX GENMASK(15, 0)
#define ADXL367_TIME_INACT_VAL_H_MASK GENMASK(15, 8)
#define ADXL367_TIME_INACT_H_MASK GENMASK(7, 0)
#define ADXL367_TIME_INACT_VAL_L_MASK GENMASK(7, 0)
#define ADXL367_TIME_INACT_L_MASK GENMASK(7, 0)
#define ADXL367_REG_ACT_INACT_CTL 0x27
#define ADXL367_ACT_EN_MASK GENMASK(1, 0)
#define ADXL367_ACT_LINKLOOP_MASK GENMASK(5, 4)
#define ADXL367_REG_FIFO_CTL 0x28
#define ADXL367_FIFO_CTL_FORMAT_MASK GENMASK(6, 3)
#define ADXL367_FIFO_CTL_MODE_MASK GENMASK(1, 0)
#define ADXL367_REG_FIFO_SAMPLES 0x29
#define ADXL367_FIFO_SIZE 512
#define ADXL367_FIFO_MAX_WATERMARK 511
#define ADXL367_SAMPLES_VAL_H_MASK BIT(8)
#define ADXL367_SAMPLES_H_MASK BIT(2)
#define ADXL367_SAMPLES_VAL_L_MASK GENMASK(7, 0)
#define ADXL367_SAMPLES_L_MASK GENMASK(7, 0)
#define ADXL367_REG_INT1_MAP 0x2A
#define ADXL367_INT_INACT_MASK BIT(5)
#define ADXL367_INT_ACT_MASK BIT(4)
#define ADXL367_INT_FIFO_WATERMARK_MASK BIT(2)
#define ADXL367_REG_FILTER_CTL 0x2C
#define ADXL367_FILTER_CTL_RANGE_MASK GENMASK(7, 6)
#define ADXL367_2G_RANGE_1G 4095
#define ADXL367_2G_RANGE_100MG 409
#define ADXL367_FILTER_CTL_ODR_MASK GENMASK(2, 0)
#define ADXL367_REG_POWER_CTL 0x2D
#define ADXL367_POWER_CTL_MODE_MASK GENMASK(1, 0)
#define ADXL367_REG_ADC_CTL 0x3C
#define ADXL367_REG_TEMP_CTL 0x3D
#define ADXL367_ADC_EN_MASK BIT(0)
enum adxl367_range {
ADXL367_2G_RANGE,
ADXL367_4G_RANGE,
ADXL367_8G_RANGE,
};
enum adxl367_fifo_mode {
ADXL367_FIFO_MODE_DISABLED = 0b00,
ADXL367_FIFO_MODE_STREAM = 0b10,
};
enum adxl367_fifo_format {
ADXL367_FIFO_FORMAT_XYZ,
ADXL367_FIFO_FORMAT_X,
ADXL367_FIFO_FORMAT_Y,
ADXL367_FIFO_FORMAT_Z,
ADXL367_FIFO_FORMAT_XYZT,
ADXL367_FIFO_FORMAT_XT,
ADXL367_FIFO_FORMAT_YT,
ADXL367_FIFO_FORMAT_ZT,
ADXL367_FIFO_FORMAT_XYZA,
ADXL367_FIFO_FORMAT_XA,
ADXL367_FIFO_FORMAT_YA,
ADXL367_FIFO_FORMAT_ZA,
};
enum adxl367_op_mode {
ADXL367_OP_STANDBY = 0b00,
ADXL367_OP_MEASURE = 0b10,
};
enum adxl367_act_proc_mode {
ADXL367_LOOPED = 0b11,
};
enum adxl367_act_en_mode {
ADXL367_ACT_DISABLED = 0b00,
ADCL367_ACT_REF_ENABLED = 0b11,
};
enum adxl367_activity_type {
ADXL367_ACTIVITY,
ADXL367_INACTIVITY,
};
enum adxl367_odr {
ADXL367_ODR_12P5HZ,
ADXL367_ODR_25HZ,
ADXL367_ODR_50HZ,
ADXL367_ODR_100HZ,
ADXL367_ODR_200HZ,
ADXL367_ODR_400HZ,
};
struct adxl367_state {
const struct adxl367_ops *ops;
void *context;
struct device *dev;
struct regmap *regmap;
/*
* Synchronize access to members of driver state, and ensure atomicity
* of consecutive regmap operations.
*/
struct mutex lock;
enum adxl367_odr odr;
enum adxl367_range range;
unsigned int act_threshold;
unsigned int act_time_ms;
unsigned int inact_threshold;
unsigned int inact_time_ms;
unsigned int fifo_set_size;
unsigned int fifo_watermark;
__be16 fifo_buf[ADXL367_FIFO_SIZE] __aligned(IIO_DMA_MINALIGN);
__be16 sample_buf;
u8 act_threshold_buf[2];
u8 inact_time_buf[2];
u8 status_buf[3];
};
static const unsigned int adxl367_threshold_h_reg_tbl[] = {
[ADXL367_ACTIVITY] = ADXL367_REG_THRESH_ACT_H,
[ADXL367_INACTIVITY] = ADXL367_REG_THRESH_INACT_H,
};
static const unsigned int adxl367_act_en_shift_tbl[] = {
[ADXL367_ACTIVITY] = 0,
[ADXL367_INACTIVITY] = 2,
};
static const unsigned int adxl367_act_int_mask_tbl[] = {
[ADXL367_ACTIVITY] = ADXL367_INT_ACT_MASK,
[ADXL367_INACTIVITY] = ADXL367_INT_INACT_MASK,
};
static const int adxl367_samp_freq_tbl[][2] = {
[ADXL367_ODR_12P5HZ] = {12, 500000},
[ADXL367_ODR_25HZ] = {25, 0},
[ADXL367_ODR_50HZ] = {50, 0},
[ADXL367_ODR_100HZ] = {100, 0},
[ADXL367_ODR_200HZ] = {200, 0},
[ADXL367_ODR_400HZ] = {400, 0},
};
/* (g * 2) * 9.80665 * 1000000 / (2^14 - 1) */
static const int adxl367_range_scale_tbl[][2] = {
[ADXL367_2G_RANGE] = {0, 2394347},
[ADXL367_4G_RANGE] = {0, 4788695},
[ADXL367_8G_RANGE] = {0, 9577391},
};
static const int adxl367_range_scale_factor_tbl[] = {
[ADXL367_2G_RANGE] = 1,
[ADXL367_4G_RANGE] = 2,
[ADXL367_8G_RANGE] = 4,
};
enum {
ADXL367_X_CHANNEL_INDEX,
ADXL367_Y_CHANNEL_INDEX,
ADXL367_Z_CHANNEL_INDEX,
ADXL367_TEMP_CHANNEL_INDEX,
ADXL367_EX_ADC_CHANNEL_INDEX
};
#define ADXL367_X_CHANNEL_MASK BIT(ADXL367_X_CHANNEL_INDEX)
#define ADXL367_Y_CHANNEL_MASK BIT(ADXL367_Y_CHANNEL_INDEX)
#define ADXL367_Z_CHANNEL_MASK BIT(ADXL367_Z_CHANNEL_INDEX)
#define ADXL367_TEMP_CHANNEL_MASK BIT(ADXL367_TEMP_CHANNEL_INDEX)
#define ADXL367_EX_ADC_CHANNEL_MASK BIT(ADXL367_EX_ADC_CHANNEL_INDEX)
static const enum adxl367_fifo_format adxl367_fifo_formats[] = {
ADXL367_FIFO_FORMAT_X,
ADXL367_FIFO_FORMAT_Y,
ADXL367_FIFO_FORMAT_Z,
ADXL367_FIFO_FORMAT_XT,
ADXL367_FIFO_FORMAT_YT,
ADXL367_FIFO_FORMAT_ZT,
ADXL367_FIFO_FORMAT_XA,
ADXL367_FIFO_FORMAT_YA,
ADXL367_FIFO_FORMAT_ZA,
ADXL367_FIFO_FORMAT_XYZ,
ADXL367_FIFO_FORMAT_XYZT,
ADXL367_FIFO_FORMAT_XYZA,
};
static const unsigned long adxl367_channel_masks[] = {
ADXL367_X_CHANNEL_MASK,
ADXL367_Y_CHANNEL_MASK,
ADXL367_Z_CHANNEL_MASK,
ADXL367_X_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK,
ADXL367_Y_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK,
ADXL367_Z_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK,
ADXL367_X_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK,
ADXL367_Y_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK,
ADXL367_Z_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK,
ADXL367_X_CHANNEL_MASK | ADXL367_Y_CHANNEL_MASK | ADXL367_Z_CHANNEL_MASK,
ADXL367_X_CHANNEL_MASK | ADXL367_Y_CHANNEL_MASK | ADXL367_Z_CHANNEL_MASK |
ADXL367_TEMP_CHANNEL_MASK,
ADXL367_X_CHANNEL_MASK | ADXL367_Y_CHANNEL_MASK | ADXL367_Z_CHANNEL_MASK |
ADXL367_EX_ADC_CHANNEL_MASK,
0,
};
static int adxl367_set_measure_en(struct adxl367_state *st, bool en)
{
enum adxl367_op_mode op_mode = en ? ADXL367_OP_MEASURE
: ADXL367_OP_STANDBY;
int ret;
ret = regmap_update_bits(st->regmap, ADXL367_REG_POWER_CTL,
ADXL367_POWER_CTL_MODE_MASK,
FIELD_PREP(ADXL367_POWER_CTL_MODE_MASK,
op_mode));
if (ret)
return ret;
/*
* Wait for acceleration output to settle after entering
* measure mode.
*/
if (en)
msleep(100);
return 0;
}
static void adxl367_scale_act_thresholds(struct adxl367_state *st,
enum adxl367_range old_range,
enum adxl367_range new_range)
{
st->act_threshold = st->act_threshold
* adxl367_range_scale_factor_tbl[old_range]
/ adxl367_range_scale_factor_tbl[new_range];
st->inact_threshold = st->inact_threshold
* adxl367_range_scale_factor_tbl[old_range]
/ adxl367_range_scale_factor_tbl[new_range];
}
static int _adxl367_set_act_threshold(struct adxl367_state *st,
enum adxl367_activity_type act,
unsigned int threshold)
{
u8 reg = adxl367_threshold_h_reg_tbl[act];
int ret;
if (threshold > ADXL367_THRESH_MAX)
return -EINVAL;
st->act_threshold_buf[0] = FIELD_PREP(ADXL367_THRESH_H_MASK,
FIELD_GET(ADXL367_THRESH_VAL_H_MASK,
threshold));
st->act_threshold_buf[1] = FIELD_PREP(ADXL367_THRESH_L_MASK,
FIELD_GET(ADXL367_THRESH_VAL_L_MASK,
threshold));
ret = regmap_bulk_write(st->regmap, reg, st->act_threshold_buf,
sizeof(st->act_threshold_buf));
if (ret)
return ret;
if (act == ADXL367_ACTIVITY)
st->act_threshold = threshold;
else
st->inact_threshold = threshold;
return 0;
}
static int adxl367_set_act_threshold(struct adxl367_state *st,
enum adxl367_activity_type act,
unsigned int threshold)
{
int ret;
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = _adxl367_set_act_threshold(st, act, threshold);
if (ret)
return ret;
return adxl367_set_measure_en(st, true);
}
static int adxl367_set_act_proc_mode(struct adxl367_state *st,
enum adxl367_act_proc_mode mode)
{
return regmap_update_bits(st->regmap, ADXL367_REG_ACT_INACT_CTL,
ADXL367_ACT_LINKLOOP_MASK,
FIELD_PREP(ADXL367_ACT_LINKLOOP_MASK,
mode));
}
static int adxl367_set_act_interrupt_en(struct adxl367_state *st,
enum adxl367_activity_type act,
bool en)
{
unsigned int mask = adxl367_act_int_mask_tbl[act];
return regmap_update_bits(st->regmap, ADXL367_REG_INT1_MAP,
mask, en ? mask : 0);
}
static int adxl367_get_act_interrupt_en(struct adxl367_state *st,
enum adxl367_activity_type act,
bool *en)
{
unsigned int mask = adxl367_act_int_mask_tbl[act];
unsigned int val;
int ret;
ret = regmap_read(st->regmap, ADXL367_REG_INT1_MAP, &val);
if (ret)
return ret;
*en = !!(val & mask);
return 0;
}
static int adxl367_set_act_en(struct adxl367_state *st,
enum adxl367_activity_type act,
enum adxl367_act_en_mode en)
{
unsigned int ctl_shift = adxl367_act_en_shift_tbl[act];
return regmap_update_bits(st->regmap, ADXL367_REG_ACT_INACT_CTL,
ADXL367_ACT_EN_MASK << ctl_shift,
en << ctl_shift);
}
static int adxl367_set_fifo_watermark_interrupt_en(struct adxl367_state *st,
bool en)
{
return regmap_update_bits(st->regmap, ADXL367_REG_INT1_MAP,
ADXL367_INT_FIFO_WATERMARK_MASK,
en ? ADXL367_INT_FIFO_WATERMARK_MASK : 0);
}
static int adxl367_get_fifo_mode(struct adxl367_state *st,
enum adxl367_fifo_mode *fifo_mode)
{
unsigned int val;
int ret;
ret = regmap_read(st->regmap, ADXL367_REG_FIFO_CTL, &val);
if (ret)
return ret;
*fifo_mode = FIELD_GET(ADXL367_FIFO_CTL_MODE_MASK, val);
return 0;
}
static int adxl367_set_fifo_mode(struct adxl367_state *st,
enum adxl367_fifo_mode fifo_mode)
{
return regmap_update_bits(st->regmap, ADXL367_REG_FIFO_CTL,
ADXL367_FIFO_CTL_MODE_MASK,
FIELD_PREP(ADXL367_FIFO_CTL_MODE_MASK,
fifo_mode));
}
static int adxl367_set_fifo_format(struct adxl367_state *st,
enum adxl367_fifo_format fifo_format)
{
return regmap_update_bits(st->regmap, ADXL367_REG_FIFO_CTL,
ADXL367_FIFO_CTL_FORMAT_MASK,
FIELD_PREP(ADXL367_FIFO_CTL_FORMAT_MASK,
fifo_format));
}
static int adxl367_set_fifo_watermark(struct adxl367_state *st,
unsigned int fifo_watermark)
{
unsigned int fifo_samples = fifo_watermark * st->fifo_set_size;
unsigned int fifo_samples_h, fifo_samples_l;
int ret;
if (fifo_samples > ADXL367_FIFO_MAX_WATERMARK)
fifo_samples = ADXL367_FIFO_MAX_WATERMARK;
fifo_samples /= st->fifo_set_size;
fifo_samples_h = FIELD_PREP(ADXL367_SAMPLES_H_MASK,
FIELD_GET(ADXL367_SAMPLES_VAL_H_MASK,
fifo_samples));
fifo_samples_l = FIELD_PREP(ADXL367_SAMPLES_L_MASK,
FIELD_GET(ADXL367_SAMPLES_VAL_L_MASK,
fifo_samples));
ret = regmap_update_bits(st->regmap, ADXL367_REG_FIFO_CTL,
ADXL367_SAMPLES_H_MASK, fifo_samples_h);
if (ret)
return ret;
ret = regmap_update_bits(st->regmap, ADXL367_REG_FIFO_SAMPLES,
ADXL367_SAMPLES_L_MASK, fifo_samples_l);
if (ret)
return ret;
st->fifo_watermark = fifo_watermark;
return 0;
}
static int adxl367_set_range(struct iio_dev *indio_dev,
enum adxl367_range range)
{
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL,
ADXL367_FILTER_CTL_RANGE_MASK,
FIELD_PREP(ADXL367_FILTER_CTL_RANGE_MASK,
range));
if (ret)
return ret;
adxl367_scale_act_thresholds(st, st->range, range);
/* Activity thresholds depend on range */
ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY,
st->act_threshold);
if (ret)
return ret;
ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY,
st->inact_threshold);
if (ret)
return ret;
ret = adxl367_set_measure_en(st, true);
if (ret)
return ret;
st->range = range;
return 0;
}
unreachable();
}
static int adxl367_time_ms_to_samples(struct adxl367_state *st, unsigned int ms)
{
int freq_hz = adxl367_samp_freq_tbl[st->odr][0];
int freq_microhz = adxl367_samp_freq_tbl[st->odr][1];
/* Scale to decihertz to prevent precision loss in 12.5Hz case. */
int freq_dhz = freq_hz * 10 + freq_microhz / 100000;
return DIV_ROUND_CLOSEST(ms * freq_dhz, 10000);
}
static int _adxl367_set_act_time_ms(struct adxl367_state *st, unsigned int ms)
{
unsigned int val = adxl367_time_ms_to_samples(st, ms);
int ret;
if (val > ADXL367_TIME_ACT_MAX)
val = ADXL367_TIME_ACT_MAX;
ret = regmap_write(st->regmap, ADXL367_REG_TIME_ACT, val);
if (ret)
return ret;
st->act_time_ms = ms;
return 0;
}
static int _adxl367_set_inact_time_ms(struct adxl367_state *st, unsigned int ms)
{
unsigned int val = adxl367_time_ms_to_samples(st, ms);
int ret;
if (val > ADXL367_TIME_INACT_MAX)
val = ADXL367_TIME_INACT_MAX;
st->inact_time_buf[0] = FIELD_PREP(ADXL367_TIME_INACT_H_MASK,
FIELD_GET(ADXL367_TIME_INACT_VAL_H_MASK,
val));
st->inact_time_buf[1] = FIELD_PREP(ADXL367_TIME_INACT_L_MASK,
FIELD_GET(ADXL367_TIME_INACT_VAL_L_MASK,
val));
ret = regmap_bulk_write(st->regmap, ADXL367_REG_TIME_INACT_H,
st->inact_time_buf, sizeof(st->inact_time_buf));
if (ret)
return ret;
st->inact_time_ms = ms;
return 0;
}
static int adxl367_set_act_time_ms(struct adxl367_state *st,
enum adxl367_activity_type act,
unsigned int ms)
{
int ret;
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
if (act == ADXL367_ACTIVITY)
ret = _adxl367_set_act_time_ms(st, ms);
else
ret = _adxl367_set_inact_time_ms(st, ms);
if (ret)
return ret;
return adxl367_set_measure_en(st, true);
}
static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr)
{
int ret;
ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL,
ADXL367_FILTER_CTL_ODR_MASK,
FIELD_PREP(ADXL367_FILTER_CTL_ODR_MASK,
odr));
if (ret)
return ret;
/* Activity timers depend on ODR */
ret = _adxl367_set_act_time_ms(st, st->act_time_ms);
if (ret)
return ret;
ret = _adxl367_set_inact_time_ms(st, st->inact_time_ms);
if (ret)
return ret;
st->odr = odr;
return 0;
}
static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr)
{
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = _adxl367_set_odr(st, odr);
if (ret)
return ret;
return adxl367_set_measure_en(st, true);
}
unreachable();
}
static int adxl367_set_temp_adc_en(struct adxl367_state *st, unsigned int reg,
bool en)
{
return regmap_update_bits(st->regmap, reg, ADXL367_ADC_EN_MASK,
en ? ADXL367_ADC_EN_MASK : 0);
}
static int adxl367_set_temp_adc_reg_en(struct adxl367_state *st,
unsigned int reg, bool en)
{
int ret;
switch (reg) {
case ADXL367_REG_TEMP_DATA_H:
ret = adxl367_set_temp_adc_en(st, ADXL367_REG_TEMP_CTL, en);
break;
case ADXL367_REG_EX_ADC_DATA_H:
ret = adxl367_set_temp_adc_en(st, ADXL367_REG_ADC_CTL, en);
break;
default:
return 0;
}
if (ret)
return ret;
if (en)
msleep(100);
return 0;
}
static int adxl367_set_temp_adc_mask_en(struct adxl367_state *st,
const unsigned long *active_scan_mask,
bool en)
{
if (*active_scan_mask & ADXL367_TEMP_CHANNEL_MASK)
return adxl367_set_temp_adc_en(st, ADXL367_REG_TEMP_CTL, en);
else if (*active_scan_mask & ADXL367_EX_ADC_CHANNEL_MASK)
return adxl367_set_temp_adc_en(st, ADXL367_REG_ADC_CTL, en);
return 0;
}
static int adxl367_find_odr(struct adxl367_state *st, int val, int val2,
enum adxl367_odr *odr)
{
size_t size = ARRAY_SIZE(adxl367_samp_freq_tbl);
int i;
for (i = 0; i < size; i++)
if (val == adxl367_samp_freq_tbl[i][0] &&
val2 == adxl367_samp_freq_tbl[i][1])
break;
if (i == size)
return -EINVAL;
*odr = i;
return 0;
}
static int adxl367_find_range(struct adxl367_state *st, int val, int val2,
enum adxl367_range *range)
{
size_t size = ARRAY_SIZE(adxl367_range_scale_tbl);
int i;
for (i = 0; i < size; i++)
if (val == adxl367_range_scale_tbl[i][0] &&
val2 == adxl367_range_scale_tbl[i][1])
break;
if (i == size)
return -EINVAL;
*range = i;
return 0;
}
static int adxl367_read_sample(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val)
{
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
struct adxl367_state *st = iio_priv(indio_dev);
u16 sample;
int ret;
guard(mutex)(&st->lock);
ret = adxl367_set_temp_adc_reg_en(st, chan->address, true);
if (ret)
return ret;
ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf,
sizeof(st->sample_buf));
if (ret)
return ret;
sample = FIELD_GET(ADXL367_DATA_MASK, be16_to_cpu(st->sample_buf));
*val = sign_extend32(sample, chan->scan_type.realbits - 1);
ret = adxl367_set_temp_adc_reg_en(st, chan->address, false);
if (ret)
return ret;
return IIO_VAL_INT;
}
unreachable();
}
static int adxl367_get_status(struct adxl367_state *st, u8 *status,
u16 *fifo_entries)
{
int ret;
/* Read STATUS, FIFO_ENT_L and FIFO_ENT_H */
ret = regmap_bulk_read(st->regmap, ADXL367_REG_STATUS,
st->status_buf, sizeof(st->status_buf));
if (ret)
return ret;
st->status_buf[2] &= ADXL367_FIFO_ENT_H_MASK;
*status = st->status_buf[0];
*fifo_entries = get_unaligned_le16(&st->status_buf[1]);
return 0;
}
static bool adxl367_push_event(struct iio_dev *indio_dev, u8 status)
{
unsigned int ev_dir;
if (FIELD_GET(ADXL367_STATUS_ACT_MASK, status))
ev_dir = IIO_EV_DIR_RISING;
else if (FIELD_GET(ADXL367_STATUS_INACT_MASK, status))
ev_dir = IIO_EV_DIR_FALLING;
else
return false;
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z,
IIO_EV_TYPE_THRESH, ev_dir),
iio_get_time_ns(indio_dev));
return true;
}
static bool adxl367_push_fifo_data(struct iio_dev *indio_dev, u8 status,
u16 fifo_entries)
{
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
int i;
if (!FIELD_GET(ADXL367_STATUS_FIFO_FULL_MASK, status))
return false;
fifo_entries -= fifo_entries % st->fifo_set_size;
ret = st->ops->read_fifo(st->context, st->fifo_buf, fifo_entries);
if (ret) {
dev_err(st->dev, "Failed to read FIFO: %d\n", ret);
return true;
}
for (i = 0; i < fifo_entries; i += st->fifo_set_size)
iio_push_to_buffers(indio_dev, &st->fifo_buf[i]);
return true;
}
static irqreturn_t adxl367_irq_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct adxl367_state *st = iio_priv(indio_dev);
u16 fifo_entries;
bool handled;
u8 status;
int ret;
ret = adxl367_get_status(st, &status, &fifo_entries);
if (ret)
return IRQ_NONE;
handled = adxl367_push_event(indio_dev, status);
handled |= adxl367_push_fifo_data(indio_dev, status, fifo_entries);
return handled ? IRQ_HANDLED : IRQ_NONE;
}
static int adxl367_reg_access(struct iio_dev *indio_dev,
unsigned int reg,
unsigned int writeval,
unsigned int *readval)
{
struct adxl367_state *st = iio_priv(indio_dev);
if (readval)
return regmap_read(st->regmap, reg, readval);
else
return regmap_write(st->regmap, reg, writeval);
}
static int adxl367_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long info)
{
struct adxl367_state *st = iio_priv(indio_dev);
switch (info) {
case IIO_CHAN_INFO_RAW:
return adxl367_read_sample(indio_dev, chan, val);
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_ACCEL: {
guard(mutex)(&st->lock);
*val = adxl367_range_scale_tbl[st->range][0];
*val2 = adxl367_range_scale_tbl[st->range][1];
return IIO_VAL_INT_PLUS_NANO;
}
case IIO_TEMP:
*val = 1000;
*val2 = ADXL367_TEMP_PER_C;
return IIO_VAL_FRACTIONAL;
case IIO_VOLTAGE:
*val = ADXL367_VOLTAGE_MAX_MV;
*val2 = ADXL367_VOLTAGE_MAX_RAW;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_OFFSET:
switch (chan->type) {
case IIO_TEMP:
*val = 25 * ADXL367_TEMP_PER_C - ADXL367_TEMP_25C;
return IIO_VAL_INT;
case IIO_VOLTAGE:
*val = ADXL367_VOLTAGE_OFFSET;
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SAMP_FREQ: {
guard(mutex)(&st->lock);
*val = adxl367_samp_freq_tbl[st->odr][0];
*val2 = adxl367_samp_freq_tbl[st->odr][1];
return IIO_VAL_INT_PLUS_MICRO;
}
default:
return -EINVAL;
}
}
static int adxl367_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long info)
{
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ: {
enum adxl367_odr odr;
ret = adxl367_find_odr(st, val, val2, &odr);
if (ret)
return ret;
return adxl367_set_odr(indio_dev, odr);
}
case IIO_CHAN_INFO_SCALE: {
enum adxl367_range range;
ret = adxl367_find_range(st, val, val2, &range);
if (ret)
return ret;
return adxl367_set_range(indio_dev, range);
}
default:
return -EINVAL;
}
}
static int adxl367_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long info)
{
switch (info) {
case IIO_CHAN_INFO_SCALE:
if (chan->type != IIO_ACCEL)
return -EINVAL;
return IIO_VAL_INT_PLUS_NANO;
default:
return IIO_VAL_INT_PLUS_MICRO;
}
}
static int adxl367_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long info)
{
switch (info) {
case IIO_CHAN_INFO_SCALE:
if (chan->type != IIO_ACCEL)
return -EINVAL;
*vals = (int *)adxl367_range_scale_tbl;
*type = IIO_VAL_INT_PLUS_NANO;
*length = ARRAY_SIZE(adxl367_range_scale_tbl) * 2;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SAMP_FREQ:
*vals = (int *)adxl367_samp_freq_tbl;
*type = IIO_VAL_INT_PLUS_MICRO;
*length = ARRAY_SIZE(adxl367_samp_freq_tbl) * 2;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static int adxl367_read_event_value(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int *val, int *val2)
{
struct adxl367_state *st = iio_priv(indio_dev);
guard(mutex)(&st->lock);
switch (info) {
case IIO_EV_INFO_VALUE: {
switch (dir) {
case IIO_EV_DIR_RISING:
*val = st->act_threshold;
return IIO_VAL_INT;
case IIO_EV_DIR_FALLING:
*val = st->inact_threshold;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
case IIO_EV_INFO_PERIOD:
switch (dir) {
case IIO_EV_DIR_RISING:
*val = st->act_time_ms;
*val2 = 1000;
return IIO_VAL_FRACTIONAL;
case IIO_EV_DIR_FALLING:
*val = st->inact_time_ms;
*val2 = 1000;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static int adxl367_write_event_value(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int val, int val2)
{
struct adxl367_state *st = iio_priv(indio_dev);
switch (info) {
case IIO_EV_INFO_VALUE:
if (val < 0)
return -EINVAL;
switch (dir) {
case IIO_EV_DIR_RISING:
return adxl367_set_act_threshold(st, ADXL367_ACTIVITY, val);
case IIO_EV_DIR_FALLING:
return adxl367_set_act_threshold(st, ADXL367_INACTIVITY, val);
default:
return -EINVAL;
}
case IIO_EV_INFO_PERIOD:
if (val < 0)
return -EINVAL;
val = val * 1000 + DIV_ROUND_UP(val2, 1000);
switch (dir) {
case IIO_EV_DIR_RISING:
return adxl367_set_act_time_ms(st, ADXL367_ACTIVITY, val);
case IIO_EV_DIR_FALLING:
return adxl367_set_act_time_ms(st, ADXL367_INACTIVITY, val);
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static int adxl367_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir)
{
struct adxl367_state *st = iio_priv(indio_dev);
bool en;
int ret;
switch (dir) {
case IIO_EV_DIR_RISING:
ret = adxl367_get_act_interrupt_en(st, ADXL367_ACTIVITY, &en);
return ret ?: en;
case IIO_EV_DIR_FALLING:
ret = adxl367_get_act_interrupt_en(st, ADXL367_INACTIVITY, &en);
return ret ?: en;
default:
return -EINVAL;
}
}
static int adxl367_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
int state)
{
enum adxl367_activity_type act;
switch (dir) {
case IIO_EV_DIR_RISING:
act = ADXL367_ACTIVITY;
break;
case IIO_EV_DIR_FALLING:
act = ADXL367_INACTIVITY;
break;
default:
return -EINVAL;
}
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = adxl367_set_act_interrupt_en(st, act, state);
if (ret)
return ret;
ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED
: ADXL367_ACT_DISABLED);
if (ret)
return ret;
return adxl367_set_measure_en(st, true);
}
unreachable();
}
static ssize_t adxl367_get_fifo_enabled(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct adxl367_state *st = iio_priv(dev_to_iio_dev(dev));
enum adxl367_fifo_mode fifo_mode;
int ret;
ret = adxl367_get_fifo_mode(st, &fifo_mode);
if (ret)
return ret;
return sysfs_emit(buf, "%d\n", fifo_mode != ADXL367_FIFO_MODE_DISABLED);
}
static ssize_t adxl367_get_fifo_watermark(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct adxl367_state *st = iio_priv(dev_to_iio_dev(dev));
unsigned int fifo_watermark;
guard(mutex)(&st->lock);
fifo_watermark = st->fifo_watermark;
return sysfs_emit(buf, "%d\n", fifo_watermark);
}
IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_min, "1");
IIO_STATIC_CONST_DEVICE_ATTR(hwfifo_watermark_max,
__stringify(ADXL367_FIFO_MAX_WATERMARK));
static IIO_DEVICE_ATTR(hwfifo_watermark, 0444,
adxl367_get_fifo_watermark, NULL, 0);
static IIO_DEVICE_ATTR(hwfifo_enabled, 0444,
adxl367_get_fifo_enabled, NULL, 0);
static const struct iio_dev_attr *adxl367_fifo_attributes[] = {
&iio_dev_attr_hwfifo_watermark_min,
&iio_dev_attr_hwfifo_watermark_max,
&iio_dev_attr_hwfifo_watermark,
&iio_dev_attr_hwfifo_enabled,
NULL,
};
static int adxl367_set_watermark(struct iio_dev *indio_dev, unsigned int val)
{
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
if (val > ADXL367_FIFO_MAX_WATERMARK)
return -EINVAL;
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = adxl367_set_fifo_watermark(st, val);
if (ret)
return ret;
return adxl367_set_measure_en(st, true);
}
static bool adxl367_find_mask_fifo_format(const unsigned long *scan_mask,
enum adxl367_fifo_format *fifo_format)
{
size_t size = ARRAY_SIZE(adxl367_fifo_formats);
int i;
for (i = 0; i < size; i++)
if (*scan_mask == adxl367_channel_masks[i])
break;
if (i == size)
return false;
*fifo_format = adxl367_fifo_formats[i];
return true;
}
static int adxl367_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *active_scan_mask)
{
struct adxl367_state *st = iio_priv(indio_dev);
enum adxl367_fifo_format fifo_format;
int ret;
if (!adxl367_find_mask_fifo_format(active_scan_mask, &fifo_format))
return -EINVAL;
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = adxl367_set_fifo_format(st, fifo_format);
if (ret)
return ret;
ret = adxl367_set_measure_en(st, true);
if (ret)
return ret;
st->fifo_set_size = bitmap_weight(active_scan_mask,
iio_get_masklength(indio_dev));
return 0;
}
static int adxl367_buffer_postenable(struct iio_dev *indio_dev)
{
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
guard(mutex)(&st->lock);
ret = adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask,
true);
if (ret)
return ret;
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = adxl367_set_fifo_watermark_interrupt_en(st, true);
if (ret)
return ret;
ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_STREAM);
if (ret)
return ret;
return adxl367_set_measure_en(st, true);
}
static int adxl367_buffer_predisable(struct iio_dev *indio_dev)
{
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_DISABLED);
if (ret)
return ret;
ret = adxl367_set_fifo_watermark_interrupt_en(st, false);
if (ret)
return ret;
ret = adxl367_set_measure_en(st, true);
if (ret)
return ret;
return adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask,
false);
}
static const struct iio_buffer_setup_ops adxl367_buffer_ops = {
.postenable = adxl367_buffer_postenable,
.predisable = adxl367_buffer_predisable,
};
static const struct iio_info adxl367_info = {
.read_raw = adxl367_read_raw,
.write_raw = adxl367_write_raw,
.write_raw_get_fmt = adxl367_write_raw_get_fmt,
.read_avail = adxl367_read_avail,
.read_event_config = adxl367_read_event_config,
.write_event_config = adxl367_write_event_config,
.read_event_value = adxl367_read_event_value,
.write_event_value = adxl367_write_event_value,
.debugfs_reg_access = adxl367_reg_access,
.hwfifo_set_watermark = adxl367_set_watermark,
.update_scan_mode = adxl367_update_scan_mode,
};
static const struct iio_event_spec adxl367_events[] = {
{
.type = IIO_EV_TYPE_MAG_REFERENCED,
.dir = IIO_EV_DIR_RISING,
.mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) |
BIT(IIO_EV_INFO_PERIOD) |
BIT(IIO_EV_INFO_VALUE),
},
{
.type = IIO_EV_TYPE_MAG_REFERENCED,
.dir = IIO_EV_DIR_FALLING,
.mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) |
BIT(IIO_EV_INFO_PERIOD) |
BIT(IIO_EV_INFO_VALUE),
},
};
#define ADXL367_ACCEL_CHANNEL(index, reg, axis) { \
.type = IIO_ACCEL, \
.address = (reg), \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_all_available = \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.event_spec = adxl367_events, \
.num_event_specs = ARRAY_SIZE(adxl367_events), \
.scan_index = (index), \
.scan_type = { \
.sign = 's', \
.realbits = 14, \
.storagebits = 16, \
.endianness = IIO_BE, \
}, \
}
#define ADXL367_CHANNEL(index, reg, _type) { \
.type = (_type), \
.address = (reg), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_OFFSET) | \
BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_index = (index), \
.scan_type = { \
.sign = 's', \
.realbits = 14, \
.storagebits = 16, \
.endianness = IIO_BE, \
}, \
}
static const struct iio_chan_spec adxl367_channels[] = {
ADXL367_ACCEL_CHANNEL(ADXL367_X_CHANNEL_INDEX, ADXL367_REG_X_DATA_H, X),
ADXL367_ACCEL_CHANNEL(ADXL367_Y_CHANNEL_INDEX, ADXL367_REG_Y_DATA_H, Y),
ADXL367_ACCEL_CHANNEL(ADXL367_Z_CHANNEL_INDEX, ADXL367_REG_Z_DATA_H, Z),
ADXL367_CHANNEL(ADXL367_TEMP_CHANNEL_INDEX, ADXL367_REG_TEMP_DATA_H,
IIO_TEMP),
ADXL367_CHANNEL(ADXL367_EX_ADC_CHANNEL_INDEX, ADXL367_REG_EX_ADC_DATA_H,
IIO_VOLTAGE),
};
static int adxl367_verify_devid(struct adxl367_state *st)
{
unsigned int val;
int ret;
ret = regmap_read(st->regmap, ADXL367_REG_DEVID, &val);
if (ret)
return dev_err_probe(st->dev, ret, "Failed to read dev id\n");
if (val != ADXL367_DEVID_AD)
return dev_err_probe(st->dev, -ENODEV,
"Invalid dev id 0x%02X, expected 0x%02X\n",
val, ADXL367_DEVID_AD);
return 0;
}
static int adxl367_setup(struct adxl367_state *st)
{
int ret;
ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY,
ADXL367_2G_RANGE_1G);
if (ret)
return ret;
ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY,
ADXL367_2G_RANGE_100MG);
if (ret)
return ret;
ret = adxl367_set_act_proc_mode(st, ADXL367_LOOPED);
if (ret)
return ret;
ret = _adxl367_set_odr(st, ADXL367_ODR_400HZ);
if (ret)
return ret;
ret = _adxl367_set_act_time_ms(st, 10);
if (ret)
return ret;
ret = _adxl367_set_inact_time_ms(st, 10000);
if (ret)
return ret;
return adxl367_set_measure_en(st, true);
}
int adxl367_probe(struct device *dev, const struct adxl367_ops *ops,
void *context, struct regmap *regmap, int irq)
{
static const char * const regulator_names[] = { "vdd", "vddio" };
struct iio_dev *indio_dev;
struct adxl367_state *st;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
st->dev = dev;
st->regmap = regmap;
st->context = context;
st->ops = ops;
mutex_init(&st->lock);
indio_dev->channels = adxl367_channels;
indio_dev->num_channels = ARRAY_SIZE(adxl367_channels);
indio_dev->available_scan_masks = adxl367_channel_masks;
indio_dev->name = "adxl367";
indio_dev->info = &adxl367_info;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = devm_regulator_bulk_get_enable(st->dev,
ARRAY_SIZE(regulator_names),
regulator_names);
if (ret)
return dev_err_probe(st->dev, ret,
"Failed to get regulators\n");
ret = regmap_write(st->regmap, ADXL367_REG_RESET, ADXL367_RESET_CODE);
if (ret)
return ret;
fsleep(15000);
ret = adxl367_verify_devid(st);
if (ret)
return ret;
ret = adxl367_setup(st);
if (ret)
return ret;
ret = devm_iio_kfifo_buffer_setup_ext(st->dev, indio_dev,
&adxl367_buffer_ops,
adxl367_fifo_attributes);
if (ret)
return ret;
ret = devm_request_threaded_irq(st->dev, irq, NULL,
adxl367_irq_handler, IRQF_ONESHOT,
indio_dev->name, indio_dev);
if (ret)
return dev_err_probe(st->dev, ret, "Failed to request irq\n");
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS_GPL(adxl367_probe, IIO_ADXL367);
MODULE_AUTHOR("Cosmin Tanislav <cosmin.tanislav@analog.com>");
MODULE_DESCRIPTION("Analog Devices ADXL367 3-axis accelerometer driver");
MODULE_LICENSE("GPL");