1483 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			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");
 |