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
commit
fc3e44e492
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -107,7 +107,6 @@ examples:
|
|||
};
|
||||
};
|
||||
- |
|
||||
# include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ additionalProperties: false
|
|||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ examples:
|
|||
};
|
||||
};
|
||||
- |
|
||||
# include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
|
|
|||
|
|
@ -93,7 +93,6 @@ unevaluatedProperties: false
|
|||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,19 +130,18 @@ allOf:
|
|||
- vcc-supply
|
||||
# Devices with a vref
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad7091r
|
||||
- adi,ad7273
|
||||
- adi,ad7274
|
||||
- adi,ad7475
|
||||
- lltc,ltc2314-14
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad7091r
|
||||
- adi,ad7273
|
||||
- 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,35 +154,58 @@ allOf:
|
|||
- adi,ad7274
|
||||
- adi,ad7475
|
||||
- lltc,ltc2314-14
|
||||
- rohm,bd79105
|
||||
then:
|
||||
required:
|
||||
- vref-supply
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad7475
|
||||
- adi,ad7495
|
||||
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: false
|
||||
|
||||
# Devices with a convstart GPIO where it is not optional
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad7091
|
||||
- adi,ad7091r
|
||||
- rohm,bd79105
|
||||
then:
|
||||
properties:
|
||||
adi,conversion-start-gpios: true
|
||||
else:
|
||||
properties:
|
||||
adi,conversion-start-gpios: false
|
||||
required:
|
||||
- adi,conversion-start-gpios
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
- interrupts
|
||||
|
||||
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>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ properties:
|
|||
- const: saradc
|
||||
- const: apb_pclk
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ unevaluatedProperties: false
|
|||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ unevaluatedProperties: false
|
|||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ examples:
|
|||
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
output-range-microvolt= <0 10000000>;
|
||||
output-range-microvolt = <0 10000000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ unevaluatedProperties: false
|
|||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,6 @@ examples:
|
|||
compatible = "dynaimage,al3010";
|
||||
reg = <0x1c>;
|
||||
vdd-supply = <&vdd_reg>;
|
||||
interrupts = <0 99 4>;
|
||||
interrupts = <99 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -40,6 +40,6 @@ examples:
|
|||
compatible = "dynaimage,al3320a";
|
||||
reg = <0x1c>;
|
||||
vdd-supply = <&vdd_reg>;
|
||||
interrupts = <0 99 4>;
|
||||
interrupts = <99 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ required:
|
|||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
@ -44,7 +44,6 @@ additionalProperties: false
|
|||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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 application’s 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.5 g/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.5 Hz 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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -28,11 +28,13 @@ Industrial I/O Kernel Drivers
|
|||
ad7606
|
||||
ad7625
|
||||
ad7944
|
||||
ade9000
|
||||
adis16475
|
||||
adis16480
|
||||
adis16550
|
||||
adxl313
|
||||
adxl380
|
||||
adxl345
|
||||
bno055
|
||||
ep93xx_adc
|
||||
opt4060
|
||||
|
|
|
|||
34
MAINTAINERS
34
MAINTAINERS
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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, ®val);
|
||||
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, ®val);
|
||||
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, ®val);
|
||||
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, ®val);
|
||||
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, ®val);
|
||||
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, ®val);
|
||||
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, ®val);
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
unsigned int channel)
|
||||
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 */
|
||||
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);
|
||||
/*
|
||||
* 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), 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");
|
||||
}
|
||||
|
||||
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 set mclk rate\n");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the power mode */
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
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,
|
||||
AD7173_FILTER_ODR0_MASK & cfg->odr);
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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] = {
|
||||
.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),
|
||||
.reset = ad7091_reset,
|
||||
},
|
||||
[ID_AD7091R] = {
|
||||
.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,
|
||||
.has_vref = true,
|
||||
.reset = ad7091_reset,
|
||||
},
|
||||
[ID_AD7273] = {
|
||||
.channel[0] = AD7940_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.has_vref = true,
|
||||
},
|
||||
[ID_AD7274] = {
|
||||
.channel[0] = AD7940_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.has_vref = true,
|
||||
},
|
||||
[ID_AD7276] = {
|
||||
.channel[0] = AD7940_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7277] = {
|
||||
.channel[0] = AD7940_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7278] = {
|
||||
.channel[0] = AD7940_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7466] = {
|
||||
.channel[0] = AD7476_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7467] = {
|
||||
.channel[0] = AD7476_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7468] = {
|
||||
.channel[0] = AD7476_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7475] = {
|
||||
.channel[0] = AD7476_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.has_vref = true,
|
||||
.has_vdrive = true,
|
||||
},
|
||||
[ID_AD7495] = {
|
||||
.channel[0] = AD7476_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.int_vref_uv = 2500000,
|
||||
.has_vdrive = true,
|
||||
},
|
||||
[ID_AD7940] = {
|
||||
.channel[0] = AD7940_CHAN(14),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_ADC081S] = {
|
||||
.channel[0] = ADC081S_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_ADC101S] = {
|
||||
.channel[0] = ADC081S_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_ADC121S] = {
|
||||
.channel[0] = ADC081S_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_ADS7866] = {
|
||||
.channel[0] = ADS786X_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_ADS7867] = {
|
||||
.channel[0] = ADS786X_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_ADS7868] = {
|
||||
.channel[0] = ADS786X_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_LTC2314_14] = {
|
||||
.channel[0] = AD7940_CHAN(14),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.has_vref = true,
|
||||
},
|
||||
static const struct ad7476_chip_info ad7091_chip_info = {
|
||||
.channel[0] = AD7091R_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.conversion_pre_op = ad7091_convst,
|
||||
.reset = ad7091_reset,
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info ad7091r_chip_info = {
|
||||
.channel[0] = AD7091R_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.conversion_pre_op = ad7091_convst,
|
||||
.int_vref_mv = 2500,
|
||||
.has_vref = true,
|
||||
.reset = ad7091_reset,
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info ad7273_chip_info = {
|
||||
.channel[0] = AD7940_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.has_vref = true,
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info ad7274_chip_info = {
|
||||
.channel[0] = AD7940_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.has_vref = true,
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info ad7276_chip_info = {
|
||||
.channel[0] = AD7940_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info ad7277_chip_info = {
|
||||
.channel[0] = AD7940_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info ad7278_chip_info = {
|
||||
.channel[0] = AD7940_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info ad7466_chip_info = {
|
||||
.channel[0] = AD7476_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info ad7467_chip_info = {
|
||||
.channel[0] = AD7476_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info ad7468_chip_info = {
|
||||
.channel[0] = AD7476_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info ad7495_chip_info = {
|
||||
.channel[0] = AD7476_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.int_vref_mv = 2500,
|
||||
.has_vdrive = true,
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info ad7940_chip_info = {
|
||||
.channel[0] = AD7940_CHAN(14),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info adc081s_chip_info = {
|
||||
.channel[0] = ADC081S_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info adc101s_chip_info = {
|
||||
.channel[0] = ADC081S_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info adc121s_chip_info = {
|
||||
.channel[0] = ADC081S_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info ads7866_chip_info = {
|
||||
.channel[0] = ADS786X_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info ads7867_chip_info = {
|
||||
.channel[0] = ADS786X_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct ad7476_chip_info ads7868_chip_info = {
|
||||
.channel[0] = ADS786X_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
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)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable, reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Either vcc or vref (below) as appropriate */
|
||||
if (!st->chip_info->int_vref_uv)
|
||||
st->ref_reg = reg;
|
||||
/* 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;
|
||||
st->scale_mv = ret / 1000;
|
||||
} else {
|
||||
ret = devm_regulator_get_enable(&spi->dev, "vcc");
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
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)<c2314_14_chip_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7476_id);
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
st->vref = devm_regulator_get_optional(&client->dev, "vref");
|
||||
|
||||
if (IS_ERR(st->vref)) {
|
||||
if (PTR_ERR(st->vref) == -ENODEV) {
|
||||
if (chip_info->has_vref) {
|
||||
st->vref = devm_regulator_get_optional(&client->dev, "vref");
|
||||
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)) {
|
||||
if (st->vref) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue