IIO: New device support, features and cleanup for the 6.15 cycle.

The usual mixture of new drivers, support in existing drivers for new
 devices, a range of features and general subsystem cleanup.
 
 Two merges of immutable branches in here:
 * SPI offload support. Culmination of a long effort to bring the ability
   to offload triggered sequences of SPI operations to specific hardware,
   allow high datarate acquisition over an SPI bus (if you have the right
   hardware / FPGA firmware)
 * GPIO set-array-helper - enables code simplification.
 
 New device support
 ==================
 
 adi,ad3552r-hs:
 - Add support for AD3541r and AD3542r via newly supported FPGA HDL.
 adi,ad4030
 - New driver supporting the AD4030, AD4630 AD4630-16, AD4640-24, AD4632-16,
   AD4632-24 1 and 2 channel high precision SPI ADCs.
 adi,ad4851
 - New driver and backend support for the AD4851, AD4852, AD4853, AD4854,
   AD4855, AD4846, AD4857, AD4858 and AD4858I high speed multichannel
   simultaneous sampling ADCs.
 adi,ad7191
 - New driver for this 24-bit ADC for precision bridge applications,
 adi,ad7380
 - Add support for the adaq4381-4 which is a 14-bit version of the
   already supported adaq4380-1
 adi,adis16550
 - New driver using the ADIS library (which needed extensions) for this
   IMU.
 brcm,apds9160
 - New driver for this proximity and ambient light sensor.
 dynaimage,al3000a
 - New driver for this illuminance sensor.
 mcube,mc3230
 - Add support for the mc3510c accelerometer with a different scale to existing
   supported parts (some rework preceded this)
 nxp,imx93
 - Add compatibles for imx94 and imx95 which are fully compatible with imx93.
 rockchip,saradc
 - Add support for the RK3528 ADC
 - Add support for the RK3562 ADC
 silab,si7210
 - New driver to support this I2C Hall effect magnetic position sensor.
 ti,ads7138
 - New driver supporting the ADS7128 and AD7138 I2C ADCs.
 
 Staging driver drop
 ===================
 
 adi,adis16240
 - Drop this impact sensor. Interesting part but complex hence never left
   staging due to ABI challenges. No longer readily available so drop driver.
 
 New features
 ============
 
 Documentation
 - A really nice overview document introduce ADC terminology and how
   it maps to IIO.
 core
 - New description for FAULT events, used in the ad7173.
 - filter_type ABI used in ad4130.
 buffer-dmaengine
 - Split DMA channel request from buffer allocation (for SPI offload)
 - Add a new _with_handle setup variant. (for SPI offload)
 adi,adf4371
 - Add control of reference clock type and support for frequency doubling
   where appropriate.
 adi,ad4695
 - Support SPI offload.
 - Support oversampling control.
 adi,ad5791
 - Support SPI offload.
 adi,ad7124
 - Add channel calibration support.
 adi,ad7380:
 - Alert support (threshold interrupts)
 - SPI offload support.
 adi,ad7606
 - Support writing registers when using backend enabling software control
   of modes.
 adi,ad7944
 - Support SPI offload.
 adi,ad9832
 - Use devm_regulator_get_enable() to simplify code.
 adi,ad9834
 - Use devm_regulator_get_enable() to simplify code.
 adi,adxl345
 - Improve IRQ handling code.
 - Add debug access to registers.
 bosch,bmi270
 - Add temperature channel support.
 - Add data ready trigger.
 google,cross_ec
 - Add trace events.
 mcube,mc3230
 - Add mount matrix support
 - Add an OF match table.
 
 Cleanup and minor bug fixes
 ===========================
 
 Tree wide:
 - Stop using iio_device_claim_direct_scoped() and introduce sparse friendly
   iio_device_claim/release_direct()
 
   The conditional scoped cleanup has proved hard to deal with, requiring
   workarounds for various compiler issues and in is rather non-intuitive
   so abandon that experiment. One of the attractions of that approach was
   that it made it much harder to have unbalanced   claim/release bugs so
   instead introduce a conditional-lock style boolean returning new pair
   of functions. These are inline in the header and have __acquire and
   __release calls allowing sparse to detect lack of balance.  There are
   occasional false positives but so far those have reflected complex code
   paths that benefited from cleanup anyway.
   The first set of driver conversions are in this pull request, more to
   follow next cycle. Various related cleanup in drivers.
   Removal of the _scoped code is completed and the definition removed.
 - Use of str_enable_disable() and similar helpers.
 - Don't set regmap cache to REGCACHE_NONE as that's the default anyway.
 - Change some caches from RBTREE to MAPLE reflecting best practice.
 - Use the new gpiod_multi_set_value_cansleep()
 - Make sure to grab direct mode for some calibrations paths.
 - Avoid using memcmp on structures when checking for matching channel configs.
   Instead just match field by field.
 dt-bindings:
 - Fix up indentation inconsistencies.
 gts-helper:
 - Simplify building of available scale table.
 adi,ad-sigma-delta
 - Make sure to disable channel after calibration done.
 - Add error handling in configuring channel during calibration.
 adi,ad2s1201
 - use a bitmap_write() rather than directly accessing underlying storage.
 adi,ad3552r-hs
 - Fix a wrong error message.
 - Make sure to use instruction mode for configuration.
 adi,ad4695
 - Add a conversion to ensure exit from conversion mode.
 - Use custom regmap to handle required sclk rate change.
 - Fix an out of bounds array access
 - Simplify oversampling ratio handling.
 adi,ad4851
 - Fix a sign bug.
 adi,ad5791
 - Fix wrong exported number of storage bits.
 adi,ad7124
 - Disable all channels at probe to avoid strange initial configurations.
 adi,ad7173
 - Rework to allow static const struct ad_sigma_delta without need
   to make a copy.
 adi,ad7623
 - Drop a BSD license tag that the authors consider unnecessary.
 adi,ad7768-1
 - Fix channels sign description exposed to user space.
 - Set MOSI idle state to avoid accidental device reset.
 - Avoid some overkill locking.
 adi,axi-dac
 - Check if device interface is busy when enabling data stream.
 - Add control of bus mode.
 bosch,bmi270
 - Move a struct definition to a c file as only used there.
 vishay,veml6030
 - Enable regmap cache to reduce bus traffic.
 - Fix ABI bug around scale reporting.
 vishay,vem6075
 - Check array bounds to harden against broken hardware.
 
 Various other minor tweaks and fixes not called out.
 
 *
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAmfTNbkRHGppYzIzQGtl
 cm5lbC5vcmcACgkQVIU0mcT0Foi41Q/+NTlPu1e9TqOqhXmpJc56EP5wnBaKuxMF
 pgPjKr1bbh6Th131kdeCjcTtUyMeLoLLPzcmSzPVVBOLanTHw9d5DQutg7+k0yUE
 8L0FEGEuSJeN6u6hnCFyKhOgu2xSPsCj1L99UCCsfXrvkUILSQgXSzoUkZDonmWj
 gDd/Zfn1Otxevxct9KG4dHhiy17f734Oxhjv3xyMwHWsUtHndAJiYeYRzRxjMJTh
 GeZHZMYvhnseLagAzfp3Dsqc5UCwm/Kmstz8wTlDW0kq/wbZqPwVLiC2xhPnTeds
 PqAL3m/n7Z1SYBa5ouKZYcn2pMfcYNsd3ZfbFMIX0gecGZZ6zRfSgulVDZKe7tp+
 cFbGhsIVWHt4TLWBevz6xihhyv/8ashi0PfIE1Gju8GZqqwwyR6wmdKJ1CNq3DmL
 W9+74w7xAbN+Fg9C/EbuSk/+uKXFrdf9w1NtpnPzKWVwnR0tFwavgpxRCJpu9xMn
 63nO/NCl+FtxibBBrpsTAb03hA4Fw595kiBBvkNSoNOYmoASj+w/bHhIFohZeteP
 pS3eAQlMRCH4ZCjRDNHIEwO9xoja5aqprhnGCNrVQYR+OCfUw5ELf6GwYpEsmvtt
 GyzgR92qxFBQz71h1/VyvAjxcaNRezO92VJajj+T2iJvj5lD6o/0+cscYWfEWEzn
 dxuaXrpF3ak=
 =4tID
 -----END PGP SIGNATURE-----

Merge tag 'iio-for-6.15a' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/jic23/iio into char-misc-next

Jonathan writes:

IIO: New device support, features and cleanup for the 6.15 cycle.

The usual mixture of new drivers, support in existing drivers for new
devices, a range of features and general subsystem cleanup.

Two merges of immutable branches in here:
* SPI offload support. Culmination of a long effort to bring the ability
  to offload triggered sequences of SPI operations to specific hardware,
  allow high datarate acquisition over an SPI bus (if you have the right
  hardware / FPGA firmware)
* GPIO set-array-helper - enables code simplification.

New device support
==================

adi,ad3552r-hs:
- Add support for AD3541r and AD3542r via newly supported FPGA HDL.
adi,ad4030
- New driver supporting the AD4030, AD4630 AD4630-16, AD4640-24, AD4632-16,
  AD4632-24 1 and 2 channel high precision SPI ADCs.
adi,ad4851
- New driver and backend support for the AD4851, AD4852, AD4853, AD4854,
  AD4855, AD4846, AD4857, AD4858 and AD4858I high speed multichannel
  simultaneous sampling ADCs.
adi,ad7191
- New driver for this 24-bit ADC for precision bridge applications,
adi,ad7380
- Add support for the adaq4381-4 which is a 14-bit version of the
  already supported adaq4380-1
adi,adis16550
- New driver using the ADIS library (which needed extensions) for this
  IMU.
brcm,apds9160
- New driver for this proximity and ambient light sensor.
dynaimage,al3000a
- New driver for this illuminance sensor.
mcube,mc3230
- Add support for the mc3510c accelerometer with a different scale to existing
  supported parts (some rework preceded this)
nxp,imx93
- Add compatibles for imx94 and imx95 which are fully compatible with imx93.
rockchip,saradc
- Add support for the RK3528 ADC
- Add support for the RK3562 ADC
silab,si7210
- New driver to support this I2C Hall effect magnetic position sensor.
ti,ads7138
- New driver supporting the ADS7128 and AD7138 I2C ADCs.

Staging driver drop
===================

adi,adis16240
- Drop this impact sensor. Interesting part but complex hence never left
  staging due to ABI challenges. No longer readily available so drop driver.

New features
============

Documentation
- A really nice overview document introduce ADC terminology and how
  it maps to IIO.
core
- New description for FAULT events, used in the ad7173.
- filter_type ABI used in ad4130.
buffer-dmaengine
- Split DMA channel request from buffer allocation (for SPI offload)
- Add a new _with_handle setup variant. (for SPI offload)
adi,adf4371
- Add control of reference clock type and support for frequency doubling
  where appropriate.
adi,ad4695
- Support SPI offload.
- Support oversampling control.
adi,ad5791
- Support SPI offload.
adi,ad7124
- Add channel calibration support.
adi,ad7380:
- Alert support (threshold interrupts)
- SPI offload support.
adi,ad7606
- Support writing registers when using backend enabling software control
  of modes.
adi,ad7944
- Support SPI offload.
adi,ad9832
- Use devm_regulator_get_enable() to simplify code.
adi,ad9834
- Use devm_regulator_get_enable() to simplify code.
adi,adxl345
- Improve IRQ handling code.
- Add debug access to registers.
bosch,bmi270
- Add temperature channel support.
- Add data ready trigger.
google,cross_ec
- Add trace events.
mcube,mc3230
- Add mount matrix support
- Add an OF match table.

Cleanup and minor bug fixes
===========================

Tree wide:
- Stop using iio_device_claim_direct_scoped() and introduce sparse friendly
  iio_device_claim/release_direct()

  The conditional scoped cleanup has proved hard to deal with, requiring
  workarounds for various compiler issues and in is rather non-intuitive
  so abandon that experiment. One of the attractions of that approach was
  that it made it much harder to have unbalanced   claim/release bugs so
  instead introduce a conditional-lock style boolean returning new pair
  of functions. These are inline in the header and have __acquire and
  __release calls allowing sparse to detect lack of balance.  There are
  occasional false positives but so far those have reflected complex code
  paths that benefited from cleanup anyway.
  The first set of driver conversions are in this pull request, more to
  follow next cycle. Various related cleanup in drivers.
  Removal of the _scoped code is completed and the definition removed.
- Use of str_enable_disable() and similar helpers.
- Don't set regmap cache to REGCACHE_NONE as that's the default anyway.
- Change some caches from RBTREE to MAPLE reflecting best practice.
- Use the new gpiod_multi_set_value_cansleep()
- Make sure to grab direct mode for some calibrations paths.
- Avoid using memcmp on structures when checking for matching channel configs.
  Instead just match field by field.
dt-bindings:
- Fix up indentation inconsistencies.
gts-helper:
- Simplify building of available scale table.
adi,ad-sigma-delta
- Make sure to disable channel after calibration done.
- Add error handling in configuring channel during calibration.
adi,ad2s1201
- use a bitmap_write() rather than directly accessing underlying storage.
adi,ad3552r-hs
- Fix a wrong error message.
- Make sure to use instruction mode for configuration.
adi,ad4695
- Add a conversion to ensure exit from conversion mode.
- Use custom regmap to handle required sclk rate change.
- Fix an out of bounds array access
- Simplify oversampling ratio handling.
adi,ad4851
- Fix a sign bug.
adi,ad5791
- Fix wrong exported number of storage bits.
adi,ad7124
- Disable all channels at probe to avoid strange initial configurations.
adi,ad7173
- Rework to allow static const struct ad_sigma_delta without need
  to make a copy.
adi,ad7623
- Drop a BSD license tag that the authors consider unnecessary.
adi,ad7768-1
- Fix channels sign description exposed to user space.
- Set MOSI idle state to avoid accidental device reset.
- Avoid some overkill locking.
adi,axi-dac
- Check if device interface is busy when enabling data stream.
- Add control of bus mode.
bosch,bmi270
- Move a struct definition to a c file as only used there.
vishay,veml6030
- Enable regmap cache to reduce bus traffic.
- Fix ABI bug around scale reporting.
vishay,vem6075
- Check array bounds to harden against broken hardware.

Various other minor tweaks and fixes not called out.

*

* tag 'iio-for-6.15a' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (223 commits)
  doc: iio: ad7380: describe offload support
  iio: ad7380: add support for SPI offload
  iio: light: Add check for array bounds in veml6075_read_int_time_ms
  iio: adc: ti-ads7924 Drop unnecessary function parameters
  staging: iio: ad9834: Use devm_regulator_get_enable()
  staging: iio: ad9832: Use devm_regulator_get_enable()
  iio: gyro: bmg160_spi: add of_match_table
  dt-bindings: iio: adc: Add i.MX94 and i.MX95 support
  iio: adc: ad7768-1: remove unnecessary locking
  Documentation: ABI: add wideband filter type to sysfs-bus-iio
  iio: adc: ad7768-1: set MOSI idle state to prevent accidental reset
  iio: adc: ad7768-1: Fix conversion result sign
  iio: adc: ad7124: Benefit of dev = indio_dev->dev.parent in ad7124_parse_channel_config()
  iio: adc: ad7124: Implement system calibration
  iio: adc: ad7124: Implement internal calibration at probe time
  iio: adc: ad_sigma_delta: Add error checking for ad_sigma_delta_set_channel()
  iio: adc: ad4130: Adapt internal names to match official filter_type ABI
  iio: adc: ad7173: Fix comparison of channel configs
  iio: adc: ad7124: Fix comparison of channel configs
  iio: adc: ad4130: Fix comparison of channel setups
  ...
This commit is contained in:
Greg Kroah-Hartman 2025-03-14 07:15:12 +01:00
commit a425990fa9
181 changed files with 16902 additions and 3104 deletions

View File

@ -2268,7 +2268,7 @@ Description:
representing the sensor unique ID number.
What: /sys/bus/iio/devices/iio:deviceX/filter_type_available
What: /sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_filter_mode_available
What: /sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_filter_type_available
KernelVersion: 6.1
Contact: linux-iio@vger.kernel.org
Description:
@ -2290,6 +2290,16 @@ Description:
* "sinc3+pf2" - Sinc3 + device specific Post Filter 2.
* "sinc3+pf3" - Sinc3 + device specific Post Filter 3.
* "sinc3+pf4" - Sinc3 + device specific Post Filter 4.
* "wideband" - filter with wideband low ripple passband
and sharp transition band.
What: /sys/bus/iio/devices/iio:deviceX/filter_type
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_filter_type
KernelVersion: 6.1
Contact: linux-iio@vger.kernel.org
Description:
Specifies which filter type apply to the channel. The possible
values are given by the filter_type_available attribute.
What: /sys/.../events/in_proximity_thresh_either_runningperiod
KernelVersion: 6.6

View File

@ -0,0 +1,20 @@
What: /sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_filter_mode_available
KernelVersion: 6.2
Contact: linux-iio@vger.kernel.org
Description:
Reading returns a list with the possible filter modes.
This ABI is only kept for backwards compatibility and the values
returned are identical to filter_type_available attribute
documented in Documentation/ABI/testing/sysfs-bus-iio. Please,
use filter_type_available like ABI to provide filter options for
new drivers.
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY-voltageZ_filter_mode
KernelVersion: 6.2
Contact: linux-iio@vger.kernel.org
Description:
This ABI is only kept for backwards compatibility and the values
returned are identical to in_voltageY-voltageZ_filter_type
attribute documented in Documentation/ABI/testing/sysfs-bus-iio.
Please, use in_voltageY-voltageZ_filter_type for new drivers.

View File

@ -0,0 +1,110 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright 2024 Analog Devices Inc.
# Copyright 2024 BayLibre, SAS.
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/adi,ad4030.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD4030 and AD4630 ADC families
maintainers:
- Michael Hennerich <michael.hennerich@analog.com>
- Nuno Sa <nuno.sa@analog.com>
description: |
Analog Devices AD4030 single channel and AD4630/AD4632 dual channel precision
SAR ADC families
* https://www.analog.com/media/en/technical-documentation/data-sheets/ad4030-24-4032-24.pdf
* https://www.analog.com/media/en/technical-documentation/data-sheets/ad4630-24_ad4632-24.pdf
* https://www.analog.com/media/en/technical-documentation/data-sheets/ad4630-16-4632-16.pdf
properties:
compatible:
enum:
- adi,ad4030-24
- adi,ad4032-24
- adi,ad4630-16
- adi,ad4630-24
- adi,ad4632-16
- adi,ad4632-24
reg:
maxItems: 1
spi-max-frequency:
maximum: 102040816
spi-rx-bus-width:
enum: [1, 2, 4]
vdd-5v-supply: true
vdd-1v8-supply: true
vio-supply: true
ref-supply:
description:
Optional External unbuffered reference. Used when refin-supply is not
connected.
refin-supply:
description:
Internal buffered Reference. Used when ref-supply is not connected.
cnv-gpios:
description:
The Convert Input (CNV). It initiates the sampling conversions.
maxItems: 1
reset-gpios:
description:
The Reset Input (/RST). Used for asynchronous device reset.
maxItems: 1
interrupts:
description:
The BUSY pin is used to signal that the conversions results are available
to be transferred when in SPI Clocking Mode. This nodes should be
connected to an interrupt that is triggered when the BUSY line goes low.
maxItems: 1
interrupt-names:
const: busy
required:
- compatible
- reg
- vdd-5v-supply
- vdd-1v8-supply
- vio-supply
- cnv-gpios
oneOf:
- required:
- ref-supply
- required:
- refin-supply
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "adi,ad4030-24";
reg = <0>;
spi-max-frequency = <80000000>;
vdd-5v-supply = <&supply_5V>;
vdd-1v8-supply = <&supply_1_8V>;
vio-supply = <&supply_1_8V>;
ref-supply = <&supply_5V>;
cnv-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
};
};

View File

@ -84,6 +84,10 @@ properties:
description: The Reset Input (RESET). Should be configured GPIO_ACTIVE_LOW.
maxItems: 1
pwms:
description: PWM signal connected to the CNV pin.
maxItems: 1
interrupts:
minItems: 1
items:
@ -106,6 +110,15 @@ properties:
The first cell is the GPn number: 0 to 3.
The second cell takes standard GPIO flags.
'#trigger-source-cells':
description: |
First cell indicates the output signal: 0 = BUSY, 1 = ALERT.
Second cell indicates which GPn pin is used: 0, 2 or 3.
For convenience, macros for these values are available in
dt-bindings/iio/adc/adi,ad4695.h.
const: 2
"#address-cells":
const: 1

View File

@ -0,0 +1,153 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright 2024 Analog Devices Inc.
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/adi,ad4851.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD485X family
maintainers:
- Sergiu Cuciurean <sergiu.cuciurean@analog.com>
- Dragos Bogdan <dragos.bogdan@analog.com>
- Antoniu Miclaus <antoniu.miclaus@analog.com>
description: |
Analog Devices AD485X fully buffered, 8-channel simultaneous sampling,
16/20-bit, 1 MSPS data acquisition system (DAS) with differential, wide
common-mode range inputs.
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4855.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4856.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4857.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4858.pdf
$ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
enum:
- adi,ad4851
- adi,ad4852
- adi,ad4853
- adi,ad4854
- adi,ad4855
- adi,ad4856
- adi,ad4857
- adi,ad4858
- adi,ad4858i
reg:
maxItems: 1
vcc-supply: true
vee-supply: true
vdd-supply: true
vddh-supply: true
vddl-supply: true
vio-supply: true
vrefbuf-supply: true
vrefio-supply: true
pwms:
description: PWM connected to the CNV pin.
maxItems: 1
io-backends:
maxItems: 1
pd-gpios:
maxItems: 1
spi-max-frequency:
maximum: 25000000
'#address-cells':
const: 1
'#size-cells':
const: 0
patternProperties:
"^channel(@[0-7])?$":
$ref: adc.yaml
type: object
description: Represents the channels which are connected to the ADC.
properties:
reg:
description:
The channel number, as specified in the datasheet (from 0 to 7).
minimum: 0
maximum: 7
diff-channels:
description:
Each channel can be configured as a bipolar differential channel.
The ADC uses the same positive and negative inputs for this.
This property must be specified as 'reg' (or the channel number) for
both positive and negative inputs (i.e. diff-channels = <reg reg>).
Since the configuration is bipolar differential, the 'bipolar'
property is required.
items:
minimum: 0
maximum: 7
bipolar: true
required:
- reg
additionalProperties: false
required:
- compatible
- reg
- vcc-supply
- vee-supply
- vdd-supply
- vio-supply
- pwms
unevaluatedProperties: false
examples:
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@0{
#address-cells = <1>;
#size-cells = <0>;
compatible = "adi,ad4858";
reg = <0>;
spi-max-frequency = <10000000>;
vcc-supply = <&vcc>;
vdd-supply = <&vdd>;
vee-supply = <&vee>;
vddh-supply = <&vddh>;
vddl-supply = <&vddl>;
vio-supply = <&vio>;
pwms = <&pwm_gen 0 0>;
io-backends = <&iio_backend>;
channel@0 {
reg = <0>;
diff-channels = <0 0>;
bipolar;
};
channel@1 {
reg = <1>;
};
};
};
...

View File

@ -0,0 +1,149 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
# Copyright 2025 Analog Devices Inc.
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/adi,ad7191.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD7191 ADC
maintainers:
- Alisa-Dariana Roman <alisa.roman@analog.com>
description: |
Bindings for the Analog Devices AD7191 ADC device. Datasheet can be
found here:
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7191.pdf
The device's PDOWN pin must be connected to the SPI controller's chip select
pin.
properties:
compatible:
enum:
- adi,ad7191
reg:
maxItems: 1
spi-cpol: true
spi-cpha: true
clocks:
maxItems: 1
description:
Must be present when CLKSEL pin is tied HIGH to select external clock
source (either a crystal between MCLK1 and MCLK2 pins, or a
CMOS-compatible clock driving MCLK2 pin). Must be absent when CLKSEL pin
is tied LOW to use the internal 4.92MHz clock.
interrupts:
maxItems: 1
avdd-supply:
description: AVdd voltage supply
dvdd-supply:
description: DVdd voltage supply
vref-supply:
description: Vref voltage supply
odr-gpios:
description:
ODR1 and ODR2 pins for output data rate selection. Should be defined if
adi,odr-value is absent.
minItems: 2
maxItems: 2
adi,odr-value:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
Should be present if ODR pins are pin-strapped. Possible values:
120 Hz (ODR1=0, ODR2=0)
60 Hz (ODR1=0, ODR2=1)
50 Hz (ODR1=1, ODR2=0)
10 Hz (ODR1=1, ODR2=1)
If defined, odr-gpios must be absent.
enum: [120, 60, 50, 10]
pga-gpios:
description:
PGA1 and PGA2 pins for gain selection. Should be defined if adi,pga-value
is absent.
minItems: 2
maxItems: 2
adi,pga-value:
$ref: /schemas/types.yaml#/definitions/uint32
description: |
Should be present if PGA pins are pin-strapped. Possible values:
Gain 1 (PGA1=0, PGA2=0)
Gain 8 (PGA1=0, PGA2=1)
Gain 64 (PGA1=1, PGA2=0)
Gain 128 (PGA1=1, PGA2=1)
If defined, pga-gpios must be absent.
enum: [1, 8, 64, 128]
temp-gpios:
description: TEMP pin for temperature sensor enable.
maxItems: 1
chan-gpios:
description: CHAN pin for input channel selection.
maxItems: 1
required:
- compatible
- reg
- interrupts
- avdd-supply
- dvdd-supply
- vref-supply
- spi-cpol
- spi-cpha
- temp-gpios
- chan-gpios
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
- oneOf:
- required:
- adi,odr-value
- required:
- odr-gpios
- oneOf:
- required:
- adi,pga-value
- required:
- pga-gpios
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "adi,ad7191";
reg = <0>;
spi-max-frequency = <1000000>;
spi-cpol;
spi-cpha;
clocks = <&ad7191_mclk>;
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
interrupt-parent = <&gpio>;
avdd-supply = <&avdd>;
dvdd-supply = <&dvdd>;
vref-supply = <&vref>;
adi,pga-value = <1>;
odr-gpios = <&gpio 23 GPIO_ACTIVE_HIGH>, <&gpio 24 GPIO_ACTIVE_HIGH>;
temp-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>;
chan-gpios = <&gpio 27 GPIO_ACTIVE_HIGH>;
};
};

View File

@ -27,6 +27,7 @@ description: |
* https://www.analog.com/en/products/ad7388-4.html
* https://www.analog.com/en/products/adaq4370-4.html
* https://www.analog.com/en/products/adaq4380-4.html
* https://www.analog.com/en/products/adaq4381-4.html
$ref: /schemas/spi/spi-peripheral-props.yaml#
@ -50,6 +51,7 @@ properties:
- adi,ad7388-4
- adi,adaq4370-4
- adi,adaq4380-4
- adi,adaq4381-4
reg:
maxItems: 1
@ -201,6 +203,7 @@ allOf:
- adi,ad7380-4
- adi,adaq4370-4
- adi,adaq4380-4
- adi,adaq4381-4
then:
properties:
refio-supply: false
@ -218,6 +221,7 @@ allOf:
enum:
- adi,adaq4370-4
- adi,adaq4380-4
- adi,adaq4381-4
then:
required:
- vs-p-supply

View File

@ -17,13 +17,25 @@ description: |
interface for the actual ADC, while this IP core will interface
to the data-lines of the ADC and handle the streaming of data into
memory via DMA.
In some cases, the AXI ADC interface is used to perform specialized
operation to a particular ADC, e.g access the physical bus through
specific registers to write ADC registers.
In this case, we use a different compatible which indicates the target
IP core's name.
The following IP is currently supported:
- AXI AD7606x: specialized version of the IP core for all the chips from
the ad7606 family.
https://wiki.analog.com/resources/fpga/docs/axi_adc_ip
https://analogdevicesinc.github.io/hdl/library/axi_ad485x/index.html
http://analogdevicesinc.github.io/hdl/library/axi_ad7606x/index.html
properties:
compatible:
enum:
- adi,axi-adc-10.0.a
- adi,axi-ad7606x
- adi,axi-ad485x
reg:
maxItems: 1
@ -47,17 +59,48 @@ properties:
'#io-backend-cells':
const: 0
'#address-cells':
const: 1
'#size-cells':
const: 0
patternProperties:
"^adc@[0-9a-f]+$":
type: object
properties:
reg:
maxItems: 1
additionalProperties: true
required:
- compatible
- reg
required:
- compatible
- dmas
- reg
- clocks
allOf:
- if:
properties:
compatible:
not:
contains:
const: adi,axi-ad7606x
then:
properties:
'#address-cells': false
'#size-cells': false
patternProperties:
"^adc@[0-9a-f]+$": false
additionalProperties: false
examples:
- |
axi-adc@44a00000 {
adc@44a00000 {
compatible = "adi,axi-adc-10.0.a";
reg = <0x44a00000 0x10000>;
dmas = <&rx_dma 0>;
@ -65,4 +108,31 @@ examples:
clocks = <&axi_clk>;
#io-backend-cells = <0>;
};
- |
#include <dt-bindings/gpio/gpio.h>
parallel_bus_controller@44a00000 {
compatible = "adi,axi-ad7606x";
reg = <0x44a00000 0x10000>;
dmas = <&rx_dma 0>;
dma-names = "rx";
clocks = <&ext_clk>;
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "adi,ad7606b";
reg = <0>;
pwms = <&axi_pwm_gen 0 0>;
pwm-names = "convst1";
avcc-supply = <&adc_vref>;
vdrive-supply = <&vdd_supply>;
reset-gpios = <&gpio0 91 GPIO_ACTIVE_HIGH>;
standby-gpios = <&gpio0 90 GPIO_ACTIVE_LOW>;
adi,range-gpios = <&gpio0 89 GPIO_ACTIVE_HIGH>;
adi,oversampling-ratio-gpios = <&gpio0 88 GPIO_ACTIVE_HIGH
&gpio0 87 GPIO_ACTIVE_HIGH
&gpio0 86 GPIO_ACTIVE_HIGH>;
io-backends = <&parallel_bus_controller>;
};
};
...

View File

@ -19,7 +19,14 @@ description:
properties:
compatible:
const: nxp,imx93-adc
oneOf:
- enum:
- nxp,imx93-adc
- items:
- enum:
- nxp,imx94-adc
- nxp,imx95-adc
- const: nxp,imx93-adc
reg:
maxItems: 1

View File

@ -15,6 +15,8 @@ properties:
- const: rockchip,saradc
- const: rockchip,rk3066-tsadc
- const: rockchip,rk3399-saradc
- const: rockchip,rk3528-saradc
- const: rockchip,rk3562-saradc
- const: rockchip,rk3588-saradc
- items:
- const: rockchip,rk3576-saradc

View File

@ -0,0 +1,63 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/ti,ads7138.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments ADS7128/ADS7138 analog-to-digital converter (ADC)
maintainers:
- Tobias Sperling <tobias.sperling@softing.com>
description: |
The ADS7128 and ADS7138 chips are 12-bit, 8 channel analog-to-digital
converters (ADC) with build-in digital window comparator (DWC), using the
I2C interface.
ADS7128 differs in the addition of further hardware features, like a
root-mean-square (RMS) and a zero-crossing-detect (ZCD) module.
Datasheets:
https://www.ti.com/product/ADS7128
https://www.ti.com/product/ADS7138
properties:
compatible:
enum:
- ti,ads7128
- ti,ads7138
reg:
maxItems: 1
avdd-supply:
description:
The regulator used as analog supply voltage as well as reference voltage.
interrupts:
description:
Interrupt on ALERT pin, triggers on low level.
maxItems: 1
required:
- compatible
- reg
- avdd-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
adc@10 {
compatible = "ti,ads7138";
reg = <0x10>;
avdd-supply = <&reg_stb_3v3>;
interrupt-parent = <&gpio2>;
interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
};
};
...

View File

@ -55,18 +55,18 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
dac@0 {
reg = <0>;
compatible = "adi,ad5390-5";
vref-supply = <&dacvref>;
reg = <0>;
compatible = "adi,ad5390-5";
vref-supply = <&dacvref>;
};
};
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
dac@42 {
reg = <0x42>;
compatible = "adi,ad5380-3";
};
#address-cells = <1>;
#size-cells = <0>;
dac@42 {
reg = <0x42>;
compatible = "adi,ad5380-3";
};
};
...

View File

@ -30,8 +30,9 @@ properties:
clock-names:
description:
Must be "clkin"
maxItems: 1
Must be "clkin" if the input reference is single ended or "clkin-diff"
if the input reference is differential.
enum: [clkin, clkin-diff]
adi,mute-till-lock-en:
type: boolean

View File

@ -43,13 +43,13 @@ additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
#address-cells = <1>;
#size-cells = <0>;
temperature-sensor@43 {
compatible = "sciosense,ens210";
reg = <0x43>;
};
temperature-sensor@43 {
compatible = "sciosense,ens210";
reg = <0x43>;
};
};
...

View File

@ -0,0 +1,74 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/imu/adi,adis16550.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices ADIS16550 and similar IMUs
maintainers:
- Nuno Sa <nuno.sa@analog.com>
- Ramona Gradinariu <ramona.gradinariu@analog.com>
- Antoniu Miclaus <antoniu.miclaus@analog.com>
- Robert Budai <robert.budai@analog.com>
properties:
compatible:
enum:
- adi,adis16550
reg:
maxItems: 1
spi-cpha: true
spi-cpol: true
spi-max-frequency:
maximum: 15000000
vdd-supply: true
interrupts:
maxItems: 1
reset-gpios:
description:
Active low RESET pin.
maxItems: 1
clocks:
description: If not provided, then the internal clock is used.
maxItems: 1
required:
- compatible
- reg
- interrupts
- spi-cpha
- spi-cpol
- spi-max-frequency
- vdd-supply
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
imu@0 {
compatible = "adi,adis16550";
reg = <0>;
spi-max-frequency = <15000000>;
spi-cpol;
spi-cpha;
vdd-supply = <&vdd>;
interrupts = <4 IRQ_TYPE_EDGE_FALLING>;
interrupt-parent = <&gpio>;
};
};

View File

@ -0,0 +1,78 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/light/brcm,apds9160.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Broadcom Combined Proximity & Ambient light sensor
maintainers:
- Mikael Gonella-Bolduc <m.gonella.bolduc@gmail.com>
description: |
Datasheet: https://docs.broadcom.com/docs/APDS-9160-003-DS
properties:
compatible:
enum:
- brcm,apds9160
reg:
maxItems: 1
interrupts:
maxItems: 1
vdd-supply: true
ps-cancellation-duration:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Proximity sensor cancellation pulse duration in half clock cycles.
This parameter determines a cancellation pulse duration.
The cancellation is applied in the integration phase to cancel out
unwanted reflected light from very near objects such as tempered glass
in front of the sensor.
default: 0
maximum: 63
ps-cancellation-current-picoamp:
description:
Proximity sensor crosstalk cancellation current in picoampere.
This parameter adjusts the current in steps of 2400 pA up to 276000 pA.
The provided value must be a multiple of 2400 and in one of these ranges
[60000 - 96000]
[120000 - 156000]
[180000 - 216000]
[240000 - 276000]
This parameter is used in conjunction with the cancellation duration.
minimum: 60000
maximum: 276000
multipleOf: 2400
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
light-sensor@53 {
compatible = "brcm,apds9160";
reg = <0x53>;
vdd-supply = <&vdd_reg>;
interrupts = <29 IRQ_TYPE_EDGE_FALLING>;
interrupt-parent = <&pinctrl>;
ps-cancellation-duration = <10>;
ps-cancellation-current-picoamp = <62400>;
};
};
...

View File

@ -4,14 +4,16 @@
$id: http://devicetree.org/schemas/iio/light/dynaimage,al3010.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Dyna-Image AL3010 sensor
title: Dyna-Image AL3000a/AL3010 sensor
maintainers:
- David Heidelberg <david@ixit.cz>
properties:
compatible:
const: dynaimage,al3010
enum:
- dynaimage,al3000a
- dynaimage,al3010
reg:
maxItems: 1

View File

@ -0,0 +1,48 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/magnetometer/silabs,si7210.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Si7210 magnetic position and temperature sensor
maintainers:
- Antoni Pokusinski <apokusinski01@gmail.com>
description: |
Silabs Si7210 I2C Hall effect magnetic position and temperature sensor.
https://www.silabs.com/documents/public/data-sheets/si7210-datasheet.pdf
properties:
compatible:
const: silabs,si7210
reg:
maxItems: 1
interrupts:
maxItems: 1
vdd-supply:
description: Regulator that provides power to the sensor
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
magnetometer@30 {
compatible = "silabs,si7210";
reg = <0x30>;
interrupt-parent = <&gpio1>;
interrupts = <4 IRQ_TYPE_EDGE_FALLING>;
vdd-supply = <&vdd_3v3_reg>;
};
};

View File

