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: amplifiers: ada4250: add support for ADA4250

The ADA4250 is an instrumentation amplifier with SPI/pin-strap
progammable gains that is optimized for ultra-low power systems.
With a minimum supply voltage of 1.7V, 26uA of quiescent current,
a shutdown mode, a sleep mode, and a fast wake up settling time,
ADA4250 can be power cycled on a battery powered system for even
futher savings.

Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>
Link: https://lore.kernel.org/r/20220223120112.8067-2-antoniu.miclaus@analog.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Antoniu Miclaus and committed by
Jonathan Cameron
28b4c30b 602744fa

+415
+11
drivers/iio/amplifiers/Kconfig
··· 23 23 To compile this driver as a module, choose M here: the 24 24 module will be called ad8366. 25 25 26 + config ADA4250 27 + tristate "Analog Devices ADA4250 Instrumentation Amplifier" 28 + depends on SPI 29 + help 30 + Say yes here to build support for Analog Devices ADA4250 31 + SPI Amplifier's support. The driver provides direct access via 32 + sysfs. 33 + 34 + To compile this driver as a module, choose M here: the 35 + module will be called ada4250. 36 + 26 37 config HMC425 27 38 tristate "Analog Devices HMC425A and similar GPIO Gain Amplifiers" 28 39 depends on GPIOLIB
+1
drivers/iio/amplifiers/Makefile
··· 5 5 6 6 # When adding new entries keep the list in alphabetical order 7 7 obj-$(CONFIG_AD8366) += ad8366.o 8 + obj-$(CONFIG_ADA4250) += ada4250.o 8 9 obj-$(CONFIG_HMC425) += hmc425a.o
+403
drivers/iio/amplifiers/ada4250.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * ADA4250 driver 4 + * 5 + * Copyright 2022 Analog Devices Inc. 6 + */ 7 + 8 + #include <linux/bitfield.h> 9 + #include <linux/bits.h> 10 + #include <linux/device.h> 11 + #include <linux/iio/iio.h> 12 + #include <linux/module.h> 13 + #include <linux/regmap.h> 14 + #include <linux/regulator/consumer.h> 15 + #include <linux/spi/spi.h> 16 + 17 + #include <asm/unaligned.h> 18 + 19 + /* ADA4250 Register Map */ 20 + #define ADA4250_REG_GAIN_MUX 0x00 21 + #define ADA4250_REG_REFBUF_EN 0x01 22 + #define ADA4250_REG_RESET 0x02 23 + #define ADA4250_REG_SNSR_CAL_VAL 0x04 24 + #define ADA4250_REG_SNSR_CAL_CNFG 0x05 25 + #define ADA4250_REG_DIE_REV 0x18 26 + #define ADA4250_REG_CHIP_ID 0x19 27 + 28 + /* ADA4250_REG_GAIN_MUX Map */ 29 + #define ADA4250_GAIN_MUX_MSK GENMASK(2, 0) 30 + 31 + /* ADA4250_REG_REFBUF Map */ 32 + #define ADA4250_REFBUF_MSK BIT(0) 33 + 34 + /* ADA4250_REG_RESET Map */ 35 + #define ADA4250_RESET_MSK BIT(0) 36 + 37 + /* ADA4250_REG_SNSR_CAL_VAL Map */ 38 + #define ADA4250_CAL_CFG_BIAS_MSK GENMASK(7, 0) 39 + 40 + /* ADA4250_REG_SNSR_CAL_CNFG Bit Definition */ 41 + #define ADA4250_BIAS_SET_MSK GENMASK(3, 2) 42 + #define ADA4250_RANGE_SET_MSK GENMASK(1, 0) 43 + 44 + /* Miscellaneous definitions */ 45 + #define ADA4250_CHIP_ID 0x4250 46 + #define ADA4250_RANGE1 0 47 + #define ADA4250_RANGE4 3 48 + 49 + /* ADA4250 current bias set */ 50 + enum ada4250_current_bias { 51 + ADA4250_BIAS_DISABLED, 52 + ADA4250_BIAS_BANDGAP, 53 + ADA4250_BIAS_AVDD, 54 + }; 55 + 56 + struct ada4250_state { 57 + struct spi_device *spi; 58 + struct regmap *regmap; 59 + struct regulator *reg; 60 + /* Protect against concurrent accesses to the device and data content */ 61 + struct mutex lock; 62 + u8 bias; 63 + u8 gain; 64 + int offset_uv; 65 + bool refbuf_en; 66 + }; 67 + 68 + /* ADA4250 Current Bias Source Settings: Disabled, Bandgap Reference, AVDD */ 69 + static const int calibbias_table[] = {0, 1, 2}; 70 + 71 + /* ADA4250 Gain (V/V) values: 1, 2, 4, 8, 16, 32, 64, 128 */ 72 + static const int hwgain_table[] = {1, 2, 4, 8, 16, 32, 64, 128}; 73 + 74 + static const struct regmap_config ada4250_regmap_config = { 75 + .reg_bits = 8, 76 + .val_bits = 8, 77 + .read_flag_mask = BIT(7), 78 + .max_register = 0x1A, 79 + }; 80 + 81 + static int ada4250_set_offset_uv(struct iio_dev *indio_dev, 82 + const struct iio_chan_spec *chan, 83 + int offset_uv) 84 + { 85 + struct ada4250_state *st = iio_priv(indio_dev); 86 + 87 + int i, ret, x[8], max_vos, min_vos, voltage_v, vlsb = 0; 88 + u8 offset_raw, range = ADA4250_RANGE1; 89 + u32 lsb_coeff[6] = {1333, 2301, 4283, 8289, 16311, 31599}; 90 + 91 + if (st->bias == 0 || st->bias == 3) 92 + return -EINVAL; 93 + 94 + voltage_v = regulator_get_voltage(st->reg); 95 + voltage_v = DIV_ROUND_CLOSEST(voltage_v, 1000000); 96 + 97 + if (st->bias == ADA4250_BIAS_AVDD) 98 + x[0] = voltage_v; 99 + else 100 + x[0] = 5; 101 + 102 + x[1] = 126 * (x[0] - 1); 103 + 104 + for (i = 0; i < 6; i++) 105 + x[i + 2] = DIV_ROUND_CLOSEST(x[1] * 1000, lsb_coeff[i]); 106 + 107 + if (st->gain == 0) 108 + return -EINVAL; 109 + 110 + /* 111 + * Compute Range and Voltage per LSB for the Sensor Offset Calibration 112 + * Example of computation for Range 1 and Range 2 (Curren Bias Set = AVDD): 113 + * Range 1 Range 2 114 + * Gain | Max Vos(mV) | LSB(mV) | Max Vos(mV) | LSB(mV) | 115 + * 2 | X1*127 | X1=0.126(AVDD-1) | X1*3*127 | X1*3 | 116 + * 4 | X2*127 | X2=X1/1.3333 | X2*3*127 | X2*3 | 117 + * 8 | X3*127 | X3=X1/2.301 | X3*3*127 | X3*3 | 118 + * 16 | X4*127 | X4=X1/4.283 | X4*3*127 | X4*3 | 119 + * 32 | X5*127 | X5=X1/8.289 | X5*3*127 | X5*3 | 120 + * 64 | X6*127 | X6=X1/16.311 | X6*3*127 | X6*3 | 121 + * 128 | X7*127 | X7=X1/31.599 | X7*3*127 | X7*3 | 122 + */ 123 + for (i = ADA4250_RANGE1; i <= ADA4250_RANGE4; i++) { 124 + max_vos = x[st->gain] * 127 * ((1 << (i + 1)) - 1); 125 + min_vos = -1 * max_vos; 126 + if (offset_uv > min_vos && offset_uv < max_vos) { 127 + range = i; 128 + vlsb = x[st->gain] * ((1 << (i + 1)) - 1); 129 + break; 130 + } 131 + } 132 + 133 + if (vlsb <= 0) 134 + return -EINVAL; 135 + 136 + offset_raw = DIV_ROUND_CLOSEST(abs(offset_uv), vlsb); 137 + 138 + mutex_lock(&st->lock); 139 + ret = regmap_update_bits(st->regmap, ADA4250_REG_SNSR_CAL_CNFG, 140 + ADA4250_RANGE_SET_MSK, 141 + FIELD_PREP(ADA4250_RANGE_SET_MSK, range)); 142 + if (ret) 143 + goto exit; 144 + 145 + st->offset_uv = offset_raw * vlsb; 146 + 147 + /* 148 + * To set the offset calibration value, use bits [6:0] and bit 7 as the 149 + * polarity bit (set to "0" for a negative offset and "1" for a positive 150 + * offset). 151 + */ 152 + if (offset_uv < 0) { 153 + offset_raw |= BIT(7); 154 + st->offset_uv *= (-1); 155 + } 156 + 157 + ret = regmap_write(st->regmap, ADA4250_REG_SNSR_CAL_VAL, offset_raw); 158 + 159 + exit: 160 + mutex_unlock(&st->lock); 161 + 162 + return ret; 163 + } 164 + 165 + static int ada4250_read_raw(struct iio_dev *indio_dev, 166 + struct iio_chan_spec const *chan, 167 + int *val, int *val2, long info) 168 + { 169 + struct ada4250_state *st = iio_priv(indio_dev); 170 + int ret; 171 + 172 + switch (info) { 173 + case IIO_CHAN_INFO_HARDWAREGAIN: 174 + ret = regmap_read(st->regmap, ADA4250_REG_GAIN_MUX, val); 175 + if (ret) 176 + return ret; 177 + 178 + *val = BIT(*val); 179 + 180 + return IIO_VAL_INT; 181 + case IIO_CHAN_INFO_OFFSET: 182 + *val = st->offset_uv; 183 + 184 + return IIO_VAL_INT; 185 + case IIO_CHAN_INFO_CALIBBIAS: 186 + ret = regmap_read(st->regmap, ADA4250_REG_SNSR_CAL_CNFG, val); 187 + if (ret) 188 + return ret; 189 + 190 + *val = FIELD_GET(ADA4250_BIAS_SET_MSK, *val); 191 + 192 + return IIO_VAL_INT; 193 + case IIO_CHAN_INFO_SCALE: 194 + *val = 1; 195 + *val2 = 1000000; 196 + 197 + return IIO_VAL_FRACTIONAL; 198 + default: 199 + return -EINVAL; 200 + } 201 + } 202 + 203 + static int ada4250_write_raw(struct iio_dev *indio_dev, 204 + struct iio_chan_spec const *chan, 205 + int val, int val2, long info) 206 + { 207 + struct ada4250_state *st = iio_priv(indio_dev); 208 + int ret; 209 + 210 + switch (info) { 211 + case IIO_CHAN_INFO_HARDWAREGAIN: 212 + ret = regmap_write(st->regmap, ADA4250_REG_GAIN_MUX, 213 + FIELD_PREP(ADA4250_GAIN_MUX_MSK, ilog2(val))); 214 + if (ret) 215 + return ret; 216 + 217 + st->gain = ilog2(val); 218 + 219 + return ret; 220 + case IIO_CHAN_INFO_OFFSET: 221 + return ada4250_set_offset_uv(indio_dev, chan, val); 222 + case IIO_CHAN_INFO_CALIBBIAS: 223 + ret = regmap_update_bits(st->regmap, ADA4250_REG_SNSR_CAL_CNFG, 224 + ADA4250_BIAS_SET_MSK, 225 + FIELD_PREP(ADA4250_BIAS_SET_MSK, val)); 226 + if (ret) 227 + return ret; 228 + 229 + st->bias = val; 230 + 231 + return ret; 232 + default: 233 + return -EINVAL; 234 + } 235 + } 236 + 237 + static int ada4250_read_avail(struct iio_dev *indio_dev, 238 + struct iio_chan_spec const *chan, 239 + const int **vals, int *type, int *length, 240 + long mask) 241 + { 242 + switch (mask) { 243 + case IIO_CHAN_INFO_CALIBBIAS: 244 + *vals = calibbias_table; 245 + *type = IIO_VAL_INT; 246 + *length = ARRAY_SIZE(calibbias_table); 247 + 248 + return IIO_AVAIL_LIST; 249 + case IIO_CHAN_INFO_HARDWAREGAIN: 250 + *vals = hwgain_table; 251 + *type = IIO_VAL_INT; 252 + *length = ARRAY_SIZE(hwgain_table); 253 + 254 + return IIO_AVAIL_LIST; 255 + default: 256 + return -EINVAL; 257 + } 258 + } 259 + 260 + static int ada4250_reg_access(struct iio_dev *indio_dev, 261 + unsigned int reg, 262 + unsigned int write_val, 263 + unsigned int *read_val) 264 + { 265 + struct ada4250_state *st = iio_priv(indio_dev); 266 + 267 + if (read_val) 268 + return regmap_read(st->regmap, reg, read_val); 269 + else 270 + return regmap_write(st->regmap, reg, write_val); 271 + } 272 + 273 + static const struct iio_info ada4250_info = { 274 + .read_raw = ada4250_read_raw, 275 + .write_raw = ada4250_write_raw, 276 + .read_avail = &ada4250_read_avail, 277 + .debugfs_reg_access = &ada4250_reg_access, 278 + }; 279 + 280 + static const struct iio_chan_spec ada4250_channels[] = { 281 + { 282 + .type = IIO_VOLTAGE, 283 + .output = 1, 284 + .indexed = 1, 285 + .channel = 0, 286 + .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | 287 + BIT(IIO_CHAN_INFO_OFFSET) | 288 + BIT(IIO_CHAN_INFO_CALIBBIAS) | 289 + BIT(IIO_CHAN_INFO_SCALE), 290 + .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBBIAS) | 291 + BIT(IIO_CHAN_INFO_HARDWAREGAIN), 292 + } 293 + }; 294 + 295 + static void ada4250_reg_disable(void *data) 296 + { 297 + regulator_disable(data); 298 + } 299 + 300 + static int ada4250_init(struct ada4250_state *st) 301 + { 302 + int ret; 303 + u16 chip_id; 304 + u8 data[2] __aligned(8) = {}; 305 + struct spi_device *spi = st->spi; 306 + 307 + st->refbuf_en = device_property_read_bool(&spi->dev, "adi,refbuf-enable"); 308 + 309 + st->reg = devm_regulator_get(&spi->dev, "avdd"); 310 + if (IS_ERR(st->reg)) 311 + return dev_err_probe(&spi->dev, PTR_ERR(st->reg), 312 + "failed to get the AVDD voltage\n"); 313 + 314 + ret = regulator_enable(st->reg); 315 + if (ret) { 316 + dev_err(&spi->dev, "Failed to enable specified AVDD supply\n"); 317 + return ret; 318 + } 319 + 320 + ret = devm_add_action_or_reset(&spi->dev, ada4250_reg_disable, st->reg); 321 + if (ret) 322 + return ret; 323 + 324 + ret = regmap_write(st->regmap, ADA4250_REG_RESET, 325 + FIELD_PREP(ADA4250_RESET_MSK, 1)); 326 + if (ret) 327 + return ret; 328 + 329 + ret = regmap_bulk_read(st->regmap, ADA4250_REG_CHIP_ID, data, 2); 330 + if (ret) 331 + return ret; 332 + 333 + chip_id = get_unaligned_le16(data); 334 + 335 + if (chip_id != ADA4250_CHIP_ID) { 336 + dev_err(&spi->dev, "Invalid chip ID.\n"); 337 + return -EINVAL; 338 + } 339 + 340 + return regmap_write(st->regmap, ADA4250_REG_REFBUF_EN, 341 + FIELD_PREP(ADA4250_REFBUF_MSK, st->refbuf_en)); 342 + } 343 + 344 + static int ada4250_probe(struct spi_device *spi) 345 + { 346 + struct iio_dev *indio_dev; 347 + struct regmap *regmap; 348 + struct ada4250_state *st; 349 + int ret; 350 + 351 + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); 352 + if (!indio_dev) 353 + return -ENOMEM; 354 + 355 + regmap = devm_regmap_init_spi(spi, &ada4250_regmap_config); 356 + if (IS_ERR(regmap)) 357 + return PTR_ERR(regmap); 358 + 359 + st = iio_priv(indio_dev); 360 + st->regmap = regmap; 361 + st->spi = spi; 362 + 363 + indio_dev->info = &ada4250_info; 364 + indio_dev->name = "ada4250"; 365 + indio_dev->channels = ada4250_channels; 366 + indio_dev->num_channels = ARRAY_SIZE(ada4250_channels); 367 + 368 + mutex_init(&st->lock); 369 + 370 + ret = ada4250_init(st); 371 + if (ret) { 372 + dev_err(&spi->dev, "ADA4250 init failed\n"); 373 + return ret; 374 + } 375 + 376 + return devm_iio_device_register(&spi->dev, indio_dev); 377 + } 378 + 379 + static const struct spi_device_id ada4250_id[] = { 380 + { "ada4250", 0 }, 381 + {} 382 + }; 383 + MODULE_DEVICE_TABLE(spi, ada4250_id); 384 + 385 + static const struct of_device_id ada4250_of_match[] = { 386 + { .compatible = "adi,ada4250" }, 387 + {}, 388 + }; 389 + MODULE_DEVICE_TABLE(of, ada4250_of_match); 390 + 391 + static struct spi_driver ada4250_driver = { 392 + .driver = { 393 + .name = "ada4250", 394 + .of_match_table = ada4250_of_match, 395 + }, 396 + .probe = ada4250_probe, 397 + .id_table = ada4250_id, 398 + }; 399 + module_spi_driver(ada4250_driver); 400 + 401 + MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com"); 402 + MODULE_DESCRIPTION("Analog Devices ADA4250"); 403 + MODULE_LICENSE("GPL v2");