IIO: New device support, features and cleanup for 6.18

New device support
 ==================
 
 ad,ade9000
 - New driver for this complex energy and power monitoring ADC.
 infineon,tlv493d
 - New driver for this 3D magnetic sensor.
 intel,dollar
 - New driver for this TI PMIC (part number unknown)
 marvel,88pm886
 - Driver for this PMIC ADC.
 microchip,mcp9600
 - Add explicit support for the mcp9601 which has some additional features
   over the mcp9600.
 rohm,bd79112
 - New driver for this ADC / GPIO Chip.
 
 Features
 ========
 Core
 - New helper to multiply data expressed in IIO types.
 - Add KUnit tests.
 - New IIO_ALTCURRENT type, similar to existing IIO_ALTVOLTAGE
 - Add some channel modifiers related to energy and power, such as
   reactive.
 adi,ad7124
 - Support external clocks sources and output of the internal clocks.
 - Filter control.
 adi,ad7173
 - Add filter support. Some fiddly interactions with other parameters on this
   device.
 adi,ad7779
 - Add backend support which required control of the number of lanes used.
 liteon,ltr390
 - Add runtime PM support.
 microchip,mcp9600
 - Add support for different thermocouple types.
 
 Cleanup and minor fixes
 =======================
 
 core
 - Switch info_mask fields to be unsigned. Not clear why they were ever
   signed.
 - Fix handling of negative channel scale in iio_convert_raw_to_processed()
 - Fix offset handling for channels without a scale attribute.
 - Improve the precision of scaling slightly.
 - Drop apparent handling of IIO_CHAN_INFO_PROCESSED for devices that don't
   have any such channels.
 various
 - Drop many pm_runtime_mark_last_busy() calls now
   pm_runtime_put_autosuspend() calls it internally.
 - Drop dev_err_probe() calls where the error code is hard coded as -ENOMEM
   as they don't do anything.
 - Drop dev_err() calls where the error code is -ENOMEM. This will reduce
   error prints, but memory failures generate a lot of messages anyway
   so unlikely we need these prints.
 current-sense-amplifier
 - Add #io-channels property this channel to be used by a consumer driver.
 adi,ad7124
 - Fix incorrect clocks dt-binding property.
 - Make the mclk clock optional in DT - this is internal to the ADC so should
   never have been in he binding.
 - Fix up sample rate to comply with ABI.
 - Use read_avail() callback rather than opencoding similar.
 - Deploy guard() to clean up some lock handling.
 adi,ad7768
 - Use devm_regulator_get_enable_read_voltage() to replace similar code.
 adi,ad7816
 - Drop an unnecessary dev_set_drvdata() call as nothing uses the data.
 ad,adxl345
 - Fix missing blank line before bullet list in documentation.
 arm,scmi
 - Use devm_kcalloc() for an array allocation rather than devm_kzalloc().
 bosch,bmi270
 - Match an ACPI ID seen in the wild. It is not spec compliant but we can't
   do much about that.
 bosch,bmp280
 - Drop overly noisy dev_info()
 - Allow for sleeping gpio controllers.
 gogle,cros-ec
 - Drop unused location attribute that has been replaced by label.
 invense,icm42600
 - Simplify the power management.
 - Use guard() to simplify some locking.
 maxim,max1238
 - Add io-channel-cells property to dt-binding as there is an in tree
   consumer.
 microchip,mcp9600
 - Specify a default value in dt-binding for the thermocouple type
 - General whitespace cleanup.
 samsung,exynos
 - Drop support for the S3C2410 including bindings, and touchscreen support
   as nothing else uses that.
 - Drop platform ID based binding as not used.
 st,vl53l0x
 - Fix returning the wrong variable in an error path.
 ti,pac1934
 - Replace open coded devm_mutex_init().
 xilinx,ams
 - Update maintainers entry.
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCgAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAmjMAQYRHGppYzIzQGtl
 cm5lbC5vcmcACgkQVIU0mcT0Foi4zQ//UKValffDHWi9OkZjyIqQ7Ee3qa/HTuCT
 rCUpROr8dh9bmS5mSO3karfGatqORBZZs5h94rQXiwG0qGq1w4U0B1+bDA53LwpU
 t3bX0AZgnU86jsQlh8KTofZbhmDL1kB7H2i5rrHBePjuc8l7HvMTcv1Ws+6fFPwO
 IevzUaDfrrD33LQY9TzlhoFNO+/NIRHjV4Phv4/zHrw0/QI+JgOjafghCs7IaO8J
 /Noss44pDfaSgduqXHQ7nd2pJM20p0zU+88jj4n1/m1lWEsD5wkvaCcePHgvhnlR
 XSK5yI00KI7nnfKolZhr6KOAJzLoii7uTDmxIQ4zS0qE6TMs+lA9qN1e2brxXMyv
 P6NL20op56C8u1clsesU51w4LBYPYDuXx8ogRcppY9K1Kra+Fv62qD1dvkmP5rFQ
 OP/SDn6AxsIKcb8T+GRmzvQVmKgPfrTUQteuuYDm5dECBtwdYY/3goC4zY/phBNQ
 jOmUC7HNZJsp0fF6Aq3lvuDDf3tG+DvI68/AEOlixnA/BhpHm5w0av0rAh+lDv8Q
 +MI5SE2/AAlr4avQ2q3iH9bS17+cgBJoV0A+wmE2ArP4DGqcVaZ/5uB6dFF6vzIS
 bx7pYjrkCpFUSAvA40PwGSrp6RXg0E0j5L0b0Yx4FkbZK7x82uGm2Rg9Lol3NcWq
 2VKMQCmZfm0=
 =iOJF
 -----END PGP SIGNATURE-----

Merge tag 'iio-for-6.18a' 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 6.18

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

ad,ade9000
- New driver for this complex energy and power monitoring ADC.
infineon,tlv493d
- New driver for this 3D magnetic sensor.
intel,dollar
- New driver for this TI PMIC (part number unknown)
marvel,88pm886
- Driver for this PMIC ADC.
microchip,mcp9600
- Add explicit support for the mcp9601 which has some additional features
  over the mcp9600.
rohm,bd79112
- New driver for this ADC / GPIO Chip.

Features
========
Core
- New helper to multiply data expressed in IIO types.
- Add KUnit tests.
- New IIO_ALTCURRENT type, similar to existing IIO_ALTVOLTAGE
- Add some channel modifiers related to energy and power, such as
  reactive.
adi,ad7124
- Support external clocks sources and output of the internal clocks.
- Filter control.
adi,ad7173
- Add filter support. Some fiddly interactions with other parameters on this
  device.
adi,ad7779
- Add backend support which required control of the number of lanes used.
liteon,ltr390
- Add runtime PM support.
microchip,mcp9600
- Add support for different thermocouple types.

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

core
- Switch info_mask fields to be unsigned. Not clear why they were ever
  signed.
- Fix handling of negative channel scale in iio_convert_raw_to_processed()
- Fix offset handling for channels without a scale attribute.
- Improve the precision of scaling slightly.
- Drop apparent handling of IIO_CHAN_INFO_PROCESSED for devices that don't
  have any such channels.
various
- Drop many pm_runtime_mark_last_busy() calls now
  pm_runtime_put_autosuspend() calls it internally.
- Drop dev_err_probe() calls where the error code is hard coded as -ENOMEM
  as they don't do anything.
- Drop dev_err() calls where the error code is -ENOMEM. This will reduce
  error prints, but memory failures generate a lot of messages anyway
  so unlikely we need these prints.
current-sense-amplifier
- Add #io-channels property this channel to be used by a consumer driver.
adi,ad7124
- Fix incorrect clocks dt-binding property.
- Make the mclk clock optional in DT - this is internal to the ADC so should
  never have been in he binding.
- Fix up sample rate to comply with ABI.
- Use read_avail() callback rather than opencoding similar.
- Deploy guard() to clean up some lock handling.
adi,ad7768
- Use devm_regulator_get_enable_read_voltage() to replace similar code.
adi,ad7816
- Drop an unnecessary dev_set_drvdata() call as nothing uses the data.
ad,adxl345
- Fix missing blank line before bullet list in documentation.
arm,scmi
- Use devm_kcalloc() for an array allocation rather than devm_kzalloc().
bosch,bmi270
- Match an ACPI ID seen in the wild. It is not spec compliant but we can't
  do much about that.
bosch,bmp280
- Drop overly noisy dev_info()
- Allow for sleeping gpio controllers.
gogle,cros-ec
- Drop unused location attribute that has been replaced by label.
invense,icm42600
- Simplify the power management.
- Use guard() to simplify some locking.
maxim,max1238
- Add io-channel-cells property to dt-binding as there is an in tree
  consumer.
microchip,mcp9600
- Specify a default value in dt-binding for the thermocouple type
- General whitespace cleanup.
samsung,exynos
- Drop support for the S3C2410 including bindings, and touchscreen support
  as nothing else uses that.
- Drop platform ID based binding as not used.
st,vl53l0x
- Fix returning the wrong variable in an error path.
ti,pac1934
- Replace open coded devm_mutex_init().
xilinx,ams
- Update maintainers entry.

* tag 'iio-for-6.18a' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (178 commits)
  MAINTAINERS: Support ROHM BD79112 ADC
  iio: adc: Support ROHM BD79112 ADC/GPIO
  dt-bindings: iio: adc: ROHM BD79112 ADC/GPIO
  iio: pressure: bmp280: Use gpiod_set_value_cansleep()
  iio: pressure: bmp280: Remove noisy dev_info()
  iio: ABI: add filter types for ad7173
  iio: adc: ad7173: support changing filter type
  iio: adc: ad7173: rename odr field
  iio: adc: ad7173: rename ad7173_chan_spec_ext_info
  iio: adc: Add driver for Marvell 88PM886 PMIC ADC
  dt-bindings: mfd: 88pm886: Add #io-channel-cells
  iio: ABI: document "sinc4+rej60" filter_type
  iio: adc: ad7124: add filter support
  iio: adc: ad7124: support fractional sampling_frequency
  iio: adc: ad7124: use guard(mutex) to simplify return paths
  iio: adc: ad7124: use read_avail() for scale_available
  iio: adc: ad7124: use clamp()
  iio: adc: ad7124: fix sample rate for multi-channel use
  Documentation: ABI: iio: add sinc4+lp
  docs: iio: add documentation for ade9000 driver
  ...
pull/1354/merge
Greg Kroah-Hartman 2025-09-23 14:15:25 +02:00
commit fc3e44e492
243 changed files with 9192 additions and 1864 deletions

View File

@ -167,7 +167,18 @@ Description:
is required is a consistent labeling. Units after application
of scale and offset are millivolts.
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_rms_raw
KernelVersion: 6.18
Contact: linux-iio@vger.kernel.org
Description:
Raw (unscaled) Root Mean Square (RMS) voltage measurement from
channel Y. Units after application of scale and offset are
millivolts.
What: /sys/bus/iio/devices/iio:deviceX/in_powerY_raw
What: /sys/bus/iio/devices/iio:deviceX/in_powerY_active_raw
What: /sys/bus/iio/devices/iio:deviceX/in_powerY_reactive_raw
What: /sys/bus/iio/devices/iio:deviceX/in_powerY_apparent_raw
KernelVersion: 4.5
Contact: linux-iio@vger.kernel.org
Description:
@ -176,6 +187,13 @@ Description:
unique to allow association with event codes. Units after
application of scale and offset are milliwatts.
What: /sys/bus/iio/devices/iio:deviceX/in_powerY_powerfactor
KernelVersion: 6.18
Contact: linux-iio@vger.kernel.org
Description:
Power factor measurement from channel Y. Power factor is the
ratio of active power to apparent power. The value is unitless.
What: /sys/bus/iio/devices/iio:deviceX/in_capacitanceY_raw
KernelVersion: 3.2
Contact: linux-iio@vger.kernel.org
@ -1569,6 +1587,9 @@ Description:
What: /sys/.../iio:deviceX/in_energy_input
What: /sys/.../iio:deviceX/in_energy_raw
What: /sys/.../iio:deviceX/in_energyY_active_raw
What: /sys/.../iio:deviceX/in_energyY_reactive_raw
What: /sys/.../iio:deviceX/in_energyY_apparent_raw
KernelVersion: 4.0
Contact: linux-iio@vger.kernel.org
Description:
@ -1707,6 +1728,14 @@ Description:
component of the signal while the 'q' channel contains the quadrature
component.
What: /sys/bus/iio/devices/iio:deviceX/in_altcurrentY_rms_raw
KernelVersion: 6.18
Contact: linux-iio@vger.kernel.org
Description:
Raw (unscaled no bias removal etc.) Root Mean Square (RMS) current
measurement from channel Y. Units after application of scale and
offset are milliamps.
What: /sys/.../iio:deviceX/in_energy_en
What: /sys/.../iio:deviceX/in_distance_en
What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_en
@ -2281,21 +2310,28 @@ Description:
conversion time. Poor noise performance.
* "sinc3" - The digital sinc3 filter. Moderate 1st
conversion time. Good noise performance.
* "sinc4" - Sinc 4. Excellent noise performance. Long
1st conversion time.
* "sinc5" - The digital sinc5 filter. Excellent noise
performance
* "sinc4+sinc1" - Sinc4 + averaging by 8. Low 1st conversion
time.
* "sinc3+rej60" - Sinc3 + 60Hz rejection.
* "sinc3+sinc1" - Sinc3 + averaging by 8. Low 1st conversion
time.
* "sinc3+pf1" - Sinc3 + device specific Post Filter 1.
* "sinc3+pf2" - Sinc3 + device specific Post Filter 2.
* "sinc3+pf3" - Sinc3 + device specific Post Filter 3.
* "sinc3+pf4" - Sinc3 + device specific Post Filter 4.
* "sinc5+pf1" - Sinc5 + device specific Post Filter 1.
* "sinc3+rej60" - Sinc3 + 60Hz rejection.
* "sinc3+sinc1" - Sinc3 + averaging by 8. Low 1st conversion
time.
* "sinc4" - Sinc 4. Excellent noise performance. Long
1st conversion time.
* "sinc4+lp" - Sinc4 + Low Pass Filter.
* "sinc4+sinc1" - Sinc4 + averaging by 8. Low 1st conversion
time.
* "sinc4+rej60" - Sinc4 + 60Hz rejection.
* "sinc5" - The digital sinc5 filter. Excellent noise
performance
* "sinc5+avg" - Sinc5 + averaging by 4.
* "sinc5+pf1" - Sinc5 + device specific Post Filter 1.
* "sinc5+sinc1" - Sinc5 + Sinc1.
* "sinc5+sinc1+pf1" - Sinc5 + Sinc1 + device specific Post Filter 1.
* "sinc5+sinc1+pf2" - Sinc5 + Sinc1 + device specific Post Filter 2.
* "sinc5+sinc1+pf3" - Sinc5 + Sinc1 + device specific Post Filter 3.
* "sinc5+sinc1+pf4" - Sinc5 + Sinc1 + device specific Post Filter 4.
* "wideband" - filter with wideband low ripple passband
and sharp transition band.

View File

@ -7,16 +7,6 @@ Description:
corresponding calibration offsets can be read from `*_calibbias`
entries.
What: /sys/bus/iio/devices/iio:deviceX/location
Date: July 2015
KernelVersion: 4.7
Contact: linux-iio@vger.kernel.org
Description:
This attribute returns a string with the physical location where
the motion sensor is placed. For example, in a laptop a motion
sensor can be located on the base or on the lid. Current valid
values are 'base' and 'lid'.
What: /sys/bus/iio/devices/iio:deviceX/id
Date: September 2017
KernelVersion: 4.14

View File

@ -7,7 +7,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: ADIS16240 Programmable Impact Sensor and Recorder driver
maintainers:
- Alexandru Tachici <alexandru.tachici@analog.com>
- Marcelo Schmitt <marcelo.schmitt@analog.com>
- Nuno Sá <nuno.sa@analog.com>
description: |
ADIS16240 Programmable Impact Sensor and Recorder driver that supports
@ -37,7 +38,6 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;

View File

@ -57,7 +57,6 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
@ -73,7 +72,6 @@ examples:
};
};
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;

View File

@ -56,7 +56,6 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
@ -72,7 +71,6 @@ examples:
};
};
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;

View File

@ -58,7 +58,6 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
@ -74,7 +73,6 @@ examples:
};
};
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;

View File

@ -7,7 +7,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices ADXL372 3-Axis, +/-(200g) Digital Accelerometer
maintainers:
- Stefan Popa <stefan.popa@analog.com>
- Marcelo Schmitt <marcelo.schmitt@analog.com>
- Nuno Sá <nuno.sa@analog.com>
description: |
Analog Devices ADXL372 3-Axis, +/-(200g) Digital Accelerometer that supports
@ -37,7 +38,6 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
@ -52,7 +52,6 @@ examples:
};
};
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;

View File

@ -107,7 +107,6 @@ examples:
};
};
- |
# include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;

View File

@ -40,7 +40,6 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;

View File

@ -57,7 +57,6 @@ examples:
};
};
- |
# include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;

View File

@ -93,7 +93,6 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;

View File

@ -8,7 +8,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD7124 ADC device driver
maintainers:
- Stefan Popa <stefan.popa@analog.com>
- Marcelo Schmitt <marcelo.schmitt@analog.com>
- Nuno Sá <nuno.sa@analog.com>
description: |
Bindings for the Analog Devices AD7124 ADC device. Datasheet can be
@ -27,12 +28,21 @@ properties:
clocks:
maxItems: 1
description: phandle to the master clock (mclk)
description: Optional external clock connected to the CLK pin.
clock-names:
deprecated: true
description:
MCLK is an internal counter in the ADC. Do not use this property.
items:
- const: mclk
'#clock-cells':
description:
The CLK pin can be used as an output. When that is the case, include
this property.
const: 0
interrupts:
description: IRQ line for the ADC
maxItems: 1
@ -66,10 +76,14 @@ properties:
required:
- compatible
- reg
- clocks
- clock-names
- interrupts
# Can't have both clock input and output at the same time.
not:
required:
- '#clock-cells'
- clocks
patternProperties:
"^channel@([0-9]|1[0-5])$":
$ref: adc.yaml
@ -135,8 +149,6 @@ examples:
interrupt-parent = <&gpio>;
rdy-gpios = <&gpio 25 GPIO_ACTIVE_LOW>;
refin1-supply = <&adc_vref>;
clocks = <&ad7124_mclk>;
clock-names = "mclk";
#address-cells = <1>;
#size-cells = <0>;

View File

@ -379,7 +379,6 @@ unevaluatedProperties: false
examples:
# Example AD7173-8 with external reference connected to REF+/REF-:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {

View File

@ -41,6 +41,7 @@ properties:
- adi,ad7910
- adi,ad7920
- adi,ad7940
- rohm,bd79105
- ti,adc081s
- ti,adc101s
- ti,adc121s
@ -55,6 +56,11 @@ properties:
reg:
maxItems: 1
interrupts:
description:
The data-ready interrupt. Provided via DOUT pin.
maxItems: 1
vcc-supply:
description:
Main powersupply voltage for the chips, sometimes referred to as VDD on
@ -75,6 +81,10 @@ properties:
description: A GPIO used to trigger the start of a conversion
maxItems: 1
rdy-gpios:
description: A GPIO for detecting the data-ready.
maxItems: 1
required:
- compatible
- reg
@ -82,6 +92,20 @@ required:
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
# Devices with an IRQ
- if:
properties:
compatible:
contains:
enum:
- rohm,bd79105
then:
properties:
interrupts: true
else:
properties:
interrupts: false
# Devices where reference is vcc
- if:
properties:
@ -106,6 +130,7 @@ allOf:
- vcc-supply
# Devices with a vref
- if:
not:
properties:
compatible:
contains:
@ -115,10 +140,8 @@ allOf:
- adi,ad7274
- adi,ad7475
- lltc,ltc2314-14
- rohm,bd79105
then:
properties:
vref-supply: true
else:
properties:
vref-supply: false
# Devices with a vref where it is not optional
@ -131,36 +154,59 @@ allOf:
- adi,ad7274
- adi,ad7475
- lltc,ltc2314-14
- rohm,bd79105
then:
required:
- vref-supply
- if:
not:
properties:
compatible:
contains:
enum:
- adi,ad7475
- adi,ad7495
- rohm,bd79105
then:
properties:
vdrive-supply: true
else:
properties:
vdrive-supply: false
# Devices which support polling the data-ready via GPIO
- if:
not:
properties:
compatible:
contains:
enum:
- rohm,bd79105
then:
properties:
rdy-gpios: false
- if:
not:
properties:
compatible:
contains:
enum:
- adi,ad7091
- adi,ad7091r
- rohm,bd79105
then:
properties:
adi,conversion-start-gpios: true
else:
properties:
adi,conversion-start-gpios: false
# Devices with a convstart GPIO where it is not optional
- if:
properties:
compatible:
contains:
enum:
- rohm,bd79105
then:
required:
- adi,conversion-start-gpios
unevaluatedProperties: false
examples:

View File

@ -80,11 +80,36 @@ properties:
reset-gpios:
maxItems: 1
io-backends:
maxItems: 1
adi,num-lanes:
description:
Number of lanes on which the data is sent on the output when the data
output interface is used.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [1, 2, 4]
default: 4
required:
- compatible
- reg
- clocks
allOf:
- if:
not:
required:
- io-backends
then:
properties:
adi,num-lanes: false
oneOf:
- required:
- interrupts
- required:
- io-backends
unevaluatedProperties: false
@ -107,4 +132,21 @@ examples:
clocks = <&adc_clk>;
};
};
- |
#include <dt-bindings/gpio/gpio.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "adi,ad7779";
reg = <0>;
start-gpios = <&gpio0 87 GPIO_ACTIVE_LOW>;
reset-gpios = <&gpio0 93 GPIO_ACTIVE_LOW>;
clocks = <&adc_clk>;
io-backends = <&iio_backend>;
adi,num-lanes = <4>;
};
};
...

View File

@ -0,0 +1,95 @@
# 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,ade9000.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices ADE9000 High Performance, Polyphase Energy Metering
maintainers:
- Antoniu Miclaus <antoniu.miclaus@analog.com>
description: |
The ADE9000 is a highly accurate, fully integrated, multiphase energy and power
quality monitoring device. Superior analog performance and a digital signal
processing (DSP) core enable accurate energy monitoring over a wide dynamic
range. An integrated high end reference ensures low drift over temperature
with a combined drift of less than ±25 ppm/°C maximum for the entire channel
including a programmable gain amplifier (PGA) and an analog-to-digital
converter (ADC).
https://www.analog.com/media/en/technical-documentation/data-sheets/ADE9000.pdf
$ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
enum:
- adi,ade9000
reg:
maxItems: 1
spi-max-frequency:
maximum: 20000000
interrupts:
maxItems: 3
interrupt-names:
items:
enum: [irq0, irq1, dready]
minItems: 1
maxItems: 3
reset-gpios:
description:
Must be the device tree identifier of the RESET pin. As the line is
active low, it should be marked GPIO_ACTIVE_LOW.
maxItems: 1
vdd-supply: true
vref-supply: true
clocks:
description: External clock source when not using crystal
maxItems: 1
"#clock-cells":
description:
ADE9000 can provide clock output via CLKOUT pin with external buffer.
const: 0
required:
- compatible
- reg
- vdd-supply
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,ade9000";
reg = <0>;
spi-max-frequency = <7000000>;
#clock-cells = <0>;
reset-gpios = <&gpio 4 GPIO_ACTIVE_LOW>;
interrupts = <2 IRQ_TYPE_EDGE_FALLING>, <3 IRQ_TYPE_EDGE_FALLING>, <4 IRQ_TYPE_EDGE_FALLING>;
interrupt-names = "irq0", "irq1", "dready";
interrupt-parent = <&gpio>;
clocks = <&ext_clock_24576khz>;
vdd-supply = <&vdd_reg>;
};
};

View File

@ -9,7 +9,6 @@ title: Linear Technology / Analog Devices LTC2496 ADC
maintainers:
- Lars-Peter Clausen <lars@metafoo.de>
- Michael Hennerich <Michael.Hennerich@analog.com>
- Stefan Popa <stefan.popa@analog.com>
properties:
compatible:

View File

@ -53,6 +53,9 @@ properties:
reg:
maxItems: 1
"#io-channel-cells":
const: 1
vcc-supply: true
vref-supply:
description: Optional external reference. If not supplied, internal

View File

@ -63,6 +63,6 @@ examples:
vdd-supply = <&adc_vdd>;
vref-supply = <&adc_vref>;
spi-max-frequency = <1000000>;
shutdown-gpios = <&gpio 26 1>;
shutdown-gpios = <&gpio 26 GPIO_ACTIVE_LOW>;
};
};

View File

@ -278,7 +278,6 @@ examples:
- |
#include <dt-bindings/iio/qcom,spmi-adc7-pmk8350.h>
#include <dt-bindings/iio/qcom,spmi-adc7-pm8350.h>
#include <dt-bindings/interrupt-controller/irq.h>
pmic {
#address-cells = <1>;

View File

@ -47,6 +47,9 @@ properties:
- const: saradc
- const: apb_pclk
power-domains:
maxItems: 1
resets:
maxItems: 1

View File

@ -14,7 +14,15 @@ description: |
properties:
compatible:
const: rohm,bd79104
oneOf:
- enum:
- rohm,bd79100
- rohm,bd79101
- rohm,bd79102
- rohm,bd79104
- items:
- const: rohm,bd79103
- const: rohm,bd79104
reg:
maxItems: 1
@ -50,7 +58,6 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;

View File

@ -0,0 +1,104 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/rohm,bd79112.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ROHM BD79112 ADC/GPO
maintainers:
- Matti Vaittinen <mazziesaccount@gmail.com>
description: |
The ROHM BD79112 is a 12-bit, 32-channel, SAR ADC. ADC input pins can be
also configured as general purpose inputs/outputs. SPI should use MODE 3.
properties:
compatible:
const: rohm,bd79112
reg:
maxItems: 1
spi-cpha: true
spi-cpol: true
gpio-controller: true
"#gpio-cells":
const: 2
vdd-supply: true
iovdd-supply: true
"#address-cells":
const: 1
"#size-cells":
const: 0
patternProperties:
"^channel@([0-9]|[12][0-9]|3[01])$":
type: object
$ref: /schemas/iio/adc/adc.yaml#
description: Represents ADC channel. Omitted channels' inputs are GPIOs.
properties:
reg:
description: AIN pin number
minimum: 0
maximum: 31
required:
- reg
additionalProperties: false
required:
- compatible
- reg
- iovdd-supply
- vdd-supply
- spi-cpha
- spi-cpol
additionalProperties: false
examples:
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
adc: adc@0 {
compatible = "rohm,bd79112";
reg = <0x0>;
spi-cpha;
spi-cpol;
vdd-supply = <&dummyreg>;
iovdd-supply = <&dummyreg>;
#address-cells = <1>;
#size-cells = <0>;
gpio-controller;
#gpio-cells = <2>;
channel@0 {
reg = <0>;
};
channel@1 {
reg = <1>;
};
channel@2 {
reg = <2>;
};
channel@16 {
reg = <16>;
};
channel@20 {
reg = <20>;
};
};
};

View File

@ -81,7 +81,7 @@ examples:
reg = <0x10>;
interrupt-parent = <&gpio1>;
interrupts = <29 8>;
interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
vdd-supply = <&dummyreg>;
iovdd-supply = <&dummyreg>;

View File

@ -18,10 +18,6 @@ properties:
- samsung,exynos3250-adc
- samsung,exynos4212-adc # Exynos4212 and Exynos4412
- samsung,exynos7-adc
- samsung,s3c2410-adc
- samsung,s3c2416-adc
- samsung,s3c2440-adc
- samsung,s3c2443-adc
- samsung,s3c6410-adc
- samsung,s5pv210-adc
- items:
@ -46,8 +42,6 @@ properties:
maxItems: 2
interrupts:
description:
ADC interrupt followed by optional touchscreen interrupt.
minItems: 1
maxItems: 2
@ -62,11 +56,6 @@ properties:
Phandle to the PMU system controller node (to access the ADC_PHY
register on Exynos3250/4x12/5250/5420/5800).
has-touchscreen:
description:
If present, indicates that a touchscreen is connected and usable.
type: boolean
required:
- compatible
- reg
@ -118,20 +107,29 @@ allOf:
- const: adc
- if:
required:
- has-touchscreen
properties:
compatible:
contains:
const: samsung,s5pv210-adc
then:
properties:
interrupts:
minItems: 2
maxItems: 2
items:
- description: main (ADC)
- description: pending (PENDN)
else:
properties:
interrupts:
maxItems: 1
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
adc: adc@12d10000 {
compatible = "samsung,exynos-adc-v1";
reg = <0x12d10000 0x100>;
interrupts = <0 106 0>;
interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
#io-channel-cells = <1>;
clocks = <&clock 303>;
@ -152,11 +150,12 @@ examples:
- |
#include <dt-bindings/clock/exynos3250.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
adc@126c0000 {
compatible = "samsung,exynos3250-adc";
reg = <0x126c0000 0x100>;
interrupts = <0 137 0>;
interrupts = <GIC_SPI 137 IRQ_TYPE_LEVEL_HIGH>;
#io-channel-cells = <1>;
clocks = <&cmu CLK_TSADC>,

View File

@ -44,7 +44,6 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;

View File

@ -59,7 +59,6 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;

View File

@ -7,7 +7,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Xilinx Zynq Ultrascale AMS controller
maintainers:
- Anand Ashok Dumbre <anand.ashok.dumbre@xilinx.com>
- Salih Erim <salih.erim@amd.com>
- Conall O'Griofa <conall.ogriofa@amd.com>
description: |
The AMS (Analog Monitoring System) includes an ADC as well as on-chip sensors

View File

@ -24,6 +24,9 @@ properties:
description: |
Channel node of a voltage io-channel.
"#io-channel-cells":
const: 0
sense-resistor-micro-ohms:
description: The sense resistance.
@ -46,6 +49,7 @@ examples:
- |
sysi {
compatible = "current-sense-amplifier";
#io-channel-cells = <0>;
io-channels = <&tiadc 0>;
sense-resistor-micro-ohms = <20000>;

View File

@ -8,7 +8,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD5770R DAC device driver
maintainers:
- Alexandru Tachici <alexandru.tachici@analog.com>
- Marcelo Schmitt <marcelo.schmitt@analog.com>
- Nuno Sá <nuno.sa@analog.com>
description: |
Bindings for the Analog Devices AD5770R current DAC device. Datasheet can be

View File

@ -7,7 +7,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices ADF4371/ADF4372 Wideband Synthesizers
maintainers:
- Popa Stefan <stefan.popa@analog.com>
- Marcelo Schmitt <marcelo.schmitt@analog.com>
- Nuno Sá <nuno.sa@analog.com>
description: |
Analog Devices ADF4371/ADF4372 SPI Wideband Synthesizers

View File

@ -44,7 +44,6 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;

View File

@ -7,7 +7,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices ADIS16480 and similar IMUs
maintainers:
- Alexandru Tachici <alexandru.tachici@analog.com>
- Marcelo Schmitt <marcelo.schmitt@analog.com>
- Nuno Sá <nuno.sa@analog.com>
properties:
compatible:

View File

@ -74,7 +74,6 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
@ -91,7 +90,6 @@ examples:
};
};
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;

View File

@ -47,7 +47,6 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
@ -63,7 +62,6 @@ examples:
};
};
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;