@ -40,15 +40,15 @@ unevaluatedProperties: false
examples:
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
#address-cells = <1>;
#size-cells = <0>;
temperature-sensor@0 {
compatible = "maxim,max31865";
reg = <0>;
spi-max-frequency = <400000>;
spi-cpha;
maxim,3-wire;
};
temperature-sensor@0 {
compatible = "maxim,max31865";
reg = <0>;
spi-max-frequency = <400000>;
spi-cpha;
maxim,3-wire;
};
};
...

View File

@ -44,8 +44,8 @@ examples:
#size-cells = <0>;
tmp117@48 {
compatible = "ti,tmp117";
reg = <0x48>;
vcc-supply = <&pmic_reg_3v3>;
compatible = "ti,tmp117";
reg = <0x48>;
vcc-supply = <&pmic_reg_3v3>;
};
};

View File

@ -0,0 +1,37 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/trigger-source/pwm-trigger.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Generic trigger source using PWM
description: Remaps a PWM channel as a trigger source.
maintainers:
- David Lechner <dlechner@baylibre.com>
properties:
compatible:
const: pwm-trigger
'#trigger-source-cells':
const: 0
pwms:
maxItems: 1
required:
- compatible
- '#trigger-source-cells'
- pwms
additionalProperties: false
examples:
- |
trigger {
compatible = "pwm-trigger";
#trigger-source-cells = <0>;
pwms = <&pwm 0 1000000 0>;
};

View File

@ -187,6 +187,8 @@ properties:
- maxim,max6621
# mCube 3-axis 8-bit digital accelerometer
- mcube,mc3230
# mCube 3-axis 8-bit digital accelerometer
- mcube,mc3510c
# Measurement Specialities I2C temperature and humidity sensor
- meas,htu21
# Measurement Specialities I2C pressure and temperature sensor

View File

@ -0,0 +1,180 @@
.. SPDX-License-Identifier: GPL-2.0-only
=============
AD4030 driver
=============
ADC driver for Analog Devices Inc. AD4030 and similar devices. The module name
is ``ad4030``.
Supported devices
=================
The following chips are supported by this driver:
* `AD4030-24 <https://www.analog.com/AD4030-24>`_
* `AD4032-24 <https://www.analog.com/AD4032-24>`_
* `AD4630-16 <https://www.analog.com/AD4630-16>`_
* `AD4630-24 <https://www.analog.com/AD4630-24>`_
* `AD4632-16 <https://www.analog.com/AD4632-16>`_
* `AD4632-24 <https://www.analog.com/AD4632-24>`_
IIO channels
============
Each "hardware" channel as described in the datasheet is split in 2 IIO
channels:
- One channel for the differential data
- One channel for the common byte.
The possible IIO channels depending on the numbers of "hardware" channel are:
+------------------------------------+------------------------------------+
| 1 channel ADC | 2 channels ADC |
+====================================+====================================+
| - voltage0-voltage1 (differential) | - voltage0-voltage1 (differential) |
| - voltage2 (common-mode) | - voltage2-voltage3 (differential) |
| | - voltage4 (common-mode) |
| | - voltage5 (common-mode) |
+------------------------------------+------------------------------------+
Labels
------
For ease of use, the IIO channels provide a label. For a differential channel,
the label is ``differentialN`` where ``N`` is the "hardware" channel id. For a
common-mode channel, the label is ``common-modeN`` where ``N`` is the
"hardware" channel id.
The possible labels are:
+-----------------+-----------------+
| 1 channel ADC | 2 channels ADC |
+=================+=================+
| - differential0 | - differential0 |
| - common-mode0 | - differential1 |
| | - common-mode0 |
| | - common-mode1 |
+-----------------+-----------------+
Supported features
==================
SPI wiring modes
----------------
The driver currently supports the following SPI wiring configurations:
One lane mode
^^^^^^^^^^^^^
In this mode, each channel has its own SDO line to send the conversion results.
At the moment this mode can only be used on AD4030 which has one channel so only
one SDO line is used.
.. code-block::
+-------------+ +-------------+
| ADC | | HOST |
| | | |
| CNV |<--------| CNV |
| CS |<--------| CS |
| SDI |<--------| SDO |
| SDO0 |-------->| SDI |
| SCLK |<--------| SCLK |
+-------------+ +-------------+
Interleaved mode
^^^^^^^^^^^^^^^^
In this mode, both channels conversion results are bit interleaved one SDO line.
As such the wiring is the same as `One lane mode`_.
SPI Clock mode
--------------
Only the SPI clocking mode is supported.
Output modes
------------
There are more exposed IIO channels than channels as describe in the devices
datasheet. This is due to the `Differential data + common-mode`_ encoding
2 types of information in one conversion result. As such a "device" channel
provides 2 IIO channels, one for the differential data and one for the common
byte.
Differential data
^^^^^^^^^^^^^^^^^
This mode is selected when:
- Only differential channels are enabled in a buffered read
- Oversampling attribute is set to 1
Differential data + common-mode
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This mode is selected when:
- Differential and common-mode channels are enabled in a buffered read
- Oversampling attribute is set to 1
For the 24-bits chips, this mode is also available with 16-bits differential
data but is not selectable yet.
Averaged differential data
^^^^^^^^^^^^^^^^^^^^^^^^^^
This mode is selected when:
- Only differential channels are selected enabled in a buffered read
- Oversampling attribute is greater than 1
Digital Gain and Offset
-----------------------
Each differential data channel has a 16-bits unsigned configurable hardware
gain applied to it. By default it's equal to 1. Note that applying gain can
cause numerical saturation.
Each differential data channel has a signed configurable hardware offset.
For the ADCs ending in ``-24``, the gain is encoded on 24-bits.
Likewise, the ADCs ending in ``-16`` have a gain encoded on 16-bits. Note that
applying an offset can cause numerical saturation.
The final differential data returned by the ADC is computed by first applying
the gain, then the offset.
The gain is controlled by the ``calibscale`` IIO attribute while the offset is
controlled by the ``calibbias`` attribute.
Reference voltage
-----------------
The chip supports an external reference voltage via the ``REF`` input or an
internal buffered reference voltage via the ``REFIN`` input. The driver looks
at the device tree to determine which is being used. If ``ref-supply`` is
present, then the external reference voltage is used and the internal buffer is
disabled. If ``refin-supply`` is present, then the internal buffered reference
voltage is used.
Reset
-----
Both hardware and software reset are supported. The driver looks first at the
device tree to see if the ``reset-gpio`` is populated.
If not present, the driver will fallback to a software reset by wiring to the
device's registers.
Unimplemented features
----------------------
- ``BUSY`` indication
- Additional wiring modes
- Additional clock modes
- Differential data 16-bits + common-mode for 24-bits chips
- Overrange events
- Test patterns

View File

@ -47,6 +47,36 @@ In this mode, CNV and CS are tied together and there is a single SDO line.
To use this mode, in the device tree, omit the ``cnv-gpios`` and
``spi-rx-bus-width`` properties.
SPI offload wiring
^^^^^^^^^^^^^^^^^^
When used with a SPI offload, the supported wiring configuration is:
.. code-block::
+-------------+ +-------------+
| GP0/BUSY |-------->| TRIGGER |
| CS |<--------| CS |
| | | |
| ADC | | SPI |
| | | |
| SDI |<--------| SDO |
| SDO |-------->| SDI |
| SCLK |<--------| SCLK |
| | | |
| | +-------------+
| CNV |<-----+--| PWM |
| | +--| GPIO |
+-------------+ +-------------+
In this case, both the ``cnv-gpios`` and ``pwms`` properties are required.
The ``#trigger-source-cells = <2>`` property is also required to connect back
to the SPI offload. The SPI offload will have ``trigger-sources`` property
with cells to indicate the busy signal and which GPx pin is used, e.g
``<&ad4695 AD4695_TRIGGER_EVENT_BUSY AD4695_TRIGGER_PIN_GP0>``.
.. seealso:: `SPI offload support`_
Channel configuration
---------------------
@ -149,15 +179,62 @@ Gain/offset calibration
System calibration is supported using the channel gain and offset registers via
the ``calibscale`` and ``calibbias`` attributes respectively.
Oversampling
------------
The chip supports per-channel oversampling when SPI offload is being used, with
available oversampling ratios (OSR) of 1 (default), 4, 16, and 64. Enabling
oversampling on a channel raises the effective number of bits of sampled data to
17 (OSR == 4), 18 (16), or 19 (64), respectively. This can be set via the
``oversampling_ratio`` attribute.
Setting the oversampling ratio for a channel also changes the sample rate for
that channel, since it requires multiple conversions per 1 sample. Specifically,
the new sampling frequency is the PWM sampling frequency divided by the
particular OSR. This is set automatically by the driver when setting the
``oversampling_ratio`` attribute. For example, if the device's current
``sampling_frequency`` is 10000 and an OSR of 4 is set on channel ``voltage0``,
the new reported sampling rate for that channel will be 2500 (ignoring PWM API
rounding), while all others will remain at 10000. Subsequently setting the
sampling frequency to a higher value on that channel will adjust the CNV trigger
period for all channels, e.g. if ``voltage0``'s sampling frequency is adjusted
from 2500 (with an OSR of 4) to 10000, the value reported by
``in_voltage0_sampling_frequency`` will be 10000, but all other channels will
now report 40000.
For simplicity, the sampling frequency of the device should be set (considering
the highest desired OSR value to be used) first, before configuring oversampling
for specific channels.
Unimplemented features
----------------------
- Additional wiring modes
- Threshold events
- Oversampling
- GPIO support
- CRC support
SPI offload support
===================
To be able to achieve the maximum sample rate, the driver can be used with the
`AXI SPI Engine`_ to provide SPI offload support.
.. _AXI SPI Engine: http://analogdevicesinc.github.io/hdl/projects/ad469x_fmc/index.html
.. seealso:: `SPI offload wiring`_
When SPI offload is being used, some attributes will be different.
* ``trigger`` directory is removed.
* ``in_voltage0_sampling_frequency`` attributes are added for setting the sample
rate.
* ``in_voltage0_sampling_frequency_available`` attributes are added for querying
the max sample rate.
* ``timestamp`` channel is removed.
* Buffer data format may be different compared to when offload is not used,
e.g. the ``buffer0/in_voltage0_type`` attribute.
Device buffers
==============
@ -165,3 +242,28 @@ This driver supports hardware triggered buffers. This uses the "advanced
sequencer" feature of the chip to trigger a burst of conversions.
Also see :doc:`iio_devbuf` for more general information.
Effective sample rate for buffered reads
----------------------------------------
When SPI offload is not used, the sample rate is determined by the trigger that
is manually configured in userspace. All enabled channels will be read in a
burst when the trigger is received.
When SPI offload is used, the sample rate is configured per channel. All
channels will have the same rate, so only one ``in_voltageY_sampling_frequency``
attribute needs to be set. Since this rate determines the delay between each
individual conversion, the effective sample rate for each sample is actually
the sum of the periods of each enabled channel in a buffered read. In other
words, it is the value of the ``in_voltageY_sampling_frequency`` attribute
divided by the number of enabled channels. So if 4 channels are enabled, with
the ``in_voltageY_sampling_frequency`` attributes set to 1 MHz, the effective
sample rate is 250 kHz.
With oversampling enabled, the effective sample rate also depends on the OSR
assigned to each channel. For example, if one of the 4 channels mentioned in the
previous case is configured with an OSR of 4, the effective sample rate for that
channel becomes (1 MHz / 4 ) = 250 kHz. The effective sample rate for all
four channels is then 1 / ( (3 / 1 MHz) + ( 1 / 250 kHz) ) ~= 142.9 kHz. Note
that in this case "sample" refers to one read of all enabled channels (i.e. one
full cycle through the auto-sequencer).

View File

@ -0,0 +1,119 @@
.. SPDX-License-Identifier: GPL-2.0-only
=============
AD7191 driver
=============
Device driver for Analog Devices AD7191 ADC.
Supported devices
=================
* `AD7191 <https://www.analog.com/AD7191>`_
The AD7191 is a high precision, low noise, 24-bit Σ-Δ ADC with integrated PGA.
It features two differential input channels, an internal temperature sensor, and
configurable sampling rates.
Devicetree
==========
Pin Configuration
-----------------
The driver supports both pin-strapped and GPIO-controlled configurations for ODR
(Output Data Rate) and PGA (Programmable Gain Amplifier) settings. These
configurations are mutually exclusive - you must use either pin-strapped or GPIO
control for each setting, not both.
ODR Configuration
^^^^^^^^^^^^^^^^^
The ODR can be configured either through GPIO control or pin-strapping:
- When using GPIO control, specify the "odr-gpios" property in the device tree
- For pin-strapped configuration, specify the "adi,odr-value" property in the
device tree
Available ODR settings:
- 120 Hz (ODR1=0, ODR2=0)
- 60 Hz (ODR1=0, ODR2=1)
- 50 Hz (ODR1=1, ODR2=0)
- 10 Hz (ODR1=1, ODR2=1)
PGA Configuration
^^^^^^^^^^^^^^^^^
The PGA can be configured either through GPIO control or pin-strapping:
- When using GPIO control, specify the "pga-gpios" property in the device tree
- For pin-strapped configuration, specify the "adi,pga-value" property in the
device tree
Available PGA gain settings:
- 1x (PGA1=0, PGA2=0)
- 8x (PGA1=0, PGA2=1)
- 64x (PGA1=1, PGA2=0)
- 128x (PGA1=1, PGA2=1)
Clock Configuration
-------------------
The AD7191 supports both internal and external clock sources:
- When CLKSEL pin is tied LOW: Uses internal 4.92MHz clock (no clock property
needed)
- When CLKSEL pin is tied HIGH: Requires external clock source
- Can be a crystal between MCLK1 and MCLK2 pins
- Or a CMOS-compatible clock driving MCLK2 pin
- Must specify the "clocks" property in device tree when using external clock
SPI Interface Requirements
--------------------------
The AD7191 has specific SPI interface requirements:
- The DOUT/RDY output is dual-purpose and requires SPI bus locking
- DOUT/RDY must be connected to an interrupt-capable GPIO
- The SPI controller's chip select must be connected to the PDOWN pin of the ADC
- When CS (PDOWN) is high, the device powers down and resets internal circuitry
- SPI mode 3 operation (CPOL=1, CPHA=1) is required
Power Supply Requirements
-------------------------
The device requires the following power supplies:
- AVdd: Analog power supply
- DVdd: Digital power supply
- Vref: Reference voltage supply (external)
All power supplies must be specified in the device tree.
Channel Configuration
=====================
The device provides three channels:
1. Temperature Sensor
- 24-bit unsigned
- Internal temperature measurement
- Temperature in millidegrees Celsius
2. Differential Input (AIN1-AIN2)
- 24-bit unsigned
- Differential voltage measurement
- Configurable gain via PGA
3. Differential Input (AIN3-AIN4)
- 24-bit unsigned
- Differential voltage measurement
- Configurable gain via PGA
Buffer Support
==============
This driver supports IIO triggered buffers. See Documentation/iio/iio_devbuf.rst
for more information about IIO triggered buffers.

View File

@ -29,6 +29,7 @@ The following chips are supported by this driver:
* `AD7388-4 <https://www.analog.com/en/products/ad7388-4.html>`_
* `ADAQ4370-4 <https://www.analog.com/en/products/adaq4370-4.html>`_
* `ADAQ4380-4 <https://www.analog.com/en/products/adaq4380-4.html>`_
* `ADAQ4381-4 <https://www.analog.com/en/products/adaq4381-4.html>`_
Supported features
@ -52,8 +53,8 @@ declared in the device tree as ``refin-supply``.
ADAQ devices
~~~~~~~~~~~~
adaq4370-4 and adaq4380-4 don't have an external reference, but use a 3.3V
internal reference derived from one of its supplies (``refin-supply``)
ADAQ devices don't have an external reference, but use a 3.3V internal reference
derived from one of its supplies (``refin-supply``)
All other devices from ad738x family
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -92,6 +93,38 @@ must restart iiod using the following command:
root:~# systemctl restart iiod
Alert
-----
2 channels variants of the ad738x family, can use the SDOB line as an alert pin
when configured in 1 SDO line mode. 4 channels variants, can use SDOD as an
alert pin when configured in 1 or 2 SDO line(s) mode, although only 1 SDO line
mode is currently supported by the driver (see `SPI wiring modes`_).
At the end of a conversion the active-low alert pin gets asserted if the
conversion result exceeds the alert high limit or falls below the alert low
limit. It is cleared, on a falling edge of CS. The alert pin is common to all
channels.
User can enable alert using the regular iio events attribute:
.. code-block:: bash
events/thresh_either_en
The high and low thresholds are common to all channels and can also be set using
regular iio events attributes:
.. code-block:: bash
events/in_thresh_falling_value
events/in_thresh_rising_value
If debugfs is available, user can read the ALERT register to determine the
faulty channel and direction.
In most use cases, user will hardwire the alert pin to trigger a shutdown.
Channel selection and sequencer (single-end chips only)
-------------------------------------------------------
@ -144,8 +177,25 @@ Unimplemented features
- Rolling average oversampling
- Power down mode
- CRC indication
- Alert
SPI offload support
===================
To be able to achieve the maximum sample rate, the driver can be used with the
`AXI SPI Engine`_ to provide SPI offload support.
.. _AXI SPI Engine: http://analogdevicesinc.github.io/hdl/projects/pulsar_adc/index.html
When SPI offload is being used, some attributes will be different.
* ``trigger`` directory is removed.
* ``in_voltage0_sampling_frequency`` attribute is added for setting the sample
rate.
* ``in_voltage0_sampling_frequency_available`` attribute is added for querying
the max sample rate.
* ``timestamp`` channel is removed.
* Buffer data format may be different compared to when offload is not used,
e.g. the ``in_voltage0_type`` attribute.
Device buffers
==============

View File

@ -46,6 +46,8 @@ CS mode, 3-wire, without busy indicator
To select this mode in the device tree, set the ``adi,spi-mode`` property to
``"single"`` and omit the ``cnv-gpios`` property.
This is the only wiring configuration supported when using `SPI offload support`_.
CS mode, 4-wire, without busy indicator
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -106,7 +108,6 @@ Unimplemented features
----------------------
- ``BUSY`` indication
- ``TURBO`` mode
Device attributes
@ -147,6 +148,27 @@ AD7986 is a fully-differential ADC and has the following attributes:
In "chain" mode, additional chips will appear as additional voltage input
channels, e.g. ``in_voltage2-voltage3_raw``.
SPI offload support
===================
To be able to achieve the maximum sample rate, the driver can be used with the
`AXI SPI Engine`_ to provide SPI offload support.
.. _AXI SPI Engine: http://analogdevicesinc.github.io/hdl/projects/pulsar_adc/index.html
When SPI offload is being used, some attributes will be different.
* ``trigger`` directory is removed.
* ``in_voltage0_sampling_frequency`` attribute is added for setting the sample
rate.
* ``in_voltage0_sampling_frequency_available`` attribute is added for querying
the max sample rate.
* ``timestamp`` channel is removed.
* Buffer data format may be different compared to when offload is not used,
e.g. the ``in_voltage0_type`` attribute.
If the ``turbo-gpios`` property is present in the device tree, the driver will
turn on TURBO during buffered reads and turn it off otherwise.
Device buffers
==============

View File

@ -0,0 +1,376 @@
.. SPDX-License-Identifier: GPL-2.0
================
ADIS16550 driver
================
This driver supports Analog Device's IMUs on SPI bus.
1. Supported devices
====================
* `ADIS16550 <https://www.analog.com/ADIS16550>`_
The ADIS16550 is a complete inertial system that includes a triaxis gyroscope
and a triaxis accelerometer. The factory calibration characterizes each sensor for
sensitivity, bias, and alignment. As a result, each sensor has its own dynamic
compensation formulas that provide accurate sensor measurements.
2. Device attributes
====================
Accelerometer, gyroscope measurements are always provided. Furthermore, the
driver offers the capability to retrieve the delta angle and the delta velocity
measurements computed by the device.
The delta angle measurements represent a calculation of angular displacement
between each sample update, while the delta velocity measurements represent a
calculation of linear velocity change between each sample update.
Finally, temperature data are provided which show a coarse measurement of
the temperature inside of the IMU device. This data is most useful for
monitoring relative changes in the thermal environment.
Each IIO device, has a device folder under ``/sys/bus/iio/devices/iio:deviceX``,
where X is the IIO index of the device. Under these folders reside a set of
device files, depending on the characteristics and features of the hardware
device in questions. These files are consistently generalized and documented in
the IIO ABI documentation.
The following tables show the adis16550 related device files, found in the
specific device folder path ``/sys/bus/iio/devices/iio:deviceX``.
+-------------------------------------------+----------------------------------------------------------+
| 3-Axis Accelerometer related device files | Description |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_scale | Scale for the accelerometer channels. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_filter_low_pass_3db_frequency | Bandwidth for the accelerometer channels. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_x_calibbias | Calibration offset for the X-axis accelerometer channel. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_x_calibscale | Calibration scale for the X-axis accelerometer channel. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_x_raw | Raw X-axis accelerometer channel value. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_y_calibbias | Calibration offset for the Y-axis accelerometer channel. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_y_calibscale | Calibration scale for the Y-axis accelerometer channel. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_y_raw | Raw Y-axis accelerometer channel value. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_z_calibbias | Calibration offset for the Z-axis accelerometer channel. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_z_calibscale | Calibration scale for the Z-axis accelerometer channel. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_z_raw | Raw Z-axis accelerometer channel value. |
+-------------------------------------------+----------------------------------------------------------+
| in_deltavelocity_scale | Scale for delta velocity channels. |
+-------------------------------------------+----------------------------------------------------------+
| in_deltavelocity_x_raw | Raw X-axis delta velocity channel value. |
+-------------------------------------------+----------------------------------------------------------+
| in_deltavelocity_y_raw | Raw Y-axis delta velocity channel value. |
+-------------------------------------------+----------------------------------------------------------+
| in_deltavelocity_z_raw | Raw Z-axis delta velocity channel value. |
+-------------------------------------------+----------------------------------------------------------+
+--------------------------------------------+------------------------------------------------------+
| 3-Axis Gyroscope related device files | Description |
+--------------------------------------------+------------------------------------------------------+
| in_anglvel_scale | Scale for the gyroscope channels. |
+--------------------------------------------+------------------------------------------------------+
| in_anglvel_filter_low_pass_3db_frequency | Scale for the gyroscope channels. |
+--------------------------------------------+------------------------------------------------------+
| in_anglvel_x_calibbias | Calibration offset for the X-axis gyroscope channel. |
+--------------------------------------------+------------------------------------------------------+
| in_anglvel_x_calibscale | Calibration scale for the X-axis gyroscope channel. |
+--------------------------------------------+------------------------------------------------------+
| in_anglvel_x_raw | Raw X-axis gyroscope channel value. |
+--------------------------------------------+------------------------------------------------------+
| in_anglvel_y_calibbias | Calibration offset for the Y-axis gyroscope channel. |
+--------------------------------------------+------------------------------------------------------+
| in_anglvel_y_calibscale | Calibration scale for the Y-axis gyroscope channel. |
+--------------------------------------------+------------------------------------------------------+
| in_anglvel_y_raw | Raw Y-axis gyroscope channel value. |
+--------------------------------------------+------------------------------------------------------+
| in_anglvel_z_calibbias | Calibration offset for the Z-axis gyroscope channel. |
+--------------------------------------------+------------------------------------------------------+
| in_anglvel_z_calibscale | Calibration scale for the Z-axis gyroscope channel. |
+--------------------------------------------+------------------------------------------------------+
| in_anglvel_z_raw | Raw Z-axis gyroscope channel value. |
+--------------------------------------------+------------------------------------------------------+
| in_deltaangl_scale | Scale for delta angle channels. |
+--------------------------------------------+------------------------------------------------------+
| in_deltaangl_x_raw | Raw X-axis delta angle channel value. |
+--------------------------------------------+------------------------------------------------------+
| in_deltaangl_y_raw | Raw Y-axis delta angle channel value. |
+--------------------------------------------+------------------------------------------------------+
| in_deltaangl_z_raw | Raw Z-axis delta angle channel value. |
+--------------------------------------------+------------------------------------------------------+
+----------------------------------+-------------------------------------------+
| Temperature sensor related files | Description |
+----------------------------------+-------------------------------------------+
| in_temp0_raw | Raw temperature channel value. |
+----------------------------------+-------------------------------------------+
| in_temp0_offset | Offset for the temperature sensor channel.|
+----------------------------------+-------------------------------------------+
| in_temp0_scale | Scale for the temperature sensor channel. |
+----------------------------------+-------------------------------------------+
+----------------------------+--------------------------------------------------------------------------------+
| Miscellaneous device files | Description |
+----------------------------+--------------------------------------------------------------------------------+
| name | Name of the IIO device. |
+----------------------------+--------------------------------------------------------------------------------+
| sampling_frequency | Currently selected sample rate. |
+----------------------------+--------------------------------------------------------------------------------+
The following table shows the adis16550 related device debug files, found in the
specific device debug folder path ``/sys/kernel/debug/iio/iio:deviceX``.
+----------------------+-------------------------------------------------------------------------+
| Debugfs device files | Description |
+----------------------+-------------------------------------------------------------------------+
| serial_number | The serial number of the chip in hexadecimal format. |
+----------------------+-------------------------------------------------------------------------+
| product_id | Chip specific product id (16550). |
+----------------------+-------------------------------------------------------------------------+
| flash_count | The number of flash writes performed on the device. |
+----------------------+-------------------------------------------------------------------------+
| firmware_revision | String containing the firmware revision in the following format ##.##. |
+----------------------+-------------------------------------------------------------------------+
| firmware_date | String containing the firmware date in the following format mm-dd-yyyy. |
+----------------------+-------------------------------------------------------------------------+
Channels processed values
-------------------------
A channel value can be read from its _raw attribute. The value returned is the
raw value as reported by the devices. To get the processed value of the channel,
apply the following formula:
.. code-block:: bash
processed value = (_raw + _offset) * _scale
Where _offset and _scale are device attributes. If no _offset attribute is
present, simply assume its value is 0.
The adis16550 driver offers data for 5 types of channels, the table below shows
the measurement units for the processed value, which are defined by the IIO
framework:
+--------------------------------------+---------------------------+
| Channel type | Measurement unit |
+--------------------------------------+---------------------------+
| Acceleration on X, Y, and Z axis | Meters per Second squared |
+--------------------------------------+---------------------------+
| Angular velocity on X, Y and Z axis | Radians per second |
+--------------------------------------+---------------------------+
| Delta velocity on X. Y, and Z axis | Meters per Second |
+--------------------------------------+---------------------------+
| Delta angle on X, Y, and Z axis | Radians |
+--------------------------------------+---------------------------+
| Temperature | Millidegrees Celsius |
+--------------------------------------+---------------------------+
Usage examples
--------------
Show device name:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat name
adis16550
Show accelerometer channels value:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_raw
6903851
root:/sys/bus/iio/devices/iio:device0> cat in_accel_y_raw
5650550
root:/sys/bus/iio/devices/iio:device0> cat in_accel_z_raw
104873530
root:/sys/bus/iio/devices/iio:device0> cat in_accel_scale
0.000000095
- X-axis acceleration = in_accel_x_raw * in_accel_scale = 0.655865845 m/s^2
- Y-axis acceleration = in_accel_y_raw * in_accel_scale = 0.53680225 m/s^2
- Z-axis acceleration = in_accel_z_raw * in_accel_scale = 9.96298535 m/s^2
Show gyroscope channels value:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_x_raw
193309
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_raw
-763676
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_z_raw
-358108
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_scale
0.000000003
- X-axis angular velocity = in_anglvel_x_raw * in_anglvel_scale = 0.000579927 rad/s
- Y-axis angular velocity = in_anglvel_y_raw * in_anglvel_scale = 0.002291028 rad/s
- Z-axis angular velocity = in_anglvel_z_raw * in_anglvel_scale = 0.001074324 rad/s
Set calibration offset for accelerometer channels:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias
0
root:/sys/bus/iio/devices/iio:device0> echo 5000 > in_accel_x_calibbias
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias
5000
Set calibration offset for gyroscope channels:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_calibbias
0
root:/sys/bus/iio/devices/iio:device0> echo -5000 > in_anglvel_y_calibbias
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_calibbias
-5000
Set sampling frequency:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat sampling_frequency
4000.000000
root:/sys/bus/iio/devices/iio:device0> echo 1000 > sampling_frequency
1000.000000
Set bandwidth for accelerometer channels:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_accel_filter_low_pass_3db_frequency
0
root:/sys/bus/iio/devices/iio:device0> echo 100 > in_accel_filter_low_pass_3db_frequency
root:/sys/bus/iio/devices/iio:device0> cat in_accel_filter_low_pass_3db_frequency
100
Show serial number:
.. code-block:: bash
root:/sys/kernel/debug/iio/iio:device0> cat serial_number
0x000000b6
Show product id:
.. code-block:: bash
root:/sys/kernel/debug/iio/iio:device0> cat product_id
16550
Show flash count:
.. code-block:: bash
root:/sys/kernel/debug/iio/iio:device0> cat flash_count
13
Show firmware revision:
.. code-block:: bash
root:/sys/kernel/debug/iio/iio:device0> cat firmware_revision
1.5
Show firmware date:
.. code-block:: bash
root:/sys/kernel/debug/iio/iio:device0> cat firmware_date
28-04-2021
3. Device buffers
=================
This driver supports IIO buffers.
The device supports retrieving the raw acceleration, gyroscope, delta velocity,
delta angle and temperature measurements using buffers.
However, when retrieving acceleration or gyroscope data using buffers, delta
readings will not be available and vice versa. This is because the device only
allows to read either acceleration and gyroscope data or delta velocity and
delta angle data at a time and switching between these two burst data selection
modes is time consuming.
Usage examples
--------------
Set device trigger in current_trigger, if not already set:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat trigger/current_trigger
root:/sys/bus/iio/devices/iio:device0> echo adis16550-dev0 > trigger/current_trigger
root:/sys/bus/iio/devices/iio:device0> cat trigger/current_trigger
adis16550-dev0
Select channels for buffer read:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_deltavelocity_x_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_deltavelocity_y_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_deltavelocity_z_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_temp0_en
Set the number of samples to be stored in the buffer:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 10 > buffer/length
Enable buffer readings:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 1 > buffer/enable
Obtain buffered data:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> hexdump -C /dev/iio\:device0
...
0000cdf0 00 00 0d 2f 00 00 08 43 00 00 09 09 00 00 a4 5f |.../...C......._|
0000ce00 00 00 0d 2f 00 00 07 de 00 00 08 db 00 00 a4 4b |.../...........K|
0000ce10 00 00 0d 2f 00 00 07 58 00 00 08 a3 00 00 a4 55 |.../...X.......U|
0000ce20 00 00 0d 2f 00 00 06 d6 00 00 08 5c 00 00 a4 62 |.../.......\...b|
0000ce30 00 00 0d 2f 00 00 06 45 00 00 08 37 00 00 a4 47 |.../...E...7...G|
0000ce40 00 00 0d 2f 00 00 05 d4 00 00 08 30 00 00 a3 fa |.../.......0....|
0000ce50 00 00 0d 2f 00 00 05 d0 00 00 08 12 00 00 a3 d3 |.../............|
0000ce60 00 00 0d 2f 00 00 05 dd 00 00 08 2e 00 00 a3 e9 |.../............|
0000ce70 00 00 0d 2f 00 00 05 cc 00 00 08 51 00 00 a3 d5 |.../.......Q....|
0000ce80 00 00 0d 2f 00 00 05 ba 00 00 08 22 00 00 a3 9a |.../......."....|
0000ce90 00 00 0d 2f 00 00 05 9c 00 00 07 d9 00 00 a3 40 |.../...........@|
0000cea0 00 00 0d 2f 00 00 05 68 00 00 07 94 00 00 a2 e4 |.../...h........|
0000ceb0 00 00 0d 2f 00 00 05 25 00 00 07 8d 00 00 a2 ce |.../...%........|
...
See ``Documentation/iio/iio_devbuf.rst`` for more information about how buffered
data is structured.
4. IIO Interfacing Tools
========================
See ``Documentation/iio/iio_tools.rst`` for the description of the available IIO
interfacing tools.

View File

@ -94,7 +94,7 @@ apply the following formula:
Where _offset and _scale are device attributes. If no _offset attribute is
present, simply assume its value is 0.
The adis16475 driver offers data for 2 types of channels, the table below shows
The ADXL380 driver offers data for 2 types of channels, the table below shows
the measurement units for the processed value, which are defined by the IIO
framework:

View File

@ -0,0 +1,305 @@
.. SPDX-License-Identifier: GPL-2.0-only
=========================
IIO Abstractions for ADCs
=========================
1. Overview
===========
The IIO subsystem supports many Analog to Digital Converters (ADCs). Some ADCs
have features and characteristics that are supported in specific ways by IIO
device drivers. This documentation describes common ADC features and explains
how they are supported by the IIO subsystem.
1. ADC Channel Types
====================
ADCs can have distinct types of inputs, each of them measuring analog voltages
in a slightly different way. An ADC digitizes the analog input voltage over a
span that is often given by the provided voltage reference, the input type, and
the input polarity. The input range allowed to an ADC channel is needed to
determine the scale factor and offset needed to obtain the measured value in
real-world units (millivolts for voltage measurement, milliamps for current
measurement, etc.).
Elaborate designs may have nonlinear characteristics or integrated components
(such as amplifiers and reference buffers) that might also have to be considered
to derive the allowed input range for an ADC. For clarity, the sections below
assume the input range only depends on the provided voltage references, input
type, and input polarity.
There are three general types of ADC inputs (single-ended, differential,
pseudo-differential) and two possible polarities (unipolar, bipolar). The input
type (single-ended, differential, pseudo-differential) is one channel
characteristic, and is completely independent of the polarity (unipolar,
bipolar) aspect. A comprehensive article about ADC input types (on which this
doc is heavily based on) can be found at
https://www.analog.com/en/resources/technical-articles/sar-adc-input-types.html.
1.1 Single-ended channels
-------------------------
Single-ended channels digitize the analog input voltage relative to ground and
can be either unipolar or bipolar.
1.1.1 Single-ended Unipolar Channels
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
---------- VREF -------------
´ ` ´ ` _____________
/ \ / \ / |
/ \ / \ --- < IN ADC |
\ / \ / \ |
`-´ `-´ \ VREF |
-------- GND (0V) ----------- +-----------+
^
|
External VREF
The input voltage to a **single-ended unipolar** channel is allowed to swing
from GND to VREF (where VREF is a voltage reference with electrical potential
higher than system ground). The maximum input voltage is also called VFS
(Voltage input Full-Scale), with VFS being determined by VREF. The voltage
reference may be provided from an external supply or derived from the chip power
source.
A single-ended unipolar channel could be described in device tree like the
following example::
adc@0 {
...
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
};
};
One is always allowed to include ADC channel nodes in the device tree. Though,
if the device has a uniform set of inputs (e.g. all inputs are single-ended),
then declaring the channel nodes is optional.
One caveat for devices that support mixed single-ended and differential channels
is that single-ended channel nodes also need to provide a ``single-channel``
property when ``reg`` is an arbitrary number that doesn't match the input pin
number.
See ``Documentation/devicetree/bindings/iio/adc/adc.yaml`` for the complete
documentation of ADC specific device tree properties.
1.1.2 Single-ended Bipolar Channels
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
---------- +VREF ------------
´ ` ´ ` _____________________
/ \ / \ / |
/ \ / \ --- < IN ADC |
\ / \ / \ |
`-´ `-´ \ +VREF -VREF |
---------- -VREF ------------ +-------------------+
^ ^
| |
External +VREF ------+ External -VREF
For a **single-ended bipolar** channel, the analog voltage input can go from
-VREF to +VREF (where -VREF is the voltage reference that has the lower
electrical potential while +VREF is the reference with the higher one). Some ADC
chips derive the lower reference from +VREF, others get it from a separate
input. Often, +VREF and -VREF are symmetric but they don't need to be so. When
-VREF is lower than system ground, these inputs are also called single-ended
true bipolar. Also, while there is a relevant difference between bipolar and
true bipolar from the electrical perspective, IIO makes no explicit distinction
between them.
Here's an example device tree description of a single-ended bipolar channel::
adc@0 {
...
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
bipolar;
};
};
1.2 Differential channels
-------------------------
A differential voltage measurement digitizes the voltage level at the positive
input (IN+) relative to the negative input (IN-) over the -VREF to +VREF span.
In other words, a differential channel measures the potential difference between
IN+ and IN-, which is often denoted by the IN+ - IN- formula.
1.2.1 Differential Bipolar Channels
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
-------- +VREF ------ +-------------------+
´ ` ´ ` / |
/ \ / \ / --- < IN+ |
`-´ `-´ | |
-------- -VREF ------ | |
| ADC |
-------- +VREF ------ | |
´ ` ´ ` | |
\ / \ / \ --- < IN- |
`-´ `-´ \ +VREF -VREF |
-------- -VREF ------ +-------------------+
^ ^
| +---- External -VREF
External +VREF
The analog signals to **differential bipolar** inputs are also allowed to swing
from -VREF to +VREF. The bipolar part of the name means that the resulting value
of the difference (IN+ - IN-) can be positive or negative. If -VREF is below
system GND, these are also called differential true bipolar inputs.
Device tree example of a differential bipolar channel::
adc@0 {
...
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
bipolar;
diff-channels = <0 1>;
};
};
In the ADC driver, ``differential = 1`` is set into ``struct iio_chan_spec`` for
the channel. Even though, there are three general input types, ``differential``
is only used to distinguish between differential and non-differential (either
single-ended or pseudo-differential) input types. See
``include/linux/iio/iio.h`` for more information.
1.2.2 Differential Unipolar Channels
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For **differential unipolar** channels, the analog voltage at the positive input
must also be higher than the voltage at the negative input. Thus, the actual
input range allowed to a differential unipolar channel is IN- to +VREF. Because
IN+ is allowed to swing with the measured analog signal and the input setup must
guarantee IN+ will not go below IN- (nor IN- will raise above IN+), most
differential unipolar channel setups have IN- fixed to a known voltage that does
not fall within the voltage range expected for the measured signal. That leads
to a setup that is equivalent to a pseudo-differential channel. Thus,
differential unipolar setups can often be supported as pseudo-differential
unipolar channels.
1.3 Pseudo-differential Channels
--------------------------------
There is a third ADC input type which is called pseudo-differential or
single-ended to differential configuration. A pseudo-differential channel is
similar to a differential channel in that it also measures IN+ relative to IN-.
However, unlike bipolar differential channels, the negative input is limited to
a narrow voltage range (taken as a constant voltage) while only IN+ is allowed
to swing. A pseudo-differential channel can be made out from a differential pair
of inputs by restricting the negative input to a known voltage while allowing
only the positive input to swing. Sometimes, the input provided to IN- is called
common-mode voltage. Besides, some parts have a COM pin that allows single-ended
inputs to be referenced to a common-mode voltage, making them
pseudo-differential channels. Often, the common mode input voltage can be
described in the device tree as a voltage regulator (e.g. ``com-supply``) since
it is basically a constant voltage source.
1.3.1 Pseudo-differential Unipolar Channels
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
-------- +VREF ------ +-------------------+
´ ` ´ ` / |
/ \ / \ / --- < IN+ |
`-´ `-´ | |
--------- IN- ------- | ADC |
| |
Common-mode voltage --> --- < IN- |
\ +VREF -VREF |
+-------------------+
^ ^
| +---- External -VREF
External +VREF
A **pseudo-differential unipolar** input has the limitations a differential
unipolar channel would have, meaning the analog voltage to the positive input
IN+ must stay within IN- to +VREF. The fixed voltage to IN- is often called
common-mode voltage and it must be within -VREF to +VREF as would be expected
from the signal to any differential channel negative input.
The voltage measured from IN+ is relative to IN- but, unlike differential
channels, pseudo-differential setups are intended to gauge single-ended input
signals. To enable applications to calculate IN+ voltage with respect to system
ground, the IIO channel may provide an ``_offset`` sysfs attribute to be added
to ADC output when converting raw data to voltage units. In many setups, the
common-mode voltage input is at GND level and the ``_offset`` attribute is
omitted due to being always zero.
Device tree example for pseudo-differential unipolar channel::
adc@0 {
...
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
single-channel = <0>;
common-mode-channel = <1>;
};
};
Do not set ``differential`` in the channel ``iio_chan_spec`` struct of
pseudo-differential channels.
1.3.2 Pseudo-differential Bipolar Channels
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
-------- +VREF ------ +-------------------+
´ ` ´ ` / |
/ \ / \ / --- < IN+ |
`-´ `-´ | |
-------- -VREF ------ | ADC |
| |
Common-mode voltage --> --- < IN- |
\ +VREF -VREF |
+-------------------+
^ ^
| +---- External -VREF
External +VREF
A **pseudo-differential bipolar** input is not limited by the level at IN- but
it will be limited to -VREF or to GND on the lower end of the input range
depending on the particular ADC. Similar to their unipolar counter parts,
pseudo-differential bipolar channels ought to declare an ``_offset`` attribute
to enable the conversion of raw ADC data to voltage units. For the setup with
IN- connected to GND, ``_offset`` is often omitted.
Device tree example for pseudo-differential bipolar channel::
adc@0 {
...
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
bipolar;
single-channel = <0>;
common-mode-channel = <1>;
};
};

