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

Configure Feed

Select the types of activity you want to include in your feed.

iio: gyro: Add driver support for ADXRS290

ADXRS290 is a high performance MEMS pitch and roll (dual-axis in-plane)
angular rate sensor (gyroscope) designed for use in stabilization
applications. It also features an internal temperature sensor and
programmable high-pass and low-pass filters.

Add support for ADXRS290 in direct-access mode for now.

Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXRS290.pdf
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Signed-off-by: Nishant Malpani <nish.malpani25@gmail.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Nishant Malpani and committed by
Jonathan Cameron
2c8920ff 96f96251

+461
+6
MAINTAINERS
··· 1108 1108 S: Maintained 1109 1109 F: drivers/media/i2c/adv7842* 1110 1110 1111 + ANALOG DEVICES INC ADXRS290 DRIVER 1112 + M: Nishant Malpani <nish.malpani25@gmail.com> 1113 + L: linux-iio@vger.kernel.org 1114 + S: Supported 1115 + F: drivers/iio/gyro/adxrs290.c 1116 + 1111 1117 ANALOG DEVICES INC ASOC CODEC DRIVERS 1112 1118 M: Lars-Peter Clausen <lars@metafoo.de> 1113 1119 M: Nuno Sá <nuno.sa@analog.com>
+10
drivers/iio/gyro/Kconfig
··· 41 41 This driver can also be built as a module. If so, the module 42 42 will be called adis16260. 43 43 44 + config ADXRS290 45 + tristate "Analog Devices ADXRS290 Dual-Axis MEMS Gyroscope SPI driver" 46 + depends on SPI 47 + help 48 + Say yes here to build support for Analog Devices ADXRS290 programmable 49 + digital output gyroscope. 50 + 51 + This driver can also be built as a module. If so, the module will be 52 + called adxrs290. 53 + 44 54 config ADXRS450 45 55 tristate "Analog Devices ADXRS450/3 Digital Output Gyroscope SPI driver" 46 56 depends on SPI
+1
drivers/iio/gyro/Makefile
··· 8 8 obj-$(CONFIG_ADIS16130) += adis16130.o 9 9 obj-$(CONFIG_ADIS16136) += adis16136.o 10 10 obj-$(CONFIG_ADIS16260) += adis16260.o 11 + obj-$(CONFIG_ADXRS290) += adxrs290.o 11 12 obj-$(CONFIG_ADXRS450) += adxrs450.o 12 13 obj-$(CONFIG_BMG160) += bmg160_core.o 13 14 obj-$(CONFIG_BMG160_I2C) += bmg160_i2c.o
+444
drivers/iio/gyro/adxrs290.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * ADXRS290 SPI Gyroscope Driver 4 + * 5 + * Copyright (C) 2020 Nishant Malpani <nish.malpani25@gmail.com> 6 + * Copyright (C) 2020 Analog Devices, Inc. 7 + */ 8 + 9 + #include <linux/bitfield.h> 10 + #include <linux/delay.h> 11 + #include <linux/device.h> 12 + #include <linux/kernel.h> 13 + #include <linux/module.h> 14 + #include <linux/spi/spi.h> 15 + 16 + #include <linux/iio/iio.h> 17 + #include <linux/iio/sysfs.h> 18 + 19 + #define ADXRS290_ADI_ID 0xAD 20 + #define ADXRS290_MEMS_ID 0x1D 21 + #define ADXRS290_DEV_ID 0x92 22 + 23 + #define ADXRS290_REG_ADI_ID 0x00 24 + #define ADXRS290_REG_MEMS_ID 0x01 25 + #define ADXRS290_REG_DEV_ID 0x02 26 + #define ADXRS290_REG_REV_ID 0x03 27 + #define ADXRS290_REG_SN0 0x04 /* Serial Number Registers, 4 bytes */ 28 + #define ADXRS290_REG_DATAX0 0x08 /* Roll Rate o/p Data Regs, 2 bytes */ 29 + #define ADXRS290_REG_DATAY0 0x0A /* Pitch Rate o/p Data Regs, 2 bytes */ 30 + #define ADXRS290_REG_TEMP0 0x0C 31 + #define ADXRS290_REG_POWER_CTL 0x10 32 + #define ADXRS290_REG_FILTER 0x11 33 + #define ADXRS290_REG_DATA_RDY 0x12 34 + 35 + #define ADXRS290_READ BIT(7) 36 + #define ADXRS290_TSM BIT(0) 37 + #define ADXRS290_MEASUREMENT BIT(1) 38 + #define ADXRS290_SYNC GENMASK(1, 0) 39 + #define ADXRS290_LPF_MASK GENMASK(2, 0) 40 + #define ADXRS290_LPF(x) FIELD_PREP(ADXRS290_LPF_MASK, x) 41 + #define ADXRS290_HPF_MASK GENMASK(7, 4) 42 + #define ADXRS290_HPF(x) FIELD_PREP(ADXRS290_HPF_MASK, x) 43 + 44 + #define ADXRS290_READ_REG(reg) (ADXRS290_READ | (reg)) 45 + 46 + #define ADXRS290_MAX_TRANSITION_TIME_MS 100 47 + 48 + enum adxrs290_mode { 49 + ADXRS290_MODE_STANDBY, 50 + ADXRS290_MODE_MEASUREMENT, 51 + }; 52 + 53 + struct adxrs290_state { 54 + struct spi_device *spi; 55 + /* Serialize reads and their subsequent processing */ 56 + struct mutex lock; 57 + enum adxrs290_mode mode; 58 + unsigned int lpf_3db_freq_idx; 59 + unsigned int hpf_3db_freq_idx; 60 + }; 61 + 62 + /* 63 + * Available cut-off frequencies of the low pass filter in Hz. 64 + * The integer part and fractional part are represented separately. 65 + */ 66 + static const int adxrs290_lpf_3db_freq_hz_table[][2] = { 67 + [0] = {480, 0}, 68 + [1] = {320, 0}, 69 + [2] = {160, 0}, 70 + [3] = {80, 0}, 71 + [4] = {56, 600000}, 72 + [5] = {40, 0}, 73 + [6] = {28, 300000}, 74 + [7] = {20, 0}, 75 + }; 76 + 77 + /* 78 + * Available cut-off frequencies of the high pass filter in Hz. 79 + * The integer part and fractional part are represented separately. 80 + */ 81 + static const int adxrs290_hpf_3db_freq_hz_table[][2] = { 82 + [0] = {0, 0}, 83 + [1] = {0, 11000}, 84 + [2] = {0, 22000}, 85 + [3] = {0, 44000}, 86 + [4] = {0, 87000}, 87 + [5] = {0, 175000}, 88 + [6] = {0, 350000}, 89 + [7] = {0, 700000}, 90 + [8] = {1, 400000}, 91 + [9] = {2, 800000}, 92 + [10] = {11, 300000}, 93 + }; 94 + 95 + static int adxrs290_get_rate_data(struct iio_dev *indio_dev, const u8 cmd, int *val) 96 + { 97 + struct adxrs290_state *st = iio_priv(indio_dev); 98 + int ret = 0; 99 + int temp; 100 + 101 + mutex_lock(&st->lock); 102 + temp = spi_w8r16(st->spi, cmd); 103 + if (temp < 0) { 104 + ret = temp; 105 + goto err_unlock; 106 + } 107 + 108 + *val = temp; 109 + 110 + err_unlock: 111 + mutex_unlock(&st->lock); 112 + return ret; 113 + } 114 + 115 + static int adxrs290_get_temp_data(struct iio_dev *indio_dev, int *val) 116 + { 117 + const u8 cmd = ADXRS290_READ_REG(ADXRS290_REG_TEMP0); 118 + struct adxrs290_state *st = iio_priv(indio_dev); 119 + int ret = 0; 120 + int temp; 121 + 122 + mutex_lock(&st->lock); 123 + temp = spi_w8r16(st->spi, cmd); 124 + if (temp < 0) { 125 + ret = temp; 126 + goto err_unlock; 127 + } 128 + 129 + /* extract lower 12 bits temperature reading */ 130 + *val = temp & 0x0FFF; 131 + 132 + err_unlock: 133 + mutex_unlock(&st->lock); 134 + return ret; 135 + } 136 + 137 + static int adxrs290_get_3db_freq(struct iio_dev *indio_dev, u8 *val, u8 *val2) 138 + { 139 + const u8 cmd = ADXRS290_READ_REG(ADXRS290_REG_FILTER); 140 + struct adxrs290_state *st = iio_priv(indio_dev); 141 + int ret = 0; 142 + short temp; 143 + 144 + mutex_lock(&st->lock); 145 + temp = spi_w8r8(st->spi, cmd); 146 + if (temp < 0) { 147 + ret = temp; 148 + goto err_unlock; 149 + } 150 + 151 + *val = FIELD_GET(ADXRS290_LPF_MASK, temp); 152 + *val2 = FIELD_GET(ADXRS290_HPF_MASK, temp); 153 + 154 + err_unlock: 155 + mutex_unlock(&st->lock); 156 + return ret; 157 + } 158 + 159 + static int adxrs290_spi_write_reg(struct spi_device *spi, const u8 reg, 160 + const u8 val) 161 + { 162 + u8 buf[2]; 163 + 164 + buf[0] = reg; 165 + buf[1] = val; 166 + 167 + return spi_write_then_read(spi, buf, ARRAY_SIZE(buf), NULL, 0); 168 + } 169 + 170 + static int adxrs290_find_match(const int (*freq_tbl)[2], const int n, 171 + const int val, const int val2) 172 + { 173 + int i; 174 + 175 + for (i = 0; i < n; i++) { 176 + if (freq_tbl[i][0] == val && freq_tbl[i][1] == val2) 177 + return i; 178 + } 179 + 180 + return -EINVAL; 181 + } 182 + 183 + static int adxrs290_set_filter_freq(struct iio_dev *indio_dev, 184 + const unsigned int lpf_idx, 185 + const unsigned int hpf_idx) 186 + { 187 + struct adxrs290_state *st = iio_priv(indio_dev); 188 + u8 val; 189 + 190 + val = ADXRS290_HPF(hpf_idx) | ADXRS290_LPF(lpf_idx); 191 + 192 + return adxrs290_spi_write_reg(st->spi, ADXRS290_REG_FILTER, val); 193 + } 194 + 195 + static int adxrs290_initial_setup(struct iio_dev *indio_dev) 196 + { 197 + struct adxrs290_state *st = iio_priv(indio_dev); 198 + 199 + st->mode = ADXRS290_MODE_MEASUREMENT; 200 + 201 + return adxrs290_spi_write_reg(st->spi, 202 + ADXRS290_REG_POWER_CTL, 203 + ADXRS290_MEASUREMENT | ADXRS290_TSM); 204 + } 205 + 206 + static int adxrs290_read_raw(struct iio_dev *indio_dev, 207 + struct iio_chan_spec const *chan, 208 + int *val, 209 + int *val2, 210 + long mask) 211 + { 212 + struct adxrs290_state *st = iio_priv(indio_dev); 213 + unsigned int t; 214 + int ret; 215 + 216 + switch (mask) { 217 + case IIO_CHAN_INFO_RAW: 218 + switch (chan->type) { 219 + case IIO_ANGL_VEL: 220 + ret = adxrs290_get_rate_data(indio_dev, 221 + ADXRS290_READ_REG(chan->address), 222 + val); 223 + if (ret < 0) 224 + return ret; 225 + 226 + return IIO_VAL_INT; 227 + case IIO_TEMP: 228 + ret = adxrs290_get_temp_data(indio_dev, val); 229 + if (ret < 0) 230 + return ret; 231 + 232 + return IIO_VAL_INT; 233 + default: 234 + return -EINVAL; 235 + } 236 + case IIO_CHAN_INFO_SCALE: 237 + switch (chan->type) { 238 + case IIO_ANGL_VEL: 239 + /* 1 LSB = 0.005 degrees/sec */ 240 + *val = 0; 241 + *val2 = 87266; 242 + return IIO_VAL_INT_PLUS_NANO; 243 + case IIO_TEMP: 244 + /* 1 LSB = 0.1 degrees Celsius */ 245 + *val = 100; 246 + return IIO_VAL_INT; 247 + default: 248 + return -EINVAL; 249 + } 250 + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: 251 + switch (chan->type) { 252 + case IIO_ANGL_VEL: 253 + t = st->lpf_3db_freq_idx; 254 + *val = adxrs290_lpf_3db_freq_hz_table[t][0]; 255 + *val2 = adxrs290_lpf_3db_freq_hz_table[t][1]; 256 + return IIO_VAL_INT_PLUS_MICRO; 257 + default: 258 + return -EINVAL; 259 + } 260 + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: 261 + switch (chan->type) { 262 + case IIO_ANGL_VEL: 263 + t = st->hpf_3db_freq_idx; 264 + *val = adxrs290_hpf_3db_freq_hz_table[t][0]; 265 + *val2 = adxrs290_hpf_3db_freq_hz_table[t][1]; 266 + return IIO_VAL_INT_PLUS_MICRO; 267 + default: 268 + return -EINVAL; 269 + } 270 + } 271 + 272 + return -EINVAL; 273 + } 274 + 275 + static int adxrs290_write_raw(struct iio_dev *indio_dev, 276 + struct iio_chan_spec const *chan, 277 + int val, 278 + int val2, 279 + long mask) 280 + { 281 + struct adxrs290_state *st = iio_priv(indio_dev); 282 + int lpf_idx, hpf_idx; 283 + 284 + switch (mask) { 285 + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: 286 + lpf_idx = adxrs290_find_match(adxrs290_lpf_3db_freq_hz_table, 287 + ARRAY_SIZE(adxrs290_lpf_3db_freq_hz_table), 288 + val, val2); 289 + if (lpf_idx < 0) 290 + return -EINVAL; 291 + /* caching the updated state of the low-pass filter */ 292 + st->lpf_3db_freq_idx = lpf_idx; 293 + /* retrieving the current state of the high-pass filter */ 294 + hpf_idx = st->hpf_3db_freq_idx; 295 + return adxrs290_set_filter_freq(indio_dev, lpf_idx, hpf_idx); 296 + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: 297 + hpf_idx = adxrs290_find_match(adxrs290_hpf_3db_freq_hz_table, 298 + ARRAY_SIZE(adxrs290_hpf_3db_freq_hz_table), 299 + val, val2); 300 + if (hpf_idx < 0) 301 + return -EINVAL; 302 + /* caching the updated state of the high-pass filter */ 303 + st->hpf_3db_freq_idx = hpf_idx; 304 + /* retrieving the current state of the low-pass filter */ 305 + lpf_idx = st->lpf_3db_freq_idx; 306 + return adxrs290_set_filter_freq(indio_dev, lpf_idx, hpf_idx); 307 + } 308 + 309 + return -EINVAL; 310 + } 311 + 312 + static int adxrs290_read_avail(struct iio_dev *indio_dev, 313 + struct iio_chan_spec const *chan, 314 + const int **vals, int *type, int *length, 315 + long mask) 316 + { 317 + switch (mask) { 318 + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: 319 + *vals = (const int *)adxrs290_lpf_3db_freq_hz_table; 320 + *type = IIO_VAL_INT_PLUS_MICRO; 321 + /* Values are stored in a 2D matrix */ 322 + *length = ARRAY_SIZE(adxrs290_lpf_3db_freq_hz_table) * 2; 323 + 324 + return IIO_AVAIL_LIST; 325 + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: 326 + *vals = (const int *)adxrs290_hpf_3db_freq_hz_table; 327 + *type = IIO_VAL_INT_PLUS_MICRO; 328 + /* Values are stored in a 2D matrix */ 329 + *length = ARRAY_SIZE(adxrs290_hpf_3db_freq_hz_table) * 2; 330 + 331 + return IIO_AVAIL_LIST; 332 + default: 333 + return -EINVAL; 334 + } 335 + } 336 + 337 + #define ADXRS290_ANGL_VEL_CHANNEL(reg, axis) { \ 338 + .type = IIO_ANGL_VEL, \ 339 + .address = reg, \ 340 + .modified = 1, \ 341 + .channel2 = IIO_MOD_##axis, \ 342 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 343 + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ 344 + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \ 345 + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ 346 + .info_mask_shared_by_type_available = \ 347 + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \ 348 + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ 349 + } 350 + 351 + static const struct iio_chan_spec adxrs290_channels[] = { 352 + ADXRS290_ANGL_VEL_CHANNEL(ADXRS290_REG_DATAX0, X), 353 + ADXRS290_ANGL_VEL_CHANNEL(ADXRS290_REG_DATAY0, Y), 354 + { 355 + .type = IIO_TEMP, 356 + .address = ADXRS290_REG_TEMP0, 357 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 358 + BIT(IIO_CHAN_INFO_SCALE), 359 + }, 360 + }; 361 + 362 + static const struct iio_info adxrs290_info = { 363 + .read_raw = &adxrs290_read_raw, 364 + .write_raw = &adxrs290_write_raw, 365 + .read_avail = &adxrs290_read_avail, 366 + }; 367 + 368 + static int adxrs290_probe(struct spi_device *spi) 369 + { 370 + struct iio_dev *indio_dev; 371 + struct adxrs290_state *st; 372 + u8 val, val2; 373 + int ret; 374 + 375 + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); 376 + if (!indio_dev) 377 + return -ENOMEM; 378 + 379 + st = iio_priv(indio_dev); 380 + st->spi = spi; 381 + 382 + indio_dev->name = "adxrs290"; 383 + indio_dev->modes = INDIO_DIRECT_MODE; 384 + indio_dev->channels = adxrs290_channels; 385 + indio_dev->num_channels = ARRAY_SIZE(adxrs290_channels); 386 + indio_dev->info = &adxrs290_info; 387 + 388 + val = spi_w8r8(spi, ADXRS290_READ_REG(ADXRS290_REG_ADI_ID)); 389 + if (val != ADXRS290_ADI_ID) { 390 + dev_err(&spi->dev, "Wrong ADI ID 0x%02x\n", val); 391 + return -ENODEV; 392 + } 393 + 394 + val = spi_w8r8(spi, ADXRS290_READ_REG(ADXRS290_REG_MEMS_ID)); 395 + if (val != ADXRS290_MEMS_ID) { 396 + dev_err(&spi->dev, "Wrong MEMS ID 0x%02x\n", val); 397 + return -ENODEV; 398 + } 399 + 400 + val = spi_w8r8(spi, ADXRS290_READ_REG(ADXRS290_REG_DEV_ID)); 401 + if (val != ADXRS290_DEV_ID) { 402 + dev_err(&spi->dev, "Wrong DEV ID 0x%02x\n", val); 403 + return -ENODEV; 404 + } 405 + 406 + /* default mode the gyroscope starts in */ 407 + st->mode = ADXRS290_MODE_STANDBY; 408 + 409 + /* switch to measurement mode and switch on the temperature sensor */ 410 + ret = adxrs290_initial_setup(indio_dev); 411 + if (ret < 0) 412 + return ret; 413 + 414 + /* max transition time to measurement mode */ 415 + msleep(ADXRS290_MAX_TRANSITION_TIME_MS); 416 + 417 + ret = adxrs290_get_3db_freq(indio_dev, &val, &val2); 418 + if (ret < 0) 419 + return ret; 420 + 421 + st->lpf_3db_freq_idx = val; 422 + st->hpf_3db_freq_idx = val2; 423 + 424 + return devm_iio_device_register(&spi->dev, indio_dev); 425 + } 426 + 427 + static const struct of_device_id adxrs290_of_match[] = { 428 + { .compatible = "adi,adxrs290" }, 429 + { } 430 + }; 431 + MODULE_DEVICE_TABLE(of, adxrs290_of_match); 432 + 433 + static struct spi_driver adxrs290_driver = { 434 + .driver = { 435 + .name = "adxrs290", 436 + .of_match_table = adxrs290_of_match, 437 + }, 438 + .probe = adxrs290_probe, 439 + }; 440 + module_spi_driver(adxrs290_driver); 441 + 442 + MODULE_AUTHOR("Nishant Malpani <nish.malpani25@gmail.com>"); 443 + MODULE_DESCRIPTION("Analog Devices ADXRS290 Gyroscope SPI driver"); 444 + MODULE_LICENSE("GPL");