View File

@ -42,6 +42,6 @@ examples:
compatible = "dynaimage,al3010";
reg = <0x1c>;
vdd-supply = <&vdd_reg>;
interrupts = <0 99 4>;
interrupts = <99 IRQ_TYPE_LEVEL_HIGH>;
};
};

View File

@ -40,6 +40,6 @@ examples:
compatible = "dynaimage,al3320a";
reg = <0x1c>;
vdd-supply = <&vdd_reg>;
interrupts = <0 99 4>;
interrupts = <99 IRQ_TYPE_LEVEL_HIGH>;
};
};

View File

@ -32,7 +32,6 @@ required:
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;

View File

@ -0,0 +1,51 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/light/vishay,veml6046x00.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Vishay VEML6046X00 High accuracy RGBIR color sensor
maintainers:
- Andreas Klinger <ak@it-klinger.de>
description:
VEML6046X00 datasheet at https://www.vishay.com/docs/80173/veml6046x00.pdf
properties:
compatible:
enum:
- vishay,veml6046x00
reg:
maxItems: 1
vdd-supply: true
interrupts:
maxItems: 1
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
color-sensor@29 {
compatible = "vishay,veml6046x00";
reg = <0x29>;
vdd-supply = <&vdd_reg>;
interrupt-parent = <&gpio2>;
interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
};
};
...

View File

@ -0,0 +1,45 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/magnetometer/infineon,tlv493d-a1b6.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Infineon Technologies TLV493D Low-Power 3D Magnetic Sensor
maintainers:
- Dixit Parmar <dixitparmar19@gmail.com>
properties:
$nodename:
pattern: '^magnetometer@[0-9a-f]+$'
compatible:
const: infineon,tlv493d-a1b6
reg:
maxItems: 1
vdd-supply:
description: 2.8V to 3.5V VDD supply
interrupts:
maxItems: 1
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
magnetometer@5e {
compatible = "infineon,tlv493d-a1b6";
reg = <0x5e>;
vdd-supply = <&hall_vcc>;
};
};

View File

@ -44,7 +44,6 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;

View File

@ -109,7 +109,6 @@ examples:
};
- |
# include <dt-bindings/gpio/gpio.h>
# include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;

View File

@ -0,0 +1,52 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/pressure/invensense,icp10100.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: InvenSense ICP-101xx Barometric Pressure Sensors
maintainers:
- Jean-Baptiste Maneyrol <jean-baptiste.maneyrol@tdk.com>
description: |
Support for ICP-101xx family: ICP-10100, ICP-10101, ICP-10110, ICP-10111.
Those devices uses a simple I2C communication bus, measuring the pressure
in a ultra-low noise at the lowest power.
Datasheet: https://product.tdk.com/system/files/dam/doc/product/sensor/pressure/capacitive-pressure/data_sheet/ds-000186-icp-101xx.pdf
properties:
compatible:
oneOf:
- items:
- enum:
- invensense,icp10101
- invensense,icp10110
- invensense,icp10111
- const: invensense,icp10100
- const: invensense,icp10100
reg:
maxItems: 1
vdd-supply: true
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
pressure@63 {
compatible = "invensense,icp10101", "invensense,icp10100";
reg = <0x63>;
vdd-supply = <&vdd_1v8>;
};
};
...

View File

@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/iio/temperature/microchip,mcp9600.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip MCP9600 thermocouple EMF converter
title: Microchip MCP9600 and similar thermocouple EMF converters
maintainers:
- Andrew Hepp <andrew.hepp@ahepp.dev>
@ -14,7 +14,11 @@ description:
properties:
compatible:
const: microchip,mcp9600
oneOf:
- const: microchip,mcp9600
- items:
- const: microchip,mcp9601
- const: microchip,mcp9600
reg:
maxItems: 1
@ -37,13 +41,43 @@ properties:
thermocouple-type:
$ref: /schemas/types.yaml#/definitions/uint32
default: 3
description:
Type of thermocouple (THERMOCOUPLE_TYPE_K if omitted).
Use defines in dt-bindings/iio/temperature/thermocouple.h.
Supported types are B, E, J, K, N, R, S, T.
microchip,vsense:
type: boolean
description:
This flag indicates that the chip has been wired with VSENSE to
enable open and short circuit detect.
vdd-supply: true
allOf:
- if:
properties:
compatible:
not:
contains:
const: microchip,mcp9601
then:
properties:
interrupts:
minItems: 1
maxItems: 4
interrupt-names:
minItems: 1
maxItems: 4
items:
enum:
- alert1
- alert2
- alert3
- alert4
microchip,vsense: false
required:
- compatible
- reg
@ -63,8 +97,24 @@ examples:
reg = <0x60>;
interrupt-parent = <&gpio>;
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "open-circuit";
interrupt-names = "alert1";
thermocouple-type = <THERMOCOUPLE_TYPE_K>;
vdd-supply = <&vdd>;
};
};
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
temperature-sensor@62 {
compatible = "microchip,mcp9601", "microchip,mcp9600";
reg = <0x62>;
interrupt-parent = <&gpio>;
interrupts = <22 IRQ_TYPE_EDGE_RISING>, <23 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "open-circuit", "short-circuit";
vdd-supply = <&vdd>;
microchip,vsense;
};
};

View File