View File

@ -7,6 +7,7 @@ Industrial I/O
.. toctree::
:maxdepth: 1
iio_adc
iio_configfs
iio_devbuf
iio_dmabuf_api
@ -19,13 +20,16 @@ Industrial I/O Kernel Drivers
:maxdepth: 1
ad4000
ad4030
ad4695
ad7191
ad7380
ad7606
ad7625
ad7944
adis16475
adis16480
adis16550
adxl380
bno055
ep93xx_adc

View File

@ -1306,11 +1306,23 @@ F: Documentation/devicetree/bindings/iio/adc/adi,ad4000.yaml
F: Documentation/iio/ad4000.rst
F: drivers/iio/adc/ad4000.c
AD4030 ADC DRIVER (AD4030-24/AD4630-16/AD4630-24/AD4632-16/AD4632-24)
M: Michael Hennerich <michael.hennerich@analog.com>
M: Nuno Sá <nuno.sa@analog.com>
R: Esteban Blanc <eblanc@baylibre.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml
F: Documentation/iio/ad4030.rst
F: drivers/iio/adc/ad4030.c
ANALOG DEVICES INC AD4130 DRIVER
M: Cosmin Tanislav <cosmin.tanislav@analog.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130
F: Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml
F: drivers/iio/adc/ad4130.c
@ -1334,6 +1346,15 @@ W: http://ez.analog.com/community/linux-device-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7091r*
F: drivers/iio/adc/ad7091r*
ANALOG DEVICES INC AD7191 DRIVER
M: Alisa-Dariana Roman <alisa.roman@analog.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7191.yaml
F: Documentation/iio/ad7191.rst
F: drivers/iio/adc/ad7191.c
ANALOG DEVICES INC AD7192 DRIVER
M: Alisa-Dariana Roman <alisa.roman@analog.com>
L: linux-iio@vger.kernel.org
@ -1485,6 +1506,16 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/imu/adi,adis16475.yaml
F: drivers/iio/imu/adis16475.c
ANALOG DEVICES INC ADIS16550 DRIVER
M: Nuno Sa <nuno.sa@analog.com>
M: Ramona Gradinariu <ramona.gradinariu@analog.com>
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
M: Robert Budai <robert.budai@analog.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/imu/adi,adis16550.yaml
ANALOG DEVICES INC ADM1177 DRIVER
M: Michael Hennerich <Michael.Hennerich@analog.com>
L: linux-hwmon@vger.kernel.org
@ -4420,6 +4451,13 @@ F: kernel/bpf/stackmap.c
F: kernel/trace/bpf_trace.c
F: lib/buildid.c
BROADCOM APDS9160 AMBIENT LIGHT SENSOR AND PROXIMITY DRIVER
M: Mikael Gonella-Bolduc <m.gonella.bolduc@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/light/brcm,apds9160.yaml
F: drivers/iio/light/apds9160.c
BROADCOM ASP 2.0 ETHERNET DRIVER
M: Justin Chen <justin.chen@broadcom.com>
M: Florian Fainelli <florian.fainelli@broadcom.com>
@ -22359,6 +22397,13 @@ F: Documentation/devicetree/bindings/mtd/jedec,spi-nor.yaml
F: drivers/mtd/spi-nor/
F: include/linux/mtd/spi-nor.h
SPI OFFLOAD
R: David Lechner <dlechner@baylibre.com>
F: drivers/spi/spi-offload-trigger-pwm.c
F: drivers/spi/spi-offload.c
F: include/linux/spi/spi-offload.h
K: spi_offload
SPI SUBSYSTEM
M: Mark Brown <broonie@kernel.org>
L: linux-spi@vger.kernel.org
@ -22514,7 +22559,6 @@ STAGING - SEPS525 LCD CONTROLLER DRIVERS
M: Michael Hennerich <michael.hennerich@analog.com>
L: linux-fbdev@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/iio/adc/adi,ad7606.yaml
F: drivers/staging/fbtft/fb_seps525.c
STAGING - SILICON MOTION SM750 FRAME BUFFER DRIVER
@ -24113,6 +24157,11 @@ W: https://github.com/srcres258/linux-doc
T: git git://github.com/srcres258/linux-doc.git doc-zh-tw
F: Documentation/translations/zh_TW/
TRIGGER SOURCE - PWM
M: David Lechner <dlechner@baylibre.com>
S: Maintained
F: Documentation/devicetree/bindings/trigger-source/pwm-trigger.yaml
TRUSTED SECURITY MODULE (TSM) ATTESTATION REPORTS
M: Dan Williams <dan.j.williams@intel.com>
L: linux-coco@lists.linux.dev

View File

@ -43,7 +43,6 @@
#define ADXL345_REG_INT_ENABLE 0x2E
#define ADXL345_REG_INT_MAP 0x2F
#define ADXL345_REG_INT_SOURCE 0x30
#define ADXL345_REG_INT_SOURCE_MSK 0xFF
#define ADXL345_REG_DATA_FORMAT 0x31
#define ADXL345_REG_XYZ_BASE 0x32
#define ADXL345_REG_DATA_AXIS(index) \

View File

