// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2021 Analog Devices, Inc. * Author: Cosmin Tanislav */ #include #include #include #include #include #include #include #include #include #include #include #include #include #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; struct regulator_bulk_data regulators[2]; /* * Synchronize access to members of driver state, and ensure atomicity * of consecutive regmap operations. */ struct mutex lock; enum adxl367_odr odr; enum adxl367_range range; unsigned int act_threshold; unsigned int act_time_ms; unsigned int inact_threshold; unsigned int inact_time_ms; unsigned int fifo_set_size; unsigned int fifo_watermark; __be16 fifo_buf[ADXL367_FIFO_SIZE] __aligned(IIO_DMA_MINALIGN); __be16 sample_buf; u8 act_threshold_buf[2]; u8 inact_time_buf[2]; u8 status_buf[3]; }; static const unsigned int adxl367_threshold_h_reg_tbl[] = { [ADXL367_ACTIVITY] = ADXL367_REG_THRESH_ACT_H, [ADXL367_INACTIVITY] = ADXL367_REG_THRESH_INACT_H, }; static const unsigned int adxl367_act_en_shift_tbl[] = { [ADXL367_ACTIVITY] = 0, [ADXL367_INACTIVITY] = 2, }; static const unsigned int adxl367_act_int_mask_tbl[] = { [ADXL367_ACTIVITY] = ADXL367_INT_ACT_MASK, [ADXL367_INACTIVITY] = ADXL367_INT_INACT_MASK, }; static const int adxl367_samp_freq_tbl[][2] = { [ADXL367_ODR_12P5HZ] = {12, 500000}, [ADXL367_ODR_25HZ] = {25, 0}, [ADXL367_ODR_50HZ] = {50, 0}, [ADXL367_ODR_100HZ] = {100, 0}, [ADXL367_ODR_200HZ] = {200, 0}, [ADXL367_ODR_400HZ] = {400, 0}, }; /* (g * 2) * 9.80665 * 1000000 / (2^14 - 1) */ static const int adxl367_range_scale_tbl[][2] = { [ADXL367_2G_RANGE] = {0, 2394347}, [ADXL367_4G_RANGE] = {0, 4788695}, [ADXL367_8G_RANGE] = {0, 9577391}, }; static const int adxl367_range_scale_factor_tbl[] = { [ADXL367_2G_RANGE] = 1, [ADXL367_4G_RANGE] = 2, [ADXL367_8G_RANGE] = 4, }; enum { ADXL367_X_CHANNEL_INDEX, ADXL367_Y_CHANNEL_INDEX, ADXL367_Z_CHANNEL_INDEX, ADXL367_TEMP_CHANNEL_INDEX, ADXL367_EX_ADC_CHANNEL_INDEX }; #define ADXL367_X_CHANNEL_MASK BIT(ADXL367_X_CHANNEL_INDEX) #define ADXL367_Y_CHANNEL_MASK BIT(ADXL367_Y_CHANNEL_INDEX) #define ADXL367_Z_CHANNEL_MASK BIT(ADXL367_Z_CHANNEL_INDEX) #define ADXL367_TEMP_CHANNEL_MASK BIT(ADXL367_TEMP_CHANNEL_INDEX) #define ADXL367_EX_ADC_CHANNEL_MASK BIT(ADXL367_EX_ADC_CHANNEL_INDEX) static const enum adxl367_fifo_format adxl367_fifo_formats[] = { ADXL367_FIFO_FORMAT_X, ADXL367_FIFO_FORMAT_Y, ADXL367_FIFO_FORMAT_Z, ADXL367_FIFO_FORMAT_XT, ADXL367_FIFO_FORMAT_YT, ADXL367_FIFO_FORMAT_ZT, ADXL367_FIFO_FORMAT_XA, ADXL367_FIFO_FORMAT_YA, ADXL367_FIFO_FORMAT_ZA, ADXL367_FIFO_FORMAT_XYZ, ADXL367_FIFO_FORMAT_XYZT, ADXL367_FIFO_FORMAT_XYZA, }; static const unsigned long adxl367_channel_masks[] = { ADXL367_X_CHANNEL_MASK, ADXL367_Y_CHANNEL_MASK, ADXL367_Z_CHANNEL_MASK, ADXL367_X_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK, ADXL367_Y_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK, ADXL367_Z_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK, ADXL367_X_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK, ADXL367_Y_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK, ADXL367_Z_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK, ADXL367_X_CHANNEL_MASK | ADXL367_Y_CHANNEL_MASK | ADXL367_Z_CHANNEL_MASK, ADXL367_X_CHANNEL_MASK | ADXL367_Y_CHANNEL_MASK | ADXL367_Z_CHANNEL_MASK | ADXL367_TEMP_CHANNEL_MASK, ADXL367_X_CHANNEL_MASK | ADXL367_Y_CHANNEL_MASK | ADXL367_Z_CHANNEL_MASK | ADXL367_EX_ADC_CHANNEL_MASK, 0, }; static int adxl367_set_measure_en(struct adxl367_state *st, bool en) { enum adxl367_op_mode op_mode = en ? ADXL367_OP_MEASURE : ADXL367_OP_STANDBY; int ret; ret = regmap_update_bits(st->regmap, ADXL367_REG_POWER_CTL, ADXL367_POWER_CTL_MODE_MASK, FIELD_PREP(ADXL367_POWER_CTL_MODE_MASK, op_mode)); if (ret) return ret; /* * Wait for acceleration output to settle after entering * measure mode. */ if (en) msleep(100); return 0; } static void adxl367_scale_act_thresholds(struct adxl367_state *st, enum adxl367_range old_range, enum adxl367_range new_range) { st->act_threshold = st->act_threshold * adxl367_range_scale_factor_tbl[old_range] / adxl367_range_scale_factor_tbl[new_range]; st->inact_threshold = st->inact_threshold * adxl367_range_scale_factor_tbl[old_range] / adxl367_range_scale_factor_tbl[new_range]; } static int _adxl367_set_act_threshold(struct adxl367_state *st, enum adxl367_activity_type act, unsigned int threshold) { u8 reg = adxl367_threshold_h_reg_tbl[act]; int ret; if (threshold > ADXL367_THRESH_MAX) return -EINVAL; st->act_threshold_buf[0] = FIELD_PREP(ADXL367_THRESH_H_MASK, FIELD_GET(ADXL367_THRESH_VAL_H_MASK, threshold)); st->act_threshold_buf[1] = FIELD_PREP(ADXL367_THRESH_L_MASK, FIELD_GET(ADXL367_THRESH_VAL_L_MASK, threshold)); ret = regmap_bulk_write(st->regmap, reg, st->act_threshold_buf, sizeof(st->act_threshold_buf)); if (ret) return ret; if (act == ADXL367_ACTIVITY) st->act_threshold = threshold; else st->inact_threshold = threshold; return 0; } static int adxl367_set_act_threshold(struct adxl367_state *st, enum adxl367_activity_type act, unsigned int threshold) { int ret; mutex_lock(&st->lock); ret = adxl367_set_measure_en(st, false); if (ret) goto out; ret = _adxl367_set_act_threshold(st, act, threshold); if (ret) goto out; ret = adxl367_set_measure_en(st, true); out: mutex_unlock(&st->lock); return ret; } static int adxl367_set_act_proc_mode(struct adxl367_state *st, enum adxl367_act_proc_mode mode) { return regmap_update_bits(st->regmap, ADXL367_REG_ACT_INACT_CTL, ADXL367_ACT_LINKLOOP_MASK, FIELD_PREP(ADXL367_ACT_LINKLOOP_MASK, mode)); } static int adxl367_set_act_interrupt_en(struct adxl367_state *st, enum adxl367_activity_type act, bool en) { unsigned int mask = adxl367_act_int_mask_tbl[act]; return regmap_update_bits(st->regmap, ADXL367_REG_INT1_MAP, mask, en ? mask : 0); } static int adxl367_get_act_interrupt_en(struct adxl367_state *st, enum adxl367_activity_type act, bool *en) { unsigned int mask = adxl367_act_int_mask_tbl[act]; unsigned int val; int ret; ret = regmap_read(st->regmap, ADXL367_REG_INT1_MAP, &val); if (ret) return ret; *en = !!(val & mask); return 0; } static int adxl367_set_act_en(struct adxl367_state *st, enum adxl367_activity_type act, enum adxl367_act_en_mode en) { unsigned int ctl_shift = adxl367_act_en_shift_tbl[act]; return regmap_update_bits(st->regmap, ADXL367_REG_ACT_INACT_CTL, ADXL367_ACT_EN_MASK << ctl_shift, en << ctl_shift); } static int adxl367_set_fifo_watermark_interrupt_en(struct adxl367_state *st, bool en) { return regmap_update_bits(st->regmap, ADXL367_REG_INT1_MAP, ADXL367_INT_FIFO_WATERMARK_MASK, en ? ADXL367_INT_FIFO_WATERMARK_MASK : 0); } static int adxl367_get_fifo_mode(struct adxl367_state *st, enum adxl367_fifo_mode *fifo_mode) { unsigned int val; int ret; ret = regmap_read(st->regmap, ADXL367_REG_FIFO_CTL, &val); if (ret) return ret; *fifo_mode = FIELD_GET(ADXL367_FIFO_CTL_MODE_MASK, val); return 0; } static int adxl367_set_fifo_mode(struct adxl367_state *st, enum adxl367_fifo_mode fifo_mode) { return regmap_update_bits(st->regmap, ADXL367_REG_FIFO_CTL, ADXL367_FIFO_CTL_MODE_MASK, FIELD_PREP(ADXL367_FIFO_CTL_MODE_MASK, fifo_mode)); } static int adxl367_set_fifo_format(struct adxl367_state *st, enum adxl367_fifo_format fifo_format) { return regmap_update_bits(st->regmap, ADXL367_REG_FIFO_CTL, ADXL367_FIFO_CTL_FORMAT_MASK, FIELD_PREP(ADXL367_FIFO_CTL_FORMAT_MASK, fifo_format)); } static int adxl367_set_fifo_watermark(struct adxl367_state *st, unsigned int fifo_watermark) { unsigned int fifo_samples = fifo_watermark * st->fifo_set_size; unsigned int fifo_samples_h, fifo_samples_l; int ret; if (fifo_samples > ADXL367_FIFO_MAX_WATERMARK) fifo_samples = ADXL367_FIFO_MAX_WATERMARK; fifo_samples /= st->fifo_set_size; fifo_samples_h = FIELD_PREP(ADXL367_SAMPLES_H_MASK, FIELD_GET(ADXL367_SAMPLES_VAL_H_MASK, fifo_samples)); fifo_samples_l = FIELD_PREP(ADXL367_SAMPLES_L_MASK, FIELD_GET(ADXL367_SAMPLES_VAL_L_MASK, fifo_samples)); ret = regmap_update_bits(st->regmap, ADXL367_REG_FIFO_CTL, ADXL367_SAMPLES_H_MASK, fifo_samples_h); if (ret) return ret; ret = regmap_update_bits(st->regmap, ADXL367_REG_FIFO_SAMPLES, ADXL367_SAMPLES_L_MASK, fifo_samples_l); if (ret) return ret; st->fifo_watermark = fifo_watermark; return 0; } static int adxl367_set_range(struct iio_dev *indio_dev, enum adxl367_range range) { struct adxl367_state *st = iio_priv(indio_dev); int ret; ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; mutex_lock(&st->lock); ret = adxl367_set_measure_en(st, false); if (ret) goto out; ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL, ADXL367_FILTER_CTL_RANGE_MASK, FIELD_PREP(ADXL367_FILTER_CTL_RANGE_MASK, range)); if (ret) goto out; adxl367_scale_act_thresholds(st, st->range, range); /* Activity thresholds depend on range */ ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY, st->act_threshold); if (ret) goto out; ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY, st->inact_threshold); if (ret) goto out; ret = adxl367_set_measure_en(st, true); if (ret) goto out; st->range = range; out: mutex_unlock(&st->lock); iio_device_release_direct_mode(indio_dev); return ret; } static int adxl367_time_ms_to_samples(struct adxl367_state *st, unsigned int ms) { int freq_hz = adxl367_samp_freq_tbl[st->odr][0]; int freq_microhz = adxl367_samp_freq_tbl[st->odr][1]; /* Scale to decihertz to prevent precision loss in 12.5Hz case. */ int freq_dhz = freq_hz * 10 + freq_microhz / 100000; return DIV_ROUND_CLOSEST(ms * freq_dhz, 10000); } static int _adxl367_set_act_time_ms(struct adxl367_state *st, unsigned int ms) { unsigned int val = adxl367_time_ms_to_samples(st, ms); int ret; if (val > ADXL367_TIME_ACT_MAX) val = ADXL367_TIME_ACT_MAX; ret = regmap_write(st->regmap, ADXL367_REG_TIME_ACT, val); if (ret) return ret; st->act_time_ms = ms; return 0; } static int _adxl367_set_inact_time_ms(struct adxl367_state *st, unsigned int ms) { unsigned int val = adxl367_time_ms_to_samples(st, ms); int ret; if (val > ADXL367_TIME_INACT_MAX) val = ADXL367_TIME_INACT_MAX; st->inact_time_buf[0] = FIELD_PREP(ADXL367_TIME_INACT_H_MASK, FIELD_GET(ADXL367_TIME_INACT_VAL_H_MASK, val)); st->inact_time_buf[1] = FIELD_PREP(ADXL367_TIME_INACT_L_MASK, FIELD_GET(ADXL367_TIME_INACT_VAL_L_MASK, val)); ret = regmap_bulk_write(st->regmap, ADXL367_REG_TIME_INACT_H, st->inact_time_buf, sizeof(st->inact_time_buf)); if (ret) return ret; st->inact_time_ms = ms; return 0; } static int adxl367_set_act_time_ms(struct adxl367_state *st, enum adxl367_activity_type act, unsigned int ms) { int ret; mutex_lock(&st->lock); ret = adxl367_set_measure_en(st, false); if (ret) goto out; if (act == ADXL367_ACTIVITY) ret = _adxl367_set_act_time_ms(st, ms); else ret = _adxl367_set_inact_time_ms(st, ms); if (ret) goto out; ret = adxl367_set_measure_en(st, true); out: mutex_unlock(&st->lock); return ret; } static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr) { int ret; ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL, ADXL367_FILTER_CTL_ODR_MASK, FIELD_PREP(ADXL367_FILTER_CTL_ODR_MASK, odr)); if (ret) return ret; /* Activity timers depend on ODR */ ret = _adxl367_set_act_time_ms(st, st->act_time_ms); if (ret) return ret; ret = _adxl367_set_inact_time_ms(st, st->inact_time_ms); if (ret) return ret; st->odr = odr; return 0; } static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr) { struct adxl367_state *st = iio_priv(indio_dev); int ret; ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; mutex_lock(&st->lock); ret = adxl367_set_measure_en(st, false); if (ret) goto out; ret = _adxl367_set_odr(st, odr); if (ret) goto out; ret = adxl367_set_measure_en(st, true); out: mutex_unlock(&st->lock); iio_device_release_direct_mode(indio_dev); return ret; } static int adxl367_set_temp_adc_en(struct adxl367_state *st, unsigned int reg, bool en) { return regmap_update_bits(st->regmap, reg, ADXL367_ADC_EN_MASK, en ? ADXL367_ADC_EN_MASK : 0); } static int adxl367_set_temp_adc_reg_en(struct adxl367_state *st, unsigned int reg, bool en) { int ret; switch (reg) { case ADXL367_REG_TEMP_DATA_H: ret = adxl367_set_temp_adc_en(st, ADXL367_REG_TEMP_CTL, en); break; case ADXL367_REG_EX_ADC_DATA_H: ret = adxl367_set_temp_adc_en(st, ADXL367_REG_ADC_CTL, en); break; default: return 0; } if (ret) return ret; if (en) msleep(100); return 0; } static int adxl367_set_temp_adc_mask_en(struct adxl367_state *st, const unsigned long *active_scan_mask, bool en) { if (*active_scan_mask & ADXL367_TEMP_CHANNEL_MASK) return adxl367_set_temp_adc_en(st, ADXL367_REG_TEMP_CTL, en); else if (*active_scan_mask & ADXL367_EX_ADC_CHANNEL_MASK) return adxl367_set_temp_adc_en(st, ADXL367_REG_ADC_CTL, en); return 0; } static int adxl367_find_odr(struct adxl367_state *st, int val, int val2, enum adxl367_odr *odr) { size_t size = ARRAY_SIZE(adxl367_samp_freq_tbl); int i; for (i = 0; i < size; i++) if (val == adxl367_samp_freq_tbl[i][0] && val2 == adxl367_samp_freq_tbl[i][1]) break; if (i == size) return -EINVAL; *odr = i; return 0; } static int adxl367_find_range(struct adxl367_state *st, int val, int val2, enum adxl367_range *range) { size_t size = ARRAY_SIZE(adxl367_range_scale_tbl); int i; for (i = 0; i < size; i++) if (val == adxl367_range_scale_tbl[i][0] && val2 == adxl367_range_scale_tbl[i][1]) break; if (i == size) return -EINVAL; *range = i; return 0; } static int adxl367_read_sample(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { struct adxl367_state *st = iio_priv(indio_dev); u16 sample; int ret; ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; mutex_lock(&st->lock); ret = adxl367_set_temp_adc_reg_en(st, chan->address, true); if (ret) goto out; ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf, sizeof(st->sample_buf)); if (ret) goto out; sample = FIELD_GET(ADXL367_DATA_MASK, be16_to_cpu(st->sample_buf)); *val = sign_extend32(sample, chan->scan_type.realbits - 1); ret = adxl367_set_temp_adc_reg_en(st, chan->address, false); out: mutex_unlock(&st->lock); iio_device_release_direct_mode(indio_dev); return ret ?: IIO_VAL_INT; } static int adxl367_get_status(struct adxl367_state *st, u8 *status, u16 *fifo_entries) { int ret; /* Read STATUS, FIFO_ENT_L and FIFO_ENT_H */ ret = regmap_bulk_read(st->regmap, ADXL367_REG_STATUS, st->status_buf, sizeof(st->status_buf)); if (ret) return ret; st->status_buf[2] &= ADXL367_FIFO_ENT_H_MASK; *status = st->status_buf[0]; *fifo_entries = get_unaligned_le16(&st->status_buf[1]); return 0; } static bool adxl367_push_event(struct iio_dev *indio_dev, u8 status) { unsigned int ev_dir; if (FIELD_GET(ADXL367_STATUS_ACT_MASK, status)) ev_dir = IIO_EV_DIR_RISING; else if (FIELD_GET(ADXL367_STATUS_INACT_MASK, status)) ev_dir = IIO_EV_DIR_FALLING; else return false; iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z, IIO_EV_TYPE_THRESH, ev_dir), iio_get_time_ns(indio_dev)); return true; } static bool adxl367_push_fifo_data(struct iio_dev *indio_dev, u8 status, u16 fifo_entries) { struct adxl367_state *st = iio_priv(indio_dev); int ret; int i; if (!FIELD_GET(ADXL367_STATUS_FIFO_FULL_MASK, status)) return false; fifo_entries -= fifo_entries % st->fifo_set_size; ret = st->ops->read_fifo(st->context, st->fifo_buf, fifo_entries); if (ret) { dev_err(st->dev, "Failed to read FIFO: %d\n", ret); return true; } for (i = 0; i < fifo_entries; i += st->fifo_set_size) iio_push_to_buffers(indio_dev, &st->fifo_buf[i]); return true; } static irqreturn_t adxl367_irq_handler(int irq, void *private) { struct iio_dev *indio_dev = private; struct adxl367_state *st = iio_priv(indio_dev); u16 fifo_entries; bool handled; u8 status; int ret; ret = adxl367_get_status(st, &status, &fifo_entries); if (ret) return IRQ_NONE; handled = adxl367_push_event(indio_dev, status); handled |= adxl367_push_fifo_data(indio_dev, status, fifo_entries); return handled ? IRQ_HANDLED : IRQ_NONE; } static int adxl367_reg_access(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval) { struct adxl367_state *st = iio_priv(indio_dev); if (readval) return regmap_read(st->regmap, reg, readval); else return regmap_write(st->regmap, reg, writeval); } static int adxl367_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long info) { struct adxl367_state *st = iio_priv(indio_dev); switch (info) { case IIO_CHAN_INFO_RAW: return adxl367_read_sample(indio_dev, chan, val); case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ACCEL: mutex_lock(&st->lock); *val = adxl367_range_scale_tbl[st->range][0]; *val2 = adxl367_range_scale_tbl[st->range][1]; mutex_unlock(&st->lock); return IIO_VAL_INT_PLUS_NANO; case IIO_TEMP: *val = 1000; *val2 = ADXL367_TEMP_PER_C; return IIO_VAL_FRACTIONAL; case IIO_VOLTAGE: *val = ADXL367_VOLTAGE_MAX_MV; *val2 = ADXL367_VOLTAGE_MAX_RAW; return IIO_VAL_FRACTIONAL; default: return -EINVAL; } case IIO_CHAN_INFO_OFFSET: switch (chan->type) { case IIO_TEMP: *val = 25 * ADXL367_TEMP_PER_C - ADXL367_TEMP_25C; return IIO_VAL_INT; case IIO_VOLTAGE: *val = ADXL367_VOLTAGE_OFFSET; return IIO_VAL_INT; default: return -EINVAL; } case IIO_CHAN_INFO_SAMP_FREQ: mutex_lock(&st->lock); *val = adxl367_samp_freq_tbl[st->odr][0]; *val2 = adxl367_samp_freq_tbl[st->odr][1]; mutex_unlock(&st->lock); return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; } } static int adxl367_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long info) { struct adxl367_state *st = iio_priv(indio_dev); int ret; switch (info) { case IIO_CHAN_INFO_SAMP_FREQ: { enum adxl367_odr odr; ret = adxl367_find_odr(st, val, val2, &odr); if (ret) return ret; return adxl367_set_odr(indio_dev, odr); } case IIO_CHAN_INFO_SCALE: { enum adxl367_range range; ret = adxl367_find_range(st, val, val2, &range); if (ret) return ret; return adxl367_set_range(indio_dev, range); } default: return -EINVAL; } } static int adxl367_write_raw_get_fmt(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, long info) { switch (info) { case IIO_CHAN_INFO_SCALE: if (chan->type != IIO_ACCEL) return -EINVAL; return IIO_VAL_INT_PLUS_NANO; default: return IIO_VAL_INT_PLUS_MICRO; } } static int adxl367_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, const int **vals, int *type, int *length, long info) { switch (info) { case IIO_CHAN_INFO_SCALE: if (chan->type != IIO_ACCEL) return -EINVAL; *vals = (int *)adxl367_range_scale_tbl; *type = IIO_VAL_INT_PLUS_NANO; *length = ARRAY_SIZE(adxl367_range_scale_tbl) * 2; return IIO_AVAIL_LIST; case IIO_CHAN_INFO_SAMP_FREQ: *vals = (int *)adxl367_samp_freq_tbl; *type = IIO_VAL_INT_PLUS_MICRO; *length = ARRAY_SIZE(adxl367_samp_freq_tbl) * 2; return IIO_AVAIL_LIST; default: return -EINVAL; } } static int adxl367_read_event_value(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, enum iio_event_info info, int *val, int *val2) { struct adxl367_state *st = iio_priv(indio_dev); switch (info) { case IIO_EV_INFO_VALUE: { switch (dir) { case IIO_EV_DIR_RISING: mutex_lock(&st->lock); *val = st->act_threshold; mutex_unlock(&st->lock); return IIO_VAL_INT; case IIO_EV_DIR_FALLING: mutex_lock(&st->lock); *val = st->inact_threshold; mutex_unlock(&st->lock); return IIO_VAL_INT; default: return -EINVAL; } } case IIO_EV_INFO_PERIOD: switch (dir) { case IIO_EV_DIR_RISING: mutex_lock(&st->lock); *val = st->act_time_ms; mutex_unlock(&st->lock); *val2 = 1000; return IIO_VAL_FRACTIONAL; case IIO_EV_DIR_FALLING: mutex_lock(&st->lock); *val = st->inact_time_ms; mutex_unlock(&st->lock); *val2 = 1000; return IIO_VAL_FRACTIONAL; default: return -EINVAL; } default: return -EINVAL; } } static int adxl367_write_event_value(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, enum iio_event_info info, int val, int val2) { struct adxl367_state *st = iio_priv(indio_dev); switch (info) { case IIO_EV_INFO_VALUE: if (val < 0) return -EINVAL; switch (dir) { case IIO_EV_DIR_RISING: return adxl367_set_act_threshold(st, ADXL367_ACTIVITY, val); case IIO_EV_DIR_FALLING: return adxl367_set_act_threshold(st, ADXL367_INACTIVITY, val); default: return -EINVAL; } case IIO_EV_INFO_PERIOD: if (val < 0) return -EINVAL; val = val * 1000 + DIV_ROUND_UP(val2, 1000); switch (dir) { case IIO_EV_DIR_RISING: return adxl367_set_act_time_ms(st, ADXL367_ACTIVITY, val); case IIO_EV_DIR_FALLING: return adxl367_set_act_time_ms(st, ADXL367_INACTIVITY, val); default: return -EINVAL; } default: return -EINVAL; } } static int adxl367_read_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir) { struct adxl367_state *st = iio_priv(indio_dev); bool en; int ret; switch (dir) { case IIO_EV_DIR_RISING: ret = adxl367_get_act_interrupt_en(st, ADXL367_ACTIVITY, &en); return ret ?: en; case IIO_EV_DIR_FALLING: ret = adxl367_get_act_interrupt_en(st, ADXL367_INACTIVITY, &en); return ret ?: en; default: return -EINVAL; } } static int adxl367_write_event_config(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, enum iio_event_type type, enum iio_event_direction dir, int state) { struct adxl367_state *st = iio_priv(indio_dev); enum adxl367_activity_type act; int ret; switch (dir) { case IIO_EV_DIR_RISING: act = ADXL367_ACTIVITY; break; case IIO_EV_DIR_FALLING: act = ADXL367_INACTIVITY; break; default: return -EINVAL; } ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; mutex_lock(&st->lock); ret = adxl367_set_measure_en(st, false); if (ret) goto out; ret = adxl367_set_act_interrupt_en(st, act, state); if (ret) goto out; ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED : ADXL367_ACT_DISABLED); if (ret) goto out; ret = adxl367_set_measure_en(st, true); out: mutex_unlock(&st->lock); iio_device_release_direct_mode(indio_dev); return ret; } static ssize_t adxl367_get_fifo_enabled(struct device *dev, struct device_attribute *attr, char *buf) { struct adxl367_state *st = iio_priv(dev_to_iio_dev(dev)); enum adxl367_fifo_mode fifo_mode; int ret; ret = adxl367_get_fifo_mode(st, &fifo_mode); if (ret) return ret; return sysfs_emit(buf, "%d\n", fifo_mode != ADXL367_FIFO_MODE_DISABLED); } static ssize_t adxl367_get_fifo_watermark(struct device *dev, struct device_attribute *attr, char *buf) { struct adxl367_state *st = iio_priv(dev_to_iio_dev(dev)); unsigned int fifo_watermark; mutex_lock(&st->lock); fifo_watermark = st->fifo_watermark; mutex_unlock(&st->lock); return sysfs_emit(buf, "%d\n", fifo_watermark); } static ssize_t hwfifo_watermark_min_show(struct device *dev, struct device_attribute *attr, char *buf) { return sysfs_emit(buf, "%s\n", "1"); } static ssize_t hwfifo_watermark_max_show(struct device *dev, struct device_attribute *attr, char *buf) { return sysfs_emit(buf, "%s\n", __stringify(ADXL367_FIFO_MAX_WATERMARK)); } static IIO_DEVICE_ATTR_RO(hwfifo_watermark_min, 0); static IIO_DEVICE_ATTR_RO(hwfifo_watermark_max, 0); 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 attribute *adxl367_fifo_attributes[] = { &iio_dev_attr_hwfifo_watermark_min.dev_attr.attr, &iio_dev_attr_hwfifo_watermark_max.dev_attr.attr, &iio_dev_attr_hwfifo_watermark.dev_attr.attr, &iio_dev_attr_hwfifo_enabled.dev_attr.attr, NULL, }; static int adxl367_set_watermark(struct iio_dev *indio_dev, unsigned int val) { struct adxl367_state *st = iio_priv(indio_dev); int ret; if (val > ADXL367_FIFO_MAX_WATERMARK) return -EINVAL; mutex_lock(&st->lock); ret = adxl367_set_measure_en(st, false); if (ret) goto out; ret = adxl367_set_fifo_watermark(st, val); if (ret) goto out; ret = adxl367_set_measure_en(st, true); out: mutex_unlock(&st->lock); return ret; } static bool adxl367_find_mask_fifo_format(const unsigned long *scan_mask, enum adxl367_fifo_format *fifo_format) { size_t size = ARRAY_SIZE(adxl367_fifo_formats); int i; for (i = 0; i < size; i++) if (*scan_mask == adxl367_channel_masks[i]) break; if (i == size) return false; *fifo_format = adxl367_fifo_formats[i]; return true; } static int adxl367_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *active_scan_mask) { struct adxl367_state *st = iio_priv(indio_dev); enum adxl367_fifo_format fifo_format; int ret; if (!adxl367_find_mask_fifo_format(active_scan_mask, &fifo_format)) return -EINVAL; mutex_lock(&st->lock); ret = adxl367_set_measure_en(st, false); if (ret) goto out; ret = adxl367_set_fifo_format(st, fifo_format); if (ret) goto out; ret = adxl367_set_measure_en(st, true); if (ret) goto out; st->fifo_set_size = bitmap_weight(active_scan_mask, indio_dev->masklength); out: mutex_unlock(&st->lock); return ret; } static int adxl367_buffer_postenable(struct iio_dev *indio_dev) { struct adxl367_state *st = iio_priv(indio_dev); int ret; mutex_lock(&st->lock); ret = adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask, true); if (ret) goto out; ret = adxl367_set_measure_en(st, false); if (ret) goto out; ret = adxl367_set_fifo_watermark_interrupt_en(st, true); if (ret) goto out; ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_STREAM); if (ret) goto out; ret = adxl367_set_measure_en(st, true); out: mutex_unlock(&st->lock); return ret; } static int adxl367_buffer_predisable(struct iio_dev *indio_dev) { struct adxl367_state *st = iio_priv(indio_dev); int ret; mutex_lock(&st->lock); ret = adxl367_set_measure_en(st, false); if (ret) goto out; ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_DISABLED); if (ret) goto out; ret = adxl367_set_fifo_watermark_interrupt_en(st, false); if (ret) goto out; ret = adxl367_set_measure_en(st, true); if (ret) goto out; ret = adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask, false); out: mutex_unlock(&st->lock); return ret; } static const struct iio_buffer_setup_ops adxl367_buffer_ops = { .postenable = adxl367_buffer_postenable, .predisable = adxl367_buffer_predisable, }; static const struct iio_info adxl367_info = { .read_raw = adxl367_read_raw, .write_raw = adxl367_write_raw, .write_raw_get_fmt = adxl367_write_raw_get_fmt, .read_avail = adxl367_read_avail, .read_event_config = adxl367_read_event_config, .write_event_config = adxl367_write_event_config, .read_event_value = adxl367_read_event_value, .write_event_value = adxl367_write_event_value, .debugfs_reg_access = adxl367_reg_access, .hwfifo_set_watermark = adxl367_set_watermark, .update_scan_mode = adxl367_update_scan_mode, }; static const struct iio_event_spec adxl367_events[] = { { .type = IIO_EV_TYPE_MAG_REFERENCED, .dir = IIO_EV_DIR_RISING, .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD) | BIT(IIO_EV_INFO_VALUE), }, { .type = IIO_EV_TYPE_MAG_REFERENCED, .dir = IIO_EV_DIR_FALLING, .mask_shared_by_type = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD) | BIT(IIO_EV_INFO_VALUE), }, }; #define ADXL367_ACCEL_CHANNEL(index, reg, axis) { \ .type = IIO_ACCEL, \ .address = (reg), \ .modified = 1, \ .channel2 = IIO_MOD_##axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .info_mask_shared_by_all_available = \ BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .event_spec = adxl367_events, \ .num_event_specs = ARRAY_SIZE(adxl367_events), \ .scan_index = (index), \ .scan_type = { \ .sign = 's', \ .realbits = 14, \ .storagebits = 16, \ .endianness = IIO_BE, \ }, \ } #define ADXL367_CHANNEL(index, reg, _type) { \ .type = (_type), \ .address = (reg), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_OFFSET) | \ BIT(IIO_CHAN_INFO_SCALE), \ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ .scan_index = (index), \ .scan_type = { \ .sign = 's', \ .realbits = 14, \ .storagebits = 16, \ .endianness = IIO_BE, \ }, \ } static const struct iio_chan_spec adxl367_channels[] = { ADXL367_ACCEL_CHANNEL(ADXL367_X_CHANNEL_INDEX, ADXL367_REG_X_DATA_H, X), ADXL367_ACCEL_CHANNEL(ADXL367_Y_CHANNEL_INDEX, ADXL367_REG_Y_DATA_H, Y), ADXL367_ACCEL_CHANNEL(ADXL367_Z_CHANNEL_INDEX, ADXL367_REG_Z_DATA_H, Z), ADXL367_CHANNEL(ADXL367_TEMP_CHANNEL_INDEX, ADXL367_REG_TEMP_DATA_H, IIO_TEMP), ADXL367_CHANNEL(ADXL367_EX_ADC_CHANNEL_INDEX, ADXL367_REG_EX_ADC_DATA_H, IIO_VOLTAGE), }; static int adxl367_verify_devid(struct adxl367_state *st) { unsigned int val; int ret; ret = regmap_read(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); } static void adxl367_disable_regulators(void *data) { struct adxl367_state *st = data; regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators); } int adxl367_probe(struct device *dev, const struct adxl367_ops *ops, void *context, struct regmap *regmap, int irq) { 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; st->regulators[0].supply = "vdd"; st->regulators[1].supply = "vddio"; ret = devm_regulator_bulk_get(st->dev, ARRAY_SIZE(st->regulators), st->regulators); if (ret) return dev_err_probe(st->dev, ret, "Failed to get regulators\n"); ret = regulator_bulk_enable(ARRAY_SIZE(st->regulators), st->regulators); if (ret) return dev_err_probe(st->dev, ret, "Failed to enable regulators\n"); ret = devm_add_action_or_reset(st->dev, adxl367_disable_regulators, st); if (ret) return dev_err_probe(st->dev, ret, "Failed to add regulators disable action\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 "); MODULE_DESCRIPTION("Analog Devices ADXL367 3-axis accelerometer driver"); MODULE_LICENSE("GPL");