@ -35,6 +35,9 @@ properties:
description: LDO or buck regulator.
unevaluatedProperties: false
'#io-channel-cells':
const: 1
required:
- compatible
- reg
@ -53,6 +56,7 @@ examples:
reg = <0x30>;
interrupts = <0 4 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&gic>;
#io-channel-cells = <1>;
wakeup-source;
regulators {

View File

@ -133,8 +133,6 @@ properties:
- infineon,ir36021
# Infineon IRPS5401 Voltage Regulator (PMIC)
- infineon,irps5401
# Infineon TLV493D-A1B6 I2C 3D Magnetic Sensor
- infineon,tlv493d-a1b6
# Infineon Hot-swap controller xdp710
- infineon,xdp710
# Infineon Multi-phase Digital VR Controller xdpe11280

View File

@ -64,7 +64,8 @@ specific debugfs path ``/sys/kernel/debug/iio/iio:deviceX``.
Usage examples
--------------
. code-block:: bash
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0# cat data_source
normal
root:/sys/bus/iio/devices/iio:device0# echo -n ramp-16bit > data_source

View File

@ -0,0 +1,268 @@
.. SPDX-License-Identifier: GPL-2.0
===============
ADE9000 driver
===============
This driver supports Analog Device's ADE9000 energy measurement IC on SPI bus.
1. Supported devices
====================
* `ADE9000 <https://www.analog.com/media/en/technical-documentation/data-sheets/ADE9000.pdf>`_
The ADE9000 is a highly accurate, fully integrated, multiphase energy and power
quality monitoring device. Superior analog performance and a digital signal
processing (DSP) core enable accurate energy monitoring over a wide dynamic
range. An integrated high end reference ensures low drift over temperature
with a combined drift of less than ±25 ppm/°C maximum for the entire channel
including a programmable gain amplifier (PGA) and an analog-to-digital
converter (ADC).
2. Device attributes
====================
Power and energy measurements are provided for voltage, current, active power,
reactive power, apparent power, and power factor across three phases.
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 question. These files are consistently generalized and documented in
the IIO ABI documentation.
The following tables show the ADE9000 related device files, found in the
specific device folder path ``/sys/bus/iio/devices/iio:deviceX``.
+---------------------------------------------------+----------------------------------------------------------+
| Current measurement related device files | Description |
+---------------------------------------------------+----------------------------------------------------------+
| in_current[0-2]_raw | Raw current measurement for phases A, B, C. |
+---------------------------------------------------+----------------------------------------------------------+
| in_current[0-2]_scale | Scale for current channels. |
+---------------------------------------------------+----------------------------------------------------------+
| in_current[0-2]_calibscale | Calibration gain for current channels (AIGAIN reg). |
+---------------------------------------------------+----------------------------------------------------------+
| in_altcurrent[0-2]_rms_raw | RMS current measurement for phases A, B, C. |
+---------------------------------------------------+----------------------------------------------------------+
| in_altcurrent[0-2]_rms_scale | Scale for RMS current channels. |
+---------------------------------------------------+----------------------------------------------------------+
| in_altcurrent[0-2]_rms_calibbias | RMS offset correction for current channels (IRMSOS reg). |
+---------------------------------------------------+----------------------------------------------------------+
+---------------------------------------------------+----------------------------------------------------------+
| Voltage measurement related device files | Description |
+---------------------------------------------------+----------------------------------------------------------+
| in_voltage[0-2]_raw | Raw voltage measurement for phases A, B, C. |
+---------------------------------------------------+----------------------------------------------------------+
| in_voltage[0-2]_scale | Scale for voltage channels. |
+---------------------------------------------------+----------------------------------------------------------+
| in_voltage[0-2]_calibscale | Calibration gain for voltage channels (AVGAIN reg). |
+---------------------------------------------------+----------------------------------------------------------+
| in_voltage[0-2]_frequency | Measured line frequency from instantaneous voltage. |
+---------------------------------------------------+----------------------------------------------------------+
| in_altvoltage[0-2]_rms_raw | RMS voltage measurement for phases A, B, C. |
+---------------------------------------------------+----------------------------------------------------------+
| in_altvoltage[0-2]_rms_scale | Scale for RMS voltage channels. |
+---------------------------------------------------+----------------------------------------------------------+
| in_altvoltage[0-2]_rms_calibbias | RMS offset correction for voltage channels (VRMSOS reg). |
+---------------------------------------------------+----------------------------------------------------------+
+---------------------------------------------------+----------------------------------------------------------+
| Power measurement related device files | Description |
+---------------------------------------------------+----------------------------------------------------------+
| in_power[0-2]_active_raw | Active power measurement for phases A, B, C. |
+---------------------------------------------------+----------------------------------------------------------+
| in_power[0-2]_active_scale | Scale for active power channels. |
+---------------------------------------------------+----------------------------------------------------------+
| in_power[0-2]_active_calibbias | Calibration offset for active power (xWATTOS regs). |
+---------------------------------------------------+----------------------------------------------------------+
| in_power[0-2]_active_calibscale | Calibration gain for active power (APGAIN reg). |
+---------------------------------------------------+----------------------------------------------------------+
| in_power[0-2]_reactive_raw | Reactive power measurement for phases A, B, C. |
+---------------------------------------------------+----------------------------------------------------------+
| in_power[0-2]_reactive_scale | Scale for reactive power channels. |
+---------------------------------------------------+----------------------------------------------------------+
| in_power[0-2]_reactive_calibbias | Calibration offset for reactive power (xVAROS regs). |
+---------------------------------------------------+----------------------------------------------------------+
| in_power[0-2]_apparent_raw | Apparent power measurement for phases A, B, C. |
+---------------------------------------------------+----------------------------------------------------------+
| in_power[0-2]_apparent_scale | Scale for apparent power channels. |
+---------------------------------------------------+----------------------------------------------------------+
| in_power[0-2]_powerfactor | Power factor for phases A, B, C. |
+---------------------------------------------------+----------------------------------------------------------+
+---------------------------------------------------+----------------------------------------------------------+
| Energy measurement related device files | Description |
+---------------------------------------------------+----------------------------------------------------------+
| in_energy[0-2]_active_raw | Active energy measurement for phases A, B, C. |
+---------------------------------------------------+----------------------------------------------------------+
| in_energy[0-2]_reactive_raw | Reactive energy measurement for phases A, B, C. |
+---------------------------------------------------+----------------------------------------------------------+
| in_energy[0-2]_apparent_raw | Apparent energy measurement for phases A, B, C. |
+---------------------------------------------------+----------------------------------------------------------+
+------------------------------+------------------------------------------------------------------+
| Shared device attributes | Description |
+------------------------------+------------------------------------------------------------------+
| name | Name of the IIO device. |
+------------------------------+------------------------------------------------------------------+
| filter_type | Waveform buffer filter type (sinc4, sinc4+lp). |
+------------------------------+------------------------------------------------------------------+
| filter_type_available | Available filter types for waveform buffer. |
+------------------------------+------------------------------------------------------------------+
3. Calibration and scaling
===========================
The ADE9000 provides multiple levels of gain and offset correction:
**Calibration Gain (per-channel)**
Fine-tuning calibration gains applied in the digital domain for each channel type.
Controlled via ``calibscale`` attributes (AIGAIN, AVGAIN, APGAIN registers).
**Calibration Bias (per-channel)**
Hardware calibration offsets applied by the device internally:
- Power measurements: Controlled via ``calibbias`` attributes for power channels (xWATTOS, xVAROS registers).
- RMS measurements: Controlled via ``calibbias`` attributes for RMS channels (IRMSOS, VRMSOS registers).
These are internal chip calibrations, not userspace-applied offsets.
4. Event attributes
===================
The ADE9000 provides various interrupts that are mapped to IIO events.
Event functionality is only available if the corresponding interrupts are
connected in the device tree.
+---------------------------------------------------+----------------------------------------------------------+
| IIO Event Attribute | ADE9000 Datasheet Equivalent |
+---------------------------------------------------+----------------------------------------------------------+
| in_voltage[0-2]_thresh_either_en | Zero crossing detection interrupt (ZXVx) |
+---------------------------------------------------+----------------------------------------------------------+
| in_altvoltage[0-2]_rms_thresh_rising_en | RMS swell detection interrupt (SWELLx) |
+---------------------------------------------------+----------------------------------------------------------+
| in_altvoltage[0-2]_rms_thresh_rising_value | RMS swell threshold (SWELL_LVL register) |
+---------------------------------------------------+----------------------------------------------------------+
| in_altvoltage[0-2]_rms_thresh_falling_en | RMS sag/dip detection interrupt (DIPx) |
+---------------------------------------------------+----------------------------------------------------------+
| in_altvoltage[0-2]_rms_thresh_falling_value | RMS sag/dip threshold (DIP_LVL register) |
+---------------------------------------------------+----------------------------------------------------------+
| in_current[0-2]_thresh_either_en | Current zero crossing detection interrupt (ZXIx) |
+---------------------------------------------------+----------------------------------------------------------+
Event directions:
- ``rising``: Upper threshold crossing (swell detection)
- ``falling``: Lower threshold crossing (sag/dip detection)
- ``either``: Any threshold crossing (zero crossing detection)
- ``none``: Timeout or non-directional events
**Note**: Event attributes are only available if the corresponding interrupts
(irq0, irq1, dready) are specified in the device tree. The driver works without
interrupts but with reduced functionality.
5. Device buffers
=================
This driver supports IIO buffers for waveform capture. Buffer functionality
requires the dready interrupt to be connected.
The device supports capturing voltage and current waveforms for power quality
analysis. The waveform buffer can be configured to capture data from different
channel combinations.
Supported channel combinations for buffered capture:
- Phase A: voltage and current (IA + VA)
- Phase B: voltage and current (IB + VB)
- Phase C: voltage and current (IC + VC)
- All phases: all voltage and current channels
- Individual channels: IA, VA, IB, VB, IC, VC
Usage examples
--------------
Enable waveform capture for Phase A:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_current0_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_voltage0_en
Set buffer length and enable:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 100 > buffer/length
root:/sys/bus/iio/devices/iio:device0> echo 1 > buffer/enable
6. Clock output
===============
The ADE9000 can provide a clock output via the CLKOUT pin when using an external
crystal/clock source. This feature is enabled by specifying ``#clock-cells = <0>``
in the device tree. The output clock will be registered as "clkout" and can be
referenced by other devices.
7. Usage examples
=================
Show device name:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat name
ade9000
Read voltage measurements:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_voltage0_raw
12345
root:/sys/bus/iio/devices/iio:device0> cat in_voltage0_scale
0.000030517
- Phase A voltage = in_voltage0_raw * in_voltage0_scale = 0.3769 V
Read power measurements:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_power0_active_raw
5678
root:/sys/bus/iio/devices/iio:device0> cat in_power0_scale
0.000244140
- Phase A active power = in_power0_active_raw * in_power0_scale = 1.386 W
Configure calibration gains:
.. code-block:: bash
# Set current channel 0 calibration gain
root:/sys/bus/iio/devices/iio:device0> echo 0x800000 > in_current0_calibscale
# Set voltage channel 0 calibration gain
root:/sys/bus/iio/devices/iio:device0> echo 0x7FFFFF > in_voltage0_calibscale
Configure RMS voltage event thresholds (requires interrupts):
.. code-block:: bash
# Set RMS sag detection threshold
root:/sys/bus/iio/devices/iio:device0> echo 180000 > events/in_altvoltage0_rms_thresh_falling_value
# Enable RMS sag detection
root:/sys/bus/iio/devices/iio:device0> echo 1 > events/in_altvoltage0_rms_thresh_falling_en
# Set RMS swell detection threshold
root:/sys/bus/iio/devices/iio:device0> echo 260000 > events/in_altvoltage0_rms_thresh_rising_value
# Enable RMS swell detection
root:/sys/bus/iio/devices/iio:device0> echo 1 > events/in_altvoltage0_rms_thresh_rising_en
8. IIO Interfacing Tools
========================
See ``Documentation/iio/iio_tools.rst`` for the description of the available IIO
interfacing tools.

View File

@ -0,0 +1,443 @@
.. SPDX-License-Identifier: GPL-2.0
===============
ADXL345 driver
===============
This driver supports Analog Device's ADXL345/375 on SPI/I2C bus.
1. Supported Devices
====================
* `ADXL345 <https://www.analog.com/ADXL345>`_
* `ADXL375 <https://www.analog.com/ADXL375>`_
The ADXL345 is a generic purpose low power, 3-axis accelerometer with selectable
measurement ranges. The ADXL345 supports the ±2 g, ±4 g, ±8 g, and ±16 g ranges.
2. Device Attributes
====================
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 table shows the ADXL345 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_sampling_frequency | Currently selected sample rate. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_sampling_frequency_available | Available sampling frequency configurations. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_scale | Scale/range for the accelerometer channels. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_scale_available | Available scale ranges for the accelerometer channel. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_x_calibbias | Calibration offset for the X-axis accelerometer channel. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_x_raw | Raw X-axis accelerometer channel value. |
+-------------------------------------------+----------------------------------------------------------+
| in_accel_y_calibbias | y-axis acceleration offset correction |
+-------------------------------------------+----------------------------------------------------------+
| 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_raw | Raw Z-axis accelerometer channel value. |
+-------------------------------------------+----------------------------------------------------------+
Channel 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.
+-------------------------------------+---------------------------+
| Channel type | Measurement unit |
+-------------------------------------+---------------------------+
| Acceleration on X, Y, and Z axis | Meters per second squared |
+-------------------------------------+---------------------------+
Sensor Events
-------------
Specific IIO events are triggered by their corresponding interrupts. The sensor
driver supports either none or a single active interrupt (INT) line, selectable
from the two available options: INT1 or INT2. The active INT line should be
specified in the device tree. If no INT line is configured, the sensor defaults
to FIFO bypass mode, where event detection is disabled and only X, Y, and Z axis
measurements are available.
The table below lists the ADXL345-related device files located in the
device-specific path: ``/sys/bus/iio/devices/iio:deviceX/events``.
Note that activity and inactivity detection are DC-coupled by default;
therefore, only the AC-coupled activity and inactivity events are explicitly
listed.
+---------------------------------------------+---------------------------------------------+
| Event handle | Description |
+---------------------------------------------+---------------------------------------------+
| in_accel_gesture_doubletap_en | Enable double tap detection on all axis |
+---------------------------------------------+---------------------------------------------+
| in_accel_gesture_doubletap_reset_timeout | Double tap window in [us] |
+---------------------------------------------+---------------------------------------------+
| in_accel_gesture_doubletap_tap2_min_delay | Double tap latent in [us] |
+---------------------------------------------+---------------------------------------------+
| in_accel_gesture_singletap_timeout | Single tap duration in [us] |
+---------------------------------------------+---------------------------------------------+
| in_accel_gesture_singletap_value | Single tap threshold value in 62.5/LSB |
+---------------------------------------------+---------------------------------------------+
| in_accel_mag_falling_period | Inactivity time in seconds |
+---------------------------------------------+---------------------------------------------+
| in_accel_mag_falling_value | Inactivity threshold value in 62.5/LSB |
+---------------------------------------------+---------------------------------------------+
| in_accel_mag_adaptive_rising_en | Enable AC coupled activity on X axis |
+---------------------------------------------+---------------------------------------------+
| in_accel_mag_adaptive_falling_period | AC coupled inactivity time in seconds |
+---------------------------------------------+---------------------------------------------+
| in_accel_mag_adaptive_falling_value | AC coupled inactivity threshold in 62.5/LSB |
+---------------------------------------------+---------------------------------------------+
| in_accel_mag_adaptive_rising_value | AC coupled activity threshold in 62.5/LSB |
+---------------------------------------------+---------------------------------------------+
| in_accel_mag_rising_en | Enable activity detection on X axis |
+---------------------------------------------+---------------------------------------------+
| in_accel_mag_rising_value | Activity threshold value in 62.5/LSB |
+---------------------------------------------+---------------------------------------------+
| in_accel_x_gesture_singletap_en | Enable single tap detection on X axis |
+---------------------------------------------+---------------------------------------------+
| in_accel_x&y&z_mag_falling_en | Enable inactivity detection on all axis |
+---------------------------------------------+---------------------------------------------+
| in_accel_x&y&z_mag_adaptive_falling_en | Enable AC coupled inactivity on all axis |
+---------------------------------------------+---------------------------------------------+
| in_accel_y_gesture_singletap_en | Enable single tap detection on Y axis |
+---------------------------------------------+---------------------------------------------+
| in_accel_z_gesture_singletap_en | Enable single tap detection on Z axis |
+---------------------------------------------+---------------------------------------------+
Please refer to the sensor's datasheet for a detailed description of this
functionality.
Manually setting the **ODR** will cause the driver to estimate default values
for inactivity detection timing, where higher ODR values correspond to longer
default wait times, and lower ODR values to shorter ones. If these defaults do
not meet your applications needs, you can explicitly configure the inactivity
wait time. Setting this value to 0 will revert to the default behavior.
When changing the **g range** configuration, the driver attempts to estimate
appropriate activity and inactivity thresholds by scaling the default values
based on the ratio of the previous range to the new one. The resulting threshold
will never be zero and will always fall between 1 and 255, corresponding to up
to 62.5g/LSB as specified in the datasheet. However, you can override these
estimated thresholds by setting explicit values.
When **activity** and **inactivity** events are enabled, the driver
automatically manages hysteresis behavior by setting the **link** and
**auto-sleep** bits. The link bit connects the activity and inactivity
functions, so that one follows the other. The auto-sleep function puts the
sensor into sleep mode when inactivity is detected, reducing power consumption
to the sub-12.5Hz rate.
The inactivity time is configurable between 1 and 255 seconds. In addition to
inactivity detection, the sensor also supports free-fall detection, which, from
the IIO perspective, is treated as a fall in magnitude across all axes. In
sensor terms, free-fall is defined using an inactivity period ranging from 0.000
to 1.000 seconds.
The driver behaves as follows:
* If the configured inactivity period is 1 second or more, the driver uses the
sensor's inactivity register. This allows the event to be linked with
activity detection, use auto-sleep, and be either AC- or DC-coupled.
* If the inactivity period is less than 1 second, the event is treated as plain
inactivity or free-fall detection. In this case, auto-sleep and coupling
(AC/DC) are not applied.
* If an inactivity time of 0 seconds is configured, the driver selects a
heuristically determined default period (greater than 1 second) to optimize
power consumption. This also uses the inactivity register.
Note: According to the datasheet, the optimal ODR for detecting activity,
or inactivity (or when operating with the free-fall register) should fall within
the range of 12.5 Hz to 400 Hz. The recommended free-fall threshold is between
300 mg and 600 mg (register values 0x05 to 0x09).
In DC-coupled mode, the current acceleration magnitude is directly compared to
the values in the THRESH_ACT and THRESH_INACT registers to determine activity or
inactivity. In contrast, AC-coupled activity detection uses the acceleration
value at the start of detection as a reference point, and subsequent samples are
compared against this reference. While DC-coupling is the default mode-comparing
live values to fixed thresholds-AC-coupling relies on an internal filter
relative to the configured threshold.
AC and DC coupling modes are configured separately for activity and inactivity
detection, but only one mode can be active at a time for each. For example, if
AC-coupled activity detection is enabled and then DC-coupled mode is set, only
DC-coupled activity detection will be active. In other words, only the most
recent configuration is applied.
**Single tap** detection can be configured per the datasheet by setting the
threshold and duration parameters. When only single tap detection is enabled,
the single tap interrupt triggers as soon as the acceleration exceeds the
threshold (marking the start of the duration) and then falls below it, provided
the duration limit is not exceeded. If both single tap and double tap detections
are enabled, the single tap interrupt is triggered only after the double tap
event has been either confirmed or dismissed.
To configure **double tap** detection, you must also set the window and latency
parameters in microseconds (µs). The latency period begins once the single tap
signal drops below the threshold and acts as a waiting time during which any
spikes are ignored for double tap detection. After the latency period ends, the
detection window starts. If the acceleration rises above the threshold and then
falls below it again within this window, a double tap event is triggered upon
the fall below the threshold.
Double tap event detection is thoroughly explained in the datasheet. After a
single tap event is detected, a double tap event may follow, provided the signal
meets certain criteria. However, double tap detection can be invalidated for
three reasons:
* If the **suppress bit** is set, any acceleration spike above the tap
threshold during the tap latency period immediately invalidates the double tap
detection. In other words, no spikes are allowed during latency when the
suppress bit is active.
* The double tap event is invalid if the acceleration is above the threshold at
the start of the double tap window.
* Double tap detection is also invalidated if the acceleration duration exceeds
the limit set by the duration register.
For double tap detection, the same duration applies as for single tap: the
acceleration must rise above the threshold and then fall below it within the
specified duration. Note that the suppress bit is typically enabled when double
tap detection is active.
Usage Examples
--------------
Show device name:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat name
adxl345
Show accelerometer channels value:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_raw
-1
root:/sys/bus/iio/devices/iio:device0> cat in_accel_y_raw
2
root:/sys/bus/iio/devices/iio:device0> cat in_accel_z_raw
-253
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 50 > in_accel_x_calibbias
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias
50
Given the 13-bit full resolution, the available ranges are calculated by the
following formula:
.. code-block:: bash
(g * 2 * 9.80665) / (2^(resolution) - 1) * 100; for g := 2|4|8|16
Scale range configuration:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat ./in_accel_scale
0.478899
root:/sys/bus/iio/devices/iio:device0> cat ./in_accel_scale_available
0.478899 0.957798 1.915595 3.831190
root:/sys/bus/iio/devices/iio:device0> echo 1.915595 > ./in_accel_scale
root:/sys/bus/iio/devices/iio:device0> cat ./in_accel_scale
1.915595
Set output data rate (ODR):
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat ./in_accel_sampling_frequency
200.000000
root:/sys/bus/iio/devices/iio:device0> cat ./in_accel_sampling_frequency_available
0.097000 0.195000 0.390000 0.781000 1.562000 3.125000 6.250000 12.500000 25.000000 50.000000 100.000000 200.000000 400.000000 800.000000 1600.000000 3200.000000
root:/sys/bus/iio/devices/iio:device0> echo 1.562000 > ./in_accel_sampling_frequency
root:/sys/bus/iio/devices/iio:device0> cat ./in_accel_sampling_frequency
1.562000
Configure one or several events:
.. code-block:: bash
root:> cd /sys/bus/iio/devices/iio:device0
root:/sys/bus/iio/devices/iio:device0> echo 1 > ./buffer0/in_accel_x_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > ./buffer0/in_accel_y_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > ./buffer0/in_accel_z_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > ./scan_elements/in_accel_x_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > ./scan_elements/in_accel_y_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > ./scan_elements/in_accel_z_en
root:/sys/bus/iio/devices/iio:device0> echo 14 > ./in_accel_x_calibbias
root:/sys/bus/iio/devices/iio:device0> echo 2 > ./in_accel_y_calibbias
root:/sys/bus/iio/devices/iio:device0> echo -250 > ./in_accel_z_calibbias
root:/sys/bus/iio/devices/iio:device0> echo 24 > ./buffer0/length
## AC coupled activity, threshold [62.5/LSB]
root:/sys/bus/iio/devices/iio:device0> echo 6 > ./events/in_accel_mag_adaptive_rising_value
## AC coupled inactivity, threshold, [62.5/LSB]
root:/sys/bus/iio/devices/iio:device0> echo 4 > ./events/in_accel_mag_adaptive_falling_value
## AC coupled inactivity, time [s]
root:/sys/bus/iio/devices/iio:device0> echo 3 > ./events/in_accel_mag_adaptive_falling_period
## singletap, threshold
root:/sys/bus/iio/devices/iio:device0> echo 35 > ./events/in_accel_gesture_singletap_value
## singletap, duration [us]
root:/sys/bus/iio/devices/iio:device0> echo 0.001875 > ./events/in_accel_gesture_singletap_timeout
## doubletap, window [us]
root:/sys/bus/iio/devices/iio:device0> echo 0.025 > ./events/in_accel_gesture_doubletap_reset_timeout
## doubletap, latent [us]
root:/sys/bus/iio/devices/iio:device0> echo 0.025 > ./events/in_accel_gesture_doubletap_tap2_min_delay
## AC coupled activity, enable
root:/sys/bus/iio/devices/iio:device0> echo 1 > ./events/in_accel_mag_adaptive_rising_en
## AC coupled inactivity, enable
root:/sys/bus/iio/devices/iio:device0> echo 1 > ./events/in_accel_x\&y\&z_mag_adaptive_falling_en
## singletap, enable
root:/sys/bus/iio/devices/iio:device0> echo 1 > ./events/in_accel_x_gesture_singletap_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > ./events/in_accel_y_gesture_singletap_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > ./events/in_accel_z_gesture_singletap_en
## doubletap, enable
root:/sys/bus/iio/devices/iio:device0> echo 1 > ./events/in_accel_gesture_doubletap_en
Verify incoming events:
.. code-block:: bash
root:# iio_event_monitor adxl345
Found IIO device with name adxl345 with device number 0
Event: time: 1739063415957073383, type: accel(z), channel: 0, evtype: mag, direction: rising
Event: time: 1739063415963770218, type: accel(z), channel: 0, evtype: mag, direction: rising
Event: time: 1739063416002563061, type: accel(z), channel: 0, evtype: gesture, direction: singletap
Event: time: 1739063426271128739, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling
Event: time: 1739063436539080713, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling
Event: time: 1739063438357970381, type: accel(z), channel: 0, evtype: mag, direction: rising
Event: time: 1739063446726161586, type: accel(z), channel: 0, evtype: mag, direction: rising
Event: time: 1739063446727892670, type: accel(z), channel: 0, evtype: mag, direction: rising
Event: time: 1739063446743019768, type: accel(z), channel: 0, evtype: mag, direction: rising
Event: time: 1739063446744650696, type: accel(z), channel: 0, evtype: mag, direction: rising
Event: time: 1739063446763559386, type: accel(z), channel: 0, evtype: gesture, direction: singletap
Event: time: 1739063448818126480, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling
...
Activity and inactivity belong together and indicate state changes as follows
.. code-block:: bash
root:# iio_event_monitor adxl345
Found IIO device with name adxl345 with device number 0
Event: time: 1744648001133946293, type: accel(x), channel: 0, evtype: mag, direction: rising
<after inactivity time elapsed>
Event: time: 1744648057724775499, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling
...
3. Device Buffers
=================
This driver supports IIO buffers.
All devices support retrieving the raw acceleration and temperature measurements
using buffers.
Usage examples
--------------
Select channels for buffer read:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_x_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_y_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_z_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:> iio_readdev -b 16 -s 1024 adxl345 | hexdump -d
WARNING: High-speed mode not enabled
0000000 00003 00012 00013 00005 00010 00011 00005 00011
0000010 00013 00004 00012 00011 00003 00012 00014 00007
0000020 00011 00013 00004 00013 00014 00003 00012 00013
0000030 00004 00012 00013 00005 00011 00011 00005 00012
0000040 00014 00005 00012 00014 00004 00010 00012 00004
0000050 00013 00011 00003 00011 00012 00005 00011 00013
0000060 00003 00012 00012 00003 00012 00012 00004 00012
0000070 00012 00003 00013 00013 00003 00013 00012 00005
0000080 00012 00013 00003 00011 00012 00005 00012 00013
0000090 00003 00013 00011 00005 00013 00014 00003 00012
00000a0 00012 00003 00012 00013 00004 00012 00015 00004
00000b0 00014 00011 00003 00014 00013 00004 00012 00011
00000c0 00004 00012 00013 00004 00014 00011 00004 00013
00000d0 00012 00002 00014 00012 00005 00012 00013 00005
00000e0 00013 00013 00003 00013 00013 00005 00012 00013
00000f0 00004 00014 00015 00005 00012 00011 00005 00012
...
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

@ -9,11 +9,11 @@ BNO055 driver
This driver supports Bosch BNO055 IMUs (on both serial and I2C busses).
Accelerometer, magnetometer and gyroscope measures are always provided.
Accelerometer, magnetometer and gyroscope measurements are always available.
When "fusion_enable" sysfs attribute is set to 1, orientation (both Euler
angles and quaternion), linear velocity and gravity vector are also
provided, but some sensor settings (e.g. low pass filtering and range)
became locked (the IMU firmware controls them).
become locked (the IMU firmware controls them).
This driver supports also IIO buffers.
@ -24,14 +24,14 @@ The IMU continuously performs an autocalibration procedure if (and only if)
operating in fusion mode. The magnetometer autocalibration can however be
disabled by writing 0 in the sysfs in_magn_calibration_fast_enable attribute.
The driver provides access to autocalibration flags (i.e. you can known if
the IMU has successfully autocalibrated) and to the calibration data blob.
The driver provides access to autocalibration flags (i.e. you can determine
if the IMU has successfully autocalibrated) and to the calibration data blob.
The user can save this blob in a firmware file (i.e. in /lib/firmware) that
the driver looks for at probe time. If found, then the IMU is initialized
with this calibration data. This saves the user from performing the
calibration procedure every time (which consist of moving the IMU in
various way).
calibration procedure every time (which consists of moving the IMU in
various ways).
The driver looks for calibration data file using two different names: first
a file whose name is suffixed with the IMU unique ID (exposed in sysfs as

View File

@ -28,11 +28,13 @@ Industrial I/O Kernel Drivers
ad7606
ad7625
ad7944
ade9000
adis16475
adis16480
adis16550
adxl313
adxl380
adxl345
bno055
ep93xx_adc
opt4060

View File

@ -457,6 +457,11 @@ F: Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml
F: Documentation/iio/ad7380.rst
F: drivers/iio/adc/ad7380.c
AD7476 ADC DRIVER FOR VARIOUS SIMPLE 1-CHANNEL SPI ADCs
M: Matti Vaittinen <mazziesaccount@gmail.com>
S: Maintained
F: drivers/iio/adc/ad7476.c
AD7877 TOUCHSCREEN DRIVER
M: Michael Hennerich <michael.hennerich@analog.com>
S: Supported
@ -12017,6 +12022,14 @@ S: Maintained
F: Documentation/devicetree/bindings/sound/infineon,peb2466.yaml
F: sound/soc/codecs/peb2466.c
INFINEON TLV493D Driver
M: Dixit Parmar <dixitparmar19@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
W: https://www.infineon.com/part/TLV493D-A1B6
F: Documentation/devicetree/bindings/iio/magnetometer/infineon,tlv493d-a1b6.yaml
F: drivers/iio/magnetometer/tlv493d.c
INFINIBAND SUBSYSTEM
M: Jason Gunthorpe <jgg@nvidia.com>
M: Leon Romanovsky <leonro@nvidia.com>
@ -14711,6 +14724,11 @@ F: drivers/regulator/88pm886-regulator.c
F: drivers/rtc/rtc-88pm886.c
F: include/linux/mfd/88pm886.h
MARVELL 88PM886 PMIC GPADC DRIVER
M: Duje Mihanović <duje@dujemihanovic.xyz>
S: Maintained
F: drivers/iio/adc/88pm886-gpadc.c
MARVELL ARMADA 3700 PHY DRIVERS
M: Miquel Raynal <miquel.raynal@bootlin.com>
S: Maintained
@ -15018,9 +15036,9 @@ F: Documentation/devicetree/bindings/regulator/maxim,max20086.yaml
F: drivers/regulator/max20086-regulator.c
MAXIM MAX30208 TEMPERATURE SENSOR DRIVER
M: Rajat Khandelwal <rajat.khandelwal@linux.intel.com>
M: Marcelo Schmitt <marcelo.schmitt@analog.com>
L: linux-iio@vger.kernel.org
S: Maintained
S: Supported
F: drivers/iio/temperature/max30208.c
MAXIM MAX77650 PMIC MFD DRIVER
@ -21888,9 +21906,10 @@ S: Supported
F: drivers/power/supply/bd99954-charger.c
F: drivers/power/supply/bd99954-charger.h
ROHM BD79124 ADC / GPO IC
ROHM BD791xx ADC / GPO IC
M: Matti Vaittinen <mazziesaccount@gmail.com>
S: Supported
F: drivers/iio/adc/rohm-bd79112.c
F: drivers/iio/adc/rohm-bd79124.c
ROHM BH1745 COLOUR SENSOR
@ -26832,6 +26851,12 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/light/vishay,veml6030.yaml
F: drivers/iio/light/veml6030.c
VISHAY VEML6046X00 RGBIR COLOR SENSOR DRIVER
M: Andreas Klinger <ak@it-klinger.de>
S: Maintained
F: Documentation/devicetree/bindings/iio/light/vishay,veml6046x00.yaml
F: drivers/iio/light/veml6046x00.c
VISHAY VEML6075 UVA AND UVB LIGHT SENSOR DRIVER
M: Javier Carrasco <javier.carrasco.cruz@gmail.com>
S: Maintained
@ -27568,7 +27593,8 @@ F: include/uapi/linux/dqblk_xfs.h
F: include/uapi/linux/fsmap.h
XILINX AMS DRIVER
M: Anand Ashok Dumbre <anand.ashok.dumbre@xilinx.com>
M: Salih Erim <salih.erim@amd.com>
M: Conall O'Griofa <conall.ogriofa@amd.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/adc/xlnx,zynqmp-ams.yaml

View File

@ -36,10 +36,29 @@
#define ADXL345_REG_TAP_AXIS_MSK GENMASK(2, 0)
#define ADXL345_REG_TAP_SUPPRESS_MSK BIT(3)
#define ADXL345_REG_TAP_SUPPRESS BIT(3)
#define ADXL345_POWER_CTL_INACT_MSK (ADXL345_POWER_CTL_AUTO_SLEEP | ADXL345_POWER_CTL_LINK)
#define ADXL345_TAP_Z_EN BIT(0)
#define ADXL345_TAP_Y_EN BIT(1)
#define ADXL345_TAP_X_EN BIT(2)
#define ADXL345_REG_TAP_SUPPRESS BIT(3)
#define ADXL345_INACT_Z_EN BIT(0)
#define ADXL345_INACT_Y_EN BIT(1)
#define ADXL345_INACT_X_EN BIT(2)
#define ADXL345_REG_INACT_ACDC BIT(3)
#define ADXL345_ACT_INACT_NO_AXIS_EN 0x00
#define ADXL345_INACT_XYZ_EN (ADXL345_INACT_Z_EN | ADXL345_INACT_Y_EN | ADXL345_INACT_X_EN)
#define ADXL345_ACT_Z_EN BIT(4)
#define ADXL345_ACT_Y_EN BIT(5)
#define ADXL345_ACT_X_EN BIT(6)
#define ADXL345_REG_ACT_ACDC BIT(7)
#define ADXL345_ACT_XYZ_EN (ADXL345_ACT_Z_EN | ADXL345_ACT_Y_EN | ADXL345_ACT_X_EN)
#define ADXL345_COUPLING_DC 0
#define ADXL345_COUPLING_AC 1
#define ADXL345_REG_NO_ACDC 0x00
/* single/double tap */
enum adxl345_tap_type {
@ -64,6 +83,39 @@ static const unsigned int adxl345_tap_time_reg[] = {
[ADXL345_TAP_TIME_DUR] = ADXL345_REG_DUR,
};
/* activity/inactivity */
enum adxl345_activity_type {
ADXL345_ACTIVITY,
ADXL345_INACTIVITY,
ADXL345_ACTIVITY_AC,
ADXL345_INACTIVITY_AC,
ADXL345_INACTIVITY_FF,
};
static const unsigned int adxl345_act_int_reg[] = {
[ADXL345_ACTIVITY] = ADXL345_INT_ACTIVITY,
[ADXL345_INACTIVITY] = ADXL345_INT_INACTIVITY,
[ADXL345_ACTIVITY_AC] = ADXL345_INT_ACTIVITY,
[ADXL345_INACTIVITY_AC] = ADXL345_INT_INACTIVITY,
[ADXL345_INACTIVITY_FF] = ADXL345_INT_FREE_FALL,
};
static const unsigned int adxl345_act_thresh_reg[] = {
[ADXL345_ACTIVITY] = ADXL345_REG_THRESH_ACT,
[ADXL345_INACTIVITY] = ADXL345_REG_THRESH_INACT,
[ADXL345_ACTIVITY_AC] = ADXL345_REG_THRESH_ACT,
[ADXL345_INACTIVITY_AC] = ADXL345_REG_THRESH_INACT,
[ADXL345_INACTIVITY_FF] = ADXL345_REG_THRESH_FF,
};
static const unsigned int adxl345_act_acdc_msk[] = {
[ADXL345_ACTIVITY] = ADXL345_REG_ACT_ACDC,
[ADXL345_INACTIVITY] = ADXL345_REG_INACT_ACDC,
[ADXL345_ACTIVITY_AC] = ADXL345_REG_ACT_ACDC,
[ADXL345_INACTIVITY_AC] = ADXL345_REG_INACT_ACDC,
[ADXL345_INACTIVITY_FF] = ADXL345_REG_NO_ACDC,
};
enum adxl345_odr {
ADXL345_ODR_0P10HZ = 0,
ADXL345_ODR_0P20HZ,
@ -129,6 +181,14 @@ static const int adxl345_fullres_range_tbl[][2] = {
[ADXL345_16G_RANGE] = { 0, 38312 },
};
/* scaling */
static const int adxl345_range_factor_tbl[] = {
[ADXL345_2G_RANGE] = 1,
[ADXL345_4G_RANGE] = 2,
[ADXL345_8G_RANGE] = 4,
[ADXL345_16G_RANGE] = 8,
};
struct adxl345_state {
const struct adxl345_chip_info *info;
struct regmap *regmap;
@ -136,6 +196,9 @@ struct adxl345_state {
u8 watermark;
u8 fifo_mode;
u8 inact_threshold;
u32 inact_time_ms;
u32 tap_duration_us;
u32 tap_latent_us;
u32 tap_window_us;
@ -144,6 +207,22 @@ struct adxl345_state {
};
static const struct iio_event_spec adxl345_events[] = {
{
/* activity */
.type = IIO_EV_TYPE_MAG,
.dir = IIO_EV_DIR_RISING,
.mask_shared_by_type =
BIT(IIO_EV_INFO_ENABLE) |
BIT(IIO_EV_INFO_VALUE),
},
{
/* activity, ac bit set */
.type = IIO_EV_TYPE_MAG_ADAPTIVE,
.dir = IIO_EV_DIR_RISING,
.mask_shared_by_type =
BIT(IIO_EV_INFO_ENABLE) |
BIT(IIO_EV_INFO_VALUE),
},
{
/* single tap */
.type = IIO_EV_TYPE_GESTURE,
@ -188,10 +267,39 @@ enum adxl345_chans {
chan_x, chan_y, chan_z,
};
static const struct iio_event_spec adxl345_fake_chan_events[] = {
{
/* inactivity */
.type = IIO_EV_TYPE_MAG,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type =
BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_PERIOD),
},
{
/* inactivity, AC bit set */
.type = IIO_EV_TYPE_MAG_ADAPTIVE,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type =
BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_PERIOD),
},
};
static const struct iio_chan_spec adxl345_channels[] = {
ADXL345_CHANNEL(0, chan_x, X),
ADXL345_CHANNEL(1, chan_y, Y),
ADXL345_CHANNEL(2, chan_z, Z),
{
.type = IIO_ACCEL,
.modified = 1,
.channel2 = IIO_MOD_X_AND_Y_AND_Z,
.scan_index = -1, /* Fake channel */
.event_spec = adxl345_fake_chan_events,
.num_event_specs = ARRAY_SIZE(adxl345_fake_chan_events),
},
};
static const unsigned long adxl345_scan_masks[] = {
@ -237,6 +345,394 @@ static int adxl345_set_measure_en(struct adxl345_state *st, bool en)
ADXL345_POWER_CTL_MEASURE, en);
}
/* activity / inactivity */
static int adxl345_set_inact_threshold(struct adxl345_state *st,
unsigned int threshold)
{
int ret;
st->inact_threshold = min(U8_MAX, threshold);
ret = regmap_write(st->regmap,
adxl345_act_thresh_reg[ADXL345_INACTIVITY],
st->inact_threshold);
if (ret)
return ret;
return regmap_write(st->regmap,
adxl345_act_thresh_reg[ADXL345_INACTIVITY_FF],
st->inact_threshold);
}
static int adxl345_set_default_time(struct adxl345_state *st)
{
int max_boundary = U8_MAX;
int min_boundary = 10;
enum adxl345_odr odr;
unsigned int regval;
unsigned int val;
int ret;
/* Generated inactivity time based on ODR */
ret = regmap_read(st->regmap, ADXL345_REG_BW_RATE, &regval);
if (ret)
return ret;
odr = FIELD_GET(ADXL345_BW_RATE_MSK, regval);
val = clamp(max_boundary - adxl345_odr_tbl[odr][0],
min_boundary, max_boundary);
st->inact_time_ms = MILLI * val;
/* Inactivity time in s */
return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val);
}
static int adxl345_set_inactivity_time(struct adxl345_state *st, u32 val_int)
{
st->inact_time_ms = MILLI * val_int;
return regmap_write(st->regmap, ADXL345_REG_TIME_INACT, val_int);
}
static int adxl345_set_freefall_time(struct adxl345_state *st, u32 val_fract)
{
/*
* Datasheet max. value is 255 * 5000 us = 1.275000 seconds.
*
* Recommended values between 100ms and 350ms (0x14 to 0x46)
*/
st->inact_time_ms = DIV_ROUND_UP(val_fract, MILLI);
return regmap_write(st->regmap, ADXL345_REG_TIME_FF,
DIV_ROUND_CLOSEST(val_fract, 5));
}
/**
* adxl345_set_inact_time - Configure inactivity time explicitly or by ODR.
* @st: The sensor state instance.
* @val_int: The inactivity time, integer part.
* @val_fract: The inactivity time, fractional part when val_int is 0.
*
* Inactivity time can be configured between 1 and 255 seconds. If a user sets
* val_s to 0, a default inactivity time is calculated automatically (since 0 is
* also invalid and undefined by the sensor).
*
* In such cases, power consumption should be considered: the inactivity period
* should be shorter at higher sampling frequencies and longer at lower ones.
* Specifically, for frequencies above 255 Hz, the default is set to 10 seconds;
* for frequencies below 10 Hz, it defaults to 255 seconds.
*
* The calculation method subtracts the integer part of the configured sample
* frequency from 255 to estimate the inactivity time in seconds. Sub-Hertz
* values are ignored in this approximation. Since the recommended output data
* rates (ODRs) for features like activity/inactivity detection, sleep modes,
* and free fall range between 12.5 Hz and 400 Hz, frequencies outside this
* range will either use the defined boundary defaults or require explicit
* configuration via val_s.
*
* Return: 0 or error value.
*/
static int adxl345_set_inact_time(struct adxl345_state *st, u32 val_int,
u32 val_fract)
{
if (val_int > 0) {
/* Time >= 1s, inactivity */
return adxl345_set_inactivity_time(st, val_int);
} else if (val_int == 0) {
if (val_fract > 0) {
/* Time < 1s, free-fall */
return adxl345_set_freefall_time(st, val_fract);
} else if (val_fract == 0) {
/* Time == 0.0s */
return adxl345_set_default_time(st);
}
}
/* Do not support negative or wrong input. */
return -EINVAL;
}
/**
* adxl345_is_act_inact_ac() - Verify if AC or DC coupling is currently enabled.
*
* @st: The device data.
* @type: The activity or inactivity type.
*
* Given a type of activity / inactivity combined with either AC coupling set or
* default to DC, this function verifies if the combination is currently
* configured, hence enabled or not.
*
* Return: true if configured coupling matches the provided type, else a negative
* error value.
*/
static int adxl345_is_act_inact_ac(struct adxl345_state *st,
enum adxl345_activity_type type)
{
unsigned int regval;
bool coupling;
int ret;
if (type == ADXL345_INACTIVITY_FF)
return true;
ret = regmap_read(st->regmap, ADXL345_REG_ACT_INACT_CTRL, &regval);
if (ret)
return ret;
coupling = adxl345_act_acdc_msk[type] & regval;
switch (type) {
case ADXL345_ACTIVITY:
case ADXL345_INACTIVITY:
return coupling == ADXL345_COUPLING_DC;
case ADXL345_ACTIVITY_AC:
case ADXL345_INACTIVITY_AC:
return coupling == ADXL345_COUPLING_AC;
default:
return -EINVAL;
}
}
/**
* adxl345_set_act_inact_ac() - Configure AC coupling or DC coupling.
*
* @st: The device data.
* @type: Provide a type of activity or inactivity.
* @cmd_en: enable or disable AC coupling.
*
* Enables AC coupling or DC coupling depending on the provided type argument.
* Note: Activity and inactivity can be either AC coupled or DC coupled not
* both at the same time.
*
* Return: 0 if successful, else error value.
*/
static int adxl345_set_act_inact_ac(struct adxl345_state *st,
enum adxl345_activity_type type,
bool cmd_en)
{
unsigned int act_inact_ac;
if (type == ADXL345_ACTIVITY_AC || type == ADXL345_INACTIVITY_AC)
act_inact_ac = ADXL345_COUPLING_AC && cmd_en;
else
act_inact_ac = ADXL345_COUPLING_DC && cmd_en;
/*
* A setting of false selects dc-coupled operation, and a setting of
* true enables ac-coupled operation. In dc-coupled operation, the
* current acceleration magnitude is compared directly with
* ADXL345_REG_THRESH_ACT and ADXL345_REG_THRESH_INACT to determine
* whether activity or inactivity is detected.
*
* In ac-coupled operation for activity detection, the acceleration
* value at the start of activity detection is taken as a reference
* value. New samples of acceleration are then compared to this
* reference value, and if the magnitude of the difference exceeds the
* ADXL345_REG_THRESH_ACT value, the device triggers an activity
* interrupt.
*
* Similarly, in ac-coupled operation for inactivity detection, a
* reference value is used for comparison and is updated whenever the
* device exceeds the inactivity threshold. After the reference value
* is selected, the device compares the magnitude of the difference
* between the reference value and the current acceleration with
* ADXL345_REG_THRESH_INACT. If the difference is less than the value in
* ADXL345_REG_THRESH_INACT for the time in ADXL345_REG_TIME_INACT, the
* device is considered inactive and the inactivity interrupt is
* triggered. [quoted from p. 24, ADXL345 datasheet Rev. G]
*
* In a conclusion, the first acceleration snapshot sample which hit the
* threshold in a particular direction is always taken as acceleration
* reference value to that direction. Since for the hardware activity
* and inactivity depend on the x/y/z axis, so do ac and dc coupling.
* Note, this sw driver always enables or disables all three x/y/z axis
* for detection via act_axis_ctrl and inact_axis_ctrl, respectively.
* Where in dc-coupling samples are compared against the thresholds, in
* ac-coupling measurement difference to the first acceleration
* reference value are compared against the threshold. So, ac-coupling
* allows for a bit more dynamic compensation depending on the initial
* sample.
*/
return regmap_assign_bits(st->regmap, ADXL345_REG_ACT_INACT_CTRL,
adxl345_act_acdc_msk[type], act_inact_ac);
}
static int adxl345_is_act_inact_en(struct adxl345_state *st,
enum adxl345_activity_type type)
{
unsigned int axis_ctrl;
unsigned int regval;
bool int_en, en;
int ret;
ret = regmap_read(st->regmap, ADXL345_REG_ACT_INACT_CTRL, &axis_ctrl);
if (ret)
return ret;
/* Check if axis for activity are enabled */
switch (type) {
case ADXL345_ACTIVITY:
case ADXL345_ACTIVITY_AC:
en = FIELD_GET(ADXL345_ACT_XYZ_EN, axis_ctrl);
if (!en)
return false;
break;
case ADXL345_INACTIVITY:
case ADXL345_INACTIVITY_AC:
en = FIELD_GET(ADXL345_INACT_XYZ_EN, axis_ctrl);
if (!en)
return false;
break;
case ADXL345_INACTIVITY_FF:
en = true;
break;
default:
return -EINVAL;
}
/* Check if specific interrupt is enabled */
ret = regmap_read(st->regmap, ADXL345_REG_INT_ENABLE, &regval);
if (ret)
return ret;
int_en = adxl345_act_int_reg[type] & regval;
if (!int_en)
return false;
/* Check if configured coupling matches provided type */
return adxl345_is_act_inact_ac(st, type);
}
static int adxl345_set_act_inact_linkbit(struct adxl345_state *st,
enum adxl345_activity_type type,
bool en)
{
int act_ac_en, inact_ac_en;
int act_en, inact_en;
act_en = adxl345_is_act_inact_en(st, ADXL345_ACTIVITY);
if (act_en < 0)
return act_en;
act_ac_en = adxl345_is_act_inact_en(st, ADXL345_ACTIVITY_AC);
if (act_ac_en < 0)
return act_ac_en;
if (type == ADXL345_INACTIVITY_FF) {
inact_en = false;
} else {
inact_en = adxl345_is_act_inact_en(st, ADXL345_INACTIVITY);
if (inact_en < 0)
return inact_en;
inact_ac_en = adxl345_is_act_inact_en(st, ADXL345_INACTIVITY_AC);
if (inact_ac_en < 0)
return inact_ac_en;
inact_en = inact_en || inact_ac_en;
}
act_en = act_en || act_ac_en;
return regmap_assign_bits(st->regmap, ADXL345_REG_POWER_CTL,
ADXL345_POWER_CTL_INACT_MSK,
en && act_en && inact_en);
}
static int adxl345_set_act_inact_en(struct adxl345_state *st,
enum adxl345_activity_type type,
bool cmd_en)
{
unsigned int axis_ctrl;
unsigned int threshold;
unsigned int period;
int ret;
if (cmd_en) {
/* When turning on, check if threshold is valid */
if (type == ADXL345_ACTIVITY || type == ADXL345_ACTIVITY_AC) {
ret = regmap_read(st->regmap,
adxl345_act_thresh_reg[type],
&threshold);
if (ret)
return ret;
} else {
threshold = st->inact_threshold;
}
if (!threshold) /* Just ignore the command if threshold is 0 */
return 0;
/* When turning on inactivity, check if inact time is valid */
if (type == ADXL345_INACTIVITY || type == ADXL345_INACTIVITY_AC) {
ret = regmap_read(st->regmap,
ADXL345_REG_TIME_INACT,
&period);
if (ret)
return ret;
if (!period)
return 0;
}
} else {
/*
* When turning off an activity, ensure that the correct
* coupling event is specified. This step helps prevent misuse -
* for example, if an AC-coupled activity is active and the
* current call attempts to turn off a DC-coupled activity, this
* inconsistency should be detected here.
*/
if (adxl345_is_act_inact_ac(st, type) <= 0)
return 0;
}
/* Start modifying configuration registers */
ret = adxl345_set_measure_en(st, false);
if (ret)
return ret;
/* Enable axis according to the command */
switch (type) {
case ADXL345_ACTIVITY:
case ADXL345_ACTIVITY_AC:
axis_ctrl = ADXL345_ACT_XYZ_EN;
break;
case ADXL345_INACTIVITY:
case ADXL345_INACTIVITY_AC:
axis_ctrl = ADXL345_INACT_XYZ_EN;
break;
case ADXL345_INACTIVITY_FF:
axis_ctrl = ADXL345_ACT_INACT_NO_AXIS_EN;
break;
default:
return -EINVAL;
}
ret = regmap_assign_bits(st->regmap, ADXL345_REG_ACT_INACT_CTRL,
axis_ctrl, cmd_en);
if (ret)
return ret;
/* Update AC/DC-coupling according to the command */
ret = adxl345_set_act_inact_ac(st, type, cmd_en);
if (ret)
return ret;
/* Enable the interrupt line, according to the command */
ret = regmap_assign_bits(st->regmap, ADXL345_REG_INT_ENABLE,
adxl345_act_int_reg[type], cmd_en);
if (ret)
return ret;
/* Set link-bit and auto-sleep only when ACT and INACT are enabled */
ret = adxl345_set_act_inact_linkbit(st, type, cmd_en);
if (ret)
return ret;
return adxl345_set_measure_en(st, true);
}
/* tap */
static int _adxl345_set_tap_int(struct adxl345_state *st,
@ -368,9 +864,8 @@ static int adxl345_set_doubletap_en(struct adxl345_state *st, bool en)
* Generally suppress detection of spikes during the latency period as
* double taps here, this is fully optional for double tap detection
*/
ret = regmap_update_bits(st->regmap, ADXL345_REG_TAP_AXIS,
ADXL345_REG_TAP_SUPPRESS_MSK,
en ? ADXL345_REG_TAP_SUPPRESS : 0x00);
ret = regmap_assign_bits(st->regmap, ADXL345_REG_TAP_AXIS,
ADXL345_REG_TAP_SUPPRESS, en);
if (ret)
return ret;
@ -466,9 +961,16 @@ static int adxl345_find_odr(struct adxl345_state *st, int val,
static int adxl345_set_odr(struct adxl345_state *st, enum adxl345_odr odr)
{
return regmap_update_bits(st->regmap, ADXL345_REG_BW_RATE,
int ret;
ret = regmap_update_bits(st->regmap, ADXL345_REG_BW_RATE,
ADXL345_BW_RATE_MSK,
FIELD_PREP(ADXL345_BW_RATE_MSK, odr));
if (ret)
return ret;
/* update inactivity time by ODR */
return adxl345_set_inact_time(st, 0, 0);
}
static int adxl345_find_range(struct adxl345_state *st, int val, int val2,
@ -489,9 +991,43 @@ static int adxl345_find_range(struct adxl345_state *st, int val, int val2,
static int adxl345_set_range(struct adxl345_state *st, enum adxl345_range range)
{
return regmap_update_bits(st->regmap, ADXL345_REG_DATA_FORMAT,
unsigned int act_threshold, inact_threshold;
unsigned int range_old;
unsigned int regval;
int ret;
ret = regmap_read(st->regmap, ADXL345_REG_DATA_FORMAT, &regval);
if (ret)
return ret;
range_old = FIELD_GET(ADXL345_DATA_FORMAT_RANGE, regval);
ret = regmap_read(st->regmap,
adxl345_act_thresh_reg[ADXL345_ACTIVITY],
&act_threshold);
if (ret)
return ret;
ret = regmap_update_bits(st->regmap, ADXL345_REG_DATA_FORMAT,
ADXL345_DATA_FORMAT_RANGE,
FIELD_PREP(ADXL345_DATA_FORMAT_RANGE, range));
if (ret)
return ret;
act_threshold = act_threshold * adxl345_range_factor_tbl[range_old]
/ adxl345_range_factor_tbl[range];
act_threshold = min(U8_MAX, max(1, act_threshold));
inact_threshold = st->inact_threshold;
inact_threshold = inact_threshold * adxl345_range_factor_tbl[range_old]
/ adxl345_range_factor_tbl[range];
inact_threshold = min(U8_MAX, max(1, inact_threshold));
ret = regmap_write(st->regmap, adxl345_act_thresh_reg[ADXL345_ACTIVITY],
act_threshold);
if (ret)
return ret;
return adxl345_set_inact_threshold(st, inact_threshold);
}
static int adxl345_read_avail(struct iio_dev *indio_dev,
@ -624,6 +1160,37 @@ static int adxl345_write_raw(struct iio_dev *indio_dev,
return adxl345_set_measure_en(st, true);
}
static int adxl345_read_mag_config(struct adxl345_state *st,
enum iio_event_direction dir,
enum adxl345_activity_type type_act,
enum adxl345_activity_type type_inact)
{
switch (dir) {
case IIO_EV_DIR_RISING:
return !!adxl345_is_act_inact_en(st, type_act);
case IIO_EV_DIR_FALLING:
return !!adxl345_is_act_inact_en(st, type_inact);
default:
return -EINVAL;
}
}
static int adxl345_write_mag_config(struct adxl345_state *st,
enum iio_event_direction dir,
enum adxl345_activity_type type_act,
enum adxl345_activity_type type_inact,
bool state)
{
switch (dir) {
case IIO_EV_DIR_RISING:
return adxl345_set_act_inact_en(st, type_act, state);
case IIO_EV_DIR_FALLING:
return adxl345_set_act_inact_en(st, type_inact, state);
default:
return -EINVAL;
}
}
static int adxl345_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
@ -634,6 +1201,14 @@ static int adxl345_read_event_config(struct iio_dev *indio_dev,
int ret;
switch (type) {
case IIO_EV_TYPE_MAG:
return adxl345_read_mag_config(st, dir,
ADXL345_ACTIVITY,
ADXL345_INACTIVITY);
case IIO_EV_TYPE_MAG_ADAPTIVE:
return adxl345_read_mag_config(st, dir,
ADXL345_ACTIVITY_AC,
ADXL345_INACTIVITY_AC);
case IIO_EV_TYPE_GESTURE:
switch (dir) {
case IIO_EV_DIR_SINGLETAP:
@ -665,6 +1240,16 @@ static int adxl345_write_event_config(struct iio_dev *indio_dev,
struct adxl345_state *st = iio_priv(indio_dev);
switch (type) {
case IIO_EV_TYPE_MAG:
return adxl345_write_mag_config(st, dir,
ADXL345_ACTIVITY,
ADXL345_INACTIVITY,
state);
case IIO_EV_TYPE_MAG_ADAPTIVE:
return adxl345_write_mag_config(st, dir,
ADXL345_ACTIVITY_AC,
ADXL345_INACTIVITY_AC,
state);
case IIO_EV_TYPE_GESTURE:
switch (dir) {
case IIO_EV_DIR_SINGLETAP:
@ -679,6 +1264,72 @@ static int adxl345_write_event_config(struct iio_dev *indio_dev,
}
}
static int adxl345_read_mag_value(struct adxl345_state *st,
enum iio_event_direction dir,
enum iio_event_info info,
enum adxl345_activity_type type_act,
enum adxl345_activity_type type_inact,
int *val, int *val2)
{
unsigned int threshold;
int ret;
switch (info) {
case IIO_EV_INFO_VALUE:
switch (dir) {
case IIO_EV_DIR_RISING:
ret = regmap_read(st->regmap,
adxl345_act_thresh_reg[type_act],
&threshold);
if (ret)
return ret;
*val = 62500 * threshold;
*val2 = MICRO;
return IIO_VAL_FRACTIONAL;
case IIO_EV_DIR_FALLING:
*val = 62500 * st->inact_threshold;
*val2 = MICRO;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
case IIO_EV_INFO_PERIOD:
*val = st->inact_time_ms;
*val2 = MILLI;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
}
static int adxl345_write_mag_value(struct adxl345_state *st,
enum iio_event_direction dir,
enum iio_event_info info,
enum adxl345_activity_type type_act,
enum adxl345_activity_type type_inact,
int val, int val2)
{
switch (info) {
case IIO_EV_INFO_VALUE:
/* Scaling factor 62.5mg/LSB, i.e. ~16g corresponds to 0xff */
val = DIV_ROUND_CLOSEST(val * MICRO + val2, 62500);
switch (dir) {
case IIO_EV_DIR_RISING:
return regmap_write(st->regmap,
adxl345_act_thresh_reg[type_act],
val);
case IIO_EV_DIR_FALLING:
return adxl345_set_inact_threshold(st, val);
default:
return -EINVAL;
}
case IIO_EV_INFO_PERIOD:
return adxl345_set_inact_time(st, val, val2);
default:
return -EINVAL;
}
}
static int adxl345_read_event_value(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
@ -691,6 +1342,16 @@ static int adxl345_read_event_value(struct iio_dev *indio_dev,
int ret;
switch (type) {
case IIO_EV_TYPE_MAG:
return adxl345_read_mag_value(st, dir, info,
ADXL345_ACTIVITY,
ADXL345_INACTIVITY,
val, val2);
case IIO_EV_TYPE_MAG_ADAPTIVE:
return adxl345_read_mag_value(st, dir, info,
ADXL345_ACTIVITY_AC,
ADXL345_INACTIVITY_AC,
val, val2);
case IIO_EV_TYPE_GESTURE:
switch (info) {
case IIO_EV_INFO_VALUE:
@ -741,6 +1402,22 @@ static int adxl345_write_event_value(struct iio_dev *indio_dev,
return ret;
switch (type) {
case IIO_EV_TYPE_MAG:
ret = adxl345_write_mag_value(st, dir, info,
ADXL345_ACTIVITY,
ADXL345_INACTIVITY,
val, val2);
if (ret)
return ret;
break;
case IIO_EV_TYPE_MAG_ADAPTIVE:
ret = adxl345_write_mag_value(st, dir, info,
ADXL345_ACTIVITY_AC,
ADXL345_INACTIVITY_AC,
val, val2);
if (ret)
return ret;
break;
case IIO_EV_TYPE_GESTURE:
switch (info) {
case IIO_EV_INFO_VALUE:
@ -980,10 +1657,12 @@ static int adxl345_fifo_push(struct iio_dev *indio_dev,
}
static int adxl345_push_event(struct iio_dev *indio_dev, int int_stat,
enum iio_modifier act_dir,
enum iio_modifier tap_dir)
{
s64 ts = iio_get_time_ns(indio_dev);
struct adxl345_state *st = iio_priv(indio_dev);
unsigned int regval;
int samples;
int ret = -ENOENT;
@ -1007,6 +1686,68 @@ static int adxl345_push_event(struct iio_dev *indio_dev, int int_stat,
return ret;
}
if (FIELD_GET(ADXL345_INT_ACTIVITY, int_stat)) {
ret = regmap_read(st->regmap, ADXL345_REG_ACT_INACT_CTRL, &regval);
if (ret)
return ret;
if (FIELD_GET(ADXL345_REG_ACT_ACDC, regval)) {
/* AC coupled */
ret = iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, act_dir,
IIO_EV_TYPE_MAG_ADAPTIVE,
IIO_EV_DIR_RISING),
ts);
} else {
/* DC coupled, relying on THRESH */
ret = iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, act_dir,
IIO_EV_TYPE_MAG,
IIO_EV_DIR_RISING),
ts);
}
if (ret)
return ret;
}
if (FIELD_GET(ADXL345_INT_INACTIVITY, int_stat)) {
ret = regmap_read(st->regmap, ADXL345_REG_ACT_INACT_CTRL, &regval);
if (ret)
return ret;
if (FIELD_GET(ADXL345_REG_INACT_ACDC, regval)) {
/* AC coupled */
ret = iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
IIO_MOD_X_AND_Y_AND_Z,
IIO_EV_TYPE_MAG_ADAPTIVE,
IIO_EV_DIR_FALLING),
ts);
} else {
/* DC coupled, relying on THRESH */
ret = iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
IIO_MOD_X_AND_Y_AND_Z,
IIO_EV_TYPE_MAG,
IIO_EV_DIR_FALLING),
ts);
}
if (ret)
return ret;
}
if (FIELD_GET(ADXL345_INT_FREE_FALL, int_stat)) {
ret = iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
IIO_MOD_X_AND_Y_AND_Z,
IIO_EV_TYPE_MAG,
IIO_EV_DIR_FALLING),
ts);
if (ret)
return ret;
}
if (FIELD_GET(ADXL345_INT_WATERMARK, int_stat)) {
samples = adxl345_get_samples(st);
if (samples < 0)
@ -1034,6 +1775,7 @@ static irqreturn_t adxl345_irq_handler(int irq, void *p)
struct adxl345_state *st = iio_priv(indio_dev);
unsigned int regval;
enum iio_modifier tap_dir = IIO_NO_MOD;
enum iio_modifier act_dir = IIO_NO_MOD;
u32 axis_ctrl;
int int_stat;
int ret;
@ -1042,7 +1784,8 @@ static irqreturn_t adxl345_irq_handler(int irq, void *p)
if (ret)
return IRQ_NONE;
if (FIELD_GET(ADXL345_REG_TAP_AXIS_MSK, axis_ctrl)) {
if (FIELD_GET(ADXL345_REG_TAP_AXIS_MSK, axis_ctrl) ||
FIELD_GET(ADXL345_ACT_XYZ_EN, axis_ctrl)) {
ret = regmap_read(st->regmap, ADXL345_REG_ACT_TAP_STATUS, &regval);
if (ret)
return IRQ_NONE;
@ -1053,12 +1796,19 @@ static irqreturn_t adxl345_irq_handler(int irq, void *p)
tap_dir = IIO_MOD_Y;
else if (FIELD_GET(ADXL345_TAP_X_EN, regval))
tap_dir = IIO_MOD_X;
if (FIELD_GET(ADXL345_ACT_Z_EN, regval))
act_dir = IIO_MOD_Z;
else if (FIELD_GET(ADXL345_ACT_Y_EN, regval))
act_dir = IIO_MOD_Y;
else if (FIELD_GET(ADXL345_ACT_X_EN, regval))
act_dir = IIO_MOD_X;
}
if (regmap_read(st->regmap, ADXL345_REG_INT_SOURCE, &int_stat))
return IRQ_NONE;
if (adxl345_push_event(indio_dev, int_stat, tap_dir))
if (adxl345_push_event(indio_dev, int_stat, act_dir, tap_dir))
goto err;
if (FIELD_GET(ADXL345_INT_OVERRUN, int_stat))
@ -1226,6 +1976,24 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
if (ret)
return ret;
/*
* Initialized with sensible default values to streamline
* sensor operation. These defaults are partly derived from
* the previous input driver for the ADXL345 and partly
* based on the recommendations provided in the datasheet.
*/
ret = regmap_write(st->regmap, ADXL345_REG_ACT_INACT_CTRL, 0);
if (ret)
return ret;
ret = regmap_write(st->regmap, ADXL345_REG_THRESH_ACT, 6);
if (ret)
return ret;
ret = adxl345_set_inact_threshold(st, 4);
if (ret)
return ret;
ret = regmap_write(st->regmap, ADXL345_REG_THRESH_TAP, tap_threshold);
if (ret)
return ret;

View File

@ -139,11 +139,6 @@ struct bma180_data {
int scale;
int bw;
bool pmode;
/* Ensure timestamp is naturally aligned */
struct {
s16 chan[4];
aligned_s64 timestamp;
} scan;
};
enum bma180_chan {
@ -870,6 +865,10 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p)
struct bma180_data *data = iio_priv(indio_dev);
s64 time_ns = iio_get_time_ns(indio_dev);
int bit, ret, i = 0;
struct {
s16 chan[4];
aligned_s64 timestamp;
} scan = { };
mutex_lock(&data->mutex);
@ -879,12 +878,12 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p)
mutex_unlock(&data->mutex);
goto err;
}
data->scan.chan[i++] = ret;
scan.chan[i++] = ret;
}
mutex_unlock(&data->mutex);
iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan), time_ns);
iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), time_ns);
err:
iio_trigger_notify_done(indio_dev->trig);

View File

@ -255,10 +255,8 @@ static int bma220_probe(struct spi_device *spi)
struct bma220_data *data;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
if (!indio_dev) {
dev_err(&spi->dev, "iio allocation failed!\n");
if (!indio_dev)
return -ENOMEM;
}
data = iio_priv(indio_dev);
data->spi_device = spi;

View File

@ -332,13 +332,10 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on)
struct device *dev = regmap_get_device(data->regmap);
int ret;
if (on) {
if (on)
ret = pm_runtime_resume_and_get(dev);
} else {
pm_runtime_mark_last_busy(dev);
else
ret = pm_runtime_put_autosuspend(dev);
}
if (ret < 0) {
dev_err(dev,
"Failed: %s for %d\n", __func__, on);

View File

@ -375,7 +375,6 @@ static int bmi088_accel_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
out_read_raw_pm_put:
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
@ -419,7 +418,6 @@ static int bmi088_accel_write_raw(struct iio_dev *indio_dev,
return ret;
ret = bmi088_accel_set_scale(data, val, val2);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
@ -428,7 +426,6 @@ static int bmi088_accel_write_raw(struct iio_dev *indio_dev,
return ret;
ret = bmi088_accel_set_sample_freq(data, val);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
default:

View File

@ -137,10 +137,8 @@ static int dmard06_probe(struct i2c_client *client)
}
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dmard06));
if (!indio_dev) {
dev_err(&client->dev, "Failed to allocate iio device\n");
if (!indio_dev)
return -ENOMEM;
}
dmard06 = iio_priv(indio_dev);
dmard06->client = client;

View File

@ -95,10 +95,8 @@ static int dmard09_probe(struct i2c_client *client)
struct dmard09_data *data;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev) {
dev_err(&client->dev, "iio allocation failed\n");
if (!indio_dev)
return -ENOMEM;
}
data = iio_priv(indio_dev);
data->client = client;

View File

@ -191,10 +191,8 @@ static int dmard10_probe(struct i2c_client *client)
return (ret < 0) ? ret : -ENODEV;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev) {
dev_err(&client->dev, "iio allocation failed!\n");
if (!indio_dev)
return -ENOMEM;
}
data = iio_priv(indio_dev);
data->client = client;

View File

@ -222,7 +222,6 @@ static int fxls8962af_power_off(struct fxls8962af_data *data)
struct device *dev = regmap_get_device(data->regmap);
int ret;
pm_runtime_mark_last_busy(dev);
ret = pm_runtime_put_autosuspend(dev);
if (ret)
dev_err(dev, "failed to power off\n");

View File

@ -636,10 +636,8 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
if (on)
ret = pm_runtime_resume_and_get(&data->client->dev);
else {
pm_runtime_mark_last_busy(&data->client->dev);
else
ret = pm_runtime_put_autosuspend(&data->client->dev);
}
if (ret < 0) {
dev_err(&data->client->dev,
"Failed: %s for %d\n", __func__, on);

View File

@ -151,7 +151,6 @@ static int kxsd9_write_raw(struct iio_dev *indio_dev,
ret = kxsd9_write_scale(indio_dev, val2);
}
pm_runtime_mark_last_busy(st->dev);
pm_runtime_put_autosuspend(st->dev);
return ret;
@ -199,7 +198,6 @@ static int kxsd9_read_raw(struct iio_dev *indio_dev,
}
error_ret:
pm_runtime_mark_last_busy(st->dev);
pm_runtime_put_autosuspend(st->dev);
return ret;
@ -250,7 +248,6 @@ static int kxsd9_buffer_postdisable(struct iio_dev *indio_dev)
{
struct kxsd9_state *st = iio_priv(indio_dev);
pm_runtime_mark_last_busy(st->dev);
pm_runtime_put_autosuspend(st->dev);
return 0;

View File

@ -169,10 +169,8 @@ static int mc3230_probe(struct i2c_client *client)
}
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev) {
dev_err(&client->dev, "iio allocation failed!\n");
if (!indio_dev)
return -ENOMEM;
}
data = iio_priv(indio_dev);
data->chip_info = chip_info;

View File

@ -192,10 +192,8 @@ static int mma7660_probe(struct i2c_client *client)
struct mma7660_data *data;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev) {
dev_err(&client->dev, "iio allocation failed!\n");
if (!indio_dev)
return -ENOMEM;
}
data = iio_priv(indio_dev);
data->client = client;

View File

@ -224,13 +224,10 @@ static int mma8452_set_runtime_pm_state(struct i2c_client *client, bool on)
#ifdef CONFIG_PM
int ret;
if (on) {
if (on)
ret = pm_runtime_resume_and_get(&client->dev);
} else {
pm_runtime_mark_last_busy(&client->dev);
else
ret = pm_runtime_put_autosuspend(&client->dev);
}
if (ret < 0) {
dev_err(&client->dev,
"failed to change power state to %d\n", on);

View File

@ -671,11 +671,8 @@ int mma9551_set_power_state(struct i2c_client *client, bool on)
if (on)
ret = pm_runtime_resume_and_get(&client->dev);
else {
pm_runtime_mark_last_busy(&client->dev);
else
ret = pm_runtime_put_autosuspend(&client->dev);
}
if (ret < 0) {
dev_err(&client->dev,
"failed to change power state to %d\n", on);

View File

@ -607,7 +607,6 @@ static int msa311_read_raw_data(struct iio_dev *indio_dev,
err = msa311_get_axis(msa311, chan, &axis);
mutex_unlock(&msa311->lock);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
iio_device_release_direct(indio_dev);
@ -741,7 +740,6 @@ static int msa311_write_scale(struct iio_dev *indio_dev, int val, int val2)
break;
}
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
if (err)
@ -781,7 +779,6 @@ static int msa311_write_samp_freq(struct iio_dev *indio_dev, int val, int val2)
break;
}
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
iio_device_release_direct(indio_dev);
@ -832,7 +829,6 @@ static int msa311_debugfs_reg_access(struct iio_dev *indio_dev,
mutex_unlock(&msa311->lock);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
if (err)
@ -855,7 +851,6 @@ static int msa311_buffer_postdisable(struct iio_dev *indio_dev)
struct msa311_priv *msa311 = iio_priv(indio_dev);
struct device *dev = msa311->dev;
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
@ -990,7 +985,7 @@ static int msa311_check_partid(struct msa311_priv *msa311)
msa311->chip_name = devm_kasprintf(dev, GFP_KERNEL,
"msa311-%02x", partid);
if (!msa311->chip_name)
return dev_err_probe(dev, -ENOMEM, "can't alloc chip name\n");
return -ENOMEM;
return 0;
}
@ -1069,8 +1064,7 @@ static int msa311_setup_interrupts(struct msa311_priv *msa311)
trig = devm_iio_trigger_alloc(dev, "%s-new-data", msa311->chip_name);
if (!trig)
return dev_err_probe(dev, -ENOMEM,
"can't allocate newdata trigger\n");
return -ENOMEM;
msa311->new_data_trig = trig;
msa311->new_data_trig->ops = &msa311_new_data_trig_ops;
@ -1153,8 +1147,7 @@ static int msa311_probe(struct i2c_client *i2c)
indio_dev = devm_iio_device_alloc(dev, sizeof(*msa311));
if (!indio_dev)
return dev_err_probe(dev, -ENOMEM,
"IIO device allocation failed\n");
return -ENOMEM;
msa311 = iio_priv(indio_dev);
msa311->dev = dev;
@ -1195,7 +1188,7 @@ static int msa311_probe(struct i2c_client *i2c)
*/
err = devm_add_action_or_reset(dev, msa311_powerdown, msa311);
if (err)
return dev_err_probe(dev, err, "can't add powerdown action\n");
return err;
err = pm_runtime_set_active(dev);
if (err)
@ -1231,7 +1224,6 @@ static int msa311_probe(struct i2c_client *i2c)
if (err)
return err;
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
err = devm_iio_device_register(dev, indio_dev);

View File

@ -504,10 +504,8 @@ static int stk8312_probe(struct i2c_client *client)
struct stk8312_data *data;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev) {
dev_err(&client->dev, "iio allocation failed!\n");
if (!indio_dev)
return -ENOMEM;
}
data = iio_priv(indio_dev);
data->client = client;

View File

@ -385,10 +385,8 @@ static int stk8ba50_probe(struct i2c_client *client)
struct stk8ba50_data *data;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev) {
dev_err(&client->dev, "iio allocation failed!\n");
if (!indio_dev)
return -ENOMEM;
}
data = iio_priv(indio_dev);
data->client = client;

View File

@ -0,0 +1,393 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2025, Duje Mihanović <duje@dujemihanovic.xyz>
*/
#include <linux/bits.h>
#include <linux/bug.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/math.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include <linux/units.h>
#include <asm/byteorder.h>
#include <linux/iio/iio.h>
#include <linux/iio/types.h>
#include <linux/mfd/88pm886.h>
struct pm886_gpadc {
struct regmap *map;
};
enum pm886_gpadc_channel {
VSC_CHAN,
VCHG_PWR_CHAN,
VCF_OUT_CHAN,
VBAT_CHAN,
VBAT_SLP_CHAN,
VBUS_CHAN,
GPADC0_CHAN,
GPADC1_CHAN,
GPADC2_CHAN,
GPADC3_CHAN,
GND_DET1_CHAN,
GND_DET2_CHAN,
MIC_DET_CHAN,
TINT_CHAN,
};
static const int pm886_gpadc_regs[] = {
[VSC_CHAN] = PM886_REG_GPADC_VSC,
[VCHG_PWR_CHAN] = PM886_REG_GPADC_VCHG_PWR,
[VCF_OUT_CHAN] = PM886_REG_GPADC_VCF_OUT,
[VBAT_CHAN] = PM886_REG_GPADC_VBAT,
[VBAT_SLP_CHAN] = PM886_REG_GPADC_VBAT_SLP,
[VBUS_CHAN] = PM886_REG_GPADC_VBUS,
[GPADC0_CHAN] = PM886_REG_GPADC_GPADC0,
[GPADC1_CHAN] = PM886_REG_GPADC_GPADC1,
[GPADC2_CHAN] = PM886_REG_GPADC_GPADC2,
[GPADC3_CHAN] = PM886_REG_GPADC_GPADC3,
[GND_DET1_CHAN] = PM886_REG_GPADC_GND_DET1,
[GND_DET2_CHAN] = PM886_REG_GPADC_GND_DET2,
[MIC_DET_CHAN] = PM886_REG_GPADC_MIC_DET,
[TINT_CHAN] = PM886_REG_GPADC_TINT,
};
#define ADC_CHANNEL_VOLTAGE(index, lsb, name) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = index, \
.address = lsb, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.datasheet_name = name, \
}
#define ADC_CHANNEL_RESISTANCE(index, lsb, name) \
{ \
.type = IIO_RESISTANCE, \
.indexed = 1, \
.channel = index, \
.address = lsb, \
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
.datasheet_name = name, \
}
#define ADC_CHANNEL_TEMPERATURE(index, lsb, name) \
{ \
.type = IIO_TEMP, \
.indexed = 1, \
.channel = index, \
.address = lsb, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_OFFSET), \
.datasheet_name = name, \
}
static const struct iio_chan_spec pm886_gpadc_channels[] = {
ADC_CHANNEL_VOLTAGE(VSC_CHAN, 1367, "vsc"),
ADC_CHANNEL_VOLTAGE(VCHG_PWR_CHAN, 1709, "vchg_pwr"),
ADC_CHANNEL_VOLTAGE(VCF_OUT_CHAN, 1367, "vcf_out"),
ADC_CHANNEL_VOLTAGE(VBAT_CHAN, 1367, "vbat"),
ADC_CHANNEL_VOLTAGE(VBAT_SLP_CHAN, 1367, "vbat_slp"),
ADC_CHANNEL_VOLTAGE(VBUS_CHAN, 1709, "vbus"),
ADC_CHANNEL_RESISTANCE(GPADC0_CHAN, 342, "gpadc0"),
ADC_CHANNEL_RESISTANCE(GPADC1_CHAN, 342, "gpadc1"),
ADC_CHANNEL_RESISTANCE(GPADC2_CHAN, 342, "gpadc2"),
ADC_CHANNEL_RESISTANCE(GPADC3_CHAN, 342, "gpadc3"),
ADC_CHANNEL_VOLTAGE(GND_DET1_CHAN, 342, "gnddet1"),
ADC_CHANNEL_VOLTAGE(GND_DET2_CHAN, 342, "gnddet2"),
ADC_CHANNEL_VOLTAGE(MIC_DET_CHAN, 1367, "mic_det"),
ADC_CHANNEL_TEMPERATURE(TINT_CHAN, 104, "tint"),
};
static const struct regmap_config pm886_gpadc_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = PM886_GPADC_MAX_REGISTER,
};
static int gpadc_get_raw(struct iio_dev *iio, enum pm886_gpadc_channel chan)
{
struct pm886_gpadc *gpadc = iio_priv(iio);
__be16 buf;
int ret;
ret = regmap_bulk_read(gpadc->map, pm886_gpadc_regs[chan], &buf, sizeof(buf));
if (ret)
return ret;
return be16_to_cpu(buf) >> 4;
}
static int
gpadc_set_bias(struct pm886_gpadc *gpadc, enum pm886_gpadc_channel chan, bool on)
{
unsigned int gpadc_num = chan - GPADC0_CHAN;
unsigned int bits = BIT(gpadc_num + 4) | BIT(gpadc_num);
return regmap_assign_bits(gpadc->map, PM886_REG_GPADC_CONFIG(0x14), bits, on);
}
static int
gpadc_find_bias_current(struct iio_dev *iio, struct iio_chan_spec const *chan,
unsigned int *raw_uV, unsigned int *raw_uA)
{
struct pm886_gpadc *gpadc = iio_priv(iio);
unsigned int gpadc_num = chan->channel - GPADC0_CHAN;
unsigned int reg = PM886_REG_GPADC_CONFIG(0xb + gpadc_num);
unsigned long lsb = chan->address;
int ret;
for (unsigned int i = 0; i < PM886_GPADC_BIAS_LEVELS; i++) {
ret = regmap_update_bits(gpadc->map, reg, GENMASK(3, 0), i);
if (ret)
return ret;
/* Wait for the new bias level to apply. */
fsleep(5 * USEC_PER_MSEC);
*raw_uA = PM886_GPADC_INDEX_TO_BIAS_uA(i);
*raw_uV = gpadc_get_raw(iio, chan->channel) * lsb;
/*
* Vendor kernel errors out above 1.25 V, but testing shows
* that the resistance of the battery detection channel (GPADC2
* on coreprimevelte) reaches about 1.4 MΩ when the battery is
* removed, which can't be measured with such a low upper
* limit. Therefore, to be able to detect the battery without
* ugly externs as used in the vendor fuel gauge driver,
* increase this limit a bit.
*/
if (WARN_ON(*raw_uV > 1500 * (MICRO / MILLI)))
return -EIO;
/*
* Vendor kernel errors out under 300 mV, but for the same
* reason as above (except the channel hovers around 3.5 kΩ
* with battery present) reduce this limit.
*/
if (*raw_uV < 200 * (MICRO / MILLI)) {
dev_dbg(&iio->dev, "bad bias for chan %d: %d uA @ %d uV\n",
chan->channel, *raw_uA, *raw_uV);
continue;
}
dev_dbg(&iio->dev, "good bias for chan %d: %d uA @ %d uV\n",
chan->channel, *raw_uA, *raw_uV);
return 0;
}
dev_err(&iio->dev, "failed to find good bias for chan %d\n", chan->channel);
return -EINVAL;
}
static int
gpadc_get_resistance_ohm(struct iio_dev *iio, struct iio_chan_spec const *chan)
{
struct pm886_gpadc *gpadc = iio_priv(iio);
unsigned int raw_uV, raw_uA;
int ret;
ret = gpadc_set_bias(gpadc, chan->channel, true);
if (ret)
goto out;
ret = gpadc_find_bias_current(iio, chan, &raw_uV, &raw_uA);
if (ret)
goto out;
ret = DIV_ROUND_CLOSEST(raw_uV, raw_uA);
out:
gpadc_set_bias(gpadc, chan->channel, false);
return ret;
}
static int
__pm886_gpadc_read_raw(struct iio_dev *iio, struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
unsigned long lsb = chan->address;
switch (mask) {
case IIO_CHAN_INFO_RAW:
*val = gpadc_get_raw(iio, chan->channel);
if (*val < 0)
return *val;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = lsb;
if (chan->type == IIO_VOLTAGE) {
*val2 = MILLI;
return IIO_VAL_FRACTIONAL;
} else {
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_OFFSET:
/* Raw value is 104 millikelvin/LSB, convert it to 104 millicelsius/LSB */
*val = ABSOLUTE_ZERO_MILLICELSIUS;
*val2 = lsb;
return IIO_VAL_FRACTIONAL;
case IIO_CHAN_INFO_PROCESSED:
*val = gpadc_get_resistance_ohm(iio, chan);
if (*val < 0)
return *val;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int pm886_gpadc_read_raw(struct iio_dev *iio, struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct device *dev = iio->dev.parent;
int ret;
ret = pm_runtime_resume_and_get(dev);
if (ret)
return ret;
ret = __pm886_gpadc_read_raw(iio, chan, val, val2, mask);
pm_runtime_put_autosuspend(dev);
return ret;
}
static int pm886_gpadc_hw_enable(struct regmap *map)
{
const u8 config[] = {
PM886_GPADC_CONFIG1_EN_ALL,
PM886_GPADC_CONFIG2_EN_ALL,
PM886_GPADC_GND_DET2_EN,
};
int ret;
/* Enable the ADC block. */
ret = regmap_set_bits(map, PM886_REG_GPADC_CONFIG(0x6), BIT(0));
if (ret)
return ret;
/* Enable all channels. */
return regmap_bulk_write(map, PM886_REG_GPADC_CONFIG(0x1), config, ARRAY_SIZE(config));
}
static int pm886_gpadc_hw_disable(struct regmap *map)
{
return regmap_clear_bits(map, PM886_REG_GPADC_CONFIG(0x6), BIT(0));
}
static const struct iio_info pm886_gpadc_iio_info = {
.read_raw = pm886_gpadc_read_raw,
};
static int pm886_gpadc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct pm886_chip *chip = dev_get_drvdata(dev->parent);
struct i2c_client *client = chip->client;
struct pm886_gpadc *gpadc;
struct i2c_client *page;
struct iio_dev *iio;
int ret;
iio = devm_iio_device_alloc(dev, sizeof(*gpadc));
if (!iio)
return -ENOMEM;
gpadc = iio_priv(iio);
dev_set_drvdata(dev, iio);
page = devm_i2c_new_dummy_device(dev, client->adapter,
client->addr + PM886_PAGE_OFFSET_GPADC);
if (IS_ERR(page))
return dev_err_probe(dev, PTR_ERR(page), "Failed to initialize GPADC page\n");
gpadc->map = devm_regmap_init_i2c(page, &pm886_gpadc_regmap_config);
if (IS_ERR(gpadc->map))
return dev_err_probe(dev, PTR_ERR(gpadc->map),
"Failed to initialize GPADC regmap\n");
iio->name = "88pm886-gpadc";
iio->modes = INDIO_DIRECT_MODE;
iio->info = &pm886_gpadc_iio_info;
iio->channels = pm886_gpadc_channels;
iio->num_channels = ARRAY_SIZE(pm886_gpadc_channels);
device_set_node(&iio->dev, dev_fwnode(dev->parent));
ret = devm_pm_runtime_enable(dev);
if (ret)
return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");
pm_runtime_set_autosuspend_delay(dev, 50);
pm_runtime_use_autosuspend(dev);
ret = devm_iio_device_register(dev, iio);
if (ret)
return dev_err_probe(dev, ret, "Failed to register ADC\n");
return 0;
}
static int pm886_gpadc_runtime_resume(struct device *dev)
{
struct iio_dev *iio = dev_get_drvdata(dev);
struct pm886_gpadc *gpadc = iio_priv(iio);
return pm886_gpadc_hw_enable(gpadc->map);
}
static int pm886_gpadc_runtime_suspend(struct device *dev)
{
struct iio_dev *iio = dev_get_drvdata(dev);
struct pm886_gpadc *gpadc = iio_priv(iio);
return pm886_gpadc_hw_disable(gpadc->map);
}
static DEFINE_RUNTIME_DEV_PM_OPS(pm886_gpadc_pm_ops,
pm886_gpadc_runtime_suspend,
pm886_gpadc_runtime_resume, NULL);
static const struct platform_device_id pm886_gpadc_id[] = {
{ "88pm886-gpadc" },
{ }
};
MODULE_DEVICE_TABLE(platform, pm886_gpadc_id);
static struct platform_driver pm886_gpadc_driver = {
.driver = {
.name = "88pm886-gpadc",
.pm = pm_ptr(&pm886_gpadc_pm_ops),
},
.probe = pm886_gpadc_probe,
.id_table = pm886_gpadc_id,
};
module_platform_driver(pm886_gpadc_driver);
MODULE_AUTHOR("Duje Mihanović <duje@dujemihanovic.xyz>");
MODULE_DESCRIPTION("Marvell 88PM886 GPADC driver");
MODULE_LICENSE("GPL");

View File

@ -9,6 +9,19 @@ menu "Analog to digital converters"
config IIO_ADC_HELPER
tristate
config 88PM886_GPADC
tristate "Marvell 88PM886 GPADC driver"
depends on MFD_88PM886_PMIC
default MFD_88PM886_PMIC
help
Say Y here to enable support for the GPADC (General Purpose ADC)
found on the Marvell 88PM886 PMIC. The GPADC measures various
internal voltages and temperatures, including (but not limited to)
system, battery and USB Vbus.
To compile this driver as a module, choose M here: the module will be
called 88pm886-gpadc.
config AB8500_GPADC
bool "ST-Ericsson AB8500 GPADC driver"
depends on AB8500_CORE && REGULATOR_AB8500
@ -389,6 +402,7 @@ config AD7779
depends on SPI
select CRC8
select IIO_BUFFER
select IIO_BACKEND
help
Say yes here to build support for Analog Devices AD777X family
(AD7770, AD7771, AD7779) analog to digital converter (ADC).
@ -507,6 +521,25 @@ config AD9467
To compile this driver as a module, choose M here: the module will be
called ad9467.
config ADE9000
tristate "Analog Devices ADE9000 Multiphase Energy, and Power Quality Monitoring IC Driver"
depends on SPI
select REGMAP_SPI
select IIO_BUFFER
select IIO_KFIFO_BUF
help
Say yes here to build support for the Analog Devices ADE9000,
a highly accurate, multiphase energy and power quality monitoring
integrated circuit.
The device features high-precision analog-to-digital converters
and digital signal processing to compute RMS values, power factor,
frequency, and harmonic analysis. It supports SPI communication
and provides buffered data output through the IIO framework.
To compile this driver as a module, choose M here: the module will
be called ade9000.
config ADI_AXI_ADC
tristate "Analog Devices Generic AXI ADC IP core driver"
depends on MICROBLAZE || NIOS2 || ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_INTEL_SOCFPGA || COMPILE_TEST
@ -766,6 +799,17 @@ config INGENIC_ADC
This driver can also be built as a module. If so, the module will be
called ingenic_adc.
config INTEL_DC_TI_ADC
tristate "Intel Bay Trail / Cherry Trail Dollar Cove TI ADC driver"
depends on INTEL_SOC_PMIC_CHTDC_TI
help
Say yes here to have support for the Dollar Cove TI PMIC ADC device.
Depending on platform configuration, this general purpose ADC can be
used for sensors such as battery voltage and thermal resistors.
To compile this driver as a module, choose M here: the module will be
called intel_dc_ti_adc.
config INTEL_MRFLD_ADC
tristate "Intel Merrifield Basin Cove ADC driver"
depends on INTEL_SOC_PMIC_MRFLD
@ -1298,6 +1342,16 @@ config RN5T618_ADC
This driver can also be built as a module. If so, the module
will be called rn5t618-adc.
config ROHM_BD79112
tristate "Rohm BD79112 ADC driver"
depends on SPI && GPIOLIB
select REGMAP_SPI
select IIO_ADC_HELPER
help
Say yes here to build support for the ROHM BD79112 ADC. The
ROHM BD79112 is a 12-bit, 32-channel, SAR ADC. Analog inputs
can also be used for GPIO.
config ROHM_BD79124
tristate "Rohm BD79124 ADC driver"
depends on I2C && GPIOLIB

View File

@ -6,6 +6,7 @@
obj-$(CONFIG_IIO_ADC_HELPER) += industrialio-adc.o
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_88PM886_GPADC) += 88pm886-gpadc.o
obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
obj-$(CONFIG_AD4000) += ad4000.o
@ -46,6 +47,7 @@ obj-$(CONFIG_AD7944) += ad7944.o
obj-$(CONFIG_AD7949) += ad7949.o
obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_AD9467) += ad9467.o
obj-$(CONFIG_ADE9000) += ade9000.o
obj-$(CONFIG_ADI_AXI_ADC) += adi-axi-adc.o
obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
@ -70,6 +72,7 @@ obj-$(CONFIG_IMX8QXP_ADC) += imx8qxp-adc.o
obj-$(CONFIG_IMX93_ADC) += imx93_adc.o
obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o
obj-$(CONFIG_INTEL_DC_TI_ADC) += intel_dc_ti_adc.o
obj-$(CONFIG_INTEL_MRFLD_ADC) += intel_mrfld_adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
@ -116,6 +119,7 @@ obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
obj-$(CONFIG_RICHTEK_RTQ6056) += rtq6056.o
obj-$(CONFIG_RN5T618_ADC) += rn5t618-adc.o
obj-$(CONFIG_ROHM_BD79112) += rohm-bd79112.o
obj-$(CONFIG_ROHM_BD79124) += rohm-bd79124.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o

View File

@ -607,7 +607,6 @@ static int ab8500_gpadc_read(struct ab8500_gpadc *gpadc,
}
/* This eventually drops the regulator */
pm_runtime_mark_last_busy(gpadc->dev);
pm_runtime_put_autosuspend(gpadc->dev);
return (high_data << 8) | low_data;

View File

@ -2035,8 +2035,7 @@ static int ad4130_probe(struct spi_device *spi)
ret = devm_add_action_or_reset(dev, ad4130_disable_regulators, st);
if (ret)
return dev_err_probe(dev, ret,
"Failed to add regulators disable action\n");
return ret;
ret = ad4130_soft_reset(st);
if (ret)

View File

@ -3,21 +3,27 @@
* AD7124 SPI ADC driver
*
* Copyright 2018 Analog Devices Inc.
* Copyright 2025 BayLibre, SAS
*/
#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>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/kfifo.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/sprintf.h>
#include <linux/units.h>
#include <linux/iio/iio.h>
#include <linux/iio/adc/ad_sigma_delta.h>
@ -44,6 +50,11 @@
#define AD7124_STATUS_POR_FLAG BIT(4)
/* AD7124_ADC_CONTROL */
#define AD7124_ADC_CONTROL_CLK_SEL GENMASK(1, 0)
#define AD7124_ADC_CONTROL_CLK_SEL_INT 0
#define AD7124_ADC_CONTROL_CLK_SEL_INT_OUT 1
#define AD7124_ADC_CONTROL_CLK_SEL_EXT 2
#define AD7124_ADC_CONTROL_CLK_SEL_EXT_DIV4 3
#define AD7124_ADC_CONTROL_MODE GENMASK(5, 2)
#define AD7124_ADC_CONTROL_MODE_CONTINUOUS 0
#define AD7124_ADC_CONTROL_MODE_SINGLE 1
@ -84,14 +95,26 @@
#define AD7124_CONFIG_PGA GENMASK(2, 0)
/* AD7124_FILTER_X */
#define AD7124_FILTER_FS GENMASK(10, 0)
#define AD7124_FILTER_FILTER GENMASK(23, 21)
#define AD7124_FILTER_FILTER_SINC4 0
#define AD7124_FILTER_FILTER_SINC3 2
#define AD7124_FILTER_FILTER_SINC4_SINC1 4
#define AD7124_FILTER_FILTER_SINC3_SINC1 5
#define AD7124_FILTER_FILTER_SINC3_PF 7
#define AD7124_FILTER_REJ60 BIT(20)
#define AD7124_FILTER_POST_FILTER GENMASK(19, 17)
#define AD7124_FILTER_POST_FILTER_47dB 2
#define AD7124_FILTER_POST_FILTER_62dB 3
#define AD7124_FILTER_POST_FILTER_86dB 5
#define AD7124_FILTER_POST_FILTER_92dB 6
#define AD7124_FILTER_SINGLE_CYCLE BIT(16)
#define AD7124_FILTER_FS GENMASK(10, 0)
#define AD7124_MAX_CONFIGS 8
#define AD7124_MAX_CHANNELS 16
#define AD7124_INT_CLK_HZ 614400
/* AD7124 input sources */
enum ad7124_ref_sel {
@ -120,9 +143,9 @@ static const unsigned int ad7124_reg_size[] = {
};
static const int ad7124_master_clk_freq_hz[3] = {
[AD7124_LOW_POWER] = 76800,
[AD7124_MID_POWER] = 153600,
[AD7124_FULL_POWER] = 614400,
[AD7124_LOW_POWER] = AD7124_INT_CLK_HZ / 8,
[AD7124_MID_POWER] = AD7124_INT_CLK_HZ / 4,
[AD7124_FULL_POWER] = AD7124_INT_CLK_HZ,
};
static const char * const ad7124_ref_names[] = {
@ -138,9 +161,24 @@ struct ad7124_chip_info {
unsigned int num_inputs;
};
enum ad7124_filter_type {
AD7124_FILTER_TYPE_SINC3,
AD7124_FILTER_TYPE_SINC3_PF1,
AD7124_FILTER_TYPE_SINC3_PF2,
AD7124_FILTER_TYPE_SINC3_PF3,
AD7124_FILTER_TYPE_SINC3_PF4,
AD7124_FILTER_TYPE_SINC3_REJ60,
AD7124_FILTER_TYPE_SINC3_SINC1,
AD7124_FILTER_TYPE_SINC4,
AD7124_FILTER_TYPE_SINC4_REJ60,
AD7124_FILTER_TYPE_SINC4_SINC1,
};
struct ad7124_channel_config {
bool live;
unsigned int cfg_slot;
unsigned int requested_odr;
unsigned int requested_odr_micro;
/*
* Following fields are used to compare for equality. If you
* make adaptations in it, you most likely also have to adapt
@ -153,9 +191,8 @@ struct ad7124_channel_config {
bool buf_negative;
unsigned int vref_mv;
unsigned int pga_bits;
unsigned int odr;
unsigned int odr_sel_bits;
unsigned int filter_type;
enum ad7124_filter_type filter_type;
unsigned int calibration_offset;
unsigned int calibration_gain;
);
@ -174,7 +211,7 @@ struct ad7124_state {
struct ad_sigma_delta sd;
struct ad7124_channel *channels;
struct regulator *vref[4];
struct clk *mclk;
u32 clk_hz;
unsigned int adc_control;
unsigned int num_channels;
struct mutex cfgs_lock; /* lock for configs access */
@ -250,44 +287,117 @@ static int ad7124_set_mode(struct ad_sigma_delta *sd,
return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
}
static void ad7124_set_channel_odr(struct ad7124_state *st, unsigned int channel, unsigned int odr)
static u32 ad7124_get_fclk_hz(struct ad7124_state *st)
{
unsigned int fclk, odr_sel_bits;
enum ad7124_power_mode power_mode;
u32 fclk_hz;
power_mode = FIELD_GET(AD7124_ADC_CONTROL_POWER_MODE, st->adc_control);
fclk_hz = st->clk_hz;
switch (power_mode) {
case AD7124_LOW_POWER:
fclk_hz /= 8;
break;
case AD7124_MID_POWER:
fclk_hz /= 4;
break;
default:
break;
}
return fclk_hz;
}
static u32 ad7124_get_fs_factor(struct ad7124_state *st, unsigned int channel)
{
enum ad7124_power_mode power_mode =
FIELD_GET(AD7124_ADC_CONTROL_POWER_MODE, st->adc_control);
u32 avg = power_mode == AD7124_LOW_POWER ? 8 : 16;
fclk = clk_get_rate(st->mclk);
/*
* FS[10:0] = fCLK / (fADC x 32) where:
* These are the "zero-latency" factors from the data sheet. For the
* sinc1 filters, these aren't documented, but derived by taking the
* single-channel formula from the sinc1 section of the data sheet and
* multiplying that by the sinc3/4 factor from the corresponding zero-
* latency sections.
*/
switch (st->channels[channel].cfg.filter_type) {
case AD7124_FILTER_TYPE_SINC4:
case AD7124_FILTER_TYPE_SINC4_REJ60:
return 4 * 32;
case AD7124_FILTER_TYPE_SINC4_SINC1:
return 4 * avg * 32;
case AD7124_FILTER_TYPE_SINC3_SINC1:
return 3 * avg * 32;
default:
return 3 * 32;
}
}
static u32 ad7124_get_fadc_divisor(struct ad7124_state *st, unsigned int channel)
{
u32 factor = ad7124_get_fs_factor(st, channel);
/*
* The output data rate (f_ADC) is f_CLK / divisor. We are returning
* the divisor.
*/
return st->channels[channel].cfg.odr_sel_bits * factor;
}
static void ad7124_set_channel_odr(struct ad7124_state *st, unsigned int channel)
{
struct ad7124_channel_config *cfg = &st->channels[channel].cfg;
unsigned int fclk, factor, divisor, odr_sel_bits;
fclk = ad7124_get_fclk_hz(st);
factor = ad7124_get_fs_factor(st, channel);
/*
* FS[10:0] = fCLK / (fADC x 32 * N) where:
* fADC is the output data rate
* fCLK is the master clock frequency
* N is number of conversions per sample (depends on filter type)
* FS[10:0] are the bits in the filter register
* FS[10:0] can have a value from 1 to 2047
*/
odr_sel_bits = DIV_ROUND_CLOSEST(fclk, odr * 32);
if (odr_sel_bits < 1)
odr_sel_bits = 1;
else if (odr_sel_bits > 2047)
odr_sel_bits = 2047;
divisor = cfg->requested_odr * factor +
cfg->requested_odr_micro * factor / MICRO;
odr_sel_bits = clamp(DIV_ROUND_CLOSEST(fclk, divisor), 1, 2047);
if (odr_sel_bits != st->channels[channel].cfg.odr_sel_bits)
st->channels[channel].cfg.live = false;
/* fADC = fCLK / (FS[10:0] x 32) */
st->channels[channel].cfg.odr = DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 32);
st->channels[channel].cfg.odr_sel_bits = odr_sel_bits;
}
static int ad7124_get_3db_filter_freq(struct ad7124_state *st,
static int ad7124_get_3db_filter_factor(struct ad7124_state *st,
unsigned int channel)
{
unsigned int fadc;
struct ad7124_channel_config *cfg = &st->channels[channel].cfg;
fadc = st->channels[channel].cfg.odr;
switch (st->channels[channel].cfg.filter_type) {
case AD7124_FILTER_FILTER_SINC3:
return DIV_ROUND_CLOSEST(fadc * 272, 1000);
case AD7124_FILTER_FILTER_SINC4:
return DIV_ROUND_CLOSEST(fadc * 230, 1000);
/*
* 3dB point is the f_CLK rate times some factor. This functions returns
* the factor times 1000.
*/
switch (cfg->filter_type) {
case AD7124_FILTER_TYPE_SINC3:
case AD7124_FILTER_TYPE_SINC3_REJ60:
case AD7124_FILTER_TYPE_SINC3_SINC1:
return 272;
case AD7124_FILTER_TYPE_SINC4:
case AD7124_FILTER_TYPE_SINC4_REJ60:
case AD7124_FILTER_TYPE_SINC4_SINC1:
return 230;
case AD7124_FILTER_TYPE_SINC3_PF1:
return 633;
case AD7124_FILTER_TYPE_SINC3_PF2:
return 605;
case AD7124_FILTER_TYPE_SINC3_PF3:
return 669;
case AD7124_FILTER_TYPE_SINC3_PF4:
return 759;
default:
return -EINVAL;
}
@ -311,9 +421,8 @@ static struct ad7124_channel_config *ad7124_find_similar_live_cfg(struct ad7124_
bool buf_negative;
unsigned int vref_mv;
unsigned int pga_bits;
unsigned int odr;
unsigned int odr_sel_bits;
unsigned int filter_type;
enum ad7124_filter_type filter_type;
unsigned int calibration_offset;
unsigned int calibration_gain;
}));
@ -328,7 +437,6 @@ static struct ad7124_channel_config *ad7124_find_similar_live_cfg(struct ad7124_
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 &&
@ -381,8 +489,9 @@ static int ad7124_init_config_vref(struct ad7124_state *st, struct ad7124_channe
static int ad7124_write_config(struct ad7124_state *st, struct ad7124_channel_config *cfg,
unsigned int cfg_slot)
{
unsigned int tmp;
unsigned int val;
unsigned int val, filter;
unsigned int rej60 = 0;
unsigned int post = 0;
int ret;
cfg->cfg_slot = cfg_slot;
@ -405,11 +514,60 @@ static int ad7124_write_config(struct ad7124_state *st, struct ad7124_channel_co
if (ret < 0)
return ret;
tmp = FIELD_PREP(AD7124_FILTER_FILTER, cfg->filter_type) |
FIELD_PREP(AD7124_FILTER_FS, cfg->odr_sel_bits);
return ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot),
AD7124_FILTER_FILTER | AD7124_FILTER_FS,
tmp, 3);
switch (cfg->filter_type) {
case AD7124_FILTER_TYPE_SINC3:
filter = AD7124_FILTER_FILTER_SINC3;
break;
case AD7124_FILTER_TYPE_SINC3_PF1:
filter = AD7124_FILTER_FILTER_SINC3_PF;
post = AD7124_FILTER_POST_FILTER_47dB;
break;
case AD7124_FILTER_TYPE_SINC3_PF2:
filter = AD7124_FILTER_FILTER_SINC3_PF;
post = AD7124_FILTER_POST_FILTER_62dB;
break;
case AD7124_FILTER_TYPE_SINC3_PF3:
filter = AD7124_FILTER_FILTER_SINC3_PF;
post = AD7124_FILTER_POST_FILTER_86dB;
break;
case AD7124_FILTER_TYPE_SINC3_PF4:
filter = AD7124_FILTER_FILTER_SINC3_PF;
post = AD7124_FILTER_POST_FILTER_92dB;
break;
case AD7124_FILTER_TYPE_SINC3_REJ60:
filter = AD7124_FILTER_FILTER_SINC3;
rej60 = 1;
break;
case AD7124_FILTER_TYPE_SINC3_SINC1:
filter = AD7124_FILTER_FILTER_SINC3_SINC1;
break;
case AD7124_FILTER_TYPE_SINC4:
filter = AD7124_FILTER_FILTER_SINC4;
break;
case AD7124_FILTER_TYPE_SINC4_REJ60:
filter = AD7124_FILTER_FILTER_SINC4;
rej60 = 1;
break;
case AD7124_FILTER_TYPE_SINC4_SINC1:
filter = AD7124_FILTER_FILTER_SINC4_SINC1;
break;
default:
return -EINVAL;
}
/*
* NB: AD7124_FILTER_SINGLE_CYCLE is always set so that we get the same
* sampling frequency even when only one channel is enabled in a
* buffered read. If it was not set, the N in ad7124_set_channel_odr()
* would be 1 and we would get a faster sampling frequency than what
* was requested.
*/
return ad_sd_write_reg(&st->sd, AD7124_FILTER(cfg->cfg_slot), 3,
FIELD_PREP(AD7124_FILTER_FILTER, filter) |
FIELD_PREP(AD7124_FILTER_REJ60, rej60) |
FIELD_PREP(AD7124_FILTER_POST_FILTER, post) |
AD7124_FILTER_SINGLE_CYCLE |
FIELD_PREP(AD7124_FILTER_FS, cfg->odr_sel_bits));
}
static struct ad7124_channel_config *ad7124_pop_config(struct ad7124_state *st)
@ -576,6 +734,33 @@ static const struct ad_sigma_delta_info ad7124_sigma_delta_info = {
.num_resetclks = 64,
};
static const int ad7124_voltage_scales[][2] = {
{ 0, 1164 },
{ 0, 2328 },
{ 0, 4656 },
{ 0, 9313 },
{ 0, 18626 },
{ 0, 37252 },
{ 0, 74505 },
{ 0, 149011 },
{ 0, 298023 },
};
static int ad7124_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length, long info)
{
switch (info) {
case IIO_CHAN_INFO_SCALE:
*vals = (const int *)ad7124_voltage_scales;
*type = IIO_VAL_INT_PLUS_NANO;
*length = ARRAY_SIZE(ad7124_voltage_scales) * 2;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static int ad7124_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long info)
@ -644,18 +829,59 @@ static int ad7124_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
case IIO_CHAN_INFO_SAMP_FREQ:
mutex_lock(&st->cfgs_lock);
*val = st->channels[chan->address].cfg.odr;
mutex_unlock(&st->cfgs_lock);
case IIO_CHAN_INFO_SAMP_FREQ: {
struct ad7124_channel_config *cfg = &st->channels[chan->address].cfg;
return IIO_VAL_INT;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
mutex_lock(&st->cfgs_lock);
*val = ad7124_get_3db_filter_freq(st, chan->scan_index);
mutex_unlock(&st->cfgs_lock);
guard(mutex)(&st->cfgs_lock);
return IIO_VAL_INT;
switch (cfg->filter_type) {
case AD7124_FILTER_TYPE_SINC3:
case AD7124_FILTER_TYPE_SINC3_REJ60:
case AD7124_FILTER_TYPE_SINC3_SINC1:
case AD7124_FILTER_TYPE_SINC4:
case AD7124_FILTER_TYPE_SINC4_REJ60:
case AD7124_FILTER_TYPE_SINC4_SINC1:
*val = ad7124_get_fclk_hz(st);
*val2 = ad7124_get_fadc_divisor(st, chan->address);
return IIO_VAL_FRACTIONAL;
/*
* Post filters force the chip to a fixed rate. These are the
* single-channel rates from the data sheet divided by 3 for
* the multi-channel case (data sheet doesn't explicitly state
* this but confirmed through testing).
*/
case AD7124_FILTER_TYPE_SINC3_PF1:
*val = 300;
*val2 = 33;
return IIO_VAL_FRACTIONAL;
case AD7124_FILTER_TYPE_SINC3_PF2:
*val = 25;
*val2 = 3;
return IIO_VAL_FRACTIONAL;
case AD7124_FILTER_TYPE_SINC3_PF3:
*val = 20;
*val2 = 3;
return IIO_VAL_FRACTIONAL;
case AD7124_FILTER_TYPE_SINC3_PF4:
*val = 50;
*val2 = 9;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
}
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: {
guard(mutex)(&st->cfgs_lock);
ret = ad7124_get_3db_filter_factor(st, chan->address);
if (ret < 0)
return ret;
/* 3dB point is the f_CLK rate times a fractional value */
*val = ret * ad7124_get_fclk_hz(st);
*val2 = MILLI * ad7124_get_fadc_divisor(st, chan->address);
return IIO_VAL_FRACTIONAL;
}
default:
return -EINVAL;
}
@ -666,25 +892,24 @@ static int ad7124_write_raw(struct iio_dev *indio_dev,
int val, int val2, long info)
{
struct ad7124_state *st = iio_priv(indio_dev);
struct ad7124_channel_config *cfg = &st->channels[chan->address].cfg;
unsigned int res, gain, full_scale, vref;
int ret = 0;
mutex_lock(&st->cfgs_lock);
guard(mutex)(&st->cfgs_lock);
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ:
if (val2 != 0 || val == 0) {
ret = -EINVAL;
break;
}
if (val2 < 0 || val < 0 || (val2 == 0 && val == 0))
return -EINVAL;
ad7124_set_channel_odr(st, chan->address, val);
break;
cfg->requested_odr = val;
cfg->requested_odr_micro = val2;
ad7124_set_channel_odr(st, chan->address);
return 0;
case IIO_CHAN_INFO_SCALE:
if (val != 0) {
ret = -EINVAL;
break;
}
if (val != 0)
return -EINVAL;
if (st->channels[chan->address].cfg.bipolar)
full_scale = 1 << (chan->scan_type.realbits - 1);
@ -700,13 +925,10 @@ static int ad7124_write_raw(struct iio_dev *indio_dev,
st->channels[chan->address].cfg.live = false;
st->channels[chan->address].cfg.pga_bits = res;
break;
return 0;
default:
ret = -EINVAL;
return -EINVAL;
}
mutex_unlock(&st->cfgs_lock);
return ret;
}
static int ad7124_reg_access(struct iio_dev *indio_dev,
@ -730,18 +952,6 @@ static int ad7124_reg_access(struct iio_dev *indio_dev,
return ret;
}
static IIO_CONST_ATTR(in_voltage_scale_available,
"0.000001164 0.000002328 0.000004656 0.000009313 0.000018626 0.000037252 0.000074505 0.000149011 0.000298023");
static struct attribute *ad7124_attributes[] = {
&iio_const_attr_in_voltage_scale_available.dev_attr.attr,
NULL,
};
static const struct attribute_group ad7124_attrs_group = {
.attrs = ad7124_attributes,
};
static int ad7124_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
@ -750,7 +960,8 @@ static int ad7124_update_scan_mode(struct iio_dev *indio_dev,
int ret;
int i;
mutex_lock(&st->cfgs_lock);
guard(mutex)(&st->cfgs_lock);
for (i = 0; i < st->num_channels; i++) {
bit_set = test_bit(i, scan_mask);
if (bit_set)
@ -758,25 +969,20 @@ static int ad7124_update_scan_mode(struct iio_dev *indio_dev,
else
ret = ad7124_spi_write_mask(st, AD7124_CHANNEL(i), AD7124_CHANNEL_ENABLE,
0, 2);
if (ret < 0) {
mutex_unlock(&st->cfgs_lock);
if (ret < 0)
return ret;
}
}
mutex_unlock(&st->cfgs_lock);
return 0;
}
static const struct iio_info ad7124_info = {
.read_avail = ad7124_read_avail,
.read_raw = ad7124_read_raw,
.write_raw = ad7124_write_raw,
.debugfs_reg_access = &ad7124_reg_access,
.validate_trigger = ad_sd_validate_trigger,
.update_scan_mode = ad7124_update_scan_mode,
.attrs = &ad7124_attrs_group,
};
/* Only called during probe, so dev_err_probe() can be used */
@ -944,6 +1150,52 @@ static const struct iio_enum ad7124_syscalib_mode_enum = {
.get = ad7124_get_syscalib_mode
};
static const char * const ad7124_filter_types[] = {
[AD7124_FILTER_TYPE_SINC3] = "sinc3",
[AD7124_FILTER_TYPE_SINC3_PF1] = "sinc3+pf1",
[AD7124_FILTER_TYPE_SINC3_PF2] = "sinc3+pf2",
[AD7124_FILTER_TYPE_SINC3_PF3] = "sinc3+pf3",
[AD7124_FILTER_TYPE_SINC3_PF4] = "sinc3+pf4",
[AD7124_FILTER_TYPE_SINC3_REJ60] = "sinc3+rej60",
[AD7124_FILTER_TYPE_SINC3_SINC1] = "sinc3+sinc1",
[AD7124_FILTER_TYPE_SINC4] = "sinc4",
[AD7124_FILTER_TYPE_SINC4_REJ60] = "sinc4+rej60",
[AD7124_FILTER_TYPE_SINC4_SINC1] = "sinc4+sinc1",
};
static int ad7124_set_filter_type_attr(struct iio_dev *dev,
const struct iio_chan_spec *chan,
unsigned int value)
{
struct ad7124_state *st = iio_priv(dev);
struct ad7124_channel_config *cfg = &st->channels[chan->address].cfg;
guard(mutex)(&st->cfgs_lock);
cfg->live = false;
cfg->filter_type = value;
ad7124_set_channel_odr(st, chan->address);
return 0;
}
static int ad7124_get_filter_type_attr(struct iio_dev *dev,
const struct iio_chan_spec *chan)
{
struct ad7124_state *st = iio_priv(dev);
guard(mutex)(&st->cfgs_lock);
return st->channels[chan->address].cfg.filter_type;
}
static const struct iio_enum ad7124_filter_type_enum = {
.items = ad7124_filter_types,
.num_items = ARRAY_SIZE(ad7124_filter_types),
.set = ad7124_set_filter_type_attr,
.get = ad7124_get_filter_type_attr,
};
static const struct iio_chan_spec_ext_info ad7124_calibsys_ext_info[] = {
{
.name = "sys_calibration",
@ -954,6 +1206,9 @@ static const struct iio_chan_spec_ext_info ad7124_calibsys_ext_info[] = {
&ad7124_syscalib_mode_enum),
IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE,
&ad7124_syscalib_mode_enum),
IIO_ENUM("filter_type", IIO_SEPARATE, &ad7124_filter_type_enum),
IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE,
&ad7124_filter_type_enum),
{ }
};
@ -966,6 +1221,7 @@ static const struct iio_chan_spec ad7124_channel_template = {
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),
.scan_type = {
.sign = 'u',
.realbits = 24,
@ -1111,24 +1367,122 @@ static int ad7124_parse_channel_config(struct iio_dev *indio_dev,
static int ad7124_setup(struct ad7124_state *st)
{
struct device *dev = &st->sd.spi->dev;
unsigned int fclk, power_mode;
unsigned int power_mode, clk_sel;
struct clk *mclk;
int i, ret;
fclk = clk_get_rate(st->mclk);
if (!fclk)
return dev_err_probe(dev, -EINVAL, "Failed to get mclk rate\n");
/*
* Always use full power mode for max performance. If needed, the driver
* could be adapted to use a dynamic power mode based on the requested
* output data rate.
*/
power_mode = AD7124_ADC_CONTROL_POWER_MODE_FULL;
/* The power mode changes the master clock frequency */
/*
* This "mclk" business is needed for backwards compatibility with old
* devicetrees that specified a fake clock named "mclk" to select the
* power mode.
*/
mclk = devm_clk_get_optional_enabled(dev, "mclk");
if (IS_ERR(mclk))
return dev_err_probe(dev, PTR_ERR(mclk), "Failed to get mclk\n");
if (mclk) {
unsigned long mclk_hz;
mclk_hz = clk_get_rate(mclk);
if (!mclk_hz)
return dev_err_probe(dev, -EINVAL,
"Failed to get mclk rate\n");
/*
* This logic is a bit backwards, which is why it is only here
* for backwards compatibility. The driver should be able to set
* the power mode as it sees fit and the f_clk/mclk rate should
* be dynamic accordingly. But here, we are selecting a fixed
* power mode based on the given "mclk" rate.
*/
power_mode = ad7124_find_closest_match(ad7124_master_clk_freq_hz,
ARRAY_SIZE(ad7124_master_clk_freq_hz),
fclk);
if (fclk != ad7124_master_clk_freq_hz[power_mode]) {
ret = clk_set_rate(st->mclk, fclk);
ARRAY_SIZE(ad7124_master_clk_freq_hz), mclk_hz);
if (mclk_hz != ad7124_master_clk_freq_hz[power_mode]) {
ret = clk_set_rate(mclk, mclk_hz);
if (ret)
return dev_err_probe(dev, ret, "Failed to set mclk rate\n");
return dev_err_probe(dev, ret,
"Failed to set mclk rate\n");
}
/* Set the power mode */
clk_sel = AD7124_ADC_CONTROL_CLK_SEL_INT;
st->clk_hz = AD7124_INT_CLK_HZ;
} else if (!device_property_present(dev, "clocks") &&
device_property_present(dev, "#clock-cells")) {
#ifdef CONFIG_COMMON_CLK
struct clk_hw *clk_hw;
const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%pfwP-clk",
dev_fwnode(dev));
if (!name)
return -ENOMEM;
clk_hw = devm_clk_hw_register_fixed_rate(dev, name, NULL, 0,
AD7124_INT_CLK_HZ);
if (IS_ERR(clk_hw))
return dev_err_probe(dev, PTR_ERR(clk_hw),
"Failed to register clock provider\n");
ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
clk_hw);
if (ret)
return dev_err_probe(dev, ret,
"Failed to add clock provider\n");
#endif
/*
* Treat the clock as always on. This way we don't have to deal
* with someone trying to enable/disable the clock while we are
* reading samples.
*/
clk_sel = AD7124_ADC_CONTROL_CLK_SEL_INT_OUT;
st->clk_hz = AD7124_INT_CLK_HZ;
} else {
struct clk *clk;
clk = devm_clk_get_optional_enabled(dev, NULL);
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk),
"Failed to get external clock\n");
if (clk) {
unsigned long clk_hz;
clk_hz = clk_get_rate(clk);
if (!clk_hz)
return dev_err_probe(dev, -EINVAL,
"Failed to get external clock rate\n");
/*
* The external clock may be 4x the nominal clock rate,
* in which case the ADC needs to be configured to
* divide it by 4. Using MEGA is a bit arbitrary, but
* the expected clock rates are either 614.4 kHz or
* 2.4576 MHz, so this should work.
*/
if (clk_hz > (1 * HZ_PER_MHZ)) {
clk_sel = AD7124_ADC_CONTROL_CLK_SEL_EXT_DIV4;
st->clk_hz = clk_hz / 4;
} else {
clk_sel = AD7124_ADC_CONTROL_CLK_SEL_EXT;
st->clk_hz = clk_hz;
}
} else {
clk_sel = AD7124_ADC_CONTROL_CLK_SEL_INT;
st->clk_hz = AD7124_INT_CLK_HZ;
}
}
st->adc_control &= ~AD7124_ADC_CONTROL_CLK_SEL;
st->adc_control |= FIELD_PREP(AD7124_ADC_CONTROL_CLK_SEL, clk_sel);
st->adc_control &= ~AD7124_ADC_CONTROL_POWER_MODE;
st->adc_control |= FIELD_PREP(AD7124_ADC_CONTROL_POWER_MODE, power_mode);
@ -1138,17 +1492,22 @@ static int ad7124_setup(struct ad7124_state *st)
mutex_init(&st->cfgs_lock);
INIT_KFIFO(st->live_cfgs_fifo);
for (i = 0; i < st->num_channels; i++) {
struct ad7124_channel_config *cfg = &st->channels[i].cfg;
ret = ad7124_init_config_vref(st, &st->channels[i].cfg);
ret = ad7124_init_config_vref(st, cfg);
if (ret < 0)
return ret;
/* Default filter type on the ADC after reset. */
cfg->filter_type = AD7124_FILTER_TYPE_SINC4;
/*
* 9.38 SPS is the minimum output data rate supported
* regardless of the selected power mode. Round it up to 10 and
* set all channels to this default value.
*/
ad7124_set_channel_odr(st, i, 10);
cfg->requested_odr = 10;
ad7124_set_channel_odr(st, i);
}
ad7124_disable_all(&st->sd);
@ -1300,13 +1659,9 @@ static int ad7124_probe(struct spi_device *spi)
ret = devm_add_action_or_reset(&spi->dev, ad7124_reg_disable,
st->vref[i]);
if (ret)
return dev_err_probe(dev, ret, "Failed to register disable handler for regulator #%d\n", i);
return ret;
}
st->mclk = devm_clk_get_enabled(&spi->dev, "mclk");
if (IS_ERR(st->mclk))
return dev_err_probe(dev, PTR_ERR(st->mclk), "Failed to get mclk\n");
ret = ad7124_soft_reset(st);
if (ret < 0)
return ret;

View File

@ -8,6 +8,7 @@
* AD7175-8/AD7176-2/AD7177-2
*
* Copyright (C) 2015, 2024 Analog Devices, Inc.
* Copyright (C) 2025 BayLibre, SAS
*/
#include <linux/array_size.h>
@ -149,7 +150,12 @@
(pin2) < st->info->num_voltage_in && \
(pin2) >= st->info->num_voltage_in_div)
#define AD7173_FILTER_ODR0_MASK GENMASK(5, 0)
#define AD7173_FILTER_SINC3_MAP BIT(15)
#define AD7173_FILTER_SINC3_MAP_DIV GENMASK(14, 0)
#define AD7173_FILTER_ENHFILTEN BIT(11)
#define AD7173_FILTER_ENHFILT_MASK GENMASK(10, 8)
#define AD7173_FILTER_ORDER BIT(6)
#define AD7173_FILTER_ODR_MASK GENMASK(5, 0)
#define AD7173_MAX_CONFIGS 8
#define AD4111_OW_DET_THRSH_MV 300
@ -190,6 +196,15 @@ struct ad7173_device_info {
u8 num_gpios;
};
enum ad7173_filter_type {
AD7173_FILTER_SINC3,
AD7173_FILTER_SINC5_SINC1,
AD7173_FILTER_SINC5_SINC1_PF1,
AD7173_FILTER_SINC5_SINC1_PF2,
AD7173_FILTER_SINC5_SINC1_PF3,
AD7173_FILTER_SINC5_SINC1_PF4,
};
struct ad7173_channel_config {
/* Openwire detection threshold */
unsigned int openwire_thrsh_raw;
@ -205,8 +220,10 @@ struct ad7173_channel_config {
struct_group(config_props,
bool bipolar;
bool input_buf;
u8 odr;
u16 sinc3_odr_div;
u8 sinc5_odr_index;
u8 ref_sel;
enum ad7173_filter_type filter_type;
);
};
@ -266,6 +283,24 @@ static const unsigned int ad7175_sinc5_data_rates[] = {
5000, /* 20 */
};
/**
* ad7173_sinc3_odr_div_from_odr() - Convert ODR to divider value
* @odr_millihz: ODR (sampling_frequency) in milliHz
* Returns: Divider value for SINC3 filter to pass.
*/
static u16 ad7173_sinc3_odr_div_from_odr(u32 odr_millihz)
{
/*
* Divider is f_MOD (1 MHz) / 32 / ODR. ODR freq is in milliHz, so
* we need to convert f_MOD to the same units. When SING_CYC=1 or
* multiple channels are enabled (currently always the case), there
* is an additional factor of 3.
*/
u32 div = DIV_ROUND_CLOSEST(MEGA * MILLI, odr_millihz * 32 * 3);
/* Avoid divide by 0 and limit to register field size. */
return clamp(div, 1U, AD7173_FILTER_SINC3_MAP_DIV);
}
static unsigned int ad4111_current_channel_config[] = {
/* Ain sel: pos neg */
0x1E8, /* 15:IIN0+ 8:IIN0 */
@ -369,7 +404,48 @@ static const struct iio_enum ad7173_syscalib_mode_enum = {
.get = ad7173_get_syscalib_mode
};
static const struct iio_chan_spec_ext_info ad7173_calibsys_ext_info[] = {
static const char * const ad7173_filter_types_str[] = {
[AD7173_FILTER_SINC3] = "sinc3",
[AD7173_FILTER_SINC5_SINC1] = "sinc5+sinc1",
[AD7173_FILTER_SINC5_SINC1_PF1] = "sinc5+sinc1+pf1",
[AD7173_FILTER_SINC5_SINC1_PF2] = "sinc5+sinc1+pf2",
[AD7173_FILTER_SINC5_SINC1_PF3] = "sinc5+sinc1+pf3",
[AD7173_FILTER_SINC5_SINC1_PF4] = "sinc5+sinc1+pf4",
};
static int ad7173_set_filter_type(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int val)
{
struct ad7173_state *st = iio_priv(indio_dev);
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
st->channels[chan->address].cfg.filter_type = val;
st->channels[chan->address].cfg.live = false;
iio_device_release_direct(indio_dev);
return 0;
}
static int ad7173_get_filter_type(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct ad7173_state *st = iio_priv(indio_dev);
return st->channels[chan->address].cfg.filter_type;
}
static const struct iio_enum ad7173_filter_type_enum = {
.items = ad7173_filter_types_str,
.num_items = ARRAY_SIZE(ad7173_filter_types_str),
.set = ad7173_set_filter_type,
.get = ad7173_get_filter_type,
};
static const struct iio_chan_spec_ext_info ad7173_chan_spec_ext_info[] = {
{
.name = "sys_calibration",
.write = ad7173_write_syscalib,
@ -379,6 +455,16 @@ static const struct iio_chan_spec_ext_info ad7173_calibsys_ext_info[] = {
&ad7173_syscalib_mode_enum),
IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE,
&ad7173_syscalib_mode_enum),
IIO_ENUM("filter_type", IIO_SEPARATE, &ad7173_filter_type_enum),
IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE,
&ad7173_filter_type_enum),
{ }
};
static const struct iio_chan_spec_ext_info ad7173_temp_chan_spec_ext_info[] = {
IIO_ENUM("filter_type", IIO_SEPARATE, &ad7173_filter_type_enum),
IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_TYPE,
&ad7173_filter_type_enum),
{ }
};
@ -582,14 +668,18 @@ static bool ad7173_is_setup_equal(const struct ad7173_channel_config *cfg1,
sizeof(struct {
bool bipolar;
bool input_buf;
u8 odr;
u16 sinc3_odr_div;
u8 sinc5_odr_index;
u8 ref_sel;
enum ad7173_filter_type filter_type;
}));
return cfg1->bipolar == cfg2->bipolar &&
cfg1->input_buf == cfg2->input_buf &&
cfg1->odr == cfg2->odr &&
cfg1->ref_sel == cfg2->ref_sel;
cfg1->sinc3_odr_div == cfg2->sinc3_odr_div &&
cfg1->sinc5_odr_index == cfg2->sinc5_odr_index &&
cfg1->ref_sel == cfg2->ref_sel &&
cfg1->filter_type == cfg2->filter_type;
}
static struct ad7173_channel_config *
@ -630,6 +720,7 @@ static int ad7173_load_config(struct ad7173_state *st,
{
unsigned int config;
int free_cfg_slot, ret;
u8 post_filter_enable, post_filter_select;
free_cfg_slot = ida_alloc_range(&st->cfg_slots_status, 0,
st->info->num_configs - 1, GFP_KERNEL);
@ -649,8 +740,49 @@ static int ad7173_load_config(struct ad7173_state *st,
if (ret)
return ret;
/*
* When SINC3_MAP flag is enabled, the rest of the register has a
* different meaning. We are using this option to allow the most
* possible sampling frequencies with SINC3 filter.
*/
if (cfg->filter_type == AD7173_FILTER_SINC3)
return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2,
AD7173_FILTER_ODR0_MASK & cfg->odr);
FIELD_PREP(AD7173_FILTER_SINC3_MAP, 1) |
FIELD_PREP(AD7173_FILTER_SINC3_MAP_DIV,
cfg->sinc3_odr_div));
switch (cfg->filter_type) {
case AD7173_FILTER_SINC5_SINC1_PF1:
post_filter_enable = 1;
post_filter_select = 2;
break;
case AD7173_FILTER_SINC5_SINC1_PF2:
post_filter_enable = 1;
post_filter_select = 3;
break;
case AD7173_FILTER_SINC5_SINC1_PF3:
post_filter_enable = 1;
post_filter_select = 5;
break;
case AD7173_FILTER_SINC5_SINC1_PF4:
post_filter_enable = 1;
post_filter_select = 6;
break;
default:
post_filter_enable = 0;
post_filter_select = 0;
break;
}
return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2,
FIELD_PREP(AD7173_FILTER_SINC3_MAP, 0) |
FIELD_PREP(AD7173_FILTER_ENHFILT_MASK,
post_filter_enable) |
FIELD_PREP(AD7173_FILTER_ENHFILTEN,
post_filter_select) |
FIELD_PREP(AD7173_FILTER_ORDER, 0) |
FIELD_PREP(AD7173_FILTER_ODR_MASK,
cfg->sinc5_odr_index));
}
static int ad7173_config_channel(struct ad7173_state *st, int addr)
@ -761,6 +893,7 @@ static const struct ad_sigma_delta_info ad7173_sigma_delta_info_4_slots = {
.set_mode = ad7173_set_mode,
.has_registers = true,
.has_named_irqs = true,
.supports_spi_offload = true,
.addr_shift = 0,
.read_mask = BIT(6),
.status_ch_mask = GENMASK(3, 0),
@ -777,6 +910,7 @@ static const struct ad_sigma_delta_info ad7173_sigma_delta_info_8_slots = {
.set_mode = ad7173_set_mode,
.has_registers = true,
.has_named_irqs = true,
.supports_spi_offload = true,
.addr_shift = 0,
.read_mask = BIT(6),
.status_ch_mask = GENMASK(3, 0),
@ -793,6 +927,7 @@ static const struct ad_sigma_delta_info ad7173_sigma_delta_info_16_slots = {
.set_mode = ad7173_set_mode,
.has_registers = true,
.has_named_irqs = true,
.supports_spi_offload = true,
.addr_shift = 0,
.read_mask = BIT(6),
.status_ch_mask = GENMASK(3, 0),
@ -1180,7 +1315,14 @@ static int ad7173_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
case IIO_CHAN_INFO_SAMP_FREQ:
reg = st->channels[chan->address].cfg.odr;
if (st->channels[chan->address].cfg.filter_type == AD7173_FILTER_SINC3) {
/* Inverse operation of ad7173_sinc3_odr_div_from_odr() */
*val = MEGA;
*val2 = 3 * 32 * st->channels[chan->address].cfg.sinc3_odr_div;
return IIO_VAL_FRACTIONAL;
}
reg = st->channels[chan->address].cfg.sinc5_odr_index;
*val = st->info->sinc5_data_rates[reg] / MILLI;
*val2 = (st->info->sinc5_data_rates[reg] % MILLI) * (MICRO / MILLI);
@ -1218,6 +1360,10 @@ static int ad7173_write_raw(struct iio_dev *indio_dev,
*
* This will cause the reading of CH1 to be actually done once every
* 200.16ms, an effective rate of 4.99sps.
*
* Both the sinc5 and sinc3 rates are set here so that if the filter
* type is changed, the requested rate will still be set (aside from
* rounding differences).
*/
case IIO_CHAN_INFO_SAMP_FREQ:
freq = val * MILLI + val2 / MILLI;
@ -1226,7 +1372,8 @@ static int ad7173_write_raw(struct iio_dev *indio_dev,
break;
cfg = &st->channels[chan->address].cfg;
cfg->odr = i;
cfg->sinc5_odr_index = i;
cfg->sinc3_odr_div = ad7173_sinc3_odr_div_from_odr(freq);
cfg->live = false;
break;
@ -1243,17 +1390,40 @@ static int ad7173_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct ad7173_state *st = iio_priv(indio_dev);
u16 sinc3_count = 0;
u16 sinc3_div = 0;
int i, j, k, ret;
for (i = 0; i < indio_dev->num_channels; i++) {
if (test_bit(i, scan_mask))
const struct ad7173_channel_config *cfg = &st->channels[i].cfg;
if (test_bit(i, scan_mask)) {
if (cfg->filter_type == AD7173_FILTER_SINC3) {
sinc3_count++;
if (sinc3_div == 0) {
sinc3_div = cfg->sinc3_odr_div;
} else if (sinc3_div != cfg->sinc3_odr_div) {
dev_err(&st->sd.spi->dev,
"All enabled channels must have the same sampling_frequency for sinc3 filter_type\n");
return -EINVAL;
}
}
ret = ad7173_set_channel(&st->sd, i);
else
} else {
ret = ad_sd_write_reg(&st->sd, AD7173_REG_CH(i), 2, 0);
}
if (ret < 0)
return ret;
}
if (sinc3_count && sinc3_count < bitmap_weight(scan_mask, indio_dev->num_channels)) {
dev_err(&st->sd.spi->dev,
"All enabled channels must have sinc3 filter_type\n");
return -EINVAL;
}
/*
* On some chips, there are more channels that setups, so if there were
* more unique setups requested than the number of available slots,
@ -1396,7 +1566,7 @@ static const struct iio_chan_spec ad7173_channel_template = {
.storagebits = 32,
.endianness = IIO_BE,
},
.ext_info = ad7173_calibsys_ext_info,
.ext_info = ad7173_chan_spec_ext_info,
};
static const struct iio_chan_spec ad7173_temp_iio_channel_template = {
@ -1412,6 +1582,7 @@ static const struct iio_chan_spec ad7173_temp_iio_channel_template = {
.storagebits = 32,
.endianness = IIO_BE,
},
.ext_info = ad7173_temp_chan_spec_ext_info,
};
static void ad7173_disable_regulators(void *data)
@ -1652,12 +1823,21 @@ 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.odr = st->info->odr_start_value;
chan_st_priv->cfg.sinc3_odr_div = ad7173_sinc3_odr_div_from_odr(
st->info->sinc5_data_rates[st->info->odr_start_value]
);
chan_st_priv->cfg.sinc5_odr_index = st->info->odr_start_value;
chan_st_priv->cfg.filter_type = AD7173_FILTER_SINC5_SINC1;
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;
if (ad_sigma_delta_has_spi_offload(&st->sd)) {
chan_arr[chan_index].scan_type.storagebits = 32;
chan_arr[chan_index].scan_type.endianness = IIO_CPU;
}
chan_index++;
}
@ -1719,7 +1899,11 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
chan->scan_index = chan_index;
chan->channel = ain[0];
chan_st_priv->cfg.input_buf = st->info->has_input_buf;
chan_st_priv->cfg.odr = st->info->odr_start_value;
chan_st_priv->cfg.sinc3_odr_div = ad7173_sinc3_odr_div_from_odr(
st->info->sinc5_data_rates[st->info->odr_start_value]
);
chan_st_priv->cfg.sinc5_odr_index = st->info->odr_start_value;
chan_st_priv->cfg.filter_type = AD7173_FILTER_SINC5_SINC1;
chan_st_priv->cfg.openwire_comp_chan = -1;
chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar");
@ -1748,6 +1932,12 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
if (st->info->data_reg_only_16bit)
chan_arr[chan_index].scan_type = ad4113_scan_type;
/* Assuming SPI offload is ad411x_ad717x HDL project. */
if (ad_sigma_delta_has_spi_offload(&st->sd)) {
chan_arr[chan_index].scan_type.storagebits = 32;
chan_arr[chan_index].scan_type.endianness = IIO_CPU;
}
chan_index++;
}
return 0;
@ -1780,8 +1970,7 @@ static int ad7173_fw_parse_device_config(struct iio_dev *indio_dev)
ret = devm_add_action_or_reset(dev, ad7173_disable_regulators, st);
if (ret)
return dev_err_probe(dev, ret,
"Failed to add regulators disable action\n");
return ret;
ret = device_property_match_property_string(dev, "clock-names",
ad7173_clk_sel,

View File

@ -6,6 +6,7 @@
* Copyright 2010 Analog Devices Inc.
*/
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@ -27,22 +28,24 @@
struct ad7476_state;
struct ad7476_chip_info {
unsigned int int_vref_uv;
unsigned int int_vref_mv;
struct iio_chan_spec channel[2];
/* channels used when convst gpio is defined */
struct iio_chan_spec convst_channel[2];
void (*reset)(struct ad7476_state *);
void (*conversion_pre_op)(struct ad7476_state *st);
void (*conversion_post_op)(struct ad7476_state *st);
bool has_vref;
bool has_vdrive;
bool convstart_required;
};
struct ad7476_state {
struct spi_device *spi;
const struct ad7476_chip_info *chip_info;
struct regulator *ref_reg;
struct gpio_desc *convst_gpio;
struct spi_transfer xfer;
struct spi_message msg;
struct iio_chan_spec channel[2];
int scale_mv;
/*
* DMA (thus cache coherency maintenance) may require the
* transfer buffers to live in their own cache lines.
@ -52,40 +55,29 @@ struct ad7476_state {
unsigned char data[ALIGN(2, sizeof(s64)) + sizeof(s64)] __aligned(IIO_DMA_MINALIGN);
};
enum ad7476_supported_device_ids {
ID_AD7091,
ID_AD7091R,
ID_AD7273,
ID_AD7274,
ID_AD7276,
ID_AD7277,
ID_AD7278,
ID_AD7466,
ID_AD7467,
ID_AD7468,
ID_AD7475,
ID_AD7495,
ID_AD7940,
ID_ADC081S,
ID_ADC101S,
ID_ADC121S,
ID_ADS7866,
ID_ADS7867,
ID_ADS7868,
ID_LTC2314_14,
};
static void ad7091_convst(struct ad7476_state *st)
{
if (!st->convst_gpio)
return;
gpiod_set_value(st->convst_gpio, 0);
gpiod_set_value_cansleep(st->convst_gpio, 0);
udelay(1); /* CONVST pulse width: 10 ns min */
gpiod_set_value(st->convst_gpio, 1);
gpiod_set_value_cansleep(st->convst_gpio, 1);
udelay(1); /* Conversion time: 650 ns max */
}
static void bd79105_convst_disable(struct ad7476_state *st)
{
gpiod_set_value_cansleep(st->convst_gpio, 0);
}
static void bd79105_convst_enable(struct ad7476_state *st)
{
gpiod_set_value_cansleep(st->convst_gpio, 1);
/* Worst case, 2790 ns required for conversion */
ndelay(2790);
}
static irqreturn_t ad7476_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
@ -93,7 +85,8 @@ static irqreturn_t ad7476_trigger_handler(int irq, void *p)
struct ad7476_state *st = iio_priv(indio_dev);
int b_sent;
ad7091_convst(st);
if (st->chip_info->conversion_pre_op)
st->chip_info->conversion_pre_op(st);
b_sent = spi_sync(st->spi, &st->msg);
if (b_sent < 0)
@ -102,6 +95,8 @@ static irqreturn_t ad7476_trigger_handler(int irq, void *p)
iio_push_to_buffers_with_ts(indio_dev, st->data, sizeof(st->data),
iio_get_time_ns(indio_dev));
done:
if (st->chip_info->conversion_post_op)
st->chip_info->conversion_post_op(st);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
@ -117,12 +112,16 @@ static int ad7476_scan_direct(struct ad7476_state *st)
{
int ret;
ad7091_convst(st);
if (st->chip_info->conversion_pre_op)
st->chip_info->conversion_pre_op(st);
ret = spi_sync(st->spi, &st->msg);
if (ret)
return ret;
if (st->chip_info->conversion_post_op)
st->chip_info->conversion_post_op(st);
return be16_to_cpup((__be16 *)st->data);
}
@ -134,7 +133,6 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
{
int ret;
struct ad7476_state *st = iio_priv(indio_dev);
int scale_uv;
switch (m) {
case IIO_CHAN_INFO_RAW:
@ -145,18 +143,11 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
*val = (ret >> st->chip_info->channel[0].scan_type.shift) &
GENMASK(st->chip_info->channel[0].scan_type.realbits - 1, 0);
*val = (ret >> chan->scan_type.shift) &
GENMASK(chan->scan_type.realbits - 1, 0);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
if (st->ref_reg) {
scale_uv = regulator_get_voltage(st->ref_reg);
if (scale_uv < 0)
return scale_uv;
} else {
scale_uv = st->chip_info->int_vref_uv;
}
*val = scale_uv / 1000;
*val = st->scale_mv;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
}
@ -185,125 +176,147 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
BIT(IIO_CHAN_INFO_RAW))
#define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0)
#define AD7091R_CONVST_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), \
BIT(IIO_CHAN_INFO_RAW))
#define ADS786X_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \
BIT(IIO_CHAN_INFO_RAW))
static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
[ID_AD7091] = {
static const struct ad7476_chip_info ad7091_chip_info = {
.channel[0] = AD7091R_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.convst_channel[0] = AD7091R_CONVST_CHAN(12),
.convst_channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.conversion_pre_op = ad7091_convst,
.reset = ad7091_reset,
},
[ID_AD7091R] = {
};
static const struct ad7476_chip_info ad7091r_chip_info = {
.channel[0] = AD7091R_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.convst_channel[0] = AD7091R_CONVST_CHAN(12),
.convst_channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.int_vref_uv = 2500000,
.conversion_pre_op = ad7091_convst,
.int_vref_mv = 2500,
.has_vref = true,
.reset = ad7091_reset,
},
[ID_AD7273] = {
};
static const struct ad7476_chip_info ad7273_chip_info = {
.channel[0] = AD7940_CHAN(10),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.has_vref = true,
},
[ID_AD7274] = {
};
static const struct ad7476_chip_info ad7274_chip_info = {
.channel[0] = AD7940_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.has_vref = true,
},
[ID_AD7276] = {
};
static const struct ad7476_chip_info ad7276_chip_info = {
.channel[0] = AD7940_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_AD7277] = {
};
static const struct ad7476_chip_info ad7277_chip_info = {
.channel[0] = AD7940_CHAN(10),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_AD7278] = {
};
static const struct ad7476_chip_info ad7278_chip_info = {
.channel[0] = AD7940_CHAN(8),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_AD7466] = {
};
static const struct ad7476_chip_info ad7466_chip_info = {
.channel[0] = AD7476_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_AD7467] = {
};
static const struct ad7476_chip_info ad7467_chip_info = {
.channel[0] = AD7476_CHAN(10),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_AD7468] = {
};
static const struct ad7476_chip_info ad7468_chip_info = {
.channel[0] = AD7476_CHAN(8),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_AD7475] = {
};
static const struct ad7476_chip_info ad7475_chip_info = {
.channel[0] = AD7476_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.has_vref = true,
.has_vdrive = true,
},
[ID_AD7495] = {
};
static const struct ad7476_chip_info ad7495_chip_info = {
.channel[0] = AD7476_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.int_vref_uv = 2500000,
.int_vref_mv = 2500,
.has_vdrive = true,
},
[ID_AD7940] = {
};
static const struct ad7476_chip_info ad7940_chip_info = {
.channel[0] = AD7940_CHAN(14),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_ADC081S] = {
};
static const struct ad7476_chip_info adc081s_chip_info = {
.channel[0] = ADC081S_CHAN(8),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_ADC101S] = {
};
static const struct ad7476_chip_info adc101s_chip_info = {
.channel[0] = ADC081S_CHAN(10),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_ADC121S] = {
};
static const struct ad7476_chip_info adc121s_chip_info = {
.channel[0] = ADC081S_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_ADS7866] = {
};
static const struct ad7476_chip_info ads7866_chip_info = {
.channel[0] = ADS786X_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_ADS7867] = {
};
static const struct ad7476_chip_info ads7867_chip_info = {
.channel[0] = ADS786X_CHAN(10),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_ADS7868] = {
};
static const struct ad7476_chip_info ads7868_chip_info = {
.channel[0] = ADS786X_CHAN(8),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_LTC2314_14] = {
};
static const struct ad7476_chip_info ltc2314_14_chip_info = {
.channel[0] = AD7940_CHAN(14),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
.has_vref = true,
},
};
static const struct ad7476_chip_info bd79105_chip_info = {
.channel[0] = AD7091R_CHAN(16),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
/*
* The BD79105 starts ADC data conversion when the CONVSTART line is
* set HIGH. The CONVSTART must be kept HIGH until the data has been
* read from the ADC.
*/
.conversion_pre_op = bd79105_convst_enable,
.conversion_post_op = bd79105_convst_disable,
/* BD79105 won't do conversion without convstart */
.convstart_required = true,
.has_vref = true,
.has_vdrive = true,
};
static const struct iio_info ad7476_info = {
.read_raw = &ad7476_read_raw,
};
static void ad7476_reg_disable(void *data)
{
struct regulator *reg = data;
regulator_disable(reg);
}
static int ad7476_probe(struct spi_device *spi)
{
struct ad7476_state *st;
struct iio_dev *indio_dev;
struct regulator *reg;
unsigned int i;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
@ -311,61 +324,37 @@ static int ad7476_probe(struct spi_device *spi)
return -ENOMEM;
st = iio_priv(indio_dev);
st->chip_info =
&ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data];
reg = devm_regulator_get(&spi->dev, "vcc");
if (IS_ERR(reg))
return PTR_ERR(reg);
st->chip_info = spi_get_device_match_data(spi);
if (!st->chip_info)
return -ENODEV;
ret = regulator_enable(reg);
if (ret)
/* Use VCC for reference voltage if vref / internal vref aren't used */
if (!st->chip_info->int_vref_mv && !st->chip_info->has_vref) {
ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vcc");
if (ret < 0)
return ret;
ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable, reg);
if (ret)
st->scale_mv = ret / 1000;
} else {
ret = devm_regulator_get_enable(&spi->dev, "vcc");
if (ret < 0)
return ret;
/* Either vcc or vref (below) as appropriate */
if (!st->chip_info->int_vref_uv)
st->ref_reg = reg;
}
if (st->chip_info->has_vref) {
/* If a device has an internal reference vref is optional */
if (st->chip_info->int_vref_uv) {
reg = devm_regulator_get_optional(&spi->dev, "vref");
if (IS_ERR(reg) && (PTR_ERR(reg) != -ENODEV))
return PTR_ERR(reg);
} else {
reg = devm_regulator_get(&spi->dev, "vref");
if (IS_ERR(reg))
return PTR_ERR(reg);
}
if (!IS_ERR(reg)) {
ret = regulator_enable(reg);
if (ret)
ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref");
if (ret < 0) {
/* Vref is optional if a device has an internal reference */
if (!st->chip_info->int_vref_mv || ret != -ENODEV)
return ret;
ret = devm_add_action_or_reset(&spi->dev,
ad7476_reg_disable,
reg);
if (ret)
return ret;
st->ref_reg = reg;
} else {
/*
* Can only get here if device supports both internal
* and external reference, but the regulator connected
* to the external reference is not connected.
* Set the reference regulator pointer to NULL to
* indicate this.
*/
st->ref_reg = NULL;
st->scale_mv = ret / 1000;
}
}
if (!st->scale_mv)
st->scale_mv = st->chip_info->int_vref_mv;
if (st->chip_info->has_vdrive) {
ret = devm_regulator_get_enable(&spi->dev, "vdrive");
if (ret)
@ -378,20 +367,35 @@ static int ad7476_probe(struct spi_device *spi)
if (IS_ERR(st->convst_gpio))
return PTR_ERR(st->convst_gpio);
if (st->chip_info->convstart_required && !st->convst_gpio)
return dev_err_probe(&spi->dev, -EINVAL, "No convstart GPIO\n");
/*
* This will never happen. Unless someone changes the channel specs
* in this driver. And if someone does, without changing the loop
* below, then we'd better immediately produce a big fat error, before
* the change proceeds from that developer's table.
*/
static_assert(ARRAY_SIZE(st->channel) == ARRAY_SIZE(st->chip_info->channel));
for (i = 0; i < ARRAY_SIZE(st->channel); i++) {
st->channel[i] = st->chip_info->channel[i];
if (st->convst_gpio)
__set_bit(IIO_CHAN_INFO_RAW,
&st->channel[i].info_mask_separate);
}
st->spi = spi;
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = st->chip_info->channel;
indio_dev->num_channels = 2;
indio_dev->channels = st->channel;
indio_dev->num_channels = ARRAY_SIZE(st->channel);
indio_dev->info = &ad7476_info;
if (st->convst_gpio)
indio_dev->channels = st->chip_info->convst_channel;
/* Setup default message */
st->xfer.rx_buf = &st->data;
st->xfer.len = st->chip_info->channel[0].scan_type.storagebits / 8;
st->xfer.len = indio_dev->channels[0].scan_type.storagebits / 8;
spi_message_init(&st->msg);
spi_message_add_tail(&st->xfer, &st->msg);
@ -408,41 +412,42 @@ static int ad7476_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7476_id[] = {
{ "ad7091", ID_AD7091 },
{ "ad7091r", ID_AD7091R },
{ "ad7273", ID_AD7273 },
{ "ad7274", ID_AD7274 },
{ "ad7276", ID_AD7276},
{ "ad7277", ID_AD7277 },
{ "ad7278", ID_AD7278 },
{ "ad7466", ID_AD7466 },
{ "ad7467", ID_AD7467 },
{ "ad7468", ID_AD7468 },
{ "ad7475", ID_AD7475 },
{ "ad7476", ID_AD7466 },
{ "ad7476a", ID_AD7466 },
{ "ad7477", ID_AD7467 },
{ "ad7477a", ID_AD7467 },
{ "ad7478", ID_AD7468 },
{ "ad7478a", ID_AD7468 },
{ "ad7495", ID_AD7495 },
{ "ad7910", ID_AD7467 },
{ "ad7920", ID_AD7466 },
{ "ad7940", ID_AD7940 },
{ "adc081s", ID_ADC081S },
{ "adc101s", ID_ADC101S },
{ "adc121s", ID_ADC121S },
{ "ads7866", ID_ADS7866 },
{ "ads7867", ID_ADS7867 },
{ "ads7868", ID_ADS7868 },
{ "ad7091", (kernel_ulong_t)&ad7091_chip_info },
{ "ad7091r", (kernel_ulong_t)&ad7091r_chip_info },
{ "ad7273", (kernel_ulong_t)&ad7273_chip_info },
{ "ad7274", (kernel_ulong_t)&ad7274_chip_info },
{ "ad7276", (kernel_ulong_t)&ad7276_chip_info },
{ "ad7277", (kernel_ulong_t)&ad7277_chip_info },
{ "ad7278", (kernel_ulong_t)&ad7278_chip_info },
{ "ad7466", (kernel_ulong_t)&ad7466_chip_info },
{ "ad7467", (kernel_ulong_t)&ad7467_chip_info },
{ "ad7468", (kernel_ulong_t)&ad7468_chip_info },
{ "ad7475", (kernel_ulong_t)&ad7475_chip_info },
{ "ad7476", (kernel_ulong_t)&ad7466_chip_info },
{ "ad7476a", (kernel_ulong_t)&ad7466_chip_info },
{ "ad7477", (kernel_ulong_t)&ad7467_chip_info },
{ "ad7477a", (kernel_ulong_t)&ad7467_chip_info },
{ "ad7478", (kernel_ulong_t)&ad7468_chip_info },
{ "ad7478a", (kernel_ulong_t)&ad7468_chip_info },
{ "ad7495", (kernel_ulong_t)&ad7495_chip_info },
{ "ad7910", (kernel_ulong_t)&ad7467_chip_info },
{ "ad7920", (kernel_ulong_t)&ad7466_chip_info },
{ "ad7940", (kernel_ulong_t)&ad7940_chip_info },
{ "adc081s", (kernel_ulong_t)&adc081s_chip_info },
{ "adc101s", (kernel_ulong_t)&adc101s_chip_info },
{ "adc121s", (kernel_ulong_t)&adc121s_chip_info },
{ "ads7866", (kernel_ulong_t)&ads7866_chip_info },
{ "ads7867", (kernel_ulong_t)&ads7867_chip_info },
{ "ads7868", (kernel_ulong_t)&ads7868_chip_info },
{ "bd79105", (kernel_ulong_t)&bd79105_chip_info },
/*
* The ROHM BU79100G is identical to the TI's ADS7866 from the software
* point of view. The binding document mandates the ADS7866 to be
* marked as a fallback for the BU79100G, but we still need the SPI ID
* here to make the module loading work.
*/
{ "bu79100g", ID_ADS7866 },
{ "ltc2314-14", ID_LTC2314_14 },
{ "bu79100g", (kernel_ulong_t)&ads7866_chip_info },
{ "ltc2314-14", (kernel_ulong_t)&ltc2314_14_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7476_id);

View File

@ -217,7 +217,7 @@ struct ad7768_state {
struct spi_device *spi;
struct regmap *regmap;
struct regmap *regmap24;
struct regulator *vref;
int vref_uv;
struct regulator_dev *vcm_rdev;
unsigned int vcm_output_sel;
struct clk *mclk;
@ -687,8 +687,6 @@ static int ad7768_set_freq(struct ad7768_state *st,
int ret;
freq = clamp(freq, 50, 1024000);
if (freq == 0)
return -EINVAL;
mclk_div = DIV_ROUND_CLOSEST(st->mclk_freq, freq * st->oversampling_ratio);
/* Find the closest match for the desired sampling frequency */
@ -776,7 +774,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
{
struct ad7768_state *st = iio_priv(indio_dev);
const struct iio_scan_type *scan_type;
int scale_uv, ret, temp;
int ret, temp;
scan_type = iio_get_current_scan_type(indio_dev, chan);
if (IS_ERR(scan_type))
@ -797,11 +795,7 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
scale_uv = regulator_get_voltage(st->vref);
if (scale_uv < 0)
return scale_uv;
*val = (scale_uv * 2) / 1000;
*val = (st->vref_uv * 2) / 1000;
*val2 = scan_type->realbits;
return IIO_VAL_FRACTIONAL_LOG2;
@ -1134,13 +1128,6 @@ static const struct iio_trigger_ops ad7768_trigger_ops = {
.validate_device = iio_trigger_validate_own_device,
};
static void ad7768_regulator_disable(void *data)
{
struct ad7768_state *st = data;
regulator_disable(st->vref);
}
static int ad7768_set_channel_label(struct iio_dev *indio_dev,
int num_channels)
{
@ -1372,19 +1359,11 @@ static int ad7768_probe(struct spi_device *spi)
return dev_err_probe(&spi->dev, PTR_ERR(st->regmap24),
"Failed to initialize regmap24");
st->vref = devm_regulator_get(&spi->dev, "vref");
if (IS_ERR(st->vref))
return PTR_ERR(st->vref);
ret = regulator_enable(st->vref);
if (ret) {
dev_err(&spi->dev, "Failed to enable specified vref supply\n");
return ret;
}
ret = devm_add_action_or_reset(&spi->dev, ad7768_regulator_disable, st);
if (ret)
return ret;
ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vref");
if (ret < 0)
return dev_err_probe(&spi->dev, ret,
"Failed to get VREF voltage\n");
st->vref_uv = ret;
st->mclk = devm_clk_get_enabled(&spi->dev, "mclk");
if (IS_ERR(st->mclk))

View File

@ -25,6 +25,7 @@
#include <linux/units.h>
#include <linux/iio/iio.h>
#include <linux/iio/backend.h>
#include <linux/iio/buffer.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
@ -145,6 +146,7 @@ struct ad7779_state {
struct completion completion;
unsigned int sampling_freq;
enum ad7779_filter filter_enabled;
struct iio_backend *back;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
@ -630,12 +632,38 @@ static int ad7779_reset(struct iio_dev *indio_dev, struct gpio_desc *reset_gpio)
return ret;
}
static int ad7779_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct ad7779_state *st = iio_priv(indio_dev);
unsigned int c;
int ret;
for (c = 0; c < AD7779_NUM_CHANNELS; c++) {
if (test_bit(c, scan_mask))
ret = iio_backend_chan_enable(st->back, c);
else
ret = iio_backend_chan_disable(st->back, c);
if (ret)
return ret;
}
return 0;
}
static const struct iio_info ad7779_info = {
.read_raw = ad7779_read_raw,
.write_raw = ad7779_write_raw,
.debugfs_reg_access = &ad7779_reg_access,
};
static const struct iio_info ad7779_info_data = {
.read_raw = ad7779_read_raw,
.write_raw = ad7779_write_raw,
.debugfs_reg_access = &ad7779_reg_access,
.update_scan_mode = &ad7779_update_scan_mode,
};
static const struct iio_enum ad7779_filter_enum = {
.items = ad7779_filter_type,
.num_items = ARRAY_SIZE(ad7779_filter_type),
@ -752,6 +780,125 @@ static int ad7779_conf(struct ad7779_state *st, struct gpio_desc *start_gpio)
return 0;
}
static int ad7779_set_data_lines(struct iio_dev *indio_dev, u32 num_lanes)
{
struct ad7779_state *st = iio_priv(indio_dev);
int ret;
if (num_lanes != 1 && num_lanes != 2 && num_lanes != 4)
return -EINVAL;
ret = ad7779_set_sampling_frequency(st, num_lanes * AD7779_DEFAULT_SAMPLING_1LINE);
if (ret)
return ret;
ret = iio_backend_num_lanes_set(st->back, num_lanes);
if (ret)
return ret;
return ad7779_spi_write_mask(st, AD7779_REG_DOUT_FORMAT,
AD7779_DOUT_FORMAT_MSK,
FIELD_PREP(AD7779_DOUT_FORMAT_MSK, 2 - ilog2(num_lanes)));
}
static int ad7779_setup_channels(struct iio_dev *indio_dev, const struct ad7779_state *st)
{
struct iio_chan_spec *channels;
struct device *dev = &st->spi->dev;
channels = devm_kmemdup_array(dev, st->chip_info->channels,
ARRAY_SIZE(ad7779_channels),
sizeof(*channels), GFP_KERNEL);
if (!channels)
return -ENOMEM;
for (unsigned int i = 0; i < ARRAY_SIZE(ad7779_channels); i++)
channels[i].scan_type.endianness = IIO_CPU;
indio_dev->channels = channels;
indio_dev->num_channels = ARRAY_SIZE(ad7779_channels);
return 0;
}
static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev *indio_dev)
{
int ret;
struct device *dev = &st->spi->dev;
indio_dev->info = &ad7779_info;
indio_dev->channels = st->chip_info->channels;
indio_dev->num_channels = ARRAY_SIZE(ad7779_channels);
st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name,
iio_device_id(indio_dev));
if (!st->trig)
return -ENOMEM;
st->trig->ops = &ad7779_trigger_ops;
iio_trigger_set_drvdata(st->trig, st);
ret = devm_request_irq(dev, st->spi->irq, iio_trigger_generic_data_rdy_poll,
IRQF_ONESHOT | IRQF_NO_AUTOEN, indio_dev->name,
st->trig);
if (ret)
return dev_err_probe(dev, ret, "request IRQ %d failed\n",
st->spi->irq);
ret = devm_iio_trigger_register(dev, st->trig);
if (ret)
return ret;
indio_dev->trig = iio_trigger_get(st->trig);
init_completion(&st->completion);
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
&iio_pollfunc_store_time,
&ad7779_trigger_handler,
&ad7779_buffer_setup_ops);
if (ret)
return ret;
return ad7779_spi_write_mask(st, AD7779_REG_DOUT_FORMAT,
AD7779_DCLK_CLK_DIV_MSK,
FIELD_PREP(AD7779_DCLK_CLK_DIV_MSK, 7));
}
static int ad7779_setup_backend(struct ad7779_state *st, struct iio_dev *indio_dev)
{
struct device *dev = &st->spi->dev;
int ret;
u32 num_lanes;
indio_dev->info = &ad7779_info_data;
ret = ad7779_setup_channels(indio_dev, st);
if (ret)
return ret;
st->back = devm_iio_backend_get(dev, NULL);
if (IS_ERR(st->back))
return dev_err_probe(dev, PTR_ERR(st->back),
"failed to get iio backend");
ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
if (ret)
return ret;
ret = devm_iio_backend_enable(dev, st->back);
if (ret)
return ret;
num_lanes = 4;
ret = device_property_read_u32(dev, "adi,num-lanes", &num_lanes);
if (ret && ret != -EINVAL)
return ret;
return ad7779_set_data_lines(indio_dev, num_lanes);
}
static int ad7779_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
@ -760,9 +907,6 @@ static int ad7779_probe(struct spi_device *spi)
struct device *dev = &spi->dev;
int ret = -EINVAL;
if (!spi->irq)
return dev_err_probe(dev, ret, "DRDY irq not present\n");
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
@ -804,45 +948,12 @@ static int ad7779_probe(struct spi_device *spi)
return ret;
indio_dev->name = st->chip_info->name;
indio_dev->info = &ad7779_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = st->chip_info->channels;
indio_dev->num_channels = ARRAY_SIZE(ad7779_channels);
st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name,
iio_device_id(indio_dev));
if (!st->trig)
return -ENOMEM;
st->trig->ops = &ad7779_trigger_ops;
iio_trigger_set_drvdata(st->trig, st);
ret = devm_request_irq(dev, spi->irq, iio_trigger_generic_data_rdy_poll,
IRQF_ONESHOT | IRQF_NO_AUTOEN, indio_dev->name,
st->trig);
if (ret)
return dev_err_probe(dev, ret, "request IRQ %d failed\n",
st->spi->irq);
ret = devm_iio_trigger_register(dev, st->trig);
if (ret)
return ret;
indio_dev->trig = iio_trigger_get(st->trig);
init_completion(&st->completion);
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
&iio_pollfunc_store_time,
&ad7779_trigger_handler,
&ad7779_buffer_setup_ops);
if (ret)
return ret;
ret = ad7779_spi_write_mask(st, AD7779_REG_DOUT_FORMAT,
AD7779_DCLK_CLK_DIV_MSK,
FIELD_PREP(AD7779_DCLK_CLK_DIV_MSK, 7));
if (device_property_present(dev, "io-backends"))
ret = ad7779_setup_backend(st, indio_dev);
else
ret = ad7779_setup_without_backend(st, indio_dev);
if (ret)
return ret;
@ -936,3 +1047,4 @@ module_spi_driver(ad7779_driver);
MODULE_AUTHOR("Ramona Alexandra Nechita <ramona.nechita@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7779 ADC");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("IIO_BACKEND");

View File

@ -316,10 +316,8 @@ static int ad7949_spi_probe(struct spi_device *spi)
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*ad7949_adc));
if (!indio_dev) {
dev_err(dev, "can not allocate iio device\n");
if (!indio_dev)
return -ENOMEM;
}
indio_dev->info = &ad7949_spi_info;
indio_dev->name = spi_get_device_id(spi)->name;

View File

@ -114,11 +114,13 @@ struct ad799x_chip_config {
* @num_channels: number of channels
* @noirq_config: device configuration w/o IRQ
* @irq_config: device configuration w/IRQ
* @has_vref: device supports external reference voltage
*/
struct ad799x_chip_info {
int num_channels;
const struct ad799x_chip_config noirq_config;
const struct ad799x_chip_config irq_config;
bool has_vref;
};
struct ad799x_state {
@ -604,6 +606,7 @@ static const struct iio_event_spec ad799x_events[] = {
static const struct ad799x_chip_info ad799x_chip_info_tbl[] = {
[ad7991] = {
.num_channels = 5,
.has_vref = true,
.noirq_config = {
.channel = {
AD799X_CHANNEL(0, 12),
@ -617,6 +620,7 @@ static const struct ad799x_chip_info ad799x_chip_info_tbl[] = {
},
[ad7995] = {
.num_channels = 5,
.has_vref = true,
.noirq_config = {
.channel = {
AD799X_CHANNEL(0, 10),
@ -630,6 +634,7 @@ static const struct ad799x_chip_info ad799x_chip_info_tbl[] = {
},
[ad7999] = {
.num_channels = 5,
.has_vref = true,
.noirq_config = {
.channel = {
AD799X_CHANNEL(0, 8),
@ -687,6 +692,7 @@ static const struct ad799x_chip_info ad799x_chip_info_tbl[] = {
},
[ad7994] = {
.num_channels = 5,
.has_vref = true,
.noirq_config = {
.channel = {
AD799X_CHANNEL(0, 12),
@ -809,32 +815,22 @@ static int ad799x_probe(struct i2c_client *client)
return ret;
/* check if an external reference is supplied */
if (chip_info->has_vref) {
st->vref = devm_regulator_get_optional(&client->dev, "vref");
if (IS_ERR(st->vref)) {
if (PTR_ERR(st->vref) == -ENODEV) {
ret = PTR_ERR_OR_ZERO(st->vref);
if (ret) {
if (ret != -ENODEV)
goto error_disable_reg;
st->vref = NULL;
dev_info(&client->dev, "Using VCC reference voltage\n");
} else {
ret = PTR_ERR(st->vref);
goto error_disable_reg;
}
}
if (st->vref) {
/*
* Use external reference voltage if supported by hardware.
* This is optional if voltage / regulator present, use VCC otherwise.
*/
if ((st->id == ad7991) || (st->id == ad7995) || (st->id == ad7999)) {
dev_info(&client->dev, "Using external reference voltage\n");
extra_config |= AD7991_REF_SEL;
ret = regulator_enable(st->vref);
if (ret)
goto error_disable_reg;
} else {
st->vref = NULL;
dev_warn(&client->dev, "Supplied reference not supported\n");
}
}

1799
drivers/iio/adc/ade9000.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -618,6 +618,7 @@ static const struct iio_backend_ops adi_axi_adc_ops = {
.chan_status = axi_adc_chan_status,
.interface_type_get = axi_adc_interface_type_get,
.oversampling_ratio_set = axi_adc_oversampling_ratio_set,
.num_lanes_set = axi_adc_num_lanes_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),
};

View File

@ -896,7 +896,6 @@ static int at91_adc_config_emr(struct at91_adc_state *st,
emr |= osr | AT91_SAMA5D2_TRACKX(trackx);
at91_adc_writel(st, EMR, emr);
pm_runtime_mark_last_busy(st->dev);
pm_runtime_put_autosuspend(st->dev);
st->oversampling_ratio = oversampling_ratio;
@ -971,7 +970,6 @@ static int at91_adc_configure_touch(struct at91_adc_state *st, bool state)
AT91_SAMA5D2_IER_PEN | AT91_SAMA5D2_IER_NOPEN);
at91_adc_writel(st, TSMR, 0);
pm_runtime_mark_last_busy(st->dev);
pm_runtime_put_autosuspend(st->dev);
return 0;
}
@ -1142,10 +1140,8 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
at91_adc_configure_trigger_registers(st, state);
if (!state) {
pm_runtime_mark_last_busy(st->dev);
if (!state)
pm_runtime_put_autosuspend(st->dev);
}
return 0;
}
@ -1336,7 +1332,6 @@ static int at91_adc_buffer_prepare(struct iio_dev *indio_dev)
at91_adc_writel(st, IER, AT91_SAMA5D2_IER_DRDY);
pm_runtime_put:
pm_runtime_mark_last_busy(st->dev);
pm_runtime_put_autosuspend(st->dev);
return ret;
}
@ -1394,7 +1389,6 @@ static int at91_adc_buffer_postdisable(struct iio_dev *indio_dev)
if (st->dma_st.dma_chan)
dmaengine_terminate_sync(st->dma_st.dma_chan);
pm_runtime_mark_last_busy(st->dev);
pm_runtime_put_autosuspend(st->dev);
return 0;
@ -1603,7 +1597,6 @@ static void at91_adc_setup_samp_freq(struct iio_dev *indio_dev, unsigned freq,
mr |= AT91_SAMA5D2_MR_TRACKTIM(tracktim);
at91_adc_writel(st, MR, mr);
pm_runtime_mark_last_busy(st->dev);
pm_runtime_put_autosuspend(st->dev);
dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u, tracktim=%u\n",
@ -1809,7 +1802,6 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev,
at91_adc_readl(st, LCDR);
pm_runtime_put:
pm_runtime_mark_last_busy(st->dev);
pm_runtime_put_autosuspend(st->dev);
return ret;
}
@ -1890,7 +1882,6 @@ static int at91_adc_read_temp(struct iio_dev *indio_dev,
restore_config:
/* Revert previous settings. */
at91_adc_temp_sensor_configure(st, false);
pm_runtime_mark_last_busy(st->dev);
pm_runtime_put_autosuspend(st->dev);
if (ret < 0)
return ret;
@ -2465,7 +2456,6 @@ static int at91_adc_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "version: %x\n",
readl_relaxed(st->base + st->soc_info.platform->layout->VERSION));
pm_runtime_mark_last_busy(st->dev);
pm_runtime_put_autosuspend(st->dev);
return 0;
@ -2567,7 +2557,6 @@ static int at91_adc_resume(struct device *dev)
at91_adc_configure_trigger_registers(st, true);
}
pm_runtime_mark_last_busy(st->dev);
pm_runtime_put_autosuspend(st->dev);
return 0;

View File

@ -511,10 +511,8 @@ static int iproc_adc_probe(struct platform_device *pdev)
indio_dev = devm_iio_device_alloc(&pdev->dev,
sizeof(*adc_priv));
if (!indio_dev) {
dev_err(&pdev->dev, "failed to allocate iio device\n");
if (!indio_dev)
return -ENOMEM;
}
adc_priv = iio_priv(indio_dev);
platform_set_drvdata(pdev, indio_dev);

View File

@ -953,11 +953,9 @@ static int cpcap_adc_probe(struct platform_device *pdev)
int error;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*ddata));
if (!indio_dev) {
dev_err(&pdev->dev, "failed to allocate iio device\n");
if (!indio_dev)
return -ENOMEM;
}
ddata = iio_priv(indio_dev);
ddata->ato = device_get_match_data(&pdev->dev);
if (!ddata->ato)

View File

@ -308,10 +308,9 @@ static int da9150_gpadc_probe(struct platform_device *pdev)
int irq, ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc));
if (!indio_dev) {
dev_err(&pdev->dev, "Failed to allocate IIO device\n");
if (!indio_dev)
return -ENOMEM;
}
gpadc = iio_priv(indio_dev);
gpadc->da9150 = da9150;

View File

@ -584,10 +584,8 @@ static int dln2_adc_probe(struct platform_device *pdev)
int i, ret, chans;
indio_dev = devm_iio_device_alloc(dev, sizeof(*dln2));
if (!indio_dev) {
dev_err(dev, "failed allocating iio device\n");
if (!indio_dev)
return -ENOMEM;
}
dln2 = iio_priv(indio_dev);
dln2->pdev = pdev;
@ -628,10 +626,9 @@ static int dln2_adc_probe(struct platform_device *pdev)
dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
indio_dev->name,
iio_device_id(indio_dev));
if (!dln2->trig) {
dev_err(dev, "failed to allocate trigger\n");
if (!dln2->trig)
return -ENOMEM;
}
iio_trigger_set_drvdata(dln2->trig, dln2);
ret = devm_iio_trigger_register(dev, dln2->trig);
if (ret) {

View File

@ -19,11 +19,9 @@
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/regulator/consumer.h>
#include <linux/of_platform.h>
#include <linux/err.h>
#include <linux/input.h>
#include <linux/iio/iio.h>
#include <linux/iio/machine.h>
@ -31,21 +29,14 @@
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <linux/platform_data/touchscreen-s3c2410.h>
/* S3C/EXYNOS4412/5250 ADC_V1 registers definitions */
#define ADC_V1_CON(x) ((x) + 0x00)
#define ADC_V1_TSC(x) ((x) + 0x04)
#define ADC_V1_DLY(x) ((x) + 0x08)
#define ADC_V1_DATX(x) ((x) + 0x0C)
#define ADC_V1_DATY(x) ((x) + 0x10)
#define ADC_V1_UPDN(x) ((x) + 0x14)
#define ADC_V1_INTCLR(x) ((x) + 0x18)
#define ADC_V1_MUX(x) ((x) + 0x1c)
#define ADC_V1_CLRINTPNDNUP(x) ((x) + 0x20)
/* S3C2410 ADC registers definitions */
#define ADC_S3C2410_MUX(x) ((x) + 0x18)
/* Future ADC_V2 registers definitions */
#define ADC_V2_CON1(x) ((x) + 0x00)
@ -61,13 +52,8 @@
#define ADC_V1_CON_PRSCLV(x) (((x) & 0xFF) << 6)
#define ADC_V1_CON_STANDBY (1u << 2)
/* Bit definitions for S3C2410 ADC */
/* Bit definitions for S3C2410 / S3C6410 ADC */
#define ADC_S3C2410_CON_SELMUX(x) (((x) & 7) << 3)
#define ADC_S3C2410_DATX_MASK 0x3FF
#define ADC_S3C2416_CON_RES_SEL (1u << 3)
/* touch screen always uses channel 0 */
#define ADC_S3C2410_MUX_TS 0
/* ADCTSC Register Bits */
#define ADC_S3C2443_TSC_UD_SEN (1u << 8)
@ -75,8 +61,6 @@
#define ADC_S3C2410_TSC_YP_SEN (1u << 6)
#define ADC_S3C2410_TSC_XM_SEN (1u << 5)
#define ADC_S3C2410_TSC_XP_SEN (1u << 4)
#define ADC_S3C2410_TSC_PULL_UP_DISABLE (1u << 3)
#define ADC_S3C2410_TSC_AUTO_PST (1u << 2)
#define ADC_S3C2410_TSC_XY_PST(x) (((x) & 0x3) << 0)
#define ADC_TSC_WAIT4INT (ADC_S3C2410_TSC_YM_SEN | \
@ -84,12 +68,6 @@
ADC_S3C2410_TSC_XP_SEN | \
ADC_S3C2410_TSC_XY_PST(3))
#define ADC_TSC_AUTOPST (ADC_S3C2410_TSC_YM_SEN | \
ADC_S3C2410_TSC_YP_SEN | \
ADC_S3C2410_TSC_XP_SEN | \
ADC_S3C2410_TSC_AUTO_PST | \
ADC_S3C2410_TSC_XY_PST(0))
/* Bit definitions for ADC_V2 */
#define ADC_V2_CON1_SOFT_RESET (1u << 2)
@ -121,14 +99,11 @@
struct exynos_adc {
struct exynos_adc_data *data;
struct device *dev;
struct input_dev *input;
void __iomem *regs;
struct regmap *pmu_map;
struct clk *clk;
struct clk *sclk;
unsigned int irq;
unsigned int tsirq;
unsigned int delay;
struct regulator *vdd;
struct completion completion;
@ -136,12 +111,6 @@ struct exynos_adc {
u32 value;
unsigned int version;
bool ts_enabled;
bool read_ts;
u32 ts_x;
u32 ts_y;
/*
* Lock to protect from potential concurrent access to the
* completion callback during a manual conversion. For this driver
@ -241,7 +210,7 @@ static void exynos_adc_v1_init_hw(struct exynos_adc *info)
writel(con1, ADC_V1_CON(info->regs));
/* set touchscreen delay */
writel(info->delay, ADC_V1_DLY(info->regs));
writel(10000, ADC_V1_DLY(info->regs));
}
static void exynos_adc_v1_exit_hw(struct exynos_adc *info)
@ -307,53 +276,6 @@ static const struct exynos_adc_data exynos_adc_s5pv210_data = {
.start_conv = exynos_adc_v1_start_conv,
};
static void exynos_adc_s3c2416_start_conv(struct exynos_adc *info,
unsigned long addr)
{
u32 con1;
/* Enable 12 bit ADC resolution */
con1 = readl(ADC_V1_CON(info->regs));
con1 |= ADC_S3C2416_CON_RES_SEL;
writel(con1, ADC_V1_CON(info->regs));
/* Select channel for S3C2416 */
writel(addr, ADC_S3C2410_MUX(info->regs));
con1 = readl(ADC_V1_CON(info->regs));
writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
}
static struct exynos_adc_data const exynos_adc_s3c2416_data = {
.num_channels = MAX_ADC_V1_CHANNELS,
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
.init_hw = exynos_adc_v1_init_hw,
.exit_hw = exynos_adc_v1_exit_hw,
.start_conv = exynos_adc_s3c2416_start_conv,
};
static void exynos_adc_s3c2443_start_conv(struct exynos_adc *info,
unsigned long addr)
{
u32 con1;
/* Select channel for S3C2433 */
writel(addr, ADC_S3C2410_MUX(info->regs));
con1 = readl(ADC_V1_CON(info->regs));
writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
}
static struct exynos_adc_data const exynos_adc_s3c2443_data = {
.num_channels = MAX_ADC_V1_CHANNELS,
.mask = ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */
.init_hw = exynos_adc_v1_init_hw,
.exit_hw = exynos_adc_v1_exit_hw,
.start_conv = exynos_adc_s3c2443_start_conv,
};
static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info,
unsigned long addr)
{
@ -365,15 +287,6 @@ static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info,
writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
}
static struct exynos_adc_data const exynos_adc_s3c24xx_data = {
.num_channels = MAX_ADC_V1_CHANNELS,
.mask = ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */
.init_hw = exynos_adc_v1_init_hw,
.exit_hw = exynos_adc_v1_exit_hw,
.start_conv = exynos_adc_s3c64xx_start_conv,
};
static struct exynos_adc_data const exynos_adc_s3c64xx_data = {
.num_channels = MAX_ADC_V1_CHANNELS,
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
@ -486,18 +399,6 @@ static const struct exynos_adc_data exynos7_adc_data = {
static const struct of_device_id exynos_adc_match[] = {
{
.compatible = "samsung,s3c2410-adc",
.data = &exynos_adc_s3c24xx_data,
}, {
.compatible = "samsung,s3c2416-adc",
.data = &exynos_adc_s3c2416_data,
}, {
.compatible = "samsung,s3c2440-adc",
.data = &exynos_adc_s3c24xx_data,
}, {
.compatible = "samsung,s3c2443-adc",
.data = &exynos_adc_s3c2443_data,
}, {
.compatible = "samsung,s3c6410-adc",
.data = &exynos_adc_s3c64xx_data,
}, {
@ -580,55 +481,13 @@ static int exynos_read_raw(struct iio_dev *indio_dev,
return ret;
}
static int exynos_read_s3c64xx_ts(struct iio_dev *indio_dev, int *x, int *y)
{
struct exynos_adc *info = iio_priv(indio_dev);
unsigned long time_left;
int ret;
mutex_lock(&info->lock);
info->read_ts = true;
reinit_completion(&info->completion);
writel(ADC_S3C2410_TSC_PULL_UP_DISABLE | ADC_TSC_AUTOPST,
ADC_V1_TSC(info->regs));
/* Select the ts channel to be used and Trigger conversion */
info->data->start_conv(info, ADC_S3C2410_MUX_TS);
time_left = wait_for_completion_timeout(&info->completion,
EXYNOS_ADC_TIMEOUT);
if (time_left == 0) {
dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n");
if (info->data->init_hw)
info->data->init_hw(info);
ret = -ETIMEDOUT;
} else {
*x = info->ts_x;
*y = info->ts_y;
ret = 0;
}
info->read_ts = false;
mutex_unlock(&info->lock);
return ret;
}
static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
{
struct exynos_adc *info = dev_id;
u32 mask = info->data->mask;
/* Read value */
if (info->read_ts) {
info->ts_x = readl(ADC_V1_DATX(info->regs));
info->ts_y = readl(ADC_V1_DATY(info->regs));
writel(ADC_TSC_WAIT4INT | ADC_S3C2443_TSC_UD_SEN, ADC_V1_TSC(info->regs));
} else {
info->value = readl(ADC_V1_DATX(info->regs)) & mask;
}
/* clear irq */
if (info->data->clear_irq)
@ -639,46 +498,6 @@ static irqreturn_t exynos_adc_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
/*
* Here we (ab)use a threaded interrupt handler to stay running
* for as long as the touchscreen remains pressed, we report
* a new event with the latest data and then sleep until the
* next timer tick. This mirrors the behavior of the old
* driver, with much less code.
*/
static irqreturn_t exynos_ts_isr(int irq, void *dev_id)
{
struct exynos_adc *info = dev_id;
struct iio_dev *dev = dev_get_drvdata(info->dev);
u32 x, y;
bool pressed;
int ret;
while (READ_ONCE(info->ts_enabled)) {
ret = exynos_read_s3c64xx_ts(dev, &x, &y);
if (ret == -ETIMEDOUT)
break;
pressed = x & y & ADC_DATX_PRESSED;
if (!pressed) {
input_report_key(info->input, BTN_TOUCH, 0);
input_sync(info->input);
break;
}
input_report_abs(info->input, ABS_X, x & ADC_DATX_MASK);
input_report_abs(info->input, ABS_Y, y & ADC_DATY_MASK);
input_report_key(info->input, BTN_TOUCH, 1);
input_sync(info->input);
usleep_range(1000, 1100);
}
writel(0, ADC_V1_CLRINTPNDNUP(info->regs));
return IRQ_HANDLED;
}
static int exynos_adc_reg_access(struct iio_dev *indio_dev,
unsigned reg, unsigned writeval,
unsigned *readval)
@ -730,78 +549,17 @@ static int exynos_adc_remove_devices(struct device *dev, void *c)
return 0;
}
static int exynos_adc_ts_open(struct input_dev *dev)
{
struct exynos_adc *info = input_get_drvdata(dev);
WRITE_ONCE(info->ts_enabled, true);
enable_irq(info->tsirq);
return 0;
}
static void exynos_adc_ts_close(struct input_dev *dev)
{
struct exynos_adc *info = input_get_drvdata(dev);
WRITE_ONCE(info->ts_enabled, false);
disable_irq(info->tsirq);
}
static int exynos_adc_ts_init(struct exynos_adc *info)
{
int ret;
if (info->tsirq <= 0)
return -ENODEV;
info->input = input_allocate_device();
if (!info->input)
return -ENOMEM;
info->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
info->input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(info->input, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(info->input, ABS_Y, 0, 0x3FF, 0, 0);
info->input->name = "S3C24xx TouchScreen";
info->input->id.bustype = BUS_HOST;
info->input->open = exynos_adc_ts_open;
info->input->close = exynos_adc_ts_close;
input_set_drvdata(info->input, info);
ret = input_register_device(info->input);
if (ret) {
input_free_device(info->input);
return ret;
}
ret = request_threaded_irq(info->tsirq, NULL, exynos_ts_isr,
IRQF_ONESHOT | IRQF_NO_AUTOEN,
"touchscreen", info);
if (ret)
input_unregister_device(info->input);
return ret;
}
static int exynos_adc_probe(struct platform_device *pdev)
{
struct exynos_adc *info = NULL;
struct device_node *np = pdev->dev.of_node;
struct s3c2410_ts_mach_info *pdata = dev_get_platdata(&pdev->dev);
struct iio_dev *indio_dev = NULL;
bool has_ts = false;
int ret;
int irq;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct exynos_adc));
if (!indio_dev) {
dev_err(&pdev->dev, "failed allocating iio device\n");
if (!indio_dev)
return -ENOMEM;
}
info = iio_priv(indio_dev);
@ -826,27 +584,10 @@ static int exynos_adc_probe(struct platform_device *pdev)
}
}
/* leave out any TS related code if unreachable */
if (IS_REACHABLE(CONFIG_INPUT)) {
has_ts = of_property_read_bool(pdev->dev.of_node,
"has-touchscreen") || pdata;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
info->irq = irq;
if (has_ts) {
irq = platform_get_irq(pdev, 1);
if (irq == -EPROBE_DEFER)
return irq;
info->tsirq = irq;
} else {
info->tsirq = -1;
}
info->dev = &pdev->dev;
init_completion(&info->completion);
@ -910,16 +651,6 @@ static int exynos_adc_probe(struct platform_device *pdev)
if (info->data->init_hw)
info->data->init_hw(info);
if (pdata)
info->delay = pdata->delay;
else
info->delay = 10000;
if (has_ts)
ret = exynos_adc_ts_init(info);
if (ret)
goto err_iio;
ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev);
if (ret < 0) {
dev_err(&pdev->dev, "failed adding child nodes\n");
@ -931,11 +662,6 @@ static int exynos_adc_probe(struct platform_device *pdev)
err_of_populate:
device_for_each_child(&indio_dev->dev, NULL,
exynos_adc_remove_devices);
if (has_ts) {
input_unregister_device(info->input);
free_irq(info->tsirq, info);
}
err_iio:
iio_device_unregister(indio_dev);
err_irq:
free_irq(info->irq, info);
@ -955,10 +681,6 @@ static void exynos_adc_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct exynos_adc *info = iio_priv(indio_dev);
if (IS_REACHABLE(CONFIG_INPUT) && info->input) {
free_irq(info->tsirq, info);
input_unregister_device(info->input);
}
device_for_each_child(&indio_dev->dev, NULL,
exynos_adc_remove_devices);
iio_device_unregister(indio_dev);

View File

@ -465,7 +465,7 @@ static int hx711_probe(struct platform_device *pdev)
indio_dev = devm_iio_device_alloc(dev, sizeof(struct hx711_data));
if (!indio_dev)
return dev_err_probe(dev, -ENOMEM, "failed to allocate IIO device\n");
return -ENOMEM;
hx711_data = iio_priv(indio_dev);
hx711_data->dev = dev;

View File

@ -482,10 +482,8 @@ static int imx7d_adc_probe(struct platform_device *pdev)
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
if (!indio_dev) {
dev_err(&pdev->dev, "Failed allocating iio device\n");
if (!indio_dev)
return -ENOMEM;
}
info = iio_priv(indio_dev);
info->dev = dev;

View File

@ -229,7 +229,6 @@ static int imx8qxp_adc_read_raw(struct iio_dev *indio_dev,
ret = wait_for_completion_interruptible_timeout(&adc->completion,
IMX8QXP_ADC_TIMEOUT);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_sync_autosuspend(dev);
if (ret == 0) {
@ -295,7 +294,6 @@ static int imx8qxp_adc_reg_access(struct iio_dev *indio_dev, unsigned int reg,
*readval = readl(adc->regs + reg);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_sync_autosuspend(dev);
return 0;
@ -315,10 +313,8 @@ static int imx8qxp_adc_probe(struct platform_device *pdev)
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
if (!indio_dev) {
dev_err(dev, "Failed allocating iio device\n");
if (!indio_dev)
return -ENOMEM;
}
adc = iio_priv(indio_dev);
adc->dev = dev;

View File

@ -32,12 +32,13 @@
#define IMX93_ADC_PCDR0 0x100
#define IMX93_ADC_PCDR1 0x104
#define IMX93_ADC_PCDR2 0x108
#define IMX93_ADC_PCDR3 0x10c
#define IMX93_ADC_PCDR3 0x10C
#define IMX93_ADC_PCDR4 0x110
#define IMX93_ADC_PCDR5 0x114
#define IMX93_ADC_PCDR6 0x118
#define IMX93_ADC_PCDR7 0x11c
#define IMX93_ADC_PCDR7 0x11C
#define IMX93_ADC_CALSTAT 0x39C
#define IMX93_ADC_CALCFG0 0x3A0
/* ADC bit shift */
#define IMX93_ADC_MCR_MODE_MASK BIT(29)
@ -58,6 +59,8 @@
#define IMX93_ADC_IMR_ECH_MASK BIT(0)
#define IMX93_ADC_PCDR_CDATA_MASK GENMASK(11, 0)
#define IMX93_ADC_CALCFG0_LDFAIL_MASK BIT(4)
/* ADC status */
#define IMX93_ADC_MSR_ADCSTATUS_IDLE 0
#define IMX93_ADC_MSR_ADCSTATUS_POWER_DOWN 1
@ -145,7 +148,7 @@ static void imx93_adc_config_ad_clk(struct imx93_adc *adc)
static int imx93_adc_calibration(struct imx93_adc *adc)
{
u32 mcr, msr;
u32 mcr, msr, calcfg;
int ret;
/* make sure ADC in power down mode */
@ -158,6 +161,11 @@ static int imx93_adc_calibration(struct imx93_adc *adc)
imx93_adc_power_up(adc);
/* Enable loading of calibrated values even in fail condition */
calcfg = readl(adc->regs + IMX93_ADC_CALCFG0);
calcfg |= IMX93_ADC_CALCFG0_LDFAIL_MASK;
writel(calcfg, adc->regs + IMX93_ADC_CALCFG0);
/*
* TODO: we use the default TSAMP/NRSMPL/AVGEN in MCR,
* can add the setting of these bit if need in future.
@ -180,9 +188,13 @@ static int imx93_adc_calibration(struct imx93_adc *adc)
/* check whether calbration is success or not */
msr = readl(adc->regs + IMX93_ADC_MSR);
if (msr & IMX93_ADC_MSR_CALFAIL_MASK) {
/*
* Only give warning here, this means the noise of the
* reference voltage do not meet the requirement:
* ADC reference voltage Noise < 1.8V * 1/2^ENOB
* And the resault of ADC is not that accurate.
*/
dev_warn(adc->dev, "ADC calibration failed!\n");
imx93_adc_power_down(adc);
return -EAGAIN;
}
return 0;
@ -248,7 +260,6 @@ static int imx93_adc_read_raw(struct iio_dev *indio_dev,
mutex_lock(&adc->lock);
ret = imx93_adc_read_channel_conversion(adc, chan->channel, val);
mutex_unlock(&adc->lock);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_sync_autosuspend(dev);
if (ret < 0)
return ret;
@ -308,8 +319,7 @@ static int imx93_adc_probe(struct platform_device *pdev)
indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
if (!indio_dev)
return dev_err_probe(dev, -ENOMEM,
"Failed allocating iio device\n");
return -ENOMEM;
adc = iio_priv(indio_dev);
adc->dev = dev;

View File

@ -0,0 +1,328 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Intel Dollar Cove TI PMIC GPADC Driver
*
* Copyright (C) 2014 Intel Corporation (Ramakrishna Pallala <ramakrishna.pallala@intel.com>)
* Copyright (C) 2024 - 2025 Hans de Goede <hansg@kernel.org>
*/
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/mfd/intel_soc_pmic.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/wait.h>
#include <linux/iio/driver.h>
#include <linux/iio/iio.h>
#include <linux/iio/machine.h>
#define DC_TI_ADC_CNTL_REG 0x50
#define DC_TI_ADC_START BIT(0)
#define DC_TI_ADC_CH_SEL GENMASK(2, 1)
#define DC_TI_ADC_EN BIT(5)
#define DC_TI_ADC_EN_EXT_BPTH_BIAS BIT(6)
#define DC_TI_VBAT_ZSE_GE_REG 0x53
#define DC_TI_VBAT_GE GENMASK(3, 0)
#define DC_TI_VBAT_ZSE GENMASK(7, 4)
/* VBAT GE gain correction is in 0.0015 increments, ZSE is in 1.0 increments */
#define DC_TI_VBAT_GE_STEP 15
#define DC_TI_VBAT_GE_DIV 10000
#define DC_TI_ADC_DATA_REG_CH(x) (0x54 + 2 * (x))
enum dc_ti_adc_id {
DC_TI_ADC_VBAT,
DC_TI_ADC_PMICTEMP,
DC_TI_ADC_BATTEMP,
DC_TI_ADC_SYSTEMP0,
};
struct dc_ti_adc_info {
struct mutex lock; /* Protects against concurrent accesses to the ADC */
wait_queue_head_t wait;
struct device *dev;
struct regmap *regmap;
int vbat_zse;
int vbat_ge;
bool conversion_done;
};
static const struct iio_chan_spec dc_ti_adc_channels[] = {
{
.indexed = 1,
.type = IIO_VOLTAGE,
.channel = DC_TI_ADC_VBAT,
.address = DC_TI_ADC_DATA_REG_CH(0),
.datasheet_name = "CH0",
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_PROCESSED),
}, {
.indexed = 1,
.type = IIO_TEMP,
.channel = DC_TI_ADC_PMICTEMP,
.address = DC_TI_ADC_DATA_REG_CH(1),
.datasheet_name = "CH1",
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}, {
.indexed = 1,
.type = IIO_TEMP,
.channel = DC_TI_ADC_BATTEMP,
.address = DC_TI_ADC_DATA_REG_CH(2),
.datasheet_name = "CH2",
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}, {
.indexed = 1,
.type = IIO_TEMP,
.channel = DC_TI_ADC_SYSTEMP0,
.address = DC_TI_ADC_DATA_REG_CH(3),
.datasheet_name = "CH3",
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}
};
static struct iio_map dc_ti_adc_default_maps[] = {
IIO_MAP("CH0", "chtdc_ti_battery", "VBAT"),
IIO_MAP("CH1", "chtdc_ti_battery", "PMICTEMP"),
IIO_MAP("CH2", "chtdc_ti_battery", "BATTEMP"),
IIO_MAP("CH3", "chtdc_ti_battery", "SYSTEMP0"),
{ }
};
static irqreturn_t dc_ti_adc_isr(int irq, void *data)
{
struct dc_ti_adc_info *info = data;
info->conversion_done = true;
wake_up(&info->wait);
return IRQ_HANDLED;
}
static int dc_ti_adc_scale(struct dc_ti_adc_info *info,
struct iio_chan_spec const *chan,
int *val, int *val2)
{
if (chan->channel != DC_TI_ADC_VBAT)
return -EINVAL;
/* Vbat ADC scale is 4.6875 mV / unit */
*val = 4;
*val2 = 687500;
return IIO_VAL_INT_PLUS_MICRO;
}
static int dc_ti_adc_raw_to_processed(struct dc_ti_adc_info *info,
struct iio_chan_spec const *chan,
int raw, int *val, int *val2)
{
if (chan->channel != DC_TI_ADC_VBAT)
return -EINVAL;
/* Apply calibration */
raw -= info->vbat_zse;
raw = raw * (DC_TI_VBAT_GE_DIV - info->vbat_ge * DC_TI_VBAT_GE_STEP) /
DC_TI_VBAT_GE_DIV;
/* Vbat ADC scale is 4.6875 mV / unit */
raw *= 46875;
/* raw is now in 10000 units / mV, convert to milli + milli/1e6 */
*val = raw / 10000;
*val2 = (raw % 10000) * 100;
return IIO_VAL_INT_PLUS_MICRO;
}
static int dc_ti_adc_sample(struct dc_ti_adc_info *info,
struct iio_chan_spec const *chan, int *val)
{
int ret, ch = chan->channel;
__be16 buf;
info->conversion_done = false;
/*
* As per TI (PMIC Vendor), the ADC enable and ADC start commands should
* not be sent together. Hence send the commands separately.
*/
ret = regmap_set_bits(info->regmap, DC_TI_ADC_CNTL_REG, DC_TI_ADC_EN);
if (ret)
return ret;
ret = regmap_update_bits(info->regmap, DC_TI_ADC_CNTL_REG,
DC_TI_ADC_CH_SEL,
FIELD_PREP(DC_TI_ADC_CH_SEL, ch));
if (ret)
return ret;
/*
* As per PMIC Vendor, a minimum of 50 ųs delay is required between ADC
* Enable and ADC START commands. This is also recommended by Intel
* Hardware team after the timing analysis of GPADC signals. Since the
* I2C Write transaction to set the channel number also imparts 25 ųs
* delay, we need to wait for another 25 ųs before issuing ADC START.
*/
fsleep(25);
ret = regmap_set_bits(info->regmap, DC_TI_ADC_CNTL_REG,
DC_TI_ADC_START);
if (ret)
return ret;
/* TI (PMIC Vendor) recommends 5 s timeout for conversion */
ret = wait_event_timeout(info->wait, info->conversion_done, 5 * HZ);
if (ret == 0) {
ret = -ETIMEDOUT;
goto disable_adc;
}
ret = regmap_bulk_read(info->regmap, chan->address, &buf, sizeof(buf));
if (ret)
goto disable_adc;
/* The ADC values are 10 bits wide */
*val = be16_to_cpu(buf) & GENMASK(9, 0);
disable_adc:
regmap_clear_bits(info->regmap, DC_TI_ADC_CNTL_REG,
DC_TI_ADC_START | DC_TI_ADC_EN);
return ret;
}
static int dc_ti_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct dc_ti_adc_info *info = iio_priv(indio_dev);
int ret;
if (mask == IIO_CHAN_INFO_SCALE)
return dc_ti_adc_scale(info, chan, val, val2);
guard(mutex)(&info->lock);
/*
* If channel BPTHERM has been selected, first enable the BPTHERM BIAS
* which provides the VREF Voltage reference to convert BPTHERM Input
* voltage to temperature.
*/
if (chan->channel == DC_TI_ADC_BATTEMP) {
ret = regmap_set_bits(info->regmap, DC_TI_ADC_CNTL_REG,
DC_TI_ADC_EN_EXT_BPTH_BIAS);
if (ret)
return ret;
/*
* As per PMIC Vendor specifications, BPTHERM BIAS should be
* enabled 35 ms before ADC_EN command.
*/
msleep(35);
}
ret = dc_ti_adc_sample(info, chan, val);
if (chan->channel == DC_TI_ADC_BATTEMP)
regmap_clear_bits(info->regmap, DC_TI_ADC_CNTL_REG,
DC_TI_ADC_EN_EXT_BPTH_BIAS);
if (ret)
return ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
return IIO_VAL_INT;
case IIO_CHAN_INFO_PROCESSED:
return dc_ti_adc_raw_to_processed(info, chan, *val, val, val2);
}
return -EINVAL;
}
static const struct iio_info dc_ti_adc_iio_info = {
.read_raw = dc_ti_adc_read_raw,
};
static int dc_ti_adc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct intel_soc_pmic *pmic = dev_get_drvdata(dev->parent);
struct dc_ti_adc_info *info;
struct iio_dev *indio_dev;
unsigned int val;
int irq, ret;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
if (!indio_dev)
return -ENOMEM;
info = iio_priv(indio_dev);
ret = devm_mutex_init(dev, &info->lock);
if (ret)
return ret;
init_waitqueue_head(&info->wait);
info->dev = dev;
info->regmap = pmic->regmap;
indio_dev->name = "dc_ti_adc";
indio_dev->channels = dc_ti_adc_channels;
indio_dev->num_channels = ARRAY_SIZE(dc_ti_adc_channels);
indio_dev->info = &dc_ti_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = regmap_read(info->regmap, DC_TI_VBAT_ZSE_GE_REG, &val);
if (ret)
return ret;
info->vbat_zse = sign_extend32(FIELD_GET(DC_TI_VBAT_ZSE, val), 3);
info->vbat_ge = sign_extend32(FIELD_GET(DC_TI_VBAT_GE, val), 3);
dev_dbg(dev, "vbat-zse %d vbat-ge %d\n", info->vbat_zse, info->vbat_ge);
ret = devm_iio_map_array_register(dev, indio_dev, dc_ti_adc_default_maps);
if (ret)
return ret;
ret = devm_request_threaded_irq(dev, irq, NULL, dc_ti_adc_isr,
IRQF_ONESHOT, indio_dev->name, info);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct platform_device_id dc_ti_adc_ids[] = {
{ .name = "chtdc_ti_adc" },
{ }
};
MODULE_DEVICE_TABLE(platform, dc_ti_adc_ids);
static struct platform_driver dc_ti_adc_driver = {
.driver = {
.name = "dc_ti_adc",
},
.probe = dc_ti_adc_probe,
.id_table = dc_ti_adc_ids,
};
module_platform_driver(dc_ti_adc_driver);
MODULE_AUTHOR("Ramakrishna Pallala (Intel)");
MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
MODULE_DESCRIPTION("Intel Dollar Cove (TI) GPADC Driver");
MODULE_LICENSE("GPL");

View File

@ -1019,7 +1019,7 @@ static int mcp3564_parse_fw_children(struct iio_dev *indio_dev)
channels = devm_kcalloc(dev, num_ch, sizeof(*channels), GFP_KERNEL);
if (!channels)
return dev_err_probe(dev, -ENOMEM, "Can't allocate memory\n");
return -ENOMEM;
device_for_each_child_node_scoped(dev, child) {
node_name = fwnode_get_name(child);

View File

@ -1357,7 +1357,7 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
if (!indio_dev)
return dev_err_probe(dev, -ENOMEM, "failed allocating iio device\n");
return -ENOMEM;
priv = iio_priv(indio_dev);
init_completion(&priv->done);

View File

@ -297,8 +297,7 @@ static int mt6577_auxadc_probe(struct platform_device *pdev)
ret = devm_add_action_or_reset(&pdev->dev, mt6577_power_off, adc_dev);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"Failed to add action to managed power off\n");
return ret;
ret = devm_iio_device_register(&pdev->dev, indio_dev);
if (ret < 0)

View File

@ -697,10 +697,8 @@ static int mxs_lradc_adc_probe(struct platform_device *pdev)
/* Allocate the IIO device. */
iio = devm_iio_device_alloc(dev, sizeof(*adc));
if (!iio) {
dev_err(dev, "Failed to allocate IIO device\n");
if (!iio)
return -ENOMEM;
}
adc = iio_priv(iio);
adc->lradc = lradc;

View File

@ -1279,8 +1279,7 @@ static int pac1921_probe(struct i2c_client *client)
ret = devm_add_action_or_reset(dev, pac1921_regulator_disable,
priv->vdd);
if (ret)
return dev_err_probe(dev, ret,
"Cannot add action for vdd regulator disposal\n");
return ret;
msleep(PAC1921_POWERUP_TIME_MS);

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