@ -76,6 +76,26 @@ static const unsigned long adxl345_scan_masks[] = {
0
};
/**
* adxl345_set_measure_en() - Enable and disable measuring.
*
* @st: The device data.
* @en: Enable measurements, else standby mode.
*
* For lowest power operation, standby mode can be used. In standby mode,
* current consumption is supposed to be reduced to 0.1uA (typical). In this
* mode no measurements are made. Placing the device into standby mode
* preserves the contents of FIFO.
*
* Return: Returns 0 if successful, or a negative error value.
*/
static int adxl345_set_measure_en(struct adxl345_state *st, bool en)
{
unsigned int val = en ? ADXL345_POWER_CTL_MEASURE : ADXL345_POWER_CTL_STANDBY;
return regmap_write(st->regmap, ADXL345_REG_POWER_CTL, val);
}
static int adxl345_set_interrupts(struct adxl345_state *st)
{
int ret;
@ -87,8 +107,7 @@ static int adxl345_set_interrupts(struct adxl345_state *st)
* interrupts to the INT1 pin, whereas bits set to 1 send their respective
* interrupts to the INT2 pin. The intio shall convert this accordingly.
*/
int_map = FIELD_GET(ADXL345_REG_INT_SOURCE_MSK,
st->intio ? st->int_map : ~st->int_map);
int_map = st->intio ? st->int_map : ~st->int_map;
ret = regmap_write(st->regmap, ADXL345_REG_INT_MAP, int_map);
if (ret)
@ -182,6 +201,16 @@ static int adxl345_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
static int adxl345_reg_access(struct iio_dev *indio_dev, unsigned int reg,
unsigned int writeval, unsigned int *readval)
{
struct adxl345_state *st = iio_priv(indio_dev);
if (readval)
return regmap_read(st->regmap, reg, readval);
return regmap_write(st->regmap, reg, writeval);
}
static int adxl345_set_watermark(struct iio_dev *indio_dev, unsigned int value)
{
struct adxl345_state *st = iio_priv(indio_dev);
@ -214,26 +243,6 @@ static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev,
}
}
/**
* adxl345_set_measure_en() - Enable and disable measuring.
*
* @st: The device data.
* @en: Enable measurements, else standby mode.
*
* For lowest power operation, standby mode can be used. In standby mode,
* current consumption is supposed to be reduced to 0.1uA (typical). In this
* mode no measurements are made. Placing the device into standby mode
* preserves the contents of FIFO.
*
* Return: Returns 0 if successful, or a negative error value.
*/
static int adxl345_set_measure_en(struct adxl345_state *st, bool en)
{
unsigned int val = en ? ADXL345_POWER_CTL_MEASURE : ADXL345_POWER_CTL_STANDBY;
return regmap_write(st->regmap, ADXL345_REG_POWER_CTL, val);
}
static void adxl345_powerdown(void *ptr)
{
struct adxl345_state *st = ptr;
@ -394,18 +403,6 @@ static const struct iio_buffer_setup_ops adxl345_buffer_ops = {
.predisable = adxl345_buffer_predisable,
};
static int adxl345_get_status(struct adxl345_state *st)
{
int ret;
unsigned int regval;
ret = regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, &regval);
if (ret < 0)
return ret;
return FIELD_GET(ADXL345_REG_INT_SOURCE_MSK, regval);
}
static int adxl345_fifo_push(struct iio_dev *indio_dev,
int samples)
{
@ -439,14 +436,10 @@ static irqreturn_t adxl345_irq_handler(int irq, void *p)
int int_stat;
int samples;
int_stat = adxl345_get_status(st);
if (int_stat <= 0)
if (regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, &int_stat))
return IRQ_NONE;
if (int_stat & ADXL345_INT_OVERRUN)
goto err;
if (int_stat & ADXL345_INT_WATERMARK) {
if (FIELD_GET(ADXL345_INT_WATERMARK, int_stat)) {
samples = adxl345_get_samples(st);
if (samples < 0)
goto err;
@ -454,6 +447,10 @@ static irqreturn_t adxl345_irq_handler(int irq, void *p)
if (adxl345_fifo_push(indio_dev, samples) < 0)
goto err;
}
if (FIELD_GET(ADXL345_INT_OVERRUN, int_stat))
goto err;
return IRQ_HANDLED;
err:
@ -467,6 +464,7 @@ static const struct iio_info adxl345_info = {
.read_raw = adxl345_read_raw,
.write_raw = adxl345_write_raw,
.write_raw_get_fmt = adxl345_write_raw_get_fmt,
.debugfs_reg_access = &adxl345_reg_access,
.hwfifo_set_watermark = adxl345_set_watermark,
};

View File

@ -477,45 +477,42 @@ static int adxl367_set_fifo_watermark(struct adxl367_state *st,
static int adxl367_set_range(struct iio_dev *indio_dev,
enum adxl367_range range)
{
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
guard(mutex)(&st->lock);
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
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)
return ret;
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)
return ret;
adxl367_scale_act_thresholds(st, st->range, range);
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)
return ret;
/* Activity thresholds depend on range */
ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY,
st->act_threshold);
if (ret)
return ret;
ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY,
st->inact_threshold);
if (ret)
return ret;
ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY,
st->inact_threshold);
if (ret)
return ret;
ret = adxl367_set_measure_en(st, true);
if (ret)
return ret;
ret = adxl367_set_measure_en(st, true);
if (ret)
return ret;
st->range = range;
st->range = range;
return 0;
}
unreachable();
return 0;
}
static int adxl367_time_ms_to_samples(struct adxl367_state *st, unsigned int ms)
@ -620,23 +617,20 @@ static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr)
static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr)
{
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
guard(mutex)(&st->lock);
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = _adxl367_set_odr(st, odr);
if (ret)
return ret;
ret = _adxl367_set_odr(st, odr);
if (ret)
return ret;
return adxl367_set_measure_en(st, true);
}
unreachable();
return adxl367_set_measure_en(st, true);
}
static int adxl367_set_temp_adc_en(struct adxl367_state *st, unsigned int reg,
@ -725,32 +719,29 @@ static int adxl367_read_sample(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val)
{
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
struct adxl367_state *st = iio_priv(indio_dev);
u16 sample;
int ret;
struct adxl367_state *st = iio_priv(indio_dev);
u16 sample;
int ret;
guard(mutex)(&st->lock);
guard(mutex)(&st->lock);
ret = adxl367_set_temp_adc_reg_en(st, chan->address, true);
if (ret)
return ret;
ret = adxl367_set_temp_adc_reg_en(st, chan->address, true);
if (ret)
return ret;
ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf,
sizeof(st->sample_buf));
if (ret)
return ret;
ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf,
sizeof(st->sample_buf));
if (ret)
return ret;
sample = FIELD_GET(ADXL367_DATA_MASK, be16_to_cpu(st->sample_buf));
*val = sign_extend32(sample, chan->scan_type.realbits - 1);
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);
if (ret)
return ret;
ret = adxl367_set_temp_adc_reg_en(st, chan->address, false);
if (ret)
return ret;
return IIO_VAL_INT;
}
unreachable();
return IIO_VAL_INT;
}
static int adxl367_get_status(struct adxl367_state *st, u8 *status,
@ -852,10 +843,15 @@ static int adxl367_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long info)
{
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
switch (info) {
case IIO_CHAN_INFO_RAW:
return adxl367_read_sample(indio_dev, chan, val);
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = adxl367_read_sample(indio_dev, chan, val);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_ACCEL: {
@ -912,7 +908,12 @@ static int adxl367_write_raw(struct iio_dev *indio_dev,
if (ret)
return ret;
return adxl367_set_odr(indio_dev, odr);
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = adxl367_set_odr(indio_dev, odr);
iio_device_release_direct(indio_dev);
return ret;
}
case IIO_CHAN_INFO_SCALE: {
enum adxl367_range range;
@ -921,7 +922,12 @@ static int adxl367_write_raw(struct iio_dev *indio_dev,
if (ret)
return ret;
return adxl367_set_range(indio_dev, range);
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = adxl367_set_range(indio_dev, range);
iio_device_release_direct(indio_dev);
return ret;
}
default:
return -EINVAL;
@ -1069,13 +1075,15 @@ static int adxl367_read_event_config(struct iio_dev *indio_dev,
}
}
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,
bool state)
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,
bool state)
{
struct adxl367_state *st = iio_priv(indio_dev);
enum adxl367_activity_type act;
int ret;
switch (dir) {
case IIO_EV_DIR_RISING:
@ -1088,28 +1096,38 @@ static int adxl367_write_event_config(struct iio_dev *indio_dev,
return -EINVAL;
}
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
guard(mutex)(&st->lock);
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = adxl367_set_act_interrupt_en(st, act, state);
if (ret)
return ret;
ret = adxl367_set_act_interrupt_en(st, act, state);
if (ret)
return ret;
ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED
: ADXL367_ACT_DISABLED);
if (ret)
return ret;
ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED
: ADXL367_ACT_DISABLED);
if (ret)
return ret;
return adxl367_set_measure_en(st, true);
}
return adxl367_set_measure_en(st, true);
}
unreachable();
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,
bool state)
{
int ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = __adxl367_write_event_config(indio_dev, chan, type, dir, state);
iio_device_release_direct(indio_dev);
return ret;
}
static ssize_t adxl367_get_fifo_enabled(struct device *dev,

View File

@ -763,12 +763,11 @@ static int adxl372_read_raw(struct iio_dev *indio_dev,
switch (info) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = adxl372_read_axis(st, chan->address);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;

View File

@ -1175,12 +1175,11 @@ static int adxl380_read_raw(struct iio_dev *indio_dev,
switch (info) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = adxl380_read_chn(st, chan->address);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;

View File

@ -540,14 +540,13 @@ static int bma180_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
mutex_lock(&data->mutex);
ret = bma180_get_data_reg(data, chan->scan_index);
mutex_unlock(&data->mutex);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
if (chan->scan_type.sign == 's') {

View File

@ -190,7 +190,7 @@ const struct regmap_config bma400_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = BMA400_CMD_REG,
.cache_type = REGCACHE_RBTREE,
.cache_type = REGCACHE_MAPLE,
.writeable_reg = bma400_is_writable_reg,
.volatile_reg = bma400_is_volatile_reg,
};

View File

@ -145,7 +145,7 @@ const struct regmap_config bmi088_regmap_conf = {
.val_bits = 8,
.max_register = 0x7E,
.volatile_table = &bmi088_volatile_table,
.cache_type = REGCACHE_RBTREE,
.cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_NS_GPL(bmi088_regmap_conf, "IIO_BMI088");
@ -313,12 +313,13 @@ static int bmi088_accel_read_raw(struct iio_dev *indio_dev,
if (ret)
return ret;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
if (!iio_device_claim_direct(indio_dev)) {
ret = -EBUSY;
goto out_read_raw_pm_put;
}
ret = bmi088_accel_get_axis(data, chan, val);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (!ret)
ret = IIO_VAL_INT;

View File

@ -460,22 +460,20 @@ static int fxls8962af_write_raw(struct iio_dev *indio_dev,
if (val != 0)
return -EINVAL;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = fxls8962af_set_full_scale(data, val2);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = fxls8962af_set_samp_freq(data, val, val2);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return ret;
default:
return -EINVAL;
@ -683,14 +681,13 @@ fxls8962af_write_event_config(struct iio_dev *indio_dev,
fxls8962af_active(data);
ret = fxls8962af_power_on(data);
} else {
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
/* Not in buffered mode so disable power */
ret = fxls8962af_power_off(data);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
}
return ret;

View File

@ -149,7 +149,7 @@ static const struct regmap_config kx022a_regmap_config = {
.rd_noinc_table = &kx022a_nir_regs,
.precious_table = &kx022a_precious_regs,
.max_register = KX022A_MAX_REGISTER,
.cache_type = REGCACHE_RBTREE,
.cache_type = REGCACHE_MAPLE,
};
/* Regmap configs kx132 */
@ -260,7 +260,7 @@ static const struct regmap_config kx132_regmap_config = {
.rd_noinc_table = &kx132_nir_regs,
.precious_table = &kx132_precious_regs,
.max_register = KX132_MAX_REGISTER,
.cache_type = REGCACHE_RBTREE,
.cache_type = REGCACHE_MAPLE,
};
struct kx022a_data {
@ -510,12 +510,63 @@ static int kx022a_write_raw_get_fmt(struct iio_dev *idev,
}
}
static int __kx022a_write_raw(struct iio_dev *idev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct kx022a_data *data = iio_priv(idev);
int ret, n;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
n = ARRAY_SIZE(kx022a_accel_samp_freq_table);
while (n--)
if (val == kx022a_accel_samp_freq_table[n][0] &&
val2 == kx022a_accel_samp_freq_table[n][1])
break;
if (n < 0)
return -EINVAL;
ret = kx022a_turn_off_lock(data);
if (ret)
return ret;
ret = regmap_update_bits(data->regmap,
data->chip_info->odcntl,
KX022A_MASK_ODR, n);
data->odr_ns = kx022a_odrs[n];
kx022a_turn_on_unlock(data);
return ret;
case IIO_CHAN_INFO_SCALE:
n = data->chip_info->scale_table_size / 2;
while (n-- > 0)
if (val == data->chip_info->scale_table[n][0] &&
val2 == data->chip_info->scale_table[n][1])
break;
if (n < 0)
return -EINVAL;
ret = kx022a_turn_off_lock(data);
if (ret)
return ret;
ret = regmap_update_bits(data->regmap, data->chip_info->cntl,
KX022A_MASK_GSEL,
n << KX022A_GSEL_SHIFT);
kx022a_turn_on_unlock(data);
return ret;
default:
return -EINVAL;
}
}
static int kx022a_write_raw(struct iio_dev *idev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct kx022a_data *data = iio_priv(idev);
int ret, n;
int ret;
/*
* We should not allow changing scale or frequency when FIFO is running
@ -526,60 +577,12 @@ static int kx022a_write_raw(struct iio_dev *idev,
* issues if users trust the watermark to be reached within known
* time-limit).
*/
ret = iio_device_claim_direct_mode(idev);
if (ret)
return ret;
if (!iio_device_claim_direct(idev))
return -EBUSY;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
n = ARRAY_SIZE(kx022a_accel_samp_freq_table);
ret = __kx022a_write_raw(idev, chan, val, val2, mask);
while (n--)
if (val == kx022a_accel_samp_freq_table[n][0] &&
val2 == kx022a_accel_samp_freq_table[n][1])
break;
if (n < 0) {
ret = -EINVAL;
goto unlock_out;
}
ret = kx022a_turn_off_lock(data);
if (ret)
break;
ret = regmap_update_bits(data->regmap,
data->chip_info->odcntl,
KX022A_MASK_ODR, n);
data->odr_ns = kx022a_odrs[n];
kx022a_turn_on_unlock(data);
break;
case IIO_CHAN_INFO_SCALE:
n = data->chip_info->scale_table_size / 2;
while (n-- > 0)
if (val == data->chip_info->scale_table[n][0] &&
val2 == data->chip_info->scale_table[n][1])
break;
if (n < 0) {
ret = -EINVAL;
goto unlock_out;
}
ret = kx022a_turn_off_lock(data);
if (ret)
break;
ret = regmap_update_bits(data->regmap, data->chip_info->cntl,
KX022A_MASK_GSEL,
n << KX022A_GSEL_SHIFT);
kx022a_turn_on_unlock(data);
break;
default:
ret = -EINVAL;
break;
}
unlock_out:
iio_device_release_direct_mode(idev);
iio_device_release_direct(idev);
return ret;
}
@ -620,15 +623,14 @@ static int kx022a_read_raw(struct iio_dev *idev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(idev);
if (ret)
return ret;
if (!iio_device_claim_direct(idev))
return -EBUSY;
mutex_lock(&data->mutex);
ret = kx022a_get_axis(data, chan, val);
mutex_unlock(&data->mutex);
iio_device_release_direct_mode(idev);
iio_device_release_direct(idev);
return ret;

View File

@ -22,20 +22,37 @@
#define MC3230_MODE_OPCON_STANDBY 0x03
#define MC3230_REG_CHIP_ID 0x18
#define MC3230_CHIP_ID 0x01
#define MC3230_REG_PRODUCT_CODE 0x3b
#define MC3230_PRODUCT_CODE 0x19
/*
* The accelerometer has one measurement range:
*
* -1.5g - +1.5g (8-bit, signed)
*
* scale = (1.5 + 1.5) * 9.81 / (2^8 - 1) = 0.115411765
*/
static const int mc3230_nscale = 115411765;
struct mc3230_chip_info {
const char *name;
const u8 chip_id;
const u8 product_code;
const int scale;
};
static const struct mc3230_chip_info mc3230_chip_info = {
.name = "mc3230",
.chip_id = 0x01,
.product_code = 0x19,
/* (1.5 + 1.5) * 9.81 / (2^8 - 1) = 0.115411765 */
.scale = 115411765,
};
static const struct mc3230_chip_info mc3510c_chip_info = {
.name = "mc3510c",
.chip_id = 0x23,
.product_code = 0x10,
/* Was obtained empirically */
.scale = 625000000,
};
#define MC3230_CHANNEL(reg, axis) { \
.type = IIO_ACCEL, \
@ -44,18 +61,35 @@ static const int mc3230_nscale = 115411765;
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.ext_info = mc3230_ext_info, \
}
struct mc3230_data {
const struct mc3230_chip_info *chip_info;
struct i2c_client *client;
struct iio_mount_matrix orientation;
};
static const struct iio_mount_matrix *
mc3230_get_mount_matrix(const struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct mc3230_data *data = iio_priv(indio_dev);
return &data->orientation;
}
static const struct iio_chan_spec_ext_info mc3230_ext_info[] = {
IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, mc3230_get_mount_matrix),
{ }
};
static const struct iio_chan_spec mc3230_channels[] = {
MC3230_CHANNEL(MC3230_REG_XOUT, X),
MC3230_CHANNEL(MC3230_REG_YOUT, Y),
MC3230_CHANNEL(MC3230_REG_ZOUT, Z),
};
struct mc3230_data {
struct i2c_client *client;
};
static int mc3230_set_opcon(struct mc3230_data *data, int opcon)
{
int ret;
@ -95,7 +129,7 @@ static int mc3230_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = mc3230_nscale;
*val2 = data->chip_info->scale;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
@ -111,15 +145,28 @@ static int mc3230_probe(struct i2c_client *client)
int ret;
struct iio_dev *indio_dev;
struct mc3230_data *data;
const struct mc3230_chip_info *chip_info;
chip_info = i2c_get_match_data(client);
if (chip_info == NULL) {
dev_err(&client->dev, "failed to get match data");
return -ENODATA;
}
/* First check chip-id and product-id */
ret = i2c_smbus_read_byte_data(client, MC3230_REG_CHIP_ID);
if (ret != MC3230_CHIP_ID)
return (ret < 0) ? ret : -ENODEV;
if (ret != chip_info->chip_id) {
dev_info(&client->dev,
"chip id check fail: 0x%x != 0x%x !\n",
ret, chip_info->chip_id);
}
ret = i2c_smbus_read_byte_data(client, MC3230_REG_PRODUCT_CODE);
if (ret != MC3230_PRODUCT_CODE)
return (ret < 0) ? ret : -ENODEV;
if (ret != chip_info->product_code) {
dev_info(&client->dev,
"product code check fail: 0x%x != 0x%x !\n",
ret, chip_info->product_code);
}
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev) {
@ -128,11 +175,12 @@ static int mc3230_probe(struct i2c_client *client)
}
data = iio_priv(indio_dev);
data->chip_info = chip_info;
data->client = client;
i2c_set_clientdata(client, indio_dev);
indio_dev->info = &mc3230_info;
indio_dev->name = "mc3230";
indio_dev->name = chip_info->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = mc3230_channels;
indio_dev->num_channels = ARRAY_SIZE(mc3230_channels);
@ -141,6 +189,10 @@ static int mc3230_probe(struct i2c_client *client)
if (ret < 0)
return ret;
ret = iio_read_mount_matrix(&client->dev, &data->orientation);
if (ret)
return ret;
ret = iio_device_register(indio_dev);
if (ret < 0) {
dev_err(&client->dev, "device_register failed\n");
@ -180,14 +232,23 @@ static int mc3230_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume);
static const struct i2c_device_id mc3230_i2c_id[] = {
{ "mc3230" },
{}
{ "mc3230", (kernel_ulong_t)&mc3230_chip_info },
{ "mc3510c", (kernel_ulong_t)&mc3510c_chip_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, mc3230_i2c_id);
static const struct of_device_id mc3230_of_match[] = {
{ .compatible = "mcube,mc3230", &mc3230_chip_info },
{ .compatible = "mcube,mc3510c", &mc3510c_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, mc3230_of_match);
static struct i2c_driver mc3230_driver = {
.driver = {
.name = "mc3230",
.of_match_table = mc3230_of_match,
.pm = pm_sleep_ptr(&mc3230_pm_ops),
},
.probe = mc3230_probe,

View File

@ -497,14 +497,13 @@ static int mma8452_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
mutex_lock(&data->lock);
ret = mma8452_read(data, buffer);
mutex_unlock(&data->lock);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
@ -707,55 +706,45 @@ static int mma8452_set_hp_filter_frequency(struct mma8452_data *data,
return mma8452_change_config(data, MMA8452_HP_FILTER_CUTOFF, reg);
}
static int mma8452_write_raw(struct iio_dev *indio_dev,
static int __mma8452_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct mma8452_data *data = iio_priv(indio_dev);
int i, ret;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
int i, j, ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
i = mma8452_get_samp_freq_index(data, val, val2);
if (i < 0) {
ret = i;
break;
}
if (i < 0)
return i;
data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK;
data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT;
data->sleep_val = mma8452_calculate_sleep(data);
ret = mma8452_change_config(data, MMA8452_CTRL_REG1,
data->ctrl_reg1);
break;
return mma8452_change_config(data, MMA8452_CTRL_REG1,
data->ctrl_reg1);
case IIO_CHAN_INFO_SCALE:
i = mma8452_get_scale_index(data, val, val2);
if (i < 0) {
ret = i;
break;
}
if (i < 0)
return i;
data->data_cfg &= ~MMA8452_DATA_CFG_FS_MASK;
data->data_cfg |= i;
ret = mma8452_change_config(data, MMA8452_DATA_CFG,
data->data_cfg);
break;
case IIO_CHAN_INFO_CALIBBIAS:
if (val < -128 || val > 127) {
ret = -EINVAL;
break;
}
return mma8452_change_config(data, MMA8452_DATA_CFG,
data->data_cfg);
ret = mma8452_change_config(data,
MMA8452_OFF_X + chan->scan_index,
val);
break;
case IIO_CHAN_INFO_CALIBBIAS:
if (val < -128 || val > 127)
return -EINVAL;
return mma8452_change_config(data,
MMA8452_OFF_X + chan->scan_index,
val);
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
if (val == 0 && val2 == 0) {
@ -764,29 +753,38 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
data->data_cfg |= MMA8452_DATA_CFG_HPF_MASK;
ret = mma8452_set_hp_filter_frequency(data, val, val2);
if (ret < 0)
break;
return ret;
}
ret = mma8452_change_config(data, MMA8452_DATA_CFG,
return mma8452_change_config(data, MMA8452_DATA_CFG,
data->data_cfg);
break;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
ret = mma8452_get_odr_index(data);
j = mma8452_get_odr_index(data);
for (i = 0; i < ARRAY_SIZE(mma8452_os_ratio); i++) {
if (mma8452_os_ratio[i][ret] == val) {
ret = mma8452_set_power_mode(data, i);
break;
}
if (mma8452_os_ratio[i][j] == val)
return mma8452_set_power_mode(data, i);
}
break;
default:
ret = -EINVAL;
break;
}
iio_device_release_direct_mode(indio_dev);
return -EINVAL;
default:
return -EINVAL;
}
}
static int mma8452_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
int ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = __mma8452_write_raw(indio_dev, chan, val, val2, mask);
iio_device_release_direct(indio_dev);
return ret;
}

View File

@ -332,7 +332,7 @@ static const struct regmap_config msa311_regmap_config = {
.wr_table = &msa311_writeable_table,
.rd_table = &msa311_readable_table,
.volatile_table = &msa311_volatile_table,
.cache_type = REGCACHE_RBTREE,
.cache_type = REGCACHE_MAPLE,
};
#define MSA311_GENMASK(field) ({ \
@ -594,23 +594,24 @@ static int msa311_read_raw_data(struct iio_dev *indio_dev,
__le16 axis;
int err;
err = pm_runtime_resume_and_get(dev);
if (err)
return err;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
err = iio_device_claim_direct_mode(indio_dev);
if (err)
err = pm_runtime_resume_and_get(dev);
if (err) {
iio_device_release_direct(indio_dev);
return err;
}
mutex_lock(&msa311->lock);
err = msa311_get_axis(msa311, chan, &axis);
mutex_unlock(&msa311->lock);
iio_device_release_direct_mode(indio_dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
iio_device_release_direct(indio_dev);
if (err) {
dev_err(dev, "can't get axis %s (%pe)\n",
chan->datasheet_name, ERR_PTR(err));
@ -756,18 +757,19 @@ static int msa311_write_samp_freq(struct iio_dev *indio_dev, int val, int val2)
unsigned int odr;
int err;
err = pm_runtime_resume_and_get(dev);
if (err)
return err;
/*
* Sampling frequency changing is prohibited when buffer mode is
* enabled, because sometimes MSA311 chip returns outliers during
* frequency values growing up in the read operation moment.
*/
err = iio_device_claim_direct_mode(indio_dev);
if (err)
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
err = pm_runtime_resume_and_get(dev);
if (err) {
iio_device_release_direct(indio_dev);
return err;
}
err = -EINVAL;
for (odr = 0; odr < ARRAY_SIZE(msa311_odr_table); odr++)
@ -779,11 +781,11 @@ static int msa311_write_samp_freq(struct iio_dev *indio_dev, int val, int val2)
break;
}
iio_device_release_direct_mode(indio_dev);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
iio_device_release_direct(indio_dev);
if (err)
dev_err(dev, "can't update frequency (%pe)\n", ERR_PTR(err));

View File

@ -33,6 +33,20 @@ config AD4000
To compile this driver as a module, choose M here: the module will be
called ad4000.
config AD4030
tristate "Analog Devices AD4030 ADC Driver"
depends on SPI
depends on GPIOLIB
select REGMAP
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Analog Devices AD4030 and AD4630 high speed
SPI analog to digital converters (ADC).
To compile this driver as a module, choose M here: the module will be
called ad4030.
config AD4130
tristate "Analog Device AD4130 ADC Driver"
depends on SPI
@ -51,9 +65,11 @@ config AD4130
config AD4695
tristate "Analog Device AD4695 ADC Driver"
depends on SPI
select REGMAP_SPI
select IIO_BUFFER
select IIO_BUFFER_DMAENGINE
select IIO_TRIGGERED_BUFFER
select REGMAP
select SPI_OFFLOAD
help
Say yes here to build support for Analog Devices AD4695 and similar
analog to digital converters (ADC).
@ -61,6 +77,20 @@ config AD4695
To compile this driver as a module, choose M here: the module will be
called ad4695.
config AD4851
tristate "Analog Device AD4851 DAS Driver"
depends on SPI
depends on PWM
select REGMAP_SPI
select IIO_BACKEND
help
Say yes here to build support for Analog Devices AD4851, AD4852,
AD4853, AD4854, AD4855, AD4856, AD4857, AD4858, AD4858I high speed
data acquisition system (DAS).
To compile this driver as a module, choose M here: the module will be
called ad4851.
config AD7091R
tristate
@ -112,6 +142,16 @@ config AD7173
To compile this driver as a module, choose M here: the module will be
called ad7173.
config AD7191
tristate "Analog Devices AD7191 ADC driver"
depends on SPI
select AD_SIGMA_DELTA
help
Say yes here to build support for Analog Devices AD7191.
To compile this driver as a module, choose M here: the
module will be called ad7191.
config AD7192
tristate "Analog Devices AD7192 and similar ADC driver"
depends on SPI
@ -188,7 +228,9 @@ config AD7298
config AD7380
tristate "Analog Devices AD7380 ADC driver"
depends on SPI_MASTER
select SPI_OFFLOAD
select IIO_BUFFER
select IIO_BUFFER_DMAENGINE
select IIO_TRIGGER
select IIO_TRIGGERED_BUFFER
help
@ -360,7 +402,9 @@ config AD7923
config AD7944
tristate "Analog Devices AD7944 and similar ADCs driver"
depends on SPI
select SPI_OFFLOAD
select IIO_BUFFER
select IIO_BUFFER_DMAENGINE
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Analog Devices
@ -1467,6 +1511,16 @@ config TI_ADS1119
This driver can also be built as a module. If so, the module will be
called ti-ads1119.
config TI_ADS7138
tristate "Texas Instruments ADS7128 and ADS7138 ADC driver"
depends on I2C
help
If you say yes here you get support for Texas Instruments ADS7128 and
ADS7138 8-channel A/D converters with 12-bit resolution.
This driver can also be built as a module. If so, the module will be
called ti-ads7138.
config TI_ADS7924
tristate "Texas Instruments ADS7924 ADC"
depends on I2C

View File

@ -7,13 +7,16 @@
obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
obj-$(CONFIG_AD4000) += ad4000.o
obj-$(CONFIG_AD4030) += ad4030.o
obj-$(CONFIG_AD4130) += ad4130.o
obj-$(CONFIG_AD4695) += ad4695.o
obj-$(CONFIG_AD4851) += ad4851.o
obj-$(CONFIG_AD7091R) += ad7091r-base.o
obj-$(CONFIG_AD7091R5) += ad7091r5.o
obj-$(CONFIG_AD7091R8) += ad7091r8.o
obj-$(CONFIG_AD7124) += ad7124.o
obj-$(CONFIG_AD7173) += ad7173.o
obj-$(CONFIG_AD7191) += ad7191.o
obj-$(CONFIG_AD7192) += ad7192.o
obj-$(CONFIG_AD7266) += ad7266.o
obj-$(CONFIG_AD7280) += ad7280a.o
@ -133,6 +136,7 @@ obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
obj-$(CONFIG_TI_ADS7138) += ti-ads7138.o
obj-$(CONFIG_TI_ADS7924) += ti-ads7924.o
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o

View File

@ -535,12 +535,16 @@ static int ad4000_read_raw(struct iio_dev *indio_dev,
int *val2, long info)
{
struct ad4000_state *st = iio_priv(indio_dev);
int ret;
switch (info) {
case IIO_CHAN_INFO_RAW:
iio_device_claim_direct_scoped(return -EBUSY, indio_dev)
return ad4000_single_conversion(indio_dev, chan, val);
unreachable();
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad4000_single_conversion(indio_dev, chan, val);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
*val = st->scale_tbl[st->span_comp][0];
*val2 = st->scale_tbl[st->span_comp][1];
@ -585,36 +589,46 @@ static int ad4000_write_raw_get_fmt(struct iio_dev *indio_dev,
}
}
static int ad4000_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2,
long mask)
static int __ad4000_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val2)
{
struct ad4000_state *st = iio_priv(indio_dev);
unsigned int reg_val;
bool span_comp_en;
int ret;
guard(mutex)(&st->lock);
ret = ad4000_read_reg(st, &reg_val);
if (ret < 0)
return ret;
span_comp_en = val2 == st->scale_tbl[1][1];
reg_val &= ~AD4000_CFG_SPAN_COMP;
reg_val |= FIELD_PREP(AD4000_CFG_SPAN_COMP, span_comp_en);
ret = ad4000_write_reg(st, reg_val);
if (ret < 0)
return ret;
st->span_comp = span_comp_en;
return 0;
}
static int ad4000_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
int ret;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
guard(mutex)(&st->lock);
ret = ad4000_read_reg(st, &reg_val);
if (ret < 0)
return ret;
span_comp_en = val2 == st->scale_tbl[1][1];
reg_val &= ~AD4000_CFG_SPAN_COMP;
reg_val |= FIELD_PREP(AD4000_CFG_SPAN_COMP, span_comp_en);
ret = ad4000_write_reg(st, reg_val);
if (ret < 0)
return ret;
st->span_comp = span_comp_en;
return 0;
}
unreachable();
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = __ad4000_write_raw(indio_dev, chan, val2);
iio_device_release_direct(indio_dev);
return ret;
default:
return -EINVAL;
}

1230
drivers/iio/adc/ad4030.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
@ -203,7 +204,7 @@ enum ad4130_mode {
AD4130_MODE_IDLE = 0b0100,
};
enum ad4130_filter_mode {
enum ad4130_filter_type {
AD4130_FILTER_SINC4,
AD4130_FILTER_SINC4_SINC1,
AD4130_FILTER_SINC3,
@ -223,6 +224,10 @@ enum ad4130_pin_function {
AD4130_PIN_FN_VBIAS = BIT(3),
};
/*
* If you make adaptations in this struct, you most likely also have to adapt
* ad4130_setup_info_eq(), too.
*/
struct ad4130_setup_info {
unsigned int iout0_val;
unsigned int iout1_val;
@ -230,7 +235,7 @@ struct ad4130_setup_info {
unsigned int pga;
unsigned int fs;
u32 ref_sel;
enum ad4130_filter_mode filter_mode;
enum ad4130_filter_type filter_type;
bool ref_bufp;
bool ref_bufm;
};
@ -251,7 +256,7 @@ struct ad4130_chan_info {
};
struct ad4130_filter_config {
enum ad4130_filter_mode filter_mode;
enum ad4130_filter_type filter_type;
unsigned int odr_div;
unsigned int fs_max;
enum iio_available_type samp_freq_avail_type;
@ -337,9 +342,9 @@ static const unsigned int ad4130_burnout_current_na_tbl[AD4130_BURNOUT_MAX] = {
[AD4130_BURNOUT_4000NA] = 4000,
};
#define AD4130_VARIABLE_ODR_CONFIG(_filter_mode, _odr_div, _fs_max) \
#define AD4130_VARIABLE_ODR_CONFIG(_filter_type, _odr_div, _fs_max) \
{ \
.filter_mode = (_filter_mode), \
.filter_type = (_filter_type), \
.odr_div = (_odr_div), \
.fs_max = (_fs_max), \
.samp_freq_avail_type = IIO_AVAIL_RANGE, \
@ -350,9 +355,9 @@ static const unsigned int ad4130_burnout_current_na_tbl[AD4130_BURNOUT_MAX] = {
}, \
}
#define AD4130_FIXED_ODR_CONFIG(_filter_mode, _odr_div) \
#define AD4130_FIXED_ODR_CONFIG(_filter_type, _odr_div) \
{ \
.filter_mode = (_filter_mode), \
.filter_type = (_filter_type), \
.odr_div = (_odr_div), \
.fs_max = AD4130_FILTER_SELECT_MIN, \
.samp_freq_avail_type = IIO_AVAIL_LIST, \
@ -374,7 +379,7 @@ static const struct ad4130_filter_config ad4130_filter_configs[] = {
AD4130_FIXED_ODR_CONFIG(AD4130_FILTER_SINC3_PF4, 148),
};
static const char * const ad4130_filter_modes_str[] = {
static const char * const ad4130_filter_types_str[] = {
[AD4130_FILTER_SINC4] = "sinc4",
[AD4130_FILTER_SINC4_SINC1] = "sinc4+sinc1",
[AD4130_FILTER_SINC3] = "sinc3",
@ -591,6 +596,40 @@ static irqreturn_t ad4130_irq_handler(int irq, void *private)
return IRQ_HANDLED;
}
static bool ad4130_setup_info_eq(struct ad4130_setup_info *a,
struct ad4130_setup_info *b)
{
/*
* This is just to make sure that the comparison is adapted after
* struct ad4130_setup_info was changed.
*/
static_assert(sizeof(*a) ==
sizeof(struct {
unsigned int iout0_val;
unsigned int iout1_val;
unsigned int burnout;
unsigned int pga;
unsigned int fs;
u32 ref_sel;
enum ad4130_filter_type filter_type;
bool ref_bufp;
bool ref_bufm;
}));
if (a->iout0_val != b->iout0_val ||
a->iout1_val != b->iout1_val ||
a->burnout != b->burnout ||
a->pga != b->pga ||
a->fs != b->fs ||
a->ref_sel != b->ref_sel ||
a->filter_type != b->filter_type ||
a->ref_bufp != b->ref_bufp ||
a->ref_bufm != b->ref_bufm)
return false;
return true;
}
static int ad4130_find_slot(struct ad4130_state *st,
struct ad4130_setup_info *target_setup_info,
unsigned int *slot, bool *overwrite)
@ -604,8 +643,7 @@ static int ad4130_find_slot(struct ad4130_state *st,
struct ad4130_slot_info *slot_info = &st->slots_info[i];
/* Immediately accept a matching setup info. */
if (!memcmp(target_setup_info, &slot_info->setup,
sizeof(*target_setup_info))) {
if (ad4130_setup_info_eq(target_setup_info, &slot_info->setup)) {
*slot = i;
return 0;
}
@ -691,7 +729,7 @@ static int ad4130_write_slot_setup(struct ad4130_state *st,
if (ret)
return ret;
val = FIELD_PREP(AD4130_FILTER_MODE_MASK, setup_info->filter_mode) |
val = FIELD_PREP(AD4130_FILTER_MODE_MASK, setup_info->filter_type) |
FIELD_PREP(AD4130_FILTER_SELECT_MASK, setup_info->fs);
ret = regmap_write(st->regmap, AD4130_FILTER_X_REG(slot), val);
@ -835,11 +873,11 @@ static int ad4130_set_channel_enable(struct ad4130_state *st,
* (used in ad4130_fs_to_freq)
*/
static void ad4130_freq_to_fs(enum ad4130_filter_mode filter_mode,
static void ad4130_freq_to_fs(enum ad4130_filter_type filter_type,
int val, int val2, unsigned int *fs)
{
const struct ad4130_filter_config *filter_config =
&ad4130_filter_configs[filter_mode];
&ad4130_filter_configs[filter_type];
u64 dividend, divisor;
int temp;
@ -858,11 +896,11 @@ static void ad4130_freq_to_fs(enum ad4130_filter_mode filter_mode,
*fs = temp;
}
static void ad4130_fs_to_freq(enum ad4130_filter_mode filter_mode,
static void ad4130_fs_to_freq(enum ad4130_filter_type filter_type,
unsigned int fs, int *val, int *val2)
{
const struct ad4130_filter_config *filter_config =
&ad4130_filter_configs[filter_mode];
&ad4130_filter_configs[filter_type];
unsigned int dividend, divisor;
u64 temp;
@ -874,7 +912,7 @@ static void ad4130_fs_to_freq(enum ad4130_filter_mode filter_mode,
*val = div_u64_rem(temp, NANO, val2);
}
static int ad4130_set_filter_mode(struct iio_dev *indio_dev,
static int ad4130_set_filter_type(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int val)
{
@ -882,17 +920,17 @@ static int ad4130_set_filter_mode(struct iio_dev *indio_dev,
unsigned int channel = chan->scan_index;
struct ad4130_chan_info *chan_info = &st->chans_info[channel];
struct ad4130_setup_info *setup_info = &chan_info->setup;
enum ad4130_filter_mode old_filter_mode;
enum ad4130_filter_type old_filter_type;
int freq_val, freq_val2;
unsigned int old_fs;
int ret = 0;
guard(mutex)(&st->lock);
if (setup_info->filter_mode == val)
if (setup_info->filter_type == val)
return 0;
old_fs = setup_info->fs;
old_filter_mode = setup_info->filter_mode;
old_filter_type = setup_info->filter_type;
/*
* When switching between filter modes, try to match the ODR as
@ -900,48 +938,55 @@ static int ad4130_set_filter_mode(struct iio_dev *indio_dev,
* using the old filter mode, then convert it back into FS using
* the new filter mode.
*/
ad4130_fs_to_freq(setup_info->filter_mode, setup_info->fs,
ad4130_fs_to_freq(setup_info->filter_type, setup_info->fs,
&freq_val, &freq_val2);
ad4130_freq_to_fs(val, freq_val, freq_val2, &setup_info->fs);
setup_info->filter_mode = val;
setup_info->filter_type = val;
ret = ad4130_write_channel_setup(st, channel, false);
if (ret) {
setup_info->fs = old_fs;
setup_info->filter_mode = old_filter_mode;
setup_info->filter_type = old_filter_type;
return ret;
}
return 0;
}
static int ad4130_get_filter_mode(struct iio_dev *indio_dev,
static int ad4130_get_filter_type(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct ad4130_state *st = iio_priv(indio_dev);
unsigned int channel = chan->scan_index;
struct ad4130_setup_info *setup_info = &st->chans_info[channel].setup;
enum ad4130_filter_mode filter_mode;
enum ad4130_filter_type filter_type;
guard(mutex)(&st->lock);
filter_mode = setup_info->filter_mode;
filter_type = setup_info->filter_type;
return filter_mode;
return filter_type;
}
static const struct iio_enum ad4130_filter_mode_enum = {
.items = ad4130_filter_modes_str,
.num_items = ARRAY_SIZE(ad4130_filter_modes_str),
.set = ad4130_set_filter_mode,
.get = ad4130_get_filter_mode,
static const struct iio_enum ad4130_filter_type_enum = {
.items = ad4130_filter_types_str,
.num_items = ARRAY_SIZE(ad4130_filter_types_str),
.set = ad4130_set_filter_type,
.get = ad4130_get_filter_type,
};
static const struct iio_chan_spec_ext_info ad4130_filter_mode_ext_info[] = {
IIO_ENUM("filter_mode", IIO_SEPARATE, &ad4130_filter_mode_enum),
static const struct iio_chan_spec_ext_info ad4130_ext_info[] = {
/*
* `filter_type` is the standardized IIO ABI for digital filtering.
* `filter_mode` is just kept for backwards compatibility.
*/
IIO_ENUM("filter_mode", IIO_SEPARATE, &ad4130_filter_type_enum),
IIO_ENUM_AVAILABLE("filter_mode", IIO_SHARED_BY_TYPE,
&ad4130_filter_mode_enum),
&ad4130_filter_type_enum),
IIO_ENUM("filter_type", IIO_SEPARATE, &ad4130_filter_type_enum),
IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE,
&ad4130_filter_type_enum),
{ }
};
@ -955,7 +1000,7 @@ static const struct iio_chan_spec ad4130_channel_template = {
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.ext_info = ad4130_filter_mode_ext_info,
.ext_info = ad4130_ext_info,
.scan_type = {
.sign = 'u',
.endianness = IIO_BE,
@ -1005,7 +1050,7 @@ static int ad4130_set_channel_freq(struct ad4130_state *st,
guard(mutex)(&st->lock);
old_fs = setup_info->fs;
ad4130_freq_to_fs(setup_info->filter_mode, val, val2, &fs);
ad4130_freq_to_fs(setup_info->filter_type, val, val2, &fs);
if (fs == setup_info->fs)
return 0;
@ -1060,13 +1105,11 @@ static int _ad4130_read_sample(struct iio_dev *indio_dev, unsigned int channel,
static int ad4130_read_sample(struct iio_dev *indio_dev, unsigned int channel,
int *val)
{
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
struct ad4130_state *st = iio_priv(indio_dev);
struct ad4130_state *st = iio_priv(indio_dev);
guard(mutex)(&st->lock);
return _ad4130_read_sample(indio_dev, channel, val);
}
unreachable();
guard(mutex)(&st->lock);
return _ad4130_read_sample(indio_dev, channel, val);
}
static int ad4130_read_raw(struct iio_dev *indio_dev,
@ -1076,10 +1119,16 @@ static int ad4130_read_raw(struct iio_dev *indio_dev,
struct ad4130_state *st = iio_priv(indio_dev);
unsigned int channel = chan->scan_index;
struct ad4130_setup_info *setup_info = &st->chans_info[channel].setup;
int ret;
switch (info) {
case IIO_CHAN_INFO_RAW:
return ad4130_read_sample(indio_dev, channel, val);
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad4130_read_sample(indio_dev, channel, val);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE: {
guard(mutex)(&st->lock);
*val = st->scale_tbls[setup_info->ref_sel][setup_info->pga][0];
@ -1093,7 +1142,7 @@ static int ad4130_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ: {
guard(mutex)(&st->lock);
ad4130_fs_to_freq(setup_info->filter_mode, setup_info->fs,
ad4130_fs_to_freq(setup_info->filter_type, setup_info->fs,
val, val2);
return IIO_VAL_INT_PLUS_NANO;
@ -1123,7 +1172,7 @@ static int ad4130_read_avail(struct iio_dev *indio_dev,
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SAMP_FREQ:
scoped_guard(mutex, &st->lock) {
filter_config = &ad4130_filter_configs[setup_info->filter_mode];
filter_config = &ad4130_filter_configs[setup_info->filter_type];
}
*vals = (int *)filter_config->samp_freq_avail;

File diff suppressed because it is too large Load Diff

1315
drivers/iio/adc/ad4851.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@
#include <linux/bitops.h>
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/interrupt.h>

View File

@ -53,6 +53,11 @@
#define AD7124_ADC_CTRL_MODE_MSK GENMASK(5, 2)
#define AD7124_ADC_CTRL_MODE(x) FIELD_PREP(AD7124_ADC_CTRL_MODE_MSK, x)
#define AD7124_MODE_CAL_INT_ZERO 0x5 /* Internal Zero-Scale Calibration */
#define AD7124_MODE_CAL_INT_FULL 0x6 /* Internal Full-Scale Calibration */
#define AD7124_MODE_CAL_SYS_ZERO 0x7 /* System Zero-Scale Calibration */
#define AD7124_MODE_CAL_SYS_FULL 0x8 /* System Full-Scale Calibration */
/* AD7124 ID */
#define AD7124_DEVICE_ID_MSK GENMASK(7, 4)
#define AD7124_DEVICE_ID_GET(x) FIELD_GET(AD7124_DEVICE_ID_MSK, x)
@ -151,7 +156,11 @@ struct ad7124_chip_info {
struct ad7124_channel_config {
bool live;
unsigned int cfg_slot;
/* Following fields are used to compare equality. */
/*
* Following fields are used to compare for equality. If you
* make adaptations in it, you most likely also have to adapt
* ad7124_find_similar_live_cfg(), too.
*/
struct_group(config_props,
enum ad7124_ref_sel refsel;
bool bipolar;
@ -162,6 +171,8 @@ struct ad7124_channel_config {
unsigned int odr;
unsigned int odr_sel_bits;
unsigned int filter_type;
unsigned int calibration_offset;
unsigned int calibration_gain;
);
};
@ -170,6 +181,7 @@ struct ad7124_channel {
struct ad7124_channel_config cfg;
unsigned int ain;
unsigned int slot;
u8 syscalib_mode;
};
struct ad7124_state {
@ -182,24 +194,13 @@ struct ad7124_state {
unsigned int num_channels;
struct mutex cfgs_lock; /* lock for configs access */
unsigned long cfg_slots_status; /* bitmap with slot status (1 means it is used) */
DECLARE_KFIFO(live_cfgs_fifo, struct ad7124_channel_config *, AD7124_MAX_CONFIGS);
};
static const struct iio_chan_spec ad7124_channel_template = {
.type = IIO_VOLTAGE,
.indexed = 1,
.differential = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
.scan_type = {
.sign = 'u',
.realbits = 24,
.storagebits = 32,
.endianness = IIO_BE,
},
/*
* Stores the power-on reset value for the GAIN(x) registers which are
* needed for measurements at gain 1 (i.e. CONFIG(x).PGA == 0)
*/
unsigned int gain_default;
DECLARE_KFIFO(live_cfgs_fifo, struct ad7124_channel_config *, AD7124_MAX_CONFIGS);
};
static struct ad7124_chip_info ad7124_chip_info_tbl[] = {
@ -338,15 +339,42 @@ static struct ad7124_channel_config *ad7124_find_similar_live_cfg(struct ad7124_
struct ad7124_channel_config *cfg)
{
struct ad7124_channel_config *cfg_aux;
ptrdiff_t cmp_size;
int i;
cmp_size = sizeof_field(struct ad7124_channel_config, config_props);
/*
* This is just to make sure that the comparison is adapted after
* struct ad7124_channel_config was changed.
*/
static_assert(sizeof_field(struct ad7124_channel_config, config_props) ==
sizeof(struct {
enum ad7124_ref_sel refsel;
bool bipolar;
bool buf_positive;
bool buf_negative;
unsigned int vref_mv;
unsigned int pga_bits;
unsigned int odr;
unsigned int odr_sel_bits;
unsigned int filter_type;
unsigned int calibration_offset;
unsigned int calibration_gain;
}));
for (i = 0; i < st->num_channels; i++) {
cfg_aux = &st->channels[i].cfg;
if (cfg_aux->live &&
!memcmp(&cfg->config_props, &cfg_aux->config_props, cmp_size))
cfg->refsel == cfg_aux->refsel &&
cfg->bipolar == cfg_aux->bipolar &&
cfg->buf_positive == cfg_aux->buf_positive &&
cfg->buf_negative == cfg_aux->buf_negative &&
cfg->vref_mv == cfg_aux->vref_mv &&
cfg->pga_bits == cfg_aux->pga_bits &&
cfg->odr == cfg_aux->odr &&
cfg->odr_sel_bits == cfg_aux->odr_sel_bits &&
cfg->filter_type == cfg_aux->filter_type &&
cfg->calibration_offset == cfg_aux->calibration_offset &&
cfg->calibration_gain == cfg_aux->calibration_gain)
return cfg_aux;
}
@ -402,6 +430,14 @@ static int ad7124_write_config(struct ad7124_state *st, struct ad7124_channel_co
cfg->cfg_slot = cfg_slot;
ret = ad_sd_write_reg(&st->sd, AD7124_OFFSET(cfg->cfg_slot), 3, cfg->calibration_offset);
if (ret)
return ret;
ret = ad_sd_write_reg(&st->sd, AD7124_GAIN(cfg->cfg_slot), 3, cfg->calibration_gain);
if (ret)
return ret;
tmp = (cfg->buf_positive << 1) + cfg->buf_negative;
val = AD7124_CONFIG_BIPOLAR(cfg->bipolar) | AD7124_CONFIG_REF_SEL(cfg->refsel) |
AD7124_CONFIG_IN_BUFF(tmp) | AD7124_CONFIG_PGA(cfg->pga_bits);
@ -540,14 +576,21 @@ static int ad7124_append_status(struct ad_sigma_delta *sd, bool append)
return 0;
}
static int ad7124_disable_all(struct ad_sigma_delta *sd)
static int ad7124_disable_one(struct ad_sigma_delta *sd, unsigned int chan)
{
struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
/* The relevant thing here is that AD7124_CHANNEL_EN_MSK is cleared. */
return ad_sd_write_reg(&st->sd, AD7124_CHANNEL(chan), 2, 0);
}
static int ad7124_disable_all(struct ad_sigma_delta *sd)
{
int ret;
int i;
for (i = 0; i < st->num_channels; i++) {
ret = ad7124_spi_write_mask(st, AD7124_CHANNEL(i), AD7124_CHANNEL_EN_MSK, 0, 2);
for (i = 0; i < 16; i++) {
ret = ad7124_disable_one(sd, i);
if (ret < 0)
return ret;
}
@ -555,13 +598,6 @@ static int ad7124_disable_all(struct ad_sigma_delta *sd)
return 0;
}
static int ad7124_disable_one(struct ad_sigma_delta *sd, unsigned int chan)
{
struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
return ad7124_spi_write_mask(st, AD7124_CHANNEL(chan), AD7124_CHANNEL_EN_MSK, 0, 2);
}
static const struct ad_sigma_delta_info ad7124_sigma_delta_info = {
.set_channel = ad7124_set_channel,
.append_status = ad7124_append_status,
@ -808,13 +844,22 @@ static int ad7124_soft_reset(struct ad7124_state *st)
return dev_err_probe(dev, ret, "Error reading status register\n");
if (!(readval & AD7124_STATUS_POR_FLAG_MSK))
return 0;
break;
/* The AD7124 requires typically 2ms to power up and settle */
usleep_range(100, 2000);
} while (--timeout);
return dev_err_probe(dev, -EIO, "Soft reset failed\n");
if (readval & AD7124_STATUS_POR_FLAG_MSK)
return dev_err_probe(dev, -EIO, "Soft reset failed\n");
ret = ad_sd_read_reg(&st->sd, AD7124_GAIN(0), 3, &st->gain_default);
if (ret < 0)
return dev_err_probe(dev, ret, "Error reading gain register\n");
dev_dbg(dev, "Reset value of GAIN register is 0x%x\n", st->gain_default);
return 0;
}
static int ad7124_check_chip_id(struct ad7124_state *st)
@ -842,6 +887,140 @@ static int ad7124_check_chip_id(struct ad7124_state *st)
return 0;
}
enum {
AD7124_SYSCALIB_ZERO_SCALE,
AD7124_SYSCALIB_FULL_SCALE,
};
static int ad7124_syscalib_locked(struct ad7124_state *st, const struct iio_chan_spec *chan)
{
struct device *dev = &st->sd.spi->dev;
struct ad7124_channel *ch = &st->channels[chan->channel];
int ret;
if (ch->syscalib_mode == AD7124_SYSCALIB_ZERO_SCALE) {
ch->cfg.calibration_offset = 0x800000;
ret = ad_sd_calibrate(&st->sd, AD7124_MODE_CAL_SYS_ZERO,
chan->address);
if (ret < 0)
return ret;
ret = ad_sd_read_reg(&st->sd, AD7124_OFFSET(ch->cfg.cfg_slot), 3,
&ch->cfg.calibration_offset);
if (ret < 0)
return ret;
dev_dbg(dev, "offset for channel %d after zero-scale calibration: 0x%x\n",
chan->channel, ch->cfg.calibration_offset);
} else {
ch->cfg.calibration_gain = st->gain_default;
ret = ad_sd_calibrate(&st->sd, AD7124_MODE_CAL_SYS_FULL,
chan->address);
if (ret < 0)
return ret;
ret = ad_sd_read_reg(&st->sd, AD7124_GAIN(ch->cfg.cfg_slot), 3,
&ch->cfg.calibration_gain);
if (ret < 0)
return ret;
dev_dbg(dev, "gain for channel %d after full-scale calibration: 0x%x\n",
chan->channel, ch->cfg.calibration_gain);
}
return 0;
}
static ssize_t ad7124_write_syscalib(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct ad7124_state *st = iio_priv(indio_dev);
bool sys_calib;
int ret;
ret = kstrtobool(buf, &sys_calib);
if (ret)
return ret;
if (!sys_calib)
return len;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7124_syscalib_locked(st, chan);
iio_device_release_direct(indio_dev);
return ret ?: len;
}
static const char * const ad7124_syscalib_modes[] = {
[AD7124_SYSCALIB_ZERO_SCALE] = "zero_scale",
[AD7124_SYSCALIB_FULL_SCALE] = "full_scale",
};
static int ad7124_set_syscalib_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int mode)
{
struct ad7124_state *st = iio_priv(indio_dev);
st->channels[chan->channel].syscalib_mode = mode;
return 0;
}
static int ad7124_get_syscalib_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct ad7124_state *st = iio_priv(indio_dev);
return st->channels[chan->channel].syscalib_mode;
}
static const struct iio_enum ad7124_syscalib_mode_enum = {
.items = ad7124_syscalib_modes,
.num_items = ARRAY_SIZE(ad7124_syscalib_modes),
.set = ad7124_set_syscalib_mode,
.get = ad7124_get_syscalib_mode
};
static const struct iio_chan_spec_ext_info ad7124_calibsys_ext_info[] = {
{
.name = "sys_calibration",
.write = ad7124_write_syscalib,
.shared = IIO_SEPARATE,
},
IIO_ENUM("sys_calibration_mode", IIO_SEPARATE,
&ad7124_syscalib_mode_enum),
IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE,
&ad7124_syscalib_mode_enum),
{ }
};
static const struct iio_chan_spec ad7124_channel_template = {
.type = IIO_VOLTAGE,
.indexed = 1,
.differential = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
.scan_type = {
.sign = 'u',
.realbits = 24,
.storagebits = 32,
.endianness = IIO_BE,
},
.ext_info = ad7124_calibsys_ext_info,
};
/*
* Input specifiers 8 - 15 are explicitly reserved for ad7124-4
* while they are fine for ad7124-8. Values above 31 don't fit
@ -881,12 +1060,12 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev,
/* Add one for temperature */
st->num_channels = min(num_channels + 1, AD7124_MAX_CHANNELS);
chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels,
chan = devm_kcalloc(dev, st->num_channels,
sizeof(*chan), GFP_KERNEL);
if (!chan)
return -ENOMEM;
channels = devm_kcalloc(indio_dev->dev.parent, st->num_channels, sizeof(*channels),
channels = devm_kcalloc(dev, st->num_channels, sizeof(*channels),
GFP_KERNEL);
if (!channels)
return -ENOMEM;
@ -1016,11 +1195,10 @@ static int ad7124_setup(struct ad7124_state *st)
* set all channels to this default value.
*/
ad7124_set_channel_odr(st, i, 10);
/* Disable all channels to prevent unintended conversions. */
ad_sd_write_reg(&st->sd, AD7124_CHANNEL(i), 2, 0);
}
ad7124_disable_all(&st->sd);
ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to setup CONTROL register\n");
@ -1028,6 +1206,91 @@ static int ad7124_setup(struct ad7124_state *st)
return ret;
}
static int __ad7124_calibrate_all(struct ad7124_state *st, struct iio_dev *indio_dev)
{
struct device *dev = &st->sd.spi->dev;
int ret, i;
for (i = 0; i < st->num_channels; i++) {
if (indio_dev->channels[i].type != IIO_VOLTAGE)
continue;
/*
* For calibration the OFFSET register should hold its reset default
* value. For the GAIN register there is no such requirement but
* for gain 1 it should hold the reset default value, too. So to
* simplify matters use the reset default value for both.
*/
st->channels[i].cfg.calibration_offset = 0x800000;
st->channels[i].cfg.calibration_gain = st->gain_default;
/*
* Full-scale calibration isn't supported at gain 1, so skip in
* that case. Note that untypically full-scale calibration has
* to happen before zero-scale calibration. This only applies to
* the internal calibration. For system calibration it's as
* usual: first zero-scale then full-scale calibration.
*/
if (st->channels[i].cfg.pga_bits > 0) {
ret = ad_sd_calibrate(&st->sd, AD7124_MODE_CAL_INT_FULL, i);
if (ret < 0)
return ret;
/*
* read out the resulting value of GAIN
* after full-scale calibration because the next
* ad_sd_calibrate() call overwrites this via
* ad_sigma_delta_set_channel() -> ad7124_set_channel()
* ... -> ad7124_enable_channel().
*/
ret = ad_sd_read_reg(&st->sd, AD7124_GAIN(st->channels[i].cfg.cfg_slot), 3,
&st->channels[i].cfg.calibration_gain);
if (ret < 0)
return ret;
}
ret = ad_sd_calibrate(&st->sd, AD7124_MODE_CAL_INT_ZERO, i);
if (ret < 0)
return ret;
ret = ad_sd_read_reg(&st->sd, AD7124_OFFSET(st->channels[i].cfg.cfg_slot), 3,
&st->channels[i].cfg.calibration_offset);
if (ret < 0)
return ret;
dev_dbg(dev, "offset and gain for channel %d = 0x%x + 0x%x\n", i,
st->channels[i].cfg.calibration_offset,
st->channels[i].cfg.calibration_gain);
}
return 0;
}
static int ad7124_calibrate_all(struct ad7124_state *st, struct iio_dev *indio_dev)
{
int ret;
unsigned int adc_control = st->adc_control;
/*
* Calibration isn't supported at full power, so speed down a bit.
* Setting .adc_control is enough here because the control register is
* written as part of ad_sd_calibrate() -> ad_sigma_delta_set_mode().
* The resulting calibration is then also valid for high-speed, so just
* restore adc_control afterwards.
*/
if (FIELD_GET(AD7124_ADC_CTRL_PWR_MSK, adc_control) >= AD7124_FULL_POWER) {
st->adc_control &= ~AD7124_ADC_CTRL_PWR_MSK;
st->adc_control |= AD7124_ADC_CTRL_PWR(AD7124_MID_POWER);
}
ret = __ad7124_calibrate_all(st, indio_dev);
st->adc_control = adc_control;
return ret;
}
static void ad7124_reg_disable(void *r)
{
regulator_disable(r);
@ -1106,6 +1369,10 @@ static int ad7124_probe(struct spi_device *spi)
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to setup triggers\n");
ret = ad7124_calibrate_all(st, indio_dev);
if (ret)
return ret;
ret = devm_iio_device_register(&spi->dev, indio_dev);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to register iio device\n");

View File

@ -35,6 +35,7 @@
#include <linux/units.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
@ -102,6 +103,7 @@
#define AD7173_GPIO_PDSW BIT(14)
#define AD7173_GPIO_OP_EN2_3 BIT(13)
#define AD4111_GPIO_GP_OW_EN BIT(12)
#define AD7173_GPIO_MUX_IO BIT(12)
#define AD7173_GPIO_SYNC_EN BIT(11)
#define AD7173_GPIO_ERR_EN BIT(10)
@ -149,6 +151,7 @@
#define AD7173_FILTER_ODR0_MASK GENMASK(5, 0)
#define AD7173_MAX_CONFIGS 8
#define AD4111_OW_DET_THRSH_MV 300
#define AD7173_MODE_CAL_INT_ZERO 0x4 /* Internal Zero-Scale Calibration */
#define AD7173_MODE_CAL_INT_FULL 0x5 /* Internal Full-Scale Calibration */
@ -171,6 +174,7 @@ struct ad7173_device_info {
unsigned int clock;
unsigned int id;
char *name;
const struct ad_sigma_delta_info *sd_info;
bool has_current_inputs;
bool has_vincom_input;
bool has_temp;
@ -181,15 +185,23 @@ struct ad7173_device_info {
bool has_int_ref;
bool has_ref2;
bool has_internal_fs_calibration;
bool has_openwire_det;
bool higher_gpio_bits;
u8 num_gpios;
};
struct ad7173_channel_config {
/* Openwire detection threshold */
unsigned int openwire_thrsh_raw;
int openwire_comp_chan;
u8 cfg_slot;
bool live;
/* Following fields are used to compare equality. */
/*
* Following fields are used to compare equality. If you
* make adaptations in it, you most likely also have to adapt
* ad7173_find_live_config(), too.
*/
struct_group(config_props,
bool bipolar;
bool input_buf;
@ -202,11 +214,11 @@ struct ad7173_channel {
unsigned int ain;
struct ad7173_channel_config cfg;
u8 syscalib_mode;
bool openwire_det_en;
};
struct ad7173_state {
struct ad_sigma_delta sd;
struct ad_sigma_delta_info sigma_delta_info;
const struct ad7173_device_info *info;
struct ad7173_channel *channels;
struct regulator_bulk_data regulators[3];
@ -265,228 +277,6 @@ static unsigned int ad4111_current_channel_config[] = {
0x18B, /* 12:IIN3+ 11:IIN3 */
};
static const struct ad7173_device_info ad4111_device_info = {
.name = "ad4111",
.id = AD4111_ID,
.num_voltage_in_div = 8,
.num_channels = 16,
.num_configs = 8,
.num_voltage_in = 8,
.num_gpios = 2,
.higher_gpio_bits = true,
.has_temp = true,
.has_vincom_input = true,
.has_input_buf = true,
.has_current_inputs = true,
.has_int_ref = true,
.has_internal_fs_calibration = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
};
static const struct ad7173_device_info ad4112_device_info = {
.name = "ad4112",
.id = AD4112_ID,
.num_voltage_in_div = 8,
.num_channels = 16,
.num_configs = 8,
.num_voltage_in = 8,
.num_gpios = 2,
.higher_gpio_bits = true,
.has_vincom_input = true,
.has_temp = true,
.has_input_buf = true,
.has_current_inputs = true,
.has_int_ref = true,
.has_internal_fs_calibration = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
};
static const struct ad7173_device_info ad4113_device_info = {
.name = "ad4113",
.id = AD4113_ID,
.num_voltage_in_div = 8,
.num_channels = 16,
.num_configs = 8,
.num_voltage_in = 8,
.num_gpios = 2,
.data_reg_only_16bit = true,
.higher_gpio_bits = true,
.has_vincom_input = true,
.has_input_buf = true,
.has_int_ref = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
};
static const struct ad7173_device_info ad4114_device_info = {
.name = "ad4114",
.id = AD4114_ID,
.num_voltage_in_div = 16,
.num_channels = 16,
.num_configs = 8,
.num_voltage_in = 16,
.num_gpios = 4,
.has_vincom_input = true,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_internal_fs_calibration = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
};
static const struct ad7173_device_info ad4115_device_info = {
.name = "ad4115",
.id = AD4115_ID,
.num_voltage_in_div = 16,
.num_channels = 16,
.num_configs = 8,
.num_voltage_in = 16,
.num_gpios = 4,
.has_vincom_input = true,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_internal_fs_calibration = true,
.clock = 8 * HZ_PER_MHZ,
.sinc5_data_rates = ad4115_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad4115_sinc5_data_rates),
};
static const struct ad7173_device_info ad4116_device_info = {
.name = "ad4116",
.id = AD4116_ID,
.num_voltage_in_div = 11,
.num_channels = 16,
.num_configs = 8,
.num_voltage_in = 16,
.num_gpios = 4,
.has_vincom_input = true,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_internal_fs_calibration = true,
.clock = 4 * HZ_PER_MHZ,
.sinc5_data_rates = ad4116_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad4116_sinc5_data_rates),
};
static const struct ad7173_device_info ad7172_2_device_info = {
.name = "ad7172-2",
.id = AD7172_2_ID,
.num_voltage_in = 5,
.num_channels = 4,
.num_configs = 4,
.num_gpios = 2,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_pow_supply_monitoring = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
};
static const struct ad7173_device_info ad7172_4_device_info = {
.name = "ad7172-4",
.id = AD7172_4_ID,
.num_voltage_in = 9,
.num_channels = 8,
.num_configs = 8,
.num_gpios = 4,
.has_input_buf = true,
.has_ref2 = true,
.has_pow_supply_monitoring = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
};
static const struct ad7173_device_info ad7173_8_device_info = {
.name = "ad7173-8",
.id = AD7173_ID,
.num_voltage_in = 17,
.num_channels = 16,
.num_configs = 8,
.num_gpios = 4,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_ref2 = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
};
static const struct ad7173_device_info ad7175_2_device_info = {
.name = "ad7175-2",
.id = AD7175_2_ID,
.num_voltage_in = 5,
.num_channels = 4,
.num_configs = 4,
.num_gpios = 2,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_pow_supply_monitoring = true,
.clock = 16 * HZ_PER_MHZ,
.sinc5_data_rates = ad7175_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
};
static const struct ad7173_device_info ad7175_8_device_info = {
.name = "ad7175-8",
.id = AD7175_8_ID,
.num_voltage_in = 17,
.num_channels = 16,
.num_configs = 8,
.num_gpios = 4,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_ref2 = true,
.has_pow_supply_monitoring = true,
.clock = 16 * HZ_PER_MHZ,
.sinc5_data_rates = ad7175_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
};
static const struct ad7173_device_info ad7176_2_device_info = {
.name = "ad7176-2",
.id = AD7176_ID,
.num_voltage_in = 5,
.num_channels = 4,
.num_configs = 4,
.num_gpios = 2,
.has_int_ref = true,
.clock = 16 * HZ_PER_MHZ,
.sinc5_data_rates = ad7175_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
};
static const struct ad7173_device_info ad7177_2_device_info = {
.name = "ad7177-2",
.id = AD7177_ID,
.num_voltage_in = 5,
.num_channels = 4,
.num_configs = 4,
.num_gpios = 2,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_pow_supply_monitoring = true,
.clock = 16 * HZ_PER_MHZ,
.odr_start_value = AD7177_ODR_START_VALUE,
.sinc5_data_rates = ad7175_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
};
static const char *const ad7173_ref_sel_str[] = {
[AD7173_SETUP_REF_SEL_EXT_REF] = "vref",
[AD7173_SETUP_REF_SEL_EXT_REF2] = "vref2",
@ -559,6 +349,9 @@ static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev,
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
mode = st->channels[chan->channel].syscalib_mode;
if (sys_calib) {
if (mode == AD7173_SYSCALIB_ZERO_SCALE)
@ -569,6 +362,8 @@ static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev,
chan->address);
}
iio_device_release_direct(indio_dev);
return ret ? : len;
}
@ -616,6 +411,76 @@ static int ad7173_calibrate_all(struct ad7173_state *st, struct iio_dev *indio_d
return 0;
}
/*
* Associative array of channel pairs for open wire detection
* The array is indexed by ain and gives the associated channel pair
* to perform the open wire detection with
* the channel pair [0] is for non differential and pair [1]
* is for differential inputs
*/
static int openwire_ain_to_channel_pair[][2][2] = {
/* AIN Single Differential */
[0] = { { 0, 15 }, { 1, 2 } },
[1] = { { 1, 2 }, { 2, 1 } },
[2] = { { 3, 4 }, { 5, 6 } },
[3] = { { 5, 6 }, { 6, 5 } },
[4] = { { 7, 8 }, { 9, 10 } },
[5] = { { 9, 10 }, { 10, 9 } },
[6] = { { 11, 12 }, { 13, 14 } },
[7] = { { 13, 14 }, { 14, 13 } },
};
/*
* Openwire detection on ad4111 works by running the same input measurement
* on two different channels and compare if the difference between the two
* measurements exceeds a certain value (typical 300mV)
*/
static int ad4111_openwire_event(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct ad7173_state *st = iio_priv(indio_dev);
struct ad7173_channel *adchan = &st->channels[chan->address];
struct ad7173_channel_config *cfg = &adchan->cfg;
int ret, val1, val2;
ret = regmap_set_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO,
AD4111_GPIO_GP_OW_EN);
if (ret)
return ret;
adchan->cfg.openwire_comp_chan =
openwire_ain_to_channel_pair[chan->channel][chan->differential][0];
ret = ad_sigma_delta_single_conversion(indio_dev, chan, &val1);
if (ret < 0) {
dev_err(&indio_dev->dev,
"Error running ad_sigma_delta single conversion: %d", ret);
goto out;
}
adchan->cfg.openwire_comp_chan =
openwire_ain_to_channel_pair[chan->channel][chan->differential][1];
ret = ad_sigma_delta_single_conversion(indio_dev, chan, &val2);
if (ret < 0) {
dev_err(&indio_dev->dev,
"Error running ad_sigma_delta single conversion: %d", ret);
goto out;
}
if (abs(val1 - val2) > cfg->openwire_thrsh_raw)
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, chan->address,
IIO_EV_TYPE_FAULT, IIO_EV_DIR_FAULT_OPENWIRE),
iio_get_time_ns(indio_dev));
out:
adchan->cfg.openwire_comp_chan = -1;
regmap_clear_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO,
AD4111_GPIO_GP_OW_EN);
return ret;
}
static int ad7173_mask_xlate(struct gpio_regmap *gpio, unsigned int base,
unsigned int offset, unsigned int *reg,
unsigned int *mask)
@ -712,15 +577,28 @@ static struct ad7173_channel_config *
ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *cfg)
{
struct ad7173_channel_config *cfg_aux;
ptrdiff_t cmp_size;
int i;
cmp_size = sizeof_field(struct ad7173_channel_config, config_props);
/*
* This is just to make sure that the comparison is adapted after
* struct ad7173_channel_config was changed.
*/
static_assert(sizeof_field(struct ad7173_channel_config, config_props) ==
sizeof(struct {
bool bipolar;
bool input_buf;
u8 odr;
u8 ref_sel;
}));
for (i = 0; i < st->num_channels; i++) {
cfg_aux = &st->channels[i].cfg;
if (cfg_aux->live &&
!memcmp(&cfg->config_props, &cfg_aux->config_props, cmp_size))
cfg->bipolar == cfg_aux->bipolar &&
cfg->input_buf == cfg_aux->input_buf &&
cfg->odr == cfg_aux->odr &&
cfg->ref_sel == cfg_aux->ref_sel)
return cfg_aux;
}
return NULL;
@ -813,6 +691,9 @@ static int ad7173_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
FIELD_PREP(AD7173_CH_SETUP_SEL_MASK, st->channels[channel].cfg.cfg_slot) |
st->channels[channel].ain;
if (st->channels[channel].cfg.openwire_comp_chan >= 0)
channel = st->channels[channel].cfg.openwire_comp_chan;
return ad_sd_write_reg(&st->sd, AD7173_REG_CH(channel), 2, val);
}
@ -861,21 +742,280 @@ static int ad7173_disable_all(struct ad_sigma_delta *sd)
static int ad7173_disable_one(struct ad_sigma_delta *sd, unsigned int chan)
{
struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd);
if (st->channels[chan].cfg.openwire_comp_chan >= 0)
chan = st->channels[chan].cfg.openwire_comp_chan;
return ad_sd_write_reg(sd, AD7173_REG_CH(chan), 2, 0);
}
static const struct ad_sigma_delta_info ad7173_sigma_delta_info = {
static const struct ad_sigma_delta_info ad7173_sigma_delta_info_4_slots = {
.set_channel = ad7173_set_channel,
.append_status = ad7173_append_status,
.disable_all = ad7173_disable_all,
.disable_one = ad7173_disable_one,
.set_mode = ad7173_set_mode,
.has_registers = true,
.has_named_irqs = true,
.addr_shift = 0,
.read_mask = BIT(6),
.status_ch_mask = GENMASK(3, 0),
.data_reg = AD7173_REG_DATA,
.num_resetclks = 64,
.num_slots = 4,
};
static const struct ad_sigma_delta_info ad7173_sigma_delta_info_8_slots = {
.set_channel = ad7173_set_channel,
.append_status = ad7173_append_status,
.disable_all = ad7173_disable_all,
.disable_one = ad7173_disable_one,
.set_mode = ad7173_set_mode,
.has_registers = true,
.has_named_irqs = true,
.addr_shift = 0,
.read_mask = BIT(6),
.status_ch_mask = GENMASK(3, 0),
.data_reg = AD7173_REG_DATA,
.num_resetclks = 64,
.num_slots = 8,
};
static const struct ad7173_device_info ad4111_device_info = {
.name = "ad4111",
.id = AD4111_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.num_voltage_in_div = 8,
.num_channels = 16,
.num_configs = 8,
.num_voltage_in = 8,
.num_gpios = 2,
.higher_gpio_bits = true,
.has_temp = true,
.has_vincom_input = true,
.has_input_buf = true,
.has_current_inputs = true,
.has_int_ref = true,
.has_internal_fs_calibration = true,
.has_openwire_det = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
};
static const struct ad7173_device_info ad4112_device_info = {
.name = "ad4112",
.id = AD4112_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.num_voltage_in_div = 8,
.num_channels = 16,
.num_configs = 8,
.num_voltage_in = 8,
.num_gpios = 2,
.higher_gpio_bits = true,
.has_vincom_input = true,
.has_temp = true,
.has_input_buf = true,
.has_current_inputs = true,
.has_int_ref = true,
.has_internal_fs_calibration = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
};
static const struct ad7173_device_info ad4113_device_info = {
.name = "ad4113",
.id = AD4113_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.num_voltage_in_div = 8,
.num_channels = 16,
.num_configs = 8,
.num_voltage_in = 8,
.num_gpios = 2,
.data_reg_only_16bit = true,
.higher_gpio_bits = true,
.has_vincom_input = true,
.has_input_buf = true,
.has_int_ref = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
};
static const struct ad7173_device_info ad4114_device_info = {
.name = "ad4114",
.id = AD4114_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.num_voltage_in_div = 16,
.num_channels = 16,
.num_configs = 8,
.num_voltage_in = 16,
.num_gpios = 4,
.has_vincom_input = true,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_internal_fs_calibration = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
};
static const struct ad7173_device_info ad4115_device_info = {
.name = "ad4115",
.id = AD4115_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.num_voltage_in_div = 16,
.num_channels = 16,
.num_configs = 8,
.num_voltage_in = 16,
.num_gpios = 4,
.has_vincom_input = true,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_internal_fs_calibration = true,
.clock = 8 * HZ_PER_MHZ,
.sinc5_data_rates = ad4115_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad4115_sinc5_data_rates),
};
static const struct ad7173_device_info ad4116_device_info = {
.name = "ad4116",
.id = AD4116_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.num_voltage_in_div = 11,
.num_channels = 16,
.num_configs = 8,
.num_voltage_in = 16,
.num_gpios = 4,
.has_vincom_input = true,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_internal_fs_calibration = true,
.clock = 4 * HZ_PER_MHZ,
.sinc5_data_rates = ad4116_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad4116_sinc5_data_rates),
};
static const struct ad7173_device_info ad7172_2_device_info = {
.name = "ad7172-2",
.id = AD7172_2_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.num_voltage_in = 5,
.num_channels = 4,
.num_configs = 4,
.num_gpios = 2,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_pow_supply_monitoring = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
};
static const struct ad7173_device_info ad7172_4_device_info = {
.name = "ad7172-4",
.id = AD7172_4_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.num_voltage_in = 9,
.num_channels = 8,
.num_configs = 8,
.num_gpios = 4,
.has_input_buf = true,
.has_ref2 = true,
.has_pow_supply_monitoring = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
};
static const struct ad7173_device_info ad7173_8_device_info = {
.name = "ad7173-8",
.id = AD7173_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.num_voltage_in = 17,
.num_channels = 16,
.num_configs = 8,
.num_gpios = 4,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_ref2 = true,
.clock = 2 * HZ_PER_MHZ,
.sinc5_data_rates = ad7173_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates),
};
static const struct ad7173_device_info ad7175_2_device_info = {
.name = "ad7175-2",
.id = AD7175_2_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.num_voltage_in = 5,
.num_channels = 4,
.num_configs = 4,
.num_gpios = 2,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_pow_supply_monitoring = true,
.clock = 16 * HZ_PER_MHZ,
.sinc5_data_rates = ad7175_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
};
static const struct ad7173_device_info ad7175_8_device_info = {
.name = "ad7175-8",
.id = AD7175_8_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.num_voltage_in = 17,
.num_channels = 16,
.num_configs = 8,
.num_gpios = 4,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_ref2 = true,
.has_pow_supply_monitoring = true,
.clock = 16 * HZ_PER_MHZ,
.sinc5_data_rates = ad7175_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
};
static const struct ad7173_device_info ad7176_2_device_info = {
.name = "ad7176-2",
.id = AD7176_ID,
.sd_info = &ad7173_sigma_delta_info_4_slots,
.num_voltage_in = 5,
.num_channels = 4,
.num_configs = 4,
.num_gpios = 2,
.has_int_ref = true,
.clock = 16 * HZ_PER_MHZ,
.sinc5_data_rates = ad7175_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
};
static const struct ad7173_device_info ad7177_2_device_info = {
.name = "ad7177-2",
.id = AD7177_ID,
.sd_info = &ad7173_sigma_delta_info_4_slots,
.num_voltage_in = 5,
.num_channels = 4,
.num_configs = 4,
.num_gpios = 2,
.has_temp = true,
.has_input_buf = true,
.has_int_ref = true,
.has_pow_supply_monitoring = true,
.clock = 16 * HZ_PER_MHZ,
.odr_start_value = AD7177_ODR_START_VALUE,
.sinc5_data_rates = ad7175_sinc5_data_rates,
.num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates),
};
static int ad7173_setup(struct iio_dev *indio_dev)
@ -969,6 +1109,12 @@ static int ad7173_read_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
if (ch->openwire_det_en) {
ret = ad4111_openwire_event(indio_dev, chan);
if (ret < 0)
return ret;
}
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
@ -1033,11 +1179,10 @@ static int ad7173_write_raw(struct iio_dev *indio_dev,
struct ad7173_state *st = iio_priv(indio_dev);
struct ad7173_channel_config *cfg;
unsigned int freq, i;
int ret;
int ret = 0;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
switch (info) {
/*
@ -1071,7 +1216,7 @@ static int ad7173_write_raw(struct iio_dev *indio_dev,
break;
}
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return ret;
}
@ -1113,12 +1258,57 @@ static int ad7173_debug_reg_access(struct iio_dev *indio_dev, unsigned int reg,
return ad_sd_write_reg(&st->sd, reg, reg_size, writeval);
}
static int ad7173_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
bool state)
{
struct ad7173_state *st = iio_priv(indio_dev);
struct ad7173_channel *adchan = &st->channels[chan->address];
switch (type) {
case IIO_EV_TYPE_FAULT:
adchan->openwire_det_en = state;
return 0;
default:
return -EINVAL;
}
}
static int ad7173_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 ad7173_state *st = iio_priv(indio_dev);
struct ad7173_channel *adchan = &st->channels[chan->address];
switch (type) {
case IIO_EV_TYPE_FAULT:
return adchan->openwire_det_en;
default:
return -EINVAL;
}
}
static const struct iio_event_spec ad4111_events[] = {
{
.type = IIO_EV_TYPE_FAULT,
.dir = IIO_EV_DIR_FAULT_OPENWIRE,
.mask_separate = BIT(IIO_EV_INFO_VALUE),
.mask_shared_by_all = BIT(IIO_EV_INFO_ENABLE),
},
};
static const struct iio_info ad7173_info = {
.read_raw = &ad7173_read_raw,
.write_raw = &ad7173_write_raw,
.debugfs_reg_access = &ad7173_debug_reg_access,
.validate_trigger = ad_sd_validate_trigger,
.update_scan_mode = ad7173_update_scan_mode,
.write_event_config = ad7173_write_event_config,
.read_event_config = ad7173_read_event_config,
};
static const struct iio_scan_type ad4113_scan_type = {
@ -1322,6 +1512,37 @@ static int ad7173_validate_reference(struct ad7173_state *st, int ref_sel)
return 0;
}
static int ad7173_validate_openwire_ain_inputs(struct ad7173_state *st,
bool differential,
unsigned int ain0,
unsigned int ain1)
{
/*
* If the channel is configured as differential,
* the ad4111 requires specific ains to be used together
*/
if (differential)
return (ain0 % 2) ? (ain0 - 1) == ain1 : (ain0 + 1) == ain1;
return ain1 == AD4111_VINCOM_INPUT;
}
static unsigned int ad7173_calc_openwire_thrsh_raw(struct ad7173_state *st,
struct iio_chan_spec *chan,
struct ad7173_channel *chan_st_priv,
unsigned int thrsh_mv) {
unsigned int thrsh_raw;
thrsh_raw =
BIT(chan->scan_type.realbits - !!(chan_st_priv->cfg.bipolar))
* thrsh_mv
/ ad7173_get_ref_voltage_milli(st, chan_st_priv->cfg.ref_sel);
if (chan->channel < st->info->num_voltage_in_div)
thrsh_raw /= AD4111_DIVIDER_RATIO;
return thrsh_raw;
}
static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
{
struct ad7173_channel *chans_st_arr, *chan_st_priv;
@ -1369,6 +1590,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
chan_st_priv->cfg.bipolar = false;
chan_st_priv->cfg.input_buf = st->info->has_input_buf;
chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF;
chan_st_priv->cfg.openwire_comp_chan = -1;
st->adc_mode |= AD7173_ADC_MODE_REF_EN;
if (st->info->data_reg_only_16bit)
chan_arr[chan_index].scan_type = ad4113_scan_type;
@ -1435,6 +1657,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
chan->channel = ain[0];
chan_st_priv->cfg.input_buf = st->info->has_input_buf;
chan_st_priv->cfg.odr = 0;
chan_st_priv->cfg.openwire_comp_chan = -1;
chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar");
if (chan_st_priv->cfg.bipolar)
@ -1449,6 +1672,14 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
chan_st_priv->cfg.input_buf = st->info->has_input_buf;
chan->channel2 = ain[1];
chan_st_priv->ain = AD7173_CH_ADDRESS(ain[0], ain[1]);
if (st->info->has_openwire_det &&
ad7173_validate_openwire_ain_inputs(st, chan->differential, ain[0], ain[1])) {
chan->event_spec = ad4111_events;
chan->num_event_specs = ARRAY_SIZE(ad4111_events);
chan_st_priv->cfg.openwire_thrsh_raw =
ad7173_calc_openwire_thrsh_raw(st, chan, chan_st_priv,
AD4111_OW_DET_THRSH_MV);
}
}
if (st->info->data_reg_only_16bit)
@ -1515,12 +1746,6 @@ static int ad7173_fw_parse_device_config(struct iio_dev *indio_dev)
return ret;
}
ret = fwnode_irq_get_byname(dev_fwnode(dev), "rdy");
if (ret < 0)
return dev_err_probe(dev, ret, "Interrupt 'rdy' is required\n");
st->sigma_delta_info.irq_line = ret;
return ad7173_fw_parse_channel_config(indio_dev);
}
@ -1552,9 +1777,7 @@ static int ad7173_probe(struct spi_device *spi)
spi->mode = SPI_MODE_3;
spi_setup(spi);
st->sigma_delta_info = ad7173_sigma_delta_info;
st->sigma_delta_info.num_slots = st->info->num_configs;
ret = ad_sd_init(&st->sd, indio_dev, spi, &st->sigma_delta_info);
ret = ad_sd_init(&st->sd, indio_dev, spi, st->info->sd_info);
if (ret)
return ret;

554
drivers/iio/adc/ad7191.c Normal file
View File

@ -0,0 +1,554 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* AD7191 ADC driver
*
* Copyright 2025 Analog Devices Inc.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include <linux/units.h>
#include <linux/iio/adc/ad_sigma_delta.h>
#include <linux/iio/iio.h>
#define ad_sigma_delta_to_ad7191(sigmad) \
container_of((sigmad), struct ad7191_state, sd)
#define AD7191_TEMP_CODES_PER_DEGREE 2815
#define AD7191_CHAN_MASK BIT(0)
#define AD7191_TEMP_MASK BIT(1)
enum ad7191_channel {
AD7191_CH_AIN1_AIN2,
AD7191_CH_AIN3_AIN4,
AD7191_CH_TEMP,
};
/*
* NOTE:
* The AD7191 features a dual-use data out ready DOUT/RDY output.
* In order to avoid contentions on the SPI bus, it's therefore necessary
* to use SPI bus locking.
*
* The DOUT/RDY output must also be wired to an interrupt-capable GPIO.
*
* The SPI controller's chip select must be connected to the PDOWN pin
* of the ADC. When CS (PDOWN) is high, it powers down the device and
* resets the internal circuitry.
*/
struct ad7191_state {
struct ad_sigma_delta sd;
struct mutex lock; /* Protect device state */
struct gpio_descs *odr_gpios;
struct gpio_descs *pga_gpios;
struct gpio_desc *temp_gpio;
struct gpio_desc *chan_gpio;
u16 int_vref_mv;
const u32 (*scale_avail)[2];
size_t scale_avail_size;
u32 scale_index;
const u32 *samp_freq_avail;
size_t samp_freq_avail_size;
u32 samp_freq_index;
struct clk *mclk;
};
static int ad7191_set_channel(struct ad_sigma_delta *sd, unsigned int address)
{
struct ad7191_state *st = ad_sigma_delta_to_ad7191(sd);
u8 temp_gpio_val, chan_gpio_val;
if (!FIELD_FIT(AD7191_CHAN_MASK | AD7191_TEMP_MASK, address))
return -EINVAL;
chan_gpio_val = FIELD_GET(AD7191_CHAN_MASK, address);
temp_gpio_val = FIELD_GET(AD7191_TEMP_MASK, address);
gpiod_set_value(st->chan_gpio, chan_gpio_val);
gpiod_set_value(st->temp_gpio, temp_gpio_val);
return 0;
}
static int ad7191_set_cs(struct ad_sigma_delta *sigma_delta, int assert)
{
struct spi_transfer t = {
.len = 0,
.cs_change = assert,
};
struct spi_message m;
spi_message_init_with_transfers(&m, &t, 1);
return spi_sync_locked(sigma_delta->spi, &m);
}
static int ad7191_set_mode(struct ad_sigma_delta *sd,
enum ad_sigma_delta_mode mode)
{
struct ad7191_state *st = ad_sigma_delta_to_ad7191(sd);
switch (mode) {
case AD_SD_MODE_CONTINUOUS:
case AD_SD_MODE_SINGLE:
return ad7191_set_cs(&st->sd, 1);
case AD_SD_MODE_IDLE:
return ad7191_set_cs(&st->sd, 0);
default:
return -EINVAL;
}
}
static const struct ad_sigma_delta_info ad7191_sigma_delta_info = {
.set_channel = ad7191_set_channel,
.set_mode = ad7191_set_mode,
.has_registers = false,
};
static int ad7191_init_regulators(struct iio_dev *indio_dev)
{
struct ad7191_state *st = iio_priv(indio_dev);
struct device *dev = &st->sd.spi->dev;
int ret;
ret = devm_regulator_get_enable(dev, "avdd");
if (ret)
return dev_err_probe(dev, ret, "Failed to enable specified AVdd supply\n");
ret = devm_regulator_get_enable(dev, "dvdd");
if (ret)
return dev_err_probe(dev, ret, "Failed to enable specified DVdd supply\n");
ret = devm_regulator_get_enable_read_voltage(dev, "vref");
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to get Vref voltage\n");
st->int_vref_mv = ret / 1000;
return 0;
}
static int ad7191_config_setup(struct iio_dev *indio_dev)
{
struct ad7191_state *st = iio_priv(indio_dev);
struct device *dev = &st->sd.spi->dev;
/* Sampling frequencies in Hz, see Table 5 */
static const u32 samp_freq[4] = { 120, 60, 50, 10 };
/* Gain options, see Table 7 */
const u32 gain[4] = { 1, 8, 64, 128 };
static u32 scale_buffer[4][2];
int odr_value, odr_index = 0, pga_value, pga_index = 0, i, ret;
u64 scale_uv;
st->samp_freq_index = 0;
st->scale_index = 0;
ret = device_property_read_u32(dev, "adi,odr-value", &odr_value);
if (ret && ret != -EINVAL)
return dev_err_probe(dev, ret, "Failed to get odr value.\n");
if (ret == -EINVAL) {
st->odr_gpios = devm_gpiod_get_array(dev, "odr", GPIOD_OUT_LOW);
if (IS_ERR(st->odr_gpios))
return dev_err_probe(dev, PTR_ERR(st->odr_gpios),
"Failed to get odr gpios.\n");
if (st->odr_gpios->ndescs != 2)
return dev_err_probe(dev, -EINVAL, "Expected 2 odr gpio pins.\n");
st->samp_freq_avail = samp_freq;
st->samp_freq_avail_size = ARRAY_SIZE(samp_freq);
} else {
for (i = 0; i < ARRAY_SIZE(samp_freq); i++) {
if (odr_value != samp_freq[i])
continue;
odr_index = i;
break;
}
st->samp_freq_avail = &samp_freq[odr_index];
st->samp_freq_avail_size = 1;
st->odr_gpios = NULL;
}
mutex_lock(&st->lock);
for (i = 0; i < ARRAY_SIZE(scale_buffer); i++) {
scale_uv = ((u64)st->int_vref_mv * NANO) >>
(indio_dev->channels[0].scan_type.realbits - 1);
do_div(scale_uv, gain[i]);
scale_buffer[i][1] = do_div(scale_uv, NANO);
scale_buffer[i][0] = scale_uv;
}
mutex_unlock(&st->lock);
ret = device_property_read_u32(dev, "adi,pga-value", &pga_value);
if (ret && ret != -EINVAL)
return dev_err_probe(dev, ret, "Failed to get pga value.\n");
if (ret == -EINVAL) {
st->pga_gpios = devm_gpiod_get_array(dev, "pga", GPIOD_OUT_LOW);
if (IS_ERR(st->pga_gpios))
return dev_err_probe(dev, PTR_ERR(st->pga_gpios),
"Failed to get pga gpios.\n");
if (st->pga_gpios->ndescs != 2)
return dev_err_probe(dev, -EINVAL, "Expected 2 pga gpio pins.\n");
st->scale_avail = scale_buffer;
st->scale_avail_size = ARRAY_SIZE(scale_buffer);
} else {
for (i = 0; i < ARRAY_SIZE(gain); i++) {
if (pga_value != gain[i])
continue;
pga_index = i;
break;
}
st->scale_avail = &scale_buffer[pga_index];
st->scale_avail_size = 1;
st->pga_gpios = NULL;
}
st->temp_gpio = devm_gpiod_get(dev, "temp", GPIOD_OUT_LOW);
if (IS_ERR(st->temp_gpio))
return dev_err_probe(dev, PTR_ERR(st->temp_gpio),
"Failed to get temp gpio.\n");
st->chan_gpio = devm_gpiod_get(dev, "chan", GPIOD_OUT_LOW);
if (IS_ERR(st->chan_gpio))
return dev_err_probe(dev, PTR_ERR(st->chan_gpio),
"Failed to get chan gpio.\n");
return 0;
}
static int ad7191_clock_setup(struct ad7191_state *st)
{
struct device *dev = &st->sd.spi->dev;
st->mclk = devm_clk_get_optional_enabled(dev, "mclk");
if (IS_ERR(st->mclk))
return dev_err_probe(dev, PTR_ERR(st->mclk),
"Failed to get mclk.\n");
return 0;
}
static int ad7191_setup(struct iio_dev *indio_dev)
{
struct ad7191_state *st = iio_priv(indio_dev);
int ret;
ret = ad7191_init_regulators(indio_dev);
if (ret)
return ret;
ret = ad7191_config_setup(indio_dev);
if (ret)
return ret;
return ad7191_clock_setup(st);
}
static int ad7191_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long m)
{
struct ad7191_state *st = iio_priv(indio_dev);
switch (m) {
case IIO_CHAN_INFO_RAW:
return ad_sigma_delta_single_conversion(indio_dev, chan, val);
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_VOLTAGE: {
guard(mutex)(&st->lock);
*val = st->scale_avail[st->scale_index][0];
*val2 = st->scale_avail[st->scale_index][1];
return IIO_VAL_INT_PLUS_NANO;
}
case IIO_TEMP:
*val = 0;
*val2 = NANO / AD7191_TEMP_CODES_PER_DEGREE;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_OFFSET:
*val = -(1 << (chan->scan_type.realbits - 1));
switch (chan->type) {
case IIO_VOLTAGE:
return IIO_VAL_INT;
case IIO_TEMP:
*val -= 273 * AD7191_TEMP_CODES_PER_DEGREE;
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SAMP_FREQ:
*val = st->samp_freq_avail[st->samp_freq_index];
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int ad7191_set_gain(struct ad7191_state *st, int gain_index)
{
DECLARE_BITMAP(bitmap, 2) = { };
st->scale_index = gain_index;
bitmap_write(bitmap, gain_index, 0, 2);
return gpiod_multi_set_value_cansleep(st->pga_gpios, bitmap);
}
static int ad7191_set_samp_freq(struct ad7191_state *st, int samp_freq_index)
{
DECLARE_BITMAP(bitmap, 2) = {};
st->samp_freq_index = samp_freq_index;
bitmap_write(bitmap, samp_freq_index, 0, 2);
return gpiod_multi_set_value_cansleep(st->odr_gpios, bitmap);
}
static int __ad7191_write_raw(struct ad7191_state *st,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
int i;
switch (mask) {
case IIO_CHAN_INFO_SCALE: {
if (!st->pga_gpios)
return -EPERM;
guard(mutex)(&st->lock);
for (i = 0; i < st->scale_avail_size; i++) {
if (val2 == st->scale_avail[i][1])
return ad7191_set_gain(st, i);
}
return -EINVAL;
}
case IIO_CHAN_INFO_SAMP_FREQ: {
if (!st->odr_gpios)
return -EPERM;
guard(mutex)(&st->lock);
for (i = 0; i < st->samp_freq_avail_size; i++) {
if (val == st->samp_freq_avail[i])
return ad7191_set_samp_freq(st, i);
}
return -EINVAL;
}
default:
return -EINVAL;
}
}
static int ad7191_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2,
long mask)
{
struct ad7191_state *st = iio_priv(indio_dev);
int ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = __ad7191_write_raw(st, chan, val, val2, mask);
iio_device_release_direct(indio_dev);
return ret;
}
static int ad7191_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_SAMP_FREQ:
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int ad7191_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, const int **vals,
int *type, int *length, long mask)
{
struct ad7191_state *st = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
*vals = (int *)st->scale_avail;
*type = IIO_VAL_INT_PLUS_NANO;
*length = st->scale_avail_size * 2;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SAMP_FREQ:
*vals = (int *)st->samp_freq_avail;
*type = IIO_VAL_INT;
*length = st->samp_freq_avail_size;
return IIO_AVAIL_LIST;
}
return -EINVAL;
}
static const struct iio_info ad7191_info = {
.read_raw = ad7191_read_raw,
.write_raw = ad7191_write_raw,
.write_raw_get_fmt = ad7191_write_raw_get_fmt,
.read_avail = ad7191_read_avail,
.validate_trigger = ad_sd_validate_trigger,
};
static const struct iio_chan_spec ad7191_channels[] = {
{
.type = IIO_TEMP,
.address = AD7191_CH_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.scan_type = {
.sign = 'u',
.realbits = 24,
.storagebits = 32,
.endianness = IIO_BE,
},
},
{
.type = IIO_VOLTAGE,
.differential = 1,
.indexed = 1,
.channel = 1,
.channel2 = 2,
.address = AD7191_CH_AIN1_AIN2,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),
.scan_index = 1,
.scan_type = {
.sign = 'u',
.realbits = 24,
.storagebits = 32,
.endianness = IIO_BE,
},
},
{
.type = IIO_VOLTAGE,
.differential = 1,
.indexed = 1,
.channel = 3,
.channel2 = 4,
.address = AD7191_CH_AIN3_AIN4,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),
.scan_index = 2,
.scan_type = {
.sign = 'u',
.realbits = 24,
.storagebits = 32,
.endianness = IIO_BE,
},
},
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static int ad7191_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct ad7191_state *st;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
ret = devm_mutex_init(dev, &st->lock);
if (ret)
return ret;
indio_dev->name = "ad7191";
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = ad7191_channels;
indio_dev->num_channels = ARRAY_SIZE(ad7191_channels);
indio_dev->info = &ad7191_info;
ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7191_sigma_delta_info);
if (ret)
return ret;
ret = devm_ad_sd_setup_buffer_and_trigger(dev, indio_dev);
if (ret)
return ret;
ret = ad7191_setup(indio_dev);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct of_device_id ad7191_of_match[] = {
{ .compatible = "adi,ad7191", },
{ }
};
MODULE_DEVICE_TABLE(of, ad7191_of_match);
static const struct spi_device_id ad7191_id_table[] = {
{ "ad7191" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7191_id_table);
static struct spi_driver ad7191_driver = {
.driver = {
.name = "ad7191",
.of_match_table = ad7191_of_match,
},
.probe = ad7191_probe,
.id_table = ad7191_id_table,
};
module_spi_driver(ad7191_driver);
MODULE_AUTHOR("Alisa-Dariana Roman <alisa.roman@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7191 ADC");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("IIO_AD_SIGMA_DELTA");

View File

@ -7,6 +7,7 @@
#include <linux/interrupt.h>
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/device.h>
@ -256,6 +257,9 @@ static ssize_t ad7192_write_syscalib(struct iio_dev *indio_dev,
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
temp = st->syscalib_mode[chan->channel];
if (sys_calib) {
if (temp == AD7192_SYSCALIB_ZERO_SCALE)
@ -266,6 +270,8 @@ static ssize_t ad7192_write_syscalib(struct iio_dev *indio_dev,
chan->address);
}
iio_device_release_direct(indio_dev);
return ret ? ret : len;
}
@ -693,9 +699,8 @@ static ssize_t ad7192_set(struct device *dev,
if (ret < 0)
return ret;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
switch ((u32)this_attr->address) {
case AD7192_REG_GPOCON:
@ -718,7 +723,7 @@ static ssize_t ad7192_set(struct device *dev,
ret = -EINVAL;
}
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return ret ? ret : len;
}
@ -945,82 +950,83 @@ static int ad7192_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
static int __ad7192_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long mask)
{
struct ad7192_state *st = iio_priv(indio_dev);
int i, div;
unsigned int tmp;
guard(mutex)(&st->lock);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) {
if (val2 != st->scale_avail[i][1])
continue;
tmp = st->conf;
st->conf &= ~AD7192_CONF_GAIN_MASK;
st->conf |= FIELD_PREP(AD7192_CONF_GAIN_MASK, i);
if (tmp == st->conf)
return 0;
ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf);
ad7192_calibrate_all(st);
return 0;
}
return -EINVAL;
case IIO_CHAN_INFO_SAMP_FREQ:
if (!val)
return -EINVAL;
div = st->fclk / (val * ad7192_get_f_order(st) * 1024);
if (div < 1 || div > 1023)
return -EINVAL;
st->mode &= ~AD7192_MODE_RATE_MASK;
st->mode |= FIELD_PREP(AD7192_MODE_RATE_MASK, div);
ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
ad7192_update_filter_freq_avail(st);
return 0;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
return ad7192_set_3db_filter_freq(st, val, val2 / 1000);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
for (i = 0; i < ARRAY_SIZE(st->oversampling_ratio_avail); i++) {
if (val != st->oversampling_ratio_avail[i])
continue;
tmp = st->mode;
st->mode &= ~AD7192_MODE_AVG_MASK;
st->mode |= FIELD_PREP(AD7192_MODE_AVG_MASK, i);
if (tmp == st->mode)
return 0;
ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
ad7192_update_filter_freq_avail(st);
return 0;
}
return -EINVAL;
default:
return -EINVAL;
}
}
static int ad7192_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long mask)
{
struct ad7192_state *st = iio_priv(indio_dev);
int ret, i, div;
unsigned int tmp;
int ret;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
mutex_lock(&st->lock);
ret = __ad7192_write_raw(indio_dev, chan, val, val2, mask);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
if (val2 == st->scale_avail[i][1]) {
ret = 0;
tmp = st->conf;
st->conf &= ~AD7192_CONF_GAIN_MASK;
st->conf |= FIELD_PREP(AD7192_CONF_GAIN_MASK, i);
if (tmp == st->conf)
break;
ad_sd_write_reg(&st->sd, AD7192_REG_CONF,
3, st->conf);
ad7192_calibrate_all(st);
break;
}
break;
case IIO_CHAN_INFO_SAMP_FREQ:
if (!val) {
ret = -EINVAL;
break;
}
div = st->fclk / (val * ad7192_get_f_order(st) * 1024);
if (div < 1 || div > 1023) {
ret = -EINVAL;
break;
}
st->mode &= ~AD7192_MODE_RATE_MASK;
st->mode |= FIELD_PREP(AD7192_MODE_RATE_MASK, div);
ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
ad7192_update_filter_freq_avail(st);
break;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
ret = ad7192_set_3db_filter_freq(st, val, val2 / 1000);
break;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(st->oversampling_ratio_avail); i++)
if (val == st->oversampling_ratio_avail[i]) {
ret = 0;
tmp = st->mode;
st->mode &= ~AD7192_MODE_AVG_MASK;
st->mode |= FIELD_PREP(AD7192_MODE_AVG_MASK, i);
if (tmp == st->mode)
break;
ad_sd_write_reg(&st->sd, AD7192_REG_MODE,
3, st->mode);
break;
}
ad7192_update_filter_freq_avail(st);
break;
default:
ret = -EINVAL;
}
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return ret;
}

View File

@ -153,11 +153,10 @@ static int ad7266_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7266_read_single(st, val, chan->address);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;

View File

@ -232,16 +232,15 @@ static int ad7298_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
if (chan->address == AD7298_CH_TEMP)
ret = ad7298_scan_temp(st, val);
else
ret = ad7298_scan_direct(st, chan->address);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;

File diff suppressed because it is too large Load Diff

View File

@ -138,11 +138,10 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7476_scan_direct(st);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;

View File

@ -5,6 +5,7 @@
* Copyright 2011 Analog Devices Inc.
*/
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
@ -85,6 +86,10 @@ static const unsigned int ad7606_oversampling_avail[7] = {
1, 2, 4, 8, 16, 32, 64,
};
static const unsigned int ad7606b_oversampling_avail[9] = {
1, 2, 4, 8, 16, 32, 64, 128, 256,
};
static const unsigned int ad7616_oversampling_avail[8] = {
1, 2, 4, 8, 16, 32, 64, 128,
};
@ -187,6 +192,8 @@ static int ad7608_chan_scale_setup(struct iio_dev *indio_dev,
struct iio_chan_spec *chan, int ch);
static int ad7609_chan_scale_setup(struct iio_dev *indio_dev,
struct iio_chan_spec *chan, int ch);
static int ad7616_sw_mode_setup(struct iio_dev *indio_dev);
static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev);
const struct ad7606_chip_info ad7605_4_info = {
.channels = ad7605_channels,
@ -239,6 +246,7 @@ const struct ad7606_chip_info ad7606b_info = {
.oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7606_16bit_chan_scale_setup,
.sw_setup_cb = ad7606b_sw_mode_setup,
};
EXPORT_SYMBOL_NS_GPL(ad7606b_info, "IIO_AD7606");
@ -250,6 +258,7 @@ const struct ad7606_chip_info ad7606c_16_info = {
.oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7606c_16bit_chan_scale_setup,
.sw_setup_cb = ad7606b_sw_mode_setup,
};
EXPORT_SYMBOL_NS_GPL(ad7606c_16_info, "IIO_AD7606");
@ -294,6 +303,7 @@ const struct ad7606_chip_info ad7606c_18_info = {
.oversampling_avail = ad7606_oversampling_avail,
.oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
.scale_setup_cb = ad7606c_18bit_chan_scale_setup,
.sw_setup_cb = ad7606b_sw_mode_setup,
};
EXPORT_SYMBOL_NS_GPL(ad7606c_18_info, "IIO_AD7606");
@ -307,6 +317,7 @@ const struct ad7606_chip_info ad7616_info = {
.oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail),
.os_req_reset = true,
.scale_setup_cb = ad7606_16bit_chan_scale_setup,
.sw_setup_cb = ad7616_sw_mode_setup,
};
EXPORT_SYMBOL_NS_GPL(ad7616_info, "IIO_AD7606");
@ -752,13 +763,13 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
ret = ad7606_scan_direct(indio_dev, chan->address, val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
}
unreachable();
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7606_scan_direct(indio_dev, chan->address, val);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
if (st->sw_mode_en)
ch = chan->address;
@ -818,8 +829,7 @@ static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val)
values[0] = val & GENMASK(2, 0);
gpiod_set_array_value(st->gpio_os->ndescs, st->gpio_os->desc,
st->gpio_os->info, values);
gpiod_multi_set_value_cansleep(st->gpio_os, values);
/* AD7616 requires a reset to update value */
if (st->chip_info->os_req_reset)
@ -852,7 +862,11 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
}
val = (val * MICRO) + val2;
i = find_closest(val, scale_avail_uv, cs->num_scales);
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = st->write_scale(indio_dev, ch, i + cs->reg_offset);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
cs->range = i;
@ -863,7 +877,11 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
i = find_closest(val, st->oversampling_avail,
st->num_os_ratios);
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = st->write_os(indio_dev, i);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
st->oversampling = st->oversampling_avail[i];
@ -1138,16 +1156,117 @@ static const struct iio_trigger_ops ad7606_trigger_ops = {
.validate_device = iio_trigger_validate_own_device,
};
static int ad7606_sw_mode_setup(struct iio_dev *indio_dev)
static int ad7606_write_mask(struct ad7606_state *st, unsigned int addr,
unsigned long mask, unsigned int val)
{
int readval;
readval = st->bops->reg_read(st, addr);
if (readval < 0)
return readval;
readval &= ~mask;
readval |= val;
return st->bops->reg_write(st, addr, readval);
}
static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
{
struct ad7606_state *st = iio_priv(indio_dev);
unsigned int ch_addr, mode, ch_index;
/*
* Ad7616 has 16 channels divided in group A and group B.
* The range of channels from A are stored in registers with address 4
* while channels from B are stored in register with address 6.
* The last bit from channels determines if it is from group A or B
* because the order of channels in iio is 0A, 0B, 1A, 1B...
*/
ch_index = ch >> 1;
ch_addr = AD7616_RANGE_CH_ADDR(ch_index);
if ((ch & 0x1) == 0) /* channel A */
ch_addr += AD7616_RANGE_CH_A_ADDR_OFF;
else /* channel B */
ch_addr += AD7616_RANGE_CH_B_ADDR_OFF;
/* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */
mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11));
return ad7606_write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index),
mode);
}
static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
{
struct ad7606_state *st = iio_priv(indio_dev);
st->sw_mode_en = st->bops->sw_mode_config &&
device_property_present(st->dev, "adi,sw-mode");
if (!st->sw_mode_en)
return 0;
return ad7606_write_mask(st, AD7616_CONFIGURATION_REGISTER,
AD7616_OS_MASK, val << 2);
}
indio_dev->info = &ad7606_info_sw_mode;
static int ad7606_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
{
struct ad7606_state *st = iio_priv(indio_dev);
return ad7606_write_mask(st, AD7606_RANGE_CH_ADDR(ch),
AD7606_RANGE_CH_MSK(ch),
AD7606_RANGE_CH_MODE(ch, val));
}
static int ad7606_write_os_sw(struct iio_dev *indio_dev, int val)
{
struct ad7606_state *st = iio_priv(indio_dev);
return st->bops->reg_write(st, AD7606_OS_MODE, val);
}
static int ad7616_sw_mode_setup(struct iio_dev *indio_dev)
{
struct ad7606_state *st = iio_priv(indio_dev);
int ret;
/*
* Scale can be configured individually for each channel
* in software mode.
*/
st->write_scale = ad7616_write_scale_sw;
st->write_os = &ad7616_write_os_sw;
ret = st->bops->sw_mode_config(indio_dev);
if (ret)
return ret;
/* Activate Burst mode and SEQEN MODE */
return ad7606_write_mask(st, AD7616_CONFIGURATION_REGISTER,
AD7616_BURST_MODE | AD7616_SEQEN_MODE,
AD7616_BURST_MODE | AD7616_SEQEN_MODE);
}
static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev)
{
struct ad7606_state *st = iio_priv(indio_dev);
DECLARE_BITMAP(os, 3);
bitmap_fill(os, 3);
/*
* Software mode is enabled when all three oversampling
* pins are set to high. If oversampling gpios are defined
* in the device tree, then they need to be set to high,
* otherwise, they must be hardwired to VDD
*/
if (st->gpio_os)
gpiod_multi_set_value_cansleep(st->gpio_os, os);
/* OS of 128 and 256 are available only in software mode */
st->oversampling_avail = ad7606b_oversampling_avail;
st->num_os_ratios = ARRAY_SIZE(ad7606b_oversampling_avail);
st->write_scale = ad7606_write_scale_sw;
st->write_os = &ad7606_write_os_sw;
return st->bops->sw_mode_config(indio_dev);
}
@ -1246,17 +1365,6 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
return -ERESTARTSYS;
}
st->write_scale = ad7606_write_scale_hw;
st->write_os = ad7606_write_os_hw;
ret = ad7606_sw_mode_setup(indio_dev);
if (ret)
return ret;
ret = ad7606_chan_scales_setup(indio_dev);
if (ret)
return ret;
/* If convst pin is not defined, setup PWM. */
if (!st->gpio_convst) {
st->cnvst_pwm = devm_pwm_get(dev, NULL);
@ -1334,6 +1442,20 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
return ret;
}
st->write_scale = ad7606_write_scale_hw;
st->write_os = ad7606_write_os_hw;
st->sw_mode_en = st->chip_info->sw_setup_cb &&
device_property_present(st->dev, "adi,sw-mode");
if (st->sw_mode_en) {
indio_dev->info = &ad7606_info_sw_mode;
st->chip_info->sw_setup_cb(indio_dev);
}
ret = ad7606_chan_scales_setup(indio_dev);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS_GPL(ad7606_probe, "IIO_AD7606");

View File

@ -10,14 +10,49 @@
#define AD760X_MAX_CHANNELS 16
#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all, bits) { \
#define AD7616_CONFIGURATION_REGISTER 0x02
#define AD7616_OS_MASK GENMASK(4, 2)
#define AD7616_BURST_MODE BIT(6)
#define AD7616_SEQEN_MODE BIT(5)
#define AD7616_RANGE_CH_A_ADDR_OFF 0x04
#define AD7616_RANGE_CH_B_ADDR_OFF 0x06
/*
* Range of channels from a group are stored in 2 registers.
* 0, 1, 2, 3 in a register followed by 4, 5, 6, 7 in second register.
* For channels from second group(8-15) the order is the same, only with
* an offset of 2 for register address.
*/
#define AD7616_RANGE_CH_ADDR(ch) ((ch) >> 2)
/* The range of the channel is stored in 2 bits */
#define AD7616_RANGE_CH_MSK(ch) (0b11 << (((ch) & 0b11) * 2))
#define AD7616_RANGE_CH_MODE(ch, mode) ((mode) << ((((ch) & 0b11)) * 2))
#define AD7606_CONFIGURATION_REGISTER 0x02
#define AD7606_SINGLE_DOUT 0x00
/*
* Range for AD7606B channels are stored in registers starting with address 0x3.
* Each register stores range for 2 channels(4 bits per channel).
*/
#define AD7606_RANGE_CH_MSK(ch) (GENMASK(3, 0) << (4 * ((ch) & 0x1)))
#define AD7606_RANGE_CH_MODE(ch, mode) \
((GENMASK(3, 0) & (mode)) << (4 * ((ch) & 0x1)))
#define AD7606_RANGE_CH_ADDR(ch) (0x03 + ((ch) >> 1))
#define AD7606_OS_MODE 0x08
#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all, \
mask_sep_avail, mask_all_avail, bits) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = num, \
.address = num, \
.info_mask_separate = mask_sep, \
.info_mask_separate_available = \
mask_sep_avail, \
.info_mask_shared_by_type = mask_type, \
.info_mask_shared_by_all = mask_all, \
.info_mask_shared_by_all_available = \
mask_all_avail, \
.scan_index = num, \
.scan_type = { \
.sign = 's', \
@ -27,37 +62,30 @@
}, \
}
#define AD7606_SW_CHANNEL(num, bits) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = num, \
.address = num, \
.info_mask_separate = \
BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_separate_available = \
BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_shared_by_all_available = \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.scan_index = num, \
.scan_type = { \
.sign = 's', \
.realbits = (bits), \
.storagebits = (bits) > 16 ? 32 : 16, \
.endianness = IIO_CPU, \
}, \
}
#define AD7606_SW_CHANNEL(num, bits) \
AD760X_CHANNEL(num, \
/* mask separate */ \
BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
/* mask type */ \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
/* mask all */ \
0, \
/* mask separate available */ \
BIT(IIO_CHAN_INFO_SCALE), \
/* mask all available */ \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
bits)
#define AD7605_CHANNEL(num) \
AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \
BIT(IIO_CHAN_INFO_SCALE), 0, 16)
BIT(IIO_CHAN_INFO_SCALE), 0, 0, 0, 16)
#define AD7606_CHANNEL(num, bits) \
AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \
BIT(IIO_CHAN_INFO_SCALE), \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), bits)
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
0, 0, bits)
#define AD7616_CHANNEL(num) AD7606_SW_CHANNEL(num, 16)
@ -65,12 +93,29 @@
AD760X_CHANNEL(num, 0, \
BIT(IIO_CHAN_INFO_SCALE), \
BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), 16)
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
0, 0, 16)
#define AD7606_BI_SW_CHANNEL(num) \
AD760X_CHANNEL(num, \
/* mask separate */ \
BIT(IIO_CHAN_INFO_SCALE), \
/* mask type */ \
0, \
/* mask all */ \
BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
/* mask separate available */ \
BIT(IIO_CHAN_INFO_SCALE), \
/* mask all available */ \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
16)
struct ad7606_state;
typedef int (*ad7606_scale_setup_cb_t)(struct iio_dev *indio_dev,
struct iio_chan_spec *chan, int ch);
typedef int (*ad7606_sw_setup_cb_t)(struct iio_dev *indio_dev);
/**
* struct ad7606_chip_info - chip specific information
@ -80,6 +125,7 @@ typedef int (*ad7606_scale_setup_cb_t)(struct iio_dev *indio_dev,
* @num_channels: number of channels
* @num_adc_channels the number of channels the ADC actually inputs.
* @scale_setup_cb: callback to setup the scales for each channel
* @sw_setup_cb: callback to setup the software mode if available.
* @oversampling_avail pointer to the array which stores the available
* oversampling ratios.
* @oversampling_num number of elements stored in oversampling_avail array
@ -94,6 +140,7 @@ struct ad7606_chip_info {
unsigned int num_adc_channels;
unsigned int num_channels;
ad7606_scale_setup_cb_t scale_setup_cb;
ad7606_sw_setup_cb_t sw_setup_cb;
const unsigned int *oversampling_avail;
unsigned int oversampling_num;
bool os_req_reset;
@ -206,10 +253,6 @@ struct ad7606_bus_ops {
int (*reg_write)(struct ad7606_state *st,
unsigned int addr,
unsigned int val);
int (*write_mask)(struct ad7606_state *st,
unsigned int addr,
unsigned long mask,
unsigned int val);
int (*update_scan_mode)(struct iio_dev *indio_dev, const unsigned long *scan_mask);
u16 (*rd_wr_cmd)(int addr, char isWriteOp);
};

View File

@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (c) 2010-2024 Analog Devices Inc.
* Copyright (c) 2025 Baylibre, SAS
*/
#ifndef __LINUX_PLATFORM_DATA_AD7606_H__
#define __LINUX_PLATFORM_DATA_AD7606_H__
struct iio_backend;
struct ad7606_platform_data {
int (*bus_reg_read)(struct iio_backend *back, u32 reg, u32 *val);
int (*bus_reg_write)(struct iio_backend *back, u32 reg, u32 val);
};
#endif /* __LINUX_PLATFORM_DATA_AD7606_H__ */

