14097da40SStefan Popa // SPDX-License-Identifier: GPL-2.0+ 24097da40SStefan Popa /* 3d9e8fd04SStefan Popa * ADXL372 3-Axis Digital Accelerometer core driver 44097da40SStefan Popa * 54097da40SStefan Popa * Copyright 2018 Analog Devices Inc. 64097da40SStefan Popa */ 74097da40SStefan Popa 84097da40SStefan Popa #include <linux/bitops.h> 9f4f55ce3SStefan Popa #include <linux/interrupt.h> 10f4f55ce3SStefan Popa #include <linux/irq.h> 114097da40SStefan Popa #include <linux/module.h> 124097da40SStefan Popa #include <linux/regmap.h> 134097da40SStefan Popa #include <linux/spi/spi.h> 144097da40SStefan Popa 154097da40SStefan Popa #include <linux/iio/iio.h> 164097da40SStefan Popa #include <linux/iio/sysfs.h> 17f4f55ce3SStefan Popa #include <linux/iio/buffer.h> 18f4f55ce3SStefan Popa #include <linux/iio/events.h> 19f4f55ce3SStefan Popa #include <linux/iio/trigger.h> 20f4f55ce3SStefan Popa #include <linux/iio/trigger_consumer.h> 21f4f55ce3SStefan Popa #include <linux/iio/triggered_buffer.h> 224097da40SStefan Popa 23d9e8fd04SStefan Popa #include "adxl372.h" 24d9e8fd04SStefan Popa 254097da40SStefan Popa /* ADXL372 registers definition */ 264097da40SStefan Popa #define ADXL372_DEVID 0x00 274097da40SStefan Popa #define ADXL372_DEVID_MST 0x01 284097da40SStefan Popa #define ADXL372_PARTID 0x02 294097da40SStefan Popa #define ADXL372_REVID 0x03 304097da40SStefan Popa #define ADXL372_STATUS_1 0x04 314097da40SStefan Popa #define ADXL372_STATUS_2 0x05 324097da40SStefan Popa #define ADXL372_FIFO_ENTRIES_2 0x06 334097da40SStefan Popa #define ADXL372_FIFO_ENTRIES_1 0x07 344097da40SStefan Popa #define ADXL372_X_DATA_H 0x08 354097da40SStefan Popa #define ADXL372_X_DATA_L 0x09 364097da40SStefan Popa #define ADXL372_Y_DATA_H 0x0A 374097da40SStefan Popa #define ADXL372_Y_DATA_L 0x0B 384097da40SStefan Popa #define ADXL372_Z_DATA_H 0x0C 394097da40SStefan Popa #define ADXL372_Z_DATA_L 0x0D 404097da40SStefan Popa #define ADXL372_X_MAXPEAK_H 0x15 414097da40SStefan Popa #define ADXL372_X_MAXPEAK_L 0x16 424097da40SStefan Popa #define ADXL372_Y_MAXPEAK_H 0x17 434097da40SStefan Popa #define ADXL372_Y_MAXPEAK_L 0x18 444097da40SStefan Popa #define ADXL372_Z_MAXPEAK_H 0x19 454097da40SStefan Popa #define ADXL372_Z_MAXPEAK_L 0x1A 464097da40SStefan Popa #define ADXL372_OFFSET_X 0x20 474097da40SStefan Popa #define ADXL372_OFFSET_Y 0x21 484097da40SStefan Popa #define ADXL372_OFFSET_Z 0x22 494097da40SStefan Popa #define ADXL372_X_THRESH_ACT_H 0x23 504097da40SStefan Popa #define ADXL372_X_THRESH_ACT_L 0x24 514097da40SStefan Popa #define ADXL372_Y_THRESH_ACT_H 0x25 524097da40SStefan Popa #define ADXL372_Y_THRESH_ACT_L 0x26 534097da40SStefan Popa #define ADXL372_Z_THRESH_ACT_H 0x27 544097da40SStefan Popa #define ADXL372_Z_THRESH_ACT_L 0x28 554097da40SStefan Popa #define ADXL372_TIME_ACT 0x29 564097da40SStefan Popa #define ADXL372_X_THRESH_INACT_H 0x2A 574097da40SStefan Popa #define ADXL372_X_THRESH_INACT_L 0x2B 584097da40SStefan Popa #define ADXL372_Y_THRESH_INACT_H 0x2C 594097da40SStefan Popa #define ADXL372_Y_THRESH_INACT_L 0x2D 604097da40SStefan Popa #define ADXL372_Z_THRESH_INACT_H 0x2E 614097da40SStefan Popa #define ADXL372_Z_THRESH_INACT_L 0x2F 624097da40SStefan Popa #define ADXL372_TIME_INACT_H 0x30 634097da40SStefan Popa #define ADXL372_TIME_INACT_L 0x31 644097da40SStefan Popa #define ADXL372_X_THRESH_ACT2_H 0x32 654097da40SStefan Popa #define ADXL372_X_THRESH_ACT2_L 0x33 664097da40SStefan Popa #define ADXL372_Y_THRESH_ACT2_H 0x34 674097da40SStefan Popa #define ADXL372_Y_THRESH_ACT2_L 0x35 684097da40SStefan Popa #define ADXL372_Z_THRESH_ACT2_H 0x36 694097da40SStefan Popa #define ADXL372_Z_THRESH_ACT2_L 0x37 704097da40SStefan Popa #define ADXL372_HPF 0x38 714097da40SStefan Popa #define ADXL372_FIFO_SAMPLES 0x39 724097da40SStefan Popa #define ADXL372_FIFO_CTL 0x3A 734097da40SStefan Popa #define ADXL372_INT1_MAP 0x3B 744097da40SStefan Popa #define ADXL372_INT2_MAP 0x3C 754097da40SStefan Popa #define ADXL372_TIMING 0x3D 764097da40SStefan Popa #define ADXL372_MEASURE 0x3E 774097da40SStefan Popa #define ADXL372_POWER_CTL 0x3F 784097da40SStefan Popa #define ADXL372_SELF_TEST 0x40 794097da40SStefan Popa #define ADXL372_RESET 0x41 804097da40SStefan Popa #define ADXL372_FIFO_DATA 0x42 814097da40SStefan Popa 824097da40SStefan Popa #define ADXL372_DEVID_VAL 0xAD 834097da40SStefan Popa #define ADXL372_PARTID_VAL 0xFA 844097da40SStefan Popa #define ADXL372_RESET_CODE 0x52 854097da40SStefan Popa 864097da40SStefan Popa /* ADXL372_POWER_CTL */ 874097da40SStefan Popa #define ADXL372_POWER_CTL_MODE_MSK GENMASK_ULL(1, 0) 884097da40SStefan Popa #define ADXL372_POWER_CTL_MODE(x) (((x) & 0x3) << 0) 894097da40SStefan Popa 904097da40SStefan Popa /* ADXL372_MEASURE */ 914097da40SStefan Popa #define ADXL372_MEASURE_LINKLOOP_MSK GENMASK_ULL(5, 4) 924097da40SStefan Popa #define ADXL372_MEASURE_LINKLOOP_MODE(x) (((x) & 0x3) << 4) 934097da40SStefan Popa #define ADXL372_MEASURE_BANDWIDTH_MSK GENMASK_ULL(2, 0) 944097da40SStefan Popa #define ADXL372_MEASURE_BANDWIDTH_MODE(x) (((x) & 0x7) << 0) 954097da40SStefan Popa 964097da40SStefan Popa /* ADXL372_TIMING */ 974097da40SStefan Popa #define ADXL372_TIMING_ODR_MSK GENMASK_ULL(7, 5) 984097da40SStefan Popa #define ADXL372_TIMING_ODR_MODE(x) (((x) & 0x7) << 5) 994097da40SStefan Popa 1004097da40SStefan Popa /* ADXL372_FIFO_CTL */ 1014097da40SStefan Popa #define ADXL372_FIFO_CTL_FORMAT_MSK GENMASK(5, 3) 1024097da40SStefan Popa #define ADXL372_FIFO_CTL_FORMAT_MODE(x) (((x) & 0x7) << 3) 1034097da40SStefan Popa #define ADXL372_FIFO_CTL_MODE_MSK GENMASK(2, 1) 1044097da40SStefan Popa #define ADXL372_FIFO_CTL_MODE_MODE(x) (((x) & 0x3) << 1) 1054097da40SStefan Popa #define ADXL372_FIFO_CTL_SAMPLES_MSK BIT(1) 1064097da40SStefan Popa #define ADXL372_FIFO_CTL_SAMPLES_MODE(x) (((x) > 0xFF) ? 1 : 0) 1074097da40SStefan Popa 1084097da40SStefan Popa /* ADXL372_STATUS_1 */ 1094097da40SStefan Popa #define ADXL372_STATUS_1_DATA_RDY(x) (((x) >> 0) & 0x1) 1104097da40SStefan Popa #define ADXL372_STATUS_1_FIFO_RDY(x) (((x) >> 1) & 0x1) 1114097da40SStefan Popa #define ADXL372_STATUS_1_FIFO_FULL(x) (((x) >> 2) & 0x1) 1124097da40SStefan Popa #define ADXL372_STATUS_1_FIFO_OVR(x) (((x) >> 3) & 0x1) 1134097da40SStefan Popa #define ADXL372_STATUS_1_USR_NVM_BUSY(x) (((x) >> 5) & 0x1) 1144097da40SStefan Popa #define ADXL372_STATUS_1_AWAKE(x) (((x) >> 6) & 0x1) 1154097da40SStefan Popa #define ADXL372_STATUS_1_ERR_USR_REGS(x) (((x) >> 7) & 0x1) 1164097da40SStefan Popa 1174097da40SStefan Popa /* ADXL372_INT1_MAP */ 1184097da40SStefan Popa #define ADXL372_INT1_MAP_DATA_RDY_MSK BIT(0) 1194097da40SStefan Popa #define ADXL372_INT1_MAP_DATA_RDY_MODE(x) (((x) & 0x1) << 0) 1204097da40SStefan Popa #define ADXL372_INT1_MAP_FIFO_RDY_MSK BIT(1) 1214097da40SStefan Popa #define ADXL372_INT1_MAP_FIFO_RDY_MODE(x) (((x) & 0x1) << 1) 1224097da40SStefan Popa #define ADXL372_INT1_MAP_FIFO_FULL_MSK BIT(2) 1234097da40SStefan Popa #define ADXL372_INT1_MAP_FIFO_FULL_MODE(x) (((x) & 0x1) << 2) 1244097da40SStefan Popa #define ADXL372_INT1_MAP_FIFO_OVR_MSK BIT(3) 1254097da40SStefan Popa #define ADXL372_INT1_MAP_FIFO_OVR_MODE(x) (((x) & 0x1) << 3) 1264097da40SStefan Popa #define ADXL372_INT1_MAP_INACT_MSK BIT(4) 1274097da40SStefan Popa #define ADXL372_INT1_MAP_INACT_MODE(x) (((x) & 0x1) << 4) 1284097da40SStefan Popa #define ADXL372_INT1_MAP_ACT_MSK BIT(5) 1294097da40SStefan Popa #define ADXL372_INT1_MAP_ACT_MODE(x) (((x) & 0x1) << 5) 1304097da40SStefan Popa #define ADXL372_INT1_MAP_AWAKE_MSK BIT(6) 1314097da40SStefan Popa #define ADXL372_INT1_MAP_AWAKE_MODE(x) (((x) & 0x1) << 6) 1324097da40SStefan Popa #define ADXL372_INT1_MAP_LOW_MSK BIT(7) 1334097da40SStefan Popa #define ADXL372_INT1_MAP_LOW_MODE(x) (((x) & 0x1) << 7) 1344097da40SStefan Popa 135f4f55ce3SStefan Popa /* The ADXL372 includes a deep, 512 sample FIFO buffer */ 136f4f55ce3SStefan Popa #define ADXL372_FIFO_SIZE 512 137f4f55ce3SStefan Popa 1384097da40SStefan Popa /* 1394097da40SStefan Popa * At +/- 200g with 12-bit resolution, scale is computed as: 1404097da40SStefan Popa * (200 + 200) * 9.81 / (2^12 - 1) = 0.958241 1414097da40SStefan Popa */ 1424097da40SStefan Popa #define ADXL372_USCALE 958241 1434097da40SStefan Popa 1444097da40SStefan Popa enum adxl372_op_mode { 1454097da40SStefan Popa ADXL372_STANDBY, 1464097da40SStefan Popa ADXL372_WAKE_UP, 1474097da40SStefan Popa ADXL372_INSTANT_ON, 1484097da40SStefan Popa ADXL372_FULL_BW_MEASUREMENT, 1494097da40SStefan Popa }; 1504097da40SStefan Popa 1514097da40SStefan Popa enum adxl372_act_proc_mode { 1524097da40SStefan Popa ADXL372_DEFAULT, 1534097da40SStefan Popa ADXL372_LINKED, 1544097da40SStefan Popa ADXL372_LOOPED, 1554097da40SStefan Popa }; 1564097da40SStefan Popa 1574097da40SStefan Popa enum adxl372_th_activity { 1584097da40SStefan Popa ADXL372_ACTIVITY, 1594097da40SStefan Popa ADXL372_ACTIVITY2, 1604097da40SStefan Popa ADXL372_INACTIVITY, 1614097da40SStefan Popa }; 1624097da40SStefan Popa 1634097da40SStefan Popa enum adxl372_odr { 1644097da40SStefan Popa ADXL372_ODR_400HZ, 1654097da40SStefan Popa ADXL372_ODR_800HZ, 1664097da40SStefan Popa ADXL372_ODR_1600HZ, 1674097da40SStefan Popa ADXL372_ODR_3200HZ, 1684097da40SStefan Popa ADXL372_ODR_6400HZ, 1694097da40SStefan Popa }; 1704097da40SStefan Popa 1714097da40SStefan Popa enum adxl372_bandwidth { 1724097da40SStefan Popa ADXL372_BW_200HZ, 1734097da40SStefan Popa ADXL372_BW_400HZ, 1744097da40SStefan Popa ADXL372_BW_800HZ, 1754097da40SStefan Popa ADXL372_BW_1600HZ, 1764097da40SStefan Popa ADXL372_BW_3200HZ, 1774097da40SStefan Popa }; 1784097da40SStefan Popa 1794097da40SStefan Popa static const unsigned int adxl372_th_reg_high_addr[3] = { 1804097da40SStefan Popa [ADXL372_ACTIVITY] = ADXL372_X_THRESH_ACT_H, 1814097da40SStefan Popa [ADXL372_ACTIVITY2] = ADXL372_X_THRESH_ACT2_H, 1824097da40SStefan Popa [ADXL372_INACTIVITY] = ADXL372_X_THRESH_INACT_H, 1834097da40SStefan Popa }; 1844097da40SStefan Popa 185f4f55ce3SStefan Popa enum adxl372_fifo_format { 186f4f55ce3SStefan Popa ADXL372_XYZ_FIFO, 187f4f55ce3SStefan Popa ADXL372_X_FIFO, 188f4f55ce3SStefan Popa ADXL372_Y_FIFO, 189f4f55ce3SStefan Popa ADXL372_XY_FIFO, 190f4f55ce3SStefan Popa ADXL372_Z_FIFO, 191f4f55ce3SStefan Popa ADXL372_XZ_FIFO, 192f4f55ce3SStefan Popa ADXL372_YZ_FIFO, 193f4f55ce3SStefan Popa ADXL372_XYZ_PEAK_FIFO, 194f4f55ce3SStefan Popa }; 195f4f55ce3SStefan Popa 196f4f55ce3SStefan Popa enum adxl372_fifo_mode { 197f4f55ce3SStefan Popa ADXL372_FIFO_BYPASSED, 198f4f55ce3SStefan Popa ADXL372_FIFO_STREAMED, 199f4f55ce3SStefan Popa ADXL372_FIFO_TRIGGERED, 200f4f55ce3SStefan Popa ADXL372_FIFO_OLD_SAVED 201f4f55ce3SStefan Popa }; 202f4f55ce3SStefan Popa 203f4f55ce3SStefan Popa static const int adxl372_samp_freq_tbl[5] = { 204f4f55ce3SStefan Popa 400, 800, 1600, 3200, 6400, 205f4f55ce3SStefan Popa }; 206f4f55ce3SStefan Popa 2077ec040afSStefan Popa static const int adxl372_bw_freq_tbl[5] = { 2087ec040afSStefan Popa 200, 400, 800, 1600, 3200, 2097ec040afSStefan Popa }; 2107ec040afSStefan Popa 211f4f55ce3SStefan Popa struct adxl372_axis_lookup { 212f4f55ce3SStefan Popa unsigned int bits; 213f4f55ce3SStefan Popa enum adxl372_fifo_format fifo_format; 214f4f55ce3SStefan Popa }; 215f4f55ce3SStefan Popa 216f4f55ce3SStefan Popa static const struct adxl372_axis_lookup adxl372_axis_lookup_table[] = { 217f4f55ce3SStefan Popa { BIT(0), ADXL372_X_FIFO }, 218f4f55ce3SStefan Popa { BIT(1), ADXL372_Y_FIFO }, 219f4f55ce3SStefan Popa { BIT(2), ADXL372_Z_FIFO }, 220f4f55ce3SStefan Popa { BIT(0) | BIT(1), ADXL372_XY_FIFO }, 221f4f55ce3SStefan Popa { BIT(0) | BIT(2), ADXL372_XZ_FIFO }, 222f4f55ce3SStefan Popa { BIT(1) | BIT(2), ADXL372_YZ_FIFO }, 223f4f55ce3SStefan Popa { BIT(0) | BIT(1) | BIT(2), ADXL372_XYZ_FIFO }, 224f4f55ce3SStefan Popa }; 225f4f55ce3SStefan Popa 2264097da40SStefan Popa #define ADXL372_ACCEL_CHANNEL(index, reg, axis) { \ 2274097da40SStefan Popa .type = IIO_ACCEL, \ 2284097da40SStefan Popa .address = reg, \ 2294097da40SStefan Popa .modified = 1, \ 2304097da40SStefan Popa .channel2 = IIO_MOD_##axis, \ 2314097da40SStefan Popa .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 2325e605a4dSStefan Popa .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ 2337ec040afSStefan Popa BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ 2347ec040afSStefan Popa BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ 235f4f55ce3SStefan Popa .scan_index = index, \ 236f4f55ce3SStefan Popa .scan_type = { \ 237f4f55ce3SStefan Popa .sign = 's', \ 238f4f55ce3SStefan Popa .realbits = 12, \ 239f4f55ce3SStefan Popa .storagebits = 16, \ 240f4f55ce3SStefan Popa .shift = 4, \ 241f4f55ce3SStefan Popa }, \ 2424097da40SStefan Popa } 2434097da40SStefan Popa 2444097da40SStefan Popa static const struct iio_chan_spec adxl372_channels[] = { 2454097da40SStefan Popa ADXL372_ACCEL_CHANNEL(0, ADXL372_X_DATA_H, X), 2464097da40SStefan Popa ADXL372_ACCEL_CHANNEL(1, ADXL372_Y_DATA_H, Y), 2474097da40SStefan Popa ADXL372_ACCEL_CHANNEL(2, ADXL372_Z_DATA_H, Z), 2484097da40SStefan Popa }; 2494097da40SStefan Popa 2504097da40SStefan Popa struct adxl372_state { 251d9e8fd04SStefan Popa int irq; 252d9e8fd04SStefan Popa struct device *dev; 2534097da40SStefan Popa struct regmap *regmap; 254f4f55ce3SStefan Popa struct iio_trigger *dready_trig; 255f4f55ce3SStefan Popa enum adxl372_fifo_mode fifo_mode; 256f4f55ce3SStefan Popa enum adxl372_fifo_format fifo_format; 2574097da40SStefan Popa enum adxl372_op_mode op_mode; 2584097da40SStefan Popa enum adxl372_act_proc_mode act_proc_mode; 2594097da40SStefan Popa enum adxl372_odr odr; 2604097da40SStefan Popa enum adxl372_bandwidth bw; 2614097da40SStefan Popa u32 act_time_ms; 2624097da40SStefan Popa u32 inact_time_ms; 263f4f55ce3SStefan Popa u8 fifo_set_size; 264f4f55ce3SStefan Popa u8 int1_bitmask; 265f4f55ce3SStefan Popa u8 int2_bitmask; 266f4f55ce3SStefan Popa u16 watermark; 267f4f55ce3SStefan Popa __be16 fifo_buf[ADXL372_FIFO_SIZE]; 268f4f55ce3SStefan Popa }; 269f4f55ce3SStefan Popa 270f4f55ce3SStefan Popa static const unsigned long adxl372_channel_masks[] = { 271f4f55ce3SStefan Popa BIT(0), BIT(1), BIT(2), 272f4f55ce3SStefan Popa BIT(0) | BIT(1), 273f4f55ce3SStefan Popa BIT(0) | BIT(2), 274f4f55ce3SStefan Popa BIT(1) | BIT(2), 275f4f55ce3SStefan Popa BIT(0) | BIT(1) | BIT(2), 276f4f55ce3SStefan Popa 0 2774097da40SStefan Popa }; 2784097da40SStefan Popa 2794097da40SStefan Popa static int adxl372_read_axis(struct adxl372_state *st, u8 addr) 2804097da40SStefan Popa { 2814097da40SStefan Popa __be16 regval; 2824097da40SStefan Popa int ret; 2834097da40SStefan Popa 2844097da40SStefan Popa ret = regmap_bulk_read(st->regmap, addr, ®val, sizeof(regval)); 2854097da40SStefan Popa if (ret < 0) 2864097da40SStefan Popa return ret; 2874097da40SStefan Popa 2884097da40SStefan Popa return be16_to_cpu(regval); 2894097da40SStefan Popa } 2904097da40SStefan Popa 2914097da40SStefan Popa static int adxl372_set_op_mode(struct adxl372_state *st, 2924097da40SStefan Popa enum adxl372_op_mode op_mode) 2934097da40SStefan Popa { 2944097da40SStefan Popa int ret; 2954097da40SStefan Popa 2964097da40SStefan Popa ret = regmap_update_bits(st->regmap, ADXL372_POWER_CTL, 2974097da40SStefan Popa ADXL372_POWER_CTL_MODE_MSK, 2984097da40SStefan Popa ADXL372_POWER_CTL_MODE(op_mode)); 2994097da40SStefan Popa if (ret < 0) 3004097da40SStefan Popa return ret; 3014097da40SStefan Popa 3024097da40SStefan Popa st->op_mode = op_mode; 3034097da40SStefan Popa 3044097da40SStefan Popa return ret; 3054097da40SStefan Popa } 3064097da40SStefan Popa 3074097da40SStefan Popa static int adxl372_set_odr(struct adxl372_state *st, 3084097da40SStefan Popa enum adxl372_odr odr) 3094097da40SStefan Popa { 3104097da40SStefan Popa int ret; 3114097da40SStefan Popa 3124097da40SStefan Popa ret = regmap_update_bits(st->regmap, ADXL372_TIMING, 3134097da40SStefan Popa ADXL372_TIMING_ODR_MSK, 3144097da40SStefan Popa ADXL372_TIMING_ODR_MODE(odr)); 3154097da40SStefan Popa if (ret < 0) 3164097da40SStefan Popa return ret; 3174097da40SStefan Popa 3184097da40SStefan Popa st->odr = odr; 3194097da40SStefan Popa 3204097da40SStefan Popa return ret; 3214097da40SStefan Popa } 3224097da40SStefan Popa 3235e605a4dSStefan Popa static int adxl372_find_closest_match(const int *array, 3245e605a4dSStefan Popa unsigned int size, int val) 3255e605a4dSStefan Popa { 3265e605a4dSStefan Popa int i; 3275e605a4dSStefan Popa 3285e605a4dSStefan Popa for (i = 0; i < size; i++) { 3295e605a4dSStefan Popa if (val <= array[i]) 3305e605a4dSStefan Popa return i; 3315e605a4dSStefan Popa } 3325e605a4dSStefan Popa 3335e605a4dSStefan Popa return size - 1; 3345e605a4dSStefan Popa } 3355e605a4dSStefan Popa 3364097da40SStefan Popa static int adxl372_set_bandwidth(struct adxl372_state *st, 3374097da40SStefan Popa enum adxl372_bandwidth bw) 3384097da40SStefan Popa { 3394097da40SStefan Popa int ret; 3404097da40SStefan Popa 3414097da40SStefan Popa ret = regmap_update_bits(st->regmap, ADXL372_MEASURE, 3424097da40SStefan Popa ADXL372_MEASURE_BANDWIDTH_MSK, 3434097da40SStefan Popa ADXL372_MEASURE_BANDWIDTH_MODE(bw)); 3444097da40SStefan Popa if (ret < 0) 3454097da40SStefan Popa return ret; 3464097da40SStefan Popa 3474097da40SStefan Popa st->bw = bw; 3484097da40SStefan Popa 3494097da40SStefan Popa return ret; 3504097da40SStefan Popa } 3514097da40SStefan Popa 3524097da40SStefan Popa static int adxl372_set_act_proc_mode(struct adxl372_state *st, 3534097da40SStefan Popa enum adxl372_act_proc_mode mode) 3544097da40SStefan Popa { 3554097da40SStefan Popa int ret; 3564097da40SStefan Popa 3574097da40SStefan Popa ret = regmap_update_bits(st->regmap, 3584097da40SStefan Popa ADXL372_MEASURE, 3594097da40SStefan Popa ADXL372_MEASURE_LINKLOOP_MSK, 3604097da40SStefan Popa ADXL372_MEASURE_LINKLOOP_MODE(mode)); 3614097da40SStefan Popa if (ret < 0) 3624097da40SStefan Popa return ret; 3634097da40SStefan Popa 3644097da40SStefan Popa st->act_proc_mode = mode; 3654097da40SStefan Popa 3664097da40SStefan Popa return ret; 3674097da40SStefan Popa } 3684097da40SStefan Popa 3694097da40SStefan Popa static int adxl372_set_activity_threshold(struct adxl372_state *st, 3704097da40SStefan Popa enum adxl372_th_activity act, 3714097da40SStefan Popa bool ref_en, bool enable, 3724097da40SStefan Popa unsigned int threshold) 3734097da40SStefan Popa { 3744097da40SStefan Popa unsigned char buf[6]; 3754097da40SStefan Popa unsigned char th_reg_high_val, th_reg_low_val, th_reg_high_addr; 3764097da40SStefan Popa 3774097da40SStefan Popa /* scale factor is 100 mg/code */ 3784097da40SStefan Popa th_reg_high_val = (threshold / 100) >> 3; 3794097da40SStefan Popa th_reg_low_val = ((threshold / 100) << 5) | (ref_en << 1) | enable; 3804097da40SStefan Popa th_reg_high_addr = adxl372_th_reg_high_addr[act]; 3814097da40SStefan Popa 3824097da40SStefan Popa buf[0] = th_reg_high_val; 3834097da40SStefan Popa buf[1] = th_reg_low_val; 3844097da40SStefan Popa buf[2] = th_reg_high_val; 3854097da40SStefan Popa buf[3] = th_reg_low_val; 3864097da40SStefan Popa buf[4] = th_reg_high_val; 3874097da40SStefan Popa buf[5] = th_reg_low_val; 3884097da40SStefan Popa 3894097da40SStefan Popa return regmap_bulk_write(st->regmap, th_reg_high_addr, 3904097da40SStefan Popa buf, ARRAY_SIZE(buf)); 3914097da40SStefan Popa } 3924097da40SStefan Popa 3934097da40SStefan Popa static int adxl372_set_activity_time_ms(struct adxl372_state *st, 3944097da40SStefan Popa unsigned int act_time_ms) 3954097da40SStefan Popa { 3964097da40SStefan Popa unsigned int reg_val, scale_factor; 3974097da40SStefan Popa int ret; 3984097da40SStefan Popa 3994097da40SStefan Popa /* 4004097da40SStefan Popa * 3.3 ms per code is the scale factor of the TIME_ACT register for 4014097da40SStefan Popa * ODR = 6400 Hz. It is 6.6 ms per code for ODR = 3200 Hz and below. 4024097da40SStefan Popa */ 4034097da40SStefan Popa if (st->odr == ADXL372_ODR_6400HZ) 4044097da40SStefan Popa scale_factor = 3300; 4054097da40SStefan Popa else 4064097da40SStefan Popa scale_factor = 6600; 4074097da40SStefan Popa 4084097da40SStefan Popa reg_val = DIV_ROUND_CLOSEST(act_time_ms * 1000, scale_factor); 4094097da40SStefan Popa 4104097da40SStefan Popa /* TIME_ACT register is 8 bits wide */ 4114097da40SStefan Popa if (reg_val > 0xFF) 4124097da40SStefan Popa reg_val = 0xFF; 4134097da40SStefan Popa 4144097da40SStefan Popa ret = regmap_write(st->regmap, ADXL372_TIME_ACT, reg_val); 4154097da40SStefan Popa if (ret < 0) 4164097da40SStefan Popa return ret; 4174097da40SStefan Popa 4184097da40SStefan Popa st->act_time_ms = act_time_ms; 4194097da40SStefan Popa 4204097da40SStefan Popa return ret; 4214097da40SStefan Popa } 4224097da40SStefan Popa 4234097da40SStefan Popa static int adxl372_set_inactivity_time_ms(struct adxl372_state *st, 4244097da40SStefan Popa unsigned int inact_time_ms) 4254097da40SStefan Popa { 4264097da40SStefan Popa unsigned int reg_val_h, reg_val_l, res, scale_factor; 4274097da40SStefan Popa int ret; 4284097da40SStefan Popa 4294097da40SStefan Popa /* 4304097da40SStefan Popa * 13 ms per code is the scale factor of the TIME_INACT register for 4314097da40SStefan Popa * ODR = 6400 Hz. It is 26 ms per code for ODR = 3200 Hz and below. 4324097da40SStefan Popa */ 4334097da40SStefan Popa if (st->odr == ADXL372_ODR_6400HZ) 4344097da40SStefan Popa scale_factor = 13; 4354097da40SStefan Popa else 4364097da40SStefan Popa scale_factor = 26; 4374097da40SStefan Popa 4384097da40SStefan Popa res = DIV_ROUND_CLOSEST(inact_time_ms, scale_factor); 4394097da40SStefan Popa reg_val_h = (res >> 8) & 0xFF; 4404097da40SStefan Popa reg_val_l = res & 0xFF; 4414097da40SStefan Popa 4424097da40SStefan Popa ret = regmap_write(st->regmap, ADXL372_TIME_INACT_H, reg_val_h); 4434097da40SStefan Popa if (ret < 0) 4444097da40SStefan Popa return ret; 4454097da40SStefan Popa 4464097da40SStefan Popa ret = regmap_write(st->regmap, ADXL372_TIME_INACT_L, reg_val_l); 4474097da40SStefan Popa if (ret < 0) 4484097da40SStefan Popa return ret; 4494097da40SStefan Popa 4504097da40SStefan Popa st->inact_time_ms = inact_time_ms; 4514097da40SStefan Popa 4524097da40SStefan Popa return ret; 4534097da40SStefan Popa } 4544097da40SStefan Popa 455f4f55ce3SStefan Popa static int adxl372_set_interrupts(struct adxl372_state *st, 456f4f55ce3SStefan Popa unsigned char int1_bitmask, 457f4f55ce3SStefan Popa unsigned char int2_bitmask) 458f4f55ce3SStefan Popa { 459f4f55ce3SStefan Popa int ret; 460f4f55ce3SStefan Popa 461f4f55ce3SStefan Popa ret = regmap_write(st->regmap, ADXL372_INT1_MAP, int1_bitmask); 462f4f55ce3SStefan Popa if (ret < 0) 463f4f55ce3SStefan Popa return ret; 464f4f55ce3SStefan Popa 465f4f55ce3SStefan Popa return regmap_write(st->regmap, ADXL372_INT2_MAP, int2_bitmask); 466f4f55ce3SStefan Popa } 467f4f55ce3SStefan Popa 468f4f55ce3SStefan Popa static int adxl372_configure_fifo(struct adxl372_state *st) 469f4f55ce3SStefan Popa { 470f4f55ce3SStefan Popa unsigned int fifo_samples, fifo_ctl; 471f4f55ce3SStefan Popa int ret; 472f4f55ce3SStefan Popa 473f4f55ce3SStefan Popa /* FIFO must be configured while in standby mode */ 474f4f55ce3SStefan Popa ret = adxl372_set_op_mode(st, ADXL372_STANDBY); 475f4f55ce3SStefan Popa if (ret < 0) 476f4f55ce3SStefan Popa return ret; 477f4f55ce3SStefan Popa 478f4f55ce3SStefan Popa fifo_samples = st->watermark & 0xFF; 479f4f55ce3SStefan Popa fifo_ctl = ADXL372_FIFO_CTL_FORMAT_MODE(st->fifo_format) | 480f4f55ce3SStefan Popa ADXL372_FIFO_CTL_MODE_MODE(st->fifo_mode) | 481f4f55ce3SStefan Popa ADXL372_FIFO_CTL_SAMPLES_MODE(st->watermark); 482f4f55ce3SStefan Popa 483f4f55ce3SStefan Popa ret = regmap_write(st->regmap, ADXL372_FIFO_SAMPLES, fifo_samples); 484f4f55ce3SStefan Popa if (ret < 0) 485f4f55ce3SStefan Popa return ret; 486f4f55ce3SStefan Popa 487f4f55ce3SStefan Popa ret = regmap_write(st->regmap, ADXL372_FIFO_CTL, fifo_ctl); 488f4f55ce3SStefan Popa if (ret < 0) 489f4f55ce3SStefan Popa return ret; 490f4f55ce3SStefan Popa 491f4f55ce3SStefan Popa return adxl372_set_op_mode(st, ADXL372_FULL_BW_MEASUREMENT); 492f4f55ce3SStefan Popa } 493f4f55ce3SStefan Popa 494f4f55ce3SStefan Popa static int adxl372_get_status(struct adxl372_state *st, 495f4f55ce3SStefan Popa u8 *status1, u8 *status2, 496f4f55ce3SStefan Popa u16 *fifo_entries) 497f4f55ce3SStefan Popa { 498f4f55ce3SStefan Popa __be32 buf; 499f4f55ce3SStefan Popa u32 val; 500f4f55ce3SStefan Popa int ret; 501f4f55ce3SStefan Popa 502f4f55ce3SStefan Popa /* STATUS1, STATUS2, FIFO_ENTRIES2 and FIFO_ENTRIES are adjacent regs */ 503f4f55ce3SStefan Popa ret = regmap_bulk_read(st->regmap, ADXL372_STATUS_1, 504f4f55ce3SStefan Popa &buf, sizeof(buf)); 505f4f55ce3SStefan Popa if (ret < 0) 506f4f55ce3SStefan Popa return ret; 507f4f55ce3SStefan Popa 508f4f55ce3SStefan Popa val = be32_to_cpu(buf); 509f4f55ce3SStefan Popa 510f4f55ce3SStefan Popa *status1 = (val >> 24) & 0x0F; 511f4f55ce3SStefan Popa *status2 = (val >> 16) & 0x0F; 512f4f55ce3SStefan Popa /* 513f4f55ce3SStefan Popa * FIFO_ENTRIES contains the least significant byte, and FIFO_ENTRIES2 514f4f55ce3SStefan Popa * contains the two most significant bits 515f4f55ce3SStefan Popa */ 516f4f55ce3SStefan Popa *fifo_entries = val & 0x3FF; 517f4f55ce3SStefan Popa 518f4f55ce3SStefan Popa return ret; 519f4f55ce3SStefan Popa } 520f4f55ce3SStefan Popa 521f4f55ce3SStefan Popa static irqreturn_t adxl372_trigger_handler(int irq, void *p) 522f4f55ce3SStefan Popa { 523f4f55ce3SStefan Popa struct iio_poll_func *pf = p; 524f4f55ce3SStefan Popa struct iio_dev *indio_dev = pf->indio_dev; 525f4f55ce3SStefan Popa struct adxl372_state *st = iio_priv(indio_dev); 526f4f55ce3SStefan Popa u8 status1, status2; 527f4f55ce3SStefan Popa u16 fifo_entries; 528f4f55ce3SStefan Popa int i, ret; 529f4f55ce3SStefan Popa 530f4f55ce3SStefan Popa ret = adxl372_get_status(st, &status1, &status2, &fifo_entries); 531f4f55ce3SStefan Popa if (ret < 0) 532f4f55ce3SStefan Popa goto err; 533f4f55ce3SStefan Popa 534f4f55ce3SStefan Popa if (st->fifo_mode != ADXL372_FIFO_BYPASSED && 535f4f55ce3SStefan Popa ADXL372_STATUS_1_FIFO_FULL(status1)) { 536f4f55ce3SStefan Popa /* 537f4f55ce3SStefan Popa * When reading data from multiple axes from the FIFO, 538f4f55ce3SStefan Popa * to ensure that data is not overwritten and stored out 539f4f55ce3SStefan Popa * of order at least one sample set must be left in the 540f4f55ce3SStefan Popa * FIFO after every read. 541f4f55ce3SStefan Popa */ 542f4f55ce3SStefan Popa fifo_entries -= st->fifo_set_size; 543f4f55ce3SStefan Popa 544f4f55ce3SStefan Popa /* Read data from the FIFO */ 545f4f55ce3SStefan Popa ret = regmap_noinc_read(st->regmap, ADXL372_FIFO_DATA, 546f4f55ce3SStefan Popa st->fifo_buf, 547f4f55ce3SStefan Popa fifo_entries * sizeof(u16)); 548f4f55ce3SStefan Popa if (ret < 0) 549f4f55ce3SStefan Popa goto err; 550f4f55ce3SStefan Popa 551f4f55ce3SStefan Popa /* Each sample is 2 bytes */ 552f4f55ce3SStefan Popa for (i = 0; i < fifo_entries * sizeof(u16); 553f4f55ce3SStefan Popa i += st->fifo_set_size * sizeof(u16)) 554f4f55ce3SStefan Popa iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); 555f4f55ce3SStefan Popa } 556f4f55ce3SStefan Popa err: 557f4f55ce3SStefan Popa iio_trigger_notify_done(indio_dev->trig); 558f4f55ce3SStefan Popa return IRQ_HANDLED; 559f4f55ce3SStefan Popa } 560f4f55ce3SStefan Popa 5614097da40SStefan Popa static int adxl372_setup(struct adxl372_state *st) 5624097da40SStefan Popa { 5634097da40SStefan Popa unsigned int regval; 5644097da40SStefan Popa int ret; 5654097da40SStefan Popa 5664097da40SStefan Popa ret = regmap_read(st->regmap, ADXL372_DEVID, ®val); 5674097da40SStefan Popa if (ret < 0) 5684097da40SStefan Popa return ret; 5694097da40SStefan Popa 5704097da40SStefan Popa if (regval != ADXL372_DEVID_VAL) { 571d9e8fd04SStefan Popa dev_err(st->dev, "Invalid chip id %x\n", regval); 5724097da40SStefan Popa return -ENODEV; 5734097da40SStefan Popa } 5744097da40SStefan Popa 5754097da40SStefan Popa ret = adxl372_set_op_mode(st, ADXL372_STANDBY); 5764097da40SStefan Popa if (ret < 0) 5774097da40SStefan Popa return ret; 5784097da40SStefan Popa 5794097da40SStefan Popa /* Set threshold for activity detection to 1g */ 5804097da40SStefan Popa ret = adxl372_set_activity_threshold(st, ADXL372_ACTIVITY, 5814097da40SStefan Popa true, true, 1000); 5824097da40SStefan Popa if (ret < 0) 5834097da40SStefan Popa return ret; 5844097da40SStefan Popa 5854097da40SStefan Popa /* Set threshold for inactivity detection to 100mg */ 5864097da40SStefan Popa ret = adxl372_set_activity_threshold(st, ADXL372_INACTIVITY, 5874097da40SStefan Popa true, true, 100); 5884097da40SStefan Popa if (ret < 0) 5894097da40SStefan Popa return ret; 5904097da40SStefan Popa 5914097da40SStefan Popa /* Set activity processing in Looped mode */ 5924097da40SStefan Popa ret = adxl372_set_act_proc_mode(st, ADXL372_LOOPED); 5934097da40SStefan Popa if (ret < 0) 5944097da40SStefan Popa return ret; 5954097da40SStefan Popa 5964097da40SStefan Popa ret = adxl372_set_odr(st, ADXL372_ODR_6400HZ); 5974097da40SStefan Popa if (ret < 0) 5984097da40SStefan Popa return ret; 5994097da40SStefan Popa 6004097da40SStefan Popa ret = adxl372_set_bandwidth(st, ADXL372_BW_3200HZ); 6014097da40SStefan Popa if (ret < 0) 6024097da40SStefan Popa return ret; 6034097da40SStefan Popa 6044097da40SStefan Popa /* Set activity timer to 1ms */ 6054097da40SStefan Popa ret = adxl372_set_activity_time_ms(st, 1); 6064097da40SStefan Popa if (ret < 0) 6074097da40SStefan Popa return ret; 6084097da40SStefan Popa 6094097da40SStefan Popa /* Set inactivity timer to 10s */ 6104097da40SStefan Popa ret = adxl372_set_inactivity_time_ms(st, 10000); 6114097da40SStefan Popa if (ret < 0) 6124097da40SStefan Popa return ret; 6134097da40SStefan Popa 6144097da40SStefan Popa /* Set the mode of operation to full bandwidth measurement mode */ 6154097da40SStefan Popa return adxl372_set_op_mode(st, ADXL372_FULL_BW_MEASUREMENT); 6164097da40SStefan Popa } 6174097da40SStefan Popa 6184097da40SStefan Popa static int adxl372_reg_access(struct iio_dev *indio_dev, 6194097da40SStefan Popa unsigned int reg, 6204097da40SStefan Popa unsigned int writeval, 6214097da40SStefan Popa unsigned int *readval) 6224097da40SStefan Popa { 6234097da40SStefan Popa struct adxl372_state *st = iio_priv(indio_dev); 6244097da40SStefan Popa 6254097da40SStefan Popa if (readval) 6264097da40SStefan Popa return regmap_read(st->regmap, reg, readval); 6274097da40SStefan Popa else 6284097da40SStefan Popa return regmap_write(st->regmap, reg, writeval); 6294097da40SStefan Popa } 6304097da40SStefan Popa 6314097da40SStefan Popa static int adxl372_read_raw(struct iio_dev *indio_dev, 6324097da40SStefan Popa struct iio_chan_spec const *chan, 6334097da40SStefan Popa int *val, int *val2, long info) 6344097da40SStefan Popa { 6354097da40SStefan Popa struct adxl372_state *st = iio_priv(indio_dev); 6364097da40SStefan Popa int ret; 6374097da40SStefan Popa 6384097da40SStefan Popa switch (info) { 6394097da40SStefan Popa case IIO_CHAN_INFO_RAW: 640f4f55ce3SStefan Popa ret = iio_device_claim_direct_mode(indio_dev); 641f4f55ce3SStefan Popa if (ret) 642f4f55ce3SStefan Popa return ret; 643f4f55ce3SStefan Popa 6444097da40SStefan Popa ret = adxl372_read_axis(st, chan->address); 645f4f55ce3SStefan Popa iio_device_release_direct_mode(indio_dev); 6464097da40SStefan Popa if (ret < 0) 6474097da40SStefan Popa return ret; 6484097da40SStefan Popa 6494097da40SStefan Popa *val = sign_extend32(ret >> chan->scan_type.shift, 6504097da40SStefan Popa chan->scan_type.realbits - 1); 6514097da40SStefan Popa return IIO_VAL_INT; 6524097da40SStefan Popa case IIO_CHAN_INFO_SCALE: 6534097da40SStefan Popa *val = 0; 6544097da40SStefan Popa *val2 = ADXL372_USCALE; 6554097da40SStefan Popa return IIO_VAL_INT_PLUS_MICRO; 6565e605a4dSStefan Popa case IIO_CHAN_INFO_SAMP_FREQ: 6575e605a4dSStefan Popa *val = adxl372_samp_freq_tbl[st->odr]; 6585e605a4dSStefan Popa return IIO_VAL_INT; 6597ec040afSStefan Popa case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: 6607ec040afSStefan Popa *val = adxl372_bw_freq_tbl[st->bw]; 6617ec040afSStefan Popa return IIO_VAL_INT; 6625e605a4dSStefan Popa } 6635e605a4dSStefan Popa 6645e605a4dSStefan Popa return -EINVAL; 6655e605a4dSStefan Popa } 6665e605a4dSStefan Popa 6675e605a4dSStefan Popa static int adxl372_write_raw(struct iio_dev *indio_dev, 6685e605a4dSStefan Popa struct iio_chan_spec const *chan, 6695e605a4dSStefan Popa int val, int val2, long info) 6705e605a4dSStefan Popa { 6715e605a4dSStefan Popa struct adxl372_state *st = iio_priv(indio_dev); 6727ec040afSStefan Popa int odr_index, bw_index, ret; 6735e605a4dSStefan Popa 6745e605a4dSStefan Popa switch (info) { 6755e605a4dSStefan Popa case IIO_CHAN_INFO_SAMP_FREQ: 6765e605a4dSStefan Popa odr_index = adxl372_find_closest_match(adxl372_samp_freq_tbl, 6775e605a4dSStefan Popa ARRAY_SIZE(adxl372_samp_freq_tbl), 6785e605a4dSStefan Popa val); 6795e605a4dSStefan Popa ret = adxl372_set_odr(st, odr_index); 6805e605a4dSStefan Popa if (ret < 0) 6815e605a4dSStefan Popa return ret; 6825e605a4dSStefan Popa /* 6835e605a4dSStefan Popa * The timer period depends on the ODR selected. 6845e605a4dSStefan Popa * At 3200 Hz and below, it is 6.6 ms; at 6400 Hz, it is 3.3 ms 6855e605a4dSStefan Popa */ 6865e605a4dSStefan Popa ret = adxl372_set_activity_time_ms(st, st->act_time_ms); 6875e605a4dSStefan Popa if (ret < 0) 6885e605a4dSStefan Popa return ret; 6895e605a4dSStefan Popa /* 6905e605a4dSStefan Popa * The timer period depends on the ODR selected. 6915e605a4dSStefan Popa * At 3200 Hz and below, it is 26 ms; at 6400 Hz, it is 13 ms 6925e605a4dSStefan Popa */ 6935e605a4dSStefan Popa ret = adxl372_set_inactivity_time_ms(st, st->inact_time_ms); 6945e605a4dSStefan Popa if (ret < 0) 6955e605a4dSStefan Popa return ret; 6965e605a4dSStefan Popa /* 6975e605a4dSStefan Popa * The maximum bandwidth is constrained to at most half of 6985e605a4dSStefan Popa * the ODR to ensure that the Nyquist criteria is not violated 6995e605a4dSStefan Popa */ 7005e605a4dSStefan Popa if (st->bw > odr_index) 7015e605a4dSStefan Popa ret = adxl372_set_bandwidth(st, odr_index); 7025e605a4dSStefan Popa 7035e605a4dSStefan Popa return ret; 7047ec040afSStefan Popa case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: 7057ec040afSStefan Popa bw_index = adxl372_find_closest_match(adxl372_bw_freq_tbl, 7067ec040afSStefan Popa ARRAY_SIZE(adxl372_bw_freq_tbl), 7077ec040afSStefan Popa val); 7087ec040afSStefan Popa return adxl372_set_bandwidth(st, bw_index); 7094097da40SStefan Popa default: 7104097da40SStefan Popa return -EINVAL; 7114097da40SStefan Popa } 7124097da40SStefan Popa } 7134097da40SStefan Popa 7147ec040afSStefan Popa static ssize_t adxl372_show_filter_freq_avail(struct device *dev, 7157ec040afSStefan Popa struct device_attribute *attr, 7167ec040afSStefan Popa char *buf) 7177ec040afSStefan Popa { 7187ec040afSStefan Popa struct iio_dev *indio_dev = dev_to_iio_dev(dev); 7197ec040afSStefan Popa struct adxl372_state *st = iio_priv(indio_dev); 7207ec040afSStefan Popa int i; 7217ec040afSStefan Popa size_t len = 0; 7227ec040afSStefan Popa 7237ec040afSStefan Popa for (i = 0; i <= st->odr; i++) 7247ec040afSStefan Popa len += scnprintf(buf + len, PAGE_SIZE - len, 7257ec040afSStefan Popa "%d ", adxl372_bw_freq_tbl[i]); 7267ec040afSStefan Popa 7277ec040afSStefan Popa buf[len - 1] = '\n'; 7287ec040afSStefan Popa 7297ec040afSStefan Popa return len; 7307ec040afSStefan Popa } 7317ec040afSStefan Popa 732f4f55ce3SStefan Popa static ssize_t adxl372_get_fifo_enabled(struct device *dev, 733f4f55ce3SStefan Popa struct device_attribute *attr, 734f4f55ce3SStefan Popa char *buf) 735f4f55ce3SStefan Popa { 736f4f55ce3SStefan Popa struct iio_dev *indio_dev = dev_to_iio_dev(dev); 737f4f55ce3SStefan Popa struct adxl372_state *st = iio_priv(indio_dev); 738f4f55ce3SStefan Popa 739f4f55ce3SStefan Popa return sprintf(buf, "%d\n", st->fifo_mode); 740f4f55ce3SStefan Popa } 741f4f55ce3SStefan Popa 742f4f55ce3SStefan Popa static ssize_t adxl372_get_fifo_watermark(struct device *dev, 743f4f55ce3SStefan Popa struct device_attribute *attr, 744f4f55ce3SStefan Popa char *buf) 745f4f55ce3SStefan Popa { 746f4f55ce3SStefan Popa struct iio_dev *indio_dev = dev_to_iio_dev(dev); 747f4f55ce3SStefan Popa struct adxl372_state *st = iio_priv(indio_dev); 748f4f55ce3SStefan Popa 749f4f55ce3SStefan Popa return sprintf(buf, "%d\n", st->watermark); 750f4f55ce3SStefan Popa } 751f4f55ce3SStefan Popa 752f4f55ce3SStefan Popa static IIO_CONST_ATTR(hwfifo_watermark_min, "1"); 753f4f55ce3SStefan Popa static IIO_CONST_ATTR(hwfifo_watermark_max, 754f4f55ce3SStefan Popa __stringify(ADXL372_FIFO_SIZE)); 755f4f55ce3SStefan Popa static IIO_DEVICE_ATTR(hwfifo_watermark, 0444, 756f4f55ce3SStefan Popa adxl372_get_fifo_watermark, NULL, 0); 757f4f55ce3SStefan Popa static IIO_DEVICE_ATTR(hwfifo_enabled, 0444, 758f4f55ce3SStefan Popa adxl372_get_fifo_enabled, NULL, 0); 759f4f55ce3SStefan Popa 760f4f55ce3SStefan Popa static const struct attribute *adxl372_fifo_attributes[] = { 761f4f55ce3SStefan Popa &iio_const_attr_hwfifo_watermark_min.dev_attr.attr, 762f4f55ce3SStefan Popa &iio_const_attr_hwfifo_watermark_max.dev_attr.attr, 763f4f55ce3SStefan Popa &iio_dev_attr_hwfifo_watermark.dev_attr.attr, 764f4f55ce3SStefan Popa &iio_dev_attr_hwfifo_enabled.dev_attr.attr, 765f4f55ce3SStefan Popa NULL, 766f4f55ce3SStefan Popa }; 767f4f55ce3SStefan Popa 768f4f55ce3SStefan Popa static int adxl372_set_watermark(struct iio_dev *indio_dev, unsigned int val) 769f4f55ce3SStefan Popa { 770f4f55ce3SStefan Popa struct adxl372_state *st = iio_priv(indio_dev); 771f4f55ce3SStefan Popa 772f4f55ce3SStefan Popa if (val > ADXL372_FIFO_SIZE) 773f4f55ce3SStefan Popa val = ADXL372_FIFO_SIZE; 774f4f55ce3SStefan Popa 775f4f55ce3SStefan Popa st->watermark = val; 776f4f55ce3SStefan Popa 777f4f55ce3SStefan Popa return 0; 778f4f55ce3SStefan Popa } 779f4f55ce3SStefan Popa 780f4f55ce3SStefan Popa static int adxl372_buffer_postenable(struct iio_dev *indio_dev) 781f4f55ce3SStefan Popa { 782f4f55ce3SStefan Popa struct adxl372_state *st = iio_priv(indio_dev); 783f4f55ce3SStefan Popa unsigned int mask; 784f4f55ce3SStefan Popa int i, ret; 785f4f55ce3SStefan Popa 786f4f55ce3SStefan Popa ret = adxl372_set_interrupts(st, ADXL372_INT1_MAP_FIFO_FULL_MSK, 0); 787f4f55ce3SStefan Popa if (ret < 0) 788f4f55ce3SStefan Popa return ret; 789f4f55ce3SStefan Popa 790f4f55ce3SStefan Popa mask = *indio_dev->active_scan_mask; 791f4f55ce3SStefan Popa 792f4f55ce3SStefan Popa for (i = 0; i < ARRAY_SIZE(adxl372_axis_lookup_table); i++) { 793f4f55ce3SStefan Popa if (mask == adxl372_axis_lookup_table[i].bits) 794f4f55ce3SStefan Popa break; 795f4f55ce3SStefan Popa } 796f4f55ce3SStefan Popa 797f4f55ce3SStefan Popa if (i == ARRAY_SIZE(adxl372_axis_lookup_table)) 798f4f55ce3SStefan Popa return -EINVAL; 799f4f55ce3SStefan Popa 800f4f55ce3SStefan Popa st->fifo_format = adxl372_axis_lookup_table[i].fifo_format; 801f4f55ce3SStefan Popa st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask, 802f4f55ce3SStefan Popa indio_dev->masklength); 803f4f55ce3SStefan Popa /* 804f4f55ce3SStefan Popa * The 512 FIFO samples can be allotted in several ways, such as: 805f4f55ce3SStefan Popa * 170 sample sets of concurrent 3-axis data 806f4f55ce3SStefan Popa * 256 sample sets of concurrent 2-axis data (user selectable) 807f4f55ce3SStefan Popa * 512 sample sets of single-axis data 808f4f55ce3SStefan Popa */ 809f4f55ce3SStefan Popa if ((st->watermark * st->fifo_set_size) > ADXL372_FIFO_SIZE) 810f4f55ce3SStefan Popa st->watermark = (ADXL372_FIFO_SIZE / st->fifo_set_size); 811f4f55ce3SStefan Popa 812f4f55ce3SStefan Popa st->fifo_mode = ADXL372_FIFO_STREAMED; 813f4f55ce3SStefan Popa 814f4f55ce3SStefan Popa ret = adxl372_configure_fifo(st); 815f4f55ce3SStefan Popa if (ret < 0) { 816f4f55ce3SStefan Popa st->fifo_mode = ADXL372_FIFO_BYPASSED; 817f4f55ce3SStefan Popa adxl372_set_interrupts(st, 0, 0); 818f4f55ce3SStefan Popa return ret; 819f4f55ce3SStefan Popa } 820f4f55ce3SStefan Popa 821f4f55ce3SStefan Popa return iio_triggered_buffer_postenable(indio_dev); 822f4f55ce3SStefan Popa } 823f4f55ce3SStefan Popa 824f4f55ce3SStefan Popa static int adxl372_buffer_predisable(struct iio_dev *indio_dev) 825f4f55ce3SStefan Popa { 826f4f55ce3SStefan Popa struct adxl372_state *st = iio_priv(indio_dev); 827f4f55ce3SStefan Popa int ret; 828f4f55ce3SStefan Popa 829f4f55ce3SStefan Popa ret = iio_triggered_buffer_predisable(indio_dev); 830f4f55ce3SStefan Popa if (ret < 0) 831f4f55ce3SStefan Popa return ret; 832f4f55ce3SStefan Popa 833f4f55ce3SStefan Popa adxl372_set_interrupts(st, 0, 0); 834f4f55ce3SStefan Popa st->fifo_mode = ADXL372_FIFO_BYPASSED; 835f4f55ce3SStefan Popa adxl372_configure_fifo(st); 836f4f55ce3SStefan Popa 837f4f55ce3SStefan Popa return 0; 838f4f55ce3SStefan Popa } 839f4f55ce3SStefan Popa 840f4f55ce3SStefan Popa static const struct iio_buffer_setup_ops adxl372_buffer_ops = { 841f4f55ce3SStefan Popa .postenable = adxl372_buffer_postenable, 842f4f55ce3SStefan Popa .predisable = adxl372_buffer_predisable, 843f4f55ce3SStefan Popa }; 844f4f55ce3SStefan Popa 845f4f55ce3SStefan Popa static int adxl372_dready_trig_set_state(struct iio_trigger *trig, 846f4f55ce3SStefan Popa bool state) 847f4f55ce3SStefan Popa { 848f4f55ce3SStefan Popa struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); 849f4f55ce3SStefan Popa struct adxl372_state *st = iio_priv(indio_dev); 850f4f55ce3SStefan Popa unsigned long int mask = 0; 851f4f55ce3SStefan Popa 852f4f55ce3SStefan Popa if (state) 853f4f55ce3SStefan Popa mask = ADXL372_INT1_MAP_FIFO_FULL_MSK; 854f4f55ce3SStefan Popa 855f4f55ce3SStefan Popa return adxl372_set_interrupts(st, mask, 0); 856f4f55ce3SStefan Popa } 857f4f55ce3SStefan Popa 8581c412a32SStefan Popa static int adxl372_validate_trigger(struct iio_dev *indio_dev, 8591c412a32SStefan Popa struct iio_trigger *trig) 8601c412a32SStefan Popa { 8611c412a32SStefan Popa struct adxl372_state *st = iio_priv(indio_dev); 8621c412a32SStefan Popa 8631c412a32SStefan Popa if (st->dready_trig != trig) 8641c412a32SStefan Popa return -EINVAL; 8651c412a32SStefan Popa 8661c412a32SStefan Popa return 0; 8671c412a32SStefan Popa } 8681c412a32SStefan Popa 869f4f55ce3SStefan Popa static const struct iio_trigger_ops adxl372_trigger_ops = { 8701c412a32SStefan Popa .validate_device = &iio_trigger_validate_own_device, 871f4f55ce3SStefan Popa .set_trigger_state = adxl372_dready_trig_set_state, 872f4f55ce3SStefan Popa }; 873f4f55ce3SStefan Popa 8745e605a4dSStefan Popa static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("400 800 1600 3200 6400"); 8757ec040afSStefan Popa static IIO_DEVICE_ATTR(in_accel_filter_low_pass_3db_frequency_available, 8767ec040afSStefan Popa 0444, adxl372_show_filter_freq_avail, NULL, 0); 8775e605a4dSStefan Popa 8785e605a4dSStefan Popa static struct attribute *adxl372_attributes[] = { 8795e605a4dSStefan Popa &iio_const_attr_sampling_frequency_available.dev_attr.attr, 8807ec040afSStefan Popa &iio_dev_attr_in_accel_filter_low_pass_3db_frequency_available.dev_attr.attr, 8815e605a4dSStefan Popa NULL, 8825e605a4dSStefan Popa }; 8835e605a4dSStefan Popa 8845e605a4dSStefan Popa static const struct attribute_group adxl372_attrs_group = { 8855e605a4dSStefan Popa .attrs = adxl372_attributes, 8865e605a4dSStefan Popa }; 8875e605a4dSStefan Popa 8884097da40SStefan Popa static const struct iio_info adxl372_info = { 8891c412a32SStefan Popa .validate_trigger = &adxl372_validate_trigger, 8905e605a4dSStefan Popa .attrs = &adxl372_attrs_group, 8914097da40SStefan Popa .read_raw = adxl372_read_raw, 8925e605a4dSStefan Popa .write_raw = adxl372_write_raw, 8934097da40SStefan Popa .debugfs_reg_access = &adxl372_reg_access, 894f4f55ce3SStefan Popa .hwfifo_set_watermark = adxl372_set_watermark, 8954097da40SStefan Popa }; 8964097da40SStefan Popa 897d9e8fd04SStefan Popa bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg) 898f4f55ce3SStefan Popa { 899f4f55ce3SStefan Popa return (reg == ADXL372_FIFO_DATA); 900f4f55ce3SStefan Popa } 901d9e8fd04SStefan Popa EXPORT_SYMBOL_GPL(adxl372_readable_noinc_reg); 902f4f55ce3SStefan Popa 903d9e8fd04SStefan Popa int adxl372_probe(struct device *dev, struct regmap *regmap, 904d9e8fd04SStefan Popa int irq, const char *name) 9054097da40SStefan Popa { 9064097da40SStefan Popa struct iio_dev *indio_dev; 9074097da40SStefan Popa struct adxl372_state *st; 9084097da40SStefan Popa int ret; 9094097da40SStefan Popa 910d9e8fd04SStefan Popa indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); 9114097da40SStefan Popa if (!indio_dev) 9124097da40SStefan Popa return -ENOMEM; 9134097da40SStefan Popa 9144097da40SStefan Popa st = iio_priv(indio_dev); 915d9e8fd04SStefan Popa dev_set_drvdata(dev, indio_dev); 9164097da40SStefan Popa 917d9e8fd04SStefan Popa st->dev = dev; 9184097da40SStefan Popa st->regmap = regmap; 919d9e8fd04SStefan Popa st->irq = irq; 9204097da40SStefan Popa 9214097da40SStefan Popa indio_dev->channels = adxl372_channels; 9224097da40SStefan Popa indio_dev->num_channels = ARRAY_SIZE(adxl372_channels); 923f4f55ce3SStefan Popa indio_dev->available_scan_masks = adxl372_channel_masks; 924d9e8fd04SStefan Popa indio_dev->dev.parent = dev; 925d9e8fd04SStefan Popa indio_dev->name = name; 9264097da40SStefan Popa indio_dev->info = &adxl372_info; 927f4f55ce3SStefan Popa indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; 9284097da40SStefan Popa 9294097da40SStefan Popa ret = adxl372_setup(st); 9304097da40SStefan Popa if (ret < 0) { 931d9e8fd04SStefan Popa dev_err(dev, "ADXL372 setup failed\n"); 9324097da40SStefan Popa return ret; 9334097da40SStefan Popa } 9344097da40SStefan Popa 935d9e8fd04SStefan Popa ret = devm_iio_triggered_buffer_setup(dev, 936f4f55ce3SStefan Popa indio_dev, NULL, 937f4f55ce3SStefan Popa adxl372_trigger_handler, 938f4f55ce3SStefan Popa &adxl372_buffer_ops); 939f4f55ce3SStefan Popa if (ret < 0) 940f4f55ce3SStefan Popa return ret; 941f4f55ce3SStefan Popa 942f4f55ce3SStefan Popa iio_buffer_set_attrs(indio_dev->buffer, adxl372_fifo_attributes); 943f4f55ce3SStefan Popa 944d9e8fd04SStefan Popa if (st->irq) { 945d9e8fd04SStefan Popa st->dready_trig = devm_iio_trigger_alloc(dev, 946f4f55ce3SStefan Popa "%s-dev%d", 947f4f55ce3SStefan Popa indio_dev->name, 948f4f55ce3SStefan Popa indio_dev->id); 949f4f55ce3SStefan Popa if (st->dready_trig == NULL) 950f4f55ce3SStefan Popa return -ENOMEM; 951f4f55ce3SStefan Popa 952f4f55ce3SStefan Popa st->dready_trig->ops = &adxl372_trigger_ops; 953d9e8fd04SStefan Popa st->dready_trig->dev.parent = dev; 954f4f55ce3SStefan Popa iio_trigger_set_drvdata(st->dready_trig, indio_dev); 955d9e8fd04SStefan Popa ret = devm_iio_trigger_register(dev, st->dready_trig); 956f4f55ce3SStefan Popa if (ret < 0) 957f4f55ce3SStefan Popa return ret; 958f4f55ce3SStefan Popa 959f4f55ce3SStefan Popa indio_dev->trig = iio_trigger_get(st->dready_trig); 960f4f55ce3SStefan Popa 961d9e8fd04SStefan Popa ret = devm_request_threaded_irq(dev, st->irq, 962f4f55ce3SStefan Popa iio_trigger_generic_data_rdy_poll, 963f4f55ce3SStefan Popa NULL, 964f4f55ce3SStefan Popa IRQF_TRIGGER_RISING | IRQF_ONESHOT, 965f4f55ce3SStefan Popa indio_dev->name, st->dready_trig); 966f4f55ce3SStefan Popa if (ret < 0) 967f4f55ce3SStefan Popa return ret; 968f4f55ce3SStefan Popa } 969f4f55ce3SStefan Popa 970d9e8fd04SStefan Popa return devm_iio_device_register(dev, indio_dev); 9714097da40SStefan Popa } 972d9e8fd04SStefan Popa EXPORT_SYMBOL_GPL(adxl372_probe); 9734097da40SStefan Popa 9744097da40SStefan Popa MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); 9754097da40SStefan Popa MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer driver"); 9764097da40SStefan Popa MODULE_LICENSE("GPL"); 977