Merge tag 'iio-for-6.11a' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/jic23/iio into char-misc-testing
Jonathan writes: IIO: 1st set of new device support, cleanups etc for 6.11 Lots of new device support and 3 entirely new drivers. Early pull request this cycle to allow for clean picking up of fixes that are dependencies for some queued patch sets. Device support ============== adi,ad3552r - Add AD3541R and AD3551R - single output variants of already supported DACs. adi,ad7192 - Add support for ad7194 24-bit ADC with integrated PGA. adi,ad7380 - New ADC driver built up in a number of steps. Supports - 2 channel differential ADCs: AD7380, AD7381 - 4 channel differential ADCs: AD7380-4, AD7381-4 - 2 channel pseudo-differential ADCs: AD7383, AD7384 - 4 channel pseudo-differential ADCs: AD7383-4, AD7384-4 adi,adis16475 - Support ADS16501 variant - ID and some different scale factors from parts already supported. - Driver refactoring then enables support for 6 more IMUs: - ADIS16575-[2,3] - ADIS16576-[2,3] - ADIS16577-[2,3] adi,adsi16480 - Driver refactoring and feature additions leading to support for 6 more IMUs - with new delta angle and delta velocity feature: - ADIS16545-[1,2,3] - ADIS16547-[1,2,3] bosch,bmi160 - Support for the bmi120 IMU: ID only. Also relax ID checking to warn only on mismatch allowing use of fallback compatibles for new devices. sciosense,ens160 - New driver for this metal oxide multi-gas sensor for indoor air quality monitoring. sensortek,stk3110 - Support for stk3311a and stk3311s34 light sensor variants. Relax ID checking to warn only on a mismatch allowing use of fallback compatibles for new devices. vishay,veml6040 - New driver for this RGBW light sensor. Note that whilst the register interface is very different, the dt-binding similar enough that it is shared with the existing vishay,veml6075 binding x-powers,axp20x - Add support for axp192, very similar to another supported PMIC ADC variant but with a few more GPIO channels. Dt-binding only =============== ti,ads1015 - Add binding (no driver support yet) for ti,tla2021 New features ============ core - Variable scan type support. We have papered over this for a long time so good to finally resolve it. Some devices will change their data output format (typically resolution) dependent on settings such as oversampling. A new callback is added to enable this. First used in the ad7380 driver. - Harden the core against missing callback functions. dt-binding: - Add a single-channel property that can be used in per channel nodes instead of reg to indicate which device channel. This is important in devices with a mixture of differential and single ended channels as reg already just acted as an index for the differential channels making things inconsistent if it had more meaning for single ended channels. adi,ad7380 - Use spi_optimize_message() to reduce reading message setup overhead. - Add oversampling support using the new core functionality to allow a device support multiple scan types. invense,icm42600 - Support for low-power accelerometer modes. When a given sampling frequency is only supported at one power mode, use that. Otherwise default to low power at the cost of some noise unless overridden via a new sysfs attribute. silicon-labs,si70720 - Add control of the heater. Cleanups and minor fixes ======================== core - Cleanup use of sizeof(struct xxxx) in favor of sizeof(*variable) Makefile - Resort the iio/adc/Makefile which has drifted away from alphabetical order. gts library - Fix sorting of lists with a zero in the middle. Doesn't happen with upstream drivers, but good to harden this code. Add a related unit test. multiple drivers - Add missing MODULE_DESCRIPTION() - Drop some unused structure fields. - Drop some entirely unused structure definitions. - Stop pointless initialization of i2c_device_id::driver_data to 0 in drivers where it isn't used. - Use spi_get_device_match_data() to replace open-coded equivalent. adi,ad3552r - Fix dt gain parameter names to reflect what the driver does. Note discussion in patch to justify fixing it in the binding not the driver. - Tidy up some naming. adi,ad7192 - Use read_avail() callback to handle the low pass filter. - Add an aincom supply for pseudo differential operation. adi,ad7606 - Use iio_device_claim_direct_scoped() to simplify error paths. adi,ad7944 - Drop an unused function parameter. adi,adrf6780 - Drop unused header. adi,ad9467 - Use a DMA safe buffer for SPI transfers. - Stop using tabs to pad structure field names. It was creating a lot of noise. adi,axi-adc - Prevent races between enable and disable calls. - Ensure the DRP (dynamic reconfiguration port) is locked. Not used in most real designs, but better safe than sorry. - Limit build to COMPILE_TEST or platforms for which the IP exists. adi,axi-dac - Limit build to COMPILE_TEST or platforms for which the IP exists. ams,iaq - Use __packed instead of ___attribute__((__packed__)) bosch,bmp280 - White space cleanup. - Use BME280 prefix for registers that do not exist on the BMP280. - Add parameter names to callback function definitions. - Rename measure function to better reflect what it does which is wait for a measurement to happen. - Drop a redundant error check. - Improve error messages - Make error checks consistent as if (ret) - Use unsigned types for inherently unsigned data. - Refactor reading functions to not rely on a hidden t_fine variable. - Make use of cleanup.h freescale,mma7660 - Add mount matrix support. invense,icm42600 - Enable the regmap cache to reduce bus accesses. amlogic,meson-saradc - Add dt-binding support for power-domains. ti,adc161s626 - Use iio_device_claim_direct_scoped() to simplify error handling. * tag 'iio-for-6.11a' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (107 commits) iio: imu: inv_icm42600: add support of accel low-power mode iio: document inv_icm42600 driver private sysfs attributes MAINTAINERS: Add ScioSense ENS160 iio: chemical: ens160: add power management support iio: chemical: ens160: add triggered buffer support iio: chemical: add driver for ENS160 sensor dt-bindings: iio: chemical: add ENS160 sensor dt-bindings: vendor-prefixes: add ScioSense iio: temperature: mcp9600: add threshold events support dt-bindings: iio: light: add VEML6040 RGBW-LS iio: light: driver for Vishay VEML6040 dt-bindings: iio: adc: amlogic,meson-saradc: add optional power-domains iio: dac: adi-axi-dac: add platform dependencies iio: adc: adi-axi-adc: add platform dependencies iio: imu: inv_icm42600: add register caching in the regmap iio: adc: mcp3564: drop redundant open-coded spi_get_device_match_data() iio: dac: max5522: simplify with spi_get_device_match_data() iio: addac: ad74413r: simplify with spi_get_device_match_data() iio: adc: ti-tsc2046: simplify with spi_get_device_match_data() iio: adc: ti-ads131e08: simplify with spi_get_device_match_data() ...
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_accel_power_mode
|
||||
KernelVersion: 6.11
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Accelerometer power mode. Setting this attribute will set the
|
||||
requested power mode to use if the ODR support it. If ODR
|
||||
support only 1 mode, power mode will be enforced.
|
||||
Reading this attribute will return the current accelerometer
|
||||
power mode if the sensor is on, or the requested value if the
|
||||
sensor is off. The value between real and requested value can
|
||||
be different for ODR supporting only 1 mode.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_accel_power_mode_available
|
||||
KernelVersion: 6.11
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
List of available accelerometer power modes that can be set in
|
||||
in_accel_power_mode attribute.
|
||||
@@ -38,6 +38,14 @@ properties:
|
||||
The first value specifies the positive input pin, the second
|
||||
specifies the negative input pin.
|
||||
|
||||
single-channel:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
When devices combine single-ended and differential channels, allow the
|
||||
channel for a single element to be specified, independent of reg (as for
|
||||
differential channels). If this and diff-channels are not present reg
|
||||
shall be used instead.
|
||||
|
||||
settling-time-us:
|
||||
description:
|
||||
Time between enabling the channel and first stable readings.
|
||||
@@ -50,4 +58,15 @@ properties:
|
||||
device design and can interact with other characteristics such as
|
||||
settling time.
|
||||
|
||||
anyOf:
|
||||
- oneOf:
|
||||
- required:
|
||||
- reg
|
||||
- diff-channels
|
||||
- required:
|
||||
- reg
|
||||
- single-channel
|
||||
- required:
|
||||
- reg
|
||||
|
||||
additionalProperties: true
|
||||
|
||||
@@ -21,8 +21,15 @@ properties:
|
||||
- adi,ad7190
|
||||
- adi,ad7192
|
||||
- adi,ad7193
|
||||
- adi,ad7194
|
||||
- adi,ad7195
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
@@ -41,6 +48,11 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
aincom-supply:
|
||||
description: |
|
||||
AINCOM voltage supply. Analog inputs AINx are referenced to this input
|
||||
when configured for pseudo-differential operation.
|
||||
|
||||
dvdd-supply:
|
||||
description: DVdd voltage supply
|
||||
|
||||
@@ -84,6 +96,42 @@ properties:
|
||||
description: see Documentation/devicetree/bindings/iio/adc/adc.yaml
|
||||
type: boolean
|
||||
|
||||
patternProperties:
|
||||
"^channel@[0-9a-f]+$":
|
||||
type: object
|
||||
$ref: adc.yaml
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description: The channel index.
|
||||
minimum: 0
|
||||
maximum: 271
|
||||
|
||||
diff-channels:
|
||||
description:
|
||||
Both inputs can be connected to pins AIN1 to AIN16 by choosing the
|
||||
appropriate value from 1 to 16.
|
||||
items:
|
||||
minimum: 1
|
||||
maximum: 16
|
||||
|
||||
single-channel:
|
||||
description:
|
||||
Positive input can be connected to pins AIN1 to AIN16 by choosing the
|
||||
appropriate value from 1 to 16. Negative input is connected to AINCOM.
|
||||
items:
|
||||
minimum: 1
|
||||
maximum: 16
|
||||
|
||||
oneOf:
|
||||
- required:
|
||||
- reg
|
||||
- diff-channels
|
||||
- required:
|
||||
- reg
|
||||
- single-channel
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@@ -98,6 +146,17 @@ required:
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7190
|
||||
- adi,ad7192
|
||||
- adi,ad7193
|
||||
- adi,ad7195
|
||||
then:
|
||||
patternProperties:
|
||||
"^channel@[0-9a-f]+$": false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
@@ -117,6 +176,7 @@ examples:
|
||||
clock-names = "mclk";
|
||||
interrupts = <25 0x2>;
|
||||
interrupt-parent = <&gpio>;
|
||||
aincom-supply = <&aincom>;
|
||||
dvdd-supply = <&dvdd>;
|
||||
avdd-supply = <&avdd>;
|
||||
vref-supply = <&vref>;
|
||||
@@ -127,3 +187,38 @@ examples:
|
||||
adi,burnout-currents-enable;
|
||||
};
|
||||
};
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@0 {
|
||||
compatible = "adi,ad7194";
|
||||
reg = <0>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
spi-max-frequency = <1000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
clocks = <&ad7192_mclk>;
|
||||
clock-names = "mclk";
|
||||
interrupts = <25 0x2>;
|
||||
interrupt-parent = <&gpio>;
|
||||
aincom-supply = <&aincom>;
|
||||
dvdd-supply = <&dvdd>;
|
||||
avdd-supply = <&avdd>;
|
||||
vref-supply = <&vref>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
diff-channels = <1 6>;
|
||||
};
|
||||
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
single-channel = <1>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/adi,ad7380.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices Simultaneous Sampling Analog to Digital Converters
|
||||
|
||||
maintainers:
|
||||
- Michael Hennerich <Michael.Hennerich@analog.com>
|
||||
- Nuno Sá <nuno.sa@analog.com>
|
||||
|
||||
description: |
|
||||
* https://www.analog.com/en/products/ad7380.html
|
||||
* https://www.analog.com/en/products/ad7381.html
|
||||
* https://www.analog.com/en/products/ad7383.html
|
||||
* https://www.analog.com/en/products/ad7384.html
|
||||
* https://www.analog.com/en/products/ad7380-4.html
|
||||
* https://www.analog.com/en/products/ad7381-4.html
|
||||
* https://www.analog.com/en/products/ad7383-4.html
|
||||
* https://www.analog.com/en/products/ad7384-4.html
|
||||
|
||||
$ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7380
|
||||
- adi,ad7381
|
||||
- adi,ad7383
|
||||
- adi,ad7384
|
||||
- adi,ad7380-4
|
||||
- adi,ad7381-4
|
||||
- adi,ad7383-4
|
||||
- adi,ad7384-4
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 80000000
|
||||
spi-cpol: true
|
||||
spi-cpha: true
|
||||
|
||||
vcc-supply:
|
||||
description: A 3V to 3.6V supply that powers the chip.
|
||||
|
||||
vlogic-supply:
|
||||
description:
|
||||
A 1.65V to 3.6V supply for the logic pins.
|
||||
|
||||
refio-supply:
|
||||
description:
|
||||
A 2.5V to 3.3V supply for the external reference voltage. When omitted,
|
||||
the internal 2.5V reference is used.
|
||||
|
||||
aina-supply:
|
||||
description:
|
||||
The common mode voltage supply for the AINA- pin on pseudo-differential
|
||||
chips.
|
||||
|
||||
ainb-supply:
|
||||
description:
|
||||
The common mode voltage supply for the AINB- pin on pseudo-differential
|
||||
chips.
|
||||
|
||||
ainc-supply:
|
||||
description:
|
||||
The common mode voltage supply for the AINC- pin on pseudo-differential
|
||||
chips.
|
||||
|
||||
aind-supply:
|
||||
description:
|
||||
The common mode voltage supply for the AIND- pin on pseudo-differential
|
||||
chips.
|
||||
|
||||
interrupts:
|
||||
description:
|
||||
When the device is using 1-wire mode, this property is used to optionally
|
||||
specify the ALERT interrupt.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vcc-supply
|
||||
- vlogic-supply
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
allOf:
|
||||
# pseudo-differential chips require common mode voltage supplies,
|
||||
# true differential chips don't use them
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7383
|
||||
- adi,ad7384
|
||||
- adi,ad7383-4
|
||||
- adi,ad7384-4
|
||||
then:
|
||||
required:
|
||||
- aina-supply
|
||||
- ainb-supply
|
||||
else:
|
||||
properties:
|
||||
aina-supply: false
|
||||
ainb-supply: false
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7383-4
|
||||
- adi,ad7384-4
|
||||
then:
|
||||
required:
|
||||
- ainc-supply
|
||||
- aind-supply
|
||||
else:
|
||||
properties:
|
||||
ainc-supply: false
|
||||
aind-supply: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@0 {
|
||||
compatible = "adi,ad7380";
|
||||
reg = <0>;
|
||||
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
spi-max-frequency = <80000000>;
|
||||
|
||||
interrupts = <27 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
|
||||
vcc-supply = <&supply_3_3V>;
|
||||
vlogic-supply = <&supply_3_3V>;
|
||||
refio-supply = <&supply_2_5V>;
|
||||
};
|
||||
};
|
||||
@@ -66,6 +66,9 @@ properties:
|
||||
nvmem-cell-names:
|
||||
const: temperature_calib
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
|
||||
@@ -18,6 +18,7 @@ properties:
|
||||
enum:
|
||||
- ti,ads1015
|
||||
- ti,ads1115
|
||||
- ti,tla2021
|
||||
- ti,tla2024
|
||||
|
||||
reg:
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/chemical/sciosense,ens160.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ScioSense ENS160 multi-gas sensor
|
||||
|
||||
maintainers:
|
||||
- Gustavo Silva <gustavograzs@gmail.com>
|
||||
|
||||
description: |
|
||||
Digital Multi-Gas Sensor for Monitoring Indoor Air Quality.
|
||||
|
||||
Datasheet:
|
||||
https://www.sciosense.com/wp-content/uploads/2023/12/ENS160-Datasheet.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- sciosense,ens160
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
vddio-supply: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
gas-sensor@52 {
|
||||
compatible = "sciosense,ens160";
|
||||
reg = <0x52>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <19 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
||||
};
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
gas-sensor@0 {
|
||||
compatible = "sciosense,ens160";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <10000000>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <19 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
@@ -13,13 +13,17 @@ maintainers:
|
||||
description: |
|
||||
Bindings for the Analog Devices AD3552R DAC device and similar.
|
||||
Datasheet can be found here:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad3541r.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad3542r.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad3551r.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad3552r.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad3541r
|
||||
- adi,ad3542r
|
||||
- adi,ad3551r
|
||||
- adi,ad3552r
|
||||
|
||||
reg:
|
||||
@@ -92,13 +96,13 @@ patternProperties:
|
||||
maximum: 511
|
||||
minimum: -511
|
||||
|
||||
adi,gain-scaling-p-inv-log2:
|
||||
description: GainP = 1 / ( 2 ^ adi,gain-scaling-p-inv-log2)
|
||||
adi,gain-scaling-p:
|
||||
description: GainP = 1 / ( 2 ^ adi,gain-scaling-p)
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [0, 1, 2, 3]
|
||||
|
||||
adi,gain-scaling-n-inv-log2:
|
||||
description: GainN = 1 / ( 2 ^ adi,gain-scaling-n-inv-log2)
|
||||
adi,gain-scaling-n:
|
||||
description: GainN = 1 / ( 2 ^ adi,gain-scaling-n)
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [0, 1, 2, 3]
|
||||
|
||||
@@ -107,8 +111,8 @@ patternProperties:
|
||||
|
||||
required:
|
||||
- adi,gain-offset
|
||||
- adi,gain-scaling-p-inv-log2
|
||||
- adi,gain-scaling-n-inv-log2
|
||||
- adi,gain-scaling-p
|
||||
- adi,gain-scaling-n
|
||||
- adi,rfb-ohms
|
||||
|
||||
required:
|
||||
@@ -128,7 +132,9 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: adi,ad3542r
|
||||
enum:
|
||||
- adi,ad3541r
|
||||
- adi,ad3542r
|
||||
then:
|
||||
patternProperties:
|
||||
"^channel@([0-1])$":
|
||||
@@ -158,7 +164,9 @@ allOf:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: adi,ad3552r
|
||||
enum:
|
||||
- adi,ad3551r
|
||||
- adi,ad3552r
|
||||
then:
|
||||
patternProperties:
|
||||
"^channel@([0-1])$":
|
||||
@@ -182,6 +190,21 @@ allOf:
|
||||
- const: -10000000
|
||||
- const: 10000000
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad3541r
|
||||
- adi,ad3551r
|
||||
then:
|
||||
properties:
|
||||
channel@1: false
|
||||
channel@0:
|
||||
properties:
|
||||
reg:
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@@ -208,8 +231,8 @@ examples:
|
||||
reg = <1>;
|
||||
custom-output-range-config {
|
||||
adi,gain-offset = <5>;
|
||||
adi,gain-scaling-p-inv-log2 = <1>;
|
||||
adi,gain-scaling-n-inv-log2 = <2>;
|
||||
adi,gain-scaling-p = <1>;
|
||||
adi,gain-scaling-n = <2>;
|
||||
adi,rfb-ohms = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -30,12 +30,19 @@ properties:
|
||||
- adi,adis16467-2
|
||||
- adi,adis16467-3
|
||||
- adi,adis16500
|
||||
- adi,adis16501
|
||||
- adi,adis16505-1
|
||||
- adi,adis16505-2
|
||||
- adi,adis16505-3
|
||||
- adi,adis16507-1
|
||||
- adi,adis16507-2
|
||||
- adi,adis16507-3
|
||||
- adi,adis16575-2
|
||||
- adi,adis16575-3
|
||||
- adi,adis16576-2
|
||||
- adi,adis16576-3
|
||||
- adi,adis16577-2
|
||||
- adi,adis16577-3
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@@ -90,12 +97,19 @@ allOf:
|
||||
contains:
|
||||
enum:
|
||||
- adi,adis16500
|
||||
- adi,adis16501
|
||||
- adi,adis16505-1
|
||||
- adi,adis16505-2
|
||||
- adi,adis16505-3
|
||||
- adi,adis16507-1
|
||||
- adi,adis16507-2
|
||||
- adi,adis16507-3
|
||||
- adi,adis16575-2
|
||||
- adi,adis16575-3
|
||||
- adi,adis16576-2
|
||||
- adi,adis16576-3
|
||||
- adi,adis16577-2
|
||||
- adi,adis16577-3
|
||||
|
||||
then:
|
||||
properties:
|
||||
@@ -112,6 +126,23 @@ allOf:
|
||||
dependencies:
|
||||
adi,sync-mode: [ clocks ]
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,adis16575-2
|
||||
- adi,adis16575-3
|
||||
- adi,adis16576-2
|
||||
- adi,adis16576-3
|
||||
- adi,adis16577-2
|
||||
- adi,adis16577-3
|
||||
|
||||
then:
|
||||
properties:
|
||||
spi-max-frequency:
|
||||
maximum: 15000000
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
||||
@@ -23,6 +23,12 @@ properties:
|
||||
- adi,adis16497-1
|
||||
- adi,adis16497-2
|
||||
- adi,adis16497-3
|
||||
- adi,adis16545-1
|
||||
- adi,adis16545-2
|
||||
- adi,adis16545-3
|
||||
- adi,adis16547-1
|
||||
- adi,adis16547-2
|
||||
- adi,adis16547-3
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
@@ -16,7 +16,11 @@ description: |
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: bosch,bmi160
|
||||
oneOf:
|
||||
- const: bosch,bmi160
|
||||
- items:
|
||||
- const: bosch,bmi120
|
||||
- const: bosch,bmi160
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
@@ -4,14 +4,19 @@
|
||||
$id: http://devicetree.org/schemas/iio/light/vishay,veml6075.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Vishay VEML6075 UVA and UVB sensor
|
||||
title: Vishay VEML6075 UVA/B and VEML6040 RGBW sensors
|
||||
|
||||
maintainers:
|
||||
- Javier Carrasco <javier.carrasco.cruz@gmail.com>
|
||||
|
||||
description:
|
||||
VEML6040 datasheet at https://www.vishay.com/docs/84276/veml6040.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: vishay,veml6075
|
||||
enum:
|
||||
- vishay,veml6040
|
||||
- vishay,veml6075
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
@@ -1254,6 +1254,8 @@ patternProperties:
|
||||
description: Smart Battery System
|
||||
"^schindler,.*":
|
||||
description: Schindler
|
||||
"^sciosense,.*":
|
||||
description: ScioSense B.V.
|
||||
"^seagate,.*":
|
||||
description: Seagate Technology PLC
|
||||
"^seeed,.*":
|
||||
|
||||
@@ -380,24 +380,5 @@ data is structured.
|
||||
4. IIO Interfacing Tools
|
||||
========================
|
||||
|
||||
Linux Kernel Tools
|
||||
------------------
|
||||
|
||||
Linux Kernel provides some userspace tools that can be used to retrieve data
|
||||
from IIO sysfs:
|
||||
|
||||
* lsiio: example application that provides a list of IIO devices and triggers
|
||||
* iio_event_monitor: example application that reads events from an IIO device
|
||||
and prints them
|
||||
* iio_generic_buffer: example application that reads data from buffer
|
||||
* iio_utils: set of APIs, typically used to access sysfs files.
|
||||
|
||||
LibIIO
|
||||
------
|
||||
|
||||
LibIIO is a C/C++ library that provides generic access to IIO devices. The
|
||||
library abstracts the low-level details of the hardware, and provides a simple
|
||||
yet complete programming interface that can be used for advanced projects.
|
||||
|
||||
For more information about LibIIO, please see:
|
||||
https://github.com/analogdevicesinc/libiio
|
||||
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
|
||||
|
||||
================
|
||||
ADIS16480 driver
|
||||
================
|
||||
|
||||
This driver supports Analog Device's IMUs on SPI bus.
|
||||
|
||||
1. Supported devices
|
||||
====================
|
||||
|
||||
* `ADIS16375 <https://www.analog.com/ADIS16375>`_
|
||||
* `ADIS16480 <https://www.analog.com/ADIS16480>`_
|
||||
* `ADIS16485 <https://www.analog.com/ADIS16485>`_
|
||||
* `ADIS16488 <https://www.analog.com/ADIS16488>`_
|
||||
* `ADIS16490 <https://www.analog.com/ADIS16490>`_
|
||||
* `ADIS16495 <https://www.analog.com/ADIS16495>`_
|
||||
* `ADIS16497 <https://www.analog.com/ADIS16497>`_
|
||||
* `ADIS16545 <https://www.analog.com/ADIS16545>`_
|
||||
* `ADIS16547 <https://www.analog.com/ADIS16547>`_
|
||||
|
||||
Each supported device is a complete inertial system that includes a triaxial
|
||||
gyroscope and a triaxial accelerometer. Each inertial sensor in device combines
|
||||
with signal conditioning that optimizes dynamic performance. The factory
|
||||
calibration characterizes each sensor for sensitivity, bias, and alignment. As
|
||||
a result, each sensor has its own dynamic compensation formulas that provide
|
||||
accurate sensor measurements.
|
||||
|
||||
2. Device attributes
|
||||
====================
|
||||
|
||||
Accelerometer, gyroscope measurements are always provided. Furthermore, the
|
||||
driver offers the capability to retrieve the delta angle and the delta velocity
|
||||
measurements computed by the device.
|
||||
|
||||
The delta angle measurements represent a calculation of angular displacement
|
||||
between each sample update, while the delta velocity measurements represent a
|
||||
calculation of linear velocity change between each sample update.
|
||||
|
||||
Finally, temperature data are provided which show a coarse measurement of
|
||||
the temperature inside of the IMU device. This data is most useful for
|
||||
monitoring relative changes in the thermal environment.
|
||||
|
||||
ADIS16480 and ADIS16488 also provide access to barometric pressure data and
|
||||
triaxial magnetometer measurements.
|
||||
|
||||
Each IIO device, has a device folder under ``/sys/bus/iio/devices/iio:deviceX``,
|
||||
where X is the IIO index of the device. Under these folders reside a set of
|
||||
device files, depending on the characteristics and features of the hardware
|
||||
device in questions. These files are consistently generalized and documented in
|
||||
the IIO ABI documentation.
|
||||
|
||||
The following tables show the adis16480 related device files, found in the
|
||||
specific device folder path ``/sys/bus/iio/devices/iio:deviceX``.
|
||||
|
||||
**Available only for ADIS16480 and ADIS16488:**
|
||||
|
||||
+------------------------------------------+---------------------------------------------------------+
|
||||
| 3-Axis Magnetometer related device files | Description |
|
||||
+------------------------------------------+---------------------------------------------------------+
|
||||
| in_magn_scale | Scale for the magnetometer channels. |
|
||||
+------------------------------------------+---------------------------------------------------------+
|
||||
| in_magn_x_calibbias | Calibration offset for the X-axis magnetometer channel. |
|
||||
+------------------------------------------+---------------------------------------------------------+
|
||||
| in_magn_x_filter_low_pass_3db_frequency | Bandwidth for the X-axis magnetometer channel. |
|
||||
+------------------------------------------+---------------------------------------------------------+
|
||||
| in_magn_x_raw | Raw X-axis magnetometer channel value. |
|
||||
+------------------------------------------+---------------------------------------------------------+
|
||||
| in_magn_y_calibbias | Calibration offset for the Y-axis magnetometer channel. |
|
||||
+------------------------------------------+---------------------------------------------------------+
|
||||
| in_magn_y_filter_low_pass_3db_frequency | Bandwidth for the Y-axis magnetometer channel. |
|
||||
+------------------------------------------+---------------------------------------------------------+
|
||||
| in_magn_y_raw | Raw Y-axis magnetometer channel value. |
|
||||
+------------------------------------------+---------------------------------------------------------+
|
||||
| in_magn_z_calibbias | Calibration offset for the Z-axis magnetometer channel. |
|
||||
+------------------------------------------+---------------------------------------------------------+
|
||||
| in_magn_z_filter_low_pass_3db_frequency | Bandwidth for the Z-axis magnetometer channel. |
|
||||
+------------------------------------------+---------------------------------------------------------+
|
||||
| in_magn_z_raw | Raw Z-axis magnetometer channel value. |
|
||||
+------------------------------------------+---------------------------------------------------------+
|
||||
|
||||
+------------------------------------------+-----------------------------------------------------+
|
||||
| Barometric pressure sensor related files | Description |
|
||||
+------------------------------------------+-----------------------------------------------------+
|
||||
| in_pressure0_calibbias | Calibration offset for barometric pressure channel. |
|
||||
+------------------------------------------+-----------------------------------------------------+
|
||||
| in_pressure0_raw | Raw barometric pressure channel value. |
|
||||
+------------------------------------------+-----------------------------------------------------+
|
||||
| in_pressure0_scale | Scale for the barometric pressure sensor channel. |
|
||||
+------------------------------------------+-----------------------------------------------------+
|
||||
|
||||
**Available for all supported devices:**
|
||||
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| 3-Axis Accelerometer related device files | Description |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_scale | Scale for the accelerometer channels. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_x_calibbias | Calibration offset for the X-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_x_calibscale | Calibration scale for the X-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_x_filter_low_pass_3db_frequency | Bandwidth for the X-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_x_raw | Raw X-axis accelerometer channel value. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_y_calibbias | Calibration offset for the Y-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_y_calibscale | Calibration scale for the Y-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_y_filter_low_pass_3db_frequency | Bandwidth for the Y-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_y_raw | Raw Y-axis accelerometer channel value. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_z_calibbias | Calibration offset for the Z-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_z_calibscale | Calibration scale for the Z-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_z_filter_low_pass_3db_frequency | Bandwidth for the Z-axis accelerometer channel. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_accel_z_raw | Raw Z-axis accelerometer channel value. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_deltavelocity_scale | Scale for delta velocity channels. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_deltavelocity_x_raw | Raw X-axis delta velocity channel value. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_deltavelocity_y_raw | Raw Y-axis delta velocity channel value. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
| in_deltavelocity_z_raw | Raw Z-axis delta velocity channel value. |
|
||||
+-------------------------------------------+----------------------------------------------------------+
|
||||
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| 3-Axis Gyroscope related device files | Description |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_scale | Scale for the gyroscope channels. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_x_calibbias | Calibration offset for the X-axis gyroscope channel. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_x_calibscale | Calibration scale for the X-axis gyroscope channel. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_x_filter_low_pass_3db_frequency | Bandwidth for the X-axis gyroscope channel. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_x_raw | Raw X-axis gyroscope channel value. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_y_calibbias | Calibration offset for the Y-axis gyroscope channel. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_y_calibscale | Calibration scale for the Y-axis gyroscope channel. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_y_filter_low_pass_3db_frequency | Bandwidth for the Y-axis gyroscope channel. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_y_raw | Raw Y-axis gyroscope channel value. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_z_calibbias | Calibration offset for the Z-axis gyroscope channel. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_z_calibscale | Calibration scale for the Z-axis gyroscope channel. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_z_filter_low_pass_3db_frequency | Bandwidth for the Z-axis gyroscope channel. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_anglvel_z_raw | Raw Z-axis gyroscope channel value. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_deltaangl_scale | Scale for delta angle channels. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_deltaangl_x_raw | Raw X-axis delta angle channel value. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_deltaangl_y_raw | Raw Y-axis delta angle channel value. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
| in_deltaangl_z_raw | Raw Z-axis delta angle channel value. |
|
||||
+--------------------------------------------+------------------------------------------------------+
|
||||
|
||||
+----------------------------------+-------------------------------------------+
|
||||
| Temperature sensor related files | Description |
|
||||
+----------------------------------+-------------------------------------------+
|
||||
| in_temp0_raw | Raw temperature channel value. |
|
||||
+----------------------------------+-------------------------------------------+
|
||||
| in_temp0_offset | Offset for the temperature sensor channel.|
|
||||
+----------------------------------+-------------------------------------------+
|
||||
| in_temp0_scale | Scale for the temperature sensor channel. |
|
||||
+----------------------------------+-------------------------------------------+
|
||||
|
||||
+-------------------------------+---------------------------------------------------------+
|
||||
| Miscellaneous device files | Description |
|
||||
+-------------------------------+---------------------------------------------------------+
|
||||
| name | Name of the IIO device. |
|
||||
+-------------------------------+---------------------------------------------------------+
|
||||
| sampling_frequency | Currently selected sample rate. |
|
||||
+-------------------------------+---------------------------------------------------------+
|
||||
|
||||
The following table shows the adis16480 related device debug files, found in the
|
||||
specific device debug folder path ``/sys/kernel/debug/iio/iio:deviceX``.
|
||||
|
||||
+----------------------+-------------------------------------------------------------------------+
|
||||
| Debugfs device files | Description |
|
||||
+----------------------+-------------------------------------------------------------------------+
|
||||
| serial_number | The serial number of the chip in hexadecimal format. |
|
||||
+----------------------+-------------------------------------------------------------------------+
|
||||
| product_id | Chip specific product id (e.g. 16480, 16488, 16545, etc.). |
|
||||
+----------------------+-------------------------------------------------------------------------+
|
||||
| flash_count | The number of flash writes performed on the device. |
|
||||
+----------------------+-------------------------------------------------------------------------+
|
||||
| firmware_revision | String containing the firmware revision in the following format ##.##. |
|
||||
+----------------------+-------------------------------------------------------------------------+
|
||||
| firmware_date | String containing the firmware date in the following format mm-dd-yyyy. |
|
||||
+----------------------+-------------------------------------------------------------------------+
|
||||
|
||||
Channels processed values
|
||||
-------------------------
|
||||
|
||||
A channel value can be read from its _raw attribute. The value returned is the
|
||||
raw value as reported by the devices. To get the processed value of the channel,
|
||||
apply the following formula:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
processed value = (_raw + _offset) * _scale
|
||||
|
||||
Where _offset and _scale are device attributes. If no _offset attribute is
|
||||
present, simply assume its value is 0.
|
||||
|
||||
The adis16480 driver offers data for 7 types of channels, the table below shows
|
||||
the measurement units for the processed value, which are defined by the IIO
|
||||
framework:
|
||||
|
||||
+--------------------------------------+---------------------------+
|
||||
| Channel type | Measurement unit |
|
||||
+--------------------------------------+---------------------------+
|
||||
| Acceleration on X, Y, and Z axis | Meters per Second squared |
|
||||
+--------------------------------------+---------------------------+
|
||||
| Angular velocity on X, Y and Z axis | Radians per second |
|
||||
+--------------------------------------+---------------------------+
|
||||
| Delta velocity on X. Y, and Z axis | Meters per Second |
|
||||
+--------------------------------------+---------------------------+
|
||||
| Delta angle on X, Y, and Z axis | Radians |
|
||||
+--------------------------------------+---------------------------+
|
||||
| Temperature | Millidegrees Celsius |
|
||||
+--------------------------------------+---------------------------+
|
||||
| Magnetic field along X, Y and Z axis | Gauss |
|
||||
+--------------------------------------+---------------------------+
|
||||
| Barometric pressure | kilo Pascal |
|
||||
+--------------------------------------+---------------------------+
|
||||
|
||||
Usage examples
|
||||
--------------
|
||||
|
||||
Show device name:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat name
|
||||
adis16545-1
|
||||
|
||||
Show accelerometer channels value:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_raw
|
||||
1376728
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_y_raw
|
||||
4487621
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_z_raw
|
||||
262773792
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_scale
|
||||
0.000000037
|
||||
|
||||
- X-axis acceleration = in_accel_x_raw * in_accel_scale = 0.050938936 m/s^2
|
||||
- Y-axis acceleration = in_accel_y_raw * in_accel_scale = 0.166041977 m/s^2
|
||||
- Z-axis acceleration = in_accel_z_raw * in_accel_scale = 9.722630304 m/s^2
|
||||
|
||||
Show gyroscope channels value:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_x_raw
|
||||
-1041702
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_raw
|
||||
-273013
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_z_raw
|
||||
2745116
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_scale
|
||||
0.000000001
|
||||
|
||||
- X-axis angular velocity = in_anglvel_x_raw * in_anglvel_scale = −0.001041702 rad/s
|
||||
- Y-axis angular velocity = in_anglvel_y_raw * in_anglvel_scale = −0.000273013 rad/s
|
||||
- Z-axis angular velocity = in_anglvel_z_raw * in_anglvel_scale = 0.002745116 rad/s
|
||||
|
||||
Set calibration offset for accelerometer channels:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias
|
||||
0
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 5000 > in_accel_x_calibbias
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias
|
||||
5000
|
||||
|
||||
Set calibration offset for gyroscope channels:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_calibbias
|
||||
0
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo -5000 > in_anglvel_y_calibbias
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_anglvel_y_calibbias
|
||||
-5000
|
||||
|
||||
Set sampling frequency:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat sampling_frequency
|
||||
4250.000000
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 1000 > sampling_frequency
|
||||
1062.500000
|
||||
|
||||
Set bandwidth for accelerometer channels:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_filter_low_pass_3db_frequency
|
||||
0
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 300 > in_accel_x_filter_low_pass_3db_frequency
|
||||
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_filter_low_pass_3db_frequency
|
||||
300
|
||||
|
||||
Show serial number:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/kernel/debug/iio/iio:device0> cat serial_number
|
||||
0x000c
|
||||
|
||||
Show product id:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/kernel/debug/iio/iio:device0> cat product_id
|
||||
16545
|
||||
|
||||
Show flash count:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/kernel/debug/iio/iio:device0> cat flash_count
|
||||
88
|
||||
|
||||
Show firmware revision:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/kernel/debug/iio/iio:device0> cat firmware_revision
|
||||
1.4
|
||||
|
||||
Show firmware date:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/kernel/debug/iio/iio:device0> cat firmware_date
|
||||
09-23-2023
|
||||
|
||||
3. Device buffers
|
||||
=================
|
||||
|
||||
This driver supports IIO buffers.
|
||||
|
||||
All devices support retrieving the raw acceleration, gyroscope and temperature
|
||||
measurements using buffers.
|
||||
|
||||
The following device families also support retrieving the delta velocity, delta
|
||||
angle and temperature measurements using buffers:
|
||||
|
||||
- ADIS16545
|
||||
- ADIS16547
|
||||
|
||||
However, when retrieving acceleration or gyroscope data using buffers, delta
|
||||
readings will not be available and vice versa. This is because the device only
|
||||
allows to read either acceleration and gyroscope data or delta velocity and
|
||||
delta angle data at a time and switching between these two burst data selection
|
||||
modes is time consuming.
|
||||
|
||||
Usage examples
|
||||
--------------
|
||||
|
||||
Set device trigger in current_trigger, if not already set:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> cat trigger/current_trigger
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo adis16545-1-dev0 > trigger/current_trigger
|
||||
root:/sys/bus/iio/devices/iio:device0> cat trigger/current_trigger
|
||||
adis16545-1-dev0
|
||||
|
||||
Select channels for buffer read:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_deltavelocity_x_en
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_deltavelocity_y_en
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_deltavelocity_z_en
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_temp0_en
|
||||
|
||||
Set the number of samples to be stored in the buffer:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 10 > buffer/length
|
||||
|
||||
Enable buffer readings:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> echo 1 > buffer/enable
|
||||
|
||||
Obtain buffered data::
|
||||
|
||||
root:/sys/bus/iio/devices/iio:device0> hexdump -C /dev/iio\:device0
|
||||
...
|
||||
00006aa0 09 62 00 00 ff ff fc a4 00 00 01 69 00 03 3c 08 |.b.........i..<.|
|
||||
00006ab0 09 61 00 00 00 00 02 96 00 00 02 8f 00 03 37 50 |.a............7P|
|
||||
00006ac0 09 61 00 00 00 00 12 3d 00 00 0b 89 00 03 2c 0b |.a.....=......,.|
|
||||
00006ad0 09 61 00 00 00 00 1e dc 00 00 16 dd 00 03 25 bf |.a............%.|
|
||||
00006ae0 09 61 00 00 00 00 1e e3 00 00 1b bf 00 03 27 0b |.a............'.|
|
||||
00006af0 09 61 00 00 00 00 15 50 00 00 19 44 00 03 30 fd |.a.....P...D..0.|
|
||||
00006b00 09 61 00 00 00 00 09 0e 00 00 14 41 00 03 3d 7f |.a.........A..=.|
|
||||
00006b10 09 61 00 00 ff ff ff f0 00 00 0e bc 00 03 48 d0 |.a............H.|
|
||||
00006b20 09 63 00 00 00 00 00 9f 00 00 0f 37 00 03 4c fe |.c.........7..L.|
|
||||
00006b30 09 64 00 00 00 00 0b f6 00 00 18 92 00 03 43 22 |.d............C"|
|
||||
00006b40 09 64 00 00 00 00 18 df 00 00 22 33 00 03 33 ab |.d........"3..3.|
|
||||
00006b50 09 63 00 00 00 00 1e 81 00 00 26 be 00 03 29 60 |.c........&...)`|
|
||||
00006b60 09 63 00 00 00 00 1b 13 00 00 22 2f 00 03 23 91 |.c........"/..#.|
|
||||
...
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,27 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=====================
|
||||
IIO Interfacing Tools
|
||||
=====================
|
||||
|
||||
1. Linux Kernel Tools
|
||||
=====================
|
||||
|
||||
Linux Kernel provides some userspace tools that can be used to retrieve data
|
||||
from IIO sysfs:
|
||||
|
||||
* lsiio: example application that provides a list of IIO devices and triggers
|
||||
* iio_event_monitor: example application that reads events from an IIO device
|
||||
and prints them
|
||||
* iio_generic_buffer: example application that reads data from buffer
|
||||
* iio_utils: set of APIs, typically used to access sysfs files.
|
||||
|
||||
2. LibIIO
|
||||
=========
|
||||
|
||||
LibIIO is a C/C++ library that provides generic access to IIO devices. The
|
||||
library abstracts the low-level details of the hardware, and provides a simple
|
||||
yet complete programming interface that can be used for advanced projects.
|
||||
|
||||
For more information about LibIIO, please see:
|
||||
https://github.com/analogdevicesinc/libiio
|
||||
@@ -9,6 +9,7 @@ Industrial I/O
|
||||
|
||||
iio_configfs
|
||||
iio_devbuf
|
||||
iio_tools
|
||||
|
||||
Industrial I/O Kernel Drivers
|
||||
=============================
|
||||
@@ -18,5 +19,6 @@ Industrial I/O Kernel Drivers
|
||||
|
||||
ad7944
|
||||
adis16475
|
||||
adis16480
|
||||
bno055
|
||||
ep93xx_adc
|
||||
|
||||
+19
@@ -439,6 +439,16 @@ W: http://wiki.analog.com/AD7142
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: drivers/input/misc/ad714x.c
|
||||
|
||||
AD738X ADC DRIVER (AD7380/1/2/4)
|
||||
M: Michael Hennerich <michael.hennerich@analog.com>
|
||||
M: Nuno Sá <nuno.sa@analog.com>
|
||||
R: David Lechner <dlechner@baylibre.com>
|
||||
S: Supported
|
||||
W: https://wiki.analog.com/resources/tools-software/linux-drivers/iio-adc/ad738x
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7380.yaml
|
||||
F: drivers/iio/adc/ad7380.c
|
||||
|
||||
AD7877 TOUCHSCREEN DRIVER
|
||||
M: Michael Hennerich <michael.hennerich@analog.com>
|
||||
S: Supported
|
||||
@@ -11510,6 +11520,7 @@ M: Jean-Baptiste Maneyrol <jmaneyrol@invensense.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Maintained
|
||||
W: https://invensense.tdk.com/
|
||||
F: Documentation/ABI/testing/sysfs-bus-iio-inv_icm42600
|
||||
F: Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
|
||||
F: drivers/iio/imu/inv_icm42600/
|
||||
|
||||
@@ -19938,6 +19949,14 @@ F: include/linux/wait.h
|
||||
F: include/uapi/linux/sched.h
|
||||
F: kernel/sched/
|
||||
|
||||
SCIOSENSE ENS160 MULTI-GAS SENSOR DRIVER
|
||||
M: Gustavo Silva <gustavograzs@gmail.com>
|
||||
S: Maintained
|
||||
F: drivers/iio/chemical/ens160_core.c
|
||||
F: drivers/iio/chemical/ens160_i2c.c
|
||||
F: drivers/iio/chemical/ens160_spi.c
|
||||
F: drivers/iio/chemical/ens160.h
|
||||
|
||||
SCSI LIBSAS SUBSYSTEM
|
||||
R: John Garry <john.g.garry@oracle.com>
|
||||
R: Jason Yan <yanaijie@huawei.com>
|
||||
|
||||
@@ -72,13 +72,7 @@ static int adxl313_spi_probe(struct spi_device *spi)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Retrieves device specific data as a pointer to a
|
||||
* adxl313_chip_info structure
|
||||
*/
|
||||
chip_data = device_get_match_data(&spi->dev);
|
||||
if (!chip_data)
|
||||
chip_data = (const struct adxl313_chip_info *)spi_get_device_id(spi)->driver_data;
|
||||
chip_data = spi_get_device_match_data(spi);
|
||||
|
||||
regmap = devm_regmap_init_spi(spi,
|
||||
&adxl31x_spi_regmap_config[chip_data->type]);
|
||||
|
||||
@@ -28,13 +28,9 @@ static int adxl355_spi_probe(struct spi_device *spi)
|
||||
const struct adxl355_chip_info *chip_data;
|
||||
struct regmap *regmap;
|
||||
|
||||
chip_data = device_get_match_data(&spi->dev);
|
||||
if (!chip_data) {
|
||||
chip_data = (void *)spi_get_device_id(spi)->driver_data;
|
||||
|
||||
if (!chip_data)
|
||||
return -EINVAL;
|
||||
}
|
||||
chip_data = spi_get_device_match_data(spi);
|
||||
if (!chip_data)
|
||||
return -EINVAL;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &adxl355_spi_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
|
||||
@@ -61,8 +61,8 @@ static int adxl367_i2c_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adxl367_i2c_id[] = {
|
||||
{ "adxl367", 0 },
|
||||
{ },
|
||||
{ "adxl367" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adxl367_i2c_id);
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ static int adxl372_i2c_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adxl372_i2c_id[] = {
|
||||
{ "adxl372", 0 },
|
||||
{ "adxl372" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adxl372_i2c_id);
|
||||
|
||||
@@ -28,7 +28,7 @@ static int bma400_i2c_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bma400_i2c_ids[] = {
|
||||
{ "bma400", 0 },
|
||||
{ "bma400" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bma400_i2c_ids);
|
||||
|
||||
@@ -114,11 +114,6 @@ enum bmi088_odr_modes {
|
||||
BMI088_ACCEL_MODE_ODR_1600 = 0xc,
|
||||
};
|
||||
|
||||
struct bmi088_scale_info {
|
||||
int scale;
|
||||
u8 reg_range;
|
||||
};
|
||||
|
||||
struct bmi088_accel_chip_info {
|
||||
const char *name;
|
||||
u8 chip_id;
|
||||
|
||||
@@ -268,7 +268,7 @@ static int da311_resume(struct device *dev)
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(da311_pm_ops, da311_suspend, da311_resume);
|
||||
|
||||
static const struct i2c_device_id da311_i2c_id[] = {
|
||||
{"da311", 0},
|
||||
{ "da311" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, da311_i2c_id);
|
||||
|
||||
@@ -201,9 +201,9 @@ static DEFINE_SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend,
|
||||
dmard06_resume);
|
||||
|
||||
static const struct i2c_device_id dmard06_id[] = {
|
||||
{ "dmard05", 0 },
|
||||
{ "dmard06", 0 },
|
||||
{ "dmard07", 0 },
|
||||
{ "dmard05" },
|
||||
{ "dmard06" },
|
||||
{ "dmard07" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, dmard06_id);
|
||||
|
||||
@@ -125,8 +125,8 @@ static int dmard09_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id dmard09_id[] = {
|
||||
{ "dmard09", 0 },
|
||||
{ },
|
||||
{ "dmard09" },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, dmard09_id);
|
||||
|
||||
@@ -231,7 +231,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend,
|
||||
dmard10_resume);
|
||||
|
||||
static const struct i2c_device_id dmard10_i2c_id[] = {
|
||||
{"dmard10", 0},
|
||||
{ "dmard10" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, dmard10_i2c_id);
|
||||
|
||||
@@ -43,8 +43,8 @@ static const struct of_device_id kxsd9_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, kxsd9_of_match);
|
||||
|
||||
static const struct i2c_device_id kxsd9_i2c_id[] = {
|
||||
{"kxsd9", 0},
|
||||
{ },
|
||||
{ "kxsd9" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, kxsd9_i2c_id);
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ static int mc3230_resume(struct device *dev)
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume);
|
||||
|
||||
static const struct i2c_device_id mc3230_i2c_id[] = {
|
||||
{"mc3230", 0},
|
||||
{ "mc3230" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mc3230_i2c_id);
|
||||
|
||||
@@ -32,8 +32,8 @@ static void mma7455_i2c_remove(struct i2c_client *i2c)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mma7455_i2c_ids[] = {
|
||||
{ "mma7455", 0 },
|
||||
{ "mma7456", 0 },
|
||||
{ "mma7455" },
|
||||
{ "mma7456" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mma7455_i2c_ids);
|
||||
|
||||
+36
-16
@@ -38,21 +38,6 @@
|
||||
|
||||
static const int mma7660_nscale = 467142857;
|
||||
|
||||
#define MMA7660_CHANNEL(reg, axis) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.address = reg, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mma7660_channels[] = {
|
||||
MMA7660_CHANNEL(MMA7660_REG_XOUT, X),
|
||||
MMA7660_CHANNEL(MMA7660_REG_YOUT, Y),
|
||||
MMA7660_CHANNEL(MMA7660_REG_ZOUT, Z),
|
||||
};
|
||||
|
||||
enum mma7660_mode {
|
||||
MMA7660_MODE_STANDBY,
|
||||
MMA7660_MODE_ACTIVE
|
||||
@@ -62,6 +47,21 @@ struct mma7660_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
enum mma7660_mode mode;
|
||||
struct iio_mount_matrix orientation;
|
||||
};
|
||||
|
||||
static const struct iio_mount_matrix *
|
||||
mma7660_get_mount_matrix(const struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct mma7660_data *data = iio_priv(indio_dev);
|
||||
|
||||
return &data->orientation;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info mma7660_ext_info[] = {
|
||||
IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, mma7660_get_mount_matrix),
|
||||
{ }
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR(in_accel_scale_available, MMA7660_SCALE_AVAIL);
|
||||
@@ -75,6 +75,22 @@ static const struct attribute_group mma7660_attribute_group = {
|
||||
.attrs = mma7660_attributes
|
||||
};
|
||||
|
||||
#define MMA7660_CHANNEL(reg, axis) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.address = reg, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.ext_info = mma7660_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mma7660_channels[] = {
|
||||
MMA7660_CHANNEL(MMA7660_REG_XOUT, X),
|
||||
MMA7660_CHANNEL(MMA7660_REG_YOUT, Y),
|
||||
MMA7660_CHANNEL(MMA7660_REG_ZOUT, Z),
|
||||
};
|
||||
|
||||
static int mma7660_set_mode(struct mma7660_data *data,
|
||||
enum mma7660_mode mode)
|
||||
{
|
||||
@@ -187,6 +203,10 @@ static int mma7660_probe(struct i2c_client *client)
|
||||
mutex_init(&data->lock);
|
||||
data->mode = MMA7660_MODE_STANDBY;
|
||||
|
||||
ret = iio_read_mount_matrix(&client->dev, &data->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->info = &mma7660_info;
|
||||
indio_dev->name = MMA7660_DRIVER_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
@@ -241,7 +261,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(mma7660_pm_ops, mma7660_suspend,
|
||||
mma7660_resume);
|
||||
|
||||
static const struct i2c_device_id mma7660_i2c_id[] = {
|
||||
{"mma7660", 0},
|
||||
{ "mma7660" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mma7660_i2c_id);
|
||||
|
||||
@@ -595,7 +595,7 @@ static const struct acpi_device_id mma9551_acpi_match[] = {
|
||||
MODULE_DEVICE_TABLE(acpi, mma9551_acpi_match);
|
||||
|
||||
static const struct i2c_device_id mma9551_id[] = {
|
||||
{"mma9551", 0},
|
||||
{ "mma9551" },
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -1234,8 +1234,8 @@ static const struct acpi_device_id mma9553_acpi_match[] = {
|
||||
MODULE_DEVICE_TABLE(acpi, mma9553_acpi_match);
|
||||
|
||||
static const struct i2c_device_id mma9553_id[] = {
|
||||
{"mma9553", 0},
|
||||
{},
|
||||
{ "mma9553" },
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, mma9553_id);
|
||||
|
||||
@@ -584,9 +584,9 @@ static const struct of_device_id mxc4005_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, mxc4005_of_match);
|
||||
|
||||
static const struct i2c_device_id mxc4005_id[] = {
|
||||
{"mxc4005", 0},
|
||||
{"mxc6655", 0},
|
||||
{ },
|
||||
{ "mxc4005" },
|
||||
{ "mxc6655" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mxc4005_id);
|
||||
|
||||
|
||||
@@ -172,8 +172,8 @@ static const struct acpi_device_id mxc6255_acpi_match[] = {
|
||||
MODULE_DEVICE_TABLE(acpi, mxc6255_acpi_match);
|
||||
|
||||
static const struct i2c_device_id mxc6255_id[] = {
|
||||
{"mxc6225", 0},
|
||||
{"mxc6255", 0},
|
||||
{ "mxc6225" },
|
||||
{ "mxc6255" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mxc6255_id);
|
||||
|
||||
@@ -633,8 +633,8 @@ static DEFINE_SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend,
|
||||
|
||||
static const struct i2c_device_id stk8312_i2c_id[] = {
|
||||
/* Deprecated in favour of lowercase form */
|
||||
{ "STK8312", 0 },
|
||||
{ "stk8312", 0 },
|
||||
{ "STK8312" },
|
||||
{ "stk8312" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, stk8312_i2c_id);
|
||||
|
||||
@@ -525,7 +525,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(stk8ba50_pm_ops, stk8ba50_suspend,
|
||||
stk8ba50_resume);
|
||||
|
||||
static const struct i2c_device_id stk8ba50_i2c_id[] = {
|
||||
{"stk8ba50", 0},
|
||||
{ "stk8ba50" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, stk8ba50_i2c_id);
|
||||
|
||||
+25
-3
@@ -88,12 +88,17 @@ config AD7173
|
||||
called ad7173.
|
||||
|
||||
config AD7192
|
||||
tristate "Analog Devices AD7190 AD7192 AD7193 AD7195 ADC driver"
|
||||
tristate "Analog Devices AD7192 and similar ADC driver"
|
||||
depends on SPI
|
||||
select AD_SIGMA_DELTA
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7190,
|
||||
AD7192, AD7193 or AD7195 SPI analog to digital converters (ADC).
|
||||
Say yes here to build support for Analog Devices SPI analog to digital
|
||||
converters (ADC):
|
||||
- AD7190
|
||||
- AD7192
|
||||
- AD7193
|
||||
- AD7194
|
||||
- AD7195
|
||||
If unsure, say N (but it's safe to say "Y").
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
@@ -155,6 +160,22 @@ config AD7298
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7298.
|
||||
|
||||
config AD7380
|
||||
tristate "Analog Devices AD7380 ADC driver"
|
||||
depends on SPI_MASTER
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
AD7380 is a family of simultaneous sampling ADCs that share the same
|
||||
SPI register map and have similar pinouts.
|
||||
|
||||
Say yes here to build support for Analog Devices AD7380 ADC and
|
||||
similar chips.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ad7380.
|
||||
|
||||
config AD7476
|
||||
tristate "Analog Devices AD7476 1-channel ADCs driver and other similar devices from AD and TI"
|
||||
depends on SPI
|
||||
@@ -332,6 +353,7 @@ config AD9467
|
||||
|
||||
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
|
||||
select IIO_BUFFER
|
||||
select IIO_BUFFER_HW_CONSUMER
|
||||
select IIO_BUFFER_DMAENGINE
|
||||
|
||||
+13
-12
@@ -18,7 +18,7 @@ obj-$(CONFIG_AD7280) += ad7280a.o
|
||||
obj-$(CONFIG_AD7291) += ad7291.o
|
||||
obj-$(CONFIG_AD7292) += ad7292.o
|
||||
obj-$(CONFIG_AD7298) += ad7298.o
|
||||
obj-$(CONFIG_AD7923) += ad7923.o
|
||||
obj-$(CONFIG_AD7380) += ad7380.o
|
||||
obj-$(CONFIG_AD7476) += ad7476.o
|
||||
obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o
|
||||
obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o
|
||||
@@ -29,6 +29,7 @@ obj-$(CONFIG_AD7780) += ad7780.o
|
||||
obj-$(CONFIG_AD7791) += ad7791.o
|
||||
obj-$(CONFIG_AD7793) += ad7793.o
|
||||
obj-$(CONFIG_AD7887) += ad7887.o
|
||||
obj-$(CONFIG_AD7923) += ad7923.o
|
||||
obj-$(CONFIG_AD7944) += ad7944.o
|
||||
obj-$(CONFIG_AD7949) += ad7949.o
|
||||
obj-$(CONFIG_AD799X) += ad799x.o
|
||||
@@ -90,42 +91,43 @@ obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
|
||||
obj-$(CONFIG_PAC1934) += pac1934.o
|
||||
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
|
||||
obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o
|
||||
obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
|
||||
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
|
||||
obj-$(CONFIG_QCOM_SPMI_RRADC) += qcom-spmi-rradc.o
|
||||
obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
|
||||
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
|
||||
obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o
|
||||
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_ROCKCHIP_SARADC) += rockchip_saradc.o
|
||||
obj-$(CONFIG_RICHTEK_RTQ6056) += rtq6056.o
|
||||
obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o
|
||||
obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o
|
||||
obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o
|
||||
obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
|
||||
obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
|
||||
obj-$(CONFIG_SUN20I_GPADC) += sun20i-gpadc-iio.o
|
||||
obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
|
||||
obj-$(CONFIG_STM32_ADC) += stm32-adc.o
|
||||
obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
|
||||
obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
|
||||
obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
|
||||
obj-$(CONFIG_STMPE_ADC) += stmpe-adc.o
|
||||
obj-$(CONFIG_SUN20I_GPADC) += sun20i-gpadc-iio.o
|
||||
obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
|
||||
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
|
||||
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
|
||||
obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o
|
||||
obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
|
||||
obj-$(CONFIG_TI_ADC108S102) += ti-adc108s102.o
|
||||
obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
|
||||
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
|
||||
obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
|
||||
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
|
||||
obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o
|
||||
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
|
||||
obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o
|
||||
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
|
||||
obj-$(CONFIG_TI_ADS7924) += ti-ads7924.o
|
||||
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
|
||||
obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o
|
||||
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
|
||||
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
|
||||
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
obj-$(CONFIG_TI_LMP92064) += ti-lmp92064.o
|
||||
obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
|
||||
@@ -134,7 +136,6 @@ obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
|
||||
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
|
||||
obj-$(CONFIG_VF610_ADC) += vf610_adc.o
|
||||
obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
|
||||
obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o
|
||||
xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o
|
||||
obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o
|
||||
obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o
|
||||
obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o
|
||||
|
||||
+225
-48
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* AD7190 AD7192 AD7193 AD7195 SPI ADC driver
|
||||
* AD7192 and similar SPI ADC driver
|
||||
*
|
||||
* Copyright 2011-2015 Analog Devices Inc.
|
||||
*/
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@@ -128,10 +129,24 @@
|
||||
#define AD7193_CH_AIN8 0x480 /* AIN7 - AINCOM */
|
||||
#define AD7193_CH_AINCOM 0x600 /* AINCOM - AINCOM */
|
||||
|
||||
#define AD7194_CH_POS(x) (((x) - 1) << 4)
|
||||
#define AD7194_CH_NEG(x) ((x) - 1)
|
||||
|
||||
/* 10th bit corresponds to CON18(Pseudo) */
|
||||
#define AD7194_CH(p) (BIT(10) | AD7194_CH_POS(p))
|
||||
|
||||
#define AD7194_DIFF_CH(p, n) (AD7194_CH_POS(p) | AD7194_CH_NEG(n))
|
||||
#define AD7194_CH_TEMP 0x100
|
||||
#define AD7194_CH_BASE_NR 2
|
||||
#define AD7194_CH_AIN_START 1
|
||||
#define AD7194_CH_AIN_NR 16
|
||||
#define AD7194_CH_MAX_NR 272
|
||||
|
||||
/* ID Register Bit Designations (AD7192_REG_ID) */
|
||||
#define CHIPID_AD7190 0x4
|
||||
#define CHIPID_AD7192 0x0
|
||||
#define CHIPID_AD7193 0x2
|
||||
#define CHIPID_AD7194 0x3
|
||||
#define CHIPID_AD7195 0x6
|
||||
#define AD7192_ID_MASK GENMASK(3, 0)
|
||||
|
||||
@@ -169,6 +184,7 @@ enum {
|
||||
ID_AD7190,
|
||||
ID_AD7192,
|
||||
ID_AD7193,
|
||||
ID_AD7194,
|
||||
ID_AD7195,
|
||||
};
|
||||
|
||||
@@ -177,7 +193,9 @@ struct ad7192_chip_info {
|
||||
const char *name;
|
||||
const struct iio_chan_spec *channels;
|
||||
u8 num_channels;
|
||||
const struct ad_sigma_delta_info *sigma_delta_info;
|
||||
const struct iio_info *info;
|
||||
int (*parse_channels)(struct iio_dev *indio_dev);
|
||||
};
|
||||
|
||||
struct ad7192_state {
|
||||
@@ -186,10 +204,12 @@ struct ad7192_state {
|
||||
struct regulator *vref;
|
||||
struct clk *mclk;
|
||||
u16 int_vref_mv;
|
||||
u32 aincom_mv;
|
||||
u32 fclk;
|
||||
u32 mode;
|
||||
u32 conf;
|
||||
u32 scale_avail[8][2];
|
||||
u32 filter_freq_avail[4][2];
|
||||
u32 oversampling_ratio_avail[4];
|
||||
u8 gpocon;
|
||||
u8 clock_sel;
|
||||
@@ -343,6 +363,18 @@ static const struct ad_sigma_delta_info ad7192_sigma_delta_info = {
|
||||
.irq_flags = IRQF_TRIGGER_FALLING,
|
||||
};
|
||||
|
||||
static const struct ad_sigma_delta_info ad7194_sigma_delta_info = {
|
||||
.set_channel = ad7192_set_channel,
|
||||
.append_status = ad7192_append_status,
|
||||
.disable_all = ad7192_disable_all,
|
||||
.set_mode = ad7192_set_mode,
|
||||
.has_registers = true,
|
||||
.addr_shift = 3,
|
||||
.read_mask = BIT(6),
|
||||
.status_ch_mask = GENMASK(3, 0),
|
||||
.irq_flags = IRQF_TRIGGER_FALLING,
|
||||
};
|
||||
|
||||
static const struct ad_sd_calib_data ad7192_calib_arr[8] = {
|
||||
{AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN1},
|
||||
{AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN1},
|
||||
@@ -473,6 +505,16 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device *dev)
|
||||
st->oversampling_ratio_avail[2] = 8;
|
||||
st->oversampling_ratio_avail[3] = 16;
|
||||
|
||||
st->filter_freq_avail[0][0] = 600;
|
||||
st->filter_freq_avail[1][0] = 800;
|
||||
st->filter_freq_avail[2][0] = 2300;
|
||||
st->filter_freq_avail[3][0] = 2720;
|
||||
|
||||
st->filter_freq_avail[0][1] = 1000;
|
||||
st->filter_freq_avail[1][1] = 1000;
|
||||
st->filter_freq_avail[2][1] = 1000;
|
||||
st->filter_freq_avail[3][1] = 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -586,48 +628,24 @@ static int ad7192_get_f_adc(struct ad7192_state *st)
|
||||
f_order * FIELD_GET(AD7192_MODE_RATE_MASK, st->mode));
|
||||
}
|
||||
|
||||
static void ad7192_get_available_filter_freq(struct ad7192_state *st,
|
||||
int *freq)
|
||||
static void ad7192_update_filter_freq_avail(struct ad7192_state *st)
|
||||
{
|
||||
unsigned int fadc;
|
||||
|
||||
/* Formulas for filter at page 25 of the datasheet */
|
||||
fadc = ad7192_compute_f_adc(st, false, true);
|
||||
freq[0] = DIV_ROUND_CLOSEST(fadc * 240, 1024);
|
||||
st->filter_freq_avail[0][0] = DIV_ROUND_CLOSEST(fadc * 240, 1024);
|
||||
|
||||
fadc = ad7192_compute_f_adc(st, true, true);
|
||||
freq[1] = DIV_ROUND_CLOSEST(fadc * 240, 1024);
|
||||
st->filter_freq_avail[1][0] = DIV_ROUND_CLOSEST(fadc * 240, 1024);
|
||||
|
||||
fadc = ad7192_compute_f_adc(st, false, false);
|
||||
freq[2] = DIV_ROUND_CLOSEST(fadc * 230, 1024);
|
||||
st->filter_freq_avail[2][0] = DIV_ROUND_CLOSEST(fadc * 230, 1024);
|
||||
|
||||
fadc = ad7192_compute_f_adc(st, true, false);
|
||||
freq[3] = DIV_ROUND_CLOSEST(fadc * 272, 1024);
|
||||
st->filter_freq_avail[3][0] = DIV_ROUND_CLOSEST(fadc * 272, 1024);
|
||||
}
|
||||
|
||||
static ssize_t ad7192_show_filter_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7192_state *st = iio_priv(indio_dev);
|
||||
unsigned int freq_avail[4], i;
|
||||
size_t len = 0;
|
||||
|
||||
ad7192_get_available_filter_freq(st, freq_avail);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(freq_avail); i++)
|
||||
len += sysfs_emit_at(buf, len, "%d.%03d ", freq_avail[i] / 1000,
|
||||
freq_avail[i] % 1000);
|
||||
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(filter_low_pass_3db_frequency_available,
|
||||
0444, ad7192_show_filter_avail, NULL, 0);
|
||||
|
||||
static IIO_DEVICE_ATTR(bridge_switch_en, 0644,
|
||||
ad7192_show_bridge_switch, ad7192_set,
|
||||
AD7192_REG_GPOCON);
|
||||
@@ -637,7 +655,6 @@ static IIO_DEVICE_ATTR(ac_excitation_en, 0644,
|
||||
AD7192_REG_CONF);
|
||||
|
||||
static struct attribute *ad7192_attributes[] = {
|
||||
&iio_dev_attr_filter_low_pass_3db_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_bridge_switch_en.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
@@ -647,7 +664,6 @@ static const struct attribute_group ad7192_attribute_group = {
|
||||
};
|
||||
|
||||
static struct attribute *ad7195_attributes[] = {
|
||||
&iio_dev_attr_filter_low_pass_3db_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_bridge_switch_en.dev_attr.attr,
|
||||
&iio_dev_attr_ac_excitation_en.dev_attr.attr,
|
||||
NULL
|
||||
@@ -665,17 +681,15 @@ static unsigned int ad7192_get_temp_scale(bool unipolar)
|
||||
static int ad7192_set_3db_filter_freq(struct ad7192_state *st,
|
||||
int val, int val2)
|
||||
{
|
||||
int freq_avail[4], i, ret, freq;
|
||||
int i, ret, freq;
|
||||
unsigned int diff_new, diff_old;
|
||||
int idx = 0;
|
||||
|
||||
diff_old = U32_MAX;
|
||||
freq = val * 1000 + val2;
|
||||
|
||||
ad7192_get_available_filter_freq(st, freq_avail);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(freq_avail); i++) {
|
||||
diff_new = abs(freq - freq_avail[i]);
|
||||
for (i = 0; i < ARRAY_SIZE(st->filter_freq_avail); i++) {
|
||||
diff_new = abs(freq - st->filter_freq_avail[i][0]);
|
||||
if (diff_new < diff_old) {
|
||||
diff_old = diff_new;
|
||||
idx = i;
|
||||
@@ -759,10 +773,24 @@ static int ad7192_read_raw(struct iio_dev *indio_dev,
|
||||
*val = -(1 << (chan->scan_type.realbits - 1));
|
||||
else
|
||||
*val = 0;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
/*
|
||||
* Only applies to pseudo-differential inputs.
|
||||
* AINCOM voltage has to be converted to "raw" units.
|
||||
*/
|
||||
if (st->aincom_mv && !chan->differential)
|
||||
*val += DIV_ROUND_CLOSEST_ULL((u64)st->aincom_mv * NANO,
|
||||
st->scale_avail[gain][1]);
|
||||
return IIO_VAL_INT;
|
||||
/* Kelvin to Celsius */
|
||||
if (chan->type == IIO_TEMP)
|
||||
case IIO_TEMP:
|
||||
*val -= 273 * ad7192_get_temp_scale(unipolar);
|
||||
return IIO_VAL_INT;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = DIV_ROUND_CLOSEST(ad7192_get_f_adc(st), 1024);
|
||||
return IIO_VAL_INT;
|
||||
@@ -792,10 +820,11 @@ static int ad7192_write_raw(struct iio_dev *indio_dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = -EINVAL;
|
||||
mutex_lock(&st->lock);
|
||||
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
|
||||
if (val2 == st->scale_avail[i][1]) {
|
||||
ret = 0;
|
||||
@@ -809,7 +838,6 @@ static int ad7192_write_raw(struct iio_dev *indio_dev,
|
||||
ad7192_calibrate_all(st);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&st->lock);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (!val) {
|
||||
@@ -826,13 +854,13 @@ static int ad7192_write_raw(struct iio_dev *indio_dev,
|
||||
st->mode &= ~AD7192_MODE_RATE_MASK;
|
||||
st->mode |= FIELD_PREP(AD7192_MODE_RATE_MASK, div);
|
||||
ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
|
||||
ad7192_update_filter_freq_avail(st);
|
||||
break;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
ret = ad7192_set_3db_filter_freq(st, val, val2 / 1000);
|
||||
break;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
ret = -EINVAL;
|
||||
mutex_lock(&st->lock);
|
||||
for (i = 0; i < ARRAY_SIZE(st->oversampling_ratio_avail); i++)
|
||||
if (val == st->oversampling_ratio_avail[i]) {
|
||||
ret = 0;
|
||||
@@ -845,12 +873,14 @@ static int ad7192_write_raw(struct iio_dev *indio_dev,
|
||||
3, st->mode);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&st->lock);
|
||||
ad7192_update_filter_freq_avail(st);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
return ret;
|
||||
@@ -888,6 +918,12 @@ static int ad7192_read_avail(struct iio_dev *indio_dev,
|
||||
/* Values are stored in a 2D matrix */
|
||||
*length = ARRAY_SIZE(st->scale_avail) * 2;
|
||||
|
||||
return IIO_AVAIL_LIST;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
*vals = (int *)st->filter_freq_avail;
|
||||
*type = IIO_VAL_FRACTIONAL;
|
||||
*length = ARRAY_SIZE(st->filter_freq_avail) * 2;
|
||||
|
||||
return IIO_AVAIL_LIST;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
*vals = (int *)st->oversampling_ratio_avail;
|
||||
@@ -930,6 +966,14 @@ static const struct iio_info ad7192_info = {
|
||||
.update_scan_mode = ad7192_update_scan_mode,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7194_info = {
|
||||
.read_raw = ad7192_read_raw,
|
||||
.write_raw = ad7192_write_raw,
|
||||
.write_raw_get_fmt = ad7192_write_raw_get_fmt,
|
||||
.read_avail = ad7192_read_avail,
|
||||
.validate_trigger = ad_sd_validate_trigger,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7195_info = {
|
||||
.read_raw = ad7192_read_raw,
|
||||
.write_raw = ad7192_write_raw,
|
||||
@@ -956,7 +1000,9 @@ static const struct iio_info ad7195_info = {
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
|
||||
(_mask_all), \
|
||||
.info_mask_shared_by_type_available = (_mask_type_av), \
|
||||
.info_mask_shared_by_all_available = (_mask_all_av), \
|
||||
.info_mask_shared_by_all_available = \
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
|
||||
(_mask_all_av), \
|
||||
.ext_info = (_ext_info), \
|
||||
.scan_index = (_si), \
|
||||
.scan_type = { \
|
||||
@@ -1019,12 +1065,95 @@ static const struct iio_chan_spec ad7193_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(14),
|
||||
};
|
||||
|
||||
static bool ad7194_validate_ain_channel(struct device *dev, u32 ain)
|
||||
{
|
||||
return in_range(ain, AD7194_CH_AIN_START, AD7194_CH_AIN_NR);
|
||||
}
|
||||
|
||||
static int ad7194_parse_channels(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct device *dev = indio_dev->dev.parent;
|
||||
struct iio_chan_spec *ad7194_channels;
|
||||
const struct iio_chan_spec ad7194_chan = AD7193_CHANNEL(0, 0, 0);
|
||||
const struct iio_chan_spec ad7194_chan_diff = AD7193_DIFF_CHANNEL(0, 0, 0, 0);
|
||||
const struct iio_chan_spec ad7194_chan_temp = AD719x_TEMP_CHANNEL(0, 0);
|
||||
const struct iio_chan_spec ad7194_chan_timestamp = IIO_CHAN_SOFT_TIMESTAMP(0);
|
||||
unsigned int num_channels, index = 0;
|
||||
u32 ain[2];
|
||||
int ret;
|
||||
|
||||
num_channels = device_get_child_node_count(dev);
|
||||
if (num_channels > AD7194_CH_MAX_NR)
|
||||
return dev_err_probe(dev, -EINVAL, "Too many channels: %u\n",
|
||||
num_channels);
|
||||
|
||||
num_channels += AD7194_CH_BASE_NR;
|
||||
|
||||
ad7194_channels = devm_kcalloc(dev, num_channels,
|
||||
sizeof(*ad7194_channels), GFP_KERNEL);
|
||||
if (!ad7194_channels)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->channels = ad7194_channels;
|
||||
indio_dev->num_channels = num_channels;
|
||||
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
ret = fwnode_property_read_u32_array(child, "diff-channels",
|
||||
ain, ARRAY_SIZE(ain));
|
||||
if (ret == 0) {
|
||||
if (!ad7194_validate_ain_channel(dev, ain[0]))
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid AIN channel: %u\n",
|
||||
ain[0]);
|
||||
|
||||
if (!ad7194_validate_ain_channel(dev, ain[1]))
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid AIN channel: %u\n",
|
||||
ain[1]);
|
||||
|
||||
*ad7194_channels = ad7194_chan_diff;
|
||||
ad7194_channels->scan_index = index++;
|
||||
ad7194_channels->channel = ain[0];
|
||||
ad7194_channels->channel2 = ain[1];
|
||||
ad7194_channels->address = AD7194_DIFF_CH(ain[0], ain[1]);
|
||||
} else {
|
||||
ret = fwnode_property_read_u32(child, "single-channel",
|
||||
&ain[0]);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Missing channel property\n");
|
||||
|
||||
if (!ad7194_validate_ain_channel(dev, ain[0]))
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid AIN channel: %u\n",
|
||||
ain[0]);
|
||||
|
||||
*ad7194_channels = ad7194_chan;
|
||||
ad7194_channels->scan_index = index++;
|
||||
ad7194_channels->channel = ain[0];
|
||||
ad7194_channels->address = AD7194_CH(ain[0]);
|
||||
}
|
||||
ad7194_channels++;
|
||||
}
|
||||
|
||||
*ad7194_channels = ad7194_chan_temp;
|
||||
ad7194_channels->scan_index = index++;
|
||||
ad7194_channels->address = AD7194_CH_TEMP;
|
||||
ad7194_channels++;
|
||||
|
||||
*ad7194_channels = ad7194_chan_timestamp;
|
||||
ad7194_channels->scan_index = index;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct ad7192_chip_info ad7192_chip_info_tbl[] = {
|
||||
[ID_AD7190] = {
|
||||
.chip_id = CHIPID_AD7190,
|
||||
.name = "ad7190",
|
||||
.channels = ad7192_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7192_channels),
|
||||
.sigma_delta_info = &ad7192_sigma_delta_info,
|
||||
.info = &ad7192_info,
|
||||
},
|
||||
[ID_AD7192] = {
|
||||
@@ -1032,6 +1161,7 @@ static const struct ad7192_chip_info ad7192_chip_info_tbl[] = {
|
||||
.name = "ad7192",
|
||||
.channels = ad7192_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7192_channels),
|
||||
.sigma_delta_info = &ad7192_sigma_delta_info,
|
||||
.info = &ad7192_info,
|
||||
},
|
||||
[ID_AD7193] = {
|
||||
@@ -1039,13 +1169,22 @@ static const struct ad7192_chip_info ad7192_chip_info_tbl[] = {
|
||||
.name = "ad7193",
|
||||
.channels = ad7193_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7193_channels),
|
||||
.sigma_delta_info = &ad7192_sigma_delta_info,
|
||||
.info = &ad7192_info,
|
||||
},
|
||||
[ID_AD7194] = {
|
||||
.chip_id = CHIPID_AD7194,
|
||||
.name = "ad7194",
|
||||
.info = &ad7194_info,
|
||||
.sigma_delta_info = &ad7194_sigma_delta_info,
|
||||
.parse_channels = ad7194_parse_channels,
|
||||
},
|
||||
[ID_AD7195] = {
|
||||
.chip_id = CHIPID_AD7195,
|
||||
.name = "ad7195",
|
||||
.channels = ad7192_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7192_channels),
|
||||
.sigma_delta_info = &ad7192_sigma_delta_info,
|
||||
.info = &ad7195_info,
|
||||
},
|
||||
};
|
||||
@@ -1059,6 +1198,7 @@ static int ad7192_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7192_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
struct regulator *aincom;
|
||||
int ret;
|
||||
|
||||
if (!spi->irq) {
|
||||
@@ -1074,6 +1214,35 @@ static int ad7192_probe(struct spi_device *spi)
|
||||
|
||||
mutex_init(&st->lock);
|
||||
|
||||
/*
|
||||
* Regulator aincom is optional to maintain compatibility with older DT.
|
||||
* Newer firmware should provide a zero volt fixed supply if wired to
|
||||
* ground.
|
||||
*/
|
||||
aincom = devm_regulator_get_optional(&spi->dev, "aincom");
|
||||
if (IS_ERR(aincom)) {
|
||||
if (PTR_ERR(aincom) != -ENODEV)
|
||||
return dev_err_probe(&spi->dev, PTR_ERR(aincom),
|
||||
"Failed to get AINCOM supply\n");
|
||||
|
||||
st->aincom_mv = 0;
|
||||
} else {
|
||||
ret = regulator_enable(aincom);
|
||||
if (ret)
|
||||
return dev_err_probe(&spi->dev, ret,
|
||||
"Failed to enable specified AINCOM supply\n");
|
||||
|
||||
ret = devm_add_action_or_reset(&spi->dev, ad7192_reg_disable, aincom);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(aincom);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(&spi->dev, ret,
|
||||
"Device tree error, AINCOM voltage undefined\n");
|
||||
st->aincom_mv = ret / MILLI;
|
||||
}
|
||||
|
||||
st->avdd = devm_regulator_get(&spi->dev, "avdd");
|
||||
if (IS_ERR(st->avdd))
|
||||
return PTR_ERR(st->avdd);
|
||||
@@ -1122,11 +1291,17 @@ static int ad7192_probe(struct spi_device *spi)
|
||||
st->chip_info = spi_get_device_match_data(spi);
|
||||
indio_dev->name = st->chip_info->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
indio_dev->info = st->chip_info->info;
|
||||
if (st->chip_info->parse_channels) {
|
||||
ret = st->chip_info->parse_channels(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
}
|
||||
|
||||
ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info);
|
||||
ret = ad_sd_init(&st->sd, indio_dev, spi, st->chip_info->sigma_delta_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -1163,6 +1338,7 @@ static const struct of_device_id ad7192_of_match[] = {
|
||||
{ .compatible = "adi,ad7190", .data = &ad7192_chip_info_tbl[ID_AD7190] },
|
||||
{ .compatible = "adi,ad7192", .data = &ad7192_chip_info_tbl[ID_AD7192] },
|
||||
{ .compatible = "adi,ad7193", .data = &ad7192_chip_info_tbl[ID_AD7193] },
|
||||
{ .compatible = "adi,ad7194", .data = &ad7192_chip_info_tbl[ID_AD7194] },
|
||||
{ .compatible = "adi,ad7195", .data = &ad7192_chip_info_tbl[ID_AD7195] },
|
||||
{}
|
||||
};
|
||||
@@ -1172,6 +1348,7 @@ static const struct spi_device_id ad7192_ids[] = {
|
||||
{ "ad7190", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7190] },
|
||||
{ "ad7192", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7192] },
|
||||
{ "ad7193", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7193] },
|
||||
{ "ad7194", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7194] },
|
||||
{ "ad7195", (kernel_ulong_t)&ad7192_chip_info_tbl[ID_AD7195] },
|
||||
{}
|
||||
};
|
||||
@@ -1188,6 +1365,6 @@ static struct spi_driver ad7192_driver = {
|
||||
module_spi_driver(ad7192_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7190, AD7192, AD7193, AD7195 ADC");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7192 and similar ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
|
||||
|
||||
@@ -536,7 +536,7 @@ static int ad7291_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad7291_id[] = {
|
||||
{ "ad7291", 0 },
|
||||
{ "ad7291" },
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,833 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Analog Devices AD738x Simultaneous Sampling SAR ADCs
|
||||
*
|
||||
* Copyright 2017 Analog Devices Inc.
|
||||
* Copyright 2024 BayLibre, SAS
|
||||
*
|
||||
* Datasheets of supported parts:
|
||||
* ad7380/1 : https://www.analog.com/media/en/technical-documentation/data-sheets/AD7380-7381.pdf
|
||||
* ad7383/4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7383-7384.pdf
|
||||
* ad7380-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7380-4.pdf
|
||||
* ad7381-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7381-4.pdf
|
||||
* ad7383/4-4 : https://www.analog.com/media/en/technical-documentation/data-sheets/ad7383-4-ad7384-4.pdf
|
||||
*/
|
||||
|
||||
#include <linux/align.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define MAX_NUM_CHANNELS 4
|
||||
/* 2.5V internal reference voltage */
|
||||
#define AD7380_INTERNAL_REF_MV 2500
|
||||
|
||||
/* reading and writing registers is more reliable at lower than max speed */
|
||||
#define AD7380_REG_WR_SPEED_HZ 10000000
|
||||
|
||||
#define AD7380_REG_WR BIT(15)
|
||||
#define AD7380_REG_REGADDR GENMASK(14, 12)
|
||||
#define AD7380_REG_DATA GENMASK(11, 0)
|
||||
|
||||
#define AD7380_REG_ADDR_NOP 0x0
|
||||
#define AD7380_REG_ADDR_CONFIG1 0x1
|
||||
#define AD7380_REG_ADDR_CONFIG2 0x2
|
||||
#define AD7380_REG_ADDR_ALERT 0x3
|
||||
#define AD7380_REG_ADDR_ALERT_LOW_TH 0x4
|
||||
#define AD7380_REG_ADDR_ALERT_HIGH_TH 0x5
|
||||
|
||||
#define AD7380_CONFIG1_OS_MODE BIT(9)
|
||||
#define AD7380_CONFIG1_OSR GENMASK(8, 6)
|
||||
#define AD7380_CONFIG1_CRC_W BIT(5)
|
||||
#define AD7380_CONFIG1_CRC_R BIT(4)
|
||||
#define AD7380_CONFIG1_ALERTEN BIT(3)
|
||||
#define AD7380_CONFIG1_RES BIT(2)
|
||||
#define AD7380_CONFIG1_REFSEL BIT(1)
|
||||
#define AD7380_CONFIG1_PMODE BIT(0)
|
||||
|
||||
#define AD7380_CONFIG2_SDO2 GENMASK(9, 8)
|
||||
#define AD7380_CONFIG2_SDO BIT(8)
|
||||
#define AD7380_CONFIG2_RESET GENMASK(7, 0)
|
||||
|
||||
#define AD7380_CONFIG2_RESET_SOFT 0x3C
|
||||
#define AD7380_CONFIG2_RESET_HARD 0xFF
|
||||
|
||||
#define AD7380_ALERT_LOW_TH GENMASK(11, 0)
|
||||
#define AD7380_ALERT_HIGH_TH GENMASK(11, 0)
|
||||
|
||||
#define T_CONVERT_NS 190 /* conversion time */
|
||||
#define T_CONVERT_0_NS 10 /* 1st conversion start time (oversampling) */
|
||||
#define T_CONVERT_X_NS 500 /* xth conversion start time (oversampling) */
|
||||
|
||||
struct ad7380_timing_specs {
|
||||
const unsigned int t_csh_ns; /* CS minimum high time */
|
||||
};
|
||||
|
||||
struct ad7380_chip_info {
|
||||
const char *name;
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
const char * const *vcm_supplies;
|
||||
unsigned int num_vcm_supplies;
|
||||
const unsigned long *available_scan_masks;
|
||||
const struct ad7380_timing_specs *timing_specs;
|
||||
};
|
||||
|
||||
enum {
|
||||
AD7380_SCAN_TYPE_NORMAL,
|
||||
AD7380_SCAN_TYPE_RESOLUTION_BOOST,
|
||||
};
|
||||
|
||||
/* Extended scan types for 14-bit chips. */
|
||||
static const struct iio_scan_type ad7380_scan_type_14[] = {
|
||||
[AD7380_SCAN_TYPE_NORMAL] = {
|
||||
.sign = 's',
|
||||
.realbits = 14,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_CPU
|
||||
},
|
||||
[AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_CPU
|
||||
},
|
||||
};
|
||||
|
||||
/* Extended scan types for 16-bit chips. */
|
||||
static const struct iio_scan_type ad7380_scan_type_16[] = {
|
||||
[AD7380_SCAN_TYPE_NORMAL] = {
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_CPU
|
||||
},
|
||||
[AD7380_SCAN_TYPE_RESOLUTION_BOOST] = {
|
||||
.sign = 's',
|
||||
.realbits = 18,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_CPU
|
||||
},
|
||||
};
|
||||
|
||||
#define AD7380_CHANNEL(index, bits, diff) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
((diff) ? 0 : BIT(IIO_CHAN_INFO_OFFSET)), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
|
||||
.info_mask_shared_by_type_available = \
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
|
||||
.indexed = 1, \
|
||||
.differential = (diff), \
|
||||
.channel = (diff) ? (2 * (index)) : (index), \
|
||||
.channel2 = (diff) ? (2 * (index) + 1) : 0, \
|
||||
.scan_index = (index), \
|
||||
.has_ext_scan_type = 1, \
|
||||
.ext_scan_type = ad7380_scan_type_##bits, \
|
||||
.num_ext_scan_type = ARRAY_SIZE(ad7380_scan_type_##bits),\
|
||||
}
|
||||
|
||||
#define DEFINE_AD7380_2_CHANNEL(name, bits, diff) \
|
||||
static const struct iio_chan_spec name[] = { \
|
||||
AD7380_CHANNEL(0, bits, diff), \
|
||||
AD7380_CHANNEL(1, bits, diff), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2), \
|
||||
}
|
||||
|
||||
#define DEFINE_AD7380_4_CHANNEL(name, bits, diff) \
|
||||
static const struct iio_chan_spec name[] = { \
|
||||
AD7380_CHANNEL(0, bits, diff), \
|
||||
AD7380_CHANNEL(1, bits, diff), \
|
||||
AD7380_CHANNEL(2, bits, diff), \
|
||||
AD7380_CHANNEL(3, bits, diff), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4), \
|
||||
}
|
||||
|
||||
/* fully differential */
|
||||
DEFINE_AD7380_2_CHANNEL(ad7380_channels, 16, 1);
|
||||
DEFINE_AD7380_2_CHANNEL(ad7381_channels, 14, 1);
|
||||
DEFINE_AD7380_4_CHANNEL(ad7380_4_channels, 16, 1);
|
||||
DEFINE_AD7380_4_CHANNEL(ad7381_4_channels, 14, 1);
|
||||
/* pseudo differential */
|
||||
DEFINE_AD7380_2_CHANNEL(ad7383_channels, 16, 0);
|
||||
DEFINE_AD7380_2_CHANNEL(ad7384_channels, 14, 0);
|
||||
DEFINE_AD7380_4_CHANNEL(ad7383_4_channels, 16, 0);
|
||||
DEFINE_AD7380_4_CHANNEL(ad7384_4_channels, 14, 0);
|
||||
|
||||
static const char * const ad7380_2_channel_vcm_supplies[] = {
|
||||
"aina", "ainb",
|
||||
};
|
||||
|
||||
static const char * const ad7380_4_channel_vcm_supplies[] = {
|
||||
"aina", "ainb", "ainc", "aind",
|
||||
};
|
||||
|
||||
/* Since this is simultaneous sampling, we don't allow individual channels. */
|
||||
static const unsigned long ad7380_2_channel_scan_masks[] = {
|
||||
GENMASK(1, 0),
|
||||
0
|
||||
};
|
||||
|
||||
static const unsigned long ad7380_4_channel_scan_masks[] = {
|
||||
GENMASK(3, 0),
|
||||
0
|
||||
};
|
||||
|
||||
static const struct ad7380_timing_specs ad7380_timing = {
|
||||
.t_csh_ns = 10,
|
||||
};
|
||||
|
||||
static const struct ad7380_timing_specs ad7380_4_timing = {
|
||||
.t_csh_ns = 20,
|
||||
};
|
||||
|
||||
/*
|
||||
* Available oversampling ratios. The indices correspond with the bit value
|
||||
* expected by the chip. The available ratios depend on the averaging mode,
|
||||
* only normal averaging is supported for now.
|
||||
*/
|
||||
static const int ad7380_oversampling_ratios[] = {
|
||||
1, 2, 4, 8, 16, 32,
|
||||
};
|
||||
|
||||
static const struct ad7380_chip_info ad7380_chip_info = {
|
||||
.name = "ad7380",
|
||||
.channels = ad7380_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7380_channels),
|
||||
.available_scan_masks = ad7380_2_channel_scan_masks,
|
||||
.timing_specs = &ad7380_timing,
|
||||
};
|
||||
|
||||
static const struct ad7380_chip_info ad7381_chip_info = {
|
||||
.name = "ad7381",
|
||||
.channels = ad7381_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7381_channels),
|
||||
.available_scan_masks = ad7380_2_channel_scan_masks,
|
||||
.timing_specs = &ad7380_timing,
|
||||
};
|
||||
|
||||
static const struct ad7380_chip_info ad7383_chip_info = {
|
||||
.name = "ad7383",
|
||||
.channels = ad7383_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7383_channels),
|
||||
.vcm_supplies = ad7380_2_channel_vcm_supplies,
|
||||
.num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies),
|
||||
.available_scan_masks = ad7380_2_channel_scan_masks,
|
||||
.timing_specs = &ad7380_timing,
|
||||
};
|
||||
|
||||
static const struct ad7380_chip_info ad7384_chip_info = {
|
||||
.name = "ad7384",
|
||||
.channels = ad7384_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7384_channels),
|
||||
.vcm_supplies = ad7380_2_channel_vcm_supplies,
|
||||
.num_vcm_supplies = ARRAY_SIZE(ad7380_2_channel_vcm_supplies),
|
||||
.available_scan_masks = ad7380_2_channel_scan_masks,
|
||||
.timing_specs = &ad7380_timing,
|
||||
};
|
||||
|
||||
static const struct ad7380_chip_info ad7380_4_chip_info = {
|
||||
.name = "ad7380-4",
|
||||
.channels = ad7380_4_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7380_4_channels),
|
||||
.available_scan_masks = ad7380_4_channel_scan_masks,
|
||||
.timing_specs = &ad7380_4_timing,
|
||||
};
|
||||
|
||||
static const struct ad7380_chip_info ad7381_4_chip_info = {
|
||||
.name = "ad7381-4",
|
||||
.channels = ad7381_4_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7381_4_channels),
|
||||
.available_scan_masks = ad7380_4_channel_scan_masks,
|
||||
.timing_specs = &ad7380_4_timing,
|
||||
};
|
||||
|
||||
static const struct ad7380_chip_info ad7383_4_chip_info = {
|
||||
.name = "ad7383-4",
|
||||
.channels = ad7383_4_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7383_4_channels),
|
||||
.vcm_supplies = ad7380_4_channel_vcm_supplies,
|
||||
.num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies),
|
||||
.available_scan_masks = ad7380_4_channel_scan_masks,
|
||||
.timing_specs = &ad7380_4_timing,
|
||||
};
|
||||
|
||||
static const struct ad7380_chip_info ad7384_4_chip_info = {
|
||||
.name = "ad7384-4",
|
||||
.channels = ad7384_4_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7384_4_channels),
|
||||
.vcm_supplies = ad7380_4_channel_vcm_supplies,
|
||||
.num_vcm_supplies = ARRAY_SIZE(ad7380_4_channel_vcm_supplies),
|
||||
.available_scan_masks = ad7380_4_channel_scan_masks,
|
||||
.timing_specs = &ad7380_4_timing,
|
||||
};
|
||||
|
||||
struct ad7380_state {
|
||||
const struct ad7380_chip_info *chip_info;
|
||||
struct spi_device *spi;
|
||||
struct regmap *regmap;
|
||||
unsigned int oversampling_ratio;
|
||||
bool resolution_boost_enabled;
|
||||
unsigned int vref_mv;
|
||||
unsigned int vcm_mv[MAX_NUM_CHANNELS];
|
||||
/* xfers, message an buffer for reading sample data */
|
||||
struct spi_transfer xfer[2];
|
||||
struct spi_message msg;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the transfer buffers
|
||||
* to live in their own cache lines.
|
||||
*
|
||||
* Make the buffer large enough for MAX_NUM_CHANNELS 32-bit samples and
|
||||
* one 64-bit aligned 64-bit timestamp.
|
||||
*/
|
||||
u8 scan_data[ALIGN(MAX_NUM_CHANNELS * sizeof(u32), sizeof(s64))
|
||||
+ sizeof(s64)] __aligned(IIO_DMA_MINALIGN);
|
||||
/* buffers for reading/writing registers */
|
||||
u16 tx;
|
||||
u16 rx;
|
||||
};
|
||||
|
||||
static int ad7380_regmap_reg_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct ad7380_state *st = context;
|
||||
struct spi_transfer xfer = {
|
||||
.speed_hz = AD7380_REG_WR_SPEED_HZ,
|
||||
.bits_per_word = 16,
|
||||
.len = 2,
|
||||
.tx_buf = &st->tx,
|
||||
};
|
||||
|
||||
st->tx = FIELD_PREP(AD7380_REG_WR, 1) |
|
||||
FIELD_PREP(AD7380_REG_REGADDR, reg) |
|
||||
FIELD_PREP(AD7380_REG_DATA, val);
|
||||
|
||||
return spi_sync_transfer(st->spi, &xfer, 1);
|
||||
}
|
||||
|
||||
static int ad7380_regmap_reg_read(void *context, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct ad7380_state *st = context;
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.speed_hz = AD7380_REG_WR_SPEED_HZ,
|
||||
.bits_per_word = 16,
|
||||
.len = 2,
|
||||
.tx_buf = &st->tx,
|
||||
.cs_change = 1,
|
||||
.cs_change_delay = {
|
||||
.value = st->chip_info->timing_specs->t_csh_ns,
|
||||
.unit = SPI_DELAY_UNIT_NSECS,
|
||||
},
|
||||
}, {
|
||||
.speed_hz = AD7380_REG_WR_SPEED_HZ,
|
||||
.bits_per_word = 16,
|
||||
.len = 2,
|
||||
.rx_buf = &st->rx,
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
|
||||
st->tx = FIELD_PREP(AD7380_REG_WR, 0) |
|
||||
FIELD_PREP(AD7380_REG_REGADDR, reg) |
|
||||
FIELD_PREP(AD7380_REG_DATA, 0);
|
||||
|
||||
ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = FIELD_GET(AD7380_REG_DATA, st->rx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_config ad7380_regmap_config = {
|
||||
.reg_bits = 3,
|
||||
.val_bits = 12,
|
||||
.reg_read = ad7380_regmap_reg_read,
|
||||
.reg_write = ad7380_regmap_reg_write,
|
||||
.max_register = AD7380_REG_ADDR_ALERT_HIGH_TH,
|
||||
.can_sleep = true,
|
||||
};
|
||||
|
||||
static int ad7380_debugfs_reg_access(struct iio_dev *indio_dev, u32 reg,
|
||||
u32 writeval, u32 *readval)
|
||||
{
|
||||
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
||||
struct ad7380_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (readval)
|
||||
return regmap_read(st->regmap, reg, readval);
|
||||
else
|
||||
return regmap_write(st->regmap, reg, writeval);
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
|
||||
/**
|
||||
* ad7380_update_xfers - update the SPI transfers base on the current scan type
|
||||
* @st: device instance specific state
|
||||
* @scan_type: current scan type
|
||||
*/
|
||||
static void ad7380_update_xfers(struct ad7380_state *st,
|
||||
const struct iio_scan_type *scan_type)
|
||||
{
|
||||
/*
|
||||
* First xfer only triggers conversion and has to be long enough for
|
||||
* all conversions to complete, which can be multiple conversion in the
|
||||
* case of oversampling. Technically T_CONVERT_X_NS is lower for some
|
||||
* chips, but we use the maximum value for simplicity for now.
|
||||
*/
|
||||
if (st->oversampling_ratio > 1)
|
||||
st->xfer[0].delay.value = T_CONVERT_0_NS + T_CONVERT_X_NS *
|
||||
(st->oversampling_ratio - 1);
|
||||
else
|
||||
st->xfer[0].delay.value = T_CONVERT_NS;
|
||||
|
||||
st->xfer[0].delay.unit = SPI_DELAY_UNIT_NSECS;
|
||||
|
||||
/*
|
||||
* Second xfer reads all channels. Data size depends on if resolution
|
||||
* boost is enabled or not.
|
||||
*/
|
||||
st->xfer[1].bits_per_word = scan_type->realbits;
|
||||
st->xfer[1].len = BITS_TO_BYTES(scan_type->storagebits) *
|
||||
(st->chip_info->num_channels - 1);
|
||||
}
|
||||
|
||||
static int ad7380_triggered_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7380_state *st = iio_priv(indio_dev);
|
||||
const struct iio_scan_type *scan_type;
|
||||
|
||||
/*
|
||||
* Currently, we always read all channels at the same time. The scan_type
|
||||
* is the same for all channels, so we just pass the first channel.
|
||||
*/
|
||||
scan_type = iio_get_current_scan_type(indio_dev, &indio_dev->channels[0]);
|
||||
if (IS_ERR(scan_type))
|
||||
return PTR_ERR(scan_type);
|
||||
|
||||
ad7380_update_xfers(st, scan_type);
|
||||
|
||||
return spi_optimize_message(st->spi, &st->msg);
|
||||
}
|
||||
|
||||
static int ad7380_triggered_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7380_state *st = iio_priv(indio_dev);
|
||||
|
||||
spi_unoptimize_message(&st->msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops ad7380_buffer_setup_ops = {
|
||||
.preenable = ad7380_triggered_buffer_preenable,
|
||||
.postdisable = ad7380_triggered_buffer_postdisable,
|
||||
};
|
||||
|
||||
static irqreturn_t ad7380_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7380_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = spi_sync(st->spi, &st->msg);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &st->scan_data,
|
||||
pf->timestamp);
|
||||
|
||||
out:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ad7380_read_direct(struct ad7380_state *st, unsigned int scan_index,
|
||||
const struct iio_scan_type *scan_type, int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ad7380_update_xfers(st, scan_type);
|
||||
|
||||
ret = spi_sync(st->spi, &st->msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (scan_type->storagebits > 16)
|
||||
*val = sign_extend32(*(u32 *)(st->scan_data + 4 * scan_index),
|
||||
scan_type->realbits - 1);
|
||||
else
|
||||
*val = sign_extend32(*(u16 *)(st->scan_data + 2 * scan_index),
|
||||
scan_type->realbits - 1);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int ad7380_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long info)
|
||||
{
|
||||
struct ad7380_state *st = iio_priv(indio_dev);
|
||||
const struct iio_scan_type *scan_type;
|
||||
|
||||
scan_type = iio_get_current_scan_type(indio_dev, chan);
|
||||
|
||||
if (IS_ERR(scan_type))
|
||||
return PTR_ERR(scan_type);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
||||
return ad7380_read_direct(st, chan->scan_index,
|
||||
scan_type, val);
|
||||
}
|
||||
unreachable();
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/*
|
||||
* According to the datasheet, the LSB size is:
|
||||
* * (2 × VREF) / 2^N, for differential chips
|
||||
* * VREF / 2^N, for pseudo-differential chips
|
||||
* where N is the ADC resolution (i.e realbits)
|
||||
*/
|
||||
*val = st->vref_mv;
|
||||
*val2 = scan_type->realbits - chan->differential;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/*
|
||||
* According to IIO ABI, offset is applied before scale,
|
||||
* so offset is: vcm_mv / scale
|
||||
*/
|
||||
*val = st->vcm_mv[chan->channel] * (1 << scan_type->realbits)
|
||||
/ st->vref_mv;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
*val = st->oversampling_ratio;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad7380_read_avail(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals, int *type, int *length,
|
||||
long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
*vals = ad7380_oversampling_ratios;
|
||||
*length = ARRAY_SIZE(ad7380_oversampling_ratios);
|
||||
*type = IIO_VAL_INT;
|
||||
|
||||
return IIO_AVAIL_LIST;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ad7380_osr_to_regval - convert ratio to OSR register value
|
||||
* @ratio: ratio to check
|
||||
*
|
||||
* Check if ratio is present in the list of available ratios and return the
|
||||
* corresponding value that needs to be written to the register to select that
|
||||
* ratio.
|
||||
*
|
||||
* Returns: register value (0 to 7) or -EINVAL if there is not an exact match
|
||||
*/
|
||||
static int ad7380_osr_to_regval(int ratio)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ad7380_oversampling_ratios); i++) {
|
||||
if (ratio == ad7380_oversampling_ratios[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad7380_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct ad7380_state *st = iio_priv(indio_dev);
|
||||
int ret, osr, boost;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
osr = ad7380_osr_to_regval(val);
|
||||
if (osr < 0)
|
||||
return osr;
|
||||
|
||||
/* always enable resolution boost when oversampling is enabled */
|
||||
boost = osr > 0 ? 1 : 0;
|
||||
|
||||
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
||||
ret = regmap_update_bits(st->regmap,
|
||||
AD7380_REG_ADDR_CONFIG1,
|
||||
AD7380_CONFIG1_OSR | AD7380_CONFIG1_RES,
|
||||
FIELD_PREP(AD7380_CONFIG1_OSR, osr) |
|
||||
FIELD_PREP(AD7380_CONFIG1_RES, boost));
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->oversampling_ratio = val;
|
||||
st->resolution_boost_enabled = boost;
|
||||
|
||||
/*
|
||||
* Perform a soft reset. This will flush the oversampling
|
||||
* block and FIFO but will maintain the content of the
|
||||
* configurable registers.
|
||||
*/
|
||||
return regmap_update_bits(st->regmap,
|
||||
AD7380_REG_ADDR_CONFIG2,
|
||||
AD7380_CONFIG2_RESET,
|
||||
FIELD_PREP(AD7380_CONFIG2_RESET,
|
||||
AD7380_CONFIG2_RESET_SOFT));
|
||||
}
|
||||
unreachable();
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad7380_get_current_scan_type(const struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad7380_state *st = iio_priv(indio_dev);
|
||||
|
||||
return st->resolution_boost_enabled ? AD7380_SCAN_TYPE_RESOLUTION_BOOST
|
||||
: AD7380_SCAN_TYPE_NORMAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad7380_info = {
|
||||
.read_raw = &ad7380_read_raw,
|
||||
.read_avail = &ad7380_read_avail,
|
||||
.write_raw = &ad7380_write_raw,
|
||||
.get_current_scan_type = &ad7380_get_current_scan_type,
|
||||
.debugfs_reg_access = &ad7380_debugfs_reg_access,
|
||||
};
|
||||
|
||||
static int ad7380_init(struct ad7380_state *st, struct regulator *vref)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* perform hard reset */
|
||||
ret = regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG2,
|
||||
AD7380_CONFIG2_RESET,
|
||||
FIELD_PREP(AD7380_CONFIG2_RESET,
|
||||
AD7380_CONFIG2_RESET_HARD));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* select internal or external reference voltage */
|
||||
ret = regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG1,
|
||||
AD7380_CONFIG1_REFSEL,
|
||||
FIELD_PREP(AD7380_CONFIG1_REFSEL,
|
||||
vref ? 1 : 0));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* This is the default value after reset. */
|
||||
st->oversampling_ratio = 1;
|
||||
|
||||
/* SPI 1-wire mode */
|
||||
return regmap_update_bits(st->regmap, AD7380_REG_ADDR_CONFIG2,
|
||||
AD7380_CONFIG2_SDO,
|
||||
FIELD_PREP(AD7380_CONFIG2_SDO, 1));
|
||||
}
|
||||
|
||||
static void ad7380_regulator_disable(void *p)
|
||||
{
|
||||
regulator_disable(p);
|
||||
}
|
||||
|
||||
static int ad7380_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad7380_state *st;
|
||||
struct regulator *vref;
|
||||
int ret, i;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
st->spi = spi;
|
||||
st->chip_info = spi_get_device_match_data(spi);
|
||||
if (!st->chip_info)
|
||||
return dev_err_probe(&spi->dev, -EINVAL, "missing match data\n");
|
||||
|
||||
vref = devm_regulator_get_optional(&spi->dev, "refio");
|
||||
if (IS_ERR(vref)) {
|
||||
if (PTR_ERR(vref) != -ENODEV)
|
||||
return dev_err_probe(&spi->dev, PTR_ERR(vref),
|
||||
"Failed to get refio regulator\n");
|
||||
|
||||
vref = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is no REFIO supply, then it means that we are using
|
||||
* the internal 2.5V reference, otherwise REFIO is reference voltage.
|
||||
*/
|
||||
if (vref) {
|
||||
ret = regulator_enable(vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(&spi->dev,
|
||||
ad7380_regulator_disable, vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(vref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st->vref_mv = ret / 1000;
|
||||
} else {
|
||||
st->vref_mv = AD7380_INTERNAL_REF_MV;
|
||||
}
|
||||
|
||||
if (st->chip_info->num_vcm_supplies > ARRAY_SIZE(st->vcm_mv))
|
||||
return dev_err_probe(&spi->dev, -EINVAL,
|
||||
"invalid number of VCM supplies\n");
|
||||
|
||||
/*
|
||||
* pseudo-differential chips have common mode supplies for the negative
|
||||
* input pin.
|
||||
*/
|
||||
for (i = 0; i < st->chip_info->num_vcm_supplies; i++) {
|
||||
struct regulator *vcm;
|
||||
|
||||
vcm = devm_regulator_get(&spi->dev,
|
||||
st->chip_info->vcm_supplies[i]);
|
||||
if (IS_ERR(vcm))
|
||||
return dev_err_probe(&spi->dev, PTR_ERR(vcm),
|
||||
"Failed to get %s regulator\n",
|
||||
st->chip_info->vcm_supplies[i]);
|
||||
|
||||
ret = regulator_enable(vcm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(&spi->dev,
|
||||
ad7380_regulator_disable, vcm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(vcm);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st->vcm_mv[i] = ret / 1000;
|
||||
}
|
||||
|
||||
st->regmap = devm_regmap_init(&spi->dev, NULL, st, &ad7380_regmap_config);
|
||||
if (IS_ERR(st->regmap))
|
||||
return dev_err_probe(&spi->dev, PTR_ERR(st->regmap),
|
||||
"failed to allocate register map\n");
|
||||
|
||||
/*
|
||||
* Setting up a low latency read for getting sample data. Used for both
|
||||
* direct read an triggered buffer. Additional fields will be set up in
|
||||
* ad7380_update_xfers() based on the current state of the driver at the
|
||||
* time of the read.
|
||||
*/
|
||||
|
||||
/* toggle CS (no data xfer) to trigger a conversion */
|
||||
st->xfer[0].cs_change = 1;
|
||||
st->xfer[0].cs_change_delay.value = st->chip_info->timing_specs->t_csh_ns;
|
||||
st->xfer[0].cs_change_delay.unit = SPI_DELAY_UNIT_NSECS;
|
||||
|
||||
/* then do a second xfer to read the data */
|
||||
st->xfer[1].rx_buf = st->scan_data;
|
||||
|
||||
spi_message_init_with_transfers(&st->msg, st->xfer, ARRAY_SIZE(st->xfer));
|
||||
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
indio_dev->name = st->chip_info->name;
|
||||
indio_dev->info = &ad7380_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->available_scan_masks = st->chip_info->available_scan_masks;
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
|
||||
iio_pollfunc_store_time,
|
||||
ad7380_trigger_handler,
|
||||
&ad7380_buffer_setup_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad7380_init(st, vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id ad7380_of_match_table[] = {
|
||||
{ .compatible = "adi,ad7380", .data = &ad7380_chip_info },
|
||||
{ .compatible = "adi,ad7381", .data = &ad7381_chip_info },
|
||||
{ .compatible = "adi,ad7383", .data = &ad7383_chip_info },
|
||||
{ .compatible = "adi,ad7384", .data = &ad7384_chip_info },
|
||||
{ .compatible = "adi,ad7380-4", .data = &ad7380_4_chip_info },
|
||||
{ .compatible = "adi,ad7381-4", .data = &ad7381_4_chip_info },
|
||||
{ .compatible = "adi,ad7383-4", .data = &ad7383_4_chip_info },
|
||||
{ .compatible = "adi,ad7384-4", .data = &ad7384_4_chip_info },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct spi_device_id ad7380_id_table[] = {
|
||||
{ "ad7380", (kernel_ulong_t)&ad7380_chip_info },
|
||||
{ "ad7381", (kernel_ulong_t)&ad7381_chip_info },
|
||||
{ "ad7383", (kernel_ulong_t)&ad7383_chip_info },
|
||||
{ "ad7384", (kernel_ulong_t)&ad7384_chip_info },
|
||||
{ "ad7380-4", (kernel_ulong_t)&ad7380_4_chip_info },
|
||||
{ "ad7381-4", (kernel_ulong_t)&ad7381_4_chip_info },
|
||||
{ "ad7383-4", (kernel_ulong_t)&ad7383_4_chip_info },
|
||||
{ "ad7384-4", (kernel_ulong_t)&ad7384_4_chip_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7380_id_table);
|
||||
|
||||
static struct spi_driver ad7380_driver = {
|
||||
.driver = {
|
||||
.name = "ad7380",
|
||||
.of_match_table = ad7380_of_match_table,
|
||||
},
|
||||
.probe = ad7380_probe,
|
||||
.id_table = ad7380_id_table,
|
||||
};
|
||||
module_spi_driver(ad7380_driver);
|
||||
|
||||
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD738x ADC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -174,17 +174,14 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad7606_scan_direct(indio_dev, chan->address);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = (short)ret;
|
||||
return IIO_VAL_INT;
|
||||
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
||||
ret = ad7606_scan_direct(indio_dev, chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = (short) ret;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
unreachable();
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (st->sw_mode_en)
|
||||
ch = chan->address;
|
||||
|
||||
@@ -259,7 +259,6 @@ static int ad7944_chain_mode_init_msg(struct device *dev, struct ad7944_adc *adc
|
||||
/**
|
||||
* ad7944_convert_and_acquire - Perform a single conversion and acquisition
|
||||
* @adc: The ADC device structure
|
||||
* @chan: The channel specification
|
||||
* Return: 0 on success, a negative error code on failure
|
||||
*
|
||||
* Perform a conversion and acquisition of a single sample using the
|
||||
@@ -268,8 +267,7 @@ static int ad7944_chain_mode_init_msg(struct device *dev, struct ad7944_adc *adc
|
||||
* Upon successful return adc->sample.raw will contain the conversion result
|
||||
* (or adc->chain_mode_buf if the device is using chain mode).
|
||||
*/
|
||||
static int ad7944_convert_and_acquire(struct ad7944_adc *adc,
|
||||
const struct iio_chan_spec *chan)
|
||||
static int ad7944_convert_and_acquire(struct ad7944_adc *adc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
@@ -291,7 +289,7 @@ static int ad7944_single_conversion(struct ad7944_adc *adc,
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad7944_convert_and_acquire(adc, chan);
|
||||
ret = ad7944_convert_and_acquire(adc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -361,7 +359,7 @@ static irqreturn_t ad7944_trigger_handler(int irq, void *p)
|
||||
struct ad7944_adc *adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = ad7944_convert_and_acquire(adc, &indio_dev->channels[0]);
|
||||
ret = ad7944_convert_and_acquire(adc);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
||||
+50
-53
@@ -107,27 +107,27 @@
|
||||
#define AD9647_MAX_TEST_POINTS 32
|
||||
|
||||
struct ad9467_chip_info {
|
||||
const char *name;
|
||||
unsigned int id;
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
const unsigned int (*scale_table)[2];
|
||||
int num_scales;
|
||||
unsigned long max_rate;
|
||||
unsigned int default_output_mode;
|
||||
unsigned int vref_mask;
|
||||
unsigned int num_lanes;
|
||||
const char *name;
|
||||
unsigned int id;
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
const unsigned int (*scale_table)[2];
|
||||
int num_scales;
|
||||
unsigned long max_rate;
|
||||
unsigned int default_output_mode;
|
||||
unsigned int vref_mask;
|
||||
unsigned int num_lanes;
|
||||
/* data clock output */
|
||||
bool has_dco;
|
||||
bool has_dco;
|
||||
};
|
||||
|
||||
struct ad9467_state {
|
||||
const struct ad9467_chip_info *info;
|
||||
struct iio_backend *back;
|
||||
struct spi_device *spi;
|
||||
struct clk *clk;
|
||||
unsigned int output_mode;
|
||||
unsigned int (*scales)[2];
|
||||
const struct ad9467_chip_info *info;
|
||||
struct iio_backend *back;
|
||||
struct spi_device *spi;
|
||||
struct clk *clk;
|
||||
unsigned int output_mode;
|
||||
unsigned int (*scales)[2];
|
||||
/*
|
||||
* Times 2 because we may also invert the signal polarity and run the
|
||||
* calibration again. For some reference on the test points (ad9265) see:
|
||||
@@ -138,12 +138,13 @@ struct ad9467_state {
|
||||
* at the io delay control section.
|
||||
*/
|
||||
DECLARE_BITMAP(calib_map, AD9647_MAX_TEST_POINTS * 2);
|
||||
struct gpio_desc *pwrdown_gpio;
|
||||
struct gpio_desc *pwrdown_gpio;
|
||||
/* ensure consistent state obtained on multiple related accesses */
|
||||
struct mutex lock;
|
||||
struct mutex lock;
|
||||
u8 buf[3] __aligned(IIO_DMA_MINALIGN);
|
||||
};
|
||||
|
||||
static int ad9467_spi_read(struct spi_device *spi, unsigned int reg)
|
||||
static int ad9467_spi_read(struct ad9467_state *st, unsigned int reg)
|
||||
{
|
||||
unsigned char tbuf[2], rbuf[1];
|
||||
int ret;
|
||||
@@ -151,7 +152,7 @@ static int ad9467_spi_read(struct spi_device *spi, unsigned int reg)
|
||||
tbuf[0] = 0x80 | (reg >> 8);
|
||||
tbuf[1] = reg & 0xFF;
|
||||
|
||||
ret = spi_write_then_read(spi,
|
||||
ret = spi_write_then_read(st->spi,
|
||||
tbuf, ARRAY_SIZE(tbuf),
|
||||
rbuf, ARRAY_SIZE(rbuf));
|
||||
|
||||
@@ -161,35 +162,32 @@ static int ad9467_spi_read(struct spi_device *spi, unsigned int reg)
|
||||
return rbuf[0];
|
||||
}
|
||||
|
||||
static int ad9467_spi_write(struct spi_device *spi, unsigned int reg,
|
||||
static int ad9467_spi_write(struct ad9467_state *st, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
unsigned char buf[3];
|
||||
st->buf[0] = reg >> 8;
|
||||
st->buf[1] = reg & 0xFF;
|
||||
st->buf[2] = val;
|
||||
|
||||
buf[0] = reg >> 8;
|
||||
buf[1] = reg & 0xFF;
|
||||
buf[2] = val;
|
||||
|
||||
return spi_write(spi, buf, ARRAY_SIZE(buf));
|
||||
return spi_write(st->spi, st->buf, ARRAY_SIZE(st->buf));
|
||||
}
|
||||
|
||||
static int ad9467_reg_access(struct iio_dev *indio_dev, unsigned int reg,
|
||||
unsigned int writeval, unsigned int *readval)
|
||||
{
|
||||
struct ad9467_state *st = iio_priv(indio_dev);
|
||||
struct spi_device *spi = st->spi;
|
||||
int ret;
|
||||
|
||||
if (!readval) {
|
||||
guard(mutex)(&st->lock);
|
||||
ret = ad9467_spi_write(spi, reg, writeval);
|
||||
ret = ad9467_spi_write(st, reg, writeval);
|
||||
if (ret)
|
||||
return ret;
|
||||
return ad9467_spi_write(spi, AN877_ADC_REG_TRANSFER,
|
||||
return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER,
|
||||
AN877_ADC_TRANSFER_SYNC);
|
||||
}
|
||||
|
||||
ret = ad9467_spi_read(spi, reg);
|
||||
ret = ad9467_spi_read(st, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*readval = ret;
|
||||
@@ -295,7 +293,7 @@ static int ad9467_get_scale(struct ad9467_state *st, int *val, int *val2)
|
||||
unsigned int i, vref_val;
|
||||
int ret;
|
||||
|
||||
ret = ad9467_spi_read(st->spi, AN877_ADC_REG_VREF);
|
||||
ret = ad9467_spi_read(st, AN877_ADC_REG_VREF);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -330,31 +328,31 @@ static int ad9467_set_scale(struct ad9467_state *st, int val, int val2)
|
||||
continue;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
ret = ad9467_spi_write(st->spi, AN877_ADC_REG_VREF,
|
||||
ret = ad9467_spi_write(st, AN877_ADC_REG_VREF,
|
||||
info->scale_table[i][1]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ad9467_spi_write(st->spi, AN877_ADC_REG_TRANSFER,
|
||||
return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER,
|
||||
AN877_ADC_TRANSFER_SYNC);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad9467_outputmode_set(struct spi_device *spi, unsigned int mode)
|
||||
static int ad9467_outputmode_set(struct ad9467_state *st, unsigned int mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad9467_spi_write(spi, AN877_ADC_REG_OUTPUT_MODE, mode);
|
||||
ret = ad9467_spi_write(st, AN877_ADC_REG_OUTPUT_MODE, mode);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ad9467_spi_write(spi, AN877_ADC_REG_TRANSFER,
|
||||
return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER,
|
||||
AN877_ADC_TRANSFER_SYNC);
|
||||
}
|
||||
|
||||
static int ad9647_calibrate_prepare(const struct ad9467_state *st)
|
||||
static int ad9647_calibrate_prepare(struct ad9467_state *st)
|
||||
{
|
||||
struct iio_backend_data_fmt data = {
|
||||
.enable = false,
|
||||
@@ -362,17 +360,17 @@ static int ad9647_calibrate_prepare(const struct ad9467_state *st)
|
||||
unsigned int c;
|
||||
int ret;
|
||||
|
||||
ret = ad9467_spi_write(st->spi, AN877_ADC_REG_TEST_IO,
|
||||
ret = ad9467_spi_write(st, AN877_ADC_REG_TEST_IO,
|
||||
AN877_ADC_TESTMODE_PN9_SEQ);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad9467_spi_write(st->spi, AN877_ADC_REG_TRANSFER,
|
||||
ret = ad9467_spi_write(st, AN877_ADC_REG_TRANSFER,
|
||||
AN877_ADC_TRANSFER_SYNC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad9467_outputmode_set(st->spi, st->info->default_output_mode);
|
||||
ret = ad9467_outputmode_set(st, st->info->default_output_mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -390,7 +388,7 @@ static int ad9647_calibrate_prepare(const struct ad9467_state *st)
|
||||
return iio_backend_chan_enable(st->back, 0);
|
||||
}
|
||||
|
||||
static int ad9647_calibrate_polarity_set(const struct ad9467_state *st,
|
||||
static int ad9647_calibrate_polarity_set(struct ad9467_state *st,
|
||||
bool invert)
|
||||
{
|
||||
enum iio_backend_sample_trigger trigger;
|
||||
@@ -401,7 +399,7 @@ static int ad9647_calibrate_polarity_set(const struct ad9467_state *st,
|
||||
if (invert)
|
||||
phase |= AN877_ADC_INVERT_DCO_CLK;
|
||||
|
||||
return ad9467_spi_write(st->spi, AN877_ADC_REG_OUTPUT_PHASE,
|
||||
return ad9467_spi_write(st, AN877_ADC_REG_OUTPUT_PHASE,
|
||||
phase);
|
||||
}
|
||||
|
||||
@@ -437,19 +435,18 @@ static unsigned int ad9467_find_optimal_point(const unsigned long *calib_map,
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static int ad9467_calibrate_apply(const struct ad9467_state *st,
|
||||
unsigned int val)
|
||||
static int ad9467_calibrate_apply(struct ad9467_state *st, unsigned int val)
|
||||
{
|
||||
unsigned int lane;
|
||||
int ret;
|
||||
|
||||
if (st->info->has_dco) {
|
||||
ret = ad9467_spi_write(st->spi, AN877_ADC_REG_OUTPUT_DELAY,
|
||||
ret = ad9467_spi_write(st, AN877_ADC_REG_OUTPUT_DELAY,
|
||||
val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ad9467_spi_write(st->spi, AN877_ADC_REG_TRANSFER,
|
||||
return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER,
|
||||
AN877_ADC_TRANSFER_SYNC);
|
||||
}
|
||||
|
||||
@@ -462,7 +459,7 @@ static int ad9467_calibrate_apply(const struct ad9467_state *st,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad9647_calibrate_stop(const struct ad9467_state *st)
|
||||
static int ad9647_calibrate_stop(struct ad9467_state *st)
|
||||
{
|
||||
struct iio_backend_data_fmt data = {
|
||||
.sign_extend = true,
|
||||
@@ -487,16 +484,16 @@ static int ad9647_calibrate_stop(const struct ad9467_state *st)
|
||||
}
|
||||
|
||||
mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
|
||||
ret = ad9467_outputmode_set(st->spi, mode);
|
||||
ret = ad9467_outputmode_set(st, mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad9467_spi_write(st->spi, AN877_ADC_REG_TEST_IO,
|
||||
ret = ad9467_spi_write(st, AN877_ADC_REG_TEST_IO,
|
||||
AN877_ADC_TESTMODE_OFF);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ad9467_spi_write(st->spi, AN877_ADC_REG_TRANSFER,
|
||||
return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER,
|
||||
AN877_ADC_TRANSFER_SYNC);
|
||||
}
|
||||
|
||||
@@ -846,7 +843,7 @@ static int ad9467_probe(struct spi_device *spi)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
id = ad9467_spi_read(spi, AN877_ADC_REG_CHIP_ID);
|
||||
id = ad9467_spi_read(st, AN877_ADC_REG_CHIP_ID);
|
||||
if (id != st->info->id) {
|
||||
dev_err(&spi->dev, "Mismatch CHIP_ID, got 0x%X, expected 0x%X\n",
|
||||
id, st->info->id);
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
#define ADI_AXI_ADC_REG_CTRL 0x0044
|
||||
#define ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK BIT(1)
|
||||
|
||||
#define ADI_AXI_ADC_REG_DRP_STATUS 0x0074
|
||||
#define ADI_AXI_ADC_DRP_LOCKED BIT(17)
|
||||
|
||||
/* ADC Channel controls */
|
||||
|
||||
#define ADI_AXI_REG_CHAN_CTRL(c) (0x0400 + (c) * 0x40)
|
||||
@@ -83,14 +86,26 @@ struct adi_axi_adc_state {
|
||||
static int axi_adc_enable(struct iio_backend *back)
|
||||
{
|
||||
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
|
||||
unsigned int __val;
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
ret = regmap_set_bits(st->regmap, ADI_AXI_REG_RSTN,
|
||||
ADI_AXI_REG_RSTN_MMCM_RSTN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fsleep(10000);
|
||||
/*
|
||||
* Make sure the DRP (Dynamic Reconfiguration Port) is locked. Not all
|
||||
* designs really use it but if they don't we still get the lock bit
|
||||
* set. So let's do it all the time so the code is generic.
|
||||
*/
|
||||
ret = regmap_read_poll_timeout(st->regmap, ADI_AXI_ADC_REG_DRP_STATUS,
|
||||
__val, __val & ADI_AXI_ADC_DRP_LOCKED,
|
||||
100, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_set_bits(st->regmap, ADI_AXI_REG_RSTN,
|
||||
ADI_AXI_REG_RSTN_RSTN | ADI_AXI_REG_RSTN_MMCM_RSTN);
|
||||
}
|
||||
@@ -99,6 +114,7 @@ static void axi_adc_disable(struct iio_backend *back)
|
||||
{
|
||||
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
regmap_write(st->regmap, ADI_AXI_REG_RSTN, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,19 @@
|
||||
#include <linux/iio/machine.h>
|
||||
#include <linux/mfd/axp20x.h>
|
||||
|
||||
#define AXP192_ADC_EN1_MASK GENMASK(7, 0)
|
||||
#define AXP192_ADC_EN2_MASK (GENMASK(3, 0) | BIT(7))
|
||||
|
||||
#define AXP20X_ADC_EN1_MASK GENMASK(7, 0)
|
||||
#define AXP20X_ADC_EN2_MASK (GENMASK(3, 2) | BIT(7))
|
||||
|
||||
#define AXP22X_ADC_EN1_MASK (GENMASK(7, 5) | BIT(0))
|
||||
|
||||
#define AXP192_GPIO30_IN_RANGE_GPIO0 BIT(0)
|
||||
#define AXP192_GPIO30_IN_RANGE_GPIO1 BIT(1)
|
||||
#define AXP192_GPIO30_IN_RANGE_GPIO2 BIT(2)
|
||||
#define AXP192_GPIO30_IN_RANGE_GPIO3 BIT(3)
|
||||
|
||||
#define AXP20X_GPIO10_IN_RANGE_GPIO0 BIT(0)
|
||||
#define AXP20X_GPIO10_IN_RANGE_GPIO1 BIT(1)
|
||||
|
||||
@@ -71,6 +79,25 @@ struct axp20x_adc_iio {
|
||||
const struct axp_data *data;
|
||||
};
|
||||
|
||||
enum axp192_adc_channel_v {
|
||||
AXP192_ACIN_V = 0,
|
||||
AXP192_VBUS_V,
|
||||
AXP192_TS_IN,
|
||||
AXP192_GPIO0_V,
|
||||
AXP192_GPIO1_V,
|
||||
AXP192_GPIO2_V,
|
||||
AXP192_GPIO3_V,
|
||||
AXP192_IPSOUT_V,
|
||||
AXP192_BATT_V,
|
||||
};
|
||||
|
||||
enum axp192_adc_channel_i {
|
||||
AXP192_ACIN_I = 0,
|
||||
AXP192_VBUS_I,
|
||||
AXP192_BATT_CHRG_I,
|
||||
AXP192_BATT_DISCHRG_I,
|
||||
};
|
||||
|
||||
enum axp20x_adc_channel_v {
|
||||
AXP20X_ACIN_V = 0,
|
||||
AXP20X_VBUS_V,
|
||||
@@ -158,6 +185,43 @@ static struct iio_map axp22x_maps[] = {
|
||||
* The only exception is for the battery. batt_v will be in_voltage6_raw and
|
||||
* charge current in_current6_raw and discharge current will be in_current7_raw.
|
||||
*/
|
||||
static const struct iio_chan_spec axp192_adc_channels[] = {
|
||||
AXP20X_ADC_CHANNEL(AXP192_ACIN_V, "acin_v", IIO_VOLTAGE,
|
||||
AXP20X_ACIN_V_ADC_H),
|
||||
AXP20X_ADC_CHANNEL(AXP192_ACIN_I, "acin_i", IIO_CURRENT,
|
||||
AXP20X_ACIN_I_ADC_H),
|
||||
AXP20X_ADC_CHANNEL(AXP192_VBUS_V, "vbus_v", IIO_VOLTAGE,
|
||||
AXP20X_VBUS_V_ADC_H),
|
||||
AXP20X_ADC_CHANNEL(AXP192_VBUS_I, "vbus_i", IIO_CURRENT,
|
||||
AXP20X_VBUS_I_ADC_H),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.address = AXP20X_TEMP_ADC_H,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.datasheet_name = "pmic_temp",
|
||||
},
|
||||
AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO0_V, "gpio0_v", IIO_VOLTAGE,
|
||||
AXP20X_GPIO0_V_ADC_H),
|
||||
AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO1_V, "gpio1_v", IIO_VOLTAGE,
|
||||
AXP20X_GPIO1_V_ADC_H),
|
||||
AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO2_V, "gpio2_v", IIO_VOLTAGE,
|
||||
AXP192_GPIO2_V_ADC_H),
|
||||
AXP20X_ADC_CHANNEL_OFFSET(AXP192_GPIO3_V, "gpio3_v", IIO_VOLTAGE,
|
||||
AXP192_GPIO3_V_ADC_H),
|
||||
AXP20X_ADC_CHANNEL(AXP192_IPSOUT_V, "ipsout_v", IIO_VOLTAGE,
|
||||
AXP20X_IPSOUT_V_HIGH_H),
|
||||
AXP20X_ADC_CHANNEL(AXP192_BATT_V, "batt_v", IIO_VOLTAGE,
|
||||
AXP20X_BATT_V_H),
|
||||
AXP20X_ADC_CHANNEL(AXP192_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT,
|
||||
AXP20X_BATT_CHRG_I_H),
|
||||
AXP20X_ADC_CHANNEL(AXP192_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT,
|
||||
AXP20X_BATT_DISCHRG_I_H),
|
||||
AXP20X_ADC_CHANNEL(AXP192_TS_IN, "ts_v", IIO_VOLTAGE,
|
||||
AXP20X_TS_IN_H),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec axp20x_adc_channels[] = {
|
||||
AXP20X_ADC_CHANNEL(AXP20X_ACIN_V, "acin_v", IIO_VOLTAGE,
|
||||
AXP20X_ACIN_V_ADC_H),
|
||||
@@ -231,6 +295,27 @@ static const struct iio_chan_spec axp813_adc_channels[] = {
|
||||
AXP288_TS_ADC_H),
|
||||
};
|
||||
|
||||
static int axp192_adc_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
struct axp20x_adc_iio *info = iio_priv(indio_dev);
|
||||
int ret, size;
|
||||
|
||||
if (chan->type == IIO_CURRENT &&
|
||||
(chan->channel == AXP192_BATT_CHRG_I ||
|
||||
chan->channel == AXP192_BATT_DISCHRG_I))
|
||||
size = 13;
|
||||
else
|
||||
size = 12;
|
||||
|
||||
ret = axp20x_read_variable_width(info->regmap, chan->address, size);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int axp20x_adc_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
@@ -283,6 +368,44 @@ static int axp813_adc_raw(struct iio_dev *indio_dev,
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int axp192_adc_scale_voltage(int channel, int *val, int *val2)
|
||||
{
|
||||
switch (channel) {
|
||||
case AXP192_ACIN_V:
|
||||
case AXP192_VBUS_V:
|
||||
*val = 1;
|
||||
*val2 = 700000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
case AXP192_GPIO0_V:
|
||||
case AXP192_GPIO1_V:
|
||||
case AXP192_GPIO2_V:
|
||||
case AXP192_GPIO3_V:
|
||||
*val = 0;
|
||||
*val2 = 500000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
case AXP192_BATT_V:
|
||||
*val = 1;
|
||||
*val2 = 100000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
case AXP192_IPSOUT_V:
|
||||
*val = 1;
|
||||
*val2 = 400000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
case AXP192_TS_IN:
|
||||
/* 0.8 mV per LSB */
|
||||
*val = 0;
|
||||
*val2 = 800000;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int axp20x_adc_scale_voltage(int channel, int *val, int *val2)
|
||||
{
|
||||
switch (channel) {
|
||||
@@ -386,6 +509,29 @@ static int axp20x_adc_scale_current(int channel, int *val, int *val2)
|
||||
}
|
||||
}
|
||||
|
||||
static int axp192_adc_scale(struct iio_chan_spec const *chan, int *val,
|
||||
int *val2)
|
||||
{
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
return axp192_adc_scale_voltage(chan->channel, val, val2);
|
||||
|
||||
case IIO_CURRENT:
|
||||
/*
|
||||
* AXP192 current channels are identical to the AXP20x,
|
||||
* therefore we can re-use the scaling function.
|
||||
*/
|
||||
return axp20x_adc_scale_current(chan->channel, val, val2);
|
||||
|
||||
case IIO_TEMP:
|
||||
*val = 100;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int axp20x_adc_scale(struct iio_chan_spec const *chan, int *val,
|
||||
int *val2)
|
||||
{
|
||||
@@ -445,6 +591,42 @@ static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val,
|
||||
}
|
||||
}
|
||||
|
||||
static int axp192_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
|
||||
int *val)
|
||||
{
|
||||
struct axp20x_adc_iio *info = iio_priv(indio_dev);
|
||||
unsigned int regval;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(info->regmap, AXP192_GPIO30_IN_RANGE, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (channel) {
|
||||
case AXP192_GPIO0_V:
|
||||
regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO0, regval);
|
||||
break;
|
||||
|
||||
case AXP192_GPIO1_V:
|
||||
regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO1, regval);
|
||||
break;
|
||||
|
||||
case AXP192_GPIO2_V:
|
||||
regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO2, regval);
|
||||
break;
|
||||
|
||||
case AXP192_GPIO3_V:
|
||||
regval = FIELD_GET(AXP192_GPIO30_IN_RANGE_GPIO3, regval);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val = regval ? 700000 : 0;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
|
||||
int *val)
|
||||
{
|
||||
@@ -473,6 +655,22 @@ static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel,
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int axp192_adc_offset(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
return axp192_adc_offset_voltage(indio_dev, chan->channel, val);
|
||||
|
||||
case IIO_TEMP:
|
||||
*val = -1447;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int axp20x_adc_offset(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val)
|
||||
{
|
||||
@@ -489,6 +687,25 @@ static int axp20x_adc_offset(struct iio_dev *indio_dev,
|
||||
}
|
||||
}
|
||||
|
||||
static int axp192_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
return axp192_adc_offset(indio_dev, chan, val);
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return axp192_adc_scale(chan, val, val2);
|
||||
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return axp192_adc_raw(indio_dev, chan, val);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int axp20x_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
@@ -549,6 +766,51 @@ static int axp813_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
}
|
||||
|
||||
static int axp192_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2,
|
||||
long mask)
|
||||
{
|
||||
struct axp20x_adc_iio *info = iio_priv(indio_dev);
|
||||
unsigned int regmask, regval;
|
||||
|
||||
/*
|
||||
* The AXP192 PMIC allows the user to choose between 0V and 0.7V offsets
|
||||
* for (independently) GPIO0-3 when in ADC mode.
|
||||
*/
|
||||
if (mask != IIO_CHAN_INFO_OFFSET)
|
||||
return -EINVAL;
|
||||
|
||||
if (val != 0 && val != 700000)
|
||||
return -EINVAL;
|
||||
|
||||
switch (chan->channel) {
|
||||
case AXP192_GPIO0_V:
|
||||
regmask = AXP192_GPIO30_IN_RANGE_GPIO0;
|
||||
regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO0, !!val);
|
||||
break;
|
||||
|
||||
case AXP192_GPIO1_V:
|
||||
regmask = AXP192_GPIO30_IN_RANGE_GPIO1;
|
||||
regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO1, !!val);
|
||||
break;
|
||||
|
||||
case AXP192_GPIO2_V:
|
||||
regmask = AXP192_GPIO30_IN_RANGE_GPIO2;
|
||||
regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO2, !!val);
|
||||
break;
|
||||
|
||||
case AXP192_GPIO3_V:
|
||||
regmask = AXP192_GPIO30_IN_RANGE_GPIO3;
|
||||
regval = FIELD_PREP(AXP192_GPIO30_IN_RANGE_GPIO3, !!val);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return regmap_update_bits(info->regmap, AXP192_GPIO30_IN_RANGE, regmask, regval);
|
||||
}
|
||||
|
||||
static int axp20x_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2,
|
||||
long mask)
|
||||
@@ -584,6 +846,11 @@ static int axp20x_write_raw(struct iio_dev *indio_dev,
|
||||
return regmap_update_bits(info->regmap, AXP20X_GPIO10_IN_RANGE, regmask, regval);
|
||||
}
|
||||
|
||||
static const struct iio_info axp192_adc_iio_info = {
|
||||
.read_raw = axp192_read_raw,
|
||||
.write_raw = axp192_write_raw,
|
||||
};
|
||||
|
||||
static const struct iio_info axp20x_adc_iio_info = {
|
||||
.read_raw = axp20x_read_raw,
|
||||
.write_raw = axp20x_write_raw,
|
||||
@@ -629,6 +896,16 @@ struct axp_data {
|
||||
struct iio_map *maps;
|
||||
};
|
||||
|
||||
static const struct axp_data axp192_data = {
|
||||
.iio_info = &axp192_adc_iio_info,
|
||||
.num_channels = ARRAY_SIZE(axp192_adc_channels),
|
||||
.channels = axp192_adc_channels,
|
||||
.adc_en1_mask = AXP192_ADC_EN1_MASK,
|
||||
.adc_en2_mask = AXP192_ADC_EN2_MASK,
|
||||
.adc_rate = axp20x_adc_rate,
|
||||
.maps = axp20x_maps,
|
||||
};
|
||||
|
||||
static const struct axp_data axp20x_data = {
|
||||
.iio_info = &axp20x_adc_iio_info,
|
||||
.num_channels = ARRAY_SIZE(axp20x_adc_channels),
|
||||
@@ -658,6 +935,7 @@ static const struct axp_data axp813_data = {
|
||||
};
|
||||
|
||||
static const struct of_device_id axp20x_adc_of_match[] = {
|
||||
{ .compatible = "x-powers,axp192-adc", .data = (void *)&axp192_data, },
|
||||
{ .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, },
|
||||
{ .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, },
|
||||
{ .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, },
|
||||
@@ -666,6 +944,7 @@ static const struct of_device_id axp20x_adc_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, axp20x_adc_of_match);
|
||||
|
||||
static const struct platform_device_id axp20x_adc_id_match[] = {
|
||||
{ .name = "axp192-adc", .driver_data = (kernel_ulong_t)&axp192_data, },
|
||||
{ .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, },
|
||||
{ .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, },
|
||||
{ .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, },
|
||||
|
||||
@@ -920,4 +920,5 @@ static struct platform_driver ingenic_adc_driver = {
|
||||
.probe = ingenic_adc_probe,
|
||||
};
|
||||
module_platform_driver(ingenic_adc_driver);
|
||||
MODULE_DESCRIPTION("ADC driver for the Ingenic JZ47xx SoCs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -124,7 +124,7 @@ static int ltc2485_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ltc2485_id[] = {
|
||||
{ "ltc2485", 0 },
|
||||
{ "ltc2485" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ltc2485_id);
|
||||
|
||||
@@ -116,10 +116,7 @@ static int max11205_probe(struct spi_device *spi)
|
||||
|
||||
ad_sd_init(&st->sd, indio_dev, spi, &max11205_sigma_delta_info);
|
||||
|
||||
st->chip_info = device_get_match_data(&spi->dev);
|
||||
if (!st->chip_info)
|
||||
st->chip_info =
|
||||
(const struct max11205_chip_info *)spi_get_device_id(spi)->driver_data;
|
||||
st->chip_info = spi_get_device_match_data(spi);
|
||||
|
||||
indio_dev->name = st->chip_info->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
@@ -1114,7 +1114,6 @@ static int mcp3564_config(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct mcp3564_state *adc = iio_priv(indio_dev);
|
||||
struct device *dev = &adc->spi->dev;
|
||||
const struct spi_device_id *dev_id;
|
||||
u8 tmp_reg;
|
||||
u16 tmp_u16;
|
||||
enum mcp3564_ids ids;
|
||||
@@ -1212,11 +1211,6 @@ static int mcp3564_config(struct iio_dev *indio_dev)
|
||||
* try using fallback compatible in device tree to deal with some newer part number.
|
||||
*/
|
||||
adc->chip_info = spi_get_device_match_data(adc->spi);
|
||||
if (!adc->chip_info) {
|
||||
dev_id = spi_get_device_id(adc->spi);
|
||||
adc->chip_info = (const struct mcp3564_chip_info *)dev_id->driver_data;
|
||||
}
|
||||
|
||||
adc->have_vref = adc->chip_info->have_vref;
|
||||
} else {
|
||||
adc->chip_info = &mcp3564_chip_infos_tbl[ids];
|
||||
|
||||
@@ -532,7 +532,7 @@ static int nau7802_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id nau7802_i2c_id[] = {
|
||||
{ "nau7802", 0 },
|
||||
{ "nau7802" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, nau7802_i2c_id);
|
||||
|
||||
@@ -227,11 +227,6 @@ struct pac1934_features {
|
||||
const char *name;
|
||||
};
|
||||
|
||||
struct samp_rate_mapping {
|
||||
u16 samp_rate;
|
||||
u8 shift2value;
|
||||
};
|
||||
|
||||
static const unsigned int samp_rate_map_tbl[] = {
|
||||
[PAC1934_SAMP_1024SPS] = 1024,
|
||||
[PAC1934_SAMP_256SPS] = 256,
|
||||
|
||||
@@ -137,17 +137,13 @@ static int ti_adc_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ti_adc_read_measurement(data, chan, val);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
||||
ret = ti_adc_read_measurement(data, chan, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
unreachable();
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(data->ref);
|
||||
if (ret < 0)
|
||||
|
||||
@@ -802,9 +802,7 @@ static int ads131e08_probe(struct spi_device *spi)
|
||||
unsigned long adc_clk_ns;
|
||||
int ret;
|
||||
|
||||
info = device_get_match_data(&spi->dev);
|
||||
if (!info)
|
||||
info = (void *)spi_get_device_id(spi)->driver_data;
|
||||
info = spi_get_device_match_data(spi);
|
||||
if (!info) {
|
||||
dev_err(&spi->dev, "failed to get match data\n");
|
||||
return -ENODEV;
|
||||
|
||||
@@ -447,7 +447,7 @@ static int ads7924_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ads7924_id[] = {
|
||||
{ "ads7924", 0 },
|
||||
{ "ads7924" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ads7924_id);
|
||||
|
||||
@@ -804,12 +804,7 @@ static int tsc2046_adc_probe(struct spi_device *spi)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dcfg = device_get_match_data(dev);
|
||||
if (!dcfg) {
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
dcfg = (const struct tsc2046_adc_dcfg *)id->driver_data;
|
||||
}
|
||||
dcfg = spi_get_device_match_data(spi);
|
||||
if (!dcfg)
|
||||
return -EINVAL;
|
||||
|
||||
|
||||
@@ -1430,5 +1430,6 @@ static struct platform_driver ams_driver = {
|
||||
};
|
||||
module_platform_driver(ams_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Xilinx AMS driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Xilinx, Inc.");
|
||||
|
||||
@@ -1365,16 +1365,9 @@ static int ad74413r_probe(struct spi_device *spi)
|
||||
|
||||
st->spi = spi;
|
||||
st->dev = &spi->dev;
|
||||
st->chip_info = device_get_match_data(&spi->dev);
|
||||
if (!st->chip_info) {
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
if (id)
|
||||
st->chip_info =
|
||||
(struct ad74413r_chip_info *)id->driver_data;
|
||||
if (!st->chip_info)
|
||||
return -EINVAL;
|
||||
}
|
||||
st->chip_info = spi_get_device_match_data(spi);
|
||||
if (!st->chip_info)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_init(&st->lock);
|
||||
init_completion(&st->adc_data_completion);
|
||||
|
||||
@@ -287,4 +287,5 @@ int devm_iio_kfifo_buffer_setup_ext(struct device *dev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_iio_kfifo_buffer_setup_ext);
|
||||
|
||||
MODULE_DESCRIPTION("Industrial I/O buffering based on kfifo");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -76,6 +76,26 @@ config CCS811
|
||||
Say Y here to build I2C interface support for the AMS
|
||||
CCS811 VOC (Volatile Organic Compounds) sensor
|
||||
|
||||
config ENS160
|
||||
tristate "ScioSense ENS160 sensor driver"
|
||||
depends on (I2C || SPI)
|
||||
select REGMAP
|
||||
select ENS160_I2C if I2C
|
||||
select ENS160_SPI if SPI
|
||||
help
|
||||
Say yes here to build support for ScioSense ENS160 multi-gas sensor.
|
||||
|
||||
This driver can also be built as a module. If so, the module for I2C
|
||||
would be called ens160_i2c and ens160_spi for SPI support.
|
||||
|
||||
config ENS160_I2C
|
||||
tristate
|
||||
select REGMAP_I2C
|
||||
|
||||
config ENS160_SPI
|
||||
tristate
|
||||
select REGMAP_SPI
|
||||
|
||||
config IAQCORE
|
||||
tristate "AMS iAQ-Core VOC sensors"
|
||||
depends on I2C
|
||||
|
||||
@@ -11,6 +11,9 @@ obj-$(CONFIG_BME680) += bme680_core.o
|
||||
obj-$(CONFIG_BME680_I2C) += bme680_i2c.o
|
||||
obj-$(CONFIG_BME680_SPI) += bme680_spi.o
|
||||
obj-$(CONFIG_CCS811) += ccs811.o
|
||||
obj-$(CONFIG_ENS160) += ens160_core.o
|
||||
obj-$(CONFIG_ENS160_I2C) += ens160_i2c.o
|
||||
obj-$(CONFIG_ENS160_SPI) += ens160_spi.o
|
||||
obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
|
||||
obj-$(CONFIG_PMS7003) += pms7003.o
|
||||
obj-$(CONFIG_SCD30_CORE) += scd30_core.o
|
||||
|
||||
@@ -24,7 +24,7 @@ struct ams_iaqcore_reading {
|
||||
u8 status;
|
||||
__be32 resistance;
|
||||
__be16 voc_ppb;
|
||||
} __attribute__((__packed__));
|
||||
} __packed;
|
||||
|
||||
struct ams_iaqcore_data {
|
||||
struct i2c_client *client;
|
||||
@@ -163,7 +163,7 @@ static int ams_iaqcore_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ams_iaqcore_id[] = {
|
||||
{ "ams-iaq-core", 0 },
|
||||
{ "ams-iaq-core" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ams_iaqcore_id);
|
||||
|
||||
@@ -36,8 +36,8 @@ static int bme680_i2c_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bme680_i2c_id[] = {
|
||||
{"bme680", 0},
|
||||
{},
|
||||
{ "bme680" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bme680_i2c_id);
|
||||
|
||||
|
||||
@@ -551,7 +551,7 @@ static void ccs811_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ccs811_id[] = {
|
||||
{"ccs811", 0},
|
||||
{ "ccs811" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ccs811_id);
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef ENS160_H_
|
||||
#define ENS160_H_
|
||||
|
||||
int devm_ens160_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
||||
const char *name);
|
||||
|
||||
extern const struct dev_pm_ops ens160_pm_ops;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,367 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ScioSense ENS160 multi-gas sensor driver
|
||||
*
|
||||
* Copyright (c) 2024 Gustavo Silva <gustavograzs@gmail.com>
|
||||
*
|
||||
* Datasheet:
|
||||
* https://www.sciosense.com/wp-content/uploads/2023/12/ENS160-Datasheet.pdf
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "ens160.h"
|
||||
|
||||
#define ENS160_PART_ID 0x160
|
||||
|
||||
#define ENS160_BOOTING_TIME_MS 10U
|
||||
|
||||
#define ENS160_REG_PART_ID 0x00
|
||||
|
||||
#define ENS160_REG_OPMODE 0x10
|
||||
|
||||
#define ENS160_REG_CONFIG 0x11
|
||||
#define ENS160_REG_CONFIG_INTEN BIT(0)
|
||||
#define ENS160_REG_CONFIG_INTDAT BIT(1)
|
||||
#define ENS160_REG_CONFIG_INT_CFG BIT(5)
|
||||
|
||||
#define ENS160_REG_MODE_DEEP_SLEEP 0x00
|
||||
#define ENS160_REG_MODE_IDLE 0x01
|
||||
#define ENS160_REG_MODE_STANDARD 0x02
|
||||
#define ENS160_REG_MODE_RESET 0xF0
|
||||
|
||||
#define ENS160_REG_COMMAND 0x12
|
||||
#define ENS160_REG_COMMAND_GET_APPVER 0x0E
|
||||
#define ENS160_REG_COMMAND_CLRGPR 0xCC
|
||||
|
||||
#define ENS160_REG_TEMP_IN 0x13
|
||||
#define ENS160_REG_RH_IN 0x15
|
||||
#define ENS160_REG_DEVICE_STATUS 0x20
|
||||
#define ENS160_REG_DATA_AQI 0x21
|
||||
#define ENS160_REG_DATA_TVOC 0x22
|
||||
#define ENS160_REG_DATA_ECO2 0x24
|
||||
#define ENS160_REG_DATA_T 0x30
|
||||
#define ENS160_REG_DATA_RH 0x32
|
||||
#define ENS160_REG_GPR_READ4 0x4C
|
||||
|
||||
#define ENS160_STATUS_VALIDITY_FLAG GENMASK(3, 2)
|
||||
|
||||
#define ENS160_STATUS_NORMAL 0x00
|
||||
|
||||
struct ens160_data {
|
||||
struct regmap *regmap;
|
||||
/* Protect reads from the sensor */
|
||||
struct mutex mutex;
|
||||
struct {
|
||||
__le16 chans[2];
|
||||
s64 timestamp __aligned(8);
|
||||
} scan __aligned(IIO_DMA_MINALIGN);
|
||||
u8 fw_version[3];
|
||||
__le16 buf;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ens160_channels[] = {
|
||||
{
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_VOC,
|
||||
.modified = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = ENS160_REG_DATA_TVOC,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_CO2,
|
||||
.modified = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = ENS160_REG_DATA_ECO2,
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_LE,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
};
|
||||
|
||||
static int ens160_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct ens160_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
||||
guard(mutex)(&data->mutex);
|
||||
ret = regmap_bulk_read(data->regmap, chan->address,
|
||||
&data->buf, sizeof(data->buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = le16_to_cpu(data->buf);
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
unreachable();
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->channel2) {
|
||||
case IIO_MOD_CO2:
|
||||
/* The sensor reads CO2 data as ppm */
|
||||
*val = 0;
|
||||
*val2 = 100;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_MOD_VOC:
|
||||
/* The sensor reads VOC data as ppb */
|
||||
*val = 0;
|
||||
*val2 = 100;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ens160_set_mode(struct ens160_data *data, u8 mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_write(data->regmap, ENS160_REG_OPMODE, mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msleep(ENS160_BOOTING_TIME_MS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ens160_set_idle(void *data)
|
||||
{
|
||||
ens160_set_mode(data, ENS160_REG_MODE_IDLE);
|
||||
}
|
||||
|
||||
static int ens160_chip_init(struct ens160_data *data)
|
||||
{
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
unsigned int status;
|
||||
int ret;
|
||||
|
||||
ret = ens160_set_mode(data, ENS160_REG_MODE_RESET);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, ENS160_REG_PART_ID, &data->buf,
|
||||
sizeof(data->buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (le16_to_cpu(data->buf) != ENS160_PART_ID)
|
||||
return -ENODEV;
|
||||
|
||||
ret = ens160_set_mode(data, ENS160_REG_MODE_IDLE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(data->regmap, ENS160_REG_COMMAND,
|
||||
ENS160_REG_COMMAND_CLRGPR);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(data->regmap, ENS160_REG_COMMAND,
|
||||
ENS160_REG_COMMAND_GET_APPVER);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, ENS160_REG_GPR_READ4,
|
||||
data->fw_version, sizeof(data->fw_version));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(dev, "firmware version: %u.%u.%u\n", data->fw_version[2],
|
||||
data->fw_version[1], data->fw_version[0]);
|
||||
|
||||
ret = ens160_set_mode(data, ENS160_REG_MODE_STANDARD);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, ens160_set_idle, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(data->regmap, ENS160_REG_DEVICE_STATUS, &status);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (FIELD_GET(ENS160_STATUS_VALIDITY_FLAG, status)
|
||||
!= ENS160_STATUS_NORMAL)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info ens160_info = {
|
||||
.read_raw = ens160_read_raw,
|
||||
};
|
||||
|
||||
static int ens160_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ens160_data *data = iio_priv(indio_dev);
|
||||
|
||||
return ens160_set_mode(data, ENS160_REG_MODE_DEEP_SLEEP);
|
||||
}
|
||||
|
||||
static int ens160_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ens160_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = ens160_set_mode(data, ENS160_REG_MODE_IDLE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ens160_set_mode(data, ENS160_REG_MODE_STANDARD);
|
||||
}
|
||||
EXPORT_NS_SIMPLE_DEV_PM_OPS(ens160_pm_ops, ens160_suspend, ens160_resume,
|
||||
IIO_ENS160);
|
||||
|
||||
static irqreturn_t ens160_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ens160_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&data->mutex);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, ENS160_REG_DATA_TVOC,
|
||||
data->scan.chans, sizeof(data->scan.chans));
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
|
||||
pf->timestamp);
|
||||
err:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ens160_set_trigger_state(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
struct ens160_data *data = iio_priv(indio_dev);
|
||||
unsigned int int_bits = ENS160_REG_CONFIG_INTEN |
|
||||
ENS160_REG_CONFIG_INTDAT |
|
||||
ENS160_REG_CONFIG_INT_CFG;
|
||||
|
||||
if (state)
|
||||
return regmap_set_bits(data->regmap, ENS160_REG_CONFIG,
|
||||
int_bits);
|
||||
else
|
||||
return regmap_clear_bits(data->regmap, ENS160_REG_CONFIG,
|
||||
int_bits);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops ens160_trigger_ops = {
|
||||
.set_trigger_state = ens160_set_trigger_state,
|
||||
.validate_device = iio_trigger_validate_own_device,
|
||||
};
|
||||
|
||||
static int ens160_setup_trigger(struct iio_dev *indio_dev, int irq)
|
||||
{
|
||||
struct device *dev = indio_dev->dev.parent;
|
||||
struct iio_trigger *trig;
|
||||
int ret;
|
||||
|
||||
trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name,
|
||||
iio_device_id(indio_dev));
|
||||
if (!trig)
|
||||
return dev_err_probe(dev, -ENOMEM,
|
||||
"failed to allocate trigger\n");
|
||||
|
||||
trig->ops = &ens160_trigger_ops;
|
||||
iio_trigger_set_drvdata(trig, indio_dev);
|
||||
|
||||
ret = devm_iio_trigger_register(dev, trig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->trig = iio_trigger_get(trig);
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq,
|
||||
iio_trigger_generic_data_rdy_poll,
|
||||
NULL,
|
||||
IRQF_ONESHOT,
|
||||
indio_dev->name,
|
||||
indio_dev->trig);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to request irq\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int devm_ens160_core_probe(struct device *dev, struct regmap *regmap, int irq,
|
||||
const char *name)
|
||||
{
|
||||
struct ens160_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->regmap = regmap;
|
||||
|
||||
indio_dev->name = name;
|
||||
indio_dev->info = &ens160_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ens160_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ens160_channels);
|
||||
|
||||
if (irq > 0) {
|
||||
ret = ens160_setup_trigger(indio_dev, irq);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to setup trigger\n");
|
||||
}
|
||||
|
||||
ret = ens160_chip_init(data);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "chip initialization failed\n");
|
||||
|
||||
mutex_init(&data->mutex);
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
|
||||
iio_pollfunc_store_time,
|
||||
ens160_trigger_handler, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_NS(devm_ens160_core_probe, IIO_ENS160);
|
||||
|
||||
MODULE_AUTHOR("Gustavo Silva <gustavograzs@gmail.com>");
|
||||
MODULE_DESCRIPTION("ScioSense ENS160 driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ScioSense ENS160 multi-gas sensor I2C driver
|
||||
*
|
||||
* Copyright (c) 2024 Gustavo Silva <gustavograzs@gmail.com>
|
||||
*
|
||||
* 7-Bit I2C slave address is:
|
||||
* - 0x52 if ADDR pin LOW
|
||||
* - 0x53 if ADDR pin HIGH
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "ens160.h"
|
||||
|
||||
static const struct regmap_config ens160_regmap_i2c_conf = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int ens160_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &ens160_regmap_i2c_conf);
|
||||
if (IS_ERR(regmap))
|
||||
return dev_err_probe(&client->dev, PTR_ERR(regmap),
|
||||
"Failed to register i2c regmap\n");
|
||||
|
||||
return devm_ens160_core_probe(&client->dev, regmap, client->irq,
|
||||
"ens160");
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ens160_i2c_id[] = {
|
||||
{ "ens160" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ens160_i2c_id);
|
||||
|
||||
static const struct of_device_id ens160_of_i2c_match[] = {
|
||||
{ .compatible = "sciosense,ens160" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ens160_of_i2c_match);
|
||||
|
||||
static struct i2c_driver ens160_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "ens160",
|
||||
.of_match_table = ens160_of_i2c_match,
|
||||
.pm = pm_sleep_ptr(&ens160_pm_ops),
|
||||
},
|
||||
.probe = ens160_i2c_probe,
|
||||
.id_table = ens160_i2c_id,
|
||||
};
|
||||
module_i2c_driver(ens160_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Gustavo Silva <gustavograzs@gmail.com>");
|
||||
MODULE_DESCRIPTION("ScioSense ENS160 I2C driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_IMPORT_NS(IIO_ENS160);
|
||||
@@ -0,0 +1,61 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ScioSense ENS160 multi-gas sensor SPI driver
|
||||
*
|
||||
* Copyright (c) 2024 Gustavo Silva <gustavograzs@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include "ens160.h"
|
||||
|
||||
#define ENS160_SPI_READ BIT(0)
|
||||
|
||||
static const struct regmap_config ens160_regmap_spi_conf = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.reg_shift = -1,
|
||||
.read_flag_mask = ENS160_SPI_READ,
|
||||
};
|
||||
|
||||
static int ens160_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &ens160_regmap_spi_conf);
|
||||
if (IS_ERR(regmap))
|
||||
return dev_err_probe(&spi->dev, PTR_ERR(regmap),
|
||||
"Failed to register spi regmap\n");
|
||||
|
||||
return devm_ens160_core_probe(&spi->dev, regmap, spi->irq, "ens160");
|
||||
}
|
||||
|
||||
static const struct of_device_id ens160_spi_of_match[] = {
|
||||
{ .compatible = "sciosense,ens160" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ens160_spi_of_match);
|
||||
|
||||
static const struct spi_device_id ens160_spi_id[] = {
|
||||
{ "ens160" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ens160_spi_id);
|
||||
|
||||
static struct spi_driver ens160_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ens160",
|
||||
.of_match_table = ens160_spi_of_match,
|
||||
.pm = pm_sleep_ptr(&ens160_pm_ops),
|
||||
},
|
||||
.probe = ens160_spi_probe,
|
||||
.id_table = ens160_spi_id,
|
||||
};
|
||||
module_spi_driver(ens160_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Gustavo Silva <gustavograzs@gmail.com>");
|
||||
MODULE_DESCRIPTION("ScioSense ENS160 SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_IMPORT_NS(IIO_ENS160);
|
||||
@@ -149,6 +149,7 @@ config AD9739A
|
||||
|
||||
config ADI_AXI_DAC
|
||||
tristate "Analog Devices Generic AXI DAC IP core driver"
|
||||
depends on MICROBLAZE || NIOS2 || ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_INTEL_SOCFPGA || COMPILE_TEST
|
||||
select IIO_BUFFER
|
||||
select IIO_BUFFER_DMAENGINE
|
||||
select REGMAP_MMIO
|
||||
|
||||
+95
-45
@@ -117,7 +117,7 @@
|
||||
#define AD3552R_REG_ADDR_CH_INPUT_24B(ch) (0x4B - (1 - ch) * 3)
|
||||
|
||||
/* Useful defines */
|
||||
#define AD3552R_NUM_CH 2
|
||||
#define AD3552R_MAX_CH 2
|
||||
#define AD3552R_MASK_CH(ch) BIT(ch)
|
||||
#define AD3552R_MASK_ALL_CH GENMASK(1, 0)
|
||||
#define AD3552R_MAX_REG_SIZE 3
|
||||
@@ -139,8 +139,10 @@ enum ad3552r_ch_vref_select {
|
||||
AD3552R_EXTERNAL_VREF_PIN_INPUT
|
||||
};
|
||||
|
||||
enum ad3542r_id {
|
||||
enum ad3552r_id {
|
||||
AD3541R_ID = 0x400b,
|
||||
AD3542R_ID = 0x4009,
|
||||
AD3551R_ID = 0x400a,
|
||||
AD3552R_ID = 0x4008,
|
||||
};
|
||||
|
||||
@@ -261,17 +263,26 @@ struct ad3552r_ch_data {
|
||||
bool range_override;
|
||||
};
|
||||
|
||||
struct ad3552r_model_data {
|
||||
const char *model_name;
|
||||
enum ad3552r_id chip_id;
|
||||
unsigned int num_hw_channels;
|
||||
const s32 (*ranges_table)[2];
|
||||
int num_ranges;
|
||||
bool requires_output_range;
|
||||
};
|
||||
|
||||
struct ad3552r_desc {
|
||||
const struct ad3552r_model_data *model_data;
|
||||
/* Used to look the spi bus for atomic operations where needed */
|
||||
struct mutex lock;
|
||||
struct gpio_desc *gpio_reset;
|
||||
struct gpio_desc *gpio_ldac;
|
||||
struct spi_device *spi;
|
||||
struct ad3552r_ch_data ch_data[AD3552R_NUM_CH];
|
||||
struct iio_chan_spec channels[AD3552R_NUM_CH + 1];
|
||||
struct ad3552r_ch_data ch_data[AD3552R_MAX_CH];
|
||||
struct iio_chan_spec channels[AD3552R_MAX_CH + 1];
|
||||
unsigned long enabled_ch;
|
||||
unsigned int num_ch;
|
||||
enum ad3542r_id chip_id;
|
||||
};
|
||||
|
||||
static const u16 addr_mask_map[][2] = {
|
||||
@@ -528,7 +539,7 @@ static int32_t ad3552r_trigger_hw_ldac(struct gpio_desc *ldac)
|
||||
static int ad3552r_write_all_channels(struct ad3552r_desc *dac, u8 *data)
|
||||
{
|
||||
int err, len;
|
||||
u8 addr, buff[AD3552R_NUM_CH * AD3552R_MAX_REG_SIZE + 1];
|
||||
u8 addr, buff[AD3552R_MAX_CH * AD3552R_MAX_REG_SIZE + 1];
|
||||
|
||||
addr = AD3552R_REG_ADDR_CH_INPUT_24B(1);
|
||||
/* CH1 */
|
||||
@@ -586,7 +597,7 @@ static irqreturn_t ad3552r_trigger_handler(int irq, void *p)
|
||||
struct iio_buffer *buf = indio_dev->buffer;
|
||||
struct ad3552r_desc *dac = iio_priv(indio_dev);
|
||||
/* Maximum size of a scan */
|
||||
u8 buff[AD3552R_NUM_CH * AD3552R_MAX_REG_SIZE];
|
||||
u8 buff[AD3552R_MAX_CH * AD3552R_MAX_REG_SIZE];
|
||||
int err;
|
||||
|
||||
memset(buff, 0, sizeof(buff));
|
||||
@@ -745,13 +756,8 @@ static void ad3552r_calc_gain_and_offset(struct ad3552r_desc *dac, s32 ch)
|
||||
} else {
|
||||
/* Normal range */
|
||||
idx = dac->ch_data[ch].range;
|
||||
if (dac->chip_id == AD3542R_ID) {
|
||||
v_min = ad3542r_ch_ranges[idx][0];
|
||||
v_max = ad3542r_ch_ranges[idx][1];
|
||||
} else {
|
||||
v_min = ad3552r_ch_ranges[idx][0];
|
||||
v_max = ad3552r_ch_ranges[idx][1];
|
||||
}
|
||||
v_min = dac->model_data->ranges_table[idx][0];
|
||||
v_max = dac->model_data->ranges_table[idx][1];
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -775,22 +781,14 @@ static void ad3552r_calc_gain_and_offset(struct ad3552r_desc *dac, s32 ch)
|
||||
dac->ch_data[ch].offset_dec = div_s64(tmp, span);
|
||||
}
|
||||
|
||||
static int ad3552r_find_range(u16 id, s32 *vals)
|
||||
static int ad3552r_find_range(const struct ad3552r_model_data *model_data,
|
||||
s32 *vals)
|
||||
{
|
||||
int i, len;
|
||||
const s32 (*ranges)[2];
|
||||
int i;
|
||||
|
||||
if (id == AD3542R_ID) {
|
||||
len = ARRAY_SIZE(ad3542r_ch_ranges);
|
||||
ranges = ad3542r_ch_ranges;
|
||||
} else {
|
||||
len = ARRAY_SIZE(ad3552r_ch_ranges);
|
||||
ranges = ad3552r_ch_ranges;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
if (vals[0] == ranges[i][0] * 1000 &&
|
||||
vals[1] == ranges[i][1] * 1000)
|
||||
for (i = 0; i < model_data->num_ranges; i++)
|
||||
if (vals[0] == model_data->ranges_table[i][0] * 1000 &&
|
||||
vals[1] == model_data->ranges_table[i][1] * 1000)
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
@@ -940,10 +938,10 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
|
||||
if (err)
|
||||
return dev_err_probe(dev, err,
|
||||
"mandatory reg property missing\n");
|
||||
if (ch >= AD3552R_NUM_CH)
|
||||
if (ch >= dac->model_data->num_hw_channels)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"reg must be less than %d\n",
|
||||
AD3552R_NUM_CH);
|
||||
dac->model_data->num_hw_channels);
|
||||
|
||||
if (fwnode_property_present(child, "adi,output-range-microvolt")) {
|
||||
err = fwnode_property_read_u32_array(child,
|
||||
@@ -954,7 +952,7 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
|
||||
return dev_err_probe(dev, err,
|
||||
"adi,output-range-microvolt property could not be parsed\n");
|
||||
|
||||
err = ad3552r_find_range(dac->chip_id, vals);
|
||||
err = ad3552r_find_range(dac->model_data, vals);
|
||||
if (err < 0)
|
||||
return dev_err_probe(dev, err,
|
||||
"Invalid adi,output-range-microvolt value\n");
|
||||
@@ -967,9 +965,10 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
|
||||
return err;
|
||||
|
||||
dac->ch_data[ch].range = val;
|
||||
} else if (dac->chip_id == AD3542R_ID) {
|
||||
} else if (dac->model_data->requires_output_range) {
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"adi,output-range-microvolt is required for ad3542r\n");
|
||||
"adi,output-range-microvolt is required for %s\n",
|
||||
dac->model_data->model_name);
|
||||
} else {
|
||||
err = ad3552r_configure_custom_gain(dac, child, ch);
|
||||
if (err)
|
||||
@@ -989,7 +988,8 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
|
||||
}
|
||||
|
||||
/* Disable unused channels */
|
||||
for_each_clear_bit(ch, &dac->enabled_ch, AD3552R_NUM_CH) {
|
||||
for_each_clear_bit(ch, &dac->enabled_ch,
|
||||
dac->model_data->num_hw_channels) {
|
||||
err = ad3552r_set_ch_value(dac, AD3552R_CH_AMPLIFIER_POWERDOWN,
|
||||
ch, 1);
|
||||
if (err)
|
||||
@@ -1032,7 +1032,7 @@ static int ad3552r_init(struct ad3552r_desc *dac)
|
||||
}
|
||||
|
||||
id |= val << 8;
|
||||
if (id != dac->chip_id) {
|
||||
if (id != dac->model_data->chip_id) {
|
||||
dev_err(&dac->spi->dev, "Product id not matching\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
@@ -1042,7 +1042,6 @@ static int ad3552r_init(struct ad3552r_desc *dac)
|
||||
|
||||
static int ad3552r_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct ad3552r_desc *dac;
|
||||
struct iio_dev *indio_dev;
|
||||
int err;
|
||||
@@ -1053,7 +1052,9 @@ static int ad3552r_probe(struct spi_device *spi)
|
||||
|
||||
dac = iio_priv(indio_dev);
|
||||
dac->spi = spi;
|
||||
dac->chip_id = id->driver_data;
|
||||
dac->model_data = spi_get_device_match_data(spi);
|
||||
if (!dac->model_data)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_init(&dac->lock);
|
||||
|
||||
@@ -1062,10 +1063,7 @@ static int ad3552r_probe(struct spi_device *spi)
|
||||
return err;
|
||||
|
||||
/* Config triggered buffer device */
|
||||
if (dac->chip_id == AD3552R_ID)
|
||||
indio_dev->name = "ad3552r";
|
||||
else
|
||||
indio_dev->name = "ad3542r";
|
||||
indio_dev->name = dac->model_data->model_name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &ad3552r_iio_info;
|
||||
indio_dev->num_channels = dac->num_ch;
|
||||
@@ -1083,16 +1081,68 @@ static int ad3552r_probe(struct spi_device *spi)
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct ad3552r_model_data ad3541r_model_data = {
|
||||
.model_name = "ad3541r",
|
||||
.chip_id = AD3541R_ID,
|
||||
.num_hw_channels = 1,
|
||||
.ranges_table = ad3542r_ch_ranges,
|
||||
.num_ranges = ARRAY_SIZE(ad3542r_ch_ranges),
|
||||
.requires_output_range = true,
|
||||
};
|
||||
|
||||
static const struct ad3552r_model_data ad3542r_model_data = {
|
||||
.model_name = "ad3542r",
|
||||
.chip_id = AD3542R_ID,
|
||||
.num_hw_channels = 2,
|
||||
.ranges_table = ad3542r_ch_ranges,
|
||||
.num_ranges = ARRAY_SIZE(ad3542r_ch_ranges),
|
||||
.requires_output_range = true,
|
||||
};
|
||||
|
||||
static const struct ad3552r_model_data ad3551r_model_data = {
|
||||
.model_name = "ad3551r",
|
||||
.chip_id = AD3551R_ID,
|
||||
.num_hw_channels = 1,
|
||||
.ranges_table = ad3552r_ch_ranges,
|
||||
.num_ranges = ARRAY_SIZE(ad3552r_ch_ranges),
|
||||
.requires_output_range = false,
|
||||
};
|
||||
|
||||
static const struct ad3552r_model_data ad3552r_model_data = {
|
||||
.model_name = "ad3552r",
|
||||
.chip_id = AD3552R_ID,
|
||||
.num_hw_channels = 2,
|
||||
.ranges_table = ad3552r_ch_ranges,
|
||||
.num_ranges = ARRAY_SIZE(ad3552r_ch_ranges),
|
||||
.requires_output_range = false,
|
||||
};
|
||||
|
||||
static const struct spi_device_id ad3552r_id[] = {
|
||||
{ "ad3542r", AD3542R_ID },
|
||||
{ "ad3552r", AD3552R_ID },
|
||||
{
|
||||
.name = "ad3541r",
|
||||
.driver_data = (kernel_ulong_t)&ad3541r_model_data
|
||||
},
|
||||
{
|
||||
.name = "ad3542r",
|
||||
.driver_data = (kernel_ulong_t)&ad3542r_model_data
|
||||
},
|
||||
{
|
||||
.name = "ad3551r",
|
||||
.driver_data = (kernel_ulong_t)&ad3551r_model_data
|
||||
},
|
||||
{
|
||||
.name = "ad3552r",
|
||||
.driver_data = (kernel_ulong_t)&ad3552r_model_data
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad3552r_id);
|
||||
|
||||
static const struct of_device_id ad3552r_of_match[] = {
|
||||
{ .compatible = "adi,ad3542r"},
|
||||
{ .compatible = "adi,ad3552r"},
|
||||
{ .compatible = "adi,ad3541r", .data = &ad3541r_model_data },
|
||||
{ .compatible = "adi,ad3542r", .data = &ad3542r_model_data },
|
||||
{ .compatible = "adi,ad3551r", .data = &ad3551r_model_data },
|
||||
{ .compatible = "adi,ad3552r", .data = &ad3552r_model_data },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad3552r_of_match);
|
||||
|
||||
@@ -132,7 +132,6 @@ static const struct regmap_config max5522_regmap_config = {
|
||||
|
||||
static int max5522_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct iio_dev *indio_dev;
|
||||
struct max5522_state *state;
|
||||
int ret;
|
||||
@@ -144,13 +143,9 @@ static int max5522_spi_probe(struct spi_device *spi)
|
||||
}
|
||||
|
||||
state = iio_priv(indio_dev);
|
||||
state->chip_info = device_get_match_data(&spi->dev);
|
||||
if (!state->chip_info) {
|
||||
state->chip_info =
|
||||
(struct max5522_chip_info *)(id->driver_data);
|
||||
if (!state->chip_info)
|
||||
return -EINVAL;
|
||||
}
|
||||
state->chip_info = spi_get_device_match_data(spi);
|
||||
if (!state->chip_info)
|
||||
return -EINVAL;
|
||||
|
||||
state->vrefin_reg = devm_regulator_get(&spi->dev, "vrefin");
|
||||
if (IS_ERR(state->vrefin_reg))
|
||||
|
||||
@@ -591,7 +591,7 @@ static int mcp4728_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mcp4728_id[] = {
|
||||
{ "mcp4728", 0 },
|
||||
{ "mcp4728" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp4728_id);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
@@ -47,9 +47,9 @@ static const struct acpi_device_id bmg160_acpi_match[] = {
|
||||
MODULE_DEVICE_TABLE(acpi, bmg160_acpi_match);
|
||||
|
||||
static const struct i2c_device_id bmg160_i2c_id[] = {
|
||||
{"bmg160", 0},
|
||||
{"bmi055_gyro", 0},
|
||||
{"bmi088_gyro", 0},
|
||||
{ "bmg160" },
|
||||
{ "bmi055_gyro" },
|
||||
{ "bmi088_gyro" },
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ static void fxas21002c_i2c_remove(struct i2c_client *i2c)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id fxas21002c_i2c_id[] = {
|
||||
{ "fxas21002c", 0 },
|
||||
{ "fxas21002c" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, fxas21002c_i2c_id);
|
||||
|
||||
@@ -387,7 +387,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(itg3200_pm_ops, itg3200_suspend,
|
||||
itg3200_resume);
|
||||
|
||||
static const struct i2c_device_id itg3200_id[] = {
|
||||
{ "itg3200", 0 },
|
||||
{ "itg3200" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, itg3200_id);
|
||||
|
||||
@@ -582,7 +582,7 @@ static int afe4404_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id afe4404_ids[] = {
|
||||
{ "afe4404", 0 },
|
||||
{ "afe4404" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, afe4404_ids);
|
||||
|
||||
@@ -483,7 +483,7 @@ static void max30100_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max30100_id[] = {
|
||||
{ "max30100", 0 },
|
||||
{ "max30100" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max30100_id);
|
||||
|
||||
@@ -253,7 +253,7 @@ static int am2315_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id am2315_i2c_id[] = {
|
||||
{"am2315", 0},
|
||||
{ "am2315" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, am2315_i2c_id);
|
||||
|
||||
@@ -396,12 +396,12 @@ static int hdc100x_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id hdc100x_id[] = {
|
||||
{ "hdc100x", 0 },
|
||||
{ "hdc1000", 0 },
|
||||
{ "hdc1008", 0 },
|
||||
{ "hdc1010", 0 },
|
||||
{ "hdc1050", 0 },
|
||||
{ "hdc1080", 0 },
|
||||
{ "hdc100x" },
|
||||
{ "hdc1000" },
|
||||
{ "hdc1008" },
|
||||
{ "hdc1010" },
|
||||
{ "hdc1050" },
|
||||
{ "hdc1080" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, hdc100x_id);
|
||||
|
||||
@@ -163,8 +163,8 @@ static int si7005_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id si7005_id[] = {
|
||||
{ "si7005", 0 },
|
||||
{ "th02", 0 },
|
||||
{ "si7005" },
|
||||
{ "th02" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si7005_id);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/stat.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@@ -33,17 +34,38 @@
|
||||
#define SI7020CMD_TEMP_HOLD 0xE3
|
||||
/* Software Reset */
|
||||
#define SI7020CMD_RESET 0xFE
|
||||
#define SI7020CMD_USR_WRITE 0xE6
|
||||
/* "Heater Enabled" bit in the User Register */
|
||||
#define SI7020_USR_HEATER_EN BIT(2)
|
||||
#define SI7020CMD_HEATER_WRITE 0x51
|
||||
/* Heater current configuration bits */
|
||||
#define SI7020_HEATER_VAL GENMASK(3, 0)
|
||||
|
||||
struct si7020_data {
|
||||
struct i2c_client *client;
|
||||
/* Lock for cached register values */
|
||||
struct mutex lock;
|
||||
u8 user_reg;
|
||||
u8 heater_reg;
|
||||
};
|
||||
|
||||
static const int si7020_heater_vals[] = { 0, 1, 0xF };
|
||||
|
||||
static int si7020_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct i2c_client **client = iio_priv(indio_dev);
|
||||
struct si7020_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = i2c_smbus_read_word_swapped(*client,
|
||||
if (chan->type == IIO_CURRENT) {
|
||||
*val = data->heater_reg;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(data->client,
|
||||
chan->type == IIO_TEMP ?
|
||||
SI7020CMD_TEMP_HOLD :
|
||||
SI7020CMD_RH_HOLD);
|
||||
@@ -96,17 +118,118 @@ static const struct iio_chan_spec si7020_channels[] = {
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
|
||||
},
|
||||
{
|
||||
.type = IIO_CURRENT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
|
||||
.extend_name = "heater",
|
||||
}
|
||||
};
|
||||
|
||||
static int si7020_update_reg(struct si7020_data *data,
|
||||
u8 *reg, u8 cmd, u8 mask, u8 val)
|
||||
{
|
||||
u8 new = (*reg & ~mask) | val;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, cmd, new);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*reg = new;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int si7020_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct si7020_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (chan->type != IIO_CURRENT || val2 != 0 ||
|
||||
val < si7020_heater_vals[0] || val > si7020_heater_vals[2])
|
||||
return -EINVAL;
|
||||
|
||||
scoped_guard(mutex, &data->lock)
|
||||
ret = si7020_update_reg(data, &data->heater_reg,
|
||||
SI7020CMD_HEATER_WRITE, SI7020_HEATER_VAL, val);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int si7020_read_available(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals,
|
||||
int *type, int *length, long mask)
|
||||
{
|
||||
if (mask != IIO_CHAN_INFO_RAW || chan->type != IIO_CURRENT)
|
||||
return -EINVAL;
|
||||
|
||||
*vals = si7020_heater_vals;
|
||||
*type = IIO_VAL_INT;
|
||||
|
||||
return IIO_AVAIL_RANGE;
|
||||
}
|
||||
|
||||
static ssize_t si7020_show_heater_en(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct si7020_data *data = iio_priv(indio_dev);
|
||||
|
||||
return sysfs_emit(buf, "%d\n", !!(data->user_reg & SI7020_USR_HEATER_EN));
|
||||
}
|
||||
|
||||
static ssize_t si7020_store_heater_en(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct si7020_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
bool val;
|
||||
|
||||
ret = kstrtobool(buf, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scoped_guard(mutex, &data->lock)
|
||||
ret = si7020_update_reg(data, &data->user_reg, SI7020CMD_USR_WRITE,
|
||||
SI7020_USR_HEATER_EN, val ? SI7020_USR_HEATER_EN : 0);
|
||||
|
||||
return ret < 0 ? ret : len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(heater_enable, 0644,
|
||||
si7020_show_heater_en, si7020_store_heater_en, 0);
|
||||
|
||||
static struct attribute *si7020_attributes[] = {
|
||||
&iio_dev_attr_heater_enable.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group si7020_attribute_group = {
|
||||
.attrs = si7020_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info si7020_info = {
|
||||
.read_raw = si7020_read_raw,
|
||||
.write_raw = si7020_write_raw,
|
||||
.read_avail = si7020_read_available,
|
||||
.attrs = &si7020_attribute_group,
|
||||
};
|
||||
|
||||
static int si7020_probe(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct i2c_client **data;
|
||||
struct si7020_data *data;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
@@ -126,7 +249,9 @@ static int si7020_probe(struct i2c_client *client)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
*data = client;
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
@@ -134,12 +259,16 @@ static int si7020_probe(struct i2c_client *client)
|
||||
indio_dev->channels = si7020_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(si7020_channels);
|
||||
|
||||
/* All the "reserved" bits in the User Register are 1s by default */
|
||||
data->user_reg = 0x3A;
|
||||
data->heater_reg = 0x0;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id si7020_id[] = {
|
||||
{ "si7020", 0 },
|
||||
{ "th06", 0 },
|
||||
{ "si7020" },
|
||||
{ "th06" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, si7020_id);
|
||||
|
||||
@@ -36,8 +36,8 @@ config ADIS16475
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices ADIS16470, ADIS16475,
|
||||
ADIS16477, ADIS16465, ADIS16467, ADIS16500, ADIS16505, ADIS16507 inertial
|
||||
sensors.
|
||||
ADIS16477, ADIS16465, ADIS16467, ADIS16500, ADIS16501, ADIS16505,
|
||||
ADIS16507 inertial sensors.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called adis16475.
|
||||
|
||||
+699
-110
File diff suppressed because it is too large
Load Diff
+343
-50
@@ -104,11 +104,10 @@
|
||||
*/
|
||||
#define ADIS16495_REG_SYNC_SCALE ADIS16480_REG(0x03, 0x10)
|
||||
#define ADIS16495_REG_BURST_CMD ADIS16480_REG(0x00, 0x7C)
|
||||
#define ADIS16495_BURST_ID 0xA5A5
|
||||
#define ADIS16495_GYRO_ACCEL_BURST_ID 0xA5A5
|
||||
#define ADIS16545_DELTA_ANG_VEL_BURST_ID 0xC3C3
|
||||
/* total number of segments in burst */
|
||||
#define ADIS16495_BURST_MAX_DATA 20
|
||||
/* spi max speed in burst mode */
|
||||
#define ADIS16495_BURST_MAX_SPEED 6000000
|
||||
|
||||
#define ADIS16480_REG_SERIAL_NUM ADIS16480_REG(0x04, 0x20)
|
||||
|
||||
@@ -134,6 +133,10 @@
|
||||
#define ADIS16480_SYNC_MODE_MSK BIT(8)
|
||||
#define ADIS16480_SYNC_MODE(x) FIELD_PREP(ADIS16480_SYNC_MODE_MSK, x)
|
||||
|
||||
#define ADIS16545_BURST_DATA_SEL_0_CHN_MASK GENMASK(5, 0)
|
||||
#define ADIS16545_BURST_DATA_SEL_1_CHN_MASK GENMASK(16, 11)
|
||||
#define ADIS16545_BURST_DATA_SEL_MASK BIT(8)
|
||||
|
||||
struct adis16480_chip_info {
|
||||
unsigned int num_channels;
|
||||
const struct iio_chan_spec *channels;
|
||||
@@ -142,11 +145,14 @@ struct adis16480_chip_info {
|
||||
unsigned int accel_max_val;
|
||||
unsigned int accel_max_scale;
|
||||
unsigned int temp_scale;
|
||||
unsigned int deltang_max_val;
|
||||
unsigned int deltvel_max_val;
|
||||
unsigned int int_clk;
|
||||
unsigned int max_dec_rate;
|
||||
const unsigned int *filter_freqs;
|
||||
bool has_pps_clk_mode;
|
||||
bool has_sleep_cnt;
|
||||
bool has_burst_delta_data;
|
||||
const struct adis_data adis_data;
|
||||
};
|
||||
|
||||
@@ -170,6 +176,7 @@ struct adis16480 {
|
||||
struct clk *ext_clk;
|
||||
enum adis16480_clock_mode clk_mode;
|
||||
unsigned int clk_freq;
|
||||
u16 burst_id;
|
||||
/* Alignment needed for the timestamp */
|
||||
__be16 data[ADIS16495_BURST_MAX_DATA] __aligned(8);
|
||||
};
|
||||
@@ -447,6 +454,12 @@ enum {
|
||||
ADIS16480_SCAN_MAGN_Z,
|
||||
ADIS16480_SCAN_BARO,
|
||||
ADIS16480_SCAN_TEMP,
|
||||
ADIS16480_SCAN_DELTANG_X,
|
||||
ADIS16480_SCAN_DELTANG_Y,
|
||||
ADIS16480_SCAN_DELTANG_Z,
|
||||
ADIS16480_SCAN_DELTVEL_X,
|
||||
ADIS16480_SCAN_DELTVEL_Y,
|
||||
ADIS16480_SCAN_DELTVEL_Z,
|
||||
};
|
||||
|
||||
static const unsigned int adis16480_calibbias_regs[] = {
|
||||
@@ -690,6 +703,14 @@ static int adis16480_read_raw(struct iio_dev *indio_dev,
|
||||
*val = 131; /* 1310mbar = 131 kPa */
|
||||
*val2 = 32767 << 16;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_DELTA_ANGL:
|
||||
*val = st->chip_info->deltang_max_val;
|
||||
*val2 = 31;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_DELTA_VELOCITY:
|
||||
*val = st->chip_info->deltvel_max_val;
|
||||
*val2 = 31;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -763,6 +784,24 @@ static int adis16480_write_raw(struct iio_dev *indio_dev,
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE), \
|
||||
32)
|
||||
|
||||
#define ADIS16480_DELTANG_CHANNEL(_mod) \
|
||||
ADIS16480_MOD_CHANNEL(IIO_DELTA_ANGL, IIO_MOD_ ## _mod, \
|
||||
ADIS16480_REG_ ## _mod ## _DELTAANG_OUT, ADIS16480_SCAN_DELTANG_ ## _mod, \
|
||||
0, 32)
|
||||
|
||||
#define ADIS16480_DELTANG_CHANNEL_NO_SCAN(_mod) \
|
||||
ADIS16480_MOD_CHANNEL(IIO_DELTA_ANGL, IIO_MOD_ ## _mod, \
|
||||
ADIS16480_REG_ ## _mod ## _DELTAANG_OUT, -1, 0, 32)
|
||||
|
||||
#define ADIS16480_DELTVEL_CHANNEL(_mod) \
|
||||
ADIS16480_MOD_CHANNEL(IIO_DELTA_VELOCITY, IIO_MOD_ ## _mod, \
|
||||
ADIS16480_REG_ ## _mod ## _DELTAVEL_OUT, ADIS16480_SCAN_DELTVEL_ ## _mod, \
|
||||
0, 32)
|
||||
|
||||
#define ADIS16480_DELTVEL_CHANNEL_NO_SCAN(_mod) \
|
||||
ADIS16480_MOD_CHANNEL(IIO_DELTA_VELOCITY, IIO_MOD_ ## _mod, \
|
||||
ADIS16480_REG_ ## _mod ## _DELTAVEL_OUT, -1, 0, 32)
|
||||
|
||||
#define ADIS16480_MAGN_CHANNEL(_mod) \
|
||||
ADIS16480_MOD_CHANNEL(IIO_MAGN, IIO_MOD_ ## _mod, \
|
||||
ADIS16480_REG_ ## _mod ## _MAGN_OUT, ADIS16480_SCAN_MAGN_ ## _mod, \
|
||||
@@ -818,7 +857,13 @@ static const struct iio_chan_spec adis16480_channels[] = {
|
||||
ADIS16480_MAGN_CHANNEL(Z),
|
||||
ADIS16480_PRESSURE_CHANNEL(),
|
||||
ADIS16480_TEMP_CHANNEL(),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(11)
|
||||
IIO_CHAN_SOFT_TIMESTAMP(11),
|
||||
ADIS16480_DELTANG_CHANNEL_NO_SCAN(X),
|
||||
ADIS16480_DELTANG_CHANNEL_NO_SCAN(Y),
|
||||
ADIS16480_DELTANG_CHANNEL_NO_SCAN(Z),
|
||||
ADIS16480_DELTVEL_CHANNEL_NO_SCAN(X),
|
||||
ADIS16480_DELTVEL_CHANNEL_NO_SCAN(Y),
|
||||
ADIS16480_DELTVEL_CHANNEL_NO_SCAN(Z),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adis16485_channels[] = {
|
||||
@@ -829,7 +874,30 @@ static const struct iio_chan_spec adis16485_channels[] = {
|
||||
ADIS16480_ACCEL_CHANNEL(Y),
|
||||
ADIS16480_ACCEL_CHANNEL(Z),
|
||||
ADIS16480_TEMP_CHANNEL(),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(7)
|
||||
IIO_CHAN_SOFT_TIMESTAMP(7),
|
||||
ADIS16480_DELTANG_CHANNEL_NO_SCAN(X),
|
||||
ADIS16480_DELTANG_CHANNEL_NO_SCAN(Y),
|
||||
ADIS16480_DELTANG_CHANNEL_NO_SCAN(Z),
|
||||
ADIS16480_DELTVEL_CHANNEL_NO_SCAN(X),
|
||||
ADIS16480_DELTVEL_CHANNEL_NO_SCAN(Y),
|
||||
ADIS16480_DELTVEL_CHANNEL_NO_SCAN(Z),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adis16545_channels[] = {
|
||||
ADIS16480_GYRO_CHANNEL(X),
|
||||
ADIS16480_GYRO_CHANNEL(Y),
|
||||
ADIS16480_GYRO_CHANNEL(Z),
|
||||
ADIS16480_ACCEL_CHANNEL(X),
|
||||
ADIS16480_ACCEL_CHANNEL(Y),
|
||||
ADIS16480_ACCEL_CHANNEL(Z),
|
||||
ADIS16480_TEMP_CHANNEL(),
|
||||
ADIS16480_DELTANG_CHANNEL(X),
|
||||
ADIS16480_DELTANG_CHANNEL(Y),
|
||||
ADIS16480_DELTANG_CHANNEL(Z),
|
||||
ADIS16480_DELTVEL_CHANNEL(X),
|
||||
ADIS16480_DELTVEL_CHANNEL(Y),
|
||||
ADIS16480_DELTVEL_CHANNEL(Z),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(17),
|
||||
};
|
||||
|
||||
enum adis16480_variant {
|
||||
@@ -844,6 +912,12 @@ enum adis16480_variant {
|
||||
ADIS16497_1,
|
||||
ADIS16497_2,
|
||||
ADIS16497_3,
|
||||
ADIS16545_1,
|
||||
ADIS16545_2,
|
||||
ADIS16545_3,
|
||||
ADIS16547_1,
|
||||
ADIS16547_2,
|
||||
ADIS16547_3
|
||||
};
|
||||
|
||||
#define ADIS16480_DIAG_STAT_XGYRO_FAIL 0
|
||||
@@ -872,33 +946,33 @@ static const char * const adis16480_status_error_msgs[] = {
|
||||
|
||||
static int adis16480_enable_irq(struct adis *adis, bool enable);
|
||||
|
||||
#define ADIS16480_DATA(_prod_id, _timeouts, _burst_len) \
|
||||
{ \
|
||||
.diag_stat_reg = ADIS16480_REG_DIAG_STS, \
|
||||
.glob_cmd_reg = ADIS16480_REG_GLOB_CMD, \
|
||||
.prod_id_reg = ADIS16480_REG_PROD_ID, \
|
||||
.prod_id = (_prod_id), \
|
||||
.has_paging = true, \
|
||||
.read_delay = 5, \
|
||||
.write_delay = 5, \
|
||||
.self_test_mask = BIT(1), \
|
||||
.self_test_reg = ADIS16480_REG_GLOB_CMD, \
|
||||
.status_error_msgs = adis16480_status_error_msgs, \
|
||||
.status_error_mask = BIT(ADIS16480_DIAG_STAT_XGYRO_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_YGYRO_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_ZGYRO_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_XACCL_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_YACCL_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_ZACCL_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_XMAGN_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_YMAGN_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_ZMAGN_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_BARO_FAIL), \
|
||||
.enable_irq = adis16480_enable_irq, \
|
||||
.timeouts = (_timeouts), \
|
||||
.burst_reg_cmd = ADIS16495_REG_BURST_CMD, \
|
||||
.burst_len = (_burst_len), \
|
||||
.burst_max_speed_hz = ADIS16495_BURST_MAX_SPEED \
|
||||
#define ADIS16480_DATA(_prod_id, _timeouts, _burst_len, _burst_max_speed) \
|
||||
{ \
|
||||
.diag_stat_reg = ADIS16480_REG_DIAG_STS, \
|
||||
.glob_cmd_reg = ADIS16480_REG_GLOB_CMD, \
|
||||
.prod_id_reg = ADIS16480_REG_PROD_ID, \
|
||||
.prod_id = (_prod_id), \
|
||||
.has_paging = true, \
|
||||
.read_delay = 5, \
|
||||
.write_delay = 5, \
|
||||
.self_test_mask = BIT(1), \
|
||||
.self_test_reg = ADIS16480_REG_GLOB_CMD, \
|
||||
.status_error_msgs = adis16480_status_error_msgs, \
|
||||
.status_error_mask = BIT(ADIS16480_DIAG_STAT_XGYRO_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_YGYRO_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_ZGYRO_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_XACCL_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_YACCL_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_ZACCL_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_XMAGN_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_YMAGN_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_ZMAGN_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_BARO_FAIL), \
|
||||
.enable_irq = adis16480_enable_irq, \
|
||||
.timeouts = (_timeouts), \
|
||||
.burst_reg_cmd = ADIS16495_REG_BURST_CMD, \
|
||||
.burst_len = (_burst_len), \
|
||||
.burst_max_speed_hz = _burst_max_speed \
|
||||
}
|
||||
|
||||
static const struct adis_timeout adis16485_timeouts = {
|
||||
@@ -925,6 +999,12 @@ static const struct adis_timeout adis16495_1_timeouts = {
|
||||
.self_test_ms = 20,
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16545_timeouts = {
|
||||
.reset_ms = 315,
|
||||
.sw_reset_ms = 270,
|
||||
.self_test_ms = 35,
|
||||
};
|
||||
|
||||
static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
[ADIS16375] = {
|
||||
.channels = adis16485_channels,
|
||||
@@ -940,11 +1020,13 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.accel_max_val = IIO_M_S_2_TO_G(21973 << 16),
|
||||
.accel_max_scale = 18,
|
||||
.temp_scale = 5650, /* 5.65 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(180),
|
||||
.deltvel_max_val = 100,
|
||||
.int_clk = 2460000,
|
||||
.max_dec_rate = 2048,
|
||||
.has_sleep_cnt = true,
|
||||
.filter_freqs = adis16480_def_filter_freqs,
|
||||
.adis_data = ADIS16480_DATA(16375, &adis16485_timeouts, 0),
|
||||
.adis_data = ADIS16480_DATA(16375, &adis16485_timeouts, 0, 0),
|
||||
},
|
||||
[ADIS16480] = {
|
||||
.channels = adis16480_channels,
|
||||
@@ -954,11 +1036,13 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.accel_max_val = IIO_M_S_2_TO_G(12500 << 16),
|
||||
.accel_max_scale = 10,
|
||||
.temp_scale = 5650, /* 5.65 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(720),
|
||||
.deltvel_max_val = 200,
|
||||
.int_clk = 2460000,
|
||||
.max_dec_rate = 2048,
|
||||
.has_sleep_cnt = true,
|
||||
.filter_freqs = adis16480_def_filter_freqs,
|
||||
.adis_data = ADIS16480_DATA(16480, &adis16480_timeouts, 0),
|
||||
.adis_data = ADIS16480_DATA(16480, &adis16480_timeouts, 0, 0),
|
||||
},
|
||||
[ADIS16485] = {
|
||||
.channels = adis16485_channels,
|
||||
@@ -968,11 +1052,13 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.accel_max_val = IIO_M_S_2_TO_G(20000 << 16),
|
||||
.accel_max_scale = 5,
|
||||
.temp_scale = 5650, /* 5.65 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(720),
|
||||
.deltvel_max_val = 50,
|
||||
.int_clk = 2460000,
|
||||
.max_dec_rate = 2048,
|
||||
.has_sleep_cnt = true,
|
||||
.filter_freqs = adis16480_def_filter_freqs,
|
||||
.adis_data = ADIS16480_DATA(16485, &adis16485_timeouts, 0),
|
||||
.adis_data = ADIS16480_DATA(16485, &adis16485_timeouts, 0, 0),
|
||||
},
|
||||
[ADIS16488] = {
|
||||
.channels = adis16480_channels,
|
||||
@@ -982,11 +1068,13 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.accel_max_val = IIO_M_S_2_TO_G(22500 << 16),
|
||||
.accel_max_scale = 18,
|
||||
.temp_scale = 5650, /* 5.65 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(720),
|
||||
.deltvel_max_val = 200,
|
||||
.int_clk = 2460000,
|
||||
.max_dec_rate = 2048,
|
||||
.has_sleep_cnt = true,
|
||||
.filter_freqs = adis16480_def_filter_freqs,
|
||||
.adis_data = ADIS16480_DATA(16488, &adis16485_timeouts, 0),
|
||||
.adis_data = ADIS16480_DATA(16488, &adis16485_timeouts, 0, 0),
|
||||
},
|
||||
[ADIS16490] = {
|
||||
.channels = adis16485_channels,
|
||||
@@ -996,11 +1084,13 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.accel_max_val = IIO_M_S_2_TO_G(16000 << 16),
|
||||
.accel_max_scale = 8,
|
||||
.temp_scale = 14285, /* 14.285 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(720),
|
||||
.deltvel_max_val = 200,
|
||||
.int_clk = 4250000,
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.adis_data = ADIS16480_DATA(16490, &adis16495_timeouts, 0),
|
||||
.adis_data = ADIS16480_DATA(16490, &adis16495_timeouts, 0, 0),
|
||||
},
|
||||
[ADIS16495_1] = {
|
||||
.channels = adis16485_channels,
|
||||
@@ -1010,13 +1100,16 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 8,
|
||||
.temp_scale = 12500, /* 12.5 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(360),
|
||||
.deltvel_max_val = 100,
|
||||
.int_clk = 4250000,
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
/* 20 elements of 16bits */
|
||||
.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts,
|
||||
ADIS16495_BURST_MAX_DATA * 2),
|
||||
ADIS16495_BURST_MAX_DATA * 2,
|
||||
6000000),
|
||||
},
|
||||
[ADIS16495_2] = {
|
||||
.channels = adis16485_channels,
|
||||
@@ -1026,13 +1119,16 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 8,
|
||||
.temp_scale = 12500, /* 12.5 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(720),
|
||||
.deltvel_max_val = 100,
|
||||
.int_clk = 4250000,
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
/* 20 elements of 16bits */
|
||||
.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts,
|
||||
ADIS16495_BURST_MAX_DATA * 2),
|
||||
ADIS16495_BURST_MAX_DATA * 2,
|
||||
6000000),
|
||||
},
|
||||
[ADIS16495_3] = {
|
||||
.channels = adis16485_channels,
|
||||
@@ -1042,13 +1138,16 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 8,
|
||||
.temp_scale = 12500, /* 12.5 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(2160),
|
||||
.deltvel_max_val = 100,
|
||||
.int_clk = 4250000,
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
/* 20 elements of 16bits */
|
||||
.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts,
|
||||
ADIS16495_BURST_MAX_DATA * 2),
|
||||
ADIS16495_BURST_MAX_DATA * 2,
|
||||
6000000),
|
||||
},
|
||||
[ADIS16497_1] = {
|
||||
.channels = adis16485_channels,
|
||||
@@ -1058,13 +1157,16 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 40,
|
||||
.temp_scale = 12500, /* 12.5 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(360),
|
||||
.deltvel_max_val = 400,
|
||||
.int_clk = 4250000,
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
/* 20 elements of 16bits */
|
||||
.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts,
|
||||
ADIS16495_BURST_MAX_DATA * 2),
|
||||
ADIS16495_BURST_MAX_DATA * 2,
|
||||
6000000),
|
||||
},
|
||||
[ADIS16497_2] = {
|
||||
.channels = adis16485_channels,
|
||||
@@ -1074,13 +1176,16 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 40,
|
||||
.temp_scale = 12500, /* 12.5 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(720),
|
||||
.deltvel_max_val = 400,
|
||||
.int_clk = 4250000,
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
/* 20 elements of 16bits */
|
||||
.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts,
|
||||
ADIS16495_BURST_MAX_DATA * 2),
|
||||
ADIS16495_BURST_MAX_DATA * 2,
|
||||
6000000),
|
||||
},
|
||||
[ADIS16497_3] = {
|
||||
.channels = adis16485_channels,
|
||||
@@ -1090,13 +1195,136 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 40,
|
||||
.temp_scale = 12500, /* 12.5 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(2160),
|
||||
.deltvel_max_val = 400,
|
||||
.int_clk = 4250000,
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
/* 20 elements of 16bits */
|
||||
.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts,
|
||||
ADIS16495_BURST_MAX_DATA * 2),
|
||||
ADIS16495_BURST_MAX_DATA * 2,
|
||||
6000000),
|
||||
},
|
||||
[ADIS16545_1] = {
|
||||
.channels = adis16545_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16545_channels),
|
||||
.gyro_max_val = 20000 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(125),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 8,
|
||||
.temp_scale = 7000, /* 7 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(360),
|
||||
.deltvel_max_val = 100,
|
||||
.int_clk = 4250000,
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.has_burst_delta_data = true,
|
||||
/* 20 elements of 16bits */
|
||||
.adis_data = ADIS16480_DATA(16545, &adis16545_timeouts,
|
||||
ADIS16495_BURST_MAX_DATA * 2,
|
||||
6500000),
|
||||
},
|
||||
[ADIS16545_2] = {
|
||||
.channels = adis16545_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16545_channels),
|
||||
.gyro_max_val = 18000 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(450),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 8,
|
||||
.temp_scale = 7000, /* 7 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(720),
|
||||
.deltvel_max_val = 100,
|
||||
.int_clk = 4250000,
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.has_burst_delta_data = true,
|
||||
/* 20 elements of 16bits */
|
||||
.adis_data = ADIS16480_DATA(16545, &adis16545_timeouts,
|
||||
ADIS16495_BURST_MAX_DATA * 2,
|
||||
6500000),
|
||||
},
|
||||
[ADIS16545_3] = {
|
||||
.channels = adis16545_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16545_channels),
|
||||
.gyro_max_val = 20000 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(2000),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 8,
|
||||
.temp_scale = 7000, /* 7 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(2160),
|
||||
.deltvel_max_val = 100,
|
||||
.int_clk = 4250000,
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.has_burst_delta_data = true,
|
||||
/* 20 elements of 16bits */
|
||||
.adis_data = ADIS16480_DATA(16545, &adis16545_timeouts,
|
||||
ADIS16495_BURST_MAX_DATA * 2,
|
||||
6500000),
|
||||
},
|
||||
[ADIS16547_1] = {
|
||||
.channels = adis16545_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16545_channels),
|
||||
.gyro_max_val = 20000 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(125),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 40,
|
||||
.temp_scale = 7000, /* 7 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(360),
|
||||
.deltvel_max_val = 400,
|
||||
.int_clk = 4250000,
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.has_burst_delta_data = true,
|
||||
/* 20 elements of 16bits */
|
||||
.adis_data = ADIS16480_DATA(16547, &adis16545_timeouts,
|
||||
ADIS16495_BURST_MAX_DATA * 2,
|
||||
6500000),
|
||||
},
|
||||
[ADIS16547_2] = {
|
||||
.channels = adis16545_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16545_channels),
|
||||
.gyro_max_val = 18000 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(450),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 40,
|
||||
.temp_scale = 7000, /* 7 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(720),
|
||||
.deltvel_max_val = 400,
|
||||
.int_clk = 4250000,
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.has_burst_delta_data = true,
|
||||
/* 20 elements of 16bits */
|
||||
.adis_data = ADIS16480_DATA(16547, &adis16545_timeouts,
|
||||
ADIS16495_BURST_MAX_DATA * 2,
|
||||
6500000),
|
||||
},
|
||||
[ADIS16547_3] = {
|
||||
.channels = adis16545_channels,
|
||||
.num_channels = ARRAY_SIZE(adis16545_channels),
|
||||
.gyro_max_val = 20000 << 16,
|
||||
.gyro_max_scale = IIO_DEGREE_TO_RAD(2000),
|
||||
.accel_max_val = IIO_M_S_2_TO_G(32000 << 16),
|
||||
.accel_max_scale = 40,
|
||||
.temp_scale = 7000, /* 7 milli degree Celsius */
|
||||
.deltang_max_val = IIO_DEGREE_TO_RAD(2160),
|
||||
.deltvel_max_val = 400,
|
||||
.int_clk = 4250000,
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.has_burst_delta_data = true,
|
||||
/* 20 elements of 16bits */
|
||||
.adis_data = ADIS16480_DATA(16547, &adis16545_timeouts,
|
||||
ADIS16495_BURST_MAX_DATA * 2,
|
||||
6500000),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1122,7 +1350,7 @@ static irqreturn_t adis16480_trigger_handler(int irq, void *p)
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
struct adis *adis = &st->adis;
|
||||
struct device *dev = &adis->spi->dev;
|
||||
int ret, bit, offset, i = 0;
|
||||
int ret, bit, offset, i = 0, buff_offset = 0;
|
||||
__be16 *buffer;
|
||||
u32 crc;
|
||||
bool valid;
|
||||
@@ -1155,8 +1383,8 @@ static irqreturn_t adis16480_trigger_handler(int irq, void *p)
|
||||
* 16-bit responses containing the BURST_ID depending on the sclk. If
|
||||
* clk > 3.6MHz, then we will have two BURST_ID in a row. If clk < 3MHZ,
|
||||
* we have only one. To manage that variation, we use the transition from the
|
||||
* BURST_ID to the SYS_E_FLAG register, which will not be equal to 0xA5A5. If
|
||||
* we not find this variation in the first 4 segments, then the data should
|
||||
* BURST_ID to the SYS_E_FLAG register, which will not be equal to 0xA5A5/0xC3C3.
|
||||
* If we not find this variation in the first 4 segments, then the data should
|
||||
* not be valid.
|
||||
*/
|
||||
buffer = adis->buffer;
|
||||
@@ -1164,7 +1392,7 @@ static irqreturn_t adis16480_trigger_handler(int irq, void *p)
|
||||
u16 curr = be16_to_cpu(buffer[offset]);
|
||||
u16 next = be16_to_cpu(buffer[offset + 1]);
|
||||
|
||||
if (curr == ADIS16495_BURST_ID && next != ADIS16495_BURST_ID) {
|
||||
if (curr == st->burst_id && next != st->burst_id) {
|
||||
offset++;
|
||||
break;
|
||||
}
|
||||
@@ -1191,11 +1419,24 @@ static irqreturn_t adis16480_trigger_handler(int irq, void *p)
|
||||
switch (bit) {
|
||||
case ADIS16480_SCAN_TEMP:
|
||||
st->data[i++] = buffer[offset + 1];
|
||||
/*
|
||||
* The temperature channel has 16-bit storage size.
|
||||
* We need to perform the padding to have the buffer
|
||||
* elements naturally aligned in case there are any
|
||||
* 32-bit storage size channels enabled which are added
|
||||
* in the buffer after the temprature data. In case
|
||||
* there is no data being added after the temperature
|
||||
* data, the padding is harmless.
|
||||
*/
|
||||
st->data[i++] = 0;
|
||||
break;
|
||||
case ADIS16480_SCAN_DELTANG_X ... ADIS16480_SCAN_DELTVEL_Z:
|
||||
buff_offset = ADIS16480_SCAN_DELTANG_X;
|
||||
fallthrough;
|
||||
case ADIS16480_SCAN_GYRO_X ... ADIS16480_SCAN_ACCEL_Z:
|
||||
/* The lower register data is sequenced first */
|
||||
st->data[i++] = buffer[2 * bit + offset + 3];
|
||||
st->data[i++] = buffer[2 * bit + offset + 2];
|
||||
st->data[i++] = buffer[2 * (bit - buff_offset) + offset + 3];
|
||||
st->data[i++] = buffer[2 * (bit - buff_offset) + offset + 2];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1207,10 +1448,41 @@ irq_done:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const unsigned long adis16545_channel_masks[] = {
|
||||
ADIS16545_BURST_DATA_SEL_0_CHN_MASK | BIT(ADIS16480_SCAN_TEMP) | BIT(17),
|
||||
ADIS16545_BURST_DATA_SEL_1_CHN_MASK | BIT(ADIS16480_SCAN_TEMP) | BIT(17),
|
||||
0,
|
||||
};
|
||||
|
||||
static int adis16480_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
u16 en;
|
||||
int ret;
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
|
||||
if (st->chip_info->has_burst_delta_data) {
|
||||
if (*scan_mask & ADIS16545_BURST_DATA_SEL_0_CHN_MASK) {
|
||||
en = FIELD_PREP(ADIS16545_BURST_DATA_SEL_MASK, 0);
|
||||
st->burst_id = ADIS16495_GYRO_ACCEL_BURST_ID;
|
||||
} else {
|
||||
en = FIELD_PREP(ADIS16545_BURST_DATA_SEL_MASK, 1);
|
||||
st->burst_id = ADIS16545_DELTA_ANG_VEL_BURST_ID;
|
||||
}
|
||||
|
||||
ret = __adis_update_bits(&st->adis, ADIS16480_REG_CONFIG,
|
||||
ADIS16545_BURST_DATA_SEL_MASK, en);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return adis_update_scan_mode(indio_dev, scan_mask);
|
||||
}
|
||||
|
||||
static const struct iio_info adis16480_info = {
|
||||
.read_raw = &adis16480_read_raw,
|
||||
.write_raw = &adis16480_write_raw,
|
||||
.update_scan_mode = adis_update_scan_mode,
|
||||
.update_scan_mode = &adis16480_update_scan_mode,
|
||||
.debugfs_reg_access = adis_debugfs_reg_access,
|
||||
};
|
||||
|
||||
@@ -1407,6 +1679,8 @@ static int adis16480_probe(struct spi_device *spi)
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
if (st->chip_info->has_burst_delta_data)
|
||||
indio_dev->available_scan_masks = adis16545_channel_masks;
|
||||
indio_dev->info = &adis16480_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
@@ -1420,6 +1694,13 @@ static int adis16480_probe(struct spi_device *spi)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* By default, use burst id for gyroscope and accelerometer data.
|
||||
* This is the only option for devices which do not offer delta angle
|
||||
* and delta velocity burst readings.
|
||||
*/
|
||||
st->burst_id = ADIS16495_GYRO_ACCEL_BURST_ID;
|
||||
|
||||
if (st->chip_info->has_sleep_cnt) {
|
||||
ret = devm_add_action_or_reset(dev, adis16480_stop, indio_dev);
|
||||
if (ret)
|
||||
@@ -1493,6 +1774,12 @@ static const struct spi_device_id adis16480_ids[] = {
|
||||
{ "adis16497-1", ADIS16497_1 },
|
||||
{ "adis16497-2", ADIS16497_2 },
|
||||
{ "adis16497-3", ADIS16497_3 },
|
||||
{ "adis16545-1", ADIS16545_1 },
|
||||
{ "adis16545-2", ADIS16545_2 },
|
||||
{ "adis16545-3", ADIS16545_3 },
|
||||
{ "adis16547-1", ADIS16547_1 },
|
||||
{ "adis16547-2", ADIS16547_2 },
|
||||
{ "adis16547-3", ADIS16547_3 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adis16480_ids);
|
||||
@@ -1509,6 +1796,12 @@ static const struct of_device_id adis16480_of_match[] = {
|
||||
{ .compatible = "adi,adis16497-1" },
|
||||
{ .compatible = "adi,adis16497-2" },
|
||||
{ .compatible = "adi,adis16497-3" },
|
||||
{ .compatible = "adi,adis16545-1" },
|
||||
{ .compatible = "adi,adis16545-2" },
|
||||
{ .compatible = "adi,adis16545-3" },
|
||||
{ .compatible = "adi,adis16547-1" },
|
||||
{ .compatible = "adi,adis16547-2" },
|
||||
{ .compatible = "adi,adis16547-3" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adis16480_of_match);
|
||||
|
||||
@@ -175,31 +175,36 @@ static void adis_buffer_cleanup(void *arg)
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_adis_setup_buffer_and_trigger() - Sets up buffer and trigger for
|
||||
* the managed adis device
|
||||
* devm_adis_setup_buffer_and_trigger_with_attrs() - Sets up buffer and trigger
|
||||
* for the managed adis device with buffer attributes.
|
||||
* @adis: The adis device
|
||||
* @indio_dev: The IIO device
|
||||
* @trigger_handler: Optional trigger handler, may be NULL.
|
||||
* @trigger_handler: Trigger handler: should handle the buffer readings.
|
||||
* @ops: Optional buffer setup functions, may be NULL.
|
||||
* @buffer_attrs: Extra buffer attributes.
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise.
|
||||
*
|
||||
* This function sets up the buffer and trigger for a adis devices. If
|
||||
* 'trigger_handler' is NULL the default trigger handler will be used. The
|
||||
* default trigger handler will simply read the registers assigned to the
|
||||
* currently active channels.
|
||||
* This function sets up the buffer (with buffer setup functions and extra
|
||||
* buffer attributes) and trigger for a adis devices with buffer attributes.
|
||||
*/
|
||||
int
|
||||
devm_adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev,
|
||||
irq_handler_t trigger_handler)
|
||||
devm_adis_setup_buffer_and_trigger_with_attrs(struct adis *adis, struct iio_dev *indio_dev,
|
||||
irq_handler_t trigger_handler,
|
||||
const struct iio_buffer_setup_ops *ops,
|
||||
const struct iio_dev_attr **buffer_attrs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!trigger_handler)
|
||||
trigger_handler = adis_trigger_handler;
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(&adis->spi->dev, indio_dev,
|
||||
&iio_pollfunc_store_time,
|
||||
trigger_handler, NULL);
|
||||
ret = devm_iio_triggered_buffer_setup_ext(&adis->spi->dev, indio_dev,
|
||||
&iio_pollfunc_store_time,
|
||||
trigger_handler,
|
||||
IIO_BUFFER_DIRECTION_IN,
|
||||
ops,
|
||||
buffer_attrs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -212,5 +217,4 @@ devm_adis_setup_buffer_and_trigger(struct adis *adis, struct iio_dev *indio_dev,
|
||||
return devm_add_action_or_reset(&adis->spi->dev, adis_buffer_cleanup,
|
||||
adis);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(devm_adis_setup_buffer_and_trigger, IIO_ADISLIB);
|
||||
|
||||
EXPORT_SYMBOL_NS_GPL(devm_adis_setup_buffer_and_trigger_with_attrs, IIO_ADISLIB);
|
||||
|
||||
@@ -34,17 +34,24 @@ static int adis_validate_irq_flag(struct adis *adis)
|
||||
if (adis->data->unmasked_drdy)
|
||||
adis->irq_flag |= IRQF_NO_AUTOEN;
|
||||
/*
|
||||
* Typically this devices have data ready either on the rising edge or
|
||||
* on the falling edge of the data ready pin. This checks enforces that
|
||||
* one of those is set in the drivers... It defaults to
|
||||
* IRQF_TRIGGER_RISING for backward compatibility with devices that
|
||||
* don't support changing the pin polarity.
|
||||
* Typically adis devices without FIFO have data ready either on the
|
||||
* rising edge or on the falling edge of the data ready pin.
|
||||
* IMU devices with FIFO support have the watermark pin level driven
|
||||
* either high or low when the FIFO is filled with the desired number
|
||||
* of samples.
|
||||
* It defaults to IRQF_TRIGGER_RISING for backward compatibility with
|
||||
* devices that don't support changing the pin polarity.
|
||||
*/
|
||||
if (direction == IRQF_TRIGGER_NONE) {
|
||||
adis->irq_flag |= IRQF_TRIGGER_RISING;
|
||||
return 0;
|
||||
} else if (direction != IRQF_TRIGGER_RISING &&
|
||||
direction != IRQF_TRIGGER_FALLING) {
|
||||
direction != IRQF_TRIGGER_FALLING && !adis->data->has_fifo) {
|
||||
dev_err(&adis->spi->dev, "Invalid IRQ mask: %08lx\n",
|
||||
adis->irq_flag);
|
||||
return -EINVAL;
|
||||
} else if (direction != IRQF_TRIGGER_HIGH &&
|
||||
direction != IRQF_TRIGGER_LOW && adis->data->has_fifo) {
|
||||
dev_err(&adis->spi->dev, "Invalid IRQ mask: %08lx\n",
|
||||
adis->irq_flag);
|
||||
return -EINVAL;
|
||||
@@ -77,11 +84,19 @@ int devm_adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_irq(&adis->spi->dev, adis->spi->irq,
|
||||
&iio_trigger_generic_data_rdy_poll,
|
||||
adis->irq_flag,
|
||||
indio_dev->name,
|
||||
adis->trig);
|
||||
if (adis->data->has_fifo)
|
||||
ret = devm_request_threaded_irq(&adis->spi->dev, adis->spi->irq,
|
||||
NULL,
|
||||
&iio_trigger_generic_data_rdy_poll,
|
||||
adis->irq_flag | IRQF_ONESHOT,
|
||||
indio_dev->name,
|
||||
adis->trig);
|
||||
else
|
||||
ret = devm_request_irq(&adis->spi->dev, adis->spi->irq,
|
||||
&iio_trigger_generic_data_rdy_poll,
|
||||
adis->irq_flag,
|
||||
indio_dev->name,
|
||||
adis->trig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "bmi160.h"
|
||||
|
||||
#define BMI160_REG_CHIP_ID 0x00
|
||||
#define BMI120_CHIP_ID_VAL 0xD3
|
||||
#define BMI160_CHIP_ID_VAL 0xD1
|
||||
|
||||
#define BMI160_REG_PMU_STATUS 0x03
|
||||
@@ -112,6 +113,11 @@
|
||||
.ext_info = bmi160_ext_info, \
|
||||
}
|
||||
|
||||
static const u8 bmi_chip_ids[] = {
|
||||
BMI120_CHIP_ID_VAL,
|
||||
BMI160_CHIP_ID_VAL,
|
||||
};
|
||||
|
||||
/* scan indexes follow DATA register order */
|
||||
enum bmi160_scan_axis {
|
||||
BMI160_SCAN_EXT_MAGN_X = 0,
|
||||
@@ -704,6 +710,16 @@ static int bmi160_setup_irq(struct iio_dev *indio_dev, int irq,
|
||||
return bmi160_probe_trigger(indio_dev, irq, irq_type);
|
||||
}
|
||||
|
||||
static int bmi160_check_chip_id(const u8 chip_id)
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(bmi_chip_ids); i++) {
|
||||
if (chip_id == bmi_chip_ids[i])
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
|
||||
{
|
||||
int ret;
|
||||
@@ -737,12 +753,10 @@ static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
|
||||
dev_err(dev, "Error reading chip id\n");
|
||||
goto disable_regulator;
|
||||
}
|
||||
if (val != BMI160_CHIP_ID_VAL) {
|
||||
dev_err(dev, "Wrong chip id, got %x expected %x\n",
|
||||
val, BMI160_CHIP_ID_VAL);
|
||||
ret = -ENODEV;
|
||||
goto disable_regulator;
|
||||
}
|
||||
|
||||
ret = bmi160_check_chip_id(val);
|
||||
if (ret)
|
||||
dev_warn(dev, "Chip id not found: %x\n", val);
|
||||
|
||||
ret = bmi160_set_mode(data, BMI160_ACCEL, true);
|
||||
if (ret)
|
||||
|
||||
@@ -37,7 +37,8 @@ static int bmi160_i2c_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bmi160_i2c_id[] = {
|
||||
{"bmi160", 0},
|
||||
{ "bmi120" },
|
||||
{ "bmi160" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bmi160_i2c_id);
|
||||
@@ -52,12 +53,14 @@ static const struct acpi_device_id bmi160_acpi_match[] = {
|
||||
* the affected devices are from 2021/2022.
|
||||
*/
|
||||
{"10EC5280", 0},
|
||||
{"BMI0120", 0},
|
||||
{"BMI0160", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match);
|
||||
|
||||
static const struct of_device_id bmi160_of_match[] = {
|
||||
{ .compatible = "bosch,bmi120" },
|
||||
{ .compatible = "bosch,bmi160" },
|
||||
{ },
|
||||
};
|
||||
|
||||
@@ -34,18 +34,21 @@ static int bmi160_spi_probe(struct spi_device *spi)
|
||||
}
|
||||
|
||||
static const struct spi_device_id bmi160_spi_id[] = {
|
||||
{"bmi120", 0},
|
||||
{"bmi160", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, bmi160_spi_id);
|
||||
|
||||
static const struct acpi_device_id bmi160_acpi_match[] = {
|
||||
{"BMI0120", 0},
|
||||
{"BMI0160", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bmi160_acpi_match);
|
||||
|
||||
static const struct of_device_id bmi160_of_match[] = {
|
||||
{ .compatible = "bosch,bmi120" },
|
||||
{ .compatible = "bosch,bmi160" },
|
||||
{ },
|
||||
};
|
||||
|
||||
@@ -2083,9 +2083,11 @@ int bmi323_core_probe(struct device *dev)
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
ret = iio_read_mount_matrix(dev, &data->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!iio_read_acpi_mount_matrix(dev, &data->orientation, "ROTM")) {
|
||||
ret = iio_read_mount_matrix(dev, &data->orientation);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev->name = "bmi323-imu";
|
||||
indio_dev->info = &bmi323_info;
|
||||
|
||||
@@ -30,7 +30,7 @@ static int bno055_i2c_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bno055_i2c_id[] = {
|
||||
{"bno055", 0},
|
||||
{ "bno055" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bno055_i2c_id);
|
||||
|
||||
@@ -36,7 +36,7 @@ static int fxos8700_i2c_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id fxos8700_i2c_id[] = {
|
||||
{"fxos8700", 0},
|
||||
{ "fxos8700" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, fxos8700_i2c_id);
|
||||
|
||||
@@ -177,11 +177,15 @@ struct inv_icm42600_state {
|
||||
* struct inv_icm42600_sensor_state - sensor state variables
|
||||
* @scales: table of scales.
|
||||
* @scales_len: length (nb of items) of the scales table.
|
||||
* @power_mode: sensor requested power mode (for common frequencies)
|
||||
* @filter: sensor filter.
|
||||
* @ts: timestamp module states.
|
||||
*/
|
||||
struct inv_icm42600_sensor_state {
|
||||
const int *scales;
|
||||
size_t scales_len;
|
||||
enum inv_icm42600_sensor_mode power_mode;
|
||||
enum inv_icm42600_filter filter;
|
||||
struct inv_sensors_timestamp ts;
|
||||
};
|
||||
|
||||
|
||||
@@ -55,8 +55,108 @@ enum inv_icm42600_accel_scan {
|
||||
INV_ICM42600_ACCEL_SCAN_TIMESTAMP,
|
||||
};
|
||||
|
||||
static const char * const inv_icm42600_accel_power_mode_items[] = {
|
||||
"low-noise",
|
||||
"low-power",
|
||||
};
|
||||
static const int inv_icm42600_accel_power_mode_values[] = {
|
||||
INV_ICM42600_SENSOR_MODE_LOW_NOISE,
|
||||
INV_ICM42600_SENSOR_MODE_LOW_POWER,
|
||||
};
|
||||
static const int inv_icm42600_accel_filter_values[] = {
|
||||
INV_ICM42600_FILTER_BW_ODR_DIV_2,
|
||||
INV_ICM42600_FILTER_AVG_16X,
|
||||
};
|
||||
|
||||
static int inv_icm42600_accel_power_mode_set(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int idx)
|
||||
{
|
||||
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
||||
struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev);
|
||||
int power_mode, filter;
|
||||
|
||||
if (chan->type != IIO_ACCEL)
|
||||
return -EINVAL;
|
||||
|
||||
if (idx >= ARRAY_SIZE(inv_icm42600_accel_power_mode_values))
|
||||
return -EINVAL;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
power_mode = inv_icm42600_accel_power_mode_values[idx];
|
||||
filter = inv_icm42600_accel_filter_values[idx];
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
|
||||
/* prevent change if power mode is not supported by the ODR */
|
||||
switch (power_mode) {
|
||||
case INV_ICM42600_SENSOR_MODE_LOW_NOISE:
|
||||
if (st->conf.accel.odr >= INV_ICM42600_ODR_6_25HZ_LP &&
|
||||
st->conf.accel.odr <= INV_ICM42600_ODR_1_5625HZ_LP)
|
||||
return -EPERM;
|
||||
break;
|
||||
case INV_ICM42600_SENSOR_MODE_LOW_POWER:
|
||||
default:
|
||||
if (st->conf.accel.odr <= INV_ICM42600_ODR_1KHZ_LN)
|
||||
return -EPERM;
|
||||
break;
|
||||
}
|
||||
|
||||
accel_st->power_mode = power_mode;
|
||||
accel_st->filter = filter;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int inv_icm42600_accel_power_mode_get(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
||||
struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev);
|
||||
unsigned int idx;
|
||||
int power_mode;
|
||||
|
||||
if (chan->type != IIO_ACCEL)
|
||||
return -EINVAL;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
|
||||
/* if sensor is on, returns actual power mode and not configured one */
|
||||
switch (st->conf.accel.mode) {
|
||||
case INV_ICM42600_SENSOR_MODE_LOW_POWER:
|
||||
case INV_ICM42600_SENSOR_MODE_LOW_NOISE:
|
||||
power_mode = st->conf.accel.mode;
|
||||
break;
|
||||
default:
|
||||
power_mode = accel_st->power_mode;
|
||||
break;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(inv_icm42600_accel_power_mode_values); ++idx) {
|
||||
if (power_mode == inv_icm42600_accel_power_mode_values[idx])
|
||||
break;
|
||||
}
|
||||
if (idx >= ARRAY_SIZE(inv_icm42600_accel_power_mode_values))
|
||||
return -EINVAL;
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static const struct iio_enum inv_icm42600_accel_power_mode_enum = {
|
||||
.items = inv_icm42600_accel_power_mode_items,
|
||||
.num_items = ARRAY_SIZE(inv_icm42600_accel_power_mode_items),
|
||||
.set = inv_icm42600_accel_power_mode_set,
|
||||
.get = inv_icm42600_accel_power_mode_get,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info inv_icm42600_accel_ext_infos[] = {
|
||||
IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42600_get_mount_matrix),
|
||||
IIO_ENUM_AVAILABLE("power_mode", IIO_SHARED_BY_TYPE,
|
||||
&inv_icm42600_accel_power_mode_enum),
|
||||
IIO_ENUM("power_mode", IIO_SHARED_BY_TYPE,
|
||||
&inv_icm42600_accel_power_mode_enum),
|
||||
{},
|
||||
};
|
||||
|
||||
@@ -120,7 +220,8 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev,
|
||||
|
||||
if (*scan_mask & INV_ICM42600_SCAN_MASK_ACCEL_3AXIS) {
|
||||
/* enable accel sensor */
|
||||
conf.mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE;
|
||||
conf.mode = accel_st->power_mode;
|
||||
conf.filter = accel_st->filter;
|
||||
ret = inv_icm42600_set_accel_conf(st, &conf, &sleep_accel);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
@@ -144,10 +245,12 @@ out_unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int inv_icm42600_accel_read_sensor(struct inv_icm42600_state *st,
|
||||
static int inv_icm42600_accel_read_sensor(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int16_t *val)
|
||||
{
|
||||
struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev);
|
||||
struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev);
|
||||
struct device *dev = regmap_get_device(st->map);
|
||||
struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT;
|
||||
unsigned int reg;
|
||||
@@ -175,7 +278,8 @@ static int inv_icm42600_accel_read_sensor(struct inv_icm42600_state *st,
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
/* enable accel sensor */
|
||||
conf.mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE;
|
||||
conf.mode = accel_st->power_mode;
|
||||
conf.filter = accel_st->filter;
|
||||
ret = inv_icm42600_set_accel_conf(st, &conf, NULL);
|
||||
if (ret)
|
||||
goto exit;
|
||||
@@ -277,6 +381,12 @@ static int inv_icm42600_accel_write_scale(struct iio_dev *indio_dev,
|
||||
|
||||
/* IIO format int + micro */
|
||||
static const int inv_icm42600_accel_odr[] = {
|
||||
/* 1.5625Hz */
|
||||
1, 562500,
|
||||
/* 3.125Hz */
|
||||
3, 125000,
|
||||
/* 6.25Hz */
|
||||
6, 250000,
|
||||
/* 12.5Hz */
|
||||
12, 500000,
|
||||
/* 25Hz */
|
||||
@@ -296,6 +406,9 @@ static const int inv_icm42600_accel_odr[] = {
|
||||
};
|
||||
|
||||
static const int inv_icm42600_accel_odr_conv[] = {
|
||||
INV_ICM42600_ODR_1_5625HZ_LP,
|
||||
INV_ICM42600_ODR_3_125HZ_LP,
|
||||
INV_ICM42600_ODR_6_25HZ_LP,
|
||||
INV_ICM42600_ODR_12_5HZ,
|
||||
INV_ICM42600_ODR_25HZ,
|
||||
INV_ICM42600_ODR_50HZ,
|
||||
@@ -581,7 +694,7 @@ static int inv_icm42600_accel_read_raw(struct iio_dev *indio_dev,
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = inv_icm42600_accel_read_sensor(st, chan, &data);
|
||||
ret = inv_icm42600_accel_read_sensor(indio_dev, chan, &data);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -754,6 +867,9 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
|
||||
accel_st->scales_len = ARRAY_SIZE(inv_icm42600_accel_scale);
|
||||
break;
|
||||
}
|
||||
/* low-power by default at init */
|
||||
accel_st->power_mode = INV_ICM42600_SENSOR_MODE_LOW_POWER;
|
||||
accel_st->filter = INV_ICM42600_FILTER_AVG_16X;
|
||||
|
||||
/*
|
||||
* clock period is 32kHz (31250ns)
|
||||
|
||||
@@ -34,12 +34,56 @@ static const struct regmap_range_cfg inv_icm42600_regmap_ranges[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_range inv_icm42600_regmap_volatile_yes_ranges[] = {
|
||||
/* Sensor data registers */
|
||||
regmap_reg_range(0x001D, 0x002A),
|
||||
/* INT status, FIFO, APEX data */
|
||||
regmap_reg_range(0x002D, 0x0038),
|
||||
/* Signal path reset */
|
||||
regmap_reg_range(0x004B, 0x004B),
|
||||
/* FIFO lost packets */
|
||||
regmap_reg_range(0x006C, 0x006D),
|
||||
/* Timestamp value */
|
||||
regmap_reg_range(0x1062, 0x1064),
|
||||
};
|
||||
|
||||
static const struct regmap_range inv_icm42600_regmap_volatile_no_ranges[] = {
|
||||
regmap_reg_range(0x0000, 0x001C),
|
||||
regmap_reg_range(0x006E, 0x1061),
|
||||
regmap_reg_range(0x1065, 0x4FFF),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table inv_icm42600_regmap_volatile_accesses[] = {
|
||||
{
|
||||
.yes_ranges = inv_icm42600_regmap_volatile_yes_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(inv_icm42600_regmap_volatile_yes_ranges),
|
||||
.no_ranges = inv_icm42600_regmap_volatile_no_ranges,
|
||||
.n_no_ranges = ARRAY_SIZE(inv_icm42600_regmap_volatile_no_ranges),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct regmap_range inv_icm42600_regmap_rd_noinc_no_ranges[] = {
|
||||
regmap_reg_range(0x0000, INV_ICM42600_REG_FIFO_DATA - 1),
|
||||
regmap_reg_range(INV_ICM42600_REG_FIFO_DATA + 1, 0x4FFF),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table inv_icm42600_regmap_rd_noinc_accesses[] = {
|
||||
{
|
||||
.no_ranges = inv_icm42600_regmap_rd_noinc_no_ranges,
|
||||
.n_no_ranges = ARRAY_SIZE(inv_icm42600_regmap_rd_noinc_no_ranges),
|
||||
},
|
||||
};
|
||||
|
||||
const struct regmap_config inv_icm42600_regmap_config = {
|
||||
.name = "inv_icm42600",
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x4FFF,
|
||||
.ranges = inv_icm42600_regmap_ranges,
|
||||
.num_ranges = ARRAY_SIZE(inv_icm42600_regmap_ranges),
|
||||
.volatile_table = inv_icm42600_regmap_volatile_accesses,
|
||||
.rd_noinc_table = inv_icm42600_regmap_rd_noinc_accesses,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(inv_icm42600_regmap_config, IIO_ICM42600);
|
||||
|
||||
@@ -248,6 +292,23 @@ int inv_icm42600_set_accel_conf(struct inv_icm42600_state *st,
|
||||
if (conf->filter < 0)
|
||||
conf->filter = oldconf->filter;
|
||||
|
||||
/* force power mode against ODR when sensor is on */
|
||||
switch (conf->mode) {
|
||||
case INV_ICM42600_SENSOR_MODE_LOW_POWER:
|
||||
case INV_ICM42600_SENSOR_MODE_LOW_NOISE:
|
||||
if (conf->odr <= INV_ICM42600_ODR_1KHZ_LN) {
|
||||
conf->mode = INV_ICM42600_SENSOR_MODE_LOW_NOISE;
|
||||
conf->filter = INV_ICM42600_FILTER_BW_ODR_DIV_2;
|
||||
} else if (conf->odr >= INV_ICM42600_ODR_6_25HZ_LP &&
|
||||
conf->odr <= INV_ICM42600_ODR_1_5625HZ_LP) {
|
||||
conf->mode = INV_ICM42600_SENSOR_MODE_LOW_POWER;
|
||||
conf->filter = INV_ICM42600_FILTER_AVG_16X;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* set ACCEL_CONFIG0 register (accel fullscale & odr) */
|
||||
if (conf->fs != oldconf->fs || conf->odr != oldconf->odr) {
|
||||
val = INV_ICM42600_ACCEL_CONFIG0_FS(conf->fs) |
|
||||
@@ -441,6 +502,16 @@ static int inv_icm42600_setup(struct inv_icm42600_state *st,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Use RC clock for accel low-power to fix glitches when switching
|
||||
* gyro on/off while accel low-power is on.
|
||||
*/
|
||||
ret = regmap_update_bits(st->map, INV_ICM42600_REG_INTF_CONFIG1,
|
||||
INV_ICM42600_INTF_CONFIG1_ACCEL_LP_CLK_RC,
|
||||
INV_ICM42600_INTF_CONFIG1_ACCEL_LP_CLK_RC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return inv_icm42600_set_conf(st, hw->conf);
|
||||
}
|
||||
|
||||
|
||||
@@ -1505,7 +1505,7 @@ static const struct acpi_device_id kmx61_acpi_match[] = {
|
||||
MODULE_DEVICE_TABLE(acpi, kmx61_acpi_match);
|
||||
|
||||
static const struct i2c_device_id kmx61_id[] = {
|
||||
{"kmx611021", 0},
|
||||
{ "kmx611021" },
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -365,8 +365,16 @@ static ssize_t iio_show_fixed_type(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
u8 type = this_attr->c->scan_type.endianness;
|
||||
const struct iio_scan_type *scan_type;
|
||||
u8 type;
|
||||
|
||||
scan_type = iio_get_current_scan_type(indio_dev, this_attr->c);
|
||||
if (IS_ERR(scan_type))
|
||||
return PTR_ERR(scan_type);
|
||||
|
||||
type = scan_type->endianness;
|
||||
|
||||
if (type == IIO_CPU) {
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
@@ -375,21 +383,21 @@ static ssize_t iio_show_fixed_type(struct device *dev,
|
||||
type = IIO_BE;
|
||||
#endif
|
||||
}
|
||||
if (this_attr->c->scan_type.repeat > 1)
|
||||
if (scan_type->repeat > 1)
|
||||
return sysfs_emit(buf, "%s:%c%d/%dX%d>>%u\n",
|
||||
iio_endian_prefix[type],
|
||||
this_attr->c->scan_type.sign,
|
||||
this_attr->c->scan_type.realbits,
|
||||
this_attr->c->scan_type.storagebits,
|
||||
this_attr->c->scan_type.repeat,
|
||||
this_attr->c->scan_type.shift);
|
||||
scan_type->sign,
|
||||
scan_type->realbits,
|
||||
scan_type->storagebits,
|
||||
scan_type->repeat,
|
||||
scan_type->shift);
|
||||
else
|
||||
return sysfs_emit(buf, "%s:%c%d/%d>>%u\n",
|
||||
iio_endian_prefix[type],
|
||||
this_attr->c->scan_type.sign,
|
||||
this_attr->c->scan_type.realbits,
|
||||
this_attr->c->scan_type.storagebits,
|
||||
this_attr->c->scan_type.shift);
|
||||
scan_type->sign,
|
||||
scan_type->realbits,
|
||||
scan_type->storagebits,
|
||||
scan_type->shift);
|
||||
}
|
||||
|
||||
static ssize_t iio_scan_el_show(struct device *dev,
|
||||
@@ -690,20 +698,27 @@ static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
|
||||
return sysfs_emit(buf, "%d\n", iio_buffer_is_active(buffer));
|
||||
}
|
||||
|
||||
static unsigned int iio_storage_bytes_for_si(struct iio_dev *indio_dev,
|
||||
unsigned int scan_index)
|
||||
static int iio_storage_bytes_for_si(struct iio_dev *indio_dev,
|
||||
unsigned int scan_index)
|
||||
{
|
||||
const struct iio_chan_spec *ch;
|
||||
const struct iio_scan_type *scan_type;
|
||||
unsigned int bytes;
|
||||
|
||||
ch = iio_find_channel_from_si(indio_dev, scan_index);
|
||||
bytes = ch->scan_type.storagebits / 8;
|
||||
if (ch->scan_type.repeat > 1)
|
||||
bytes *= ch->scan_type.repeat;
|
||||
scan_type = iio_get_current_scan_type(indio_dev, ch);
|
||||
if (IS_ERR(scan_type))
|
||||
return PTR_ERR(scan_type);
|
||||
|
||||
bytes = scan_type->storagebits / 8;
|
||||
|
||||
if (scan_type->repeat > 1)
|
||||
bytes *= scan_type->repeat;
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static unsigned int iio_storage_bytes_for_timestamp(struct iio_dev *indio_dev)
|
||||
static int iio_storage_bytes_for_timestamp(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
|
||||
|
||||
@@ -721,6 +736,9 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev,
|
||||
for_each_set_bit(i, mask,
|
||||
indio_dev->masklength) {
|
||||
length = iio_storage_bytes_for_si(indio_dev, i);
|
||||
if (length < 0)
|
||||
return length;
|
||||
|
||||
bytes = ALIGN(bytes, length);
|
||||
bytes += length;
|
||||
largest = max(largest, length);
|
||||
@@ -728,6 +746,9 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev,
|
||||
|
||||
if (timestamp) {
|
||||
length = iio_storage_bytes_for_timestamp(indio_dev);
|
||||
if (length < 0)
|
||||
return length;
|
||||
|
||||
bytes = ALIGN(bytes, length);
|
||||
bytes += length;
|
||||
largest = max(largest, length);
|
||||
@@ -1007,14 +1028,22 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev,
|
||||
indio_dev->masklength,
|
||||
in_ind + 1);
|
||||
while (in_ind != out_ind) {
|
||||
length = iio_storage_bytes_for_si(indio_dev, in_ind);
|
||||
ret = iio_storage_bytes_for_si(indio_dev, in_ind);
|
||||
if (ret < 0)
|
||||
goto error_clear_mux_table;
|
||||
|
||||
length = ret;
|
||||
/* Make sure we are aligned */
|
||||
in_loc = roundup(in_loc, length) + length;
|
||||
in_ind = find_next_bit(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength,
|
||||
in_ind + 1);
|
||||
}
|
||||
length = iio_storage_bytes_for_si(indio_dev, in_ind);
|
||||
ret = iio_storage_bytes_for_si(indio_dev, in_ind);
|
||||
if (ret < 0)
|
||||
goto error_clear_mux_table;
|
||||
|
||||
length = ret;
|
||||
out_loc = roundup(out_loc, length);
|
||||
in_loc = roundup(in_loc, length);
|
||||
ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length);
|
||||
@@ -1025,7 +1054,11 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev,
|
||||
}
|
||||
/* Relies on scan_timestamp being last */
|
||||
if (buffer->scan_timestamp) {
|
||||
length = iio_storage_bytes_for_timestamp(indio_dev);
|
||||
ret = iio_storage_bytes_for_timestamp(indio_dev);
|
||||
if (ret < 0)
|
||||
goto error_clear_mux_table;
|
||||
|
||||
length = ret;
|
||||
out_loc = roundup(out_loc, length);
|
||||
in_loc = roundup(in_loc, length);
|
||||
ret = iio_buffer_add_demux(buffer, &p, in_loc, out_loc, length);
|
||||
@@ -1592,6 +1625,22 @@ static long iio_device_buffer_ioctl(struct iio_dev *indio_dev, struct file *filp
|
||||
}
|
||||
}
|
||||
|
||||
static int iio_channel_validate_scan_type(struct device *dev, int ch,
|
||||
const struct iio_scan_type *scan_type)
|
||||
{
|
||||
/* Verify that sample bits fit into storage */
|
||||
if (scan_type->storagebits < scan_type->realbits + scan_type->shift) {
|
||||
dev_err(dev,
|
||||
"Channel %d storagebits (%d) < shifted realbits (%d + %d)\n",
|
||||
ch, scan_type->storagebits,
|
||||
scan_type->realbits,
|
||||
scan_type->shift);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
|
||||
struct iio_dev *indio_dev,
|
||||
int index)
|
||||
@@ -1616,20 +1665,38 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
|
||||
if (channels) {
|
||||
/* new magic */
|
||||
for (i = 0; i < indio_dev->num_channels; i++) {
|
||||
const struct iio_scan_type *scan_type;
|
||||
|
||||
if (channels[i].scan_index < 0)
|
||||
continue;
|
||||
|
||||
/* Verify that sample bits fit into storage */
|
||||
if (channels[i].scan_type.storagebits <
|
||||
channels[i].scan_type.realbits +
|
||||
channels[i].scan_type.shift) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"Channel %d storagebits (%d) < shifted realbits (%d + %d)\n",
|
||||
i, channels[i].scan_type.storagebits,
|
||||
channels[i].scan_type.realbits,
|
||||
channels[i].scan_type.shift);
|
||||
ret = -EINVAL;
|
||||
goto error_cleanup_dynamic;
|
||||
if (channels[i].has_ext_scan_type) {
|
||||
int j;
|
||||
|
||||
/*
|
||||
* get_current_scan_type is required when using
|
||||
* extended scan types.
|
||||
*/
|
||||
if (!indio_dev->info->get_current_scan_type) {
|
||||
ret = -EINVAL;
|
||||
goto error_cleanup_dynamic;
|
||||
}
|
||||
|
||||
for (j = 0; j < channels[i].num_ext_scan_type; j++) {
|
||||
scan_type = &channels[i].ext_scan_type[j];
|
||||
|
||||
ret = iio_channel_validate_scan_type(
|
||||
&indio_dev->dev, i, scan_type);
|
||||
if (ret)
|
||||
goto error_cleanup_dynamic;
|
||||
}
|
||||
} else {
|
||||
scan_type = &channels[i].scan_type;
|
||||
|
||||
ret = iio_channel_validate_scan_type(
|
||||
&indio_dev->dev, i, scan_type);
|
||||
if (ret)
|
||||
goto error_cleanup_dynamic;
|
||||
}
|
||||
|
||||
ret = iio_buffer_add_channel_sysfs(indio_dev, buffer,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user