Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: accel: Add driver support for ADXL355

ADXL355 is a 3-axis MEMS Accelerometer. It offers low noise density,
low 0g offset drift, low power with selectable measurement ranges.
It also features programmable high-pass and low-pass filters.

Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/adxl354_adxl355.pdf
Reviewed-by: Alexandru Ardelean <ardeleanalex@gmail.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Puranjay Mohan <puranjay12@gmail.com>
Link: https://lore.kernel.org/r/20210811073027.124619-3-puranjay12@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Puranjay Mohan and committed by
Jonathan Cameron
12ed2786 bf43a71a

+802
+10
MAINTAINERS
··· 598 598 F: Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml 599 599 F: drivers/input/misc/adxl34x.c 600 600 601 + ADXL355 THREE-AXIS DIGITAL ACCELEROMETER DRIVER 602 + M: Puranjay Mohan <puranjay12@gmail.com> 603 + L: linux-iio@vger.kernel.org 604 + S: Supported 605 + F: Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml 606 + F: drivers/iio/accel/adxl355.h 607 + F: drivers/iio/accel/adxl355_core.c 608 + F: drivers/iio/accel/adxl355_i2c.c 609 + F: drivers/iio/accel/adxl355_spi.c 610 + 601 611 ADXL372 THREE-AXIS DIGITAL ACCELEROMETER DRIVER 602 612 M: Michael Hennerich <michael.hennerich@analog.com> 603 613 S: Supported
+29
drivers/iio/accel/Kconfig
··· 61 61 will be called adxl345_spi and you will also get adxl345_core 62 62 for the core module. 63 63 64 + config ADXL355 65 + tristate 66 + 67 + config ADXL355_I2C 68 + tristate "Analog Devices ADXL355 3-Axis Digital Accelerometer I2C Driver" 69 + depends on I2C 70 + select ADXL355 71 + select REGMAP_I2C 72 + help 73 + Say Y here if you want to build i2c support for the Analog Devices 74 + ADXL355 3-axis digital accelerometer. 75 + 76 + To compile this driver as a module, choose M here: the module 77 + will be called adxl355_i2c and you will also get adxl355_core 78 + for the core module. 79 + 80 + config ADXL355_SPI 81 + tristate "Analog Devices ADXL355 3-Axis Digital Accelerometer SPI Driver" 82 + depends on SPI 83 + select ADXL355 84 + select REGMAP_SPI 85 + help 86 + Say Y here if you want to build spi support for the Analog Devices 87 + ADXL355 3-axis digital accelerometer. 88 + 89 + To compile this driver as a module, choose M here: the module 90 + will be called adxl355_spi and you will also get adxl355_core 91 + for the core module. 92 + 64 93 config ADXL372 65 94 tristate 66 95 select IIO_BUFFER
+3
drivers/iio/accel/Makefile
··· 9 9 obj-$(CONFIG_ADXL345) += adxl345_core.o 10 10 obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o 11 11 obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o 12 + obj-$(CONFIG_ADXL355) += adxl355_core.o 13 + obj-$(CONFIG_ADXL355_I2C) += adxl355_i2c.o 14 + obj-$(CONFIG_ADXL355_SPI) += adxl355_spi.o 12 15 obj-$(CONFIG_ADXL372) += adxl372.o 13 16 obj-$(CONFIG_ADXL372_I2C) += adxl372_i2c.o 14 17 obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o
+21
drivers/iio/accel/adxl355.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * ADXL355 3-Axis Digital Accelerometer 4 + * 5 + * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com> 6 + */ 7 + 8 + #ifndef _ADXL355_H_ 9 + #define _ADXL355_H_ 10 + 11 + #include <linux/regmap.h> 12 + 13 + struct device; 14 + 15 + extern const struct regmap_access_table adxl355_readable_regs_tbl; 16 + extern const struct regmap_access_table adxl355_writeable_regs_tbl; 17 + 18 + int adxl355_core_probe(struct device *dev, struct regmap *regmap, 19 + const char *name); 20 + 21 + #endif /* _ADXL355_H_ */
+612
drivers/iio/accel/adxl355_core.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * ADXL355 3-Axis Digital Accelerometer IIO core driver 4 + * 5 + * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com> 6 + * 7 + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/adxl354_adxl355.pdf 8 + */ 9 + 10 + #include <linux/bits.h> 11 + #include <linux/bitfield.h> 12 + #include <linux/iio/iio.h> 13 + #include <linux/limits.h> 14 + #include <linux/math64.h> 15 + #include <linux/module.h> 16 + #include <linux/mod_devicetable.h> 17 + #include <linux/regmap.h> 18 + #include <asm/unaligned.h> 19 + 20 + #include "adxl355.h" 21 + 22 + /* ADXL355 Register Definitions */ 23 + #define ADXL355_DEVID_AD_REG 0x00 24 + #define ADXL355_DEVID_MST_REG 0x01 25 + #define ADXL355_PARTID_REG 0x02 26 + #define ADXL355_STATUS_REG 0x04 27 + #define ADXL355_FIFO_ENTRIES_REG 0x05 28 + #define ADXL355_TEMP2_REG 0x06 29 + #define ADXL355_XDATA3_REG 0x08 30 + #define ADXL355_YDATA3_REG 0x0B 31 + #define ADXL355_ZDATA3_REG 0x0E 32 + #define ADXL355_FIFO_DATA_REG 0x11 33 + #define ADXL355_OFFSET_X_H_REG 0x1E 34 + #define ADXL355_OFFSET_Y_H_REG 0x20 35 + #define ADXL355_OFFSET_Z_H_REG 0x22 36 + #define ADXL355_ACT_EN_REG 0x24 37 + #define ADXL355_ACT_THRESH_H_REG 0x25 38 + #define ADXL355_ACT_THRESH_L_REG 0x26 39 + #define ADXL355_ACT_COUNT_REG 0x27 40 + #define ADXL355_FILTER_REG 0x28 41 + #define ADXL355_FILTER_ODR_MSK GENMASK(3, 0) 42 + #define ADXL355_FILTER_HPF_MSK GENMASK(6, 4) 43 + #define ADXL355_FIFO_SAMPLES_REG 0x29 44 + #define ADXL355_INT_MAP_REG 0x2A 45 + #define ADXL355_SYNC_REG 0x2B 46 + #define ADXL355_RANGE_REG 0x2C 47 + #define ADXL355_POWER_CTL_REG 0x2D 48 + #define ADXL355_POWER_CTL_MODE_MSK GENMASK(1, 0) 49 + #define ADXL355_SELF_TEST_REG 0x2E 50 + #define ADXL355_RESET_REG 0x2F 51 + 52 + #define ADXL355_DEVID_AD_VAL 0xAD 53 + #define ADXL355_DEVID_MST_VAL 0x1D 54 + #define ADXL355_PARTID_VAL 0xED 55 + #define ADXL355_RESET_CODE 0x52 56 + 57 + #define MEGA 1000000UL 58 + #define TERA 1000000000000ULL 59 + 60 + static const struct regmap_range adxl355_read_reg_range[] = { 61 + regmap_reg_range(ADXL355_DEVID_AD_REG, ADXL355_FIFO_DATA_REG), 62 + regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_SELF_TEST_REG), 63 + }; 64 + 65 + const struct regmap_access_table adxl355_readable_regs_tbl = { 66 + .yes_ranges = adxl355_read_reg_range, 67 + .n_yes_ranges = ARRAY_SIZE(adxl355_read_reg_range), 68 + }; 69 + EXPORT_SYMBOL_GPL(adxl355_readable_regs_tbl); 70 + 71 + static const struct regmap_range adxl355_write_reg_range[] = { 72 + regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_RESET_REG), 73 + }; 74 + 75 + const struct regmap_access_table adxl355_writeable_regs_tbl = { 76 + .yes_ranges = adxl355_write_reg_range, 77 + .n_yes_ranges = ARRAY_SIZE(adxl355_write_reg_range), 78 + }; 79 + EXPORT_SYMBOL_GPL(adxl355_writeable_regs_tbl); 80 + 81 + enum adxl355_op_mode { 82 + ADXL355_MEASUREMENT, 83 + ADXL355_STANDBY, 84 + ADXL355_TEMP_OFF, 85 + }; 86 + 87 + enum adxl355_odr { 88 + ADXL355_ODR_4000HZ, 89 + ADXL355_ODR_2000HZ, 90 + ADXL355_ODR_1000HZ, 91 + ADXL355_ODR_500HZ, 92 + ADXL355_ODR_250HZ, 93 + ADXL355_ODR_125HZ, 94 + ADXL355_ODR_62_5HZ, 95 + ADXL355_ODR_31_25HZ, 96 + ADXL355_ODR_15_625HZ, 97 + ADXL355_ODR_7_813HZ, 98 + ADXL355_ODR_3_906HZ, 99 + }; 100 + 101 + enum adxl355_hpf_3db { 102 + ADXL355_HPF_OFF, 103 + ADXL355_HPF_24_7, 104 + ADXL355_HPF_6_2084, 105 + ADXL355_HPF_1_5545, 106 + ADXL355_HPF_0_3862, 107 + ADXL355_HPF_0_0954, 108 + ADXL355_HPF_0_0238, 109 + }; 110 + 111 + static const int adxl355_odr_table[][2] = { 112 + [0] = {4000, 0}, 113 + [1] = {2000, 0}, 114 + [2] = {1000, 0}, 115 + [3] = {500, 0}, 116 + [4] = {250, 0}, 117 + [5] = {125, 0}, 118 + [6] = {62, 500000}, 119 + [7] = {31, 250000}, 120 + [8] = {15, 625000}, 121 + [9] = {7, 813000}, 122 + [10] = {3, 906000}, 123 + }; 124 + 125 + static const int adxl355_hpf_3db_multipliers[] = { 126 + 0, 127 + 247000, 128 + 62084, 129 + 15545, 130 + 3862, 131 + 954, 132 + 238, 133 + }; 134 + 135 + enum adxl355_chans { 136 + chan_x, chan_y, chan_z, 137 + }; 138 + 139 + struct adxl355_chan_info { 140 + u8 data_reg; 141 + u8 offset_reg; 142 + }; 143 + 144 + static const struct adxl355_chan_info adxl355_chans[] = { 145 + [chan_x] = { 146 + .data_reg = ADXL355_XDATA3_REG, 147 + .offset_reg = ADXL355_OFFSET_X_H_REG 148 + }, 149 + [chan_y] = { 150 + .data_reg = ADXL355_YDATA3_REG, 151 + .offset_reg = ADXL355_OFFSET_Y_H_REG 152 + }, 153 + [chan_z] = { 154 + .data_reg = ADXL355_ZDATA3_REG, 155 + .offset_reg = ADXL355_OFFSET_Z_H_REG 156 + }, 157 + }; 158 + 159 + struct adxl355_data { 160 + struct regmap *regmap; 161 + struct device *dev; 162 + struct mutex lock; /* lock to protect op_mode */ 163 + enum adxl355_op_mode op_mode; 164 + enum adxl355_odr odr; 165 + enum adxl355_hpf_3db hpf_3db; 166 + int calibbias[3]; 167 + int adxl355_hpf_3db_table[7][2]; 168 + u8 transf_buf[3] ____cacheline_aligned; 169 + }; 170 + 171 + static int adxl355_set_op_mode(struct adxl355_data *data, 172 + enum adxl355_op_mode op_mode) 173 + { 174 + int ret; 175 + 176 + if (data->op_mode == op_mode) 177 + return 0; 178 + 179 + ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG, 180 + ADXL355_POWER_CTL_MODE_MSK, op_mode); 181 + if (ret) 182 + return ret; 183 + 184 + data->op_mode = op_mode; 185 + 186 + return ret; 187 + } 188 + 189 + static void adxl355_fill_3db_frequency_table(struct adxl355_data *data) 190 + { 191 + u32 multiplier; 192 + u64 div, rem; 193 + u64 odr; 194 + int i; 195 + 196 + odr = mul_u64_u32_shr(adxl355_odr_table[data->odr][0], MEGA, 0) + 197 + adxl355_odr_table[data->odr][1]; 198 + 199 + for (i = 0; i < ARRAY_SIZE(adxl355_hpf_3db_multipliers); i++) { 200 + multiplier = adxl355_hpf_3db_multipliers[i]; 201 + div = div64_u64_rem(mul_u64_u32_shr(odr, multiplier, 0), 202 + TERA * 100, &rem); 203 + 204 + data->adxl355_hpf_3db_table[i][0] = div; 205 + data->adxl355_hpf_3db_table[i][1] = div_u64(rem, MEGA * 100); 206 + } 207 + } 208 + 209 + static int adxl355_setup(struct adxl355_data *data) 210 + { 211 + unsigned int regval; 212 + int ret; 213 + 214 + ret = regmap_read(data->regmap, ADXL355_DEVID_AD_REG, &regval); 215 + if (ret) 216 + return ret; 217 + 218 + if (regval != ADXL355_DEVID_AD_VAL) { 219 + dev_err(data->dev, "Invalid ADI ID 0x%02x\n", regval); 220 + return -ENODEV; 221 + } 222 + 223 + ret = regmap_read(data->regmap, ADXL355_DEVID_MST_REG, &regval); 224 + if (ret) 225 + return ret; 226 + 227 + if (regval != ADXL355_DEVID_MST_VAL) { 228 + dev_err(data->dev, "Invalid MEMS ID 0x%02x\n", regval); 229 + return -ENODEV; 230 + } 231 + 232 + ret = regmap_read(data->regmap, ADXL355_PARTID_REG, &regval); 233 + if (ret) 234 + return ret; 235 + 236 + if (regval != ADXL355_PARTID_VAL) { 237 + dev_err(data->dev, "Invalid DEV ID 0x%02x\n", regval); 238 + return -ENODEV; 239 + } 240 + 241 + /* 242 + * Perform a software reset to make sure the device is in a consistent 243 + * state after start-up. 244 + */ 245 + ret = regmap_write(data->regmap, ADXL355_RESET_REG, ADXL355_RESET_CODE); 246 + if (ret) 247 + return ret; 248 + 249 + adxl355_fill_3db_frequency_table(data); 250 + 251 + return adxl355_set_op_mode(data, ADXL355_MEASUREMENT); 252 + } 253 + 254 + static int adxl355_get_temp_data(struct adxl355_data *data, u8 addr) 255 + { 256 + return regmap_bulk_read(data->regmap, addr, data->transf_buf, 2); 257 + } 258 + 259 + static int adxl355_read_axis(struct adxl355_data *data, u8 addr) 260 + { 261 + int ret; 262 + 263 + ret = regmap_bulk_read(data->regmap, addr, data->transf_buf, 264 + ARRAY_SIZE(data->transf_buf)); 265 + if (ret < 0) 266 + return ret; 267 + 268 + return get_unaligned_be24(data->transf_buf); 269 + } 270 + 271 + static int adxl355_find_match(const int (*freq_tbl)[2], const int n, 272 + const int val, const int val2) 273 + { 274 + int i; 275 + 276 + for (i = 0; i < n; i++) { 277 + if (freq_tbl[i][0] == val && freq_tbl[i][1] == val2) 278 + return i; 279 + } 280 + 281 + return -EINVAL; 282 + } 283 + 284 + static int adxl355_set_odr(struct adxl355_data *data, 285 + enum adxl355_odr odr) 286 + { 287 + int ret; 288 + 289 + mutex_lock(&data->lock); 290 + 291 + if (data->odr == odr) { 292 + mutex_unlock(&data->lock); 293 + return 0; 294 + } 295 + 296 + ret = adxl355_set_op_mode(data, ADXL355_STANDBY); 297 + if (ret < 0) 298 + goto err_unlock; 299 + 300 + ret = regmap_update_bits(data->regmap, ADXL355_FILTER_REG, 301 + ADXL355_FILTER_ODR_MSK, 302 + FIELD_PREP(ADXL355_FILTER_ODR_MSK, odr)); 303 + if (ret < 0) 304 + goto err_set_opmode; 305 + 306 + data->odr = odr; 307 + adxl355_fill_3db_frequency_table(data); 308 + 309 + ret = adxl355_set_op_mode(data, ADXL355_MEASUREMENT); 310 + if (ret) 311 + goto err_set_opmode; 312 + 313 + mutex_unlock(&data->lock); 314 + return 0; 315 + 316 + err_set_opmode: 317 + adxl355_set_op_mode(data, ADXL355_MEASUREMENT); 318 + err_unlock: 319 + mutex_unlock(&data->lock); 320 + return ret; 321 + } 322 + 323 + static int adxl355_set_hpf_3db(struct adxl355_data *data, 324 + enum adxl355_hpf_3db hpf) 325 + { 326 + int ret; 327 + 328 + mutex_lock(&data->lock); 329 + 330 + if (data->hpf_3db == hpf) { 331 + mutex_unlock(&data->lock); 332 + return 0; 333 + } 334 + 335 + ret = adxl355_set_op_mode(data, ADXL355_STANDBY); 336 + if (ret < 0) 337 + goto err_unlock; 338 + 339 + ret = regmap_update_bits(data->regmap, ADXL355_FILTER_REG, 340 + ADXL355_FILTER_HPF_MSK, 341 + FIELD_PREP(ADXL355_FILTER_HPF_MSK, hpf)); 342 + if (ret) 343 + goto err_set_opmode; 344 + 345 + data->hpf_3db = hpf; 346 + 347 + ret = adxl355_set_op_mode(data, ADXL355_MEASUREMENT); 348 + if (ret) 349 + goto err_set_opmode; 350 + 351 + mutex_unlock(&data->lock); 352 + return 0; 353 + 354 + err_set_opmode: 355 + adxl355_set_op_mode(data, ADXL355_MEASUREMENT); 356 + err_unlock: 357 + mutex_unlock(&data->lock); 358 + return ret; 359 + } 360 + 361 + static int adxl355_set_calibbias(struct adxl355_data *data, 362 + enum adxl355_chans chan, int calibbias) 363 + { 364 + int ret; 365 + 366 + mutex_lock(&data->lock); 367 + 368 + ret = adxl355_set_op_mode(data, ADXL355_STANDBY); 369 + if (ret < 0) 370 + goto err_unlock; 371 + 372 + put_unaligned_be16(calibbias, data->transf_buf); 373 + ret = regmap_bulk_write(data->regmap, 374 + adxl355_chans[chan].offset_reg, 375 + data->transf_buf, 2); 376 + if (ret) 377 + goto err_set_opmode; 378 + 379 + data->calibbias[chan] = calibbias; 380 + 381 + ret = adxl355_set_op_mode(data, ADXL355_MEASUREMENT); 382 + if (ret) 383 + goto err_set_opmode; 384 + 385 + mutex_unlock(&data->lock); 386 + return 0; 387 + 388 + err_set_opmode: 389 + adxl355_set_op_mode(data, ADXL355_MEASUREMENT); 390 + err_unlock: 391 + mutex_unlock(&data->lock); 392 + return ret; 393 + } 394 + 395 + static int adxl355_read_raw(struct iio_dev *indio_dev, 396 + struct iio_chan_spec const *chan, 397 + int *val, int *val2, long mask) 398 + { 399 + struct adxl355_data *data = iio_priv(indio_dev); 400 + int ret; 401 + 402 + switch (mask) { 403 + case IIO_CHAN_INFO_RAW: 404 + switch (chan->type) { 405 + case IIO_TEMP: 406 + ret = adxl355_get_temp_data(data, chan->address); 407 + if (ret < 0) 408 + return ret; 409 + *val = get_unaligned_be16(data->transf_buf); 410 + 411 + return IIO_VAL_INT; 412 + case IIO_ACCEL: 413 + ret = adxl355_read_axis(data, adxl355_chans[ 414 + chan->address].data_reg); 415 + if (ret < 0) 416 + return ret; 417 + *val = sign_extend32(ret >> chan->scan_type.shift, 418 + chan->scan_type.realbits - 1); 419 + return IIO_VAL_INT; 420 + default: 421 + return -EINVAL; 422 + } 423 + 424 + case IIO_CHAN_INFO_SCALE: 425 + switch (chan->type) { 426 + /* 427 + * The datasheet defines an intercept of 1885 LSB at 25 degC 428 + * and a slope of -9.05 LSB/C. The following formula can be used 429 + * to find the temperature: 430 + * Temp = ((RAW - 1885)/(-9.05)) + 25 but this doesn't follow 431 + * the format of the IIO which is Temp = (RAW + OFFSET) * SCALE. 432 + * Hence using some rearranging we get the scale as -110.497238 433 + * and offset as -2111.25. 434 + */ 435 + case IIO_TEMP: 436 + *val = -110; 437 + *val2 = 497238; 438 + return IIO_VAL_INT_PLUS_MICRO; 439 + /* 440 + * At +/- 2g with 20-bit resolution, scale is given in datasheet 441 + * as 3.9ug/LSB = 0.0000039 * 9.80665 = 0.00003824593 m/s^2. 442 + */ 443 + case IIO_ACCEL: 444 + *val = 0; 445 + *val2 = 38245; 446 + return IIO_VAL_INT_PLUS_NANO; 447 + default: 448 + return -EINVAL; 449 + } 450 + case IIO_CHAN_INFO_OFFSET: 451 + *val = -2111; 452 + *val2 = 250000; 453 + return IIO_VAL_INT_PLUS_MICRO; 454 + case IIO_CHAN_INFO_CALIBBIAS: 455 + *val = sign_extend32(data->calibbias[chan->address], 15); 456 + return IIO_VAL_INT; 457 + case IIO_CHAN_INFO_SAMP_FREQ: 458 + *val = adxl355_odr_table[data->odr][0]; 459 + *val2 = adxl355_odr_table[data->odr][1]; 460 + return IIO_VAL_INT_PLUS_MICRO; 461 + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: 462 + *val = data->adxl355_hpf_3db_table[data->hpf_3db][0]; 463 + *val2 = data->adxl355_hpf_3db_table[data->hpf_3db][1]; 464 + return IIO_VAL_INT_PLUS_MICRO; 465 + default: 466 + return -EINVAL; 467 + } 468 + } 469 + 470 + static int adxl355_write_raw(struct iio_dev *indio_dev, 471 + struct iio_chan_spec const *chan, 472 + int val, int val2, long mask) 473 + { 474 + struct adxl355_data *data = iio_priv(indio_dev); 475 + int odr_idx, hpf_idx, calibbias; 476 + 477 + switch (mask) { 478 + case IIO_CHAN_INFO_SAMP_FREQ: 479 + odr_idx = adxl355_find_match(adxl355_odr_table, 480 + ARRAY_SIZE(adxl355_odr_table), 481 + val, val2); 482 + if (odr_idx < 0) 483 + return odr_idx; 484 + 485 + return adxl355_set_odr(data, odr_idx); 486 + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: 487 + hpf_idx = adxl355_find_match(data->adxl355_hpf_3db_table, 488 + ARRAY_SIZE(data->adxl355_hpf_3db_table), 489 + val, val2); 490 + if (hpf_idx < 0) 491 + return hpf_idx; 492 + 493 + return adxl355_set_hpf_3db(data, hpf_idx); 494 + case IIO_CHAN_INFO_CALIBBIAS: 495 + calibbias = clamp_t(int, val, S16_MIN, S16_MAX); 496 + 497 + return adxl355_set_calibbias(data, chan->address, calibbias); 498 + default: 499 + return -EINVAL; 500 + } 501 + } 502 + 503 + static int adxl355_read_avail(struct iio_dev *indio_dev, 504 + struct iio_chan_spec const *chan, 505 + const int **vals, int *type, int *length, 506 + long mask) 507 + { 508 + struct adxl355_data *data = iio_priv(indio_dev); 509 + 510 + switch (mask) { 511 + case IIO_CHAN_INFO_SAMP_FREQ: 512 + *vals = (const int *)adxl355_odr_table; 513 + *type = IIO_VAL_INT_PLUS_MICRO; 514 + /* Values are stored in a 2D matrix */ 515 + *length = ARRAY_SIZE(adxl355_odr_table) * 2; 516 + 517 + return IIO_AVAIL_LIST; 518 + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: 519 + *vals = (const int *)data->adxl355_hpf_3db_table; 520 + *type = IIO_VAL_INT_PLUS_MICRO; 521 + /* Values are stored in a 2D matrix */ 522 + *length = ARRAY_SIZE(data->adxl355_hpf_3db_table) * 2; 523 + 524 + return IIO_AVAIL_LIST; 525 + default: 526 + return -EINVAL; 527 + } 528 + } 529 + 530 + static const struct iio_info adxl355_info = { 531 + .read_raw = adxl355_read_raw, 532 + .write_raw = adxl355_write_raw, 533 + .read_avail = &adxl355_read_avail, 534 + }; 535 + 536 + #define ADXL355_ACCEL_CHANNEL(index, reg, axis) { \ 537 + .type = IIO_ACCEL, \ 538 + .address = reg, \ 539 + .modified = 1, \ 540 + .channel2 = IIO_MOD_##axis, \ 541 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 542 + BIT(IIO_CHAN_INFO_CALIBBIAS), \ 543 + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ 544 + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ 545 + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ 546 + .info_mask_shared_by_type_available = \ 547 + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ 548 + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ 549 + .scan_type = { \ 550 + .sign = 's', \ 551 + .realbits = 20, \ 552 + .storagebits = 32, \ 553 + .shift = 4, \ 554 + .endianness = IIO_BE, \ 555 + } \ 556 + } 557 + 558 + static const struct iio_chan_spec adxl355_channels[] = { 559 + ADXL355_ACCEL_CHANNEL(0, chan_x, X), 560 + ADXL355_ACCEL_CHANNEL(1, chan_y, Y), 561 + ADXL355_ACCEL_CHANNEL(2, chan_z, Z), 562 + { 563 + .type = IIO_TEMP, 564 + .address = ADXL355_TEMP2_REG, 565 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 566 + BIT(IIO_CHAN_INFO_SCALE) | 567 + BIT(IIO_CHAN_INFO_OFFSET), 568 + .scan_type = { 569 + .sign = 's', 570 + .realbits = 12, 571 + .storagebits = 16, 572 + .endianness = IIO_BE, 573 + }, 574 + }, 575 + }; 576 + 577 + int adxl355_core_probe(struct device *dev, struct regmap *regmap, 578 + const char *name) 579 + { 580 + struct adxl355_data *data; 581 + struct iio_dev *indio_dev; 582 + int ret; 583 + 584 + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 585 + if (!indio_dev) 586 + return -ENOMEM; 587 + 588 + data = iio_priv(indio_dev); 589 + data->regmap = regmap; 590 + data->dev = dev; 591 + data->op_mode = ADXL355_STANDBY; 592 + mutex_init(&data->lock); 593 + 594 + indio_dev->name = name; 595 + indio_dev->info = &adxl355_info; 596 + indio_dev->modes = INDIO_DIRECT_MODE; 597 + indio_dev->channels = adxl355_channels; 598 + indio_dev->num_channels = ARRAY_SIZE(adxl355_channels); 599 + 600 + ret = adxl355_setup(data); 601 + if (ret < 0) { 602 + dev_err(dev, "ADXL355 setup failed\n"); 603 + return ret; 604 + } 605 + 606 + return devm_iio_device_register(dev, indio_dev); 607 + } 608 + EXPORT_SYMBOL_GPL(adxl355_core_probe); 609 + 610 + MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>"); 611 + MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer core driver"); 612 + MODULE_LICENSE("GPL v2");
+62
drivers/iio/accel/adxl355_i2c.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * ADXL355 3-Axis Digital Accelerometer I2C driver 4 + * 5 + * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com> 6 + */ 7 + 8 + #include <linux/i2c.h> 9 + #include <linux/module.h> 10 + #include <linux/mod_devicetable.h> 11 + #include <linux/regmap.h> 12 + 13 + #include "adxl355.h" 14 + 15 + static const struct regmap_config adxl355_i2c_regmap_config = { 16 + .reg_bits = 8, 17 + .val_bits = 8, 18 + .max_register = 0x2F, 19 + .rd_table = &adxl355_readable_regs_tbl, 20 + .wr_table = &adxl355_writeable_regs_tbl, 21 + }; 22 + 23 + static int adxl355_i2c_probe(struct i2c_client *client) 24 + { 25 + struct regmap *regmap; 26 + 27 + regmap = devm_regmap_init_i2c(client, &adxl355_i2c_regmap_config); 28 + if (IS_ERR(regmap)) { 29 + dev_err(&client->dev, "Error initializing i2c regmap: %ld\n", 30 + PTR_ERR(regmap)); 31 + 32 + return PTR_ERR(regmap); 33 + } 34 + 35 + return adxl355_core_probe(&client->dev, regmap, client->name); 36 + } 37 + 38 + static const struct i2c_device_id adxl355_i2c_id[] = { 39 + { "adxl355", 0 }, 40 + { } 41 + }; 42 + MODULE_DEVICE_TABLE(i2c, adxl355_i2c_id); 43 + 44 + static const struct of_device_id adxl355_of_match[] = { 45 + { .compatible = "adi,adxl355" }, 46 + { } 47 + }; 48 + MODULE_DEVICE_TABLE(of, adxl355_of_match); 49 + 50 + static struct i2c_driver adxl355_i2c_driver = { 51 + .driver = { 52 + .name = "adxl355_i2c", 53 + .of_match_table = adxl355_of_match, 54 + }, 55 + .probe_new = adxl355_i2c_probe, 56 + .id_table = adxl355_i2c_id, 57 + }; 58 + module_i2c_driver(adxl355_i2c_driver); 59 + 60 + MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>"); 61 + MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer I2C driver"); 62 + MODULE_LICENSE("GPL v2");
+65
drivers/iio/accel/adxl355_spi.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * ADXL355 3-Axis Digital Accelerometer SPI driver 4 + * 5 + * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com> 6 + */ 7 + 8 + #include <linux/module.h> 9 + #include <linux/mod_devicetable.h> 10 + #include <linux/regmap.h> 11 + #include <linux/spi/spi.h> 12 + 13 + #include "adxl355.h" 14 + 15 + static const struct regmap_config adxl355_spi_regmap_config = { 16 + .reg_bits = 7, 17 + .pad_bits = 1, 18 + .val_bits = 8, 19 + .read_flag_mask = BIT(0), 20 + .max_register = 0x2F, 21 + .rd_table = &adxl355_readable_regs_tbl, 22 + .wr_table = &adxl355_writeable_regs_tbl, 23 + }; 24 + 25 + static int adxl355_spi_probe(struct spi_device *spi) 26 + { 27 + const struct spi_device_id *id = spi_get_device_id(spi); 28 + struct regmap *regmap; 29 + 30 + regmap = devm_regmap_init_spi(spi, &adxl355_spi_regmap_config); 31 + if (IS_ERR(regmap)) { 32 + dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", 33 + PTR_ERR(regmap)); 34 + 35 + return PTR_ERR(regmap); 36 + } 37 + 38 + return adxl355_core_probe(&spi->dev, regmap, id->name); 39 + } 40 + 41 + static const struct spi_device_id adxl355_spi_id[] = { 42 + { "adxl355", 0 }, 43 + { } 44 + }; 45 + MODULE_DEVICE_TABLE(spi, adxl355_spi_id); 46 + 47 + static const struct of_device_id adxl355_of_match[] = { 48 + { .compatible = "adi,adxl355" }, 49 + { } 50 + }; 51 + MODULE_DEVICE_TABLE(of, adxl355_of_match); 52 + 53 + static struct spi_driver adxl355_spi_driver = { 54 + .driver = { 55 + .name = "adxl355_spi", 56 + .of_match_table = adxl355_of_match, 57 + }, 58 + .probe = adxl355_spi_probe, 59 + .id_table = adxl355_spi_id, 60 + }; 61 + module_spi_driver(adxl355_spi_driver); 62 + 63 + MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>"); 64 + MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer SPI driver"); 65 + MODULE_LICENSE("GPL v2");