mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 06:02:05 +00:00
0a33755c4b
The iio_triggered_buffer_setup_ext() and the
devm_iio_kfifo_buffer_setup_ext() were changed by
commit 15097c7a1a
("iio: buffer: wrap all buffer attributes into iio_dev_attr")
to silently expect that all attributes given in buffer_attrs array are
device-attributes. This expectation was not forced by the API - and some
drivers did register attributes created by IIO_CONST_ATTR().
When using IIO_CONST_ATTRs the added attribute "wrapping" does not copy
the pointer to stored string constant and when the sysfs file is read the
kernel will access to invalid location.
Change the function signatures to expect an array of iio_dev_attrs to
avoid similar errors in the future.
Merge conflict resolved whilst applying due to patch crossing with
two new drivers (kx022a accelerometer and ad4130 ADC).
Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Tested-by: Claudiu Beznea <claudiu.beznea@microchip.com>
Link: https://lore.kernel.org/r/63f54787a684eb1232f1c5d275a09c786987fe4a.1664782676.git.mazziesaccount@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
1540 lines
36 KiB
C
1540 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 <asm/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;
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
ret = adxl367_set_measure_en(st, false);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = _adxl367_set_act_threshold(st, act, threshold);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_measure_en(st, true);
|
|
|
|
out:
|
|
mutex_unlock(&st->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct adxl367_state *st = iio_priv(indio_dev);
|
|
int ret;
|
|
|
|
ret = iio_device_claim_direct_mode(indio_dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
ret = adxl367_set_measure_en(st, false);
|
|
if (ret)
|
|
goto out;
|
|
|
|
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)
|
|
goto out;
|
|
|
|
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)
|
|
goto out;
|
|
|
|
ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY,
|
|
st->inact_threshold);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_measure_en(st, true);
|
|
if (ret)
|
|
goto out;
|
|
|
|
st->range = range;
|
|
|
|
out:
|
|
mutex_unlock(&st->lock);
|
|
|
|
iio_device_release_direct_mode(indio_dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
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;
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
ret = adxl367_set_measure_en(st, false);
|
|
if (ret)
|
|
goto out;
|
|
|
|
if (act == ADXL367_ACTIVITY)
|
|
ret = _adxl367_set_act_time_ms(st, ms);
|
|
else
|
|
ret = _adxl367_set_inact_time_ms(st, ms);
|
|
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_measure_en(st, true);
|
|
|
|
out:
|
|
mutex_unlock(&st->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct adxl367_state *st = iio_priv(indio_dev);
|
|
int ret;
|
|
|
|
ret = iio_device_claim_direct_mode(indio_dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
ret = adxl367_set_measure_en(st, false);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = _adxl367_set_odr(st, odr);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_measure_en(st, true);
|
|
|
|
out:
|
|
mutex_unlock(&st->lock);
|
|
|
|
iio_device_release_direct_mode(indio_dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct adxl367_state *st = iio_priv(indio_dev);
|
|
u16 sample;
|
|
int ret;
|
|
|
|
ret = iio_device_claim_direct_mode(indio_dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
ret = adxl367_set_temp_adc_reg_en(st, chan->address, true);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf,
|
|
sizeof(st->sample_buf));
|
|
if (ret)
|
|
goto out;
|
|
|
|
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);
|
|
|
|
out:
|
|
mutex_unlock(&st->lock);
|
|
|
|
iio_device_release_direct_mode(indio_dev);
|
|
|
|
return ret ?: IIO_VAL_INT;
|
|
}
|
|
|
|
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:
|
|
mutex_lock(&st->lock);
|
|
*val = adxl367_range_scale_tbl[st->range][0];
|
|
*val2 = adxl367_range_scale_tbl[st->range][1];
|
|
mutex_unlock(&st->lock);
|
|
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:
|
|
mutex_lock(&st->lock);
|
|
*val = adxl367_samp_freq_tbl[st->odr][0];
|
|
*val2 = adxl367_samp_freq_tbl[st->odr][1];
|
|
mutex_unlock(&st->lock);
|
|
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);
|
|
|
|
switch (info) {
|
|
case IIO_EV_INFO_VALUE: {
|
|
switch (dir) {
|
|
case IIO_EV_DIR_RISING:
|
|
mutex_lock(&st->lock);
|
|
*val = st->act_threshold;
|
|
mutex_unlock(&st->lock);
|
|
return IIO_VAL_INT;
|
|
case IIO_EV_DIR_FALLING:
|
|
mutex_lock(&st->lock);
|
|
*val = st->inact_threshold;
|
|
mutex_unlock(&st->lock);
|
|
return IIO_VAL_INT;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
case IIO_EV_INFO_PERIOD:
|
|
switch (dir) {
|
|
case IIO_EV_DIR_RISING:
|
|
mutex_lock(&st->lock);
|
|
*val = st->act_time_ms;
|
|
mutex_unlock(&st->lock);
|
|
*val2 = 1000;
|
|
return IIO_VAL_FRACTIONAL;
|
|
case IIO_EV_DIR_FALLING:
|
|
mutex_lock(&st->lock);
|
|
*val = st->inact_time_ms;
|
|
mutex_unlock(&st->lock);
|
|
*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)
|
|
{
|
|
struct adxl367_state *st = iio_priv(indio_dev);
|
|
enum adxl367_activity_type act;
|
|
int ret;
|
|
|
|
switch (dir) {
|
|
case IIO_EV_DIR_RISING:
|
|
act = ADXL367_ACTIVITY;
|
|
break;
|
|
case IIO_EV_DIR_FALLING:
|
|
act = ADXL367_INACTIVITY;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = iio_device_claim_direct_mode(indio_dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
ret = adxl367_set_measure_en(st, false);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_act_interrupt_en(st, act, state);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED
|
|
: ADXL367_ACT_DISABLED);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_measure_en(st, true);
|
|
|
|
out:
|
|
mutex_unlock(&st->lock);
|
|
|
|
iio_device_release_direct_mode(indio_dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
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;
|
|
|
|
mutex_lock(&st->lock);
|
|
fifo_watermark = st->fifo_watermark;
|
|
mutex_unlock(&st->lock);
|
|
|
|
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;
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
ret = adxl367_set_measure_en(st, false);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_fifo_watermark(st, val);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_measure_en(st, true);
|
|
|
|
out:
|
|
mutex_unlock(&st->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
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;
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
ret = adxl367_set_measure_en(st, false);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_fifo_format(st, fifo_format);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_measure_en(st, true);
|
|
if (ret)
|
|
goto out;
|
|
|
|
st->fifo_set_size = bitmap_weight(active_scan_mask,
|
|
indio_dev->masklength);
|
|
|
|
out:
|
|
mutex_unlock(&st->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int adxl367_buffer_postenable(struct iio_dev *indio_dev)
|
|
{
|
|
struct adxl367_state *st = iio_priv(indio_dev);
|
|
int ret;
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
ret = adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask,
|
|
true);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_measure_en(st, false);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_fifo_watermark_interrupt_en(st, true);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_STREAM);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_measure_en(st, true);
|
|
|
|
out:
|
|
mutex_unlock(&st->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int adxl367_buffer_predisable(struct iio_dev *indio_dev)
|
|
{
|
|
struct adxl367_state *st = iio_priv(indio_dev);
|
|
int ret;
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
ret = adxl367_set_measure_en(st, false);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_DISABLED);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_fifo_watermark_interrupt_en(st, false);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_measure_en(st, true);
|
|
if (ret)
|
|
goto out;
|
|
|
|
ret = adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask,
|
|
false);
|
|
|
|
out:
|
|
mutex_unlock(&st->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
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_poll_timeout(st->regmap, ADXL367_REG_DEVID, val,
|
|
val == ADXL367_DEVID_AD, 1000, 10000);
|
|
if (ret)
|
|
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;
|
|
|
|
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");
|