View File

@ -19,6 +19,7 @@
#include <linux/iio/iio.h>
#include "ad7606.h"
#include "ad7606_bus_iface.h"
static const struct iio_chan_spec ad7606b_bi_channels[] = {
AD7606_BI_CHANNEL(0),
@ -31,7 +32,19 @@ static const struct iio_chan_spec ad7606b_bi_channels[] = {
AD7606_BI_CHANNEL(7),
};
static int ad7606_bi_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask)
static const struct iio_chan_spec ad7606b_bi_sw_channels[] = {
AD7606_BI_SW_CHANNEL(0),
AD7606_BI_SW_CHANNEL(1),
AD7606_BI_SW_CHANNEL(2),
AD7606_BI_SW_CHANNEL(3),
AD7606_BI_SW_CHANNEL(4),
AD7606_BI_SW_CHANNEL(5),
AD7606_BI_SW_CHANNEL(6),
AD7606_BI_SW_CHANNEL(7),
};
static int ad7606_par_bus_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct ad7606_state *st = iio_priv(indio_dev);
unsigned int c, ret;
@ -48,7 +61,8 @@ static int ad7606_bi_update_scan_mode(struct iio_dev *indio_dev, const unsigned
return 0;
}
static int ad7606_bi_setup_iio_backend(struct device *dev, struct iio_dev *indio_dev)
static int ad7606_par_bus_setup_iio_backend(struct device *dev,
struct iio_dev *indio_dev)
{
struct ad7606_state *st = iio_priv(indio_dev);
unsigned int ret, c;
@ -86,9 +100,39 @@ static int ad7606_bi_setup_iio_backend(struct device *dev, struct iio_dev *indio
return 0;
}
static int ad7606_par_bus_reg_read(struct ad7606_state *st, unsigned int addr)
{
struct ad7606_platform_data *pdata = st->dev->platform_data;
int val, ret;
ret = pdata->bus_reg_read(st->back, addr, &val);
if (ret)
return ret;
return val;
}
static int ad7606_par_bus_reg_write(struct ad7606_state *st, unsigned int addr,
unsigned int val)
{
struct ad7606_platform_data *pdata = st->dev->platform_data;
return pdata->bus_reg_write(st->back, addr, val);
}
static int ad7606_par_bus_sw_mode_config(struct iio_dev *indio_dev)
{
indio_dev->channels = ad7606b_bi_sw_channels;
return 0;
}
static const struct ad7606_bus_ops ad7606_bi_bops = {
.iio_backend_config = ad7606_bi_setup_iio_backend,
.update_scan_mode = ad7606_bi_update_scan_mode,
.iio_backend_config = ad7606_par_bus_setup_iio_backend,
.update_scan_mode = ad7606_par_bus_update_scan_mode,
.reg_read = ad7606_par_bus_reg_read,
.reg_write = ad7606_par_bus_reg_write,
.sw_mode_config = ad7606_par_bus_sw_mode_config,
};
static int ad7606_par16_read_block(struct device *dev,

View File

@ -15,36 +15,6 @@
#define MAX_SPI_FREQ_HZ 23500000 /* VDRIVE above 4.75 V */
#define AD7616_CONFIGURATION_REGISTER 0x02
#define AD7616_OS_MASK GENMASK(4, 2)
#define AD7616_BURST_MODE BIT(6)
#define AD7616_SEQEN_MODE BIT(5)
#define AD7616_RANGE_CH_A_ADDR_OFF 0x04
#define AD7616_RANGE_CH_B_ADDR_OFF 0x06
/*
* Range of channels from a group are stored in 2 registers.
* 0, 1, 2, 3 in a register followed by 4, 5, 6, 7 in second register.
* For channels from second group(8-15) the order is the same, only with
* an offset of 2 for register address.
*/
#define AD7616_RANGE_CH_ADDR(ch) ((ch) >> 2)
/* The range of the channel is stored in 2 bits */
#define AD7616_RANGE_CH_MSK(ch) (0b11 << (((ch) & 0b11) * 2))
#define AD7616_RANGE_CH_MODE(ch, mode) ((mode) << ((((ch) & 0b11)) * 2))
#define AD7606_CONFIGURATION_REGISTER 0x02
#define AD7606_SINGLE_DOUT 0x00
/*
* Range for AD7606B channels are stored in registers starting with address 0x3.
* Each register stores range for 2 channels(4 bits per channel).
*/
#define AD7606_RANGE_CH_MSK(ch) (GENMASK(3, 0) << (4 * ((ch) & 0x1)))
#define AD7606_RANGE_CH_MODE(ch, mode) \
((GENMASK(3, 0) & mode) << (4 * ((ch) & 0x1)))
#define AD7606_RANGE_CH_ADDR(ch) (0x03 + ((ch) >> 1))
#define AD7606_OS_MODE 0x08
static const struct iio_chan_spec ad7616_sw_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(16),
AD7616_CHANNEL(0),
@ -89,10 +59,6 @@ static const struct iio_chan_spec ad7606c_18_sw_channels[] = {
AD7606_SW_CHANNEL(7, 18),
};
static const unsigned int ad7606B_oversampling_avail[9] = {
1, 2, 4, 8, 16, 32, 64, 128, 256
};
static u16 ad7616_spi_rd_wr_cmd(int addr, char isWriteOp)
{
/*
@ -194,118 +160,20 @@ static int ad7606_spi_reg_write(struct ad7606_state *st,
return spi_write(spi, &st->d16[0], sizeof(st->d16[0]));
}
static int ad7606_spi_write_mask(struct ad7606_state *st,
unsigned int addr,
unsigned long mask,
unsigned int val)
{
int readval;
readval = st->bops->reg_read(st, addr);
if (readval < 0)
return readval;
readval &= ~mask;
readval |= val;
return st->bops->reg_write(st, addr, readval);
}
static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
{
struct ad7606_state *st = iio_priv(indio_dev);
unsigned int ch_addr, mode, ch_index;
/*
* Ad7616 has 16 channels divided in group A and group B.
* The range of channels from A are stored in registers with address 4
* while channels from B are stored in register with address 6.
* The last bit from channels determines if it is from group A or B
* because the order of channels in iio is 0A, 0B, 1A, 1B...
*/
ch_index = ch >> 1;
ch_addr = AD7616_RANGE_CH_ADDR(ch_index);
if ((ch & 0x1) == 0) /* channel A */
ch_addr += AD7616_RANGE_CH_A_ADDR_OFF;
else /* channel B */
ch_addr += AD7616_RANGE_CH_B_ADDR_OFF;
/* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */
mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11));
return st->bops->write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index),
mode);
}
static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
{
struct ad7606_state *st = iio_priv(indio_dev);
return st->bops->write_mask(st, AD7616_CONFIGURATION_REGISTER,
AD7616_OS_MASK, val << 2);
}
static int ad7606_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
{
struct ad7606_state *st = iio_priv(indio_dev);
return ad7606_spi_write_mask(st,
AD7606_RANGE_CH_ADDR(ch),
AD7606_RANGE_CH_MSK(ch),
AD7606_RANGE_CH_MODE(ch, val));
}
static int ad7606_write_os_sw(struct iio_dev *indio_dev, int val)
{
struct ad7606_state *st = iio_priv(indio_dev);
return ad7606_spi_reg_write(st, AD7606_OS_MODE, val);
}
static int ad7616_sw_mode_config(struct iio_dev *indio_dev)
{
struct ad7606_state *st = iio_priv(indio_dev);
/*
* Scale can be configured individually for each channel
* in software mode.
*/
indio_dev->channels = ad7616_sw_channels;
st->write_scale = ad7616_write_scale_sw;
st->write_os = &ad7616_write_os_sw;
/* Activate Burst mode and SEQEN MODE */
return st->bops->write_mask(st,
AD7616_CONFIGURATION_REGISTER,
AD7616_BURST_MODE | AD7616_SEQEN_MODE,
AD7616_BURST_MODE | AD7616_SEQEN_MODE);
return 0;
}
static int ad7606B_sw_mode_config(struct iio_dev *indio_dev)
{
struct ad7606_state *st = iio_priv(indio_dev);
DECLARE_BITMAP(os, 3);
bitmap_fill(os, 3);
/*
* Software mode is enabled when all three oversampling
* pins are set to high. If oversampling gpios are defined
* in the device tree, then they need to be set to high,
* otherwise, they must be hardwired to VDD
*/
if (st->gpio_os) {
gpiod_set_array_value(st->gpio_os->ndescs,
st->gpio_os->desc, st->gpio_os->info, os);
}
/* OS of 128 and 256 are available only in software mode */
st->oversampling_avail = ad7606B_oversampling_avail;
st->num_os_ratios = ARRAY_SIZE(ad7606B_oversampling_avail);
st->write_scale = ad7606_write_scale_sw;
st->write_os = &ad7606_write_os_sw;
/* Configure device spi to output on a single channel */
st->bops->reg_write(st,
@ -350,7 +218,6 @@ static const struct ad7606_bus_ops ad7616_spi_bops = {
.read_block = ad7606_spi_read_block,
.reg_read = ad7606_spi_reg_read,
.reg_write = ad7606_spi_reg_write,
.write_mask = ad7606_spi_write_mask,
.rd_wr_cmd = ad7616_spi_rd_wr_cmd,
.sw_mode_config = ad7616_sw_mode_config,
};
@ -359,7 +226,6 @@ static const struct ad7606_bus_ops ad7606b_spi_bops = {
.read_block = ad7606_spi_read_block,
.reg_read = ad7606_spi_reg_read,
.reg_write = ad7606_spi_reg_write,
.write_mask = ad7606_spi_write_mask,
.rd_wr_cmd = ad7606B_spi_rd_wr_cmd,
.sw_mode_config = ad7606B_sw_mode_config,
};
@ -368,7 +234,6 @@ static const struct ad7606_bus_ops ad7606c_18_spi_bops = {
.read_block = ad7606_spi_read_block18to32,
.reg_read = ad7606_spi_reg_read,
.reg_write = ad7606_spi_reg_write,
.write_mask = ad7606_spi_write_mask,
.rd_wr_cmd = ad7606B_spi_rd_wr_cmd,
.sw_mode_config = ad7606c_18_sw_mode_config,
};

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
// SPDX-License-Identifier: (GPL-2.0-only)
/*
* Analog Devices Inc. AD7625 ADC driver
*
@ -248,12 +248,15 @@ static int ad7625_write_raw(struct iio_dev *indio_dev,
int val, int val2, long info)
{
struct ad7625_state *st = iio_priv(indio_dev);
int ret;
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ:
iio_device_claim_direct_scoped(return -EBUSY, indio_dev)
return ad7625_set_sampling_freq(st, val);
unreachable();
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7625_set_sampling_freq(st, val);
iio_device_release_direct(indio_dev);
return ret;
default:
return -EINVAL;
}
@ -680,5 +683,5 @@ module_platform_driver(ad7625_driver);
MODULE_AUTHOR("Trevor Gamblin <tgamblin@baylibre.com>");
MODULE_DESCRIPTION("Analog Devices AD7625 ADC");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("IIO_BACKEND");

View File

@ -142,7 +142,7 @@ static const struct iio_chan_spec ad7768_channels[] = {
.channel = 0,
.scan_index = 0,
.scan_type = {
.sign = 'u',
.sign = 's',
.realbits = 24,
.storagebits = 32,
.shift = 8,
@ -154,7 +154,6 @@ static const struct iio_chan_spec ad7768_channels[] = {
struct ad7768_state {
struct spi_device *spi;
struct regulator *vref;
struct mutex lock;
struct clk *mclk;
unsigned int mclk_freq;
unsigned int samp_freq;
@ -256,18 +255,20 @@ static int ad7768_reg_access(struct iio_dev *indio_dev,
struct ad7768_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
if (readval) {
ret = ad7768_spi_reg_read(st, reg, 1);
if (ret < 0)
goto err_unlock;
goto err_release;
*readval = ret;
ret = 0;
} else {
ret = ad7768_spi_reg_write(st, reg, writeval);
}
err_unlock:
mutex_unlock(&st->lock);
err_release:
iio_device_release_direct(indio_dev);
return ret;
}
@ -365,17 +366,15 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
switch (info) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7768_scan_direct(indio_dev);
if (ret >= 0)
*val = ret;
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
*val = sign_extend32(ret, chan->scan_type.realbits - 1);
return IIO_VAL_INT;
@ -471,18 +470,15 @@ static irqreturn_t ad7768_trigger_handler(int irq, void *p)
struct ad7768_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
ret = spi_read(st->spi, &st->data.scan.chan, 3);
if (ret < 0)
goto err_unlock;
goto out;
iio_push_to_buffers_with_timestamp(indio_dev, &st->data.scan,
iio_get_time_ns(indio_dev));
err_unlock:
out:
iio_trigger_notify_done(indio_dev->trig);
mutex_unlock(&st->lock);
return IRQ_HANDLED;
}
@ -574,6 +570,21 @@ static int ad7768_probe(struct spi_device *spi)
return -ENOMEM;
st = iio_priv(indio_dev);
/*
* Datasheet recommends SDI line to be kept high when data is not being
* clocked out of the controller and the spi clock is free running,
* to prevent accidental reset.
* Since many controllers do not support the SPI_MOSI_IDLE_HIGH flag
* yet, only request the MOSI idle state to enable if the controller
* supports it.
*/
if (spi->controller->mode_bits & SPI_MOSI_IDLE_HIGH) {
spi->mode |= SPI_MOSI_IDLE_HIGH;
ret = spi_setup(spi);
if (ret < 0)
return ret;
}
st->spi = spi;
st->vref = devm_regulator_get(&spi->dev, "vref");
@ -596,8 +607,6 @@ static int ad7768_probe(struct spi_device *spi)
st->mclk_freq = clk_get_rate(st->mclk);
mutex_init(&st->lock);
indio_dev->channels = ad7768_channels;
indio_dev->num_channels = ARRAY_SIZE(ad7768_channels);
indio_dev->name = spi_get_device_id(spi)->name;

View File

@ -467,59 +467,82 @@ static int ad7779_set_calibbias(struct ad7779_state *st, int channel, int val)
calibbias[2]);
}
static int ad7779_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
static int __ad7779_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct ad7779_state *st = iio_priv(indio_dev);
int ret;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
switch (mask) {
case IIO_CHAN_INFO_CALIBSCALE:
ret = ad7779_get_calibscale(st, chan->channel);
if (ret < 0)
return ret;
*val = ret;
*val2 = GAIN_REL;
return IIO_VAL_FRACTIONAL;
case IIO_CHAN_INFO_CALIBBIAS:
ret = ad7779_get_calibbias(st, chan->channel);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = st->sampling_freq;
if (*val < 0)
return -EINVAL;
return IIO_VAL_INT;
default:
switch (mask) {
case IIO_CHAN_INFO_CALIBSCALE:
ret = ad7779_get_calibscale(st, chan->channel);
if (ret < 0)
return ret;
*val = ret;
*val2 = GAIN_REL;
return IIO_VAL_FRACTIONAL;
case IIO_CHAN_INFO_CALIBBIAS:
ret = ad7779_get_calibbias(st, chan->channel);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = st->sampling_freq;
if (*val < 0)
return -EINVAL;
}
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int ad7779_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
int ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = __ad7779_read_raw(indio_dev, chan, val, val2, mask);
iio_device_release_direct(indio_dev);
return ret;
}
static int __ad7779_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2,
long mask)
{
struct ad7779_state *st = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_CALIBSCALE:
return ad7779_set_calibscale(st, chan->channel, val2);
case IIO_CHAN_INFO_CALIBBIAS:
return ad7779_set_calibbias(st, chan->channel, val);
case IIO_CHAN_INFO_SAMP_FREQ:
return ad7779_set_sampling_frequency(st, val);
default:
return -EINVAL;
}
unreachable();
}
static int ad7779_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2,
long mask)
{
struct ad7779_state *st = iio_priv(indio_dev);
int ret;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
switch (mask) {
case IIO_CHAN_INFO_CALIBSCALE:
return ad7779_set_calibscale(st, chan->channel, val2);
case IIO_CHAN_INFO_CALIBBIAS:
return ad7779_set_calibbias(st, chan->channel, val);
case IIO_CHAN_INFO_SAMP_FREQ:
return ad7779_set_sampling_frequency(st, val);
default:
return -EINVAL;
}
}
unreachable();
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = __ad7779_write_raw(indio_dev, chan, val, val2, mask);
iio_device_release_direct(indio_dev);
return ret;
}
static int ad7779_buffer_preenable(struct iio_dev *indio_dev)

View File

@ -310,15 +310,11 @@ static int ad7791_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
static int ad7791_write_raw(struct iio_dev *indio_dev,
static int __ad7791_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2, long mask)
{
struct ad7791_state *st = iio_priv(indio_dev);
int ret, i;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
int i;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
@ -328,22 +324,31 @@ static int ad7791_write_raw(struct iio_dev *indio_dev,
break;
}
if (i == ARRAY_SIZE(ad7791_sample_freq_avail)) {
ret = -EINVAL;
break;
}
if (i == ARRAY_SIZE(ad7791_sample_freq_avail))
return -EINVAL;
st->filter &= ~AD7791_FILTER_RATE_MASK;
st->filter |= i;
ad_sd_write_reg(&st->sd, AD7791_REG_FILTER,
sizeof(st->filter),
st->filter);
break;
return 0;
default:
ret = -EINVAL;
return -EINVAL;
}
}
iio_device_release_direct_mode(indio_dev);
static int ad7791_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2, long mask)
{
int ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = __ad7791_write_raw(indio_dev, chan, val, val2, mask);
iio_device_release_direct(indio_dev);
return ret;
}

View File

@ -462,64 +462,68 @@ static int ad7793_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
static int ad7793_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long mask)
static int __ad7793_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct ad7793_state *st = iio_priv(indio_dev);
int ret, i;
int i;
unsigned int tmp;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
if (val2 == st->scale_avail[i][1]) {
ret = 0;
tmp = st->conf;
st->conf &= ~AD7793_CONF_GAIN(-1);
st->conf |= AD7793_CONF_GAIN(i);
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) {
if (val2 != st->scale_avail[i][1])
continue;
if (tmp == st->conf)
break;
tmp = st->conf;
st->conf &= ~AD7793_CONF_GAIN(-1);
st->conf |= AD7793_CONF_GAIN(i);
ad_sd_write_reg(&st->sd, AD7793_REG_CONF,
sizeof(st->conf), st->conf);
ad7793_calibrate_all(st);
break;
}
break;
case IIO_CHAN_INFO_SAMP_FREQ:
if (!val) {
ret = -EINVAL;
break;
if (tmp == st->conf)
return 0;
ad_sd_write_reg(&st->sd, AD7793_REG_CONF,
sizeof(st->conf), st->conf);
ad7793_calibrate_all(st);
return 0;
}
return -EINVAL;
case IIO_CHAN_INFO_SAMP_FREQ:
if (!val)
return -EINVAL;
for (i = 0; i < 16; i++)
if (val == st->chip_info->sample_freq_avail[i])
break;
if (i == 16) {
ret = -EINVAL;
break;
}
if (i == 16)
return -EINVAL;
st->mode &= ~AD7793_MODE_RATE(-1);
st->mode |= AD7793_MODE_RATE(i);
ad_sd_write_reg(&st->sd, AD7793_REG_MODE, sizeof(st->mode),
st->mode);
break;
return 0;
default:
ret = -EINVAL;
return -EINVAL;
}
}
static int ad7793_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
int ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = __ad7793_write_raw(indio_dev, chan, val, val2, mask);
iio_device_release_direct(indio_dev);
iio_device_release_direct_mode(indio_dev);
return ret;
}

View File

@ -152,11 +152,10 @@ static int ad7887_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7887_scan_direct(st, chan->address);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;

View File

@ -260,11 +260,10 @@ static int ad7923_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7923_scan_direct(st, chan->address);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;

View File

