diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2022-03-18 12:41:32 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2022-03-18 12:41:32 +0100 |
commit | cc6ce5ac2c998d7e869d7289736e0097ce7d2ad1 (patch) | |
tree | c34dfb7eab4193f35db84504f734658efeedb215 /drivers/iio/amplifiers/ada4250.c | |
parent | Merge tag 'icc-5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/dja... (diff) | |
parent | iio: adc: xilinx-ams: Fix single channel switching sequence (diff) | |
download | linux-cc6ce5ac2c998d7e869d7289736e0097ce7d2ad1.tar.xz linux-cc6ce5ac2c998d7e869d7289736e0097ce7d2ad1.zip |
Merge tag 'iio-for-5.18a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into char-misc-next
Jonathan writes:
First set of new device support, fixes, cleanups and features for IIO in 5.18
This cycle we had quite a few series that applied similar changes
to lots of drivers. To keep this description manageable I have
called those out in their own section rather than per driver.
Particularly pleased to see the long running AFE precision series
going in this cycle.
Series includes some late breaking fixes.
New device support
* adi,ada4250 amplifier
- New driver and dt bindings for this programmable gain amplifier.
* adi,admv1014 microwave down-converter
- New driver, dt bindings and some device specific ABI that
may be generalized as more drivers for devices similar to this
are added.
* adi,admv4420 K Band down-converter.
- New driver and dt bindings.
* adi,adxl367 accelerometer driver.
- New driver, dt-bindings + some new IIO ABI definitions to support
reference magnitude events where an estimate of the acceleration
due to gravity has been removed.
- A few fixes as follow up patches.
* adi,ltc2688 DAC with toggle and dither modes.
- New driver and bindings. Includes some new driver specific (for now)
ABI for handling toggle mode and the addition of a dither waveform to
the DAC output.
* AFE (analog front end) add support for additional types of analog device
in front of an ADC.
- RTD temperature sensors with dt bindings.
- Temperature transducers wit dt bindings.
- Related cleanup and features listed in other sections below.
* maxim,ds3502 potentiometer.
- Add support to ds1803 driver which required significant rework.
* mediatek,mt2701-auxadc driver
- Add mediatek,mt8186-auxadc - id table and chip specific info only.
* semtech,sx9324, semtech,ax9360
- Substantial refactoring of sx9310 to extract core logic for reuse
into a separate module
- New driver using this supporting sx9324 proximity sensors.
- New driver using this supporting sx9360 proximity sensors.
* silan,sc7a20
- Compatible with the st,lis2dh (or nearly anyway) so add ID and
chip specific info to enable support. Also silan vendor ID added
for dt-bindings.
Staging graduation
* adi,ad7280a monitoring ADC for stacked lithium-ion batteries in
electric cars and similar.
- Substantial rework of driver required to bring inline with current
IIO best practice. An unusual device in IIO so some interesting features
we may see more of in future.
Multiple driver/core cleanup
- Use sysfs_emit() in simple locations where there is no path to change
to various core created attributes.
- Trivial white space fixes around inconsistency between space after { and
before } in id tables.
- Introduce new handling for fractional types to avoid repeated similar
implementations. Use this in 3 drivers. Note this is also targeted
at future use in the AFE driver and was motivated by discussions
around the precision related work on that driver.
- of related header cleanups - drop of*.h and add mod_devicetable.h as
appropriate.
- Move a number of symbol exports into IIO_* namespaces. Two categories,
1) Library used by multiple drivers e.g. st_sensors
2) Core driver module exporting functions used by bus specific modules.
A few related cleanups in this set.
- Switch from CONFIG_PM_* guards to new DEFINE_SIMPLE_DEV_PM_OPS() and
similar to simplify drivers and take advantage of these new macros
allowing the compiler to do the job or removing unused code without
the need for __maybe_unused markings. Conversion of other drivers to
these new macros ongoing.
Features
* adi,adf4350
- Switch from of specific to generic device properties enabling use with
other firmware types.
* adi,adx345
- Switch from of specific to generic device properties.
- Add ACPI ID ADS0345
- Related driver cleanup.
* adi,hmc425a
- Switch from of specific to generic device properties.
* afe analog rescaler driver
- Wider range of types supported for scale.
- Support offset.
- Kunit tests.
* atlas,ezo-sensor
- Convert from of to device properties.
* fsl,mma8452
- Support mount matrix.
* infineon,dps310:
- Add ACPI ID IFX3100.
* invensense,mpu6050
- Convert to generic device properties.
* maxim,ds1803
- Add out_raw_available before supporting more devices.
- Convert from of specific to device properties.
* samsung,ssp_sensors
- Convert from of specific to device properties.
* st,stm32-timer trigger
- Convert from of specific to device properties.
* ti,hdc101x
- Add ACPI ID TXNW1010.
* ti,tsc2046:
- Add read_raw support to enable use of iio_hwmon and similar.
Fixes / cleanup.
* mailmap
- Update for Cai Huoqing
* MAINTAINERS
- Fix Analog Devices related links.
- Add entry for ADRF6780
- Add entry for ADMV1013
- Add entry for AD7293
- Add entry for ADMV8818
- Update files listed for adis-lib
* iio core:
- Fix wrong comment about current_mode being something a driver should
ever access.
- Use struct_size() rather than open coding in industrialio-hw-consumer
* adi,axl355
- Use units.h definitions instead of local versions.
* adi,adis-lib
- Simplify *updated_bits() macro
- Whitespace cleanup.
* afe - Note many of these fixes only apply to particular configurations
so the problems have probably not been seen in the wild, but will be
visible with new usecases enabled this cycle.
- Fix application of consumer scale for IIO_VAL_INT.
- Apply a scale of 1 when no scale is provided.
- Make best effort to establish a valid offset value for fractional
cases.
- Use s64 for scale calculations where parameters may be signed.
- Tidy up include order.
- Improve accuracy for small fractional sales
- Reduce risk of integer overflow.
* ams,as3935
- Use devm_delayed_work_autocancel() to replace open coded equivalent.
* aspeed,adc
- Fix wrong use of divider flag.
* atmel,sama5d2-adc
- Relax atmel,trigger-edge-type to optional.
- Drop Ludovic Desroches from listed maintainers of the dt-binding
inline with previous MAINTAINERS entry update.
* fsl,mma8452
- Fix probing when i2c_device_id used.
- dev_get_drvdata() on the iio_dev->dev, no longer returns iio_dev.
Use dev_to_iio_dev() instead. Note the original path in here
worked more by luck than design.
* invensense,mpu6050
- Drop ACPI_PTR() protection to avoid an unused warning.
- Use fact ACPI_COMPANION() returns null when ACPI_HANDLE() does to
simplify handling.
* motorola,cpcap-adc
- Drop unused assignment.
* qcom,spmi-adc
- Fix wrong example of 'reg' in binding document.
* renesas,rzg2l-adc
- Trivial typo fix.
* semtech,sx9360
- Fix wrong register handling for event generation.
* st_sensors
- Allow manual disabling of I2C or SPI module if not needed for a particular
board. Default is still to enable the bus specific module if
appropriate bus is supported.
* st,lsm6dsx
- dev_get_drvdata() on the iio_dev->dev, no longer returns iio_dev.
Use dev_to_iio_dev() instead.
* ti,palmas-gpadc
- Split the interrupt fields in the dt-binding example
* ti,tsc2046
- Rework state machine to improve readability after recent debugging of
an issue fixed elsewhere.
- Add a sanity check to avoid very large memory allocations if a crazy
delay is specified.
* ti,twl6030
- Add error handling if devm_request_threaded_irq() fails.
* xilinx,ams
- Use devm_delayed_work_autocancel() instead of open coding equivalent.
- Fix missing required clock entry in dt-binding.
- Fix miss counting of channels resulting in ps channels not
being enabled.
- Fix incorrect values written to sequencer registers.
- Fix sequence for single channel reading.
* tag 'iio-for-5.18a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (245 commits)
iio: adc: xilinx-ams: Fix single channel switching sequence
iio: adc: xilinx-ams: Fixed wrong sequencer register settings
iio: adc: xilinx-ams: Fixed missing PS channels
dt-bindings: iio: adc: zynqmp_ams: Add clock entry
iio: accel: mma8452: use the correct logic to get mma8452_data
iio: adc: aspeed: Add divider flag to fix incorrect voltage reading.
iio: imu: st_lsm6dsx: use dev_to_iio_dev() to get iio_dev struct
dt-bindings: iio: Add ltc2688 documentation
iio: ABI: add ABI file for the LTC2688 DAC
iio: dac: add support for ltc2688
dt-bindings: iio: afe: add bindings for temperature transducers
dt-bindings: iio: afe: add bindings for temperature-sense-rtd
iio: afe: rescale: add temperature transducers
iio: afe: rescale: add RTD temperature sensor support
iio: test: add basic tests for the iio-rescale driver
iio: afe: rescale: reduce risk of integer overflow
iio: afe: rescale: fix accuracy for small fractional scales
iio: afe: rescale: add offset support
iio: afe: rescale: add INT_PLUS_{MICRO,NANO} support
iio: afe: rescale: expose scale processing function
...
Diffstat (limited to 'drivers/iio/amplifiers/ada4250.c')
-rw-r--r-- | drivers/iio/amplifiers/ada4250.c | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/drivers/iio/amplifiers/ada4250.c b/drivers/iio/amplifiers/ada4250.c new file mode 100644 index 000000000000..4b32d350dc5d --- /dev/null +++ b/drivers/iio/amplifiers/ada4250.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADA4250 driver + * + * Copyright 2022 Analog Devices Inc. + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/iio/iio.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#include <asm/unaligned.h> + +/* ADA4250 Register Map */ +#define ADA4250_REG_GAIN_MUX 0x00 +#define ADA4250_REG_REFBUF_EN 0x01 +#define ADA4250_REG_RESET 0x02 +#define ADA4250_REG_SNSR_CAL_VAL 0x04 +#define ADA4250_REG_SNSR_CAL_CNFG 0x05 +#define ADA4250_REG_DIE_REV 0x18 +#define ADA4250_REG_CHIP_ID 0x19 + +/* ADA4250_REG_GAIN_MUX Map */ +#define ADA4250_GAIN_MUX_MSK GENMASK(2, 0) + +/* ADA4250_REG_REFBUF Map */ +#define ADA4250_REFBUF_MSK BIT(0) + +/* ADA4250_REG_RESET Map */ +#define ADA4250_RESET_MSK BIT(0) + +/* ADA4250_REG_SNSR_CAL_VAL Map */ +#define ADA4250_CAL_CFG_BIAS_MSK GENMASK(7, 0) + +/* ADA4250_REG_SNSR_CAL_CNFG Bit Definition */ +#define ADA4250_BIAS_SET_MSK GENMASK(3, 2) +#define ADA4250_RANGE_SET_MSK GENMASK(1, 0) + +/* Miscellaneous definitions */ +#define ADA4250_CHIP_ID 0x4250 +#define ADA4250_RANGE1 0 +#define ADA4250_RANGE4 3 + +/* ADA4250 current bias set */ +enum ada4250_current_bias { + ADA4250_BIAS_DISABLED, + ADA4250_BIAS_BANDGAP, + ADA4250_BIAS_AVDD, +}; + +struct ada4250_state { + struct spi_device *spi; + struct regmap *regmap; + struct regulator *reg; + /* Protect against concurrent accesses to the device and data content */ + struct mutex lock; + u8 bias; + u8 gain; + int offset_uv; + bool refbuf_en; +}; + +/* ADA4250 Current Bias Source Settings: Disabled, Bandgap Reference, AVDD */ +static const int calibbias_table[] = {0, 1, 2}; + +/* ADA4250 Gain (V/V) values: 1, 2, 4, 8, 16, 32, 64, 128 */ +static const int hwgain_table[] = {1, 2, 4, 8, 16, 32, 64, 128}; + +static const struct regmap_config ada4250_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = BIT(7), + .max_register = 0x1A, +}; + +static int ada4250_set_offset_uv(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int offset_uv) +{ + struct ada4250_state *st = iio_priv(indio_dev); + + int i, ret, x[8], max_vos, min_vos, voltage_v, vlsb = 0; + u8 offset_raw, range = ADA4250_RANGE1; + u32 lsb_coeff[6] = {1333, 2301, 4283, 8289, 16311, 31599}; + + if (st->bias == 0 || st->bias == 3) + return -EINVAL; + + voltage_v = regulator_get_voltage(st->reg); + voltage_v = DIV_ROUND_CLOSEST(voltage_v, 1000000); + + if (st->bias == ADA4250_BIAS_AVDD) + x[0] = voltage_v; + else + x[0] = 5; + + x[1] = 126 * (x[0] - 1); + + for (i = 0; i < 6; i++) + x[i + 2] = DIV_ROUND_CLOSEST(x[1] * 1000, lsb_coeff[i]); + + if (st->gain == 0) + return -EINVAL; + + /* + * Compute Range and Voltage per LSB for the Sensor Offset Calibration + * Example of computation for Range 1 and Range 2 (Curren Bias Set = AVDD): + * Range 1 Range 2 + * Gain | Max Vos(mV) | LSB(mV) | Max Vos(mV) | LSB(mV) | + * 2 | X1*127 | X1=0.126(AVDD-1) | X1*3*127 | X1*3 | + * 4 | X2*127 | X2=X1/1.3333 | X2*3*127 | X2*3 | + * 8 | X3*127 | X3=X1/2.301 | X3*3*127 | X3*3 | + * 16 | X4*127 | X4=X1/4.283 | X4*3*127 | X4*3 | + * 32 | X5*127 | X5=X1/8.289 | X5*3*127 | X5*3 | + * 64 | X6*127 | X6=X1/16.311 | X6*3*127 | X6*3 | + * 128 | X7*127 | X7=X1/31.599 | X7*3*127 | X7*3 | + */ + for (i = ADA4250_RANGE1; i <= ADA4250_RANGE4; i++) { + max_vos = x[st->gain] * 127 * ((1 << (i + 1)) - 1); + min_vos = -1 * max_vos; + if (offset_uv > min_vos && offset_uv < max_vos) { + range = i; + vlsb = x[st->gain] * ((1 << (i + 1)) - 1); + break; + } + } + + if (vlsb <= 0) + return -EINVAL; + + offset_raw = DIV_ROUND_CLOSEST(abs(offset_uv), vlsb); + + mutex_lock(&st->lock); + ret = regmap_update_bits(st->regmap, ADA4250_REG_SNSR_CAL_CNFG, + ADA4250_RANGE_SET_MSK, + FIELD_PREP(ADA4250_RANGE_SET_MSK, range)); + if (ret) + goto exit; + + st->offset_uv = offset_raw * vlsb; + + /* + * To set the offset calibration value, use bits [6:0] and bit 7 as the + * polarity bit (set to "0" for a negative offset and "1" for a positive + * offset). + */ + if (offset_uv < 0) { + offset_raw |= BIT(7); + st->offset_uv *= (-1); + } + + ret = regmap_write(st->regmap, ADA4250_REG_SNSR_CAL_VAL, offset_raw); + +exit: + mutex_unlock(&st->lock); + + return ret; +} + +static int ada4250_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ada4250_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_HARDWAREGAIN: + ret = regmap_read(st->regmap, ADA4250_REG_GAIN_MUX, val); + if (ret) + return ret; + + *val = BIT(*val); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + *val = st->offset_uv; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + ret = regmap_read(st->regmap, ADA4250_REG_SNSR_CAL_CNFG, val); + if (ret) + return ret; + + *val = FIELD_GET(ADA4250_BIAS_SET_MSK, *val); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 1; + *val2 = 1000000; + + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } +} + +static int ada4250_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ada4250_state *st = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_HARDWAREGAIN: + ret = regmap_write(st->regmap, ADA4250_REG_GAIN_MUX, + FIELD_PREP(ADA4250_GAIN_MUX_MSK, ilog2(val))); + if (ret) + return ret; + + st->gain = ilog2(val); + + return ret; + case IIO_CHAN_INFO_OFFSET: + return ada4250_set_offset_uv(indio_dev, chan, val); + case IIO_CHAN_INFO_CALIBBIAS: + ret = regmap_update_bits(st->regmap, ADA4250_REG_SNSR_CAL_CNFG, + ADA4250_BIAS_SET_MSK, + FIELD_PREP(ADA4250_BIAS_SET_MSK, val)); + if (ret) + return ret; + + st->bias = val; + + return ret; + default: + return -EINVAL; + } +} + +static int ada4250_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + *vals = calibbias_table; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(calibbias_table); + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_HARDWAREGAIN: + *vals = hwgain_table; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(hwgain_table); + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int ada4250_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int write_val, + unsigned int *read_val) +{ + struct ada4250_state *st = iio_priv(indio_dev); + + if (read_val) + return regmap_read(st->regmap, reg, read_val); + else + return regmap_write(st->regmap, reg, write_val); +} + +static const struct iio_info ada4250_info = { + .read_raw = ada4250_read_raw, + .write_raw = ada4250_write_raw, + .read_avail = &ada4250_read_avail, + .debugfs_reg_access = &ada4250_reg_access, +}; + +static const struct iio_chan_spec ada4250_channels[] = { + { + .type = IIO_VOLTAGE, + .output = 1, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_CALIBBIAS) | + BIT(IIO_CHAN_INFO_HARDWAREGAIN), + } +}; + +static void ada4250_reg_disable(void *data) +{ + regulator_disable(data); +} + +static int ada4250_init(struct ada4250_state *st) +{ + int ret; + u16 chip_id; + u8 data[2] __aligned(8) = {}; + struct spi_device *spi = st->spi; + + st->refbuf_en = device_property_read_bool(&spi->dev, "adi,refbuf-enable"); + + st->reg = devm_regulator_get(&spi->dev, "avdd"); + if (IS_ERR(st->reg)) + return dev_err_probe(&spi->dev, PTR_ERR(st->reg), + "failed to get the AVDD voltage\n"); + + ret = regulator_enable(st->reg); + if (ret) { + dev_err(&spi->dev, "Failed to enable specified AVDD supply\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, ada4250_reg_disable, st->reg); + if (ret) + return ret; + + ret = regmap_write(st->regmap, ADA4250_REG_RESET, + FIELD_PREP(ADA4250_RESET_MSK, 1)); + if (ret) + return ret; + + ret = regmap_bulk_read(st->regmap, ADA4250_REG_CHIP_ID, data, 2); + if (ret) + return ret; + + chip_id = get_unaligned_le16(data); + + if (chip_id != ADA4250_CHIP_ID) { + dev_err(&spi->dev, "Invalid chip ID.\n"); + return -EINVAL; + } + + return regmap_write(st->regmap, ADA4250_REG_REFBUF_EN, + FIELD_PREP(ADA4250_REFBUF_MSK, st->refbuf_en)); +} + +static int ada4250_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct regmap *regmap; + struct ada4250_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + regmap = devm_regmap_init_spi(spi, &ada4250_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + st = iio_priv(indio_dev); + st->regmap = regmap; + st->spi = spi; + + indio_dev->info = &ada4250_info; + indio_dev->name = "ada4250"; + indio_dev->channels = ada4250_channels; + indio_dev->num_channels = ARRAY_SIZE(ada4250_channels); + + mutex_init(&st->lock); + + ret = ada4250_init(st); + if (ret) { + dev_err(&spi->dev, "ADA4250 init failed\n"); + return ret; + } + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ada4250_id[] = { + { "ada4250", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ada4250_id); + +static const struct of_device_id ada4250_of_match[] = { + { .compatible = "adi,ada4250" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ada4250_of_match); + +static struct spi_driver ada4250_driver = { + .driver = { + .name = "ada4250", + .of_match_table = ada4250_of_match, + }, + .probe = ada4250_probe, + .id_table = ada4250_id, +}; +module_spi_driver(ada4250_driver); + +MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com"); +MODULE_DESCRIPTION("Analog Devices ADA4250"); +MODULE_LICENSE("GPL v2"); |