@ -16,11 +16,14 @@
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/offload/consumer.h>
#include <linux/spi/spi.h>
#include <linux/string_helpers.h>
#include <linux/units.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer-dmaengine.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
@ -54,6 +57,12 @@ struct ad7944_adc {
enum ad7944_spi_mode spi_mode;
struct spi_transfer xfers[3];
struct spi_message msg;
struct spi_transfer offload_xfers[2];
struct spi_message offload_msg;
struct spi_offload *offload;
struct spi_offload_trigger *offload_trigger;
unsigned long offload_trigger_hz;
int sample_freq_range[3];
void *chain_mode_buf;
/* Chip-specific timing specifications. */
const struct ad7944_timing_spec *timing_spec;
@ -81,6 +90,8 @@ struct ad7944_adc {
/* quite time before CNV rising edge */
#define AD7944_T_QUIET_NS 20
/* minimum CNV high time to trigger conversion */
#define AD7944_T_CNVH_NS 10
static const struct ad7944_timing_spec ad7944_timing_spec = {
.conv_ns = 420,
@ -95,20 +106,27 @@ static const struct ad7944_timing_spec ad7986_timing_spec = {
struct ad7944_chip_info {
const char *name;
const struct ad7944_timing_spec *timing_spec;
u32 max_sample_rate_hz;
const struct iio_chan_spec channels[2];
const struct iio_chan_spec offload_channels[1];
};
/* get number of bytes for SPI xfer */
#define AD7944_SPI_BYTES(scan_type) ((scan_type).realbits > 16 ? 4 : 2)
/*
* AD7944_DEFINE_CHIP_INFO - Define a chip info structure for a specific chip
* @_name: The name of the chip
* @_ts: The timing specification for the chip
* @_max: The maximum sample rate in Hz
* @_bits: The number of bits in the conversion result
* @_diff: Whether the chip is true differential or not
*/
#define AD7944_DEFINE_CHIP_INFO(_name, _ts, _bits, _diff) \
#define AD7944_DEFINE_CHIP_INFO(_name, _ts, _max, _bits, _diff) \
static const struct ad7944_chip_info _name##_chip_info = { \
.name = #_name, \
.timing_spec = &_ts##_timing_spec, \
.max_sample_rate_hz = _max, \
.channels = { \
{ \
.type = IIO_VOLTAGE, \
@ -126,13 +144,43 @@ static const struct ad7944_chip_info _name##_chip_info = { \
}, \
IIO_CHAN_SOFT_TIMESTAMP(1), \
}, \
.offload_channels = { \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.differential = _diff, \
.channel = 0, \
.channel2 = _diff ? 1 : 0, \
.scan_index = 0, \
.scan_type.sign = _diff ? 's' : 'u', \
.scan_type.realbits = _bits, \
.scan_type.storagebits = 32, \
.scan_type.endianness = IIO_CPU, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
| BIT(IIO_CHAN_INFO_SCALE) \
| BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_separate_available = \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
}, \
}, \
}
/*
* Notes on the offload channels:
* - There is no soft timestamp since everything is done in hardware.
* - There is a sampling frequency attribute added. This controls the SPI
* offload trigger.
* - The storagebits value depends on the SPI offload provider. Currently there
* is only one supported provider, namely the ADI PULSAR ADC HDL project,
* which always uses 32-bit words for data values, even for <= 16-bit ADCs.
* So the value is just hardcoded to 32 for now.
*/
/* pseudo-differential with ground sense */
AD7944_DEFINE_CHIP_INFO(ad7944, ad7944, 14, 0);
AD7944_DEFINE_CHIP_INFO(ad7985, ad7944, 16, 0);
AD7944_DEFINE_CHIP_INFO(ad7944, ad7944, 2.5 * MEGA, 14, 0);
AD7944_DEFINE_CHIP_INFO(ad7985, ad7944, 2.5 * MEGA, 16, 0);
/* fully differential */
AD7944_DEFINE_CHIP_INFO(ad7986, ad7986, 18, 1);
AD7944_DEFINE_CHIP_INFO(ad7986, ad7986, 2 * MEGA, 18, 1);
static int ad7944_3wire_cs_mode_init_msg(struct device *dev, struct ad7944_adc *adc,
const struct iio_chan_spec *chan)
@ -164,7 +212,7 @@ static int ad7944_3wire_cs_mode_init_msg(struct device *dev, struct ad7944_adc *
/* Then we can read the data during the acquisition phase */
xfers[2].rx_buf = &adc->sample.raw;
xfers[2].len = BITS_TO_BYTES(chan->scan_type.storagebits);
xfers[2].len = AD7944_SPI_BYTES(chan->scan_type);
xfers[2].bits_per_word = chan->scan_type.realbits;
spi_message_init_with_transfers(&adc->msg, xfers, 3);
@ -193,7 +241,7 @@ static int ad7944_4wire_mode_init_msg(struct device *dev, struct ad7944_adc *adc
xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
xfers[1].rx_buf = &adc->sample.raw;
xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits);
xfers[1].len = AD7944_SPI_BYTES(chan->scan_type);
xfers[1].bits_per_word = chan->scan_type.realbits;
spi_message_init_with_transfers(&adc->msg, xfers, 2);
@ -228,7 +276,7 @@ static int ad7944_chain_mode_init_msg(struct device *dev, struct ad7944_adc *adc
xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
xfers[1].rx_buf = adc->chain_mode_buf;
xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits) * n_chain_dev;
xfers[1].len = AD7944_SPI_BYTES(chan->scan_type) * n_chain_dev;
xfers[1].bits_per_word = chan->scan_type.realbits;
spi_message_init_with_transfers(&adc->msg, xfers, 2);
@ -236,6 +284,48 @@ static int ad7944_chain_mode_init_msg(struct device *dev, struct ad7944_adc *adc
return devm_spi_optimize_message(dev, adc->spi, &adc->msg);
}
/*
* Unlike ad7944_3wire_cs_mode_init_msg(), this creates a message that reads
* during the conversion phase instead of the acquisition phase when reading
* a sample from the ADC. This is needed to be able to read at the maximum
* sample rate. It requires the SPI controller to have offload support and a
* high enough SCLK rate to read the sample during the conversion phase.
*/
static int ad7944_3wire_cs_mode_init_offload_msg(struct device *dev,
struct ad7944_adc *adc,
const struct iio_chan_spec *chan)
{
struct spi_transfer *xfers = adc->offload_xfers;
int ret;
/*
* CS is tied to CNV and we need a low to high transition to start the
* conversion, so place CNV low for t_QUIET to prepare for this.
*/
xfers[0].delay.value = AD7944_T_QUIET_NS;
xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
/* CNV has to be high for a minimum time to trigger conversion. */
xfers[0].cs_change = 1;
xfers[0].cs_change_delay.value = AD7944_T_CNVH_NS;
xfers[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
/* Then we can read the previous sample during the conversion phase */
xfers[1].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
xfers[1].len = AD7944_SPI_BYTES(chan->scan_type);
xfers[1].bits_per_word = chan->scan_type.realbits;
spi_message_init_with_transfers(&adc->offload_msg, xfers,
ARRAY_SIZE(adc->offload_xfers));
adc->offload_msg.offload = adc->offload;
ret = devm_spi_optimize_message(dev, adc->spi, &adc->offload_msg);
if (ret)
return dev_err_probe(dev, ret, "failed to prepare offload msg\n");
return 0;
}
/**
* ad7944_convert_and_acquire - Perform a single conversion and acquisition
* @adc: The ADC device structure
@ -274,12 +364,12 @@ static int ad7944_single_conversion(struct ad7944_adc *adc,
return ret;
if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) {
if (chan->scan_type.storagebits > 16)
if (chan->scan_type.realbits > 16)
*val = ((u32 *)adc->chain_mode_buf)[chan->scan_index];
else
*val = ((u16 *)adc->chain_mode_buf)[chan->scan_index];
} else {
if (chan->scan_type.storagebits > 16)
if (chan->scan_type.realbits > 16)
*val = adc->sample.raw.u32;
else
*val = adc->sample.raw.u16;
@ -291,6 +381,23 @@ static int ad7944_single_conversion(struct ad7944_adc *adc,
return IIO_VAL_INT;
}
static int ad7944_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
struct ad7944_adc *adc = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
*vals = adc->sample_freq_range;
*type = IIO_VAL_INT;
return IIO_AVAIL_RANGE;
default:
return -EINVAL;
}
}
static int ad7944_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long info)
@ -300,12 +407,11 @@ static int ad7944_read_raw(struct iio_dev *indio_dev,
switch (info) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7944_single_conversion(adc, chan, val);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
@ -323,13 +429,104 @@ static int ad7944_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
case IIO_CHAN_INFO_SAMP_FREQ:
*val = adc->offload_trigger_hz;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int ad7944_set_sample_freq(struct ad7944_adc *adc, int val)
{
struct spi_offload_trigger_config config = {
.type = SPI_OFFLOAD_TRIGGER_PERIODIC,
.periodic = {
.frequency_hz = val,
},
};
int ret;
ret = spi_offload_trigger_validate(adc->offload_trigger, &config);
if (ret)
return ret;
adc->offload_trigger_hz = config.periodic.frequency_hz;
return 0;
}
static int ad7944_write_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
int val, int val2, long info)
{
struct ad7944_adc *adc = iio_priv(indio_dev);
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ:
if (val < 1 || val > adc->sample_freq_range[2])
return -EINVAL;
return ad7944_set_sample_freq(adc, val);
default:
return -EINVAL;
}
}
static int ad7944_write_raw_get_fmt(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
return IIO_VAL_INT;
default:
return IIO_VAL_INT_PLUS_MICRO;
}
}
static const struct iio_info ad7944_iio_info = {
.read_avail = &ad7944_read_avail,
.read_raw = &ad7944_read_raw,
.write_raw = &ad7944_write_raw,
.write_raw_get_fmt = &ad7944_write_raw_get_fmt,
};
static int ad7944_offload_buffer_postenable(struct iio_dev *indio_dev)
{
struct ad7944_adc *adc = iio_priv(indio_dev);
struct spi_offload_trigger_config config = {
.type = SPI_OFFLOAD_TRIGGER_PERIODIC,
.periodic = {
.frequency_hz = adc->offload_trigger_hz,
},
};
int ret;
gpiod_set_value_cansleep(adc->turbo, 1);
ret = spi_offload_trigger_enable(adc->offload, adc->offload_trigger,
&config);
if (ret)
gpiod_set_value_cansleep(adc->turbo, 0);
return ret;
}
static int ad7944_offload_buffer_predisable(struct iio_dev *indio_dev)
{
struct ad7944_adc *adc = iio_priv(indio_dev);
spi_offload_trigger_disable(adc->offload, adc->offload_trigger);
gpiod_set_value_cansleep(adc->turbo, 0);
return 0;
}
static const struct iio_buffer_setup_ops ad7944_offload_buffer_setup_ops = {
.postenable = &ad7944_offload_buffer_postenable,
.predisable = &ad7944_offload_buffer_predisable,
};
static irqreturn_t ad7944_trigger_handler(int irq, void *p)
@ -409,8 +606,7 @@ static int ad7944_chain_mode_alloc(struct device *dev,
/* 1 word for each voltage channel + aligned u64 for timestamp */
chain_mode_buf_size = ALIGN(n_chain_dev *
BITS_TO_BYTES(chan[0].scan_type.storagebits), sizeof(u64))
+ sizeof(u64);
AD7944_SPI_BYTES(chan[0].scan_type), sizeof(u64)) + sizeof(u64);
buf = devm_kzalloc(dev, chain_mode_buf_size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
@ -444,6 +640,11 @@ static const char * const ad7944_power_supplies[] = {
"avdd", "dvdd", "bvdd", "vio"
};
static const struct spi_offload_config ad7944_offload_config = {
.capability_flags = SPI_OFFLOAD_CAP_TRIGGER |
SPI_OFFLOAD_CAP_RX_STREAM_DMA,
};
static int ad7944_probe(struct spi_device *spi)
{
const struct ad7944_chip_info *chip_info;
@ -469,6 +670,10 @@ static int ad7944_probe(struct spi_device *spi)
adc->timing_spec = chip_info->timing_spec;
adc->sample_freq_range[0] = 1; /* min */
adc->sample_freq_range[1] = 1; /* step */
adc->sample_freq_range[2] = chip_info->max_sample_rate_hz; /* max */
ret = device_property_match_property_string(dev, "adi,spi-mode",
ad7944_spi_modes,
ARRAY_SIZE(ad7944_spi_modes));
@ -588,20 +793,74 @@ static int ad7944_probe(struct spi_device *spi)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &ad7944_iio_info;
if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) {
indio_dev->available_scan_masks = chain_scan_masks;
indio_dev->channels = chain_chan;
indio_dev->num_channels = n_chain_dev + 1;
} else {
indio_dev->channels = chip_info->channels;
indio_dev->num_channels = ARRAY_SIZE(chip_info->channels);
}
adc->offload = devm_spi_offload_get(dev, spi, &ad7944_offload_config);
ret = PTR_ERR_OR_ZERO(adc->offload);
if (ret && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to get offload\n");
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
iio_pollfunc_store_time,
ad7944_trigger_handler, NULL);
if (ret)
return ret;
/* Fall back to low speed usage when no SPI offload available. */
if (ret == -ENODEV) {
if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) {
indio_dev->available_scan_masks = chain_scan_masks;
indio_dev->channels = chain_chan;
indio_dev->num_channels = n_chain_dev + 1;
} else {
indio_dev->channels = chip_info->channels;
indio_dev->num_channels = ARRAY_SIZE(chip_info->channels);
}
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
iio_pollfunc_store_time,
ad7944_trigger_handler,
NULL);
if (ret)
return ret;
} else {
struct dma_chan *rx_dma;
if (adc->spi_mode != AD7944_SPI_MODE_SINGLE)
return dev_err_probe(dev, -EINVAL,
"offload only supported in single mode\n");
indio_dev->setup_ops = &ad7944_offload_buffer_setup_ops;
indio_dev->channels = chip_info->offload_channels;
indio_dev->num_channels = ARRAY_SIZE(chip_info->offload_channels);
adc->offload_trigger = devm_spi_offload_trigger_get(dev,
adc->offload, SPI_OFFLOAD_TRIGGER_PERIODIC);
if (IS_ERR(adc->offload_trigger))
return dev_err_probe(dev, PTR_ERR(adc->offload_trigger),
"failed to get offload trigger\n");
ret = ad7944_set_sample_freq(adc, 2 * MEGA);
if (ret)
return dev_err_probe(dev, ret,
"failed to init sample rate\n");
rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev,
adc->offload);
if (IS_ERR(rx_dma))
return dev_err_probe(dev, PTR_ERR(rx_dma),
"failed to get offload RX DMA\n");
/*
* REVISIT: ideally, we would confirm that the offload RX DMA
* buffer layout is the same as what is hard-coded in
* offload_channels. Right now, the only supported offload
* is the pulsar_adc project which always uses 32-bit word
* size for data values, regardless of the SPI bits per word.
*/
ret = devm_iio_dmaengine_buffer_setup_with_handle(dev,
indio_dev, rx_dma, IIO_BUFFER_DIRECTION_IN);
if (ret)
return ret;
ret = ad7944_3wire_cs_mode_init_offload_msg(dev, adc,
&chip_info->offload_channels[0]);
if (ret)
return ret;
}
return devm_iio_device_register(dev, indio_dev);
}
@ -636,3 +895,4 @@ module_spi_driver(ad7944_driver);
MODULE_AUTHOR("David Lechner <dlechner@baylibre.com>");
MODULE_DESCRIPTION("Analog Devices AD7944 PulSAR ADC family driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");

View File

@ -291,13 +291,12 @@ static int ad799x_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
mutex_lock(&st->lock);
ret = ad799x_scan_direct(st, chan->scan_index);
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
@ -411,9 +410,8 @@ static int ad799x_write_event_config(struct iio_dev *indio_dev,
struct ad799x_state *st = iio_priv(indio_dev);
int ret;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
mutex_lock(&st->lock);
@ -429,7 +427,7 @@ static int ad799x_write_event_config(struct iio_dev *indio_dev,
ret = ad799x_write_config(st, st->config);
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return ret;
}

View File

@ -813,6 +813,18 @@ static int ad9467_read_raw(struct iio_dev *indio_dev,
}
}
static int __ad9467_update_clock(struct ad9467_state *st, long r_clk)
{
int ret;
ret = clk_set_rate(st->clk, r_clk);
if (ret)
return ret;
guard(mutex)(&st->lock);
return ad9467_calibrate(st);
}
static int ad9467_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
@ -842,14 +854,11 @@ static int ad9467_write_raw(struct iio_dev *indio_dev,
if (sample_rate == r_clk)
return 0;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
ret = clk_set_rate(st->clk, r_clk);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
guard(mutex)(&st->lock);
ret = ad9467_calibrate(st);
}
ret = __ad9467_update_clock(st, r_clk);
iio_device_release_direct(indio_dev);
return ret;
default:
return -EINVAL;

View File

@ -339,6 +339,7 @@ int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
out:
sigma_delta->keep_cs_asserted = false;
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
ad_sigma_delta_disable_one(sigma_delta, channel);
sigma_delta->bus_locked = false;
spi_bus_unlock(sigma_delta->spi->controller);
@ -386,11 +387,12 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
unsigned int data_reg;
int ret = 0;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ad_sigma_delta_set_channel(sigma_delta, chan->address);
ret = ad_sigma_delta_set_channel(sigma_delta, chan->address);
if (ret)
goto out_release;
spi_bus_lock(sigma_delta->spi->controller);
sigma_delta->bus_locked = true;
@ -431,7 +433,8 @@ out_unlock:
sigma_delta->keep_cs_asserted = false;
sigma_delta->bus_locked = false;
spi_bus_unlock(sigma_delta->spi->controller);
iio_device_release_direct_mode(indio_dev);
out_release:
iio_device_release_direct(indio_dev);
if (ret)
return ret;
@ -801,10 +804,15 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
spin_lock_init(&sigma_delta->irq_lock);
if (info->irq_line)
sigma_delta->irq_line = info->irq_line;
else
if (info->has_named_irqs) {
sigma_delta->irq_line = fwnode_irq_get_byname(dev_fwnode(&spi->dev),
"rdy");
if (sigma_delta->irq_line < 0)
return dev_err_probe(&spi->dev, sigma_delta->irq_line,
"Interrupt 'rdy' is required\n");
} else {
sigma_delta->irq_line = spi->irq;
}
sigma_delta->rdy_gpiod = devm_gpiod_get_optional(&spi->dev, "rdy", GPIOD_IN);
if (IS_ERR(sigma_delta->rdy_gpiod))

View File

@ -12,9 +12,9 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
@ -27,6 +27,7 @@
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include "ad7606_bus_iface.h"
/*
* Register definitions:
* https://wiki.analog.com/resources/fpga/docs/axi_adc_ip#register_map
@ -39,9 +40,19 @@
#define ADI_AXI_REG_RSTN_MMCM_RSTN BIT(1)
#define ADI_AXI_REG_RSTN_RSTN BIT(0)
#define ADI_AXI_ADC_REG_CONFIG 0x000c
#define ADI_AXI_ADC_REG_CONFIG_CMOS_OR_LVDS_N BIT(7)
#define ADI_AXI_ADC_REG_CTRL 0x0044
#define ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK BIT(1)
#define ADI_AXI_ADC_REG_CNTRL_3 0x004c
#define AXI_AD485X_CNTRL_3_OS_EN_MSK BIT(2)
#define AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK GENMASK(1, 0)
#define AXI_AD485X_PACKET_FORMAT_20BIT 0x0
#define AXI_AD485X_PACKET_FORMAT_24BIT 0x1
#define AXI_AD485X_PACKET_FORMAT_32BIT 0x2
#define ADI_AXI_ADC_REG_DRP_STATUS 0x0074
#define ADI_AXI_ADC_DRP_LOCKED BIT(17)
@ -73,6 +84,12 @@
#define ADI_AXI_ADC_REG_DELAY(l) (0x0800 + (l) * 0x4)
#define AXI_ADC_DELAY_CTRL_MASK GENMASK(4, 0)
#define ADI_AXI_REG_CONFIG_WR 0x0080
#define ADI_AXI_REG_CONFIG_RD 0x0084
#define ADI_AXI_REG_CONFIG_CTRL 0x008c
#define ADI_AXI_REG_CONFIG_CTRL_READ 0x03
#define ADI_AXI_REG_CONFIG_CTRL_WRITE 0x01
#define ADI_AXI_ADC_MAX_IO_NUM_LANES 15
#define ADI_AXI_REG_CHAN_CTRL_DEFAULTS \
@ -80,7 +97,20 @@
ADI_AXI_REG_CHAN_CTRL_FMT_EN | \
ADI_AXI_REG_CHAN_CTRL_ENABLE)
#define ADI_AXI_REG_READ_BIT 0x8000
#define ADI_AXI_REG_ADDRESS_MASK 0xff00
#define ADI_AXI_REG_VALUE_MASK 0x00ff
struct axi_adc_info {
unsigned int version;
const struct iio_backend_info *backend_info;
bool has_child_nodes;
const void *pdata;
unsigned int pdata_sz;
};
struct adi_axi_adc_state {
const struct axi_adc_info *info;
struct regmap *regmap;
struct device *dev;
/* lock to protect multiple accesses to the device registers */
@ -290,6 +320,88 @@ static int axi_adc_chan_disable(struct iio_backend *back, unsigned int chan)
ADI_AXI_REG_CHAN_CTRL_ENABLE);
}
static int axi_adc_interface_type_get(struct iio_backend *back,
enum iio_backend_interface_type *type)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
unsigned int val;
int ret;
ret = regmap_read(st->regmap, ADI_AXI_ADC_REG_CONFIG, &val);
if (ret)
return ret;
if (val & ADI_AXI_ADC_REG_CONFIG_CMOS_OR_LVDS_N)
*type = IIO_BACKEND_INTERFACE_SERIAL_CMOS;
else
*type = IIO_BACKEND_INTERFACE_SERIAL_LVDS;
return 0;
}
static int axi_adc_ad485x_data_size_set(struct iio_backend *back,
unsigned int size)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
unsigned int val;
switch (size) {
/*
* There are two different variants of the AXI AXI_AD485X IP block, a
* 16-bit and a 20-bit variant.
* The 0x0 value (AXI_AD485X_PACKET_FORMAT_20BIT) is corresponding also
* to the 16-bit variant of the IP block.
*/
case 16:
case 20:
val = AXI_AD485X_PACKET_FORMAT_20BIT;
break;
case 24:
val = AXI_AD485X_PACKET_FORMAT_24BIT;
break;
/*
* The 0x2 (AXI_AD485X_PACKET_FORMAT_32BIT) corresponds only to the
* 20-bit variant of the IP block. Setting this value properly is
* ensured by the upper layers of the drivers calling the axi-adc
* functions.
* Also, for 16-bit IP block, the 0x2 (AXI_AD485X_PACKET_FORMAT_32BIT)
* value is handled as maximum size available which is 24-bit for this
* configuration.
*/
case 32:
val = AXI_AD485X_PACKET_FORMAT_32BIT;
break;
default:
return -EINVAL;
}
return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3,
AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK,
FIELD_PREP(AXI_AD485X_CNTRL_3_PACKET_FORMAT_MSK, val));
}
static int axi_adc_ad485x_oversampling_ratio_set(struct iio_backend *back,
unsigned int ratio)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
/* The current state of the function enables or disables the
* oversampling in REG_CNTRL_3 register. A ratio equal to 1 implies no
* oversampling, while a value greater than 1 implies oversampling being
* enabled.
*/
switch (ratio) {
case 0:
return -EINVAL;
case 1:
return regmap_clear_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3,
AXI_AD485X_CNTRL_3_OS_EN_MSK);
default:
return regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3,
AXI_AD485X_CNTRL_3_OS_EN_MSK);
}
}
static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back,
struct iio_dev *indio_dev)
{
@ -302,10 +414,79 @@ static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back,
return iio_dmaengine_buffer_setup(st->dev, indio_dev, dma_name);
}
static int axi_adc_raw_write(struct iio_backend *back, u32 val)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
regmap_write(st->regmap, ADI_AXI_REG_CONFIG_WR, val);
regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL,
ADI_AXI_REG_CONFIG_CTRL_WRITE);
fsleep(100);
regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL, 0x00);
fsleep(100);
return 0;
}
static int axi_adc_raw_read(struct iio_backend *back, u32 *val)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL,
ADI_AXI_REG_CONFIG_CTRL_READ);
fsleep(100);
regmap_read(st->regmap, ADI_AXI_REG_CONFIG_RD, val);
regmap_write(st->regmap, ADI_AXI_REG_CONFIG_CTRL, 0x00);
fsleep(100);
return 0;
}
static int ad7606_bus_reg_read(struct iio_backend *back, u32 reg, u32 *val)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
int addr;
guard(mutex)(&st->lock);
/*
* The address is written on the highest weight byte, and the MSB set
* at 1 indicates a read operation.
*/
addr = FIELD_PREP(ADI_AXI_REG_ADDRESS_MASK, reg) | ADI_AXI_REG_READ_BIT;
axi_adc_raw_write(back, addr);
axi_adc_raw_read(back, val);
/* Write 0x0 on the bus to get back to ADC mode */
axi_adc_raw_write(back, 0);
return 0;
}
static int ad7606_bus_reg_write(struct iio_backend *back, u32 reg, u32 val)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
u32 buf;
guard(mutex)(&st->lock);
/* Write any register to switch to register mode */
axi_adc_raw_write(back, 0xaf00);
buf = FIELD_PREP(ADI_AXI_REG_ADDRESS_MASK, reg) |
FIELD_PREP(ADI_AXI_REG_VALUE_MASK, val);
axi_adc_raw_write(back, buf);
/* Write 0x0 on the bus to get back to ADC mode */
axi_adc_raw_write(back, 0);
return 0;
}
static void axi_adc_free_buffer(struct iio_backend *back,
struct iio_buffer *buffer)
{
iio_dmaengine_buffer_free(buffer);
iio_dmaengine_buffer_teardown(buffer);
}
static int axi_adc_reg_access(struct iio_backend *back, unsigned int reg,
@ -325,6 +506,36 @@ static const struct regmap_config axi_adc_regmap_config = {
.reg_stride = 4,
};
static void axi_adc_child_remove(void *data)
{
platform_device_unregister(data);
}
static int axi_adc_create_platform_device(struct adi_axi_adc_state *st,
struct fwnode_handle *child)
{
struct platform_device_info pi = {
.parent = st->dev,
.name = fwnode_get_name(child),
.id = PLATFORM_DEVID_AUTO,
.fwnode = child,
.data = st->info->pdata,
.size_data = st->info->pdata_sz,
};
struct platform_device *pdev;
int ret;
pdev = platform_device_register_full(&pi);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
ret = devm_add_action_or_reset(st->dev, axi_adc_child_remove, pdev);
if (ret)
return ret;
return 0;
}
static const struct iio_backend_ops adi_axi_adc_ops = {
.enable = axi_adc_enable,
.disable = axi_adc_disable,
@ -337,6 +548,7 @@ static const struct iio_backend_ops adi_axi_adc_ops = {
.iodelay_set = axi_adc_iodelays_set,
.test_pattern_set = axi_adc_test_pattern_set,
.chan_status = axi_adc_chan_status,
.interface_type_get = axi_adc_interface_type_get,
.debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access),
.debugfs_print_chan_status = iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status),
};
@ -346,9 +558,32 @@ static const struct iio_backend_info adi_axi_adc_generic = {
.ops = &adi_axi_adc_ops,
};
static const struct iio_backend_ops adi_ad485x_ops = {
.enable = axi_adc_enable,
.disable = axi_adc_disable,
.data_format_set = axi_adc_data_format_set,
.chan_enable = axi_adc_chan_enable,
.chan_disable = axi_adc_chan_disable,
.request_buffer = axi_adc_request_buffer,
.free_buffer = axi_adc_free_buffer,
.data_sample_trigger = axi_adc_data_sample_trigger,
.iodelay_set = axi_adc_iodelays_set,
.chan_status = axi_adc_chan_status,
.interface_type_get = axi_adc_interface_type_get,
.data_size_set = axi_adc_ad485x_data_size_set,
.oversampling_ratio_set = axi_adc_ad485x_oversampling_ratio_set,
.debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access),
.debugfs_print_chan_status =
iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status),
};
static const struct iio_backend_info axi_ad485x = {
.name = "axi-ad485x",
.ops = &adi_ad485x_ops,
};
static int adi_axi_adc_probe(struct platform_device *pdev)
{
const unsigned int *expected_ver;
struct adi_axi_adc_state *st;
void __iomem *base;
unsigned int ver;
@ -370,8 +605,8 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, PTR_ERR(st->regmap),
"failed to init register map\n");
expected_ver = device_get_match_data(&pdev->dev);
if (!expected_ver)
st->info = device_get_match_data(&pdev->dev);
if (!st->info)
return -ENODEV;
clk = devm_clk_get_enabled(&pdev->dev, NULL);
@ -391,23 +626,46 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
if (ret)
return ret;
if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) {
if (ADI_AXI_PCORE_VER_MAJOR(ver) !=
ADI_AXI_PCORE_VER_MAJOR(st->info->version)) {
dev_err(&pdev->dev,
"Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
ADI_AXI_PCORE_VER_MAJOR(*expected_ver),
ADI_AXI_PCORE_VER_MINOR(*expected_ver),
ADI_AXI_PCORE_VER_PATCH(*expected_ver),
ADI_AXI_PCORE_VER_MAJOR(st->info->version),
ADI_AXI_PCORE_VER_MINOR(st->info->version),
ADI_AXI_PCORE_VER_PATCH(st->info->version),
ADI_AXI_PCORE_VER_MAJOR(ver),
ADI_AXI_PCORE_VER_MINOR(ver),
ADI_AXI_PCORE_VER_PATCH(ver));
return -ENODEV;
}
ret = devm_iio_backend_register(&pdev->dev, &adi_axi_adc_generic, st);
ret = devm_iio_backend_register(&pdev->dev, st->info->backend_info, st);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"failed to register iio backend\n");
device_for_each_child_node_scoped(&pdev->dev, child) {
int val;
if (!st->info->has_child_nodes)
return dev_err_probe(&pdev->dev, -EINVAL,
"invalid fdt axi-dac compatible.");
/* Processing only reg 0 node */
ret = fwnode_property_read_u32(child, "reg", &val);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"invalid reg property.");
if (val != 0)
return dev_err_probe(&pdev->dev, -EINVAL,
"invalid node address.");
ret = axi_adc_create_platform_device(st, child);
if (ret)
return dev_err_probe(&pdev->dev, -EINVAL,
"cannot create device.");
}
dev_info(&pdev->dev, "AXI ADC IP core (%d.%.2d.%c) probed\n",
ADI_AXI_PCORE_VER_MAJOR(ver),
ADI_AXI_PCORE_VER_MINOR(ver),
@ -416,11 +674,34 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
return 0;
}
static unsigned int adi_axi_adc_10_0_a_info = ADI_AXI_PCORE_VER(10, 0, 'a');
static const struct axi_adc_info adc_generic = {
.version = ADI_AXI_PCORE_VER(10, 0, 'a'),
.backend_info = &adi_axi_adc_generic,
};
static const struct axi_adc_info adi_axi_ad485x = {
.version = ADI_AXI_PCORE_VER(10, 0, 'a'),
.backend_info = &axi_ad485x,
};
static const struct ad7606_platform_data ad7606_pdata = {
.bus_reg_read = ad7606_bus_reg_read,
.bus_reg_write = ad7606_bus_reg_write,
};
static const struct axi_adc_info adc_ad7606 = {
.version = ADI_AXI_PCORE_VER(10, 0, 'a'),
.backend_info = &adi_axi_adc_generic,
.pdata = &ad7606_pdata,
.pdata_sz = sizeof(ad7606_pdata),
.has_child_nodes = true,
};
/* Match table for of_platform binding */
static const struct of_device_id adi_axi_adc_of_match[] = {
{ .compatible = "adi,axi-adc-10.0.a", .data = &adi_axi_adc_10_0_a_info },
{ .compatible = "adi,axi-adc-10.0.a", .data = &adc_generic },
{ .compatible = "adi,axi-ad485x", .data = &adi_axi_ad485x },
{ .compatible = "adi,axi-ad7606x", .data = &adc_ad7606 },
{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, adi_axi_adc_of_match);

View File

@ -9,6 +9,7 @@
*/
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
@ -1826,19 +1827,10 @@ static int at91_adc_read_info_locked(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val)
{
struct at91_adc_state *st = iio_priv(indio_dev);
int ret;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
guard(mutex)(&st->lock);
mutex_lock(&st->lock);
ret = at91_adc_read_info_raw(indio_dev, chan, val);
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
return ret;
return at91_adc_read_info_raw(indio_dev, chan, val);
}
static void at91_adc_temp_sensor_configure(struct at91_adc_state *st,
@ -1883,14 +1875,11 @@ static int at91_adc_read_temp(struct iio_dev *indio_dev,
u32 tmp;
int ret, vbg, vtemp;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
ret = pm_runtime_resume_and_get(st->dev);
if (ret < 0)
goto unlock;
return ret;
at91_adc_temp_sensor_configure(st, true);
@ -1912,9 +1901,6 @@ restore_config:
at91_adc_temp_sensor_configure(st, false);
pm_runtime_mark_last_busy(st->dev);
pm_runtime_put_autosuspend(st->dev);
unlock:
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
if (ret < 0)
return ret;
@ -1936,10 +1922,16 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long mask)
{
struct at91_adc_state *st = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
return at91_adc_read_info_locked(indio_dev, chan, val);
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = at91_adc_read_info_locked(indio_dev, chan, val);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
*val = st->vref_uv / 1000;
@ -1951,7 +1943,13 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_PROCESSED:
if (chan->type != IIO_TEMP)
return -EINVAL;
return at91_adc_read_temp(indio_dev, chan, val);
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = at91_adc_read_temp(indio_dev, chan, val);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = at91_adc_get_sample_freq(st);
@ -1979,28 +1977,26 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev,
if (val == st->oversampling_ratio)
return 0;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
mutex_lock(&st->lock);
/* update ratio */
ret = at91_adc_config_emr(st, val, 0);
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
if (val < st->soc_info.min_sample_rate ||
val > st->soc_info.max_sample_rate)
return -EINVAL;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
mutex_lock(&st->lock);
at91_adc_setup_samp_freq(indio_dev, val,
st->soc_info.startup_time, 0);
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return 0;
default:
return -EINVAL;

View File

@ -314,15 +314,14 @@ static int dln2_adc_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret < 0)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
mutex_lock(&dln2->mutex);
ret = dln2_adc_read(dln2, chan->channel);
mutex_unlock(&dln2->mutex);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;

View File

@ -336,10 +336,6 @@ static int max1027_read_single_value(struct iio_dev *indio_dev,
int ret;
struct max1027_state *st = iio_priv(indio_dev);
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
/* Configure conversion register with the requested chan */
st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) |
MAX1027_NOSCAN;
@ -349,7 +345,7 @@ static int max1027_read_single_value(struct iio_dev *indio_dev,
if (ret < 0) {
dev_err(&indio_dev->dev,
"Failed to configure conversion register\n");
goto release;
return ret;
}
/*
@ -359,14 +355,10 @@ static int max1027_read_single_value(struct iio_dev *indio_dev,
*/
ret = max1027_wait_eoc(indio_dev);
if (ret)
goto release;
return ret;
/* Read result */
ret = spi_read(st->spi, st->buffer, (chan->type == IIO_TEMP) ? 4 : 2);
release:
iio_device_release_direct_mode(indio_dev);
if (ret < 0)
return ret;
@ -382,37 +374,32 @@ static int max1027_read_raw(struct iio_dev *indio_dev,
int ret = 0;
struct max1027_state *st = iio_priv(indio_dev);
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = max1027_read_single_value(indio_dev, chan, val);
break;
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_TEMP:
*val = 1;
*val2 = 8;
ret = IIO_VAL_FRACTIONAL;
break;
return IIO_VAL_FRACTIONAL;
case IIO_VOLTAGE:
*val = 2500;
*val2 = chan->scan_type.realbits;
ret = IIO_VAL_FRACTIONAL_LOG2;
break;
return IIO_VAL_FRACTIONAL_LOG2;
default:
ret = -EINVAL;
break;
return -EINVAL;
}
break;
default:
ret = -EINVAL;
break;
return -EINVAL;
}
mutex_unlock(&st->lock);
return ret;
}
static int max1027_debugfs_reg_access(struct iio_dev *indio_dev,

View File

@ -471,9 +471,8 @@ static int max11410_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
mutex_lock(&state->lock);
@ -481,7 +480,7 @@ static int max11410_read_raw(struct iio_dev *indio_dev,
mutex_unlock(&state->lock);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret)
return ret;
@ -507,12 +506,37 @@ static int max11410_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
static int __max11410_write_samp_freq(struct max11410_state *st,
int val, int val2)
{
int ret, i, reg_val, filter;
guard(mutex)(&st->lock);
ret = regmap_read(st->regmap, MAX11410_REG_FILTER, &reg_val);
if (ret)
return ret;
filter = FIELD_GET(MAX11410_FILTER_LINEF_MASK, reg_val);
for (i = 0; i < max11410_sampling_len[filter]; ++i) {
if (val == max11410_sampling_rates[filter][i][0] &&
val2 == max11410_sampling_rates[filter][i][1])
break;
}
if (i == max11410_sampling_len[filter])
return -EINVAL;
return regmap_write_bits(st->regmap, MAX11410_REG_FILTER,
MAX11410_FILTER_RATE_MASK, i);
}
static int max11410_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct max11410_state *st = iio_priv(indio_dev);
int i, ret, reg_val, filter, gain;
int ret, gain;
u32 *scale_avail;
switch (mask) {
@ -525,9 +549,8 @@ static int max11410_write_raw(struct iio_dev *indio_dev,
if (val != 0 || val2 == 0)
return -EINVAL;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
/* Convert from INT_PLUS_MICRO to FRACTIONAL_LOG2 */
val2 = val2 * DIV_ROUND_CLOSEST(BIT(24), 1000000);
@ -536,38 +559,15 @@ static int max11410_write_raw(struct iio_dev *indio_dev,
st->channels[chan->address].gain = clamp_val(gain, 0, 7);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return 0;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
mutex_lock(&st->lock);
ret = regmap_read(st->regmap, MAX11410_REG_FILTER, &reg_val);
if (ret)
goto out;
filter = FIELD_GET(MAX11410_FILTER_LINEF_MASK, reg_val);
for (i = 0; i < max11410_sampling_len[filter]; ++i) {
if (val == max11410_sampling_rates[filter][i][0] &&
val2 == max11410_sampling_rates[filter][i][1])
break;
}
if (i == max11410_sampling_len[filter]) {
ret = -EINVAL;
goto out;
}
ret = regmap_write_bits(st->regmap, MAX11410_REG_FILTER,
MAX11410_FILTER_RATE_MASK, i);
out:
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
ret = __max11410_write_samp_freq(st, val, val2);
iio_device_release_direct(indio_dev);
return ret;
default:

View File

@ -364,55 +364,52 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev,
int *val,
long m)
{
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
s32 data;
u8 rxbuf[2];
struct max1363_state *st = iio_priv(indio_dev);
struct i2c_client *client = st->client;
s32 data;
u8 rxbuf[2];
struct max1363_state *st = iio_priv(indio_dev);
struct i2c_client *client = st->client;
guard(mutex)(&st->lock);
guard(mutex)(&st->lock);
/*
* If monitor mode is enabled, the method for reading a single
* channel will have to be rather different and has not yet
* been implemented.
*
* Also, cannot read directly if buffered capture enabled.
*/
if (st->monitor_on)
return -EBUSY;
/*
* If monitor mode is enabled, the method for reading a single
* channel will have to be rather different and has not yet
* been implemented.
*
* Also, cannot read directly if buffered capture enabled.
*/
if (st->monitor_on)
return -EBUSY;
/* Check to see if current scan mode is correct */
if (st->current_mode != &max1363_mode_table[chan->address]) {
int ret;
/* Check to see if current scan mode is correct */
if (st->current_mode != &max1363_mode_table[chan->address]) {
int ret;
/* Update scan mode if needed */
st->current_mode = &max1363_mode_table[chan->address];
ret = max1363_set_scan_mode(st);
if (ret < 0)
return ret;
}
if (st->chip_info->bits != 8) {
/* Get reading */
data = st->recv(client, rxbuf, 2);
if (data < 0)
return data;
data = get_unaligned_be16(rxbuf) &
((1 << st->chip_info->bits) - 1);
} else {
/* Get reading */
data = st->recv(client, rxbuf, 1);
if (data < 0)
return data;
data = rxbuf[0];
}
*val = data;
return 0;
/* Update scan mode if needed */
st->current_mode = &max1363_mode_table[chan->address];
ret = max1363_set_scan_mode(st);
if (ret < 0)
return ret;
}
unreachable();
if (st->chip_info->bits != 8) {
/* Get reading */
data = st->recv(client, rxbuf, 2);
if (data < 0)
return data;
data = get_unaligned_be16(rxbuf) &
((1 << st->chip_info->bits) - 1);
} else {
/* Get reading */
data = st->recv(client, rxbuf, 1);
if (data < 0)
return data;
data = rxbuf[0];
}
*val = data;
return 0;
}
static int max1363_read_raw(struct iio_dev *indio_dev,
@ -426,7 +423,11 @@ static int max1363_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = max1363_read_single_chan(indio_dev, chan, val, m);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
return IIO_VAL_INT;
@ -947,46 +948,58 @@ error_ret:
return ret;
}
static int __max1363_write_event_config(struct max1363_state *st,
const struct iio_chan_spec *chan,
enum iio_event_direction dir, bool state)
{
int number = chan->channel;
u16 unifiedmask;
int ret;
guard(mutex)(&st->lock);
unifiedmask = st->mask_low | st->mask_high;
if (dir == IIO_EV_DIR_FALLING) {
if (state == 0)
st->mask_low &= ~(1 << number);
else {
ret = __max1363_check_event_mask((1 << number),
unifiedmask);
if (ret)
return ret;
st->mask_low |= (1 << number);
}
} else {
if (state == 0)
st->mask_high &= ~(1 << number);
else {
ret = __max1363_check_event_mask((1 << number),
unifiedmask);
if (ret)
return ret;
st->mask_high |= (1 << number);
}
}
return 0;
}
static int max1363_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, enum iio_event_type type,
enum iio_event_direction dir, bool state)
{
struct max1363_state *st = iio_priv(indio_dev);
int ret;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
int number = chan->channel;
u16 unifiedmask;
int ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
guard(mutex)(&st->lock);
unifiedmask = st->mask_low | st->mask_high;
if (dir == IIO_EV_DIR_FALLING) {
if (state == 0)
st->mask_low &= ~(1 << number);
else {
ret = __max1363_check_event_mask((1 << number),
unifiedmask);
if (ret)
return ret;
st->mask_low |= (1 << number);
}
} else {
if (state == 0)
st->mask_high &= ~(1 << number);
else {
ret = __max1363_check_event_mask((1 << number),
unifiedmask);
if (ret)
return ret;
st->mask_high |= (1 << number);
}
}
}
ret = __max1363_write_event_config(st, chan, dir, state);
iio_device_release_direct(indio_dev);
max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low));
return 0;
return ret;
}
/*

View File

@ -8,6 +8,7 @@
*/
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/module.h>

View File

@ -7,6 +7,7 @@
#include <linux/unaligned.h>
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/i2c.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Rockchip Successive Approximation Register (SAR) A/D Converter
* Copyright (C) 2014 ROCKCHIP, Inc.
* Copyright (C) 2014 Rockchip Electronics Co., Ltd.
*/
#include <linux/bitfield.h>
@ -275,6 +275,40 @@ static const struct rockchip_saradc_data rk3399_saradc_data = {
.power_down = rockchip_saradc_power_down_v1,
};
static const struct iio_chan_spec rockchip_rk3528_saradc_iio_channels[] = {
SARADC_CHANNEL(0, "adc0", 10),
SARADC_CHANNEL(1, "adc1", 10),
SARADC_CHANNEL(2, "adc2", 10),
SARADC_CHANNEL(3, "adc3", 10),
};
static const struct rockchip_saradc_data rk3528_saradc_data = {
.channels = rockchip_rk3528_saradc_iio_channels,
.num_channels = ARRAY_SIZE(rockchip_rk3528_saradc_iio_channels),
.clk_rate = 1000000,
.start = rockchip_saradc_start_v2,
.read = rockchip_saradc_read_v2,
};
static const struct iio_chan_spec rockchip_rk3562_saradc_iio_channels[] = {
SARADC_CHANNEL(0, "adc0", 10),
SARADC_CHANNEL(1, "adc1", 10),
SARADC_CHANNEL(2, "adc2", 10),
SARADC_CHANNEL(3, "adc3", 10),
SARADC_CHANNEL(4, "adc4", 10),
SARADC_CHANNEL(5, "adc5", 10),
SARADC_CHANNEL(6, "adc6", 10),
SARADC_CHANNEL(7, "adc7", 10),
};
static const struct rockchip_saradc_data rk3562_saradc_data = {
.channels = rockchip_rk3562_saradc_iio_channels,
.num_channels = ARRAY_SIZE(rockchip_rk3562_saradc_iio_channels),
.clk_rate = 1000000,
.start = rockchip_saradc_start_v2,
.read = rockchip_saradc_read_v2,
};
static const struct iio_chan_spec rockchip_rk3568_saradc_iio_channels[] = {
SARADC_CHANNEL(0, "adc0", 10),
SARADC_CHANNEL(1, "adc1", 10),
@ -324,6 +358,12 @@ static const struct of_device_id rockchip_saradc_match[] = {
}, {
.compatible = "rockchip,rk3399-saradc",
.data = &rk3399_saradc_data,
}, {
.compatible = "rockchip,rk3528-saradc",
.data = &rk3528_saradc_data,
}, {
.compatible = "rockchip,rk3562-saradc",
.data = &rk3562_saradc_data,
}, {
.compatible = "rockchip,rk3568-saradc",
.data = &rk3568_saradc_data,

View File

@ -514,26 +514,37 @@ static int rtq6056_adc_read_avail(struct iio_dev *indio_dev,
}
}
static int rtq6056_adc_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
static int __rtq6056_adc_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
long mask)
{
struct rtq6056_priv *priv = iio_priv(indio_dev);
const struct richtek_dev_data *devdata = priv->devdata;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
if (devdata->fixed_samp_freq)
return -EINVAL;
return rtq6056_adc_set_samp_freq(priv, chan, val);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
return devdata->set_average(priv, val);
default:
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
if (devdata->fixed_samp_freq)
return -EINVAL;
}
return rtq6056_adc_set_samp_freq(priv, chan, val);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
return devdata->set_average(priv, val);
default:
return -EINVAL;
}
unreachable();
}
static int rtq6056_adc_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
int ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = __rtq6056_adc_write_raw(indio_dev, chan, val, mask);
iio_device_release_direct(indio_dev);
return ret;
}
static const char *rtq6056_channel_labels[RTQ6056_MAX_CHANNEL] = {
@ -590,9 +601,8 @@ static ssize_t shunt_resistor_store(struct device *dev,
struct rtq6056_priv *priv = iio_priv(indio_dev);
int val, val_fract, ret;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = iio_str_to_fixpoint(buf, 100000, &val, &val_fract);
if (ret)
@ -601,7 +611,7 @@ static ssize_t shunt_resistor_store(struct device *dev,
ret = rtq6056_set_shunt_resistor(priv, val * 1000000 + val_fract);
out_store:
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return ret ?: len;
}

View File

@ -615,8 +615,7 @@ static int stm32_adc_core_switches_probe(struct device *dev,
}
/* Booster can be used to supply analog switches (optional) */
if (priv->cfg->has_syscfg & HAS_VBOOSTER &&
of_property_read_bool(np, "booster-supply")) {
if (priv->cfg->has_syscfg & HAS_VBOOSTER) {
priv->booster = devm_regulator_get_optional(dev, "booster");
if (IS_ERR(priv->booster)) {
ret = PTR_ERR(priv->booster);
@ -628,8 +627,7 @@ static int stm32_adc_core_switches_probe(struct device *dev,
}
/* Vdd can be used to supply analog switches (optional) */
if (priv->cfg->has_syscfg & HAS_ANASWVDD &&
of_property_read_bool(np, "vdd-supply")) {
if (priv->cfg->has_syscfg & HAS_ANASWVDD) {
priv->vdd = devm_regulator_get_optional(dev, "vdd");
if (IS_ERR(priv->vdd)) {
ret = PTR_ERR(priv->vdd);

View File

@ -1471,9 +1471,8 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
case IIO_CHAN_INFO_PROCESSED:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
if (chan->type == IIO_VOLTAGE)
ret = stm32_adc_single_conv(indio_dev, chan, val);
else
@ -1482,7 +1481,7 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
if (mask == IIO_CHAN_INFO_PROCESSED)
*val = STM32_ADC_VREFINT_VOLTAGE * adc->vrefint.vrefint_cal / *val;
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:

View File

@ -1275,9 +1275,8 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = stm32_dfsdm_compute_all_osrs(indio_dev, val);
if (!ret) {
@ -1287,25 +1286,56 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
adc->oversamp = val;
adc->sample_freq = spi_freq / val;
}
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
if (!val)
return -EINVAL;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = dfsdm_adc_set_samp_freq(indio_dev, val, spi_freq);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return ret;
}
return -EINVAL;
}
static int __stm32_dfsdm_read_info_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
int ret = 0;
if (adc->hwc)
ret = iio_hw_consumer_enable(adc->hwc);
if (adc->backend)
ret = iio_backend_enable(adc->backend[chan->scan_index]);
if (ret < 0) {
dev_err(&indio_dev->dev,
"%s: IIO enable failed (channel %d)\n",
__func__, chan->channel);
return ret;
}
ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
if (adc->hwc)
iio_hw_consumer_disable(adc->hwc);
if (adc->backend)
iio_backend_disable(adc->backend[chan->scan_index]);
if (ret < 0) {
dev_err(&indio_dev->dev,
"%s: Conversion failed (channel %d)\n",
__func__, chan->channel);
return ret;
}
return 0;
}
static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
@ -1323,33 +1353,13 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = __stm32_dfsdm_read_info_raw(indio_dev, chan, val);
iio_device_release_direct(indio_dev);
if (ret)
return ret;
if (adc->hwc)
ret = iio_hw_consumer_enable(adc->hwc);
if (adc->backend)
ret = iio_backend_enable(adc->backend[idx]);
if (ret < 0) {
dev_err(&indio_dev->dev,
"%s: IIO enable failed (channel %d)\n",
__func__, chan->channel);
iio_device_release_direct_mode(indio_dev);
return ret;
}
ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
if (adc->hwc)
iio_hw_consumer_disable(adc->hwc);
if (adc->backend)
iio_backend_disable(adc->backend[idx]);
if (ret < 0) {
dev_err(&indio_dev->dev,
"%s: Conversion failed (channel %d)\n",
__func__, chan->channel);
iio_device_release_direct_mode(indio_dev);
return ret;
}
iio_device_release_direct_mode(indio_dev);
return IIO_VAL_INT;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:

View File

@ -96,19 +96,18 @@ static int adc084s021_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret < 0)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = regulator_enable(adc->reg);
if (ret) {
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return ret;
}
adc->tx_buf[0] = channel->channel << 3;
ret = adc084s021_adc_conversion(adc, &be_val);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
regulator_disable(adc->reg);
if (ret < 0)
return ret;

View File

@ -181,13 +181,12 @@ static int adc108s102_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = adc108s102_scan_direct(st, chan->address);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;

View File

@ -137,13 +137,13 @@ static int ti_adc_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
ret = ti_adc_read_measurement(data, chan, val);
if (ret)
return ret;
return IIO_VAL_INT;
}
unreachable();
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ti_adc_read_measurement(data, chan, val);
iio_device_release_direct(indio_dev);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
ret = regulator_get_voltage(data->ref);
if (ret < 0)

View File

@ -336,19 +336,24 @@ static int ads1119_read_raw(struct iio_dev *indio_dev,
{
struct ads1119_state *st = iio_priv(indio_dev);
unsigned int index = chan->address;
int ret;
if (index >= st->num_channels_cfg)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_RAW:
iio_device_claim_direct_scoped(return -EBUSY, indio_dev)
return ads1119_single_conversion(st, chan, val, false);
unreachable();
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ads1119_single_conversion(st, chan, val, false);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_OFFSET:
iio_device_claim_direct_scoped(return -EBUSY, indio_dev)
return ads1119_single_conversion(st, chan, val, true);
unreachable();
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ads1119_single_conversion(st, chan, val, true);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE:
*val = st->vref_uV / 1000;
*val /= st->channels_cfg[index].gain;

View File

@ -184,7 +184,7 @@ static int ads124s_reset(struct iio_dev *indio_dev)
if (priv->reset_gpio) {
gpiod_set_value_cansleep(priv->reset_gpio, 0);
udelay(200);
fsleep(200);
gpiod_set_value_cansleep(priv->reset_gpio, 1);
} else {
return ads124s_write_cmd(indio_dev, ADS124S08_CMD_RESET);

View File

@ -319,13 +319,12 @@ static int ads1298_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ads1298_read_one(priv, chan->scan_index);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret)
return ret;

View File

@ -505,12 +505,11 @@ static int ads131e08_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ads131e08_read_direct(indio_dev, channel, value);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret)
return ret;
@ -551,12 +550,11 @@ static int ads131e08_write_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ads131e08_set_data_rate(st, value);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
return ret;
default:

View File

@ -0,0 +1,749 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADS7138 - Texas Instruments Analog-to-Digital Converter
*/
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/unaligned.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/types.h>
/*
* Always assume 16 bits resolution as HW registers are aligned like that and
* with enabled oversampling/averaging it actually corresponds to 16 bits.
*/
#define ADS7138_RES_BITS 16
/* ADS7138 operation codes */
#define ADS7138_OPCODE_SINGLE_WRITE 0x08
#define ADS7138_OPCODE_SET_BIT 0x18
#define ADS7138_OPCODE_CLEAR_BIT 0x20
#define ADS7138_OPCODE_BLOCK_WRITE 0x28
#define ADS7138_OPCODE_BLOCK_READ 0x30
/* ADS7138 registers */
#define ADS7138_REG_GENERAL_CFG 0x01
#define ADS7138_REG_OSR_CFG 0x03
#define ADS7138_REG_OPMODE_CFG 0x04
#define ADS7138_REG_SEQUENCE_CFG 0x10
#define ADS7138_REG_AUTO_SEQ_CH_SEL 0x12
#define ADS7138_REG_ALERT_CH_SEL 0x14
#define ADS7138_REG_EVENT_FLAG 0x18
#define ADS7138_REG_EVENT_HIGH_FLAG 0x1A
#define ADS7138_REG_EVENT_LOW_FLAG 0x1C
#define ADS7138_REG_HIGH_TH_HYS_CH(x) ((x) * 4 + 0x20)
#define ADS7138_REG_LOW_TH_CNT_CH(x) ((x) * 4 + 0x22)
#define ADS7138_REG_MAX_LSB_CH(x) ((x) * 2 + 0x60)
#define ADS7138_REG_MIN_LSB_CH(x) ((x) * 2 + 0x80)
#define ADS7138_REG_RECENT_LSB_CH(x) ((x) * 2 + 0xA0)
#define ADS7138_GENERAL_CFG_RST BIT(0)
#define ADS7138_GENERAL_CFG_DWC_EN BIT(4)
#define ADS7138_GENERAL_CFG_STATS_EN BIT(5)
#define ADS7138_OSR_CFG_MASK GENMASK(2, 0)
#define ADS7138_OPMODE_CFG_CONV_MODE BIT(5)
#define ADS7138_OPMODE_CFG_FREQ_MASK GENMASK(4, 0)
#define ADS7138_SEQUENCE_CFG_SEQ_MODE BIT(0)
#define ADS7138_SEQUENCE_CFG_SEQ_START BIT(4)
#define ADS7138_THRESHOLD_LSB_MASK GENMASK(7, 4)
enum ads7138_modes {
ADS7138_MODE_MANUAL,
ADS7138_MODE_AUTO,
};
struct ads7138_chip_data {
const char *name;
const int channel_num;
};
struct ads7138_data {
/* Protects RMW access to the I2C interface */
struct mutex lock;
struct i2c_client *client;
struct regulator *vref_regu;
const struct ads7138_chip_data *chip_data;
};
/*
* 2D array of available sampling frequencies and the corresponding register
* values. Structured like this to be easily usable in read_avail function.
*/
static const int ads7138_samp_freqs_bits[2][26] = {
{
163, 244, 326, 488, 651, 977, 1302, 1953,
2604, 3906, 5208, 7813, 10417, 15625, 20833, 31250,
41667, 62500, 83333, 125000, 166667, 250000, 333333, 500000,
666667, 1000000
}, {
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
/* Here is a hole, due to duplicate frequencies */
0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
0x01, 0x00
}
};
static const int ads7138_oversampling_ratios[] = {
1, 2, 4, 8, 16, 32, 64, 128
};
static int ads7138_i2c_write_block(const struct i2c_client *client, u8 reg,
u8 *values, u8 length)
{
int ret;
int len = length + 2; /* "+ 2" for OPCODE and reg */
u8 *buf __free(kfree) = kmalloc(len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
buf[0] = ADS7138_OPCODE_BLOCK_WRITE;
buf[1] = reg;
memcpy(&buf[2], values, length);
ret = i2c_master_send(client, buf, len);
if (ret < 0)
return ret;
if (ret != len)
return -EIO;
return 0;
}
static int ads7138_i2c_write_with_opcode(const struct i2c_client *client,
u8 reg, u8 regval, u8 opcode)
{
u8 buf[3] = { opcode, reg, regval };
int ret;
ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
if (ret < 0)
return ret;
if (ret != ARRAY_SIZE(buf))
return -EIO;
return 0;
}
static int ads7138_i2c_write(const struct i2c_client *client, u8 reg, u8 value)
{
return ads7138_i2c_write_with_opcode(client, reg, value,
ADS7138_OPCODE_SINGLE_WRITE);
}
static int ads7138_i2c_set_bit(const struct i2c_client *client, u8 reg, u8 bits)
{
return ads7138_i2c_write_with_opcode(client, reg, bits,
ADS7138_OPCODE_SET_BIT);
}
static int ads7138_i2c_clear_bit(const struct i2c_client *client, u8 reg, u8 bits)
{
return ads7138_i2c_write_with_opcode(client, reg, bits,
ADS7138_OPCODE_CLEAR_BIT);
}
static int ads7138_i2c_read_block(const struct i2c_client *client, u8 reg,
u8 *out_values, u8 length)
{
u8 buf[2] = { ADS7138_OPCODE_BLOCK_READ, reg };
int ret;
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.len = ARRAY_SIZE(buf),
.buf = buf,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = length,
.buf = out_values,
},
};
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret < 0)
return ret;
if (ret != ARRAY_SIZE(msgs))
return -EIO;
return 0;
}
static int ads7138_i2c_read(const struct i2c_client *client, u8 reg)
{
u8 value;
int ret;
ret = ads7138_i2c_read_block(client, reg, &value, sizeof(value));
if (ret)
return ret;
return value;
}
static int ads7138_freq_to_bits(int freq)
{
int i;
for (i = 0; i < ARRAY_SIZE(ads7138_samp_freqs_bits[0]); i++)
if (freq == ads7138_samp_freqs_bits[0][i])
return ads7138_samp_freqs_bits[1][i];
return -EINVAL;
}
static int ads7138_bits_to_freq(int bits)
{
int i;
for (i = 0; i < ARRAY_SIZE(ads7138_samp_freqs_bits[1]); i++)
if (bits == ads7138_samp_freqs_bits[1][i])
return ads7138_samp_freqs_bits[0][i];
return -EINVAL;
}
static int ads7138_osr_to_bits(int osr)
{
int i;
for (i = 0; i < ARRAY_SIZE(ads7138_oversampling_ratios); i++)
if (osr == ads7138_oversampling_ratios[i])
return i;
return -EINVAL;
}
static int ads7138_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct ads7138_data *data = iio_priv(indio_dev);
int ret, vref, bits;
u8 values[2];
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = ads7138_i2c_read_block(data->client,
ADS7138_REG_RECENT_LSB_CH(chan->channel),
values, ARRAY_SIZE(values));
if (ret)
return ret;
*val = get_unaligned_le16(values);
return IIO_VAL_INT;
case IIO_CHAN_INFO_PEAK:
ret = ads7138_i2c_read_block(data->client,
ADS7138_REG_MAX_LSB_CH(chan->channel),
values, ARRAY_SIZE(values));
if (ret)
return ret;
*val = get_unaligned_le16(values);
return IIO_VAL_INT;
case IIO_CHAN_INFO_TROUGH:
ret = ads7138_i2c_read_block(data->client,
ADS7138_REG_MIN_LSB_CH(chan->channel),
values, ARRAY_SIZE(values));
if (ret)
return ret;
*val = get_unaligned_le16(values);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = ads7138_i2c_read(data->client, ADS7138_REG_OPMODE_CFG);
if (ret < 0)
return ret;
bits = FIELD_GET(ADS7138_OPMODE_CFG_FREQ_MASK, ret);
*val = ads7138_bits_to_freq(bits);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
vref = regulator_get_voltage(data->vref_regu);
if (vref < 0)
return vref;
*val = vref / 1000;
*val2 = ADS7138_RES_BITS;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
ret = ads7138_i2c_read(data->client, ADS7138_REG_OSR_CFG);
if (ret < 0)
return ret;
bits = FIELD_GET(ADS7138_OSR_CFG_MASK, ret);
*val = ads7138_oversampling_ratios[bits];
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int ads7138_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct ads7138_data *data = iio_priv(indio_dev);
int bits, ret;
u8 value;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ: {
bits = ads7138_freq_to_bits(val);
if (bits < 0)
return bits;
guard(mutex)(&data->lock);
ret = ads7138_i2c_read(data->client, ADS7138_REG_OPMODE_CFG);
if (ret < 0)
return ret;
value = ret & ~ADS7138_OPMODE_CFG_FREQ_MASK;
value |= FIELD_PREP(ADS7138_OPMODE_CFG_FREQ_MASK, bits);
return ads7138_i2c_write(data->client, ADS7138_REG_OPMODE_CFG,
value);
}
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
bits = ads7138_osr_to_bits(val);
if (bits < 0)
return bits;
return ads7138_i2c_write(data->client, ADS7138_REG_OSR_CFG,
bits);
default:
return -EINVAL;
}
}
static int ads7138_read_event(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 ads7138_data *data = iio_priv(indio_dev);
u8 reg, values[2];
int ret;
switch (info) {
case IIO_EV_INFO_VALUE:
reg = (dir == IIO_EV_DIR_RISING) ?
ADS7138_REG_HIGH_TH_HYS_CH(chan->channel) :
ADS7138_REG_LOW_TH_CNT_CH(chan->channel);
ret = ads7138_i2c_read_block(data->client, reg, values,
ARRAY_SIZE(values));
if (ret)
return ret;
*val = ((values[1] << 4) | (values[0] >> 4));
return IIO_VAL_INT;
case IIO_EV_INFO_HYSTERESIS:
ret = ads7138_i2c_read(data->client,
ADS7138_REG_HIGH_TH_HYS_CH(chan->channel));
if (ret < 0)
return ret;
*val = ret & ~ADS7138_THRESHOLD_LSB_MASK;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int ads7138_write_event(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 ads7138_data *data = iio_priv(indio_dev);
u8 reg, values[2];
int ret;
switch (info) {
case IIO_EV_INFO_VALUE: {
if (val >= BIT(12) || val < 0)
return -EINVAL;
reg = (dir == IIO_EV_DIR_RISING) ?
ADS7138_REG_HIGH_TH_HYS_CH(chan->channel) :
ADS7138_REG_LOW_TH_CNT_CH(chan->channel);
guard(mutex)(&data->lock);
ret = ads7138_i2c_read(data->client, reg);
if (ret < 0)
return ret;
values[0] = ret & ~ADS7138_THRESHOLD_LSB_MASK;
values[0] |= FIELD_PREP(ADS7138_THRESHOLD_LSB_MASK, val);
values[1] = (val >> 4);
return ads7138_i2c_write_block(data->client, reg, values,
ARRAY_SIZE(values));
}
case IIO_EV_INFO_HYSTERESIS: {
if (val >= BIT(4) || val < 0)
return -EINVAL;
reg = ADS7138_REG_HIGH_TH_HYS_CH(chan->channel);
guard(mutex)(&data->lock);
ret = ads7138_i2c_read(data->client, reg);
if (ret < 0)
return ret;
values[0] = val & ~ADS7138_THRESHOLD_LSB_MASK;
values[0] |= FIELD_PREP(ADS7138_THRESHOLD_LSB_MASK, ret >> 4);
return ads7138_i2c_write(data->client, reg, values[0]);
}
default:
return -EINVAL;
}
}
static int ads7138_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 ads7138_data *data = iio_priv(indio_dev);
int ret;
if (dir != IIO_EV_DIR_EITHER)
return -EINVAL;
ret = ads7138_i2c_read(data->client, ADS7138_REG_ALERT_CH_SEL);
if (ret < 0)
return ret;
return (ret & BIT(chan->channel)) ? 1 : 0;
}
static int ads7138_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir, bool state)
{
struct ads7138_data *data = iio_priv(indio_dev);
if (dir != IIO_EV_DIR_EITHER)
return -EINVAL;
if (state)
return ads7138_i2c_set_bit(data->client,
ADS7138_REG_ALERT_CH_SEL,
BIT(chan->channel));
else
return ads7138_i2c_clear_bit(data->client,
ADS7138_REG_ALERT_CH_SEL,
BIT(chan->channel));
}
static int ads7138_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_SAMP_FREQ:
*vals = ads7138_samp_freqs_bits[0];
*length = ARRAY_SIZE(ads7138_samp_freqs_bits[0]);
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*vals = ads7138_oversampling_ratios;
*length = ARRAY_SIZE(ads7138_oversampling_ratios);
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static const struct iio_info ti_ads7138_info = {
.read_raw = &ads7138_read_raw,
.read_avail = &ads7138_read_avail,
.write_raw = &ads7138_write_raw,
.read_event_value = &ads7138_read_event,
.write_event_value = &ads7138_write_event,
.read_event_config = &ads7138_read_event_config,
.write_event_config = &ads7138_write_event_config,
};
static const struct iio_event_spec ads7138_events[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_VALUE)
}, {
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_VALUE),
}, {
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_EITHER,
.mask_separate = BIT(IIO_EV_INFO_HYSTERESIS) |
BIT(IIO_EV_INFO_ENABLE),
},
};
#define ADS7138_V_CHAN(_chan) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = _chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_PEAK) | \
BIT(IIO_CHAN_INFO_TROUGH), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_shared_by_type_available = \
BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.datasheet_name = "AIN"#_chan, \
.event_spec = ads7138_events, \
.num_event_specs = ARRAY_SIZE(ads7138_events), \
}
static const struct iio_chan_spec ads7138_channels[] = {
ADS7138_V_CHAN(0),
ADS7138_V_CHAN(1),
ADS7138_V_CHAN(2),
ADS7138_V_CHAN(3),
ADS7138_V_CHAN(4),
ADS7138_V_CHAN(5),
ADS7138_V_CHAN(6),
ADS7138_V_CHAN(7),
};
static irqreturn_t ads7138_event_handler(int irq, void *priv)
{
struct iio_dev *indio_dev = priv;
struct ads7138_data *data = iio_priv(indio_dev);
struct device *dev = &data->client->dev;
u8 i, events_high, events_low;
u64 code;
int ret;
/* Check if interrupt was trigger by us */
ret = ads7138_i2c_read(data->client, ADS7138_REG_EVENT_FLAG);
if (ret <= 0)
return IRQ_NONE;
ret = ads7138_i2c_read(data->client, ADS7138_REG_EVENT_HIGH_FLAG);
if (ret < 0) {
dev_warn(dev, "Failed to read event high flags: %d\n", ret);
return IRQ_HANDLED;
}
events_high = ret;
ret = ads7138_i2c_read(data->client, ADS7138_REG_EVENT_LOW_FLAG);
if (ret < 0) {
dev_warn(dev, "Failed to read event low flags: %d\n", ret);
return IRQ_HANDLED;
}
events_low = ret;
for (i = 0; i < data->chip_data->channel_num; i++) {
if (events_high & BIT(i)) {
code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING);
iio_push_event(indio_dev, code,
iio_get_time_ns(indio_dev));
}
if (events_low & BIT(i)) {
code = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_FALLING);
iio_push_event(indio_dev, code,
iio_get_time_ns(indio_dev));
}
}
/* Try to clear all interrupt flags */
ret = ads7138_i2c_write(data->client, ADS7138_REG_EVENT_HIGH_FLAG, 0xFF);
if (ret)
dev_warn(dev, "Failed to clear event high flags: %d\n", ret);
ret = ads7138_i2c_write(data->client, ADS7138_REG_EVENT_LOW_FLAG, 0xFF);
if (ret)
dev_warn(dev, "Failed to clear event low flags: %d\n", ret);
return IRQ_HANDLED;
}
static int ads7138_set_conv_mode(struct ads7138_data *data,
enum ads7138_modes mode)
{
if (mode == ADS7138_MODE_AUTO)
return ads7138_i2c_set_bit(data->client, ADS7138_REG_OPMODE_CFG,
ADS7138_OPMODE_CFG_CONV_MODE);
return ads7138_i2c_clear_bit(data->client, ADS7138_REG_OPMODE_CFG,
ADS7138_OPMODE_CFG_CONV_MODE);
}
static int ads7138_init_hw(struct ads7138_data *data)
{
struct device *dev = &data->client->dev;
int ret;
data->vref_regu = devm_regulator_get(dev, "avdd");
if (IS_ERR(data->vref_regu))
return dev_err_probe(dev, PTR_ERR(data->vref_regu),
"Failed to get avdd regulator\n");
ret = regulator_get_voltage(data->vref_regu);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to get avdd voltage\n");
/* Reset the chip to get a defined starting configuration */
ret = ads7138_i2c_set_bit(data->client, ADS7138_REG_GENERAL_CFG,
ADS7138_GENERAL_CFG_RST);
if (ret)
return ret;
ret = ads7138_set_conv_mode(data, ADS7138_MODE_AUTO);
if (ret)
return ret;
/* Enable statistics and digital window comparator */
ret = ads7138_i2c_set_bit(data->client, ADS7138_REG_GENERAL_CFG,
ADS7138_GENERAL_CFG_STATS_EN |
ADS7138_GENERAL_CFG_DWC_EN);
if (ret)
return ret;
/* Enable all channels for auto sequencing */
ret = ads7138_i2c_set_bit(data->client, ADS7138_REG_AUTO_SEQ_CH_SEL, 0xFF);
if (ret)
return ret;
/* Set auto sequence mode and start sequencing */
return ads7138_i2c_set_bit(data->client, ADS7138_REG_SEQUENCE_CFG,
ADS7138_SEQUENCE_CFG_SEQ_START |
ADS7138_SEQUENCE_CFG_SEQ_MODE);
}
static int ads7138_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct iio_dev *indio_dev;
struct ads7138_data *data;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->client = client;
data->chip_data = i2c_get_match_data(client);
if (!data->chip_data)
return -ENODEV;
ret = devm_mutex_init(dev, &data->lock);
if (ret)
return ret;
indio_dev->name = data->chip_data->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = ads7138_channels;
indio_dev->num_channels = ARRAY_SIZE(ads7138_channels);
indio_dev->info = &ti_ads7138_info;
i2c_set_clientdata(client, indio_dev);
if (client->irq > 0) {
ret = devm_request_threaded_irq(dev, client->irq,
NULL, ads7138_event_handler,
IRQF_TRIGGER_LOW |
IRQF_ONESHOT | IRQF_SHARED,
client->name, indio_dev);
if (ret)
return ret;
}
ret = ads7138_init_hw(data);
if (ret)
return dev_err_probe(dev, ret, "Failed to initialize device\n");
ret = devm_iio_device_register(dev, indio_dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to register iio device\n");
return 0;
}
static int ads7138_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct ads7138_data *data = iio_priv(indio_dev);
return ads7138_set_conv_mode(data, ADS7138_MODE_MANUAL);
}
static int ads7138_runtime_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct ads7138_data *data = iio_priv(indio_dev);
return ads7138_set_conv_mode(data, ADS7138_MODE_AUTO);
}
static DEFINE_RUNTIME_DEV_PM_OPS(ads7138_pm_ops,
ads7138_runtime_suspend,
ads7138_runtime_resume,
NULL);
static const struct ads7138_chip_data ads7128_data = {
.name = "ads7128",
.channel_num = 8,
};
static const struct ads7138_chip_data ads7138_data = {
.name = "ads7138",
.channel_num = 8,
};
static const struct of_device_id ads7138_of_match[] = {
{ .compatible = "ti,ads7128", .data = &ads7128_data },
{ .compatible = "ti,ads7138", .data = &ads7138_data },
{ }
};
MODULE_DEVICE_TABLE(of, ads7138_of_match);
static const struct i2c_device_id ads7138_device_ids[] = {
{ "ads7128", (kernel_ulong_t)&ads7128_data },
{ "ads7138", (kernel_ulong_t)&ads7138_data },
{ }
};
MODULE_DEVICE_TABLE(i2c, ads7138_device_ids);
static struct i2c_driver ads7138_driver = {
.driver = {
.name = "ads7138",
.of_match_table = ads7138_of_match,
.pm = pm_ptr(&ads7138_pm_ops),
},
.id_table = ads7138_device_ids,
.probe = ads7138_probe,
};
module_i2c_driver(ads7138_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tobias Sperling <tobias.sperling@softing.com>");
MODULE_DESCRIPTION("Driver for TI ADS7138 ADCs");

View File

@ -251,11 +251,8 @@ static const struct iio_info ads7924_info = {
.read_raw = ads7924_read_raw,
};
static int ads7924_get_channels_config(struct i2c_client *client,
struct iio_dev *indio_dev)
static int ads7924_get_channels_config(struct device *dev)
{
struct ads7924_data *priv = iio_priv(indio_dev);
struct device *dev = priv->dev;
struct fwnode_handle *node;
int num_channels = 0;
@ -380,7 +377,7 @@ static int ads7924_probe(struct i2c_client *client)
indio_dev->num_channels = ARRAY_SIZE(ads7924_channels);
indio_dev->info = &ads7924_info;
ret = ads7924_get_channels_config(client, indio_dev);
ret = ads7924_get_channels_config(dev);
if (ret < 0)
return dev_err_probe(dev, ret,
"failed to get channels configuration\n");

View File

@ -131,11 +131,10 @@ static int tlc4541_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = spi_sync(st->spi, &st->scan_single_msg);
iio_device_release_direct_mode(indio_dev);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
*val = be16_to_cpu(st->rx_buf[0]);

Some files were not shown because too many files have changed in this diff Show More