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

Jonathan writes:

IIO: New device support, features, late breaking fixes and cleanup for 6.17

The normal mixed bag. A few more fixes than usual as I failed to send
them out earlier.

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

adi,ad4080
- New driver for this high speed ADC. Includes extensions to iio-backends
  necessary to support filter config, variable data lands and data
  alignment control.
adi,ad4170-4
- New driver for this 24-bit very feature rich ADC suited for weigh scale
  and thermocouple applications.
adi,ad7405
- New driver for this single channel isolated ADC with backend support
  (adi-axi-adc)
google,cros_ec_activity
- Add activity detection to the existing set of cros_ec drivers covering
  both human body and significant motion detection.
mediatek,mt6359
- Add support for MT6363 and MT6373 PMIC Auxiliary ADCs.
nicera,d3-323-aa
- New driver for this configurable Passive InfraRed sensor.

Device ID only
==============

mediatek,mt7981-auxadc
- Add ID to mt2701 driver as fully compatible with mt7986-auxadc.
rohm,bu79100g
- Add ID to ad7476 driver as fully compatible with TI ADS7866.

Features
========
Core
- New in_voltageY_convdelay to allow for devices to control timing
  offsets between sampling different channels.
adi,ad-sigma-delta-library
- Support SPI offload (later fix for missing Kconfig dependency)
adi,ad4851
- SPI 3-wire support.
adi,ad7606
- Power supply control.
- convdelay and calibbias support for calibration purposes.
- gain calibration support based on external filter resistance provided
  from device tree.
adi,ad7768-1
- Add output regulator for VCM output, typically used for preconditioning
  circuits.
- Add gpio controller for the 4 GPIOs.
- Multiple scan type support to enable 16-bit modes.
- Support synchronization over SPI.
- Filter type and oversampling ratio control.
- Low pass filter cut off read only attribute.
adi,adxl313
- FIFO support
- DC activity, inactivity detection with power-save on inactivity
- AC coupled activity detection
- Documentation for this complex driver.
- debugfs register access.
adi,adxl345
- Sampling frequency and sensor range controls.
bosch,bmi270
- Add step counter support.
invensense,icm42600
- Wake on motion support.

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

backend
- Drop unused parameter from iio_backend_ovesampling_ratio_set()
docs
- Fix ABI docs around I and Q modifiers.
treewide
- Switch remaining drives to use maple tree regcache.
- Drop use of DRIVER_NAME style definitions when only used in one
  place.
- Drop unused export.h includes.
- Use = { } in place of memset in various drivers.
- Constify various info structures and related.
- Switch some drivers from array of chip_info structures to individual
  named structures.
adi,ad-sigma_delta library
- Fix over allocation of scan buffer. (bits/bytes confusion)
- Sort includes and apply iwyu principles to ensure sensible set.
- Use u8 instead of uint8_t
- Replace hard coded type sizes with sizeof() and BITS_TO_BYTES() as
  appropriate.
- Factor out setting of read address to reduce duplication.
- Switch to buffer predisable so error handling on buffer enable
  functions correctly (balanced against postenable).
adi,ad4000
- Don't use sift_right() on an unsigned value.
adi,ad7173
- Add missing check on spi_setup() succeeding.
- Simplify clock enable disable code using devm_clk_get_enabled()
- Fix channel index for syscalib_mode
- Fix number of configuration slots for some devics.
- Fix the channel used for calibration.
- Fix setting ODR up in probe.
adi,ad7380
- Drop unused oversampling_ratio getter function call as value never
  used.
adi,ad7606
- Exit if invalid dt_schema encountered rather than carrying on with
  unknown config.
adi,ad7768-1
- Ensure SYNC_IN pulse is long enough.
- Switch sampling_frequency_available to read_avail() callback.
adi,ada4250
- Ensuring a dma-safe buffer for regmap_bulk_read()
- Use a local dev variable to simplify code
- Relax chip ID matching to allow for fallback dt compatibles.
- Make use of devm_regulator_get_enabled_read_voltage() to replace
  equivalent code.
- Shuffle elements around in struct to improve logical groupings and
  reduce holes.
- Use dev_err_probe()
adi,adxl313
- Use regcache to reduce traffic.
- Factor out enabling of measurement.
adi,adxl345
- Drop irq from struct as only used locally in code
- Simplify measure enable function using regmap_update_bits()
- Replace some magic numbers by units.h defines
- Simplify interrupt mapping code
- Simplify FIFO read out.
adi,axi-dac
- Factor out code to check for bus free to reduce duplication.
avago,apds9306
- Use a helper to get register address in both get and set functions.
bosch,bmi160+bmi270
- Ensure triggers suspended and resumed correctly.
bosch,bmo055
- Fix theoretical OOB acces to hw_xlate array.
freescale,vf610
- Drop -ENOMEM error message as plenty of existing prints if memory
  allocation fails.
- Use dev_err_probe() and devm_clk_geT_enabled() to simplify probe().
kionix,kx022a
- Apply include what you use principles to includes.
invensense,itg3200
- Add missing dt-binding for this gyroscope.
invensense,icm42600
- Switch from int64_t and similar to s64 and other kernel types.
- Simplify arrangement of DMA safe buffers and potentially reduce
  structure size a little.
invensense,mpu6050
- Reduce duplication in aux read/write code.
- Use sysfs_emit() to replace scnprintf()
murata,irsd200
- Drop duplicate printing of ret in dev_err_probe()
nxp,lpc3220-adc
- Add missing clocks property to dt-binding.
st,spear600
- Convert dt-binding that got left behind in staging to yaml in the main
  tree.
st,stm32-adc
- Use dev_fwnode() rather than directly accessing the of_node.
vti,sca3000
- Use direct returns instead of gotos where simple.

Various other minor typo and white space fixes.

* tag 'iio-for-6.17a' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (201 commits)
  iio: adc: ad_sigma_delta: Select IIO_BUFFER_DMAENGINE and SPI_OFFLOAD
  iio: adc: ad7173: fix setting ODR in probe
  iio: adc: ad7173: fix calibration channel
  iio: adc: ad7173: fix num_slots
  iio: adc: ad7173: fix channels index for syscalib_mode
  iio: adc: ad_sigma_delta: change to buffer predisable
  iio: ABI: fix correctness of I and Q modifiers
  iio: Add driver for Nicera D3-323-AA PIR sensor
  dt-bindings: iio: proximity: Add Nicera D3-323-AA PIR sensor
  dt-bindings: vendor-prefixes: Add Nicera
  iio: dac: vf610: Simplify with devm_clk_get_enabled()
  iio: adc: vf610: Simplify with dev_err_probe
  iio: adc: vf610: Drop -ENOMEM error message
  iio: imu: bno055: make bno055_sysfs_attr const
  iio: imu: bno055: fix OOB access of hw_xlate array
  dt-bindings: iio: adc: Add support for MT7981
  iio: accel: kionix-kx022a: Apply approximate iwyu principles to includes
  iio: adc: ad4170-4: Add support for weigh scale, thermocouple, and RTD sens
  iio: adc: ad4170-4: Add support for internal temperature sensor
  iio: adc: ad4170-4: Add GPIO controller support
  ...
This commit is contained in:
Greg Kroah-Hartman
2025-07-19 09:42:35 +02:00
180 changed files with 11059 additions and 1322 deletions

View File

@@ -48,10 +48,6 @@ What: /sys/.../iio:deviceX/scan_elements/in_timestamp_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY-voltageZ_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_en
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_en
What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_en
What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_en
What: /sys/.../iio:deviceX/scan_elements/in_incli_x_en
What: /sys/.../iio:deviceX/scan_elements/in_incli_y_en
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_en
@@ -73,10 +69,6 @@ What: /sys/.../iio:deviceX/scan_elements/in_incli_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_type
What: /sys/.../iio:deviceX/scan_elements/in_voltage_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_type
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_type
What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_type
What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_type
What: /sys/.../iio:deviceX/scan_elements/in_timestamp_type
What: /sys/.../iio:deviceX/scan_elements/in_pressureY_type
What: /sys/.../iio:deviceX/scan_elements/in_pressure_type
@@ -110,10 +102,6 @@ Description:
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_index
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_supply_index
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_i_index
What: /sys/.../iio:deviceX/scan_elements/in_voltageY_q_index
What: /sys/.../iio:deviceX/scan_elements/in_voltage_i_index
What: /sys/.../iio:deviceX/scan_elements/in_voltage_q_index
What: /sys/.../iio:deviceX/scan_elements/in_accel_x_index
What: /sys/.../iio:deviceX/scan_elements/in_accel_y_index
What: /sys/.../iio:deviceX/scan_elements/in_accel_z_index

View File

@@ -141,8 +141,6 @@ Description:
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_raw
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_raw
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_raw
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_raw
KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
Description:
@@ -417,18 +415,14 @@ What: /sys/bus/iio/devices/iio:deviceX/in_accel_offset
What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_offset
What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_offset
What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_offset
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage_q_offset
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage_i_offset
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_offset
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_offset
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_offset
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_offset
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_offset
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_offset
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_offset
What: /sys/bus/iio/devices/iio:deviceX/in_current_offset
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_i_offset
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_q_offset
What: /sys/bus/iio/devices/iio:deviceX/in_current_q_offset
What: /sys/bus/iio/devices/iio:deviceX/in_current_i_offset
What: /sys/bus/iio/devices/iio:deviceX/in_tempY_offset
What: /sys/bus/iio/devices/iio:deviceX/in_temp_offset
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_offset
@@ -456,21 +450,15 @@ Description:
to the _raw output.
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_scale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage-voltage_scale
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_scale
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_scale
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_scale
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_supply_scale
What: /sys/bus/iio/devices/iio:deviceX/in_current_scale
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_i_scale
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_q_scale
What: /sys/bus/iio/devices/iio:deviceX/in_current_i_scale
What: /sys/bus/iio/devices/iio:deviceX/in_current_q_scale
What: /sys/bus/iio/devices/iio:deviceX/in_accel_scale
What: /sys/bus/iio/devices/iio:deviceX/in_accel_peak_scale
@@ -559,6 +547,30 @@ Description:
- a small discrete set of values like "0 2 4 6 8"
- a range specified as "[min step max]"
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_convdelay
KernelVersion: 6.17
Contact: linux-iio@vger.kernel.org
Description:
Delay of start of conversion from common reference point shared
by all channels. Can be writable when used to compensate for
delay variation introduced by external filters feeding a
simultaneous sampling ADC.
E.g., for the ad7606 ADC series, this value is intended as a
configurable time delay in seconds, to correct delay introduced
by an optional external filtering circuit.
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_convdelay_available
KernelVersion: 6.16
Contact: linux-iio@vger.kernel.org
Description:
Available values of convdelay. Maybe expressed as:
- a range specified as "[min step max]"
If shared across all channels, <type>_convdelay_available
is used.
What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale
@@ -579,11 +591,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_i_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltage_q_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_i_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_q_calibscale
What: /sys/bus/iio/devices/iio:deviceX/in_voltageY_supply_calibscale
What: /sys/bus/iio/devices/iio:deviceX/out_currentY_calibscale
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_calibscale
@@ -805,7 +813,11 @@ Description:
all the other channels, since it involves changing the VCO
fundamental output frequency.
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_i_phase
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltageY_q_phase
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_phase
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_i_phase
What: /sys/bus/iio/devices/iio:deviceX/out_altvoltageY_q_phase
KernelVersion: 3.4.0
Contact: linux-iio@vger.kernel.org
Description:
@@ -1434,10 +1446,6 @@ What: /sys/.../iio:deviceX/bufferY/in_timestamp_en
What: /sys/.../iio:deviceX/bufferY/in_voltageY_supply_en
What: /sys/.../iio:deviceX/bufferY/in_voltageY_en
What: /sys/.../iio:deviceX/bufferY/in_voltageY-voltageZ_en
What: /sys/.../iio:deviceX/bufferY/in_voltageY_i_en
What: /sys/.../iio:deviceX/bufferY/in_voltageY_q_en
What: /sys/.../iio:deviceX/bufferY/in_voltage_i_en
What: /sys/.../iio:deviceX/bufferY/in_voltage_q_en
What: /sys/.../iio:deviceX/bufferY/in_incli_x_en
What: /sys/.../iio:deviceX/bufferY/in_incli_y_en
What: /sys/.../iio:deviceX/bufferY/in_pressureY_en
@@ -1458,10 +1466,6 @@ What: /sys/.../iio:deviceX/bufferY/in_incli_type
What: /sys/.../iio:deviceX/bufferY/in_voltageY_type
What: /sys/.../iio:deviceX/bufferY/in_voltage_type
What: /sys/.../iio:deviceX/bufferY/in_voltageY_supply_type
What: /sys/.../iio:deviceX/bufferY/in_voltageY_i_type
What: /sys/.../iio:deviceX/bufferY/in_voltageY_q_type
What: /sys/.../iio:deviceX/bufferY/in_voltage_i_type
What: /sys/.../iio:deviceX/bufferY/in_voltage_q_type
What: /sys/.../iio:deviceX/bufferY/in_timestamp_type
What: /sys/.../iio:deviceX/bufferY/in_pressureY_type
What: /sys/.../iio:deviceX/bufferY/in_pressure_type
@@ -1499,10 +1503,6 @@ Description:
What: /sys/.../iio:deviceX/bufferY/in_voltageY_index
What: /sys/.../iio:deviceX/bufferY/in_voltageY_supply_index
What: /sys/.../iio:deviceX/bufferY/in_voltageY_i_index
What: /sys/.../iio:deviceX/bufferY/in_voltageY_q_index
What: /sys/.../iio:deviceX/bufferY/in_voltage_i_index
What: /sys/.../iio:deviceX/bufferY/in_voltage_q_index
What: /sys/.../iio:deviceX/bufferY/in_accel_x_index
What: /sys/.../iio:deviceX/bufferY/in_accel_y_index
What: /sys/.../iio:deviceX/bufferY/in_accel_z_index
@@ -1692,8 +1692,6 @@ Description:
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_raw
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_supply_raw
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_i_raw
What: /sys/bus/iio/devices/iio:deviceX/in_currentY_q_raw
KernelVersion: 3.17
Contact: linux-iio@vger.kernel.org
Description:
@@ -2278,6 +2276,9 @@ Description:
Reading returns a list with the possible filter modes. Options
for the attribute:
* "none" - Filter is disabled/bypassed.
* "sinc1" - The digital sinc1 filter. Fast 1st
conversion time. Poor noise performance.
* "sinc3" - The digital sinc3 filter. Moderate 1st
conversion time. Good noise performance.
* "sinc4" - Sinc 4. Excellent noise performance. Long
@@ -2293,6 +2294,8 @@ Description:
* "sinc3+pf2" - Sinc3 + device specific Post Filter 2.
* "sinc3+pf3" - Sinc3 + device specific Post Filter 3.
* "sinc3+pf4" - Sinc3 + device specific Post Filter 4.
* "sinc5+pf1" - Sinc5 + device specific Post Filter 1.
* "sinc5+avg" - Sinc5 + averaging by 4.
* "wideband" - filter with wideband low ripple passband
and sharp transition band.

View File

@@ -1,10 +1,10 @@
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0-1_i_calibphase
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0-altvoltage1_i_calibphase
KernelVersion:
Contact: linux-iio@vger.kernel.org
Description:
Read/write unscaled value for the Local Oscillatior path quadrature I phase shift.
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0-1_q_calibphase
What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0-altvoltage1_q_calibphase
KernelVersion:
Contact: linux-iio@vger.kernel.org
Description:

View File

@@ -0,0 +1,96 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright 2025 Analog Devices Inc.
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/adi,ad4080.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD4080 20-Bit, 40 MSPS, Differential SAR ADC
maintainers:
- Antoniu Miclaus <antoniu.miclaus@analog.com>
description: |
The AD4080 is a high speed, low noise, low distortion, 20-bit, Easy Drive,
successive approximation register (SAR) analog-to-digital converter (ADC).
Maintaining high performance (signal-to-noise and distortion (SINAD) ratio
> 90 dBFS) at signal frequencies in excess of 1 MHz enables the AD4080 to
service a wide variety of precision, wide bandwidth data acquisition
applications.
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4080.pdf
$ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
enum:
- adi,ad4080
reg:
maxItems: 1
spi-max-frequency:
description: Configuration of the SPI bus.
maximum: 50000000
clocks:
maxItems: 1
clock-names:
items:
- const: cnv
vdd33-supply: true
vdd11-supply: true
vddldo-supply: true
iovdd-supply: true
vrefin-supply: true
io-backends:
maxItems: 1
adi,lvds-cnv-enable:
description: Enable the LVDS signal type on the CNV pin. Default is CMOS.
type: boolean
adi,num-lanes:
description:
Number of lanes on which the data is sent on the output (DA, DB pins).
$ref: /schemas/types.yaml#/definitions/uint32
enum: [1, 2]
default: 1
required:
- compatible
- reg
- clocks
- clock-names
- vdd33-supply
- vrefin-supply
additionalProperties: false
examples:
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "adi,ad4080";
reg = <0>;
spi-max-frequency = <10000000>;
vdd33-supply = <&vdd33>;
vddldo-supply = <&vddldo>;
vrefin-supply = <&vrefin>;
clocks = <&cnv>;
clock-names = "cnv";
io-backends = <&iio_backend>;
};
};
...

View File

@@ -0,0 +1,554 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/adi,ad4170-4.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD4170-4 and similar Analog to Digital Converters
maintainers:
- Marcelo Schmitt <marcelo.schmitt@analog.com>
description: |
Analog Devices AD4170-4 series of Sigma-delta Analog to Digital Converters.
Specifications can be found at:
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4170-4.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4190-4.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4195-4.pdf
$ref: /schemas/spi/spi-peripheral-props.yaml#
$defs:
reference-buffer:
description: |
Enable precharge buffer, full buffer, or skip reference buffering of
the positive/negative voltage reference. Because the output impedance
of the source driving the voltage reference inputs may be dynamic,
resistive/capacitive combinations of those inputs can cause DC gain
errors if the reference inputs go unbuffered into the ADC. Enable
reference buffering if the provided reference source has dynamic high
impedance output. Note the absolute voltage allowed on REFINn+ and REFINn-
inputs is from AVSS - 50 mV to AVDD + 50 mV when the reference buffers are
disabled but narrows to AVSS to AVDD when reference buffering is enabled
or in precharge mode.
$ref: /schemas/types.yaml#/definitions/string
enum: [ precharge, full, disabled ]
default: full
properties:
compatible:
enum:
- adi,ad4170-4
- adi,ad4190-4
- adi,ad4195-4
avss-supply:
description:
Reference voltage supply for AVSS. A 2.625V minimum and 0V maximum supply
that powers the chip. If not provided, AVSS is assumed to be at system
ground (0V).
avdd-supply:
description:
A supply of 4.75V to 5.25V relative to AVSS that powers the chip (AVDD).
iovdd-supply:
description: 1.7V to 5.25V reference supply to the serial interface (IOVDD).
refin1p-supply:
description: REFIN+ supply that can be used as reference for conversion.
refin1n-supply:
description: REFIN- supply that can be used as reference for conversion.
refin2p-supply:
description: REFIN2+ supply that can be used as reference for conversion.
refin2n-supply:
description: REFIN2- supply that can be used as reference for conversion.
spi-cpol: true
spi-cpha: true
interrupts:
description:
Interrupt for signaling the completion of conversion results. The data
ready signal (RDY) used as interrupt is by default provided on the SDO
pin. Alternatively, it can be provided on the DIG_AUX1 pin in which case
the chip disables the RDY function on SDO. Thus, there can be only one
data ready interrupt enabled at a time.
interrupt-names:
description:
Specify which pin should be configured as Data Ready interrupt.
enum:
- sdo
- dig_aux1
clocks:
maxItems: 1
description:
Optional external clock source. Can specify either an external clock or
external crystal.
clock-names:
enum:
- ext-clk
- xtal
default: ext-clk
'#clock-cells':
const: 0
clock-output-names:
maxItems: 1
gpio-controller: true
"#gpio-cells":
const: 2
description: |
The first cell is for the GPIO number: 0 to 3.
The second cell takes standard GPIO flags.
ldac-gpios:
description:
GPIO connected to DIG_AUX2 pin to be used as LDAC toggle to control the
transfer of data from the DAC_INPUT_A register to the DAC.
maxItems: 1
'#address-cells':
const: 1
'#size-cells':
const: 0
adi,vbias-pins:
description: Analog inputs to apply a voltage bias of (AVDD AVSS) / 2 to.
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1
maxItems: 9
items:
minimum: 0
maximum: 8
allOf:
# Some devices don't have integrated DAC
- if:
properties:
compatible:
contains:
enum:
- adi,ad4190-4
- adi,ad4195-4
then:
properties:
ldac-gpios: false
# Require to specify the interrupt pin when using interrupts
- if:
required:
- interrupts
then:
required:
- interrupt-names
# If an external clock is set, the internal clock cannot go out and vice versa
- oneOf:
- required: [clocks]
properties:
'#clock-cells': false
- required: ['#clock-cells']
properties:
clocks: false
required:
- compatible
- reg
- avdd-supply
- iovdd-supply
- spi-cpol
- spi-cpha
unevaluatedProperties: false
patternProperties:
"^channel@[0-9a-f]$":
$ref: /schemas/iio/adc/adc.yaml#
unevaluatedProperties: false
description:
Represents the external channels which are connected to the ADC.
properties:
reg:
description:
The channel number.
minimum: 0
maximum: 15
diff-channels:
description: |
This property is used for defining the inputs of a differential
voltage channel. The first value is the positive input and the second
value is the negative input of the channel.
Besides the analog input pins AIN0 to AIN8, there are special inputs
that can be selected with the following values:
17: Internal temperature sensor
18: (AVDD-AVSS)/5
19: (IOVDD-DGND)/5
20: DAC output
21: ALDO
22: DLDO
23: AVSS
24: DGND
25: REFIN+
26: REFIN-
27: REFIN2+
28: REFIN2-
29: REFOUT
For the internal temperature sensor, use the input number for both
inputs (i.e. diff-channels = <17 17>).
items:
enum: [0, 1, 2, 3, 4, 5, 6, 7, 8, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29]
adi,reference-select:
description: |
Select the reference source to use when converting on the
specific channel. Valid values are:
0: REFIN+/REFIN-
1: REFIN2+/REFIN2
2: REFOUT/AVSS (internal reference)
3: AVDD/AVSS
If not specified, REFOUT/AVSS is used.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
default: 1
adi,positive-reference-buffer:
$ref: '#/$defs/reference-buffer'
adi,negative-reference-buffer:
$ref: '#/$defs/reference-buffer'
adi,sensor-type:
description:
The AD4170-4 and similar designs have features to aid interfacing with
load cell weigh scale, RTD, and thermocouple sensors. Each of those
sensor types requires either distinct wiring configuration or
external circuitry for proper sensor operation and can use different
ADC chip functionality on their setups. A key characteristic of those
external sensors is that they must be excited either by voltage supply
or by ADC chip excitation signals. The sensor can then be read through
a pair of analog inputs. This property specifies which particular
sensor type is connected to the ADC so it can be properly setup and
handled. Omit this property for conventional (not weigh scale, RTD, or
thermocouple) ADC channel setups.
$ref: /schemas/types.yaml#/definitions/string
enum: [ weighscale, rtd, thermocouple ]
adi,excitation-pin-0:
description:
Analog input to apply excitation current to while the channel
is active.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 20
default: 0
adi,excitation-pin-1:
description:
Analog input to apply excitation current to while the channel
is active.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 20
default: 0
adi,excitation-pin-2:
description:
Analog input to apply excitation current to while the channel
is active.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 20
default: 0
adi,excitation-pin-3:
description:
Analog input to apply excitation current to while the channel
is active.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 20
default: 0
adi,excitation-current-0-microamp:
description:
Excitation current in microamperes to be applied to pin specified in
adi,excitation-pin-0 while this channel is active.
enum: [0, 10, 50, 100, 250, 500, 1000, 1500]
default: 0
adi,excitation-current-1-microamp:
description:
Excitation current in microamperes to be applied to pin specified in
adi,excitation-pin-1 while this channel is active.
enum: [0, 10, 50, 100, 250, 500, 1000, 1500]
default: 0
adi,excitation-current-2-microamp:
description:
Excitation current in microamperes to be applied to pin specified in
adi,excitation-pin-2 while this channel is active.
enum: [0, 10, 50, 100, 250, 500, 1000, 1500]
default: 0
adi,excitation-current-3-microamp:
description:
Excitation current in microamperes to be applied to pin specified in
adi,excitation-pin-3 while this channel is active.
enum: [0, 10, 50, 100, 250, 500, 1000, 1500]
default: 0
adi,excitation-ac:
type: boolean
description:
Whether the external sensor has to be AC or DC excited. When omitted,
it is DC excited.
allOf:
- oneOf:
- required: [single-channel, common-mode-channel]
properties:
diff-channels: false
- required: [diff-channels]
properties:
single-channel: false
common-mode-channel: false
# Usual ADC channels don't need external circuitry excitation.
- if:
not:
required:
- adi,sensor-type
then:
properties:
adi,excitation-pin-0: false
adi,excitation-pin-1: false
adi,excitation-pin-2: false
adi,excitation-pin-3: false
adi,excitation-current-0-microamp: false
adi,excitation-current-1-microamp: false
adi,excitation-current-2-microamp: false
adi,excitation-current-3-microamp: false
adi,excitation-ac: false
# Weigh scale bridge AC excited with one pair of predefined signals.
- if:
allOf:
- properties:
adi,sensor-type:
contains:
const: weighscale
- required:
- adi,excitation-ac
- adi,excitation-pin-2
- adi,excitation-pin-3
- not:
required:
- adi,excitation-current-2-microamp
- adi,excitation-current-3-microamp
then:
properties:
adi,excitation-pin-2:
const: 19
adi,excitation-pin-3:
const: 20
# Weigh scale bridge AC excited with two pairs of predefined signals.
- if:
allOf:
- properties:
adi,sensor-type:
contains:
const: weighscale
- required:
- adi,excitation-ac
- adi,excitation-pin-0
- adi,excitation-pin-1
- adi,excitation-pin-2
- adi,excitation-pin-3
- not:
required:
- adi,excitation-current-0-microamp
- adi,excitation-current-1-microamp
- adi,excitation-current-2-microamp
- adi,excitation-current-3-microamp
then:
properties:
adi,excitation-pin-0:
const: 17
adi,excitation-pin-1:
const: 18
adi,excitation-pin-2:
const: 19
adi,excitation-pin-3:
const: 20
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "adi,ad4170-4";
reg = <0>;
spi-max-frequency = <20000000>;
spi-cpol;
spi-cpha;
avdd-supply = <&avdd>;
iovdd-supply = <&iovdd>;
clocks = <&clk>;
clock-names = "xtal";
interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
interrupt-names = "dig_aux1";
adi,vbias-pins = <8>;
#address-cells = <1>;
#size-cells = <0>;
// Sample AIN0 with respect to DGND throughout AVDD/DGND input range
// Pseudo-differential unipolar
channel@0 {
reg = <0>;
single-channel = <0>;
common-mode-channel = <24>;
adi,reference-select = <3>;
};
// Weigh scale sensor
channel@1 {
reg = <1>;
bipolar;
diff-channels = <1 2>;
adi,reference-select = <0>;
adi,positive-reference-buffer = "precharge";
adi,negative-reference-buffer = "precharge";
adi,sensor-type = "weighscale";
adi,excitation-pin-2 = <19>;
adi,excitation-pin-3 = <20>;
adi,excitation-ac;
};
// RTD sensor
channel@2 {
reg = <2>;
bipolar;
diff-channels = <3 4>;
adi,reference-select = <0>;
adi,sensor-type = "rtd";
adi,excitation-pin-0 = <5>;
adi,excitation-pin-1 = <6>;
adi,excitation-current-0-microamp = <500>;
adi,excitation-current-1-microamp = <500>;
adi,excitation-ac;
};
// Thermocouple sensor
channel@3 {
reg = <3>;
bipolar;
diff-channels = <7 8>;
adi,reference-select = <0>;
adi,sensor-type = "thermocouple";
adi,excitation-pin-0 = <18>;
adi,excitation-current-0-microamp = <500>;
};
};
};
- |
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "adi,ad4170-4";
reg = <0>;
spi-max-frequency = <20000000>;
spi-cpol;
spi-cpha;
avdd-supply = <&avdd>;
iovdd-supply = <&iovdd>;
#clock-cells = <0>;
clock-output-names = "ad4170-clk16mhz";
interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
interrupt-names = "dig_aux1";
#address-cells = <1>;
#size-cells = <0>;
// Sample AIN0 with respect to AIN1 throughout AVDD/AVSS input range
// Differential bipolar. If AVSS < 0V, differential true bipolar
channel@0 {
reg = <0>;
bipolar;
diff-channels = <0 1>;
adi,reference-select = <3>;
};
// Sample AIN2 with respect to DGND throughout AVDD/DGND input range
// Pseudo-differential unipolar
channel@1 {
reg = <1>;
single-channel = <2>;
common-mode-channel = <24>;
adi,reference-select = <3>;
};
// Sample AIN3 with respect to 2.5V throughout AVDD/AVSS input range
// Pseudo-differential bipolar
channel@2 {
reg = <2>;
bipolar;
single-channel = <3>;
common-mode-channel = <29>;
adi,reference-select = <3>;
};
// Sample AIN4 with respect to DGND throughout AVDD/AVSS input range
// Pseudo-differential bipolar
channel@3 {
reg = <3>;
bipolar;
single-channel = <4>;
common-mode-channel = <24>;
adi,reference-select = <3>;
};
// Sample AIN5 with respect to 2.5V throughout AVDD/AVSS input range
// Pseudo-differential unipolar (AD4170-4 datasheet page 46 example)
channel@4 {
reg = <4>;
single-channel = <5>;
common-mode-channel = <29>;
adi,reference-select = <3>;
};
// Sample AIN6 with respect to 2.5V throughout REFIN+/REFIN- input range
// Pseudo-differential bipolar
channel@5 {
reg = <5>;
bipolar;
single-channel = <6>;
common-mode-channel = <29>;
adi,reference-select = <0>;
};
// Weigh scale sensor
channel@6 {
reg = <6>;
bipolar;
diff-channels = <7 8>;
adi,reference-select = <0>;
adi,sensor-type = "weighscale";
adi,excitation-pin-0 = <17>;
adi,excitation-pin-1 = <18>;
adi,excitation-pin-2 = <19>;
adi,excitation-pin-3 = <20>;
adi,excitation-ac;
};
};
};
...

View File

@@ -69,6 +69,8 @@ properties:
spi-max-frequency:
maximum: 25000000
spi-3wire: true
'#address-cells':
const: 1

View File

@@ -0,0 +1,60 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright 2025 Analog Devices Inc.
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/adi,ad7405.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD7405 family
maintainers:
- Dragos Bogdan <dragos.bogdan@analog.com>
- Pop Ioan Daniel <pop.ioan-daniel@analog.com>
description: |
Analog Devices AD7405 is a high performance isolated ADC, 1-channel,
16-bit with a second-order Σ-Δ modulator that converts an analog input signal
into a high speed, single-bit data stream.
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7405.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/adum7701.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/adum7702.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ADuM7703.pdf
properties:
compatible:
enum:
- adi,ad7405
- adi,adum7701
- adi,adum7702
- adi,adum7703
clocks:
maxItems: 1
vdd1-supply: true
vdd2-supply: true
io-backends:
maxItems: 1
required:
- compatible
- clocks
- vdd1-supply
- vdd2-supply
- io-backends
additionalProperties: false
examples:
- |
adc {
compatible = "adi,ad7405";
clocks = <&axi_clk_gen 0>;
vdd1-supply = <&vdd1>;
vdd2-supply = <&vdd2>;
io-backends = <&axi_adc>;
};
...

View File

@@ -204,6 +204,15 @@ patternProperties:
considered a bipolar differential channel. Otherwise it is bipolar
single-ended.
adi,rfilter-ohms:
description:
For ADCs that supports gain calibration, this property must be set to
the value of the external RFilter resistor. Proper gain error
correction is applied based on this value.
default: 0
minimum: 0
maximum: 64512
required:
- reg
- bipolar
@@ -256,6 +265,25 @@ allOf:
properties:
adi,oversampling-ratio-gpios: false
- if:
properties:
compatible:
contains:
enum:
- adi,ad7605-4
- adi,ad7606-4
- adi,ad7606-6
- adi,ad7606-8
- adi,ad7607
- adi,ad7608
- adi,ad7609
- adi,ad7616
then:
patternProperties:
"^channel@[0-9a-f]+$":
properties:
adi,rfilter-ohms: false
- if:
properties:
compatible:
@@ -398,6 +426,7 @@ examples:
reg = <8>;
diff-channels = <8 8>;
bipolar;
adi,rfilter-ohms = <2048>;
};
};

View File

@@ -26,7 +26,26 @@ properties:
clock-names:
const: mclk
trigger-sources:
$ref: /schemas/types.yaml#/definitions/phandle-array
minItems: 1
maxItems: 2
description: |
A list of phandles referencing trigger source providers. Each entry
represents a trigger source for the ADC:
- First entry specifies the device responsible for driving the
synchronization (SYNC_IN) pin, as an alternative to adi,sync-in-gpios.
This can be a `gpio-trigger` or another `ad7768-1` device. If the
device's own SYNC_OUT pin is internally connected to its SYNC_IN pin,
reference the device itself or omit this property.
- Second entry optionally defines a GPIO3 pin used as a START signal trigger.
Use the accompanying trigger source cell to identify the type of each entry.
interrupts:
description:
DRDY (Data Ready) pin, which signals conversion results are available.
maxItems: 1
'#address-cells':
@@ -47,6 +66,19 @@ properties:
in any way, for example if the filter decimation rate changes.
As the line is active low, it should be marked GPIO_ACTIVE_LOW.
regulators:
type: object
description:
list of regulators provided by this controller.
properties:
vcm-output:
$ref: /schemas/regulator/regulator.yaml#
type: object
unevaluatedProperties: false
additionalProperties: false
reset-gpios:
maxItems: 1
@@ -57,6 +89,23 @@ properties:
"#io-channel-cells":
const: 1
"#trigger-source-cells":
description: |
Cell indicates the trigger output signal: 0 = SYNC_OUT, 1 = GPIO3,
2 = DRDY.
For better readability, macros for these values are available in
dt-bindings/iio/adc/adi,ad7768-1.h.
const: 1
gpio-controller: true
"#gpio-cells":
const: 2
description: |
The first cell is for the GPIO number: 0 to 3.
The second cell takes standard GPIO flags.
required:
- compatible
- reg
@@ -65,7 +114,16 @@ required:
- vref-supply
- spi-cpol
- spi-cpha
- adi,sync-in-gpios
dependencies:
adi,sync-in-gpios:
not:
required:
- trigger-sources
trigger-sources:
not:
required:
- adi,sync-in-gpios
patternProperties:
"^channel@([0-9]|1[0-5])$":
@@ -105,6 +163,8 @@ examples:
spi-max-frequency = <2000000>;
spi-cpol;
spi-cpha;
gpio-controller;
#gpio-cells = <2>;
vref-supply = <&adc_vref>;
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
interrupt-parent = <&gpio>;
@@ -120,6 +180,12 @@ examples:
reg = <0>;
label = "channel_0";
};
regulators {
vcm_reg: vcm-output {
regulator-name = "ad7768-1-vcm";
};
};
};
};
...

View File

@@ -27,6 +27,7 @@ description: |
the ad7606 family.
https://wiki.analog.com/resources/fpga/docs/axi_adc_ip
https://analogdevicesinc.github.io/hdl/library/axi_ad408x/index.html
https://analogdevicesinc.github.io/hdl/library/axi_ad485x/index.html
http://analogdevicesinc.github.io/hdl/library/axi_ad7606x/index.html
@@ -34,6 +35,7 @@ properties:
compatible:
enum:
- adi,axi-adc-10.0.a
- adi,axi-ad408x
- adi,axi-ad7606x
- adi,axi-ad485x

View File

@@ -32,6 +32,10 @@ properties:
- enum:
- mediatek,mt7623-auxadc
- const: mediatek,mt2701-auxadc
- items:
- enum:
- mediatek,mt7981-auxadc
- const: mediatek,mt7986-auxadc
- items:
- enum:
- mediatek,mt6893-auxadc

View File

@@ -22,6 +22,8 @@ properties:
- mediatek,mt6357-auxadc
- mediatek,mt6358-auxadc
- mediatek,mt6359-auxadc
- mediatek,mt6363-auxadc
- mediatek,mt6373-auxadc
"#io-channel-cells":
const: 1

View File

@@ -22,6 +22,9 @@ properties:
interrupts:
maxItems: 1
clocks:
maxItems: 1
vref-supply: true
"#io-channel-cells":

View File

@@ -0,0 +1,69 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/st,spear600-adc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ST SPEAr ADC device driver
maintainers:
- Jonathan Cameron <jic23@kernel.org>
description: |
Integrated ADC inside the ST SPEAr SoC, SPEAr600, supporting
10-bit resolution. Datasheet can be found here:
https://www.st.com/resource/en/datasheet/spear600.pdf
properties:
compatible:
enum:
- st,spear600-adc
reg:
maxItems: 1
interrupts:
maxItems: 1
sampling-frequency:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 2500000
maximum: 20000000
description:
Default sampling frequency of the ADC in Hz.
vref-external:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1000
maximum: 2800
description:
External voltage reference in milli-volts. If omitted the internal voltage
reference will be used.
average-samples:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 15
default: 0
description:
Number of samples to generate an average value. If omitted, single data
conversion will be used.
required:
- compatible
- reg
- interrupts
- sampling-frequency
additionalProperties: false
examples:
- |
adc@d8200000 {
compatible = "st,spear600-adc";
reg = <0xd8200000 0x1000>;
interrupt-parent = <&vic1>;
interrupts = <6>;
sampling-frequency = <5000000>;
vref-external = <2500>; /* 2.5V VRef */
};

View File

@@ -0,0 +1,59 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/gyroscope/invensense,itg3200.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Invensense ITG-3200 Gyroscope
maintainers:
- Jonathan Cameron <jic23@kernel.org>
description: |
Triple-axis, digital output gyroscope with a three 16-bit analog-to-digital
converters (ADCs) for digitizing the gyro outputs, a user-selectable internal
low-pass filter bandwidth, and a Fast-Mode I2C.
properties:
compatible:
const: invensense,itg3200
reg:
maxItems: 1
vdd-supply: true
vlogic-supply: true
interrupts:
maxItems: 1
mount-matrix:
description: an optional 3x3 mounting rotation matrix.
clocks:
maxItems: 1
clock-names:
items:
- const: ext_clock
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
gyroscope@68 {
compatible = "invensense,itg3200";
reg = <0x68>;
interrupt-parent = <&gpio2>;
interrupts = <24 IRQ_TYPE_EDGE_FALLING>;
};
};

View File

@@ -0,0 +1,62 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/proximity/nicera,d3323aa.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Nicera D3-323-AA PIR sensor
maintainers:
- Waqar Hameed <waqar.hameed@axis.com>
description: |
PIR sensor for human detection.
Datasheet: https://www.endrich.com/Datenbl%C3%A4tter/Sensoren/D3-323-AA_e.pdf
properties:
compatible:
const: nicera,d3323aa
vdd-supply:
description:
Supply voltage (1.8 to 5.5 V).
vout-clk-gpios:
maxItems: 1
description:
GPIO for clock and detection.
After reset, the device signals with two falling edges on this pin that it
is ready for configuration (within 1.2 s).
During configuration, it is used as clock for data reading and writing (on
data-gpios).
After all this, when device is in operational mode, it signals on this pin
for any detections.
data-gpios:
maxItems: 1
description:
GPIO for data reading and writing. This is denoted "DO (SI)" in datasheet.
During configuration, this pin is used for writing and reading
configuration data (together with vout-clk-gpios as clock).
After this, during operational mode, the device will output serial data on
this GPIO.
required:
- compatible
- vdd-supply
- vout-clk-gpios
- data-gpios
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
proximity {
compatible = "nicera,d3323aa";
vdd-supply = <&regulator_3v3>;
vout-clk-gpios = <&gpio 78 GPIO_ACTIVE_HIGH>;
data-gpios = <&gpio 76 GPIO_ACTIVE_HIGH>;
};
...

View File

@@ -1,24 +0,0 @@
* ST SPEAr ADC device driver
Required properties:
- compatible: Should be "st,spear600-adc"
- reg: Address and length of the register set for the device
- interrupts: Should contain the ADC interrupt
- sampling-frequency: Default sampling frequency
Optional properties:
- vref-external: External voltage reference in milli-volts. If omitted
the internal voltage reference will be used.
- average-samples: Number of samples to generate an average value. If
omitted, single data conversion will be used.
Examples:
adc: adc@d8200000 {
compatible = "st,spear600-adc";
reg = <0xd8200000 0x1000>;
interrupt-parent = <&vic1>;
interrupts = <6>;
sampling-frequency = <5000000>;
vref-external = <2500>; /* 2.5V VRef */
};

View File

@@ -0,0 +1,40 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/trigger-source/gpio-trigger.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Generic trigger source using GPIO
description: A GPIO used as a trigger source.
maintainers:
- Jonathan Santos <Jonathan.Santos@analog.com>
properties:
compatible:
const: gpio-trigger
'#trigger-source-cells':
const: 0
gpios:
maxItems: 1
description: GPIO to be used as a trigger source.
required:
- compatible
- '#trigger-source-cells'
- gpios
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
trigger {
compatible = "gpio-trigger";
#trigger-source-cells = <0>;
gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
};

View File

@@ -1067,6 +1067,8 @@ patternProperties:
description: Next Thing Co.
"^ni,.*":
description: National Instruments
"^nicera,.*":
description: Nippon Ceramic Co., Ltd.
"^nintendo,.*":
description: Nintendo
"^nlt,.*":

View File

@@ -0,0 +1,293 @@
.. SPDX-License-Identifier: GPL-2.0
===============
ADXL313 driver
===============
This driver supports Analog Device's ADXL313 on SPI/I2C bus.
1. Supported devices
====================
* `ADXL313 <https://www.analog.com/ADXL313>`_
The ADXL313is a low noise density, low power, 3-axis accelerometer with
selectable measurement ranges. The ADXL313 supports the ±0.5 g, ±1 g, ±2 g and
±4 g ranges.
2. Device attributes
====================
Accelerometer measurements are always provided.
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 adxl313 related device files, found in the
specific device folder path ``/sys/bus/iio/devices/iio:deviceX``.
+---------------------------------------------------+----------------------------------------------------------+
| 3-Axis Accelerometer related device files | Description |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_scale | Scale for the accelerometer channels. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_x_calibbias | Calibration offset for the X-axis accelerometer channel. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_x_raw | Raw X-axis accelerometer channel value. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_y_calibbias | y-axis acceleration offset correction |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_y_raw | Raw Y-axis accelerometer channel value. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_z_calibbias | Calibration offset for the Z-axis accelerometer channel. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_z_raw | Raw Z-axis accelerometer channel value. |
+---------------------------------------------------+----------------------------------------------------------+
+---------------------------------------+----------------------------------------------+
| Miscellaneous device files | Description |
+---------------------------------------+----------------------------------------------+
| name | Name of the IIO device. |
+---------------------------------------+----------------------------------------------+
| in_accel_sampling_frequency | Currently selected sample rate. |
+---------------------------------------+----------------------------------------------+
| in_accel_sampling_frequency_available | Available sampling frequency configurations. |
+---------------------------------------+----------------------------------------------+
The iio event related settings, found in ``/sys/bus/iio/devices/iio:deviceX/events``.
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_mag_adaptive_falling_period | AC coupled inactivity time. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_mag_adaptive_falling_value | AC coupled inactivity threshold. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_mag_adaptive_rising_value | AC coupled activity threshold. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_mag_falling_period | Inactivity time. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_mag_falling_value | Inactivity threshold. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_mag_rising_value | Activity threshold. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_x\&y\&z_mag_adaptive_falling_en | Enable or disable AC coupled inactivity events. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_x\|y\|z_mag_adaptive_rising_en | Enable or disable AC coupled activity events. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_x\&y\&z_mag_falling_en | Enable or disable inactivity events. |
+---------------------------------------------------+----------------------------------------------------------+
| in_accel_x\|y\|z_mag_rising_en | Enable or disable activity events. |
+---------------------------------------------------+----------------------------------------------------------+
The default coupling is DC coupled events. In this case the threshold will
be in place as such, where for the AC coupled case an adaptive threshold
(described in the datasheet) will be applied by the sensor. In general activity,
i.e. ``ACTIVITY`` or ``ACTIVITY_AC`` and inactivity i.e. ``INACTIVITY`` or
``INACTIVITY_AC``, will be linked with auto-sleep enabled when both are enabled.
This means in particular ``ACTIVITY`` can also be linked to ``INACTIVITY_AC``
and vice versa, without problem.
Note here, that ``ACTIVITY`` and ``ACTIVITY_AC`` are mutually exclusive. This
means, that the most recent configuration will be set. For instance, if
``ACTIVITY`` is enabled, and ``ACTIVITY_AC`` will be enabled, the sensor driver
will have ``ACTIVITY`` disabled, but ``ACTIVITY_AC`` enabled. The same is valid
for inactivity. In case of turning off an event, it has to match to what is
actually enabled, i.e. enabling ``ACTIVITY_AC`` and then disabling ``ACTIVITY``
is simply ignored as it is already disabled. Or, as if it was any other not
enabled event, too.
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::
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 ADXL313 driver offers data for a single 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 |
+-------------------------------------+---------------------------+
Usage examples
--------------
Show device name:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat name
adxl313
Show accelerometer channels value:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_raw
2
root:/sys/bus/iio/devices/iio:device0> cat in_accel_y_raw
-57
root:/sys/bus/iio/devices/iio:device0> cat in_accel_z_raw
2
root:/sys/bus/iio/devices/iio:device0> cat in_accel_scale
0.009576806
The accelerometer values will be:
- X-axis acceleration = in_accel_x_raw * in_accel_scale = 0.0191536 m/s^2
- Y-axis acceleration = in_accel_y_raw * in_accel_scale = -0.5458779 m/s^2
- Z-axis acceleration = in_accel_z_raw * in_accel_scale = 0.0191536 m/s^2
Set calibration offset for accelerometer channels. Note, that the calibration
will be rounded according to the graduation of LSB units:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias
0
root:/sys/bus/iio/devices/iio:device0> echo 50 > in_accel_x_calibbias
root:/sys/bus/iio/devices/iio:device0> cat in_accel_x_calibbias
48
Set sampling frequency:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> cat in_accel_sampling_frequency
100.000000
root:/sys/bus/iio/devices/iio:device0> cat in_accel_sampling_frequency_available
6.250000 12.500000 25.000000 50.000000 100.000000 200.000000 400.000000 800.000000 1600.000000 3200.000000
root:/sys/bus/iio/devices/iio:device0> echo 400 > in_accel_sampling_frequency
root:/sys/bus/iio/devices/iio:device0> cat in_accel_sampling_frequency
400.000000
3. Device buffers and triggers
==============================
This driver supports IIO buffers.
All devices support retrieving the raw acceleration measurements using buffers.
Usage examples
--------------
Select channels for buffer read:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_x_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_y_en
root:/sys/bus/iio/devices/iio:device0> echo 1 > scan_elements/in_accel_z_en
Set the number of samples to be stored in the buffer:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 10 > buffer/length
Enable buffer readings:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 1 > buffer/enable
Obtain buffered data:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> hexdump -C /dev/iio\:device0
...
000000d0 01 fc 31 00 c7 ff 03 fc 31 00 c7 ff 04 fc 33 00 |..1.....1.....3.|
000000e0 c8 ff 03 fc 32 00 c5 ff ff fc 32 00 c7 ff 0a fc |....2.....2.....|
000000f0 30 00 c8 ff 06 fc 33 00 c7 ff 01 fc 2f 00 c8 ff |0.....3...../...|
00000100 02 fc 32 00 c6 ff 04 fc 33 00 c8 ff 05 fc 33 00 |..2.....3.....3.|
00000110 ca ff 02 fc 31 00 c7 ff 02 fc 30 00 c9 ff 09 fc |....1.....0.....|
00000120 35 00 c9 ff 08 fc 35 00 c8 ff 02 fc 31 00 c5 ff |5.....5.....1...|
00000130 03 fc 32 00 c7 ff 04 fc 32 00 c7 ff 02 fc 31 00 |..2.....2.....1.|
00000140 c7 ff 08 fc 30 00 c7 ff 02 fc 32 00 c5 ff ff fc |....0.....2.....|
00000150 31 00 c5 ff 04 fc 31 00 c8 ff 03 fc 32 00 c8 ff |1.....1.....2...|
00000160 01 fc 31 00 c7 ff 05 fc 31 00 c3 ff 04 fc 31 00 |..1.....1.....1.|
00000170 c5 ff 04 fc 30 00 c7 ff 03 fc 31 00 c9 ff 03 fc |....0.....1.....|
...
Enabling activity detection:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 1.28125 > ./events/in_accel_mag_rising_value
root:/sys/bus/iio/devices/iio:device0> echo 1 > ./events/in_accel_x\|y\|z_mag_rising_en
root:/sys/bus/iio/devices/iio:device0> iio_event_monitor adxl313
Found IIO device with name adxl313 with device number 0
<only while moving the sensor>
Event: time: 1748795762298351281, type: accel(x|y|z), channel: 0, evtype: mag, direction: rising
Event: time: 1748795762302653704, type: accel(x|y|z), channel: 0, evtype: mag, direction: rising
Event: time: 1748795762304340726, type: accel(x|y|z), channel: 0, evtype: mag, direction: rising
...
Disabling activity detection:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 0 > ./events/in_accel_x\|y\|z_mag_rising_en
root:/sys/bus/iio/devices/iio:device0> iio_event_monitor adxl313
<nothing>
Enabling inactivity detection:
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 1.234375 > ./events/in_accel_mag_falling_value
root:/sys/bus/iio/devices/iio:device0> echo 5 > ./events/in_accel_mag_falling_period
root:/sys/bus/iio/devices/iio:device0> echo 1 > ./events/in_accel_x\&y\&z_mag_falling_en
root:/sys/bus/iio/devices/iio:device0> iio_event_monitor adxl313
Found IIO device with name adxl313 with device number 0
Event: time: 1748796324115962975, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling
Event: time: 1748796329329981772, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling
Event: time: 1748796334543399706, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling
...
<every 5s now indicates inactivity>
Now, enabling activity, e.g. the AC coupled counter-part ``ACTIVITY_AC``
.. code-block:: bash
root:/sys/bus/iio/devices/iio:device0> echo 1.28125 > ./events/in_accel_mag_rising_value
root:/sys/bus/iio/devices/iio:device0> echo 1 > ./events/in_accel_x\|y\|z_mag_rising_en
root:/sys/bus/iio/devices/iio:device0> iio_event_monitor adxl313
Found IIO device with name adxl313 with device number 0
<some activity with the sensor>
Event: time: 1748796880354686777, type: accel(x|y|z), channel: 0, evtype: mag_adaptive, direction: rising
<5s of inactivity, then>
Event: time: 1748796885543252017, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling
<some other activity detected by accelerating the sensor>
Event: time: 1748796887756634678, type: accel(x|y|z), channel: 0, evtype: mag_adaptive, direction: rising
<again, 5s of inactivity>
Event: time: 1748796892964368352, type: accel(x&y&z), channel: 0, evtype: mag, direction: falling
<stays like this until next activity in auto-sleep>
Note, when AC coupling is in place, the event type will be of ``mag_adaptive``.
AC- or DC-coupled (the default) events are used similarly.
4. IIO Interfacing Tools
========================
See Documentation/iio/iio_tools.rst for the description of the available IIO
interfacing tools.

View File

@@ -31,6 +31,7 @@ Industrial I/O Kernel Drivers
adis16475
adis16480
adis16550
adxl313
adxl380
bno055
ep93xx_adc

View File

@@ -1374,6 +1374,14 @@ F: Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml
F: Documentation/iio/ad4030.rst
F: drivers/iio/adc/ad4030.c
ANALOG DEVICES INC AD4080 DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad4080.yaml
F: drivers/iio/adc/ad4080.c
ANALOG DEVICES INC AD4130 DRIVER
M: Cosmin Tanislav <cosmin.tanislav@analog.com>
L: linux-iio@vger.kernel.org
@@ -1383,6 +1391,14 @@ F: Documentation/ABI/testing/sysfs-bus-iio-adc-ad4130
F: Documentation/devicetree/bindings/iio/adc/adi,ad4130.yaml
F: drivers/iio/adc/ad4130.c
ANALOG DEVICES INC AD4170-4 DRIVER
M: Marcelo Schmitt <marcelo.schmitt@analog.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad4170-4.yaml
F: drivers/iio/adc/ad4170-4.c
ANALOG DEVICES INC AD4695 DRIVER
M: Michael Hennerich <michael.hennerich@analog.com>
M: Nuno Sá <nuno.sa@analog.com>
@@ -1481,6 +1497,7 @@ S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.yaml
F: drivers/iio/adc/ad7768-1.c
F: include/dt-bindings/iio/adc/adi,ad7768-1.h
ANALOG DEVICES INC AD7780 DRIVER
M: Michael Hennerich <Michael.Hennerich@analog.com>
@@ -23535,7 +23552,6 @@ STAGING - INDUSTRIAL IO
M: Jonathan Cameron <jic23@kernel.org>
L: linux-iio@vger.kernel.org
S: Odd Fixes
F: Documentation/devicetree/bindings/staging/iio/
F: drivers/staging/iio/
STAGING - NVIDIA COMPLIANT EMBEDDED CONTROLLER INTERFACE (nvec)
@@ -25194,9 +25210,10 @@ W: https://github.com/srcres258/linux-doc
T: git git://github.com/srcres258/linux-doc.git doc-zh-tw
F: Documentation/translations/zh_TW/
TRIGGER SOURCE - PWM
TRIGGER SOURCE
M: David Lechner <dlechner@baylibre.com>
S: Maintained
F: Documentation/devicetree/bindings/trigger-source/gpio-trigger.yaml
F: Documentation/devicetree/bindings/trigger-source/pwm-trigger.yaml
TRUSTED SECURITY MODULE (TSM) INFRASTRUCTURE

View File

@@ -18,10 +18,14 @@
#define ADXL313_REG_SOFT_RESET 0x18
#define ADXL313_REG_OFS_AXIS(index) (0x1E + (index))
#define ADXL313_REG_THRESH_ACT 0x24
#define ADXL313_REG_THRESH_INACT 0x25
#define ADXL313_REG_TIME_INACT 0x26
#define ADXL313_REG_ACT_INACT_CTL 0x27
#define ADXL313_REG_BW_RATE 0x2C
#define ADXL313_REG_POWER_CTL 0x2D
#define ADXL313_REG_INT_ENABLE 0x2E
#define ADXL313_REG_INT_MAP 0x2F
#define ADXL313_REG_INT_SOURCE 0x30
#define ADXL313_REG_DATA_FORMAT 0x31
#define ADXL313_REG_DATA_AXIS(index) (0x32 + ((index) * 2))
#define ADXL313_REG_FIFO_CTL 0x38
@@ -36,8 +40,10 @@
#define ADXL313_RATE_MSK GENMASK(3, 0)
#define ADXL313_RATE_BASE 6
#define ADXL313_POWER_CTL_MSK GENMASK(3, 2)
#define ADXL313_MEASUREMENT_MODE BIT(3)
#define ADXL313_POWER_CTL_MSK BIT(3)
#define ADXL313_POWER_CTL_INACT_MSK GENMASK(5, 4)
#define ADXL313_POWER_CTL_LINK BIT(5)
#define ADXL313_POWER_CTL_AUTO_SLEEP BIT(4)
#define ADXL313_RANGE_MSK GENMASK(1, 0)
#define ADXL313_RANGE_MAX 3
@@ -46,6 +52,25 @@
#define ADXL313_SPI_3WIRE BIT(6)
#define ADXL313_I2C_DISABLE BIT(6)
#define ADXL313_INT_OVERRUN BIT(0)
#define ADXL313_INT_WATERMARK BIT(1)
#define ADXL313_INT_INACTIVITY BIT(3)
#define ADXL313_INT_ACTIVITY BIT(4)
#define ADXL313_INT_DREADY BIT(7)
/* FIFO entries: how many values are stored in the FIFO */
#define ADXL313_REG_FIFO_STATUS_ENTRIES_MSK GENMASK(5, 0)
/* FIFO samples: number of samples needed for watermark (FIFO mode) */
#define ADXL313_REG_FIFO_CTL_SAMPLES_MSK GENMASK(4, 0)
#define ADXL313_REG_FIFO_CTL_MODE_MSK GENMASK(7, 6)
#define ADXL313_FIFO_BYPASS 0
#define ADXL313_FIFO_STREAM 2
#define ADXL313_FIFO_SIZE 32
#define ADXL313_NUM_AXIS 3
extern const struct regmap_access_table adxl312_readable_regs_table;
extern const struct regmap_access_table adxl313_readable_regs_table;
extern const struct regmap_access_table adxl314_readable_regs_table;
@@ -54,6 +79,8 @@ extern const struct regmap_access_table adxl312_writable_regs_table;
extern const struct regmap_access_table adxl313_writable_regs_table;
extern const struct regmap_access_table adxl314_writable_regs_table;
bool adxl313_is_volatile_reg(struct device *dev, unsigned int reg);
enum adxl313_device_type {
ADXL312,
ADXL313,
@@ -64,7 +91,9 @@ struct adxl313_data {
struct regmap *regmap;
const struct adxl313_chip_info *chip_info;
struct mutex lock; /* lock to protect transf_buf */
u8 watermark;
__le16 transf_buf __aligned(IIO_DMA_MINALIGN);
__le16 fifo_buf[ADXL313_NUM_AXIS * ADXL313_FIFO_SIZE + 1];
};
struct adxl313_chip_info {

View File

@@ -8,11 +8,62 @@
*/
#include <linux/bitfield.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/overflow.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/units.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
#include <linux/iio/kfifo_buf.h>
#include "adxl313.h"
#define ADXL313_INT_NONE U8_MAX
#define ADXL313_INT1 1
#define ADXL313_INT2 2
#define ADXL313_REG_XYZ_BASE ADXL313_REG_DATA_AXIS(0)
#define ADXL313_ACT_XYZ_EN GENMASK(6, 4)
#define ADXL313_INACT_XYZ_EN GENMASK(2, 0)
#define ADXL313_REG_ACT_ACDC_MSK BIT(7)
#define ADXL313_REG_INACT_ACDC_MSK BIT(3)
#define ADXL313_COUPLING_DC 0
#define ADXL313_COUPLING_AC 1
/* activity/inactivity */
enum adxl313_activity_type {
ADXL313_ACTIVITY,
ADXL313_INACTIVITY,
ADXL313_ACTIVITY_AC,
ADXL313_INACTIVITY_AC,
};
static const unsigned int adxl313_act_int_reg[] = {
[ADXL313_ACTIVITY] = ADXL313_INT_ACTIVITY,
[ADXL313_INACTIVITY] = ADXL313_INT_INACTIVITY,
[ADXL313_ACTIVITY_AC] = ADXL313_INT_ACTIVITY,
[ADXL313_INACTIVITY_AC] = ADXL313_INT_INACTIVITY,
};
static const unsigned int adxl313_act_thresh_reg[] = {
[ADXL313_ACTIVITY] = ADXL313_REG_THRESH_ACT,
[ADXL313_INACTIVITY] = ADXL313_REG_THRESH_INACT,
[ADXL313_ACTIVITY_AC] = ADXL313_REG_THRESH_ACT,
[ADXL313_INACTIVITY_AC] = ADXL313_REG_THRESH_INACT,
};
static const unsigned int adxl313_act_acdc_msk[] = {
[ADXL313_ACTIVITY] = ADXL313_REG_ACT_ACDC_MSK,
[ADXL313_INACTIVITY] = ADXL313_REG_INACT_ACDC_MSK,
[ADXL313_ACTIVITY_AC] = ADXL313_REG_ACT_ACDC_MSK,
[ADXL313_INACTIVITY_AC] = ADXL313_REG_INACT_ACDC_MSK,
};
static const struct regmap_range adxl312_readable_reg_range[] = {
regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0),
regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
@@ -46,6 +97,30 @@ const struct regmap_access_table adxl314_readable_regs_table = {
};
EXPORT_SYMBOL_NS_GPL(adxl314_readable_regs_table, "IIO_ADXL313");
bool adxl313_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case ADXL313_REG_DATA_AXIS(0):
case ADXL313_REG_DATA_AXIS(1):
case ADXL313_REG_DATA_AXIS(2):
case ADXL313_REG_DATA_AXIS(3):
case ADXL313_REG_DATA_AXIS(4):
case ADXL313_REG_DATA_AXIS(5):
case ADXL313_REG_FIFO_STATUS:
case ADXL313_REG_INT_SOURCE:
return true;
default:
return false;
}
}
EXPORT_SYMBOL_NS_GPL(adxl313_is_volatile_reg, "IIO_ADXL313");
static int adxl313_set_measure_en(struct adxl313_data *data, bool en)
{
return regmap_assign_bits(data->regmap, ADXL313_REG_POWER_CTL,
ADXL313_POWER_CTL_MSK, en);
}
static int adxl312_check_id(struct device *dev,
struct adxl313_data *data)
{
@@ -171,9 +246,10 @@ static const int adxl313_odr_freqs[][2] = {
[9] = { 3200, 0 },
};
#define ADXL313_ACCEL_CHANNEL(index, axis) { \
#define ADXL313_ACCEL_CHANNEL(index, reg, axis) { \
.type = IIO_ACCEL, \
.address = index, \
.scan_index = (index), \
.address = (reg), \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
@@ -183,14 +259,77 @@ static const int adxl313_odr_freqs[][2] = {
.info_mask_shared_by_type_available = \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_type = { \
.sign = 's', \
.realbits = 13, \
.storagebits = 16, \
.endianness = IIO_BE, \
}, \
}
static const struct iio_event_spec adxl313_activity_events[] = {
{
.type = IIO_EV_TYPE_MAG,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
},
{
/* activity, AC bit set */
.type = IIO_EV_TYPE_MAG_ADAPTIVE,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
},
};
static const struct iio_event_spec adxl313_inactivity_events[] = {
{
/* inactivity */
.type = IIO_EV_TYPE_MAG,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_PERIOD),
},
{
/* inactivity, AC bit set */
.type = IIO_EV_TYPE_MAG_ADAPTIVE,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_PERIOD),
},
};
enum adxl313_chans {
chan_x, chan_y, chan_z,
};
static const struct iio_chan_spec adxl313_channels[] = {
ADXL313_ACCEL_CHANNEL(0, X),
ADXL313_ACCEL_CHANNEL(1, Y),
ADXL313_ACCEL_CHANNEL(2, Z),
ADXL313_ACCEL_CHANNEL(0, chan_x, X),
ADXL313_ACCEL_CHANNEL(1, chan_y, Y),
ADXL313_ACCEL_CHANNEL(2, chan_z, Z),
{
.type = IIO_ACCEL,
.modified = 1,
.channel2 = IIO_MOD_X_OR_Y_OR_Z,
.scan_index = -1, /* Fake channel for axis OR'ing */
.event_spec = adxl313_activity_events,
.num_event_specs = ARRAY_SIZE(adxl313_activity_events),
},
{
.type = IIO_ACCEL,
.modified = 1,
.channel2 = IIO_MOD_X_AND_Y_AND_Z,
.scan_index = -1, /* Fake channel for axis AND'ing */
.event_spec = adxl313_inactivity_events,
.num_event_specs = ARRAY_SIZE(adxl313_inactivity_events),
},
};
static const unsigned long adxl313_scan_masks[] = {
BIT(chan_x) | BIT(chan_y) | BIT(chan_z),
0
};
static int adxl313_set_odr(struct adxl313_data *data,
@@ -248,6 +387,230 @@ static int adxl313_read_freq_avail(struct iio_dev *indio_dev,
}
}
static int adxl313_set_inact_time_s(struct adxl313_data *data,
unsigned int val_s)
{
unsigned int max_boundary = U8_MAX; /* by register size */
unsigned int val = min(val_s, max_boundary);
return regmap_write(data->regmap, ADXL313_REG_TIME_INACT, val);
}
/**
* adxl313_is_act_inact_ac() - Check if AC coupling is enabled.
* @data: The device data.
* @type: The activity or inactivity type.
*
* Provide a type of activity or inactivity, combined with either AC coupling
* set, or default to DC coupling. This function verifies if the combination is
* currently enabled or not.
*
* Return: if the provided activity type has AC coupling enabled or a negative
* error value.
*/
static int adxl313_is_act_inact_ac(struct adxl313_data *data,
enum adxl313_activity_type type)
{
unsigned int regval;
bool coupling;
int ret;
ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, &regval);
if (ret)
return ret;
coupling = adxl313_act_acdc_msk[type] & regval;
switch (type) {
case ADXL313_ACTIVITY:
case ADXL313_INACTIVITY:
return coupling == ADXL313_COUPLING_DC;
case ADXL313_ACTIVITY_AC:
case ADXL313_INACTIVITY_AC:
return coupling == ADXL313_COUPLING_AC;
default:
return -EINVAL;
}
}
static int adxl313_set_act_inact_ac(struct adxl313_data *data,
enum adxl313_activity_type type,
bool cmd_en)
{
unsigned int act_inact_ac;
switch (type) {
case ADXL313_ACTIVITY_AC:
case ADXL313_INACTIVITY_AC:
act_inact_ac = ADXL313_COUPLING_AC && cmd_en;
break;
case ADXL313_ACTIVITY:
case ADXL313_INACTIVITY:
act_inact_ac = ADXL313_COUPLING_DC && cmd_en;
break;
default:
return -EINVAL;
}
return regmap_assign_bits(data->regmap, ADXL313_REG_ACT_INACT_CTL,
adxl313_act_acdc_msk[type], act_inact_ac);
}
static int adxl313_is_act_inact_en(struct adxl313_data *data,
enum adxl313_activity_type type)
{
unsigned int axis_ctrl;
unsigned int regval;
bool int_en;
int ret;
ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, &axis_ctrl);
if (ret)
return ret;
/* Check if axis for activity are enabled */
switch (type) {
case ADXL313_ACTIVITY:
case ADXL313_ACTIVITY_AC:
if (!FIELD_GET(ADXL313_ACT_XYZ_EN, axis_ctrl))
return false;
break;
case ADXL313_INACTIVITY:
case ADXL313_INACTIVITY_AC:
if (!FIELD_GET(ADXL313_INACT_XYZ_EN, axis_ctrl))
return false;
break;
default:
return -EINVAL;
}
/* Check if specific interrupt is enabled */
ret = regmap_read(data->regmap, ADXL313_REG_INT_ENABLE, &regval);
if (ret)
return ret;
int_en = adxl313_act_int_reg[type] & regval;
if (!int_en)
return false;
/* Check if configured coupling matches provided type */
return adxl313_is_act_inact_ac(data, type);
}
static int adxl313_set_act_inact_linkbit(struct adxl313_data *data, bool en)
{
int act_ac_en, inact_ac_en;
int act_en, inact_en;
act_en = adxl313_is_act_inact_en(data, ADXL313_ACTIVITY);
if (act_en < 0)
return act_en;
act_ac_en = adxl313_is_act_inact_en(data, ADXL313_ACTIVITY_AC);
if (act_ac_en < 0)
return act_ac_en;
inact_en = adxl313_is_act_inact_en(data, ADXL313_INACTIVITY);
if (inact_en < 0)
return inact_en;
inact_ac_en = adxl313_is_act_inact_en(data, ADXL313_INACTIVITY_AC);
if (inact_ac_en < 0)
return inact_ac_en;
act_en = act_en || act_ac_en;
inact_en = inact_en || inact_ac_en;
return regmap_assign_bits(data->regmap, ADXL313_REG_POWER_CTL,
ADXL313_POWER_CTL_AUTO_SLEEP | ADXL313_POWER_CTL_LINK,
en && act_en && inact_en);
}
static int adxl313_set_act_inact_en(struct adxl313_data *data,
enum adxl313_activity_type type,
bool cmd_en)
{
unsigned int axis_ctrl;
unsigned int threshold;
unsigned int inact_time_s;
int ret;
if (cmd_en) {
/* When turning on, check if threshold is valid */
ret = regmap_read(data->regmap, adxl313_act_thresh_reg[type],
&threshold);
if (ret)
return ret;
if (!threshold) /* Just ignore the command if threshold is 0 */
return 0;
/* When turning on inactivity, check if inact time is valid */
if (type == ADXL313_INACTIVITY || type == ADXL313_INACTIVITY_AC) {
ret = regmap_read(data->regmap,
ADXL313_REG_TIME_INACT,
&inact_time_s);
if (ret)
return ret;
if (!inact_time_s)
return 0;
}
} else {
/*
* When turning off an activity, ensure that the correct
* coupling event is specified. This step helps prevent misuse -
* for example, if an AC-coupled activity is active and the
* current call attempts to turn off a DC-coupled activity, this
* inconsistency should be detected here.
*/
if (adxl313_is_act_inact_ac(data, type) <= 0)
return 0;
}
/* Start modifying configuration registers */
ret = adxl313_set_measure_en(data, false);
if (ret)
return ret;
/* Enable axis according to the command */
switch (type) {
case ADXL313_ACTIVITY:
case ADXL313_ACTIVITY_AC:
axis_ctrl = ADXL313_ACT_XYZ_EN;
break;
case ADXL313_INACTIVITY:
case ADXL313_INACTIVITY_AC:
axis_ctrl = ADXL313_INACT_XYZ_EN;
break;
default:
return -EINVAL;
}
ret = regmap_assign_bits(data->regmap, ADXL313_REG_ACT_INACT_CTL,
axis_ctrl, cmd_en);
if (ret)
return ret;
/* Update AC/DC-coupling according to the command */
ret = adxl313_set_act_inact_ac(data, type, cmd_en);
if (ret)
return ret;
/* Enable the interrupt line, according to the command */
ret = regmap_assign_bits(data->regmap, ADXL313_REG_INT_ENABLE,
adxl313_act_int_reg[type], cmd_en);
if (ret)
return ret;
/* Set link-bit and auto-sleep only when ACT and INACT are enabled */
ret = adxl313_set_act_inact_linkbit(data, cmd_en);
if (ret)
return ret;
return adxl313_set_measure_en(data, true);
}
static int adxl313_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@@ -321,10 +684,474 @@ static int adxl313_write_raw(struct iio_dev *indio_dev,
}
}
static int adxl313_read_mag_config(struct adxl313_data *data,
enum iio_event_direction dir,
enum adxl313_activity_type type_act,
enum adxl313_activity_type type_inact)
{
switch (dir) {
case IIO_EV_DIR_RISING:
return !!adxl313_is_act_inact_en(data, type_act);
case IIO_EV_DIR_FALLING:
return !!adxl313_is_act_inact_en(data, type_inact);
default:
return -EINVAL;
}
}
static int adxl313_write_mag_config(struct adxl313_data *data,
enum iio_event_direction dir,
enum adxl313_activity_type type_act,
enum adxl313_activity_type type_inact,
bool state)
{
switch (dir) {
case IIO_EV_DIR_RISING:
return adxl313_set_act_inact_en(data, type_act, state);
case IIO_EV_DIR_FALLING:
return adxl313_set_act_inact_en(data, type_inact, state);
default:
return -EINVAL;
}
}
static int adxl313_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir)
{
struct adxl313_data *data = iio_priv(indio_dev);
switch (type) {
case IIO_EV_TYPE_MAG:
return adxl313_read_mag_config(data, dir,
ADXL313_ACTIVITY,
ADXL313_INACTIVITY);
case IIO_EV_TYPE_MAG_ADAPTIVE:
return adxl313_read_mag_config(data, dir,
ADXL313_ACTIVITY_AC,
ADXL313_INACTIVITY_AC);
default:
return -EINVAL;
}
}
static int adxl313_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
bool state)
{
struct adxl313_data *data = iio_priv(indio_dev);
switch (type) {
case IIO_EV_TYPE_MAG:
return adxl313_write_mag_config(data, dir,
ADXL313_ACTIVITY,
ADXL313_INACTIVITY,
state);
case IIO_EV_TYPE_MAG_ADAPTIVE:
return adxl313_write_mag_config(data, dir,
ADXL313_ACTIVITY_AC,
ADXL313_INACTIVITY_AC,
state);
default:
return -EINVAL;
}
}
static int adxl313_read_mag_value(struct adxl313_data *data,
enum iio_event_direction dir,
enum iio_event_info info,
enum adxl313_activity_type type_act,
enum adxl313_activity_type type_inact,
int *val, int *val2)
{
unsigned int threshold;
unsigned int period;
int ret;
switch (info) {
case IIO_EV_INFO_VALUE:
switch (dir) {
case IIO_EV_DIR_RISING:
ret = regmap_read(data->regmap,
adxl313_act_thresh_reg[type_act],
&threshold);
if (ret)
return ret;
*val = threshold * 15625;
*val2 = MICRO;
return IIO_VAL_FRACTIONAL;
case IIO_EV_DIR_FALLING:
ret = regmap_read(data->regmap,
adxl313_act_thresh_reg[type_inact],
&threshold);
if (ret)
return ret;
*val = threshold * 15625;
*val2 = MICRO;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
case IIO_EV_INFO_PERIOD:
ret = regmap_read(data->regmap, ADXL313_REG_TIME_INACT,
&period);
if (ret)
return ret;
*val = period;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int adxl313_write_mag_value(struct adxl313_data *data,
enum iio_event_direction dir,
enum iio_event_info info,
enum adxl313_activity_type type_act,
enum adxl313_activity_type type_inact,
int val, int val2)
{
unsigned int regval;
switch (info) {
case IIO_EV_INFO_VALUE:
/* Scale factor 15.625 mg/LSB */
regval = DIV_ROUND_CLOSEST(MICRO * val + val2, 15625);
switch (dir) {
case IIO_EV_DIR_RISING:
return regmap_write(data->regmap,
adxl313_act_thresh_reg[type_act],
regval);
case IIO_EV_DIR_FALLING:
return regmap_write(data->regmap,
adxl313_act_thresh_reg[type_inact],
regval);
default:
return -EINVAL;
}
case IIO_EV_INFO_PERIOD:
return adxl313_set_inact_time_s(data, val);
default:
return -EINVAL;
}
}
static int adxl313_read_event_value(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int *val, int *val2)
{
struct adxl313_data *data = iio_priv(indio_dev);
switch (type) {
case IIO_EV_TYPE_MAG:
return adxl313_read_mag_value(data, dir, info,
ADXL313_ACTIVITY,
ADXL313_INACTIVITY,
val, val2);
case IIO_EV_TYPE_MAG_ADAPTIVE:
return adxl313_read_mag_value(data, dir, info,
ADXL313_ACTIVITY_AC,
ADXL313_INACTIVITY_AC,
val, val2);
default:
return -EINVAL;
}
}
static int adxl313_write_event_value(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int val, int val2)
{
struct adxl313_data *data = iio_priv(indio_dev);
switch (type) {
case IIO_EV_TYPE_MAG:
return adxl313_write_mag_value(data, dir, info,
ADXL313_ACTIVITY,
ADXL313_INACTIVITY,
val, val2);
case IIO_EV_TYPE_MAG_ADAPTIVE:
return adxl313_write_mag_value(data, dir, info,
ADXL313_ACTIVITY_AC,
ADXL313_INACTIVITY_AC,
val, val2);
default:
return -EINVAL;
}
}
static int adxl313_set_watermark(struct iio_dev *indio_dev, unsigned int value)
{
struct adxl313_data *data = iio_priv(indio_dev);
int ret;
value = min(value, ADXL313_FIFO_SIZE - 1);
ret = adxl313_set_measure_en(data, false);
if (ret)
return ret;
ret = regmap_update_bits(data->regmap, ADXL313_REG_FIFO_CTL,
ADXL313_REG_FIFO_CTL_MODE_MSK, value);
if (ret)
return ret;
data->watermark = value;
ret = regmap_set_bits(data->regmap, ADXL313_REG_INT_ENABLE,
ADXL313_INT_WATERMARK);
if (ret)
return ret;
return adxl313_set_measure_en(data, true);
}
static int adxl313_get_samples(struct adxl313_data *data)
{
unsigned int regval;
int ret;
ret = regmap_read(data->regmap, ADXL313_REG_FIFO_STATUS, &regval);
if (ret)
return ret;
return FIELD_GET(ADXL313_REG_FIFO_STATUS_ENTRIES_MSK, regval);
}
static int adxl313_fifo_transfer(struct adxl313_data *data, int samples)
{
unsigned int i;
int ret;
for (i = 0; i < samples; i++) {
ret = regmap_bulk_read(data->regmap, ADXL313_REG_XYZ_BASE,
data->fifo_buf + (i * ADXL313_NUM_AXIS),
sizeof(data->fifo_buf[0]) * ADXL313_NUM_AXIS);
if (ret)
return ret;
}
return 0;
}
/**
* adxl313_fifo_reset() - Reset the FIFO and interrupt status registers.
* @data: The device data.
*
* Reset the FIFO status registers. Reading out status registers clears the
* FIFO and interrupt configuration. Thus do not evaluate regmap return values.
* Ignore particular read register content. Register content is not processed
* any further. Therefore the function returns void.
*/
static void adxl313_fifo_reset(struct adxl313_data *data)
{
unsigned int regval;
int samples;
adxl313_set_measure_en(data, false);
samples = adxl313_get_samples(data);
if (samples > 0)
adxl313_fifo_transfer(data, samples);
regmap_read(data->regmap, ADXL313_REG_INT_SOURCE, &regval);
adxl313_set_measure_en(data, true);
}
static int adxl313_buffer_postenable(struct iio_dev *indio_dev)
{
struct adxl313_data *data = iio_priv(indio_dev);
int ret;
/* Set FIFO modes with measurement turned off, according to datasheet */
ret = adxl313_set_measure_en(data, false);
if (ret)
return ret;
ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL,
FIELD_PREP(ADXL313_REG_FIFO_CTL_SAMPLES_MSK, data->watermark) |
FIELD_PREP(ADXL313_REG_FIFO_CTL_MODE_MSK, ADXL313_FIFO_STREAM));
if (ret)
return ret;
return adxl313_set_measure_en(data, true);
}
static int adxl313_buffer_predisable(struct iio_dev *indio_dev)
{
struct adxl313_data *data = iio_priv(indio_dev);
int ret;
ret = adxl313_set_measure_en(data, false);
if (ret)
return ret;
ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL,
FIELD_PREP(ADXL313_REG_FIFO_CTL_MODE_MSK, ADXL313_FIFO_BYPASS));
ret = regmap_write(data->regmap, ADXL313_REG_INT_ENABLE, 0);
if (ret)
return ret;
return adxl313_set_measure_en(data, true);
}
static const struct iio_buffer_setup_ops adxl313_buffer_ops = {
.postenable = adxl313_buffer_postenable,
.predisable = adxl313_buffer_predisable,
};
static int adxl313_fifo_push(struct iio_dev *indio_dev, int samples)
{
struct adxl313_data *data = iio_priv(indio_dev);
unsigned int i;
int ret;
ret = adxl313_fifo_transfer(data, samples);
if (ret)
return ret;
for (i = 0; i < ADXL313_NUM_AXIS * samples; i += ADXL313_NUM_AXIS)
iio_push_to_buffers(indio_dev, &data->fifo_buf[i]);
return 0;
}
static int adxl313_push_events(struct iio_dev *indio_dev, int int_stat)
{
s64 ts = iio_get_time_ns(indio_dev);
struct adxl313_data *data = iio_priv(indio_dev);
unsigned int regval;
int ret = -ENOENT;
if (FIELD_GET(ADXL313_INT_ACTIVITY, int_stat)) {
ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, &regval);
if (ret)
return ret;
if (FIELD_GET(ADXL313_REG_ACT_ACDC_MSK, regval)) {
/* AC coupled */
ret = iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
IIO_MOD_X_OR_Y_OR_Z,
IIO_EV_TYPE_MAG_ADAPTIVE,
IIO_EV_DIR_RISING),
ts);
if (ret)
return ret;
} else {
/* DC coupled, relying on THRESH */
ret = iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
IIO_MOD_X_OR_Y_OR_Z,
IIO_EV_TYPE_MAG,
IIO_EV_DIR_RISING),
ts);
if (ret)
return ret;
}
}
if (FIELD_GET(ADXL313_INT_INACTIVITY, int_stat)) {
ret = regmap_read(data->regmap, ADXL313_REG_ACT_INACT_CTL, &regval);
if (ret)
return ret;
if (FIELD_GET(ADXL313_REG_INACT_ACDC_MSK, regval)) {
/* AC coupled */
ret = iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
IIO_MOD_X_AND_Y_AND_Z,
IIO_EV_TYPE_MAG_ADAPTIVE,
IIO_EV_DIR_FALLING),
ts);
if (ret)
return ret;
} else {
/* DC coupled, relying on THRESH */
ret = iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
IIO_MOD_X_AND_Y_AND_Z,
IIO_EV_TYPE_MAG,
IIO_EV_DIR_FALLING),
ts);
if (ret)
return ret;
}
}
return ret;
}
static irqreturn_t adxl313_irq_handler(int irq, void *p)
{
struct iio_dev *indio_dev = p;
struct adxl313_data *data = iio_priv(indio_dev);
int samples, int_stat;
if (regmap_read(data->regmap, ADXL313_REG_INT_SOURCE, &int_stat))
return IRQ_NONE;
/*
* In cases of sensor events not handled (still not implemented) by
* this driver, the FIFO needs to be drained to become operational
* again. In general the sensor configuration only should issue events
* which were configured by this driver. Anyway a miss-configuration
* easily might end up in a hanging sensor FIFO.
*/
if (adxl313_push_events(indio_dev, int_stat))
goto err_reset_fifo;
if (FIELD_GET(ADXL313_INT_WATERMARK, int_stat)) {
samples = adxl313_get_samples(data);
if (samples < 0)
goto err_reset_fifo;
if (adxl313_fifo_push(indio_dev, samples))
goto err_reset_fifo;
}
if (FIELD_GET(ADXL313_INT_OVERRUN, int_stat))
goto err_reset_fifo;
return IRQ_HANDLED;
err_reset_fifo:
adxl313_fifo_reset(data);
return IRQ_HANDLED;
}
static int adxl313_reg_access(struct iio_dev *indio_dev, unsigned int reg,
unsigned int writeval, unsigned int *readval)
{
struct adxl313_data *data = iio_priv(indio_dev);
if (readval)
return regmap_read(data->regmap, reg, readval);
return regmap_write(data->regmap, reg, writeval);
}
static const struct iio_info adxl313_info = {
.read_raw = adxl313_read_raw,
.write_raw = adxl313_write_raw,
.read_event_config = adxl313_read_event_config,
.write_event_config = adxl313_write_event_config,
.read_event_value = adxl313_read_event_value,
.write_event_value = adxl313_write_event_value,
.read_avail = adxl313_read_freq_avail,
.hwfifo_set_watermark = adxl313_set_watermark,
.debugfs_reg_access = &adxl313_reg_access,
};
static int adxl313_setup(struct device *dev, struct adxl313_data *data,
@@ -369,9 +1196,20 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data,
}
/* Enables measurement mode */
return regmap_update_bits(data->regmap, ADXL313_REG_POWER_CTL,
ADXL313_POWER_CTL_MSK,
ADXL313_MEASUREMENT_MODE);
return adxl313_set_measure_en(data, true);
}
static unsigned int adxl313_get_int_type(struct device *dev, int *irq)
{
*irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT1");
if (*irq > 0)
return ADXL313_INT1;
*irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT2");
if (*irq > 0)
return ADXL313_INT2;
return ADXL313_INT_NONE;
}
/**
@@ -391,7 +1229,9 @@ int adxl313_core_probe(struct device *dev,
{
struct adxl313_data *data;
struct iio_dev *indio_dev;
int ret;
u8 int_line;
u8 int_map_msk;
int irq, ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
@@ -408,6 +1248,7 @@ int adxl313_core_probe(struct device *dev,
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = adxl313_channels;
indio_dev->num_channels = ARRAY_SIZE(adxl313_channels);
indio_dev->available_scan_masks = adxl313_scan_masks;
ret = adxl313_setup(dev, data, setup);
if (ret) {
@@ -415,6 +1256,70 @@ int adxl313_core_probe(struct device *dev,
return ret;
}
int_line = adxl313_get_int_type(dev, &irq);
if (int_line == ADXL313_INT_NONE) {
/*
* FIFO_BYPASSED mode
*
* When no interrupt lines are specified, the driver falls back
* to use the sensor in FIFO_BYPASS mode. This means turning off
* internal FIFO and interrupt generation (since there is no
* line specified). Unmaskable interrupts such as overrun or
* data ready won't interfere. Even that a FIFO_STREAM mode w/o
* connected interrupt line might allow for obtaining raw
* measurements, a fallback to disable interrupts when no
* interrupt lines are connected seems to be the cleaner
* solution.
*/
ret = regmap_write(data->regmap, ADXL313_REG_FIFO_CTL,
FIELD_PREP(ADXL313_REG_FIFO_CTL_MODE_MSK,
ADXL313_FIFO_BYPASS));
if (ret)
return ret;
} else {
/* FIFO_STREAM mode */
int_map_msk = ADXL313_INT_DREADY | ADXL313_INT_ACTIVITY |
ADXL313_INT_INACTIVITY | ADXL313_INT_WATERMARK |
ADXL313_INT_OVERRUN;
ret = regmap_assign_bits(data->regmap, ADXL313_REG_INT_MAP,
int_map_msk, int_line == ADXL313_INT2);
if (ret)
return ret;
/*
* Reset or configure the registers with reasonable default
* values. As having 0 in most cases may result in undesirable
* behavior if the interrupts are enabled.
*/
ret = regmap_write(data->regmap, ADXL313_REG_ACT_INACT_CTL, 0x00);
if (ret)
return ret;
ret = regmap_write(data->regmap, ADXL313_REG_TIME_INACT, 5);
if (ret)
return ret;
ret = regmap_write(data->regmap, ADXL313_REG_THRESH_INACT, 0x4f);
if (ret)
return ret;
ret = regmap_write(data->regmap, ADXL313_REG_THRESH_ACT, 0x52);
if (ret)
return ret;
ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
&adxl313_buffer_ops);
if (ret)
return ret;
ret = devm_request_threaded_irq(dev, irq, NULL,
&adxl313_irq_handler,
IRQF_SHARED | IRQF_ONESHOT,
indio_dev->name, indio_dev);
if (ret)
return ret;
}
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS_GPL(adxl313_core_probe, "IIO_ADXL313");

View File

@@ -21,6 +21,8 @@ static const struct regmap_config adxl31x_i2c_regmap_config[] = {
.rd_table = &adxl312_readable_regs_table,
.wr_table = &adxl312_writable_regs_table,
.max_register = 0x39,
.volatile_reg = adxl313_is_volatile_reg,
.cache_type = REGCACHE_MAPLE,
},
[ADXL313] = {
.reg_bits = 8,
@@ -28,6 +30,8 @@ static const struct regmap_config adxl31x_i2c_regmap_config[] = {
.rd_table = &adxl313_readable_regs_table,
.wr_table = &adxl313_writable_regs_table,
.max_register = 0x39,
.volatile_reg = adxl313_is_volatile_reg,
.cache_type = REGCACHE_MAPLE,
},
[ADXL314] = {
.reg_bits = 8,
@@ -35,6 +39,8 @@ static const struct regmap_config adxl31x_i2c_regmap_config[] = {
.rd_table = &adxl314_readable_regs_table,
.wr_table = &adxl314_writable_regs_table,
.max_register = 0x39,
.volatile_reg = adxl313_is_volatile_reg,
.cache_type = REGCACHE_MAPLE,
},
};

View File

@@ -24,6 +24,8 @@ static const struct regmap_config adxl31x_spi_regmap_config[] = {
.max_register = 0x39,
/* Setting bits 7 and 6 enables multiple-byte read */
.read_flag_mask = BIT(7) | BIT(6),
.volatile_reg = adxl313_is_volatile_reg,
.cache_type = REGCACHE_MAPLE,
},
[ADXL313] = {
.reg_bits = 8,
@@ -33,6 +35,8 @@ static const struct regmap_config adxl31x_spi_regmap_config[] = {
.max_register = 0x39,
/* Setting bits 7 and 6 enables multiple-byte read */
.read_flag_mask = BIT(7) | BIT(6),
.volatile_reg = adxl313_is_volatile_reg,
.cache_type = REGCACHE_MAPLE,
},
[ADXL314] = {
.reg_bits = 8,
@@ -42,6 +46,8 @@ static const struct regmap_config adxl31x_spi_regmap_config[] = {
.max_register = 0x39,
/* Setting bits 7 and 6 enables multiple-byte read */
.read_flag_mask = BIT(7) | BIT(6),
.volatile_reg = adxl313_is_volatile_reg,
.cache_type = REGCACHE_MAPLE,
},
};

View File

@@ -69,11 +69,10 @@
* BW_RATE bits - Bandwidth and output data rate. The default value is
* 0x0A, which translates to a 100 Hz output data rate
*/
#define ADXL345_BW_RATE GENMASK(3, 0)
#define ADXL345_BW_RATE_MSK GENMASK(3, 0)
#define ADXL345_BW_LOW_POWER BIT(4)
#define ADXL345_BASE_RATE_NANO_HZ 97656250LL
#define ADXL345_POWER_CTL_STANDBY 0x00
#define ADXL345_POWER_CTL_WAKEUP GENMASK(1, 0)
#define ADXL345_POWER_CTL_SLEEP BIT(2)
#define ADXL345_POWER_CTL_MEASURE BIT(3)

View File

@@ -64,11 +64,75 @@ static const unsigned int adxl345_tap_time_reg[] = {
[ADXL345_TAP_TIME_DUR] = ADXL345_REG_DUR,
};
enum adxl345_odr {
ADXL345_ODR_0P10HZ = 0,
ADXL345_ODR_0P20HZ,
ADXL345_ODR_0P39HZ,
ADXL345_ODR_0P78HZ,
ADXL345_ODR_1P56HZ,
ADXL345_ODR_3P13HZ,
ADXL345_ODR_6P25HZ,
ADXL345_ODR_12P50HZ,
ADXL345_ODR_25HZ,
ADXL345_ODR_50HZ,
ADXL345_ODR_100HZ,
ADXL345_ODR_200HZ,
ADXL345_ODR_400HZ,
ADXL345_ODR_800HZ,
ADXL345_ODR_1600HZ,
ADXL345_ODR_3200HZ,
};
enum adxl345_range {
ADXL345_2G_RANGE = 0,
ADXL345_4G_RANGE,
ADXL345_8G_RANGE,
ADXL345_16G_RANGE,
};
/* Certain features recommend 12.5 Hz - 400 Hz ODR */
static const int adxl345_odr_tbl[][2] = {
[ADXL345_ODR_0P10HZ] = { 0, 97000 },
[ADXL345_ODR_0P20HZ] = { 0, 195000 },
[ADXL345_ODR_0P39HZ] = { 0, 390000 },
[ADXL345_ODR_0P78HZ] = { 0, 781000 },
[ADXL345_ODR_1P56HZ] = { 1, 562000 },
[ADXL345_ODR_3P13HZ] = { 3, 125000 },
[ADXL345_ODR_6P25HZ] = { 6, 250000 },
[ADXL345_ODR_12P50HZ] = { 12, 500000 },
[ADXL345_ODR_25HZ] = { 25, 0 },
[ADXL345_ODR_50HZ] = { 50, 0 },
[ADXL345_ODR_100HZ] = { 100, 0 },
[ADXL345_ODR_200HZ] = { 200, 0 },
[ADXL345_ODR_400HZ] = { 400, 0 },
[ADXL345_ODR_800HZ] = { 800, 0 },
[ADXL345_ODR_1600HZ] = { 1600, 0 },
[ADXL345_ODR_3200HZ] = { 3200, 0 },
};
/*
* Full resolution frequency table:
* (g * 2 * 9.80665) / (2^(resolution) - 1)
*
* resolution := 13 (full)
* g := 2|4|8|16
*
* 2g at 13bit: 0.004789
* 4g at 13bit: 0.009578
* 8g at 13bit: 0.019156
* 16g at 16bit: 0.038312
*/
static const int adxl345_fullres_range_tbl[][2] = {
[ADXL345_2G_RANGE] = { 0, 4789 },
[ADXL345_4G_RANGE] = { 0, 9578 },
[ADXL345_8G_RANGE] = { 0, 19156 },
[ADXL345_16G_RANGE] = { 0, 38312 },
};
struct adxl345_state {
const struct adxl345_chip_info *info;
struct regmap *regmap;
bool fifo_delay; /* delay: delay is needed for SPI */
int irq;
u8 watermark;
u8 fifo_mode;
@@ -79,7 +143,7 @@ struct adxl345_state {
__le16 fifo_buf[ADXL345_DIRS * ADXL345_FIFO_SIZE + 1] __aligned(IIO_DMA_MINALIGN);
};
static struct iio_event_spec adxl345_events[] = {
static const struct iio_event_spec adxl345_events[] = {
{
/* single tap */
.type = IIO_EV_TYPE_GESTURE,
@@ -107,6 +171,8 @@ static struct iio_event_spec adxl345_events[] = {
BIT(IIO_CHAN_INFO_CALIBBIAS), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.scan_index = (index), \
.scan_type = { \
.sign = 's', \
@@ -167,9 +233,8 @@ EXPORT_SYMBOL_NS_GPL(adxl345_is_volatile_reg, "IIO_ADXL345");
*/
static int adxl345_set_measure_en(struct adxl345_state *st, bool en)
{
unsigned int val = en ? ADXL345_POWER_CTL_MEASURE : ADXL345_POWER_CTL_STANDBY;
return regmap_write(st->regmap, ADXL345_REG_POWER_CTL, val);
return regmap_assign_bits(st->regmap, ADXL345_REG_POWER_CTL,
ADXL345_POWER_CTL_MEASURE, en);
}
/* tap */
@@ -383,14 +448,82 @@ static int adxl345_set_tap_latent(struct adxl345_state *st, u32 val_int,
return _adxl345_set_tap_time(st, ADXL345_TAP_TIME_LATENT, val_fract_us);
}
static int adxl345_find_odr(struct adxl345_state *st, int val,
int val2, enum adxl345_odr *odr)
{
int i;
for (i = 0; i < ARRAY_SIZE(adxl345_odr_tbl); i++) {
if (val == adxl345_odr_tbl[i][0] &&
val2 == adxl345_odr_tbl[i][1]) {
*odr = i;
return 0;
}
}
return -EINVAL;
}
static int adxl345_set_odr(struct adxl345_state *st, enum adxl345_odr odr)
{
return regmap_update_bits(st->regmap, ADXL345_REG_BW_RATE,
ADXL345_BW_RATE_MSK,
FIELD_PREP(ADXL345_BW_RATE_MSK, odr));
}
static int adxl345_find_range(struct adxl345_state *st, int val, int val2,
enum adxl345_range *range)
{
int i;
for (i = 0; i < ARRAY_SIZE(adxl345_fullres_range_tbl); i++) {
if (val == adxl345_fullres_range_tbl[i][0] &&
val2 == adxl345_fullres_range_tbl[i][1]) {
*range = i;
return 0;
}
}
return -EINVAL;
}
static int adxl345_set_range(struct adxl345_state *st, enum adxl345_range range)
{
return regmap_update_bits(st->regmap, ADXL345_REG_DATA_FORMAT,
ADXL345_DATA_FORMAT_RANGE,
FIELD_PREP(ADXL345_DATA_FORMAT_RANGE, range));
}
static int adxl345_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_SCALE:
*vals = (int *)adxl345_fullres_range_tbl;
*type = IIO_VAL_INT_PLUS_MICRO;
*length = ARRAY_SIZE(adxl345_fullres_range_tbl) * 2;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SAMP_FREQ:
*vals = (int *)adxl345_odr_tbl;
*type = IIO_VAL_INT_PLUS_MICRO;
*length = ARRAY_SIZE(adxl345_odr_tbl) * 2;
return IIO_AVAIL_LIST;
}
return -EINVAL;
}
static int adxl345_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct adxl345_state *st = iio_priv(indio_dev);
__le16 accel;
long long samp_freq_nhz;
unsigned int regval;
enum adxl345_odr odr;
enum adxl345_range range;
int ret;
switch (mask) {
@@ -409,8 +542,12 @@ static int adxl345_read_raw(struct iio_dev *indio_dev,
*val = sign_extend32(le16_to_cpu(accel), 12);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
*val2 = st->info->uscale;
ret = regmap_read(st->regmap, ADXL345_REG_DATA_FORMAT, &regval);
if (ret)
return ret;
range = FIELD_GET(ADXL345_DATA_FORMAT_RANGE, regval);
*val = adxl345_fullres_range_tbl[range][0];
*val2 = adxl345_fullres_range_tbl[range][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_CALIBBIAS:
ret = regmap_read(st->regmap,
@@ -428,12 +565,10 @@ static int adxl345_read_raw(struct iio_dev *indio_dev,
ret = regmap_read(st->regmap, ADXL345_REG_BW_RATE, &regval);
if (ret)
return ret;
samp_freq_nhz = ADXL345_BASE_RATE_NANO_HZ <<
(regval & ADXL345_BW_RATE);
*val = div_s64_rem(samp_freq_nhz, NANOHZ_PER_HZ, val2);
return IIO_VAL_INT_PLUS_NANO;
odr = FIELD_GET(ADXL345_BW_RATE_MSK, regval);
*val = adxl345_odr_tbl[odr][0];
*val2 = adxl345_odr_tbl[odr][1];
return IIO_VAL_INT_PLUS_MICRO;
}
return -EINVAL;
@@ -444,7 +579,13 @@ static int adxl345_write_raw(struct iio_dev *indio_dev,
int val, int val2, long mask)
{
struct adxl345_state *st = iio_priv(indio_dev);
s64 n;
enum adxl345_range range;
enum adxl345_odr odr;
int ret;
ret = adxl345_set_measure_en(st, false);
if (ret)
return ret;
switch (mask) {
case IIO_CHAN_INFO_CALIBBIAS:
@@ -452,20 +593,35 @@ static int adxl345_write_raw(struct iio_dev *indio_dev,
* 8-bit resolution at +/- 2g, that is 4x accel data scale
* factor
*/
return regmap_write(st->regmap,
ADXL345_REG_OFS_AXIS(chan->address),
val / 4);
ret = regmap_write(st->regmap,
ADXL345_REG_OFS_AXIS(chan->address),
val / 4);
if (ret)
return ret;
break;
case IIO_CHAN_INFO_SAMP_FREQ:
n = div_s64(val * NANOHZ_PER_HZ + val2,
ADXL345_BASE_RATE_NANO_HZ);
ret = adxl345_find_odr(st, val, val2, &odr);
if (ret)
return ret;
return regmap_update_bits(st->regmap, ADXL345_REG_BW_RATE,
ADXL345_BW_RATE,
clamp_val(ilog2(n), 0,
ADXL345_BW_RATE));
ret = adxl345_set_odr(st, odr);
if (ret)
return ret;
break;
case IIO_CHAN_INFO_SCALE:
ret = adxl345_find_range(st, val, val2, &range);
if (ret)
return ret;
ret = adxl345_set_range(st, range);
if (ret)
return ret;
break;
default:
return -EINVAL;
}
return -EINVAL;
return adxl345_set_measure_en(st, true);
}
static int adxl345_read_event_config(struct iio_dev *indio_dev,
@@ -552,15 +708,15 @@ static int adxl345_read_event_value(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_EV_INFO_TIMEOUT:
*val = st->tap_duration_us;
*val2 = 1000000;
*val2 = MICRO;
return IIO_VAL_FRACTIONAL;
case IIO_EV_INFO_RESET_TIMEOUT:
*val = st->tap_window_us;
*val2 = 1000000;
*val2 = MICRO;
return IIO_VAL_FRACTIONAL;
case IIO_EV_INFO_TAP2_MIN_DELAY:
*val = st->tap_latent_us;
*val2 = 1000000;
*val2 = MICRO;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
@@ -653,8 +809,10 @@ static int adxl345_write_raw_get_fmt(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_CALIBBIAS:
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
return IIO_VAL_INT_PLUS_NANO;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
@@ -667,19 +825,6 @@ static void adxl345_powerdown(void *ptr)
adxl345_set_measure_en(st, false);
}
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
"0.09765625 0.1953125 0.390625 0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600 3200"
);
static struct attribute *adxl345_attrs[] = {
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
NULL
};
static const struct attribute_group adxl345_attrs_group = {
.attrs = adxl345_attrs,
};
static int adxl345_set_fifo(struct adxl345_state *st)
{
unsigned int intio;
@@ -740,15 +885,12 @@ static int adxl345_get_samples(struct adxl345_state *st)
*/
static int adxl345_fifo_transfer(struct adxl345_state *st, int samples)
{
size_t count;
int i, ret = 0;
/* count is the 3x the fifo_buf element size, hence 6B */
count = sizeof(st->fifo_buf[0]) * ADXL345_DIRS;
for (i = 0; i < samples; i++) {
/* read 3x 2 byte elements from base address into next fifo_buf position */
ret = regmap_bulk_read(st->regmap, ADXL345_REG_XYZ_BASE,
st->fifo_buf + (i * count / 2), count);
st->fifo_buf + (i * ADXL345_DIRS),
sizeof(st->fifo_buf[0]) * ADXL345_DIRS);
if (ret)
return ret;
@@ -931,9 +1073,9 @@ static irqreturn_t adxl345_irq_handler(int irq, void *p)
}
static const struct iio_info adxl345_info = {
.attrs = &adxl345_attrs_group,
.read_raw = adxl345_read_raw,
.write_raw = adxl345_write_raw,
.read_avail = adxl345_read_avail,
.write_raw_get_fmt = adxl345_write_raw_get_fmt,
.read_event_config = adxl345_read_event_config,
.write_event_config = adxl345_write_event_config,
@@ -943,6 +1085,19 @@ static const struct iio_info adxl345_info = {
.hwfifo_set_watermark = adxl345_set_watermark,
};
static int adxl345_get_int_line(struct device *dev, int *irq)
{
*irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT1");
if (*irq > 0)
return ADXL345_INT1;
*irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT2");
if (*irq > 0)
return ADXL345_INT2;
return ADXL345_INT_NONE;
}
/**
* adxl345_core_probe() - Probe and setup for the accelerometer.
* @dev: Driver model representation of the device
@@ -973,6 +1128,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
ADXL345_DATA_FORMAT_FULL_RES |
ADXL345_DATA_FORMAT_SELF_TEST);
unsigned int tap_threshold;
int irq;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
@@ -999,6 +1155,19 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
indio_dev->num_channels = ARRAY_SIZE(adxl345_channels);
indio_dev->available_scan_masks = adxl345_scan_masks;
/*
* Using I2C at 100kHz would limit the maximum ODR to 200Hz, operation
* at an output rate above the recommended maximum may result in
* undesired behavior.
*/
ret = adxl345_set_odr(st, ADXL345_ODR_200HZ);
if (ret)
return ret;
ret = adxl345_set_range(st, ADXL345_16G_RANGE);
if (ret)
return ret;
/* Reset interrupts at start up */
ret = regmap_write(st->regmap, ADXL345_REG_INT_ENABLE, 0x00);
if (ret)
@@ -1044,23 +1213,16 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
if (ret)
return ret;
st->irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT1");
if (st->irq < 0) {
intio = ADXL345_INT2;
st->irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT2");
if (st->irq < 0)
intio = ADXL345_INT_NONE;
}
intio = adxl345_get_int_line(dev, &irq);
if (intio != ADXL345_INT_NONE) {
/*
* Any bits set to 0 in the INT map register send their respective
* interrupts to the INT1 pin, whereas bits set to 1 send their respective
* interrupts to the INT2 pin. The intio shall convert this accordingly.
* In the INT map register, bits set to 0 route their
* corresponding interrupts to the INT1 pin, while bits set to 1
* route them to the INT2 pin. The intio should handle this
* mapping accordingly.
*/
regval = intio ? 0xff : 0;
ret = regmap_write(st->regmap, ADXL345_REG_INT_MAP, regval);
ret = regmap_assign_bits(st->regmap, ADXL345_REG_INT_MAP,
U8_MAX, intio);
if (ret)
return ret;
@@ -1073,7 +1235,7 @@ int adxl345_core_probe(struct device *dev, struct regmap *regmap,
if (ret)
return ret;
ret = devm_request_threaded_irq(dev, st->irq, NULL,
ret = devm_request_threaded_irq(dev, irq, NULL,
&adxl345_irq_handler,
IRQF_SHARED | IRQF_ONESHOT,
indio_dev->name, indio_dev);

View File

@@ -600,10 +600,9 @@ static int adxl372_get_status(struct adxl372_state *st,
static void adxl372_arrange_axis_data(struct adxl372_state *st, __be16 *sample)
{
__be16 axis_sample[3];
__be16 axis_sample[3] = { };
int i = 0;
memset(axis_sample, 0, 3 * sizeof(__be16));
if (ADXL372_X_AXIS_EN(st->fifo_axis_mask))
axis_sample[i++] = sample[0];
if (ADXL372_Y_AXIS_EN(st->fifo_axis_mask))

View File

@@ -29,9 +29,6 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#define BMA180_DRV_NAME "bma180"
#define BMA180_IRQ_NAME "bma180_event"
enum chip_ids {
BMA023,
BMA150,

View File

@@ -25,9 +25,6 @@
#include "bmc150-accel.h"
#define BMC150_ACCEL_DRV_NAME "bmc150_accel"
#define BMC150_ACCEL_IRQ_NAME "bmc150_accel_event"
#define BMC150_ACCEL_REG_CHIP_ID 0x00
#define BMC150_ACCEL_REG_INT_STATUS_2 0x0B
@@ -1706,7 +1703,7 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq,
bmc150_accel_irq_handler,
bmc150_accel_irq_thread_handler,
IRQF_TRIGGER_RISING,
BMC150_ACCEL_IRQ_NAME,
"bmc150_accel_event",
indio_dev);
if (ret)
goto err_buffer_cleanup;

View File

@@ -5,27 +5,37 @@
* ROHM/KIONIX accelerometer driver
*/
#include <linux/array_size.h>
#include <linux/bitmap.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/interrupt.h>
#include <linux/math64.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <linux/sysfs.h>
#include <linux/time64.h>
#include <linux/types.h>
#include <linux/units.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <asm/byteorder.h>
#include "kionix-kx022a.h"
/*

View File

@@ -26,9 +26,6 @@
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/accel/kxcjk_1013.h>
#define KXCJK1013_DRV_NAME "kxcjk1013"
#define KXCJK1013_IRQ_NAME "kxcjk1013_event"
#define KXTF9_REG_HP_XOUT_L 0x00
#define KXTF9_REG_HP_XOUT_H 0x01
#define KXTF9_REG_HP_YOUT_L 0x02
@@ -1464,7 +1461,7 @@ static int kxcjk1013_probe(struct i2c_client *client)
kxcjk1013_data_rdy_trig_poll,
kxcjk1013_event_handler,
IRQF_TRIGGER_RISING,
KXCJK1013_IRQ_NAME,
"kxcjk1013_event",
indio_dev);
if (ret)
goto err_poweroff;
@@ -1674,7 +1671,7 @@ MODULE_DEVICE_TABLE(acpi, kx_acpi_match);
static struct i2c_driver kxcjk1013_driver = {
.driver = {
.name = KXCJK1013_DRV_NAME,
.name = "kxcjk1013",
.acpi_match_table = kx_acpi_match,
.of_match_table = kxcjk1013_of_match,
.pm = pm_ptr(&kxcjk1013_pm_ops),

View File

@@ -17,8 +17,6 @@
#include <linux/pm_runtime.h>
#include "mma9551_core.h"
#define MMA9551_DRV_NAME "mma9551"
#define MMA9551_IRQ_NAME "mma9551_event"
#define MMA9551_GPIO_COUNT 4
/* Tilt application (inclination in IIO terms). */
@@ -422,7 +420,7 @@ static int mma9551_gpio_probe(struct iio_dev *indio_dev)
ret = devm_request_threaded_irq(dev, data->irqs[i],
NULL, mma9551_event_handler,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
MMA9551_IRQ_NAME, indio_dev);
"mma9551_event", indio_dev);
if (ret < 0) {
dev_err(dev, "request irq %d failed\n", data->irqs[i]);
return ret;
@@ -592,7 +590,7 @@ MODULE_DEVICE_TABLE(i2c, mma9551_id);
static struct i2c_driver mma9551_driver = {
.driver = {
.name = MMA9551_DRV_NAME,
.name = "mma9551",
.acpi_match_table = mma9551_acpi_match,
.pm = pm_ptr(&mma9551_pm_ops),
},

View File

@@ -15,9 +15,6 @@
#include <linux/pm_runtime.h>
#include "mma9551_core.h"
#define MMA9553_DRV_NAME "mma9553"
#define MMA9553_IRQ_NAME "mma9553_event"
/* Pedometer configuration registers (R/W) */
#define MMA9553_REG_CONF_SLEEPMIN 0x00
#define MMA9553_REG_CONF_SLEEPMAX 0x02
@@ -100,7 +97,7 @@ enum activity_level {
ACTIVITY_RUNNING,
};
static struct mma9553_event_info {
static const struct mma9553_event_info {
enum iio_chan_type type;
enum iio_modifier mod;
enum iio_event_direction dir;
@@ -155,7 +152,7 @@ static struct mma9553_event_info {
#define MMA9553_EVENTS_INFO_SIZE ARRAY_SIZE(mma9553_events_info)
struct mma9553_event {
struct mma9553_event_info *info;
const struct mma9553_event_info *info;
bool enabled;
};
@@ -1102,7 +1099,7 @@ static int mma9553_probe(struct i2c_client *client)
mma9553_irq_handler,
mma9553_event_handler,
IRQF_TRIGGER_RISING,
MMA9553_IRQ_NAME, indio_dev);
"mma9553_event", indio_dev);
if (ret < 0) {
dev_err(&client->dev, "request irq %d failed\n",
client->irq);
@@ -1230,7 +1227,7 @@ MODULE_DEVICE_TABLE(i2c, mma9553_id);
static struct i2c_driver mma9553_driver = {
.driver = {
.name = MMA9553_DRV_NAME,
.name = "mma9553",
.acpi_match_table = mma9553_acpi_match,
.pm = pm_ptr(&mma9553_pm_ops),
},

View File

@@ -897,9 +897,7 @@ static irqreturn_t msa311_buffer_thread(int irq, void *p)
struct {
__le16 channels[MSA311_SI_Z + 1];
aligned_s64 ts;
} buf;
memset(&buf, 0, sizeof(buf));
} buf = { };
mutex_lock(&msa311->lock);

View File

@@ -19,8 +19,6 @@
#include <linux/iio/trigger_consumer.h>
#define MXC4005_DRV_NAME "mxc4005"
#define MXC4005_IRQ_NAME "mxc4005_event"
#define MXC4005_REGMAP_NAME "mxc4005_regmap"
#define MXC4005_REG_XOUT_UPPER 0x03
#define MXC4005_REG_XOUT_LOWER 0x04
@@ -138,7 +136,7 @@ static bool mxc4005_is_writeable_reg(struct device *dev, unsigned int reg)
}
static const struct regmap_config mxc4005_regmap_config = {
.name = MXC4005_REGMAP_NAME,
.name = "mxc4005_regmap",
.reg_bits = 8,
.val_bits = 8,
@@ -493,7 +491,7 @@ static int mxc4005_probe(struct i2c_client *client)
NULL,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
MXC4005_IRQ_NAME,
"mxc4005_event",
data->dready_trig);
if (ret) {
dev_err(&client->dev,

View File

@@ -17,7 +17,6 @@
#include <linux/iio/sysfs.h>
#define MXC6255_DRV_NAME "mxc6255"
#define MXC6255_REGMAP_NAME "mxc6255_regmap"
#define MXC6255_REG_XOUT 0x00
#define MXC6255_REG_YOUT 0x01
@@ -105,7 +104,7 @@ static bool mxc6255_is_readable_reg(struct device *dev, unsigned int reg)
}
static const struct regmap_config mxc6255_regmap_config = {
.name = MXC6255_REGMAP_NAME,
.name = "mxc6255_regmap",
.reg_bits = 8,
.val_bits = 8,

View File

@@ -369,23 +369,20 @@ static int sca3000_write_ctrl_reg(struct sca3000_state *st,
ret = sca3000_reg_lock_on(st);
if (ret < 0)
goto error_ret;
return ret;
if (ret) {
ret = __sca3000_unlock_reg_lock(st);
if (ret)
goto error_ret;
return ret;
}
/* Set the control select register */
ret = sca3000_write_reg(st, SCA3000_REG_CTRL_SEL_ADDR, sel);
if (ret)
goto error_ret;
return ret;
/* Write the actual value into the register */
ret = sca3000_write_reg(st, SCA3000_REG_CTRL_DATA_ADDR, val);
error_ret:
return ret;
return sca3000_write_reg(st, SCA3000_REG_CTRL_DATA_ADDR, val);
}
/**
@@ -402,22 +399,20 @@ static int sca3000_read_ctrl_reg(struct sca3000_state *st,
ret = sca3000_reg_lock_on(st);
if (ret < 0)
goto error_ret;
return ret;
if (ret) {
ret = __sca3000_unlock_reg_lock(st);
if (ret)
goto error_ret;
return ret;
}
/* Set the control select register */
ret = sca3000_write_reg(st, SCA3000_REG_CTRL_SEL_ADDR, ctrl_reg);
if (ret)
goto error_ret;
return ret;
ret = sca3000_read_data_short(st, SCA3000_REG_CTRL_DATA_ADDR, 1);
if (ret)
goto error_ret;
return ret;
return st->rx[0];
error_ret:
return ret;
}
/**
@@ -577,7 +572,8 @@ static inline int __sca3000_get_base_freq(struct sca3000_state *st,
ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
if (ret)
goto error_ret;
return ret;
switch (SCA3000_REG_MODE_MODE_MASK & st->rx[0]) {
case SCA3000_REG_MODE_MEAS_MODE_NORMAL:
*base_freq = info->measurement_mode_freq;
@@ -591,7 +587,6 @@ static inline int __sca3000_get_base_freq(struct sca3000_state *st,
default:
ret = -EINVAL;
}
error_ret:
return ret;
}
@@ -834,7 +829,7 @@ static ssize_t sca3000_read_av_freq(struct device *dev,
val = st->rx[0];
mutex_unlock(&st->lock);
if (ret)
goto error_ret;
return ret;
switch (val & SCA3000_REG_MODE_MODE_MASK) {
case SCA3000_REG_MODE_MEAS_MODE_NORMAL:
@@ -857,8 +852,6 @@ static ssize_t sca3000_read_av_freq(struct device *dev,
break;
}
return len;
error_ret:
return ret;
}
/*

View File

@@ -20,8 +20,6 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#define SCA3300_ALIAS "sca3300"
#define SCA3300_CRC8_POLYNOMIAL 0x1d
/* Device mode register */
@@ -674,7 +672,7 @@ MODULE_DEVICE_TABLE(spi, sca3300_ids);
static struct spi_driver sca3300_driver = {
.driver = {
.name = SCA3300_ALIAS,
.name = "sca3300",
.of_match_table = sca3300_dt_ids,
},
.probe = sca3300_probe,

View File

@@ -46,7 +46,6 @@
#define STK8312_ALL_CHANNEL_SIZE 3
#define STK8312_DRIVER_NAME "stk8312"
#define STK8312_IRQ_NAME "stk8312_event"
/*
* The accelerometer has two measurement ranges:
@@ -543,7 +542,7 @@ static int stk8312_probe(struct i2c_client *client)
NULL,
IRQF_TRIGGER_RISING |
IRQF_ONESHOT,
STK8312_IRQ_NAME,
"stk8312_event",
indio_dev);
if (ret < 0) {
dev_err(&client->dev, "request irq %d failed\n",

View File

@@ -42,7 +42,6 @@
#define STK8BA50_ALL_CHANNEL_SIZE 6
#define STK8BA50_DRIVER_NAME "stk8ba50"
#define STK8BA50_IRQ_NAME "stk8ba50_event"
#define STK8BA50_SCALE_AVAIL "0.0384 0.0767 0.1534 0.3069"
@@ -436,7 +435,7 @@ static int stk8ba50_probe(struct i2c_client *client)
NULL,
IRQF_TRIGGER_RISING |
IRQF_ONESHOT,
STK8BA50_IRQ_NAME,
"stk8ba50_event",
indio_dev);
if (ret < 0) {
dev_err(&client->dev, "request irq %d failed\n",

View File

@@ -22,7 +22,9 @@ config AB8500_GPADC
config AD_SIGMA_DELTA
tristate
select IIO_BUFFER
select IIO_BUFFER_DMAENGINE
select IIO_TRIGGERED_BUFFER
select SPI_OFFLOAD
config AD4000
tristate "Analog Devices AD4000 ADC Driver"
@@ -55,6 +57,20 @@ config AD4030
To compile this driver as a module, choose M here: the module will be
called ad4030.
config AD4080
tristate "Analog Devices AD4080 high speed ADC"
depends on SPI
select REGMAP_SPI
select IIO_BACKEND
help
Say yes here to build support for Analog Devices AD4080
high speed, low noise, low distortion, 20-bit, Easy Drive,
successive approximation register (SAR) analog-to-digital
converter (ADC). Supports iio_backended devices for AD4080.
To compile this driver as a module, choose M here: the module will be
called ad4080.
config AD4130
tristate "Analog Device AD4130 ADC Driver"
depends on SPI
@@ -70,6 +86,22 @@ config AD4130
To compile this driver as a module, choose M here: the module will be
called ad4130.
config AD4170_4
tristate "Analog Device AD4170-4 ADC Driver"
depends on SPI
select REGMAP_SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
depends on COMMON_CLK
depends on GPIOLIB
help
Say yes here to build support for Analog Devices AD4170-4 SPI analog
to digital converters (ADC).
To compile this driver as a module, choose M here: the module will be
called ad4170-4.
config AD4695
tristate "Analog Device AD4695 ADC Driver"
depends on SPI
@@ -252,6 +284,16 @@ config AD7380
To compile this driver as a module, choose M here: the module will be
called ad7380.
config AD7405
tristate "Analog Device AD7405 ADC Driver"
depends on IIO_BACKEND
help
Say yes here to build support for Analog Devices AD7405, ADUM7701,
ADUM7702, ADUM7703 analog to digital converters (ADC).
To compile this driver as a module, choose M here: the module will be
called ad7405.
config AD7476
tristate "Analog Devices AD7476 1-channel ADCs driver and other similar devices from AD and TI"
depends on SPI
@@ -330,6 +372,7 @@ config AD7766
config AD7768_1
tristate "Analog Devices AD7768-1 ADC driver"
depends on SPI
select REGULATOR
select REGMAP_SPI
select IIO_BUFFER
select IIO_TRIGGER

View File

@@ -10,7 +10,9 @@ obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
obj-$(CONFIG_AD4000) += ad4000.o
obj-$(CONFIG_AD4030) += ad4030.o
obj-$(CONFIG_AD4080) += ad4080.o
obj-$(CONFIG_AD4130) += ad4130.o
obj-$(CONFIG_AD4170_4) += ad4170-4.o
obj-$(CONFIG_AD4695) += ad4695.o
obj-$(CONFIG_AD4851) += ad4851.o
obj-$(CONFIG_AD7091R) += ad7091r-base.o
@@ -26,6 +28,7 @@ obj-$(CONFIG_AD7291) += ad7291.o
obj-$(CONFIG_AD7292) += ad7292.o
obj-$(CONFIG_AD7298) += ad7298.o
obj-$(CONFIG_AD7380) += ad7380.o
obj-$(CONFIG_AD7405) += ad7405.o
obj-$(CONFIG_AD7476) += ad7476.o
obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o
obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o

View File

@@ -554,7 +554,7 @@ static void ad4000_fill_scale_tbl(struct ad4000_state *st,
val = mult_frac(st->vref_mv, MICRO, st->gain_milli);
/* Would multiply by NANO here but we multiplied by extra MILLI */
tmp2 = shift_right((u64)val * MICRO, scale_bits);
tmp2 = (u64)val * MICRO >> scale_bits;
tmp0 = div_s64_rem(tmp2, NANO, &tmp1);
/* Store scale for when span compression is disabled */

619
drivers/iio/adc/ad4080.c Normal file
View File

@@ -0,0 +1,619 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Analog Devices AD4080 SPI ADC driver
*
* Copyright 2025 Analog Devices Inc.
*/
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/iio/backend.h>
#include <linux/iio/iio.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include <linux/unaligned.h>
#include <linux/units.h>
/* Register Definition */
#define AD4080_REG_INTERFACE_CONFIG_A 0x00
#define AD4080_REG_INTERFACE_CONFIG_B 0x01
#define AD4080_REG_DEVICE_CONFIG 0x02
#define AD4080_REG_CHIP_TYPE 0x03
#define AD4080_REG_PRODUCT_ID_L 0x04
#define AD4080_REG_PRODUCT_ID_H 0x05
#define AD4080_REG_CHIP_GRADE 0x06
#define AD4080_REG_SCRATCH_PAD 0x0A
#define AD4080_REG_SPI_REVISION 0x0B
#define AD4080_REG_VENDOR_L 0x0C
#define AD4080_REG_VENDOR_H 0x0D
#define AD4080_REG_STREAM_MODE 0x0E
#define AD4080_REG_TRANSFER_CONFIG 0x0F
#define AD4080_REG_INTERFACE_CONFIG_C 0x10
#define AD4080_REG_INTERFACE_STATUS_A 0x11
#define AD4080_REG_DEVICE_STATUS 0x14
#define AD4080_REG_ADC_DATA_INTF_CONFIG_A 0x15
#define AD4080_REG_ADC_DATA_INTF_CONFIG_B 0x16
#define AD4080_REG_ADC_DATA_INTF_CONFIG_C 0x17
#define AD4080_REG_PWR_CTRL 0x18
#define AD4080_REG_GPIO_CONFIG_A 0x19
#define AD4080_REG_GPIO_CONFIG_B 0x1A
#define AD4080_REG_GPIO_CONFIG_C 0x1B
#define AD4080_REG_GENERAL_CONFIG 0x1C
#define AD4080_REG_FIFO_WATERMARK_LSB 0x1D
#define AD4080_REG_FIFO_WATERMARK_MSB 0x1E
#define AD4080_REG_EVENT_HYSTERESIS_LSB 0x1F
#define AD4080_REG_EVENT_HYSTERESIS_MSB 0x20
#define AD4080_REG_EVENT_DETECTION_HI_LSB 0x21
#define AD4080_REG_EVENT_DETECTION_HI_MSB 0x22
#define AD4080_REG_EVENT_DETECTION_LO_LSB 0x23
#define AD4080_REG_EVENT_DETECTION_LO_MSB 0x24
#define AD4080_REG_OFFSET_LSB 0x25
#define AD4080_REG_OFFSET_MSB 0x26
#define AD4080_REG_GAIN_LSB 0x27
#define AD4080_REG_GAIN_MSB 0x28
#define AD4080_REG_FILTER_CONFIG 0x29
/* AD4080_REG_INTERFACE_CONFIG_A Bit Definition */
#define AD4080_INTERFACE_CONFIG_A_SW_RESET (BIT(7) | BIT(0))
#define AD4080_INTERFACE_CONFIG_A_ADDR_ASC BIT(5)
#define AD4080_INTERFACE_CONFIG_A_SDO_ENABLE BIT(4)
/* AD4080_REG_INTERFACE_CONFIG_B Bit Definition */
#define AD4080_INTERFACE_CONFIG_B_SINGLE_INST BIT(7)
#define AD4080_INTERFACE_CONFIG_B_SHORT_INST BIT(3)
/* AD4080_REG_DEVICE_CONFIG Bit Definition */
#define AD4080_DEVICE_CONFIG_OPERATING_MODES_MSK GENMASK(1, 0)
/* AD4080_REG_TRANSFER_CONFIG Bit Definition */
#define AD4080_TRANSFER_CONFIG_KEEP_STREAM_LENGTH_VAL BIT(2)
/* AD4080_REG_INTERFACE_CONFIG_C Bit Definition */
#define AD4080_INTERFACE_CONFIG_C_STRICT_REG_ACCESS BIT(5)
/* AD4080_REG_ADC_DATA_INTF_CONFIG_A Bit Definition */
#define AD4080_ADC_DATA_INTF_CONFIG_A_RESERVED_CONFIG_A BIT(6)
#define AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN BIT(4)
#define AD4080_ADC_DATA_INTF_CONFIG_A_SPI_LVDS_LANES BIT(2)
#define AD4080_ADC_DATA_INTF_CONFIG_A_DATA_INTF_MODE BIT(0)
/* AD4080_REG_ADC_DATA_INTF_CONFIG_B Bit Definition */
#define AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK GENMASK(7, 4)
#define AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_SELF_CLK_MODE BIT(3)
#define AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_EN BIT(0)
/* AD4080_REG_ADC_DATA_INTF_CONFIG_C Bit Definition */
#define AD4080_ADC_DATA_INTF_CONFIG_C_LVDS_VOD_MSK GENMASK(6, 4)
/* AD4080_REG_PWR_CTRL Bit Definition */
#define AD4080_PWR_CTRL_ANA_DIG_LDO_PD BIT(1)
#define AD4080_PWR_CTRL_INTF_LDO_PD BIT(0)
/* AD4080_REG_GPIO_CONFIG_A Bit Definition */
#define AD4080_GPIO_CONFIG_A_GPO_1_EN BIT(1)
#define AD4080_GPIO_CONFIG_A_GPO_0_EN BIT(0)
/* AD4080_REG_GPIO_CONFIG_B Bit Definition */
#define AD4080_GPIO_CONFIG_B_GPIO_1_SEL_MSK GENMASK(7, 4)
#define AD4080_GPIO_CONFIG_B_GPIO_0_SEL_MSK GENMASK(3, 0)
#define AD4080_GPIO_CONFIG_B_GPIO_SPI_SDO 0
#define AD4080_GPIO_CONFIG_B_GPIO_FIFO_FULL 1
#define AD4080_GPIO_CONFIG_B_GPIO_FIFO_READ_DONE 2
#define AD4080_GPIO_CONFIG_B_GPIO_FILTER_RES_RDY 3
#define AD4080_GPIO_CONFIG_B_GPIO_H_THRESH 4
#define AD4080_GPIO_CONFIG_B_GPIO_L_THRESH 5
#define AD4080_GPIO_CONFIG_B_GPIO_STATUS_ALERT 6
#define AD4080_GPIO_CONFIG_B_GPIO_GPIO_DATA 7
#define AD4080_GPIO_CONFIG_B_GPIO_FILTER_SYNC 8
#define AD4080_GPIO_CONFIG_B_GPIO_EXTERNAL_EVENT 9
/* AD4080_REG_FIFO_CONFIG Bit Definition */
#define AD4080_FIFO_CONFIG_FIFO_MODE_MSK GENMASK(1, 0)
/* AD4080_REG_FILTER_CONFIG Bit Definition */
#define AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK GENMASK(6, 3)
#define AD4080_FILTER_CONFIG_FILTER_SEL_MSK GENMASK(1, 0)
/* Miscellaneous Definitions */
#define AD4080_SPI_READ BIT(7)
#define AD4080_CHIP_ID GENMASK(2, 0)
#define AD4080_LVDS_CNV_CLK_CNT_MAX 7
#define AD4080_MAX_SAMP_FREQ 40000000
#define AD4080_MIN_SAMP_FREQ 1250000
enum ad4080_filter_type {
FILTER_NONE,
SINC_1,
SINC_5,
SINC_5_COMP
};
static const unsigned int ad4080_scale_table[][2] = {
{ 6000, 0 },
};
static const char *const ad4080_filter_type_iio_enum[] = {
[FILTER_NONE] = "none",
[SINC_1] = "sinc1",
[SINC_5] = "sinc5",
[SINC_5_COMP] = "sinc5+pf1",
};
static const int ad4080_dec_rate_avail[] = {
2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,
};
static const int ad4080_dec_rate_none[] = { 1 };
static const char * const ad4080_power_supplies[] = {
"vdd33", "vdd11", "vddldo", "iovdd", "vrefin",
};
struct ad4080_chip_info {
const char *name;
unsigned int product_id;
int num_scales;
const unsigned int (*scale_table)[2];
const struct iio_chan_spec *channels;
unsigned int num_channels;
};
struct ad4080_state {
struct regmap *regmap;
struct iio_backend *back;
const struct ad4080_chip_info *info;
/*
* Synchronize access to members the of driver state, and ensure
* atomicity of consecutive regmap operations.
*/
struct mutex lock;
unsigned int num_lanes;
unsigned int dec_rate;
unsigned long clk_rate;
enum ad4080_filter_type filter_type;
bool lvds_cnv_en;
};
static const struct regmap_config ad4080_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
.read_flag_mask = BIT(7),
.max_register = 0x29,
};
static int ad4080_reg_access(struct iio_dev *indio_dev, unsigned int reg,
unsigned int writeval, unsigned int *readval)
{
struct ad4080_state *st = iio_priv(indio_dev);
if (readval)
return regmap_read(st->regmap, reg, readval);
return regmap_write(st->regmap, reg, writeval);
}
static int ad4080_get_scale(struct ad4080_state *st, int *val, int *val2)
{
unsigned int tmp;
tmp = (st->info->scale_table[0][0] * 1000000ULL) >>
st->info->channels[0].scan_type.realbits;
*val = tmp / 1000000;
*val2 = tmp % 1000000;
return IIO_VAL_INT_PLUS_NANO;
}
static unsigned int ad4080_get_dec_rate(struct iio_dev *dev,
const struct iio_chan_spec *chan)
{
struct ad4080_state *st = iio_priv(dev);
int ret;
unsigned int data;
ret = regmap_read(st->regmap, AD4080_REG_FILTER_CONFIG, &data);
if (ret)
return ret;
return 1 << (FIELD_GET(AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK, data) + 1);
}
static int ad4080_set_dec_rate(struct iio_dev *dev,
const struct iio_chan_spec *chan,
unsigned int mode)
{
struct ad4080_state *st = iio_priv(dev);
guard(mutex)(&st->lock);
if ((st->filter_type >= SINC_5 && mode >= 512) || mode < 2)
return -EINVAL;
return regmap_update_bits(st->regmap, AD4080_REG_FILTER_CONFIG,
AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK,
FIELD_PREP(AD4080_FILTER_CONFIG_SINC_DEC_RATE_MSK,
(ilog2(mode) - 1)));
}
static int ad4080_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long m)
{
struct ad4080_state *st = iio_priv(indio_dev);
int dec_rate;
switch (m) {
case IIO_CHAN_INFO_SCALE:
return ad4080_get_scale(st, val, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
dec_rate = ad4080_get_dec_rate(indio_dev, chan);
if (dec_rate < 0)
return dec_rate;
if (st->filter_type == SINC_5_COMP)
dec_rate *= 2;
if (st->filter_type)
*val = DIV_ROUND_CLOSEST(st->clk_rate, dec_rate);
else
*val = st->clk_rate;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
if (st->filter_type == FILTER_NONE) {
*val = 1;
} else {
*val = ad4080_get_dec_rate(indio_dev, chan);
if (*val < 0)
return *val;
}
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int ad4080_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct ad4080_state *st = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
if (st->filter_type == FILTER_NONE && val > 1)
return -EINVAL;
return ad4080_set_dec_rate(indio_dev, chan, val);
default:
return -EINVAL;
}
}
static int ad4080_lvds_sync_write(struct ad4080_state *st)
{
struct device *dev = regmap_get_device(st->regmap);
int ret;
ret = regmap_set_bits(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A,
AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN);
if (ret)
return ret;
ret = iio_backend_interface_data_align(st->back, 10000);
if (ret)
return dev_err_probe(dev, ret,
"Data alignment process failed\n");
dev_dbg(dev, "Success: Pattern correct and Locked!\n");
return regmap_clear_bits(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A,
AD4080_ADC_DATA_INTF_CONFIG_A_INTF_CHK_EN);
}
static int ad4080_get_filter_type(struct iio_dev *dev,
const struct iio_chan_spec *chan)
{
struct ad4080_state *st = iio_priv(dev);
unsigned int data;
int ret;
ret = regmap_read(st->regmap, AD4080_REG_FILTER_CONFIG, &data);
if (ret)
return ret;
return FIELD_GET(AD4080_FILTER_CONFIG_FILTER_SEL_MSK, data);
}
static int ad4080_set_filter_type(struct iio_dev *dev,
const struct iio_chan_spec *chan,
unsigned int mode)
{
struct ad4080_state *st = iio_priv(dev);
int dec_rate;
int ret;
guard(mutex)(&st->lock);
dec_rate = ad4080_get_dec_rate(dev, chan);
if (dec_rate < 0)
return dec_rate;
if (mode >= SINC_5 && dec_rate >= 512)
return -EINVAL;
ret = iio_backend_filter_type_set(st->back, mode);
if (ret)
return ret;
ret = regmap_update_bits(st->regmap, AD4080_REG_FILTER_CONFIG,
AD4080_FILTER_CONFIG_FILTER_SEL_MSK,
FIELD_PREP(AD4080_FILTER_CONFIG_FILTER_SEL_MSK,
mode));
if (ret)
return ret;
st->filter_type = mode;
return 0;
}
static int ad4080_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
struct ad4080_state *st = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
switch (st->filter_type) {
case FILTER_NONE:
*vals = ad4080_dec_rate_none;
*length = ARRAY_SIZE(ad4080_dec_rate_none);
break;
default:
*vals = ad4080_dec_rate_avail;
*length = st->filter_type >= SINC_5 ?
(ARRAY_SIZE(ad4080_dec_rate_avail) - 2) :
ARRAY_SIZE(ad4080_dec_rate_avail);
break;
}
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static const struct iio_info ad4080_iio_info = {
.debugfs_reg_access = ad4080_reg_access,
.read_raw = ad4080_read_raw,
.write_raw = ad4080_write_raw,
.read_avail = ad4080_read_avail,
};
static const struct iio_enum ad4080_filter_type_enum = {
.items = ad4080_filter_type_iio_enum,
.num_items = ARRAY_SIZE(ad4080_filter_type_iio_enum),
.set = ad4080_set_filter_type,
.get = ad4080_get_filter_type,
};
static struct iio_chan_spec_ext_info ad4080_ext_info[] = {
IIO_ENUM("filter_type", IIO_SHARED_BY_ALL, &ad4080_filter_type_enum),
IIO_ENUM_AVAILABLE("filter_type", IIO_SHARED_BY_ALL,
&ad4080_filter_type_enum),
{ }
};
static const struct iio_chan_spec ad4080_channel = {
.type = IIO_VOLTAGE,
.indexed = 1,
.channel = 0,
.info_mask_separate = BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.info_mask_shared_by_all_available =
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.ext_info = ad4080_ext_info,
.scan_index = 0,
.scan_type = {
.sign = 's',
.realbits = 20,
.storagebits = 32,
},
};
static const struct ad4080_chip_info ad4080_chip_info = {
.name = "ad4080",
.product_id = AD4080_CHIP_ID,
.scale_table = ad4080_scale_table,
.num_scales = ARRAY_SIZE(ad4080_scale_table),
.num_channels = 1,
.channels = &ad4080_channel,
};
static int ad4080_setup(struct iio_dev *indio_dev)
{
struct ad4080_state *st = iio_priv(indio_dev);
struct device *dev = regmap_get_device(st->regmap);
unsigned int id;
int ret;
ret = regmap_write(st->regmap, AD4080_REG_INTERFACE_CONFIG_A,
AD4080_INTERFACE_CONFIG_A_SW_RESET);
if (ret)
return ret;
ret = regmap_write(st->regmap, AD4080_REG_INTERFACE_CONFIG_A,
AD4080_INTERFACE_CONFIG_A_SDO_ENABLE);
if (ret)
return ret;
ret = regmap_read(st->regmap, AD4080_REG_CHIP_TYPE, &id);
if (ret)
return ret;
if (id != AD4080_CHIP_ID)
dev_info(dev, "Unrecognized CHIP_ID 0x%X\n", id);
ret = regmap_set_bits(st->regmap, AD4080_REG_GPIO_CONFIG_A,
AD4080_GPIO_CONFIG_A_GPO_1_EN);
if (ret)
return ret;
ret = regmap_write(st->regmap, AD4080_REG_GPIO_CONFIG_B,
FIELD_PREP(AD4080_GPIO_CONFIG_B_GPIO_1_SEL_MSK,
AD4080_GPIO_CONFIG_B_GPIO_FILTER_RES_RDY));
if (ret)
return ret;
ret = iio_backend_num_lanes_set(st->back, st->num_lanes);
if (ret)
return ret;
if (!st->lvds_cnv_en)
return 0;
/* Set maximum LVDS Data Transfer Latency */
ret = regmap_update_bits(st->regmap,
AD4080_REG_ADC_DATA_INTF_CONFIG_B,
AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK,
FIELD_PREP(AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_CLK_CNT_MSK,
AD4080_LVDS_CNV_CLK_CNT_MAX));
if (ret)
return ret;
if (st->num_lanes > 1) {
ret = regmap_set_bits(st->regmap, AD4080_REG_ADC_DATA_INTF_CONFIG_A,
AD4080_ADC_DATA_INTF_CONFIG_A_SPI_LVDS_LANES);
if (ret)
return ret;
}
ret = regmap_set_bits(st->regmap,
AD4080_REG_ADC_DATA_INTF_CONFIG_B,
AD4080_ADC_DATA_INTF_CONFIG_B_LVDS_CNV_EN);
if (ret)
return ret;
return ad4080_lvds_sync_write(st);
}
static int ad4080_properties_parse(struct ad4080_state *st)
{
struct device *dev = regmap_get_device(st->regmap);
st->lvds_cnv_en = device_property_read_bool(dev, "adi,lvds-cnv-enable");
st->num_lanes = 1;
device_property_read_u32(dev, "adi,num-lanes", &st->num_lanes);
if (!st->num_lanes || st->num_lanes > 2)
return dev_err_probe(dev, -EINVAL,
"Invalid 'adi,num-lanes' value: %u",
st->num_lanes);
return 0;
}
static int ad4080_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct device *dev = &spi->dev;
struct ad4080_state *st;
struct clk *clk;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
ret = devm_regulator_bulk_get_enable(dev,
ARRAY_SIZE(ad4080_power_supplies),
ad4080_power_supplies);
if (ret)
return dev_err_probe(dev, ret,
"failed to get and enable supplies\n");
st->regmap = devm_regmap_init_spi(spi, &ad4080_regmap_config);
if (IS_ERR(st->regmap))
return PTR_ERR(st->regmap);
st->info = spi_get_device_match_data(spi);
if (!st->info)
return -ENODEV;
ret = devm_mutex_init(dev, &st->lock);
if (ret)
return ret;
indio_dev->name = st->info->name;
indio_dev->channels = st->info->channels;
indio_dev->num_channels = st->info->num_channels;
indio_dev->info = &ad4080_iio_info;
ret = ad4080_properties_parse(st);
if (ret)
return ret;
clk = devm_clk_get_enabled(&spi->dev, "cnv");
if (IS_ERR(clk))
return PTR_ERR(clk);
st->clk_rate = clk_get_rate(clk);
st->back = devm_iio_backend_get(dev, NULL);
if (IS_ERR(st->back))
return PTR_ERR(st->back);
ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
if (ret)
return ret;
ret = devm_iio_backend_enable(dev, st->back);
if (ret)
return ret;
ret = ad4080_setup(indio_dev);
if (ret)
return ret;
return devm_iio_device_register(&spi->dev, indio_dev);
}
static const struct spi_device_id ad4080_id[] = {
{ "ad4080", (kernel_ulong_t)&ad4080_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4080_id);
static const struct of_device_id ad4080_of_match[] = {
{ .compatible = "adi,ad4080", &ad4080_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, ad4080_of_match);
static struct spi_driver ad4080_driver = {
.driver = {
.name = "ad4080",
.of_match_table = ad4080_of_match,
},
.probe = ad4080_probe,
.id_table = ad4080_id,
};
module_spi_driver(ad4080_driver);
MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com");
MODULE_DESCRIPTION("Analog Devices AD4080");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("IIO_BACKEND");

3027
drivers/iio/adc/ad4170-4.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -294,7 +294,6 @@ static int ad4851_scale_fill(struct iio_dev *indio_dev)
}
static int ad4851_set_oversampling_ratio(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int osr)
{
struct ad4851_state *st = iio_priv(indio_dev);
@@ -321,7 +320,8 @@ static int ad4851_set_oversampling_ratio(struct iio_dev *indio_dev,
return ret;
}
ret = iio_backend_oversampling_ratio_set(st->back, osr);
/* Channel is ignored by the backend being used here */
ret = iio_backend_oversampling_ratio_set(st->back, 0, osr);
if (ret)
return ret;
@@ -444,10 +444,12 @@ static int ad4851_setup(struct ad4851_state *st)
if (ret)
return ret;
ret = regmap_write(st->regmap, AD4851_REG_INTERFACE_CONFIG_A,
AD4851_SDO_ENABLE);
if (ret)
return ret;
if (!(st->spi->mode & SPI_3WIRE)) {
ret = regmap_write(st->regmap, AD4851_REG_INTERFACE_CONFIG_A,
AD4851_SDO_ENABLE);
if (ret)
return ret;
}
ret = regmap_read(st->regmap, AD4851_REG_PRODUCT_ID_L, &product_id);
if (ret)
@@ -831,7 +833,7 @@ static int ad4851_write_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_CALIBBIAS:
return ad4851_set_calibbias(st, chan->channel, val);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
return ad4851_set_oversampling_ratio(indio_dev, chan, val);
return ad4851_set_oversampling_ratio(indio_dev, val);
default:
return -EINVAL;
}

View File

@@ -92,7 +92,7 @@ static void ad7091r5_regmap_init(struct ad7091r_state *st,
st->map = devm_regmap_init_i2c(i2c, regmap_conf);
}
static struct ad7091r_init_info ad7091r5_init_info = {
static const struct ad7091r_init_info ad7091r5_init_info = {
.info_irq = &ad7091r5_chip_info_irq,
.info_no_irq = &ad7091r5_chip_info_noirq,
.regmap_config = &ad7091r_regmap_config,

View File

@@ -206,14 +206,14 @@ static int ad7091r8_gpio_setup(struct ad7091r_state *st)
return 0;
}
static struct ad7091r_init_info ad7091r2_init_info = {
static const struct ad7091r_init_info ad7091r2_init_info = {
.info_no_irq = &ad7091r8_infos[AD7091R2_INFO],
.regmap_config = &ad7091r2_reg_conf,
.init_adc_regmap = &ad7091r8_regmap_init,
.setup = &ad7091r8_gpio_setup
};
static struct ad7091r_init_info ad7091r4_init_info = {
static const struct ad7091r_init_info ad7091r4_init_info = {
.info_no_irq = &ad7091r8_infos[AD7091R4_INFO],
.info_irq = &ad7091r8_infos[AD7091R4_INFO_IRQ],
.regmap_config = &ad7091r4_reg_conf,
@@ -221,7 +221,7 @@ static struct ad7091r_init_info ad7091r4_init_info = {
.setup = &ad7091r8_gpio_setup
};
static struct ad7091r_init_info ad7091r8_init_info = {
static const struct ad7091r_init_info ad7091r8_init_info = {
.info_no_irq = &ad7091r8_infos[AD7091R8_INFO],
.info_irq = &ad7091r8_infos[AD7091R8_INFO_IRQ],
.regmap_config = &ad7091r8_reg_conf,

View File

@@ -94,11 +94,6 @@
/* AD7124 input sources */
enum ad7124_ids {
ID_AD7124_4,
ID_AD7124_8,
};
enum ad7124_ref_sel {
AD7124_REFIN1,
AD7124_REFIN2,
@@ -193,17 +188,16 @@ struct ad7124_state {
DECLARE_KFIFO(live_cfgs_fifo, struct ad7124_channel_config *, AD7124_MAX_CONFIGS);
};
static struct ad7124_chip_info ad7124_chip_info_tbl[] = {
[ID_AD7124_4] = {
.name = "ad7124-4",
.chip_id = AD7124_ID_DEVICE_ID_AD7124_4,
.num_inputs = 8,
},
[ID_AD7124_8] = {
.name = "ad7124-8",
.chip_id = AD7124_ID_DEVICE_ID_AD7124_8,
.num_inputs = 16,
},
static const struct ad7124_chip_info ad7124_4_chip_info = {
.name = "ad7124-4",
.chip_id = AD7124_ID_DEVICE_ID_AD7124_4,
.num_inputs = 8,
};
static const struct ad7124_chip_info ad7124_8_chip_info = {
.name = "ad7124-8",
.chip_id = AD7124_ID_DEVICE_ID_AD7124_8,
.num_inputs = 16,
};
static int ad7124_find_closest_match(const int *array,
@@ -1341,17 +1335,15 @@ static int ad7124_probe(struct spi_device *spi)
}
static const struct of_device_id ad7124_of_match[] = {
{ .compatible = "adi,ad7124-4",
.data = &ad7124_chip_info_tbl[ID_AD7124_4], },
{ .compatible = "adi,ad7124-8",
.data = &ad7124_chip_info_tbl[ID_AD7124_8], },
{ .compatible = "adi,ad7124-4", .data = &ad7124_4_chip_info },
{ .compatible = "adi,ad7124-8", .data = &ad7124_8_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, ad7124_of_match);
static const struct spi_device_id ad71124_ids[] = {
{ "ad7124-4", (kernel_ulong_t)&ad7124_chip_info_tbl[ID_AD7124_4] },
{ "ad7124-8", (kernel_ulong_t)&ad7124_chip_info_tbl[ID_AD7124_8] },
{ "ad7124-4", (kernel_ulong_t)&ad7124_4_chip_info },
{ "ad7124-8", (kernel_ulong_t)&ad7124_8_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad71124_ids);

View File

@@ -228,7 +228,6 @@ struct ad7173_state {
struct ida cfg_slots_status;
unsigned long long config_usage_counter;
unsigned long long *config_cnts;
struct clk *ext_clk;
struct clk_hw int_clk_hw;
struct regmap *reg_gpiocon_regmap;
struct gpio_regmap *gpio_regmap;
@@ -319,7 +318,7 @@ static int ad7173_set_syscalib_mode(struct iio_dev *indio_dev,
{
struct ad7173_state *st = iio_priv(indio_dev);
st->channels[chan->channel].syscalib_mode = mode;
st->channels[chan->address].syscalib_mode = mode;
return 0;
}
@@ -329,7 +328,7 @@ static int ad7173_get_syscalib_mode(struct iio_dev *indio_dev,
{
struct ad7173_state *st = iio_priv(indio_dev);
return st->channels[chan->channel].syscalib_mode;
return st->channels[chan->address].syscalib_mode;
}
static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev,
@@ -348,7 +347,7 @@ static ssize_t ad7173_write_syscalib(struct iio_dev *indio_dev,
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
mode = st->channels[chan->channel].syscalib_mode;
mode = st->channels[chan->address].syscalib_mode;
if (sys_calib) {
if (mode == AD7173_SYSCALIB_ZERO_SCALE)
ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_SYS_ZERO,
@@ -392,13 +391,12 @@ static int ad7173_calibrate_all(struct ad7173_state *st, struct iio_dev *indio_d
if (indio_dev->channels[i].type != IIO_VOLTAGE)
continue;
ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_ZERO, st->channels[i].ain);
ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_ZERO, i);
if (ret < 0)
return ret;
if (st->info->has_internal_fs_calibration) {
ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_FULL,
st->channels[i].ain);
ret = ad_sd_calibrate(&st->sd, AD7173_MODE_CAL_INT_FULL, i);
if (ret < 0)
return ret;
}
@@ -772,10 +770,26 @@ static const struct ad_sigma_delta_info ad7173_sigma_delta_info_8_slots = {
.num_slots = 8,
};
static const struct ad_sigma_delta_info ad7173_sigma_delta_info_16_slots = {
.set_channel = ad7173_set_channel,
.append_status = ad7173_append_status,
.disable_all = ad7173_disable_all,
.disable_one = ad7173_disable_one,
.set_mode = ad7173_set_mode,
.has_registers = true,
.has_named_irqs = true,
.addr_shift = 0,
.read_mask = BIT(6),
.status_ch_mask = GENMASK(3, 0),
.data_reg = AD7173_REG_DATA,
.num_resetclks = 64,
.num_slots = 16,
};
static const struct ad7173_device_info ad4111_device_info = {
.name = "ad4111",
.id = AD4111_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in_div = 8,
.num_channels = 16,
.num_configs = 8,
@@ -797,7 +811,7 @@ static const struct ad7173_device_info ad4111_device_info = {
static const struct ad7173_device_info ad4112_device_info = {
.name = "ad4112",
.id = AD4112_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in_div = 8,
.num_channels = 16,
.num_configs = 8,
@@ -818,7 +832,7 @@ static const struct ad7173_device_info ad4112_device_info = {
static const struct ad7173_device_info ad4113_device_info = {
.name = "ad4113",
.id = AD4113_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in_div = 8,
.num_channels = 16,
.num_configs = 8,
@@ -837,7 +851,7 @@ static const struct ad7173_device_info ad4113_device_info = {
static const struct ad7173_device_info ad4114_device_info = {
.name = "ad4114",
.id = AD4114_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in_div = 16,
.num_channels = 16,
.num_configs = 8,
@@ -856,7 +870,7 @@ static const struct ad7173_device_info ad4114_device_info = {
static const struct ad7173_device_info ad4115_device_info = {
.name = "ad4115",
.id = AD4115_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in_div = 16,
.num_channels = 16,
.num_configs = 8,
@@ -875,7 +889,7 @@ static const struct ad7173_device_info ad4115_device_info = {
static const struct ad7173_device_info ad4116_device_info = {
.name = "ad4116",
.id = AD4116_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in_div = 11,
.num_channels = 16,
.num_configs = 8,
@@ -894,7 +908,7 @@ static const struct ad7173_device_info ad4116_device_info = {
static const struct ad7173_device_info ad7172_2_device_info = {
.name = "ad7172-2",
.id = AD7172_2_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.sd_info = &ad7173_sigma_delta_info_4_slots,
.num_voltage_in = 5,
.num_channels = 4,
.num_configs = 4,
@@ -927,7 +941,7 @@ static const struct ad7173_device_info ad7172_4_device_info = {
static const struct ad7173_device_info ad7173_8_device_info = {
.name = "ad7173-8",
.id = AD7173_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in = 17,
.num_channels = 16,
.num_configs = 8,
@@ -944,7 +958,7 @@ static const struct ad7173_device_info ad7173_8_device_info = {
static const struct ad7173_device_info ad7175_2_device_info = {
.name = "ad7175-2",
.id = AD7175_2_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.sd_info = &ad7173_sigma_delta_info_4_slots,
.num_voltage_in = 5,
.num_channels = 4,
.num_configs = 4,
@@ -961,7 +975,7 @@ static const struct ad7173_device_info ad7175_2_device_info = {
static const struct ad7173_device_info ad7175_8_device_info = {
.name = "ad7175-8",
.id = AD7175_8_ID,
.sd_info = &ad7173_sigma_delta_info_8_slots,
.sd_info = &ad7173_sigma_delta_info_16_slots,
.num_voltage_in = 17,
.num_channels = 16,
.num_configs = 8,
@@ -1344,11 +1358,6 @@ static void ad7173_disable_regulators(void *data)
regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators);
}
static void ad7173_clk_disable_unprepare(void *clk)
{
clk_disable_unprepare(clk);
}
static unsigned long ad7173_sel_clk(struct ad7173_state *st,
unsigned int clk_sel)
{
@@ -1580,6 +1589,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
chan_st_priv->cfg.bipolar = false;
chan_st_priv->cfg.input_buf = st->info->has_input_buf;
chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF;
chan_st_priv->cfg.odr = st->info->odr_start_value;
chan_st_priv->cfg.openwire_comp_chan = -1;
st->adc_mode |= AD7173_ADC_MODE_REF_EN;
if (st->info->data_reg_only_16bit)
@@ -1646,7 +1656,7 @@ static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev)
chan->scan_index = chan_index;
chan->channel = ain[0];
chan_st_priv->cfg.input_buf = st->info->has_input_buf;
chan_st_priv->cfg.odr = 0;
chan_st_priv->cfg.odr = st->info->odr_start_value;
chan_st_priv->cfg.openwire_comp_chan = -1;
chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar");
@@ -1718,22 +1728,14 @@ static int ad7173_fw_parse_device_config(struct iio_dev *indio_dev)
AD7173_ADC_MODE_CLOCKSEL_INT);
ad7173_register_clk_provider(indio_dev);
} else {
struct clk *clk;
st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK,
AD7173_ADC_MODE_CLOCKSEL_EXT + ret);
st->ext_clk = devm_clk_get(dev, ad7173_clk_sel[ret]);
if (IS_ERR(st->ext_clk))
return dev_err_probe(dev, PTR_ERR(st->ext_clk),
clk = devm_clk_get_enabled(dev, ad7173_clk_sel[ret]);
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk),
"Failed to get external clock\n");
ret = clk_prepare_enable(st->ext_clk);
if (ret)
return dev_err_probe(dev, ret,
"Failed to enable external clock\n");
ret = devm_add_action_or_reset(dev, ad7173_clk_disable_unprepare,
st->ext_clk);
if (ret)
return ret;
}
return ad7173_fw_parse_channel_config(indio_dev);
@@ -1765,7 +1767,9 @@ static int ad7173_probe(struct spi_device *spi)
indio_dev->info = &ad7173_info;
spi->mode = SPI_MODE_3;
spi_setup(spi);
ret = spi_setup(spi);
if (ret)
return ret;
ret = ad_sd_init(&st->sd, indio_dev, spi, st->info->sd_info);
if (ret)

View File

@@ -1165,7 +1165,6 @@ static int ad7380_init_offload_msg(struct ad7380_state *st,
struct spi_transfer *xfer = &st->offload_xfer;
struct device *dev = &st->spi->dev;
const struct iio_scan_type *scan_type;
int oversampling_ratio;
int ret;
scan_type = iio_get_current_scan_type(indio_dev,
@@ -1195,10 +1194,6 @@ static int ad7380_init_offload_msg(struct ad7380_state *st,
}
}
ret = ad7380_get_osr(st, &oversampling_ratio);
if (ret)
return ret;
xfer->bits_per_word = scan_type->realbits;
xfer->offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
xfer->len = AD7380_SPI_BYTES(scan_type) * st->chip_info->num_simult_channels;

253
drivers/iio/adc/ad7405.c Normal file
View File

@@ -0,0 +1,253 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Analog Devices AD7405 driver
*
* Copyright 2025 Analog Devices Inc.
*/
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/util_macros.h>
#include <linux/iio/backend.h>
#include <linux/iio/iio.h>
static const unsigned int ad7405_dec_rates_range[] = {
32, 1, 4096,
};
struct ad7405_chip_info {
const char *name;
const unsigned int full_scale_mv;
};
struct ad7405_state {
struct iio_backend *back;
const struct ad7405_chip_info *info;
unsigned int ref_frequency;
unsigned int dec_rate;
};
static int ad7405_set_dec_rate(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int dec_rate)
{
struct ad7405_state *st = iio_priv(indio_dev);
int ret;
if (dec_rate > 4096 || dec_rate < 32)
return -EINVAL;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = iio_backend_oversampling_ratio_set(st->back, chan->scan_index, dec_rate);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
st->dec_rate = dec_rate;
return 0;
}
static int ad7405_read_raw(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, int *val,
int *val2, long info)
{
struct ad7405_state *st = iio_priv(indio_dev);
switch (info) {
case IIO_CHAN_INFO_SCALE:
*val = st->info->full_scale_mv;
*val2 = indio_dev->channels[0].scan_type.realbits - 1;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*val = st->dec_rate;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = DIV_ROUND_CLOSEST_ULL(st->ref_frequency, st->dec_rate);
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
*val = -(1 << (indio_dev->channels[0].scan_type.realbits - 1));
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int ad7405_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long info)
{
switch (info) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
if (val < 0)
return -EINVAL;
return ad7405_set_dec_rate(indio_dev, chan, val);
default:
return -EINVAL;
}
}
static int ad7405_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long info)
{
switch (info) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*vals = ad7405_dec_rates_range;
*type = IIO_VAL_INT;
return IIO_AVAIL_RANGE;
default:
return -EINVAL;
}
}
static const struct iio_info ad7405_iio_info = {
.read_raw = &ad7405_read_raw,
.write_raw = &ad7405_write_raw,
.read_avail = &ad7405_read_avail,
};
static const struct iio_chan_spec ad7405_channel = {
.type = IIO_VOLTAGE,
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.info_mask_shared_by_all = IIO_CHAN_INFO_SAMP_FREQ |
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.info_mask_shared_by_all_available =
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.indexed = 1,
.channel = 0,
.channel2 = 1,
.differential = 1,
.scan_index = 0,
.scan_type = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
},
};
static const struct ad7405_chip_info ad7405_chip_info = {
.name = "ad7405",
.full_scale_mv = 320,
};
static const struct ad7405_chip_info adum7701_chip_info = {
.name = "adum7701",
.full_scale_mv = 320,
};
static const struct ad7405_chip_info adum7702_chip_info = {
.name = "adum7702",
.full_scale_mv = 64,
};
static const struct ad7405_chip_info adum7703_chip_info = {
.name = "adum7703",
.full_scale_mv = 320,
};
static const char * const ad7405_power_supplies[] = {
"vdd1", "vdd2",
};
static int ad7405_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct iio_dev *indio_dev;
struct ad7405_state *st;
struct clk *clk;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
st->info = device_get_match_data(dev);
if (!st->info)
return dev_err_probe(dev, -EINVAL, "no chip info\n");
ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad7405_power_supplies),
ad7405_power_supplies);
if (ret)
return dev_err_probe(dev, ret, "failed to get and enable supplies");
clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
return PTR_ERR(clk);
st->ref_frequency = clk_get_rate(clk);
if (!st->ref_frequency)
return -EINVAL;
indio_dev->name = st->info->name;
indio_dev->channels = &ad7405_channel;
indio_dev->num_channels = 1;
indio_dev->info = &ad7405_iio_info;
st->back = devm_iio_backend_get(dev, NULL);
if (IS_ERR(st->back))
return dev_err_probe(dev, PTR_ERR(st->back),
"failed to get IIO backend");
ret = iio_backend_chan_enable(st->back, 0);
if (ret)
return ret;
ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
if (ret)
return ret;
ret = devm_iio_backend_enable(dev, st->back);
if (ret)
return ret;
/*
* Set 256 decimation rate. The default value in the AXI_ADC register
* is 0, so we set the register with a decimation rate value that is
* functional for all parts.
*/
ret = ad7405_set_dec_rate(indio_dev, &indio_dev->channels[0], 256);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct of_device_id ad7405_of_match[] = {
{ .compatible = "adi,ad7405", .data = &ad7405_chip_info, },
{ .compatible = "adi,adum7701", .data = &adum7701_chip_info, },
{ .compatible = "adi,adum7702", .data = &adum7702_chip_info, },
{ .compatible = "adi,adum7703", .data = &adum7703_chip_info, },
{ }
};
MODULE_DEVICE_TABLE(of, ad7405_of_match);
static struct platform_driver ad7405_driver = {
.driver = {
.name = "ad7405",
.of_match_table = ad7405_of_match,
},
.probe = ad7405_probe,
};
module_platform_driver(ad7405_driver);
MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>");
MODULE_AUTHOR("Pop Ioan Daniel <pop.ioan-daniel@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7405 driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("IIO_BACKEND");

View File

@@ -435,6 +435,13 @@ static const struct spi_device_id ad7476_id[] = {
{ "ads7866", ID_ADS7866 },
{ "ads7867", ID_ADS7867 },
{ "ads7868", ID_ADS7868 },
/*
* The ROHM BU79100G is identical to the TI's ADS7866 from the software
* point of view. The binding document mandates the ADS7866 to be
* marked as a fallback for the BU79100G, but we still need the SPI ID
* here to make the module loading work.
*/
{ "bu79100g", ID_ADS7866 },
{ "ltc2314-14", ID_LTC2314_14 },
{ }
};

View File

@@ -33,6 +33,10 @@
#include "ad7606.h"
#define AD7606_CALIB_GAIN_MIN 0
#define AD7606_CALIB_GAIN_STEP 1024
#define AD7606_CALIB_GAIN_MAX (63 * AD7606_CALIB_GAIN_STEP)
/*
* Scales are computed as 5000/32768 and 10000/32768 respectively,
* so that when applied to the raw values they provide mV values.
@@ -95,6 +99,22 @@ static const unsigned int ad7616_oversampling_avail[8] = {
1, 2, 4, 8, 16, 32, 64, 128,
};
static const int ad7606_calib_offset_avail[3] = {
-128, 1, 127,
};
static const int ad7606c_18bit_calib_offset_avail[3] = {
-512, 4, 508,
};
static const int ad7606b_calib_phase_avail[][2] = {
{ 0, 0 }, { 0, 1250 }, { 0, 318750 },
};
static const int ad7606c_calib_phase_avail[][2] = {
{ 0, 0 }, { 0, 1000 }, { 0, 255000 },
};
static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev,
struct iio_chan_spec *chan);
static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev,
@@ -164,6 +184,9 @@ const struct ad7606_chip_info ad7606b_info = {
.scale_setup_cb = ad7606_16bit_chan_scale_setup,
.sw_setup_cb = ad7606b_sw_mode_setup,
.offload_storagebits = 32,
.calib_gain_avail = true,
.calib_offset_avail = ad7606_calib_offset_avail,
.calib_phase_avail = ad7606b_calib_phase_avail,
};
EXPORT_SYMBOL_NS_GPL(ad7606b_info, "IIO_AD7606");
@@ -177,6 +200,9 @@ const struct ad7606_chip_info ad7606c_16_info = {
.scale_setup_cb = ad7606c_16bit_chan_scale_setup,
.sw_setup_cb = ad7606b_sw_mode_setup,
.offload_storagebits = 32,
.calib_gain_avail = true,
.calib_offset_avail = ad7606_calib_offset_avail,
.calib_phase_avail = ad7606c_calib_phase_avail,
};
EXPORT_SYMBOL_NS_GPL(ad7606c_16_info, "IIO_AD7606");
@@ -226,6 +252,9 @@ const struct ad7606_chip_info ad7606c_18_info = {
.scale_setup_cb = ad7606c_18bit_chan_scale_setup,
.sw_setup_cb = ad7606b_sw_mode_setup,
.offload_storagebits = 32,
.calib_gain_avail = true,
.calib_offset_avail = ad7606c_18bit_calib_offset_avail,
.calib_phase_avail = ad7606c_calib_phase_avail,
};
EXPORT_SYMBOL_NS_GPL(ad7606c_18_info, "IIO_AD7606");
@@ -261,21 +290,21 @@ static int ad7606_16bit_chan_scale_setup(struct iio_dev *indio_dev,
struct iio_chan_spec *chan)
{
struct ad7606_state *st = iio_priv(indio_dev);
struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index];
struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index];
if (!st->sw_mode_en) {
/* tied to logic low, analog input range is +/- 5V */
cs->range = 0;
cs->scale_avail = ad7606_16bit_hw_scale_avail;
cs->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail);
ci->range = 0;
ci->scale_avail = ad7606_16bit_hw_scale_avail;
ci->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail);
return 0;
}
/* Scale of 0.076293 is only available in sw mode */
/* After reset, in software mode, ±10 V is set by default */
cs->range = 2;
cs->scale_avail = ad7606_16bit_sw_scale_avail;
cs->num_scales = ARRAY_SIZE(ad7606_16bit_sw_scale_avail);
ci->range = 2;
ci->scale_avail = ad7606_16bit_sw_scale_avail;
ci->num_scales = ARRAY_SIZE(ad7606_16bit_sw_scale_avail);
return 0;
}
@@ -284,6 +313,7 @@ static int ad7606_get_chan_config(struct iio_dev *indio_dev, int ch,
bool *bipolar, bool *differential)
{
struct ad7606_state *st = iio_priv(indio_dev);
struct ad7606_chan_info *ci;
unsigned int num_channels = st->chip_info->num_adc_channels;
struct device *dev = st->dev;
int ret;
@@ -297,15 +327,13 @@ static int ad7606_get_chan_config(struct iio_dev *indio_dev, int ch,
ret = fwnode_property_read_u32(child, "reg", &reg);
if (ret)
continue;
return ret;
/* channel number (here) is from 1 to num_channels */
if (reg < 1 || reg > num_channels) {
dev_warn(dev,
"Invalid channel number (ignoring): %d\n", reg);
continue;
}
if (reg < 1 || reg > num_channels)
return -EINVAL;
/* Loop until we are in the right channel. */
if (reg != (ch + 1))
continue;
@@ -329,6 +357,14 @@ static int ad7606_get_chan_config(struct iio_dev *indio_dev, int ch,
return -EINVAL;
}
ci = &st->chan_info[reg - 1];
ci->r_gain = 0;
ret = fwnode_property_read_u32(child, "adi,rfilter-ohms",
&ci->r_gain);
if (ret == 0 && ci->r_gain > AD7606_CALIB_GAIN_MAX)
return -EINVAL;
return 0;
}
@@ -339,14 +375,14 @@ static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev,
struct iio_chan_spec *chan)
{
struct ad7606_state *st = iio_priv(indio_dev);
struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index];
struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index];
bool bipolar, differential;
int ret;
if (!st->sw_mode_en) {
cs->range = 0;
cs->scale_avail = ad7606_18bit_hw_scale_avail;
cs->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail);
ci->range = 0;
ci->scale_avail = ad7606_18bit_hw_scale_avail;
ci->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail);
return 0;
}
@@ -356,12 +392,12 @@ static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev,
return ret;
if (differential) {
cs->scale_avail = ad7606c_18bit_differential_bipolar_scale_avail;
cs->num_scales =
ci->scale_avail = ad7606c_18bit_differential_bipolar_scale_avail;
ci->num_scales =
ARRAY_SIZE(ad7606c_18bit_differential_bipolar_scale_avail);
/* Bipolar differential ranges start at 8 (b1000) */
cs->reg_offset = 8;
cs->range = 1;
ci->reg_offset = 8;
ci->range = 1;
chan->differential = 1;
chan->channel2 = chan->channel;
@@ -371,23 +407,23 @@ static int ad7606c_18bit_chan_scale_setup(struct iio_dev *indio_dev,
chan->differential = 0;
if (bipolar) {
cs->scale_avail = ad7606c_18bit_single_ended_bipolar_scale_avail;
cs->num_scales =
ci->scale_avail = ad7606c_18bit_single_ended_bipolar_scale_avail;
ci->num_scales =
ARRAY_SIZE(ad7606c_18bit_single_ended_bipolar_scale_avail);
/* Bipolar single-ended ranges start at 0 (b0000) */
cs->reg_offset = 0;
cs->range = 3;
ci->reg_offset = 0;
ci->range = 3;
chan->scan_type.sign = 's';
return 0;
}
cs->scale_avail = ad7606c_18bit_single_ended_unipolar_scale_avail;
cs->num_scales =
ci->scale_avail = ad7606c_18bit_single_ended_unipolar_scale_avail;
ci->num_scales =
ARRAY_SIZE(ad7606c_18bit_single_ended_unipolar_scale_avail);
/* Unipolar single-ended ranges start at 5 (b0101) */
cs->reg_offset = 5;
cs->range = 1;
ci->reg_offset = 5;
ci->range = 1;
chan->scan_type.sign = 'u';
return 0;
@@ -397,14 +433,14 @@ static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev,
struct iio_chan_spec *chan)
{
struct ad7606_state *st = iio_priv(indio_dev);
struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index];
struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index];
bool bipolar, differential;
int ret;
if (!st->sw_mode_en) {
cs->range = 0;
cs->scale_avail = ad7606_16bit_hw_scale_avail;
cs->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail);
ci->range = 0;
ci->scale_avail = ad7606_16bit_hw_scale_avail;
ci->num_scales = ARRAY_SIZE(ad7606_16bit_hw_scale_avail);
return 0;
}
@@ -414,12 +450,12 @@ static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev,
return ret;
if (differential) {
cs->scale_avail = ad7606c_16bit_differential_bipolar_scale_avail;
cs->num_scales =
ci->scale_avail = ad7606c_16bit_differential_bipolar_scale_avail;
ci->num_scales =
ARRAY_SIZE(ad7606c_16bit_differential_bipolar_scale_avail);
/* Bipolar differential ranges start at 8 (b1000) */
cs->reg_offset = 8;
cs->range = 1;
ci->reg_offset = 8;
ci->range = 1;
chan->differential = 1;
chan->channel2 = chan->channel;
chan->scan_type.sign = 's';
@@ -430,23 +466,23 @@ static int ad7606c_16bit_chan_scale_setup(struct iio_dev *indio_dev,
chan->differential = 0;
if (bipolar) {
cs->scale_avail = ad7606c_16bit_single_ended_bipolar_scale_avail;
cs->num_scales =
ci->scale_avail = ad7606c_16bit_single_ended_bipolar_scale_avail;
ci->num_scales =
ARRAY_SIZE(ad7606c_16bit_single_ended_bipolar_scale_avail);
/* Bipolar single-ended ranges start at 0 (b0000) */
cs->reg_offset = 0;
cs->range = 3;
ci->reg_offset = 0;
ci->range = 3;
chan->scan_type.sign = 's';
return 0;
}
cs->scale_avail = ad7606c_16bit_single_ended_unipolar_scale_avail;
cs->num_scales =
ci->scale_avail = ad7606c_16bit_single_ended_unipolar_scale_avail;
ci->num_scales =
ARRAY_SIZE(ad7606c_16bit_single_ended_unipolar_scale_avail);
/* Unipolar single-ended ranges start at 5 (b0101) */
cs->reg_offset = 5;
cs->range = 1;
ci->reg_offset = 5;
ci->range = 1;
chan->scan_type.sign = 'u';
return 0;
@@ -456,11 +492,11 @@ static int ad7607_chan_scale_setup(struct iio_dev *indio_dev,
struct iio_chan_spec *chan)
{
struct ad7606_state *st = iio_priv(indio_dev);
struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index];
struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index];
cs->range = 0;
cs->scale_avail = ad7607_hw_scale_avail;
cs->num_scales = ARRAY_SIZE(ad7607_hw_scale_avail);
ci->range = 0;
ci->scale_avail = ad7607_hw_scale_avail;
ci->num_scales = ARRAY_SIZE(ad7607_hw_scale_avail);
return 0;
}
@@ -468,11 +504,11 @@ static int ad7608_chan_scale_setup(struct iio_dev *indio_dev,
struct iio_chan_spec *chan)
{
struct ad7606_state *st = iio_priv(indio_dev);
struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index];
struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index];
cs->range = 0;
cs->scale_avail = ad7606_18bit_hw_scale_avail;
cs->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail);
ci->range = 0;
ci->scale_avail = ad7606_18bit_hw_scale_avail;
ci->num_scales = ARRAY_SIZE(ad7606_18bit_hw_scale_avail);
return 0;
}
@@ -480,11 +516,11 @@ static int ad7609_chan_scale_setup(struct iio_dev *indio_dev,
struct iio_chan_spec *chan)
{
struct ad7606_state *st = iio_priv(indio_dev);
struct ad7606_chan_scale *cs = &st->chan_scales[chan->scan_index];
struct ad7606_chan_info *ci = &st->chan_info[chan->scan_index];
cs->range = 0;
cs->scale_avail = ad7609_hw_scale_avail;
cs->num_scales = ARRAY_SIZE(ad7609_hw_scale_avail);
ci->range = 0;
ci->scale_avail = ad7609_hw_scale_avail;
ci->num_scales = ARRAY_SIZE(ad7609_hw_scale_avail);
return 0;
}
@@ -681,6 +717,40 @@ static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch,
return ret;
}
static int ad7606_get_calib_offset(struct ad7606_state *st, int ch, int *val)
{
int ret;
ret = st->bops->reg_read(st, AD7606_CALIB_OFFSET(ch));
if (ret < 0)
return ret;
*val = st->chip_info->calib_offset_avail[0] +
ret * st->chip_info->calib_offset_avail[1];
return 0;
}
static int ad7606_get_calib_phase(struct ad7606_state *st, int ch, int *val,
int *val2)
{
int ret;
ret = st->bops->reg_read(st, AD7606_CALIB_PHASE(ch));
if (ret < 0)
return ret;
*val = 0;
/*
* ad7606b: phase delay from 0 to 318.75 μs in steps of 1.25 μs.
* ad7606c-16/18: phase delay from 0 µs to 255 µs in steps of 1 µs.
*/
*val2 = ret * st->chip_info->calib_phase_avail[1][1];
return 0;
}
static int ad7606_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
@@ -689,7 +759,7 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
{
int ret, ch = 0;
struct ad7606_state *st = iio_priv(indio_dev);
struct ad7606_chan_scale *cs;
struct ad7606_chan_info *ci;
struct pwm_state cnvst_pwm_state;
switch (m) {
@@ -704,9 +774,9 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SCALE:
if (st->sw_mode_en)
ch = chan->scan_index;
cs = &st->chan_scales[ch];
*val = cs->scale_avail[cs->range][0];
*val2 = cs->scale_avail[cs->range][1];
ci = &st->chan_info[ch];
*val = ci->scale_avail[ci->range][0];
*val2 = ci->scale_avail[ci->range][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*val = st->oversampling;
@@ -715,6 +785,22 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
pwm_get_state(st->cnvst_pwm, &cnvst_pwm_state);
*val = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, cnvst_pwm_state.period);
return IIO_VAL_INT;
case IIO_CHAN_INFO_CALIBBIAS:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7606_get_calib_offset(st, chan->scan_index, val);
iio_device_release_direct(indio_dev);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_CONVDELAY:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7606_get_calib_phase(st, chan->scan_index, val, val2);
iio_device_release_direct(indio_dev);
if (ret)
return ret;
return IIO_VAL_INT_PLUS_NANO;
}
return -EINVAL;
}
@@ -725,12 +811,12 @@ static ssize_t in_voltage_scale_available_show(struct device *dev,
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ad7606_state *st = iio_priv(indio_dev);
struct ad7606_chan_scale *cs = &st->chan_scales[0];
const unsigned int (*vals)[2] = cs->scale_avail;
struct ad7606_chan_info *ci = &st->chan_info[0];
const unsigned int (*vals)[2] = ci->scale_avail;
unsigned int i;
size_t len = 0;
for (i = 0; i < cs->num_scales; i++)
for (i = 0; i < ci->num_scales; i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ",
vals[i][0], vals[i][1]);
buf[len - 1] = '\n';
@@ -765,6 +851,64 @@ static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val)
return 0;
}
static int ad7606_set_calib_offset(struct ad7606_state *st, int ch, int val)
{
int start_val, step_val, stop_val;
int offset;
start_val = st->chip_info->calib_offset_avail[0];
step_val = st->chip_info->calib_offset_avail[1];
stop_val = st->chip_info->calib_offset_avail[2];
if (val < start_val || val > stop_val)
return -EINVAL;
offset = (val - start_val) / step_val;
return st->bops->reg_write(st, AD7606_CALIB_OFFSET(ch), offset);
}
static int ad7606_set_calib_phase(struct ad7606_state *st, int ch, int val,
int val2)
{
int wreg, start_ns, step_ns, stop_ns;
if (val != 0)
return -EINVAL;
start_ns = st->chip_info->calib_phase_avail[0][1];
step_ns = st->chip_info->calib_phase_avail[1][1];
stop_ns = st->chip_info->calib_phase_avail[2][1];
/*
* ad7606b: phase delay from 0 to 318.75 μs in steps of 1.25 μs.
* ad7606c-16/18: phase delay from 0 µs to 255 µs in steps of 1 µs.
*/
if (val2 < start_ns || val2 > stop_ns)
return -EINVAL;
wreg = val2 / step_ns;
return st->bops->reg_write(st, AD7606_CALIB_PHASE(ch), wreg);
}
static int ad7606_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, long info)
{
switch (info) {
case IIO_CHAN_INFO_SCALE:
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
case IIO_CHAN_INFO_CALIBBIAS:
return IIO_VAL_INT;
case IIO_CHAN_INFO_CONVDELAY:
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static int ad7606_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
@@ -773,7 +917,7 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
{
struct ad7606_state *st = iio_priv(indio_dev);
unsigned int scale_avail_uv[AD760X_MAX_SCALES];
struct ad7606_chan_scale *cs;
struct ad7606_chan_info *ci;
int i, ret, ch = 0;
guard(mutex)(&st->lock);
@@ -782,21 +926,21 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SCALE:
if (st->sw_mode_en)
ch = chan->scan_index;
cs = &st->chan_scales[ch];
for (i = 0; i < cs->num_scales; i++) {
scale_avail_uv[i] = cs->scale_avail[i][0] * MICRO +
cs->scale_avail[i][1];
ci = &st->chan_info[ch];
for (i = 0; i < ci->num_scales; i++) {
scale_avail_uv[i] = ci->scale_avail[i][0] * MICRO +
ci->scale_avail[i][1];
}
val = (val * MICRO) + val2;
i = find_closest(val, scale_avail_uv, cs->num_scales);
i = find_closest(val, scale_avail_uv, ci->num_scales);
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = st->write_scale(indio_dev, ch, i + cs->reg_offset);
ret = st->write_scale(indio_dev, ch, i + ci->reg_offset);
iio_device_release_direct(indio_dev);
if (ret < 0)
return ret;
cs->range = i;
ci->range = i;
return 0;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
@@ -818,6 +962,18 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
if (val < 0 && val2 != 0)
return -EINVAL;
return ad7606_set_sampling_freq(st, val);
case IIO_CHAN_INFO_CALIBBIAS:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7606_set_calib_offset(st, chan->scan_index, val);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_CONVDELAY:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7606_set_calib_phase(st, chan->scan_index, val, val2);
iio_device_release_direct(indio_dev);
return ret;
default:
return -EINVAL;
}
@@ -975,7 +1131,7 @@ static int ad7606_read_avail(struct iio_dev *indio_dev,
long info)
{
struct ad7606_state *st = iio_priv(indio_dev);
struct ad7606_chan_scale *cs;
struct ad7606_chan_info *ci;
unsigned int ch = 0;
switch (info) {
@@ -990,12 +1146,20 @@ static int ad7606_read_avail(struct iio_dev *indio_dev,
if (st->sw_mode_en)
ch = chan->scan_index;
cs = &st->chan_scales[ch];
*vals = (int *)cs->scale_avail;
*length = cs->num_scales * 2;
ci = &st->chan_info[ch];
*vals = (int *)ci->scale_avail;
*length = ci->num_scales * 2;
*type = IIO_VAL_INT_PLUS_MICRO;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_CALIBBIAS:
*vals = st->chip_info->calib_offset_avail;
*type = IIO_VAL_INT;
return IIO_AVAIL_RANGE;
case IIO_CHAN_INFO_CONVDELAY:
*vals = (const int *)st->chip_info->calib_phase_avail;
*type = IIO_VAL_INT_PLUS_NANO;
return IIO_AVAIL_RANGE;
}
return -EINVAL;
}
@@ -1058,6 +1222,7 @@ static const struct iio_info ad7606_info_sw_mode = {
.read_raw = &ad7606_read_raw,
.write_raw = &ad7606_write_raw,
.read_avail = &ad7606_read_avail,
.write_raw_get_fmt = ad7606_write_raw_get_fmt,
.debugfs_reg_access = &ad7606_reg_access,
.validate_trigger = &ad7606_validate_trigger,
.update_scan_mode = &ad7606_update_scan_mode,
@@ -1203,6 +1368,23 @@ static int ad7606b_sw_mode_setup(struct iio_dev *indio_dev)
return st->bops->sw_mode_config(indio_dev);
}
static int ad7606_set_gain_calib(struct ad7606_state *st)
{
struct ad7606_chan_info *ci;
int i, ret;
for (i = 0; i < st->chip_info->num_adc_channels; i++) {
ci = &st->chan_info[i];
ret = st->bops->reg_write(st, AD7606_CALIB_GAIN(i),
DIV_ROUND_CLOSEST(ci->r_gain,
AD7606_CALIB_GAIN_STEP));
if (ret)
return ret;
}
return 0;
}
static int ad7606_probe_channels(struct iio_dev *indio_dev)
{
struct ad7606_state *st = iio_priv(indio_dev);
@@ -1250,6 +1432,15 @@ static int ad7606_probe_channels(struct iio_dev *indio_dev)
chan->info_mask_separate_available |=
BIT(IIO_CHAN_INFO_SCALE);
if (st->chip_info->calib_offset_avail) {
chan->info_mask_separate |=
BIT(IIO_CHAN_INFO_CALIBBIAS) |
BIT(IIO_CHAN_INFO_CONVDELAY);
chan->info_mask_separate_available |=
BIT(IIO_CHAN_INFO_CALIBBIAS) |
BIT(IIO_CHAN_INFO_CONVDELAY);
}
/*
* All chips with software mode support oversampling,
* so we skip the oversampling_available check. And the
@@ -1330,6 +1521,16 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
return dev_err_probe(dev, ret,
"Failed to enable specified AVcc supply\n");
ret = devm_regulator_get_enable(dev, "vdrive");
if (ret)
return dev_err_probe(dev, ret,
"Failed to enable Vdrive supply\n");
ret = devm_regulator_get_enable_optional(dev, "refin");
if (ret && ret != -ENODEV)
return dev_err_probe(dev, ret,
"Failed to enable REFIN supply\n");
st->chip_info = chip_info;
if (st->chip_info->oversampling_num) {
@@ -1462,6 +1663,12 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
st->chip_info->sw_setup_cb(indio_dev);
}
if (st->sw_mode_en && st->chip_info->calib_gain_avail) {
ret = ad7606_set_gain_calib(st);
if (ret)
return ret;
}
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS_GPL(ad7606_probe, "IIO_AD7606");
@@ -1487,7 +1694,7 @@ static int ad7606_resume(struct device *dev)
struct ad7606_state *st = iio_priv(indio_dev);
if (st->gpio_standby) {
gpiod_set_value(st->gpio_range, st->chan_scales[0].range);
gpiod_set_value(st->gpio_range, st->chan_info[0].range);
gpiod_set_value(st->gpio_standby, 1);
ad7606_reset(st);
}

View File

@@ -40,6 +40,11 @@
#define AD7606_RANGE_CH_ADDR(ch) (0x03 + ((ch) >> 1))
#define AD7606_OS_MODE 0x08
#define AD7606_CALIB_GAIN(ch) (0x09 + (ch))
#define AD7606_CALIB_GAIN_MASK GENMASK(5, 0)
#define AD7606_CALIB_OFFSET(ch) (0x11 + (ch))
#define AD7606_CALIB_PHASE(ch) (0x19 + (ch))
struct ad7606_state;
typedef int (*ad7606_scale_setup_cb_t)(struct iio_dev *indio_dev,
@@ -61,6 +66,9 @@ typedef int (*ad7606_sw_setup_cb_t)(struct iio_dev *indio_dev);
* @init_delay_ms: required delay in milliseconds for initialization
* after a restart
* @offload_storagebits: storage bits used by the offload hw implementation
* @calib_gain_avail: chip supports gain calibration
* @calib_offset_avail: pointer to offset calibration range/limits array
* @calib_phase_avail: pointer to phase calibration range/limits array
*/
struct ad7606_chip_info {
unsigned int max_samplerate;
@@ -74,22 +82,28 @@ struct ad7606_chip_info {
bool os_req_reset;
unsigned long init_delay_ms;
u8 offload_storagebits;
bool calib_gain_avail;
const int *calib_offset_avail;
const int (*calib_phase_avail)[2];
};
/**
* struct ad7606_chan_scale - channel scale configuration
* struct ad7606_chan_info - channel configuration
* @scale_avail: pointer to the array which stores the available scales
* @num_scales: number of elements stored in the scale_avail array
* @range: voltage range selection, selects which scale to apply
* @reg_offset: offset for the register value, to be applied when
* writing the value of 'range' to the register value
* @r_gain: gain resistor value in ohms, to be set to match the
* external r_filter value
*/
struct ad7606_chan_scale {
struct ad7606_chan_info {
#define AD760X_MAX_SCALES 16
const unsigned int (*scale_avail)[2];
unsigned int num_scales;
unsigned int range;
unsigned int reg_offset;
unsigned int r_gain;
};
/**
@@ -97,7 +111,7 @@ struct ad7606_chan_scale {
* @dev: pointer to kernel device
* @chip_info: entry in the table of chips that describes this device
* @bops: bus operations (SPI or parallel)
* @chan_scales: scale configuration for channels
* @chan_info: scale configuration for channels
* @oversampling: oversampling selection
* @cnvst_pwm: pointer to the PWM device connected to the cnvst pin
* @base_address: address from where to read data in parallel operation
@@ -128,7 +142,7 @@ struct ad7606_state {
struct device *dev;
const struct ad7606_chip_info *chip_info;
const struct ad7606_bus_ops *bops;
struct ad7606_chan_scale chan_scales[AD760X_MAX_CHANNELS];
struct ad7606_chan_info chan_info[AD760X_MAX_CHANNELS];
unsigned int oversampling;
struct pwm_device *cnvst_pwm;
void __iomem *base_address;

File diff suppressed because it is too large Load Diff

View File

@@ -7,24 +7,33 @@
*/
#include <linux/align.h>
#include <linux/interrupt.h>
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/find.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/adc/ad_sigma_delta.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/spi/offload/consumer.h>
#include <linux/spi/spi.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/unaligned.h>
#include <linux/iio/adc/ad_sigma_delta.h>
#include <linux/iio/buffer-dmaengine.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#define AD_SD_COMM_CHAN_MASK 0x3
@@ -40,7 +49,7 @@
* @sigma_delta: The sigma delta device
* @comm: New value for the communications register
*/
void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm)
void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, u8 comm)
{
/* Some variants use the lower two bits of the communications register
* to select the channel */
@@ -61,7 +70,7 @@ EXPORT_SYMBOL_NS_GPL(ad_sd_set_comm, "IIO_AD_SIGMA_DELTA");
int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg,
unsigned int size, unsigned int val)
{
uint8_t *data = sigma_delta->tx_buf;
u8 *data = sigma_delta->tx_buf;
struct spi_transfer t = {
.tx_buf = data,
.len = size + 1,
@@ -100,10 +109,18 @@ int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg,
}
EXPORT_SYMBOL_NS_GPL(ad_sd_write_reg, "IIO_AD_SIGMA_DELTA");
static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta,
unsigned int reg, unsigned int size, uint8_t *val)
static void ad_sd_set_read_reg_addr(struct ad_sigma_delta *sigma_delta, u8 reg,
u8 *data)
{
uint8_t *data = sigma_delta->tx_buf;
data[0] = reg << sigma_delta->info->addr_shift;
data[0] |= sigma_delta->info->read_mask;
data[0] |= sigma_delta->comm;
}
static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta,
unsigned int reg, unsigned int size, u8 *val)
{
u8 *data = sigma_delta->tx_buf;
int ret;
struct spi_transfer t[] = {
{
@@ -120,9 +137,7 @@ static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta,
spi_message_init(&m);
if (sigma_delta->info->has_registers) {
data[0] = reg << sigma_delta->info->addr_shift;
data[0] |= sigma_delta->info->read_mask;
data[0] |= sigma_delta->comm;
ad_sd_set_read_reg_addr(sigma_delta, reg, data);
spi_message_add_tail(&t[0], &m);
}
spi_message_add_tail(&t[1], &m);
@@ -187,11 +202,11 @@ EXPORT_SYMBOL_NS_GPL(ad_sd_read_reg, "IIO_AD_SIGMA_DELTA");
int ad_sd_reset(struct ad_sigma_delta *sigma_delta)
{
unsigned int reset_length = sigma_delta->info->num_resetclks;
uint8_t *buf;
unsigned int size;
u8 *buf;
int ret;
size = DIV_ROUND_UP(reset_length, 8);
size = BITS_TO_BYTES(reset_length);
buf = kcalloc(size, sizeof(*buf), GFP_KERNEL);
if (!buf)
return -ENOMEM;
@@ -281,9 +296,7 @@ static int ad_sigma_delta_clear_pending_event(struct ad_sigma_delta *sigma_delta
if (sigma_delta->info->has_registers) {
unsigned int data_reg = sigma_delta->info->data_reg ?: AD_SD_REG_DATA;
data[0] = data_reg << sigma_delta->info->addr_shift;
data[0] |= sigma_delta->info->read_mask;
data[0] |= sigma_delta->comm;
ad_sd_set_read_reg_addr(sigma_delta, data_reg, data);
t[0].tx_buf = data;
spi_message_add_tail(&t[0], &m);
}
@@ -420,7 +433,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
data_reg = AD_SD_REG_DATA;
ret = ad_sd_read_reg(sigma_delta, data_reg,
DIV_ROUND_UP(chan->scan_type.realbits + chan->scan_type.shift, 8),
BITS_TO_BYTES(chan->scan_type.realbits + chan->scan_type.shift),
&raw_sample);
out:
@@ -454,9 +467,10 @@ EXPORT_SYMBOL_NS_GPL(ad_sigma_delta_single_conversion, "IIO_AD_SIGMA_DELTA");
static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
{
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
unsigned int i, slot, samples_buf_size;
unsigned int channel;
uint8_t *samples_buf;
const struct iio_scan_type *scan_type = &indio_dev->channels[0].scan_type;
struct spi_transfer *xfer = sigma_delta->sample_xfer;
unsigned int i, slot, channel;
u8 *samples_buf;
int ret;
if (sigma_delta->num_slots == 1) {
@@ -483,20 +497,55 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
sigma_delta->active_slots = slot;
sigma_delta->current_slot = 0;
if (sigma_delta->active_slots > 1) {
ret = ad_sigma_delta_append_status(sigma_delta, true);
if (ret)
return ret;
if (ad_sigma_delta_has_spi_offload(sigma_delta)) {
xfer[1].offload_flags = SPI_OFFLOAD_XFER_RX_STREAM;
xfer[1].bits_per_word = scan_type->realbits;
xfer[1].len = spi_bpw_to_bytes(scan_type->realbits);
} else {
unsigned int samples_buf_size, scan_size;
if (sigma_delta->active_slots > 1) {
ret = ad_sigma_delta_append_status(sigma_delta, true);
if (ret)
return ret;
}
samples_buf_size =
ALIGN(slot * BITS_TO_BYTES(scan_type->storagebits),
sizeof(s64));
samples_buf_size += sizeof(s64);
samples_buf = devm_krealloc(&sigma_delta->spi->dev,
sigma_delta->samples_buf,
samples_buf_size, GFP_KERNEL);
if (!samples_buf)
return -ENOMEM;
sigma_delta->samples_buf = samples_buf;
scan_size = BITS_TO_BYTES(scan_type->realbits + scan_type->shift);
/* For 24-bit data, there is an extra byte of padding. */
xfer[1].rx_buf = &sigma_delta->rx_buf[scan_size == 3 ? 1 : 0];
xfer[1].len = scan_size + (sigma_delta->status_appended ? 1 : 0);
}
xfer[1].cs_change = 1;
if (sigma_delta->info->has_registers) {
xfer[0].tx_buf = &sigma_delta->sample_addr;
xfer[0].len = 1;
ad_sd_set_read_reg_addr(sigma_delta,
sigma_delta->info->data_reg ?: AD_SD_REG_DATA,
&sigma_delta->sample_addr);
spi_message_init_with_transfers(&sigma_delta->sample_msg, xfer, 2);
} else {
spi_message_init_with_transfers(&sigma_delta->sample_msg,
&xfer[1], 1);
}
samples_buf_size = ALIGN(slot * indio_dev->channels[0].scan_type.storagebits, 8);
samples_buf_size += sizeof(int64_t);
samples_buf = devm_krealloc(&sigma_delta->spi->dev, sigma_delta->samples_buf,
samples_buf_size, GFP_KERNEL);
if (!samples_buf)
return -ENOMEM;
sigma_delta->sample_msg.offload = sigma_delta->offload;
sigma_delta->samples_buf = samples_buf;
ret = spi_optimize_message(sigma_delta->spi, &sigma_delta->sample_msg);
if (ret)
return ret;
spi_bus_lock(sigma_delta->spi->controller);
sigma_delta->bus_locked = true;
@@ -510,24 +559,42 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
if (ret)
goto err_unlock;
ad_sd_enable_irq(sigma_delta);
if (ad_sigma_delta_has_spi_offload(sigma_delta)) {
struct spi_offload_trigger_config config = {
.type = SPI_OFFLOAD_TRIGGER_DATA_READY,
};
ret = spi_offload_trigger_enable(sigma_delta->offload,
sigma_delta->offload_trigger,
&config);
if (ret)
goto err_unlock;
} else {
ad_sd_enable_irq(sigma_delta);
}
return 0;
err_unlock:
spi_bus_unlock(sigma_delta->spi->controller);
spi_unoptimize_message(&sigma_delta->sample_msg);
return ret;
}
static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)
static int ad_sd_buffer_predisable(struct iio_dev *indio_dev)
{
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
reinit_completion(&sigma_delta->completion);
wait_for_completion_timeout(&sigma_delta->completion, HZ);
if (ad_sigma_delta_has_spi_offload(sigma_delta)) {
spi_offload_trigger_disable(sigma_delta->offload,
sigma_delta->offload_trigger);
} else {
reinit_completion(&sigma_delta->completion);
wait_for_completion_timeout(&sigma_delta->completion, HZ);
ad_sd_disable_irq(sigma_delta);
ad_sd_disable_irq(sigma_delta);
}
sigma_delta->keep_cs_asserted = false;
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
@@ -537,61 +604,32 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)
ad_sigma_delta_disable_all(sigma_delta);
sigma_delta->bus_locked = false;
return spi_bus_unlock(sigma_delta->spi->controller);
spi_bus_unlock(sigma_delta->spi->controller);
spi_unoptimize_message(&sigma_delta->sample_msg);
return 0;
}
static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
const struct iio_scan_type *scan_type = &indio_dev->channels[0].scan_type;
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
uint8_t *data = sigma_delta->rx_buf;
unsigned int transfer_size;
u8 *data = sigma_delta->rx_buf;
unsigned int sample_size;
unsigned int sample_pos;
unsigned int status_pos;
unsigned int reg_size;
unsigned int data_reg;
int ret;
reg_size = indio_dev->channels[0].scan_type.realbits +
indio_dev->channels[0].scan_type.shift;
reg_size = DIV_ROUND_UP(reg_size, 8);
reg_size = BITS_TO_BYTES(scan_type->realbits + scan_type->shift);
/* For 24-bit data, there is an extra byte of padding. */
status_pos = reg_size + (reg_size == 3 ? 1 : 0);
if (sigma_delta->info->data_reg != 0)
data_reg = sigma_delta->info->data_reg;
else
data_reg = AD_SD_REG_DATA;
/* Status word will be appended to the sample during transfer */
if (sigma_delta->status_appended)
transfer_size = reg_size + 1;
else
transfer_size = reg_size;
switch (reg_size) {
case 4:
case 2:
case 1:
status_pos = reg_size;
ad_sd_read_reg_raw(sigma_delta, data_reg, transfer_size, &data[0]);
break;
case 3:
/*
* Data array after transfer will look like (if status is appended):
* data[] = { [0][sample][sample][sample][status] }
* Keeping the first byte 0 shifts the status position by 1 byte to the right.
*/
status_pos = reg_size + 1;
/* We store 24 bit samples in a 32 bit word. Keep the upper
* byte set to zero. */
ad_sd_read_reg_raw(sigma_delta, data_reg, transfer_size, &data[1]);
break;
default:
dev_err_ratelimited(&indio_dev->dev, "Unsupported reg_size: %u\n", reg_size);
ret = spi_sync_locked(sigma_delta->spi, &sigma_delta->sample_msg);
if (ret)
goto irq_handled;
}
/*
* For devices sampling only one channel at
@@ -617,7 +655,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
}
}
sample_size = indio_dev->channels[0].scan_type.storagebits / 8;
sample_size = BITS_TO_BYTES(scan_type->storagebits);
sample_pos = sample_size * sigma_delta->current_slot;
memcpy(&sigma_delta->samples_buf[sample_pos], data, sample_size);
sigma_delta->current_slot++;
@@ -644,7 +682,7 @@ static bool ad_sd_validate_scan_mask(struct iio_dev *indio_dev, const unsigned l
static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = {
.postenable = &ad_sd_buffer_postenable,
.postdisable = &ad_sd_buffer_postdisable,
.predisable = &ad_sd_buffer_predisable,
.validate_scan_mask = &ad_sd_validate_scan_mask,
};
@@ -671,7 +709,8 @@ static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private)
if ((!sigma_delta->rdy_gpiod || gpiod_get_value(sigma_delta->rdy_gpiod)) &&
ad_sd_disable_irq(sigma_delta)) {
complete(&sigma_delta->completion);
iio_trigger_poll(sigma_delta->trig);
if (sigma_delta->trig)
iio_trigger_poll(sigma_delta->trig);
return IRQ_HANDLED;
}
@@ -704,17 +743,6 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de
unsigned long irq_flags = irq_get_trigger_type(sigma_delta->irq_line);
int ret;
if (dev != &sigma_delta->spi->dev) {
dev_err(dev, "Trigger parent should be '%s', got '%s'\n",
dev_name(dev), dev_name(&sigma_delta->spi->dev));
return -EFAULT;
}
sigma_delta->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name,
iio_device_id(indio_dev));
if (sigma_delta->trig == NULL)
return -ENOMEM;
init_completion(&sigma_delta->completion);
sigma_delta->irq_dis = true;
@@ -734,14 +762,33 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de
if (ret)
return ret;
iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta);
if (ad_sigma_delta_has_spi_offload(sigma_delta)) {
sigma_delta->offload_trigger =
devm_spi_offload_trigger_get(dev, sigma_delta->offload,
SPI_OFFLOAD_TRIGGER_DATA_READY);
if (IS_ERR(sigma_delta->offload_trigger))
return dev_err_probe(dev, PTR_ERR(sigma_delta->offload_trigger),
"Failed to get SPI offload trigger\n");
} else {
if (dev != &sigma_delta->spi->dev)
return dev_err_probe(dev, -EFAULT,
"Trigger parent should be '%s', got '%s'\n",
dev_name(dev), dev_name(&sigma_delta->spi->dev));
ret = devm_iio_trigger_register(dev, sigma_delta->trig);
if (ret)
return ret;
sigma_delta->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
indio_dev->name, iio_device_id(indio_dev));
if (!sigma_delta->trig)
return -ENOMEM;
/* select default trigger */
indio_dev->trig = iio_trigger_get(sigma_delta->trig);
iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta);
ret = devm_iio_trigger_register(dev, sigma_delta->trig);
if (ret)
return ret;
/* select default trigger */
indio_dev->trig = iio_trigger_get(sigma_delta->trig);
}
return 0;
}
@@ -761,12 +808,29 @@ int devm_ad_sd_setup_buffer_and_trigger(struct device *dev, struct iio_dev *indi
if (!sigma_delta->slots)
return -ENOMEM;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
&iio_pollfunc_store_time,
&ad_sd_trigger_handler,
&ad_sd_buffer_setup_ops);
if (ret)
return ret;
if (ad_sigma_delta_has_spi_offload(sigma_delta)) {
struct dma_chan *rx_dma;
rx_dma = devm_spi_offload_rx_stream_request_dma_chan(dev,
sigma_delta->offload);
if (IS_ERR(rx_dma))
return dev_err_probe(dev, PTR_ERR(rx_dma),
"Failed to get RX DMA channel\n");
ret = devm_iio_dmaengine_buffer_setup_with_handle(dev, indio_dev,
rx_dma, IIO_BUFFER_DIRECTION_IN);
if (ret)
return dev_err_probe(dev, ret, "Cannot setup DMA buffer\n");
indio_dev->setup_ops = &ad_sd_buffer_setup_ops;
} else {
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
&iio_pollfunc_store_time,
&ad_sd_trigger_handler,
&ad_sd_buffer_setup_ops);
if (ret)
return ret;
}
return devm_ad_sd_probe_trigger(dev, indio_dev);
}
@@ -829,6 +893,20 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
return sigma_delta->irq_line;
}
if (info->supports_spi_offload) {
struct spi_offload_config offload_config = {
.capability_flags = SPI_OFFLOAD_CAP_TRIGGER |
SPI_OFFLOAD_CAP_RX_STREAM_DMA,
};
int ret;
sigma_delta->offload = devm_spi_offload_get(&spi->dev, spi,
&offload_config);
ret = PTR_ERR_OR_ZERO(sigma_delta->offload);
if (ret && ret != -ENODEV)
return dev_err_probe(&spi->dev, ret, "Failed to get SPI offload\n");
}
iio_device_set_drvdata(indio_dev, sigma_delta);
return 0;
@@ -838,3 +916,4 @@ EXPORT_SYMBOL_NS_GPL(ad_sd_init, "IIO_AD_SIGMA_DELTA");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS("IIO_DMAENGINE_BUFFER");

View File

@@ -44,6 +44,8 @@
#define ADI_AXI_ADC_REG_CONFIG_CMOS_OR_LVDS_N BIT(7)
#define ADI_AXI_ADC_REG_CTRL 0x0044
#define ADI_AXI_ADC_CTRL_NUM_LANES_MSK GENMASK(12, 8)
#define ADI_AXI_ADC_CTRL_SYNC_MSK BIT(3)
#define ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK BIT(1)
#define ADI_AXI_ADC_REG_CNTRL_3 0x004c
@@ -52,6 +54,10 @@
#define AXI_AD485X_PACKET_FORMAT_20BIT 0x0
#define AXI_AD485X_PACKET_FORMAT_24BIT 0x1
#define AXI_AD485X_PACKET_FORMAT_32BIT 0x2
#define AXI_AD408X_CNTRL_3_FILTER_EN_MSK BIT(0)
#define ADI_AXI_ADC_REG_SYNC_STATUS 0x0068
#define ADI_AXI_ADC_SYNC_STATUS_ADC_SYNC_MSK BIT(0)
#define ADI_AXI_ADC_REG_DRP_STATUS 0x0074
#define ADI_AXI_ADC_DRP_LOCKED BIT(17)
@@ -80,6 +86,9 @@
#define ADI_AXI_ADC_REG_CHAN_CTRL_3(c) (0x0418 + (c) * 0x40)
#define ADI_AXI_ADC_CHAN_PN_SEL_MASK GENMASK(19, 16)
#define ADI_AXI_ADC_REG_CHAN_USR_CTRL_2(c) (0x0424 + (c) * 0x40)
#define ADI_AXI_ADC_CHAN_USR_CTRL_2_DEC_RATE_N_MASK GENMASK(15, 0)
/* IO Delays */
#define ADI_AXI_ADC_REG_DELAY(l) (0x0800 + (l) * 0x4)
#define AXI_ADC_DELAY_CTRL_MASK GENMASK(4, 0)
@@ -242,6 +251,19 @@ static int axi_adc_test_pattern_set(struct iio_backend *back,
}
}
static int axi_adc_oversampling_ratio_set(struct iio_backend *back,
unsigned int chan,
unsigned int rate)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
return regmap_update_bits(st->regmap,
ADI_AXI_ADC_REG_CHAN_USR_CTRL_2(chan),
ADI_AXI_ADC_CHAN_USR_CTRL_2_DEC_RATE_N_MASK,
FIELD_PREP(ADI_AXI_ADC_CHAN_USR_CTRL_2_DEC_RATE_N_MASK,
rate));
}
static int axi_adc_read_chan_status(struct adi_axi_adc_state *st, unsigned int chan,
unsigned int *status)
{
@@ -381,7 +403,8 @@ static int axi_adc_ad485x_data_size_set(struct iio_backend *back,
}
static int axi_adc_ad485x_oversampling_ratio_set(struct iio_backend *back,
unsigned int ratio)
unsigned int chan,
unsigned int ratio)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
@@ -402,6 +425,50 @@ static int axi_adc_ad485x_oversampling_ratio_set(struct iio_backend *back,
}
}
static int axi_adc_ad408x_filter_type_set(struct iio_backend *back,
enum iio_backend_filter_type type)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
if (type)
return regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3,
AXI_AD408X_CNTRL_3_FILTER_EN_MSK);
return regmap_clear_bits(st->regmap, ADI_AXI_ADC_REG_CNTRL_3,
AXI_AD408X_CNTRL_3_FILTER_EN_MSK);
}
static int axi_adc_ad408x_interface_data_align(struct iio_backend *back,
u32 timeout_us)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
u32 val;
int ret;
ret = regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CTRL,
ADI_AXI_ADC_CTRL_SYNC_MSK);
if (ret)
return ret;
return regmap_read_poll_timeout(st->regmap, ADI_AXI_ADC_REG_SYNC_STATUS,
val,
FIELD_GET(ADI_AXI_ADC_SYNC_STATUS_ADC_SYNC_MSK, val),
1, timeout_us);
}
static int axi_adc_num_lanes_set(struct iio_backend *back,
unsigned int num_lanes)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
if (!num_lanes)
return -EINVAL;
return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CTRL,
ADI_AXI_ADC_CTRL_NUM_LANES_MSK,
FIELD_PREP(ADI_AXI_ADC_CTRL_NUM_LANES_MSK, num_lanes));
}
static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back,
struct iio_dev *indio_dev)
{
@@ -549,6 +616,7 @@ static const struct iio_backend_ops adi_axi_adc_ops = {
.test_pattern_set = axi_adc_test_pattern_set,
.chan_status = axi_adc_chan_status,
.interface_type_get = axi_adc_interface_type_get,
.oversampling_ratio_set = axi_adc_oversampling_ratio_set,
.debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access),
.debugfs_print_chan_status = iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status),
};
@@ -582,6 +650,26 @@ static const struct iio_backend_info axi_ad485x = {
.ops = &adi_ad485x_ops,
};
static const struct iio_backend_ops adi_ad408x_ops = {
.enable = axi_adc_enable,
.disable = axi_adc_disable,
.chan_enable = axi_adc_chan_enable,
.chan_disable = axi_adc_chan_disable,
.request_buffer = axi_adc_request_buffer,
.free_buffer = axi_adc_free_buffer,
.data_sample_trigger = axi_adc_data_sample_trigger,
.filter_type_set = axi_adc_ad408x_filter_type_set,
.interface_data_align = axi_adc_ad408x_interface_data_align,
.num_lanes_set = axi_adc_num_lanes_set,
.debugfs_reg_access = iio_backend_debugfs_ptr(axi_adc_reg_access),
.debugfs_print_chan_status = iio_backend_debugfs_ptr(axi_adc_debugfs_print_chan_status),
};
static const struct iio_backend_info axi_ad408x = {
.name = "axi-ad408x",
.ops = &adi_ad408x_ops,
};
static int adi_axi_adc_probe(struct platform_device *pdev)
{
struct adi_axi_adc_state *st;
@@ -697,9 +785,15 @@ static const struct axi_adc_info adc_ad7606 = {
.has_child_nodes = true,
};
static const struct axi_adc_info adi_axi_ad408x = {
.version = ADI_AXI_PCORE_VER(10, 0, 'a'),
.backend_info = &axi_ad408x,
};
/* Match table for of_platform binding */
static const struct of_device_id adi_axi_adc_of_match[] = {
{ .compatible = "adi,axi-adc-10.0.a", .data = &adc_generic },
{ .compatible = "adi,axi-ad408x", .data = &adi_axi_ad408x },
{ .compatible = "adi,axi-ad485x", .data = &adi_axi_ad485x },
{ .compatible = "adi,axi-ad7606x", .data = &adc_ad7606 },
{ }

View File

@@ -1226,7 +1226,7 @@ static const struct at91_adc_trigger at91sam9260_triggers[] = {
{ .name = "external", .value = 0xd, .is_external = true },
};
static struct at91_adc_caps at91sam9260_caps = {
static const struct at91_adc_caps at91sam9260_caps = {
.calc_startup_ticks = calc_startup_ticks_9260,
.num_channels = 4,
.low_res_bits = 8,
@@ -1250,7 +1250,7 @@ static const struct at91_adc_trigger at91sam9x5_triggers[] = {
{ .name = "continuous", .value = 0x6 },
};
static struct at91_adc_caps at91sam9rl_caps = {
static const struct at91_adc_caps at91sam9rl_caps = {
.has_ts = true,
.calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */
.num_channels = 6,
@@ -1268,7 +1268,7 @@ static struct at91_adc_caps at91sam9rl_caps = {
.trigger_number = ARRAY_SIZE(at91sam9x5_triggers),
};
static struct at91_adc_caps at91sam9g45_caps = {
static const struct at91_adc_caps at91sam9g45_caps = {
.has_ts = true,
.calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */
.num_channels = 8,
@@ -1286,7 +1286,7 @@ static struct at91_adc_caps at91sam9g45_caps = {
.trigger_number = ARRAY_SIZE(at91sam9x5_triggers),
};
static struct at91_adc_caps at91sam9x5_caps = {
static const struct at91_adc_caps at91sam9x5_caps = {
.has_ts = true,
.has_tsmr = true,
.ts_filter_average = 3,
@@ -1308,7 +1308,7 @@ static struct at91_adc_caps at91sam9x5_caps = {
.trigger_number = ARRAY_SIZE(at91sam9x5_triggers),
};
static struct at91_adc_caps sama5d3_caps = {
static const struct at91_adc_caps sama5d3_caps = {
.has_ts = true,
.has_tsmr = true,
.ts_filter_average = 3,

View File

@@ -173,7 +173,7 @@ static const struct iio_map axp22x_maps[] = {
{ }
};
static struct iio_map axp717_maps[] = {
static const struct iio_map axp717_maps[] = {
{
.consumer_dev_name = "axp20x-usb-power-supply",
.consumer_channel = "vbus_v",

View File

@@ -467,7 +467,7 @@ static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
struct {
__le16 values[DLN2_ADC_MAX_CHANNELS];
aligned_s64 timestamp_space;
} data;
} data = { };
struct dln2_adc_get_all_vals dev_data;
struct dln2_adc *dln2 = iio_priv(indio_dev);
const struct dln2_adc_demux_table *t;
@@ -479,8 +479,6 @@ static irqreturn_t dln2_adc_trigger_h(int irq, void *p)
if (ret < 0)
goto done;
memset(&data, 0, sizeof(data));
/* Demux operation */
for (i = 0; i < dln2->demux_count; ++i) {
t = &dln2->demux[i];

View File

@@ -19,8 +19,6 @@
#include <linux/spi/spi.h>
#include <linux/gpio/consumer.h>
#define DRV_NAME "hi8435"
/* Register offsets for HI-8435 */
#define HI8435_CTRL_REG 0x02
#define HI8435_PSEN_REG 0x04
@@ -536,7 +534,7 @@ MODULE_DEVICE_TABLE(spi, hi8435_id);
static struct spi_driver hi8435_driver = {
.driver = {
.name = DRV_NAME,
.name = "hi8435",
.of_match_table = hi8435_dt_ids,
},
.probe = hi8435_probe,

View File

@@ -25,8 +25,6 @@
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#define DRIVER_NAME "max9611"
/* max9611 register addresses */
#define MAX9611_REG_CSA_DATA 0x00
#define MAX9611_REG_RS_DATA 0x02
@@ -553,7 +551,7 @@ static int max9611_probe(struct i2c_client *client)
static struct i2c_driver max9611_driver = {
.driver = {
.name = DRIVER_NAME,
.name = "max9611",
.of_match_table = max9611_of_table,
},
.probe = max9611_probe,

View File

@@ -44,7 +44,7 @@ struct mp2629_adc {
struct device *dev;
};
static struct iio_chan_spec mp2629_channels[] = {
static const struct iio_chan_spec mp2629_channels[] = {
MP2629_ADC_CHAN(BATT_VOLT, IIO_VOLTAGE),
MP2629_ADC_CHAN(SYSTEM_VOLT, IIO_VOLTAGE),
MP2629_ADC_CHAN(INPUT_VOLT, IIO_VOLTAGE),

View File

@@ -7,6 +7,7 @@
* Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
@@ -24,11 +25,11 @@
#include <dt-bindings/iio/adc/mediatek,mt6357-auxadc.h>
#include <dt-bindings/iio/adc/mediatek,mt6358-auxadc.h>
#include <dt-bindings/iio/adc/mediatek,mt6359-auxadc.h>
#include <dt-bindings/iio/adc/mediatek,mt6363-auxadc.h>
#define AUXADC_AVG_TIME_US 10
#define AUXADC_POLL_DELAY_US 100
#define AUXADC_TIMEOUT_US 32000
#define AUXADC_VOLT_FULL 1800
#define IMP_STOP_DELAY_US 150
#define IMP_POLL_DELAY_US 1000
@@ -46,6 +47,12 @@
#define MT6359_IMP0_CONV_EN BIT(0)
#define MT6359_IMP1_IRQ_RDY BIT(15)
#define MT6363_EXT_CHAN_MASK GENMASK(2, 0)
#define MT6363_EXT_PURES_MASK GENMASK(4, 3)
#define MT6363_PULLUP_RES_100K 0
#define MT6363_PULLUP_RES_30K 1
#define MT6363_PULLUP_RES_OPEN 3
enum mtk_pmic_auxadc_regs {
PMIC_AUXADC_ADC0,
PMIC_AUXADC_DCM_CON,
@@ -54,6 +61,8 @@ enum mtk_pmic_auxadc_regs {
PMIC_AUXADC_IMP3,
PMIC_AUXADC_RQST0,
PMIC_AUXADC_RQST1,
PMIC_AUXADC_RQST3,
PMIC_AUXADC_SDMADC_CON0,
PMIC_HK_TOP_WKEY,
PMIC_HK_TOP_RST_CON0,
PMIC_FGADC_R_CON0,
@@ -75,7 +84,16 @@ enum mtk_pmic_auxadc_channels {
PMIC_AUXADC_CHAN_TSX_TEMP,
PMIC_AUXADC_CHAN_HPOFS_CAL,
PMIC_AUXADC_CHAN_DCXO_TEMP,
PMIC_AUXADC_CHAN_VTREF,
PMIC_AUXADC_CHAN_VBIF,
PMIC_AUXADC_CHAN_VSYSSNS,
PMIC_AUXADC_CHAN_VIN1,
PMIC_AUXADC_CHAN_VIN2,
PMIC_AUXADC_CHAN_VIN3,
PMIC_AUXADC_CHAN_VIN4,
PMIC_AUXADC_CHAN_VIN5,
PMIC_AUXADC_CHAN_VIN6,
PMIC_AUXADC_CHAN_VIN7,
PMIC_AUXADC_CHAN_IBAT,
PMIC_AUXADC_CHAN_VBAT,
PMIC_AUXADC_CHAN_MAX
@@ -101,12 +119,22 @@ struct mt6359_auxadc {
* struct mtk_pmic_auxadc_chan - PMIC AUXADC channel data
* @req_idx: Request register number
* @req_mask: Bitmask to activate a channel
* @rdy_idx: Readiness register number
* @rdy_mask: Bitmask to determine channel readiness
* @ext_sel_idx: PMIC GPIO channel register number
* @ext_sel_ch: PMIC GPIO number
* @ext_sel_pu: PMIC GPIO channel pullup resistor selector
* @num_samples: Number of AUXADC samples for averaging
* @r_ratio: Resistance ratio fractional
*/
struct mtk_pmic_auxadc_chan {
u8 req_idx;
u16 req_mask;
u8 rdy_idx;
u16 rdy_mask;
s8 ext_sel_idx;
u8 ext_sel_ch;
u8 ext_sel_pu;
u16 num_samples;
struct u8_fract r_ratio;
};
@@ -119,7 +147,10 @@ struct mtk_pmic_auxadc_chan {
* @desc: PMIC AUXADC channel data
* @regs: List of PMIC specific registers
* @sec_unlock_key: Security unlock key for HK_TOP writes
* @vref_mV: AUXADC Reference Voltage (VREF) in millivolts
* @imp_adc_num: ADC channel for battery impedance readings
* @is_spmi: Defines whether this PMIC communicates over SPMI
* @no_reset: If true, this PMIC does not support ADC reset
* @read_imp: Callback to read impedance channels
*/
struct mtk_pmic_auxadc_info {
@@ -129,18 +160,34 @@ struct mtk_pmic_auxadc_info {
const struct mtk_pmic_auxadc_chan *desc;
const u16 *regs;
u16 sec_unlock_key;
u32 vref_mV;
u8 imp_adc_num;
int (*read_imp)(struct mt6359_auxadc *adc_dev, int *vbat, int *ibat);
bool is_spmi;
bool no_reset;
int (*read_imp)(struct mt6359_auxadc *adc_dev,
const struct iio_chan_spec *chan, int *vbat, int *ibat);
};
#define MTK_PMIC_ADC_CHAN(_ch_idx, _req_idx, _req_bit, _samples, _rnum, _rdiv) \
#define MTK_PMIC_ADC_EXT_CHAN(_ch_idx, _req_idx, _req_bit, _rdy_idx, _rdy_bit, \
_ext_sel_idx, _ext_sel_ch, _ext_sel_pu, \
_samples, _rnum, _rdiv) \
[PMIC_AUXADC_CHAN_##_ch_idx] = { \
.req_idx = _req_idx, \
.req_mask = BIT(_req_bit), \
.rdy_idx = _rdy_idx, \
.rdy_mask = BIT(_rdy_bit), \
.ext_sel_idx = _ext_sel_idx, \
.ext_sel_ch = _ext_sel_ch, \
.ext_sel_pu = _ext_sel_pu, \
.num_samples = _samples, \
.r_ratio = { _rnum, _rdiv } \
}
#define MTK_PMIC_ADC_CHAN(_ch_idx, _req_idx, _req_bit, _rdy_idx, _rdy_bit, \
_samples, _rnum, _rdiv) \
MTK_PMIC_ADC_EXT_CHAN(_ch_idx, _req_idx, _req_bit, _rdy_idx, _rdy_bit, \
-1, 0, 0, _samples, _rnum, _rdiv)
#define MTK_PMIC_IIO_CHAN(_model, _name, _ch_idx, _adc_idx, _nbits, _ch_type) \
{ \
.type = _ch_type, \
@@ -177,21 +224,21 @@ static const struct iio_chan_spec mt6357_auxadc_channels[] = {
};
static const struct mtk_pmic_auxadc_chan mt6357_auxadc_ch_desc[] = {
MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, 128, 3, 1),
MTK_PMIC_ADC_CHAN(ISENSE, PMIC_AUXADC_RQST0, 0, 128, 3, 1),
MTK_PMIC_ADC_CHAN(VCDT, PMIC_AUXADC_RQST0, 0, 8, 1, 1),
MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, 8, 1, 1),
MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, 8, 1, 1),
MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, 8, 1, 1),
MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, 128, 1, 1),
MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, 256, 1, 1),
MTK_PMIC_ADC_CHAN(DCXO_TEMP, PMIC_AUXADC_RQST0, 10, 16, 1, 1),
MTK_PMIC_ADC_CHAN(VBIF, PMIC_AUXADC_RQST0, 11, 8, 1, 1),
MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST1, 5, 8, 1, 1),
MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST1, 6, 8, 1, 1),
MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP0, 8, 128, 3, 1),
MTK_PMIC_ADC_CHAN(ISENSE, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP0, 8, 128, 3, 1),
MTK_PMIC_ADC_CHAN(VCDT, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP0, 8, 8, 1, 1),
MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, PMIC_AUXADC_IMP0, 8, 8, 1, 1),
MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_IMP0, 8, 8, 1, 1),
MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, PMIC_AUXADC_IMP0, 8, 8, 1, 1),
MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, PMIC_AUXADC_IMP0, 8, 128, 1, 1),
MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, PMIC_AUXADC_IMP0, 8, 256, 1, 1),
MTK_PMIC_ADC_CHAN(DCXO_TEMP, PMIC_AUXADC_RQST0, 10, PMIC_AUXADC_IMP0, 8, 16, 1, 1),
MTK_PMIC_ADC_CHAN(VBIF, PMIC_AUXADC_RQST0, 11, PMIC_AUXADC_IMP0, 8, 8, 1, 1),
MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST1, 5, PMIC_AUXADC_IMP0, 8, 8, 1, 1),
MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST1, 6, PMIC_AUXADC_IMP0, 8, 8, 1, 1),
/* Battery impedance channels */
MTK_PMIC_ADC_CHAN(VBAT, 0, 0, 128, 3, 1),
MTK_PMIC_ADC_CHAN(VBAT, 0, 0, PMIC_AUXADC_IMP0, 8, 128, 3, 1),
};
static const u16 mt6357_auxadc_regs[] = {
@@ -224,22 +271,22 @@ static const struct iio_chan_spec mt6358_auxadc_channels[] = {
};
static const struct mtk_pmic_auxadc_chan mt6358_auxadc_ch_desc[] = {
MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, 128, 3, 1),
MTK_PMIC_ADC_CHAN(VCDT, PMIC_AUXADC_RQST0, 0, 8, 1, 1),
MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, 8, 2, 1),
MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, 8, 1, 1),
MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, 8, 1, 1),
MTK_PMIC_ADC_CHAN(VDCXO, PMIC_AUXADC_RQST0, 6, 8, 3, 2),
MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, 128, 1, 1),
MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, 256, 1, 1),
MTK_PMIC_ADC_CHAN(DCXO_TEMP, PMIC_AUXADC_RQST0, 10, 16, 1, 1),
MTK_PMIC_ADC_CHAN(VBIF, PMIC_AUXADC_RQST0, 11, 8, 2, 1),
MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST1, 8, 8, 1, 1),
MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST1, 9, 8, 1, 1),
MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST1, 10, 8, 1, 1),
MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP0, 8, 128, 3, 1),
MTK_PMIC_ADC_CHAN(VCDT, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP0, 8, 8, 1, 1),
MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, PMIC_AUXADC_IMP0, 8, 8, 2, 1),
MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_IMP0, 8, 8, 1, 1),
MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, PMIC_AUXADC_IMP0, 8, 8, 1, 1),
MTK_PMIC_ADC_CHAN(VDCXO, PMIC_AUXADC_RQST0, 6, PMIC_AUXADC_IMP0, 8, 8, 3, 2),
MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, PMIC_AUXADC_IMP0, 8, 128, 1, 1),
MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, PMIC_AUXADC_IMP0, 8, 256, 1, 1),
MTK_PMIC_ADC_CHAN(DCXO_TEMP, PMIC_AUXADC_RQST0, 10, PMIC_AUXADC_IMP0, 8, 16, 1, 1),
MTK_PMIC_ADC_CHAN(VBIF, PMIC_AUXADC_RQST0, 11, PMIC_AUXADC_IMP0, 8, 8, 2, 1),
MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST1, 8, PMIC_AUXADC_IMP0, 8, 8, 1, 1),
MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST1, 9, PMIC_AUXADC_IMP0, 8, 8, 1, 1),
MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST1, 10, PMIC_AUXADC_IMP0, 8, 8, 1, 1),
/* Battery impedance channels */
MTK_PMIC_ADC_CHAN(VBAT, 0, 0, 128, 7, 2),
MTK_PMIC_ADC_CHAN(VBAT, 0, 0, PMIC_AUXADC_IMP0, 8, 128, 7, 2),
};
static const u16 mt6358_auxadc_regs[] = {
@@ -272,22 +319,22 @@ static const struct iio_chan_spec mt6359_auxadc_channels[] = {
};
static const struct mtk_pmic_auxadc_chan mt6359_auxadc_ch_desc[] = {
MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, 128, 7, 2),
MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, 8, 5, 2),
MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, 8, 1, 1),
MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, 8, 1, 1),
MTK_PMIC_ADC_CHAN(VDCXO, PMIC_AUXADC_RQST0, 6, 8, 3, 2),
MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, 128, 1, 1),
MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, 256, 1, 1),
MTK_PMIC_ADC_CHAN(DCXO_TEMP, PMIC_AUXADC_RQST0, 10, 16, 1, 1),
MTK_PMIC_ADC_CHAN(VBIF, PMIC_AUXADC_RQST0, 11, 8, 5, 2),
MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST1, 8, 8, 1, 1),
MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST1, 9, 8, 1, 1),
MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST1, 10, 8, 1, 1),
MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_IMP1, 15, 128, 7, 2),
MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, PMIC_AUXADC_IMP1, 15, 8, 5, 2),
MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_IMP1, 15, 8, 1, 1),
MTK_PMIC_ADC_CHAN(ACCDET, PMIC_AUXADC_RQST0, 5, PMIC_AUXADC_IMP1, 15 ,8, 1, 1),
MTK_PMIC_ADC_CHAN(VDCXO, PMIC_AUXADC_RQST0, 6, PMIC_AUXADC_IMP1, 15, 8, 3, 2),
MTK_PMIC_ADC_CHAN(TSX_TEMP, PMIC_AUXADC_RQST0, 7, PMIC_AUXADC_IMP1, 15, 128, 1, 1),
MTK_PMIC_ADC_CHAN(HPOFS_CAL, PMIC_AUXADC_RQST0, 9, PMIC_AUXADC_IMP1, 15, 256, 1, 1),
MTK_PMIC_ADC_CHAN(DCXO_TEMP, PMIC_AUXADC_RQST0, 10, PMIC_AUXADC_IMP1, 15, 16, 1, 1),
MTK_PMIC_ADC_CHAN(VBIF, PMIC_AUXADC_RQST0, 11, PMIC_AUXADC_IMP1, 15, 8, 5, 2),
MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST1, 8, PMIC_AUXADC_IMP1, 15, 8, 1, 1),
MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST1, 9, PMIC_AUXADC_IMP1, 15, 8, 1, 1),
MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST1, 10, PMIC_AUXADC_IMP1, 15, 8, 1, 1),
/* Battery impedance channels */
MTK_PMIC_ADC_CHAN(VBAT, 0, 0, 128, 7, 2),
MTK_PMIC_ADC_CHAN(IBAT, 0, 0, 128, 7, 2),
MTK_PMIC_ADC_CHAN(VBAT, 0, 0, PMIC_AUXADC_IMP1, 15, 128, 7, 2),
MTK_PMIC_ADC_CHAN(IBAT, 0, 0, PMIC_AUXADC_IMP1, 15, 128, 7, 2),
};
static const u16 mt6359_auxadc_regs[] = {
@@ -302,6 +349,107 @@ static const u16 mt6359_auxadc_regs[] = {
[PMIC_AUXADC_IMP3] = 0x120e,
};
static const struct iio_chan_spec mt6363_auxadc_channels[] = {
MTK_PMIC_IIO_CHAN(MT6363, bat_adc, BATADC, 0, 15, IIO_RESISTANCE),
MTK_PMIC_IIO_CHAN(MT6363, cdt_v, VCDT, 2, 12, IIO_TEMP),
MTK_PMIC_IIO_CHAN(MT6363, batt_temp, BAT_TEMP, 3, 12, IIO_TEMP),
MTK_PMIC_IIO_CHAN(MT6363, chip_temp, CHIP_TEMP, 4, 12, IIO_TEMP),
MTK_PMIC_IIO_CHAN(MT6363, sys_sns_v, VSYSSNS, 6, 15, IIO_VOLTAGE),
MTK_PMIC_IIO_CHAN(MT6363, tref_v, VTREF, 11, 12, IIO_VOLTAGE),
MTK_PMIC_IIO_CHAN(MT6363, vcore_temp, VCORE_TEMP, 38, 12, IIO_TEMP),
MTK_PMIC_IIO_CHAN(MT6363, vproc_temp, VPROC_TEMP, 39, 12, IIO_TEMP),
MTK_PMIC_IIO_CHAN(MT6363, vgpu_temp, VGPU_TEMP, 40, 12, IIO_TEMP),
/* For VIN, ADC12 holds the result depending on which GPIO was activated */
MTK_PMIC_IIO_CHAN(MT6363, in1_v, VIN1, 45, 15, IIO_VOLTAGE),
MTK_PMIC_IIO_CHAN(MT6363, in2_v, VIN2, 45, 15, IIO_VOLTAGE),
MTK_PMIC_IIO_CHAN(MT6363, in3_v, VIN3, 45, 15, IIO_VOLTAGE),
MTK_PMIC_IIO_CHAN(MT6363, in4_v, VIN4, 45, 15, IIO_VOLTAGE),
MTK_PMIC_IIO_CHAN(MT6363, in5_v, VIN5, 45, 15, IIO_VOLTAGE),
MTK_PMIC_IIO_CHAN(MT6363, in6_v, VIN6, 45, 15, IIO_VOLTAGE),
MTK_PMIC_IIO_CHAN(MT6363, in7_v, VIN7, 45, 15, IIO_VOLTAGE),
};
static const struct mtk_pmic_auxadc_chan mt6363_auxadc_ch_desc[] = {
MTK_PMIC_ADC_CHAN(BATADC, PMIC_AUXADC_RQST0, 0, PMIC_AUXADC_ADC0, 15, 64, 4, 1),
MTK_PMIC_ADC_CHAN(VCDT, PMIC_AUXADC_RQST0, 2, PMIC_AUXADC_ADC0, 15, 32, 1, 1),
MTK_PMIC_ADC_CHAN(BAT_TEMP, PMIC_AUXADC_RQST0, 3, PMIC_AUXADC_ADC0, 15, 32, 3, 2),
MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_ADC0, 15, 32, 1, 1),
MTK_PMIC_ADC_CHAN(VSYSSNS, PMIC_AUXADC_RQST1, 6, PMIC_AUXADC_ADC0, 15, 64, 3, 1),
MTK_PMIC_ADC_CHAN(VTREF, PMIC_AUXADC_RQST1, 3, PMIC_AUXADC_ADC0, 15, 32, 3, 2),
MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST3, 0, PMIC_AUXADC_ADC0, 15, 32, 1, 1),
MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST3, 1, PMIC_AUXADC_ADC0, 15, 32, 1, 1),
MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST3, 2, PMIC_AUXADC_ADC0, 15, 32, 1, 1),
MTK_PMIC_ADC_EXT_CHAN(VIN1,
PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15,
PMIC_AUXADC_SDMADC_CON0, 1, MT6363_PULLUP_RES_100K, 32, 1, 1),
MTK_PMIC_ADC_EXT_CHAN(VIN2,
PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15,
PMIC_AUXADC_SDMADC_CON0, 2, MT6363_PULLUP_RES_100K, 32, 1, 1),
MTK_PMIC_ADC_EXT_CHAN(VIN3,
PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15,
PMIC_AUXADC_SDMADC_CON0, 3, MT6363_PULLUP_RES_100K, 32, 1, 1),
MTK_PMIC_ADC_EXT_CHAN(VIN4,
PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15,
PMIC_AUXADC_SDMADC_CON0, 4, MT6363_PULLUP_RES_100K, 32, 1, 1),
MTK_PMIC_ADC_EXT_CHAN(VIN5,
PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15,
PMIC_AUXADC_SDMADC_CON0, 5, MT6363_PULLUP_RES_100K, 32, 1, 1),
MTK_PMIC_ADC_EXT_CHAN(VIN6,
PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15,
PMIC_AUXADC_SDMADC_CON0, 6, MT6363_PULLUP_RES_100K, 32, 1, 1),
MTK_PMIC_ADC_EXT_CHAN(VIN7,
PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15,
PMIC_AUXADC_SDMADC_CON0, 7, MT6363_PULLUP_RES_100K, 32, 1, 1),
};
static const u16 mt6363_auxadc_regs[] = {
[PMIC_AUXADC_RQST0] = 0x1108,
[PMIC_AUXADC_RQST1] = 0x1109,
[PMIC_AUXADC_RQST3] = 0x110c,
[PMIC_AUXADC_ADC0] = 0x1088,
[PMIC_AUXADC_IMP0] = 0x1208,
[PMIC_AUXADC_IMP1] = 0x1209,
};
static const struct iio_chan_spec mt6373_auxadc_channels[] = {
MTK_PMIC_IIO_CHAN(MT6363, chip_temp, CHIP_TEMP, 4, 12, IIO_TEMP),
MTK_PMIC_IIO_CHAN(MT6363, vcore_temp, VCORE_TEMP, 38, 12, IIO_TEMP),
MTK_PMIC_IIO_CHAN(MT6363, vproc_temp, VPROC_TEMP, 39, 12, IIO_TEMP),
MTK_PMIC_IIO_CHAN(MT6363, vgpu_temp, VGPU_TEMP, 40, 12, IIO_TEMP),
/* For VIN, ADC12 holds the result depending on which GPIO was activated */
MTK_PMIC_IIO_CHAN(MT6363, in1_v, VIN1, 45, 15, IIO_VOLTAGE),
MTK_PMIC_IIO_CHAN(MT6363, in2_v, VIN2, 45, 15, IIO_VOLTAGE),
MTK_PMIC_IIO_CHAN(MT6363, in3_v, VIN3, 45, 15, IIO_VOLTAGE),
MTK_PMIC_IIO_CHAN(MT6363, in4_v, VIN4, 45, 15, IIO_VOLTAGE),
MTK_PMIC_IIO_CHAN(MT6363, in5_v, VIN5, 45, 15, IIO_VOLTAGE),
};
static const struct mtk_pmic_auxadc_chan mt6373_auxadc_ch_desc[] = {
MTK_PMIC_ADC_CHAN(CHIP_TEMP, PMIC_AUXADC_RQST0, 4, PMIC_AUXADC_ADC0, 15, 32, 1, 1),
MTK_PMIC_ADC_CHAN(VCORE_TEMP, PMIC_AUXADC_RQST3, 0, PMIC_AUXADC_ADC0, 15, 32, 1, 1),
MTK_PMIC_ADC_CHAN(VPROC_TEMP, PMIC_AUXADC_RQST3, 1, PMIC_AUXADC_ADC0, 15, 32, 1, 1),
MTK_PMIC_ADC_CHAN(VGPU_TEMP, PMIC_AUXADC_RQST3, 2, PMIC_AUXADC_ADC0, 15, 32, 1, 1),
MTK_PMIC_ADC_EXT_CHAN(VIN1,
PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15,
PMIC_AUXADC_SDMADC_CON0, 1, MT6363_PULLUP_RES_30K, 32, 1, 1),
MTK_PMIC_ADC_EXT_CHAN(VIN2,
PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15,
PMIC_AUXADC_SDMADC_CON0, 2, MT6363_PULLUP_RES_OPEN, 32, 1, 1),
MTK_PMIC_ADC_EXT_CHAN(VIN3,
PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15,
PMIC_AUXADC_SDMADC_CON0, 3, MT6363_PULLUP_RES_OPEN, 32, 1, 1),
MTK_PMIC_ADC_EXT_CHAN(VIN4,
PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15,
PMIC_AUXADC_SDMADC_CON0, 4, MT6363_PULLUP_RES_OPEN, 32, 1, 1),
MTK_PMIC_ADC_EXT_CHAN(VIN5,
PMIC_AUXADC_RQST1, 4, PMIC_AUXADC_ADC0, 15,
PMIC_AUXADC_SDMADC_CON0, 5, MT6363_PULLUP_RES_OPEN, 32, 1, 1),
};
static void mt6358_stop_imp_conv(struct mt6359_auxadc *adc_dev)
{
const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info;
@@ -313,9 +461,10 @@ static void mt6358_stop_imp_conv(struct mt6359_auxadc *adc_dev)
regmap_clear_bits(regmap, cinfo->regs[PMIC_AUXADC_DCM_CON], MT6358_DCM_CK_SW_EN);
}
static int mt6358_start_imp_conv(struct mt6359_auxadc *adc_dev)
static int mt6358_start_imp_conv(struct mt6359_auxadc *adc_dev, const struct iio_chan_spec *chan)
{
const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info;
const struct mtk_pmic_auxadc_chan *desc = &cinfo->desc[chan->scan_index];
struct regmap *regmap = adc_dev->regmap;
u32 val;
int ret;
@@ -323,8 +472,8 @@ static int mt6358_start_imp_conv(struct mt6359_auxadc *adc_dev)
regmap_set_bits(regmap, cinfo->regs[PMIC_AUXADC_DCM_CON], MT6358_DCM_CK_SW_EN);
regmap_set_bits(regmap, cinfo->regs[PMIC_AUXADC_IMP1], MT6358_IMP1_AUTOREPEAT_EN);
ret = regmap_read_poll_timeout(adc_dev->regmap, cinfo->regs[PMIC_AUXADC_IMP0],
val, val & MT6358_IMP0_IRQ_RDY,
ret = regmap_read_poll_timeout(regmap, cinfo->regs[desc->rdy_idx],
val, val & desc->rdy_mask,
IMP_POLL_DELAY_US, AUXADC_TIMEOUT_US);
if (ret) {
mt6358_stop_imp_conv(adc_dev);
@@ -334,7 +483,8 @@ static int mt6358_start_imp_conv(struct mt6359_auxadc *adc_dev)
return 0;
}
static int mt6358_read_imp(struct mt6359_auxadc *adc_dev, int *vbat, int *ibat)
static int mt6358_read_imp(struct mt6359_auxadc *adc_dev,
const struct iio_chan_spec *chan, int *vbat, int *ibat)
{
const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info;
struct regmap *regmap = adc_dev->regmap;
@@ -342,7 +492,7 @@ static int mt6358_read_imp(struct mt6359_auxadc *adc_dev, int *vbat, int *ibat)
u32 val_v;
int ret;
ret = mt6358_start_imp_conv(adc_dev);
ret = mt6358_start_imp_conv(adc_dev, chan);
if (ret)
return ret;
@@ -359,17 +509,19 @@ static int mt6358_read_imp(struct mt6359_auxadc *adc_dev, int *vbat, int *ibat)
return 0;
}
static int mt6359_read_imp(struct mt6359_auxadc *adc_dev, int *vbat, int *ibat)
static int mt6359_read_imp(struct mt6359_auxadc *adc_dev,
const struct iio_chan_spec *chan, int *vbat, int *ibat)
{
const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info;
const struct mtk_pmic_auxadc_chan *desc = &cinfo->desc[chan->scan_index];
struct regmap *regmap = adc_dev->regmap;
u32 val, val_v, val_i;
int ret;
/* Start conversion */
regmap_write(regmap, cinfo->regs[PMIC_AUXADC_IMP0], MT6359_IMP0_CONV_EN);
ret = regmap_read_poll_timeout(regmap, cinfo->regs[PMIC_AUXADC_IMP1],
val, val & MT6359_IMP1_IRQ_RDY,
ret = regmap_read_poll_timeout(regmap, cinfo->regs[desc->rdy_idx],
val, val & desc->rdy_mask,
IMP_POLL_DELAY_US, AUXADC_TIMEOUT_US);
/* Stop conversion regardless of the result */
@@ -404,6 +556,7 @@ static const struct mtk_pmic_auxadc_info mt6357_chip_info = {
.regs = mt6357_auxadc_regs,
.imp_adc_num = MT6357_IMP_ADC_NUM,
.read_imp = mt6358_read_imp,
.vref_mV = 1800,
};
static const struct mtk_pmic_auxadc_info mt6358_chip_info = {
@@ -414,6 +567,7 @@ static const struct mtk_pmic_auxadc_info mt6358_chip_info = {
.regs = mt6358_auxadc_regs,
.imp_adc_num = MT6358_IMP_ADC_NUM,
.read_imp = mt6358_read_imp,
.vref_mV = 1800,
};
static const struct mtk_pmic_auxadc_info mt6359_chip_info = {
@@ -424,6 +578,29 @@ static const struct mtk_pmic_auxadc_info mt6359_chip_info = {
.regs = mt6359_auxadc_regs,
.sec_unlock_key = 0x6359,
.read_imp = mt6359_read_imp,
.vref_mV = 1800,
};
static const struct mtk_pmic_auxadc_info mt6363_chip_info = {
.model_name = "MT6363",
.channels = mt6363_auxadc_channels,
.num_channels = ARRAY_SIZE(mt6363_auxadc_channels),
.desc = mt6363_auxadc_ch_desc,
.regs = mt6363_auxadc_regs,
.is_spmi = true,
.no_reset = true,
.vref_mV = 1840,
};
static const struct mtk_pmic_auxadc_info mt6373_chip_info = {
.model_name = "MT6373",
.channels = mt6373_auxadc_channels,
.num_channels = ARRAY_SIZE(mt6373_auxadc_channels),
.desc = mt6373_auxadc_ch_desc,
.regs = mt6363_auxadc_regs,
.is_spmi = true,
.no_reset = true,
.vref_mV = 1840,
};
static void mt6359_auxadc_reset(struct mt6359_auxadc *adc_dev)
@@ -431,6 +608,10 @@ static void mt6359_auxadc_reset(struct mt6359_auxadc *adc_dev)
const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info;
struct regmap *regmap = adc_dev->regmap;
/* Some PMICs do not support reset */
if (cinfo->no_reset)
return;
/* Unlock HK_TOP writes */
if (cinfo->sec_unlock_key)
regmap_write(regmap, cinfo->regs[PMIC_HK_TOP_WKEY], cinfo->sec_unlock_key);
@@ -446,13 +627,29 @@ static void mt6359_auxadc_reset(struct mt6359_auxadc *adc_dev)
regmap_write(regmap, cinfo->regs[PMIC_HK_TOP_WKEY], 0);
}
static int mt6359_auxadc_read_adc(struct mt6359_auxadc *adc_dev,
const struct iio_chan_spec *chan, int *out)
/**
* mt6359_auxadc_sample_adc_val() - Start ADC channel sampling and read value
* @adc_dev: Main driver structure
* @chan: IIO Channel spec for requested ADC
* @out: Preallocated variable to store the value read from HW
*
* This function starts the sampling for an ADC channel, waits until all
* of the samples are averaged and then reads the value from the HW.
*
* Note that the caller must stop the ADC sampling on its own, as this
* function *never* stops it.
*
* Return:
* Negative number for error;
* Upon success returns zero and writes the read value to *out.
*/
static int mt6359_auxadc_sample_adc_val(struct mt6359_auxadc *adc_dev,
const struct iio_chan_spec *chan, u32 *out)
{
const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info;
const struct mtk_pmic_auxadc_chan *desc = &cinfo->desc[chan->scan_index];
struct regmap *regmap = adc_dev->regmap;
u32 val;
u32 reg, rdy_mask, val, lval;
int ret;
/* Request to start sampling for ADC channel */
@@ -463,16 +660,95 @@ static int mt6359_auxadc_read_adc(struct mt6359_auxadc *adc_dev,
/* Wait until all samples are averaged */
fsleep(desc->num_samples * AUXADC_AVG_TIME_US);
ret = regmap_read_poll_timeout(regmap,
cinfo->regs[PMIC_AUXADC_ADC0] + (chan->address << 1),
val, val & PMIC_AUXADC_RDY_BIT,
reg = cinfo->regs[PMIC_AUXADC_ADC0] + (chan->address << 1);
rdy_mask = PMIC_AUXADC_RDY_BIT;
/*
* Even though for both PWRAP and SPMI cases the ADC HW signals that
* the data is ready by setting AUXADC_RDY_BIT, for SPMI the register
* read is only 8 bits long: for this case, the check has to be done
* on the ADC(x)_H register (high bits) and the rdy_mask needs to be
* shifted to the right by the same 8 bits.
*/
if (cinfo->is_spmi) {
rdy_mask >>= 8;
reg += 1;
}
ret = regmap_read_poll_timeout(regmap, reg, val, val & rdy_mask,
AUXADC_POLL_DELAY_US, AUXADC_TIMEOUT_US);
if (ret) {
dev_dbg(adc_dev->dev, "ADC read timeout for chan %lu\n", chan->address);
return ret;
}
if (cinfo->is_spmi) {
ret = regmap_read(regmap, reg - 1, &lval);
if (ret)
return ret;
val = (val << 8) | lval;
}
*out = val;
return 0;
}
static int mt6359_auxadc_read_adc(struct mt6359_auxadc *adc_dev,
const struct iio_chan_spec *chan, int *out)
{
const struct mtk_pmic_auxadc_info *cinfo = adc_dev->chip_info;
const struct mtk_pmic_auxadc_chan *desc = &cinfo->desc[chan->scan_index];
struct regmap *regmap = adc_dev->regmap;
int ret, adc_stop_err;
u8 ext_sel;
u32 val;
if (desc->ext_sel_idx >= 0) {
ext_sel = FIELD_PREP(MT6363_EXT_PURES_MASK, desc->ext_sel_pu);
ext_sel |= FIELD_PREP(MT6363_EXT_CHAN_MASK, desc->ext_sel_ch);
ret = regmap_update_bits(regmap, cinfo->regs[desc->ext_sel_idx],
MT6363_EXT_PURES_MASK | MT6363_EXT_CHAN_MASK,
ext_sel);
if (ret)
return ret;
}
/*
* Get sampled value, then stop sampling unconditionally; the gathered
* value is good regardless of if the ADC could be stopped.
*
* Note that if the ADC cannot be stopped but sampling was ok, this
* function will not return any error, but will set the timed_out
* status: this is not critical, as the ADC may auto recover and auto
* stop after some time (depending on the PMIC model); if not, the next
* read attempt will return -ETIMEDOUT and, for models that support it,
* reset will be triggered.
*/
ret = mt6359_auxadc_sample_adc_val(adc_dev, chan, &val);
adc_stop_err = regmap_write(regmap, cinfo->regs[desc->req_idx], 0);
if (adc_stop_err) {
dev_warn(adc_dev->dev, "Could not stop the ADC: %d\n,", adc_stop_err);
adc_dev->timed_out = true;
}
/* If any sampling error occurred, the retrieved value is invalid */
if (ret)
return ret;
/* Stop sampling */
regmap_write(regmap, cinfo->regs[desc->req_idx], 0);
/* ...and deactivate the ADC GPIO if previously done */
if (desc->ext_sel_idx >= 0) {
ext_sel = FIELD_PREP(MT6363_EXT_PURES_MASK, MT6363_PULLUP_RES_OPEN);
ret = regmap_update_bits(regmap, cinfo->regs[desc->ext_sel_idx],
MT6363_EXT_PURES_MASK, ext_sel);
if (ret)
return ret;
}
/* Everything went fine, give back the ADC reading */
*out = val & GENMASK(chan->scan_type.realbits - 1, 0);
return 0;
}
@@ -493,7 +769,7 @@ static int mt6359_auxadc_read_raw(struct iio_dev *indio_dev,
int ret;
if (mask == IIO_CHAN_INFO_SCALE) {
*val = desc->r_ratio.numerator * AUXADC_VOLT_FULL;
*val = desc->r_ratio.numerator * cinfo->vref_mV;
if (desc->r_ratio.denominator > 1) {
*val2 = desc->r_ratio.denominator;
@@ -506,10 +782,16 @@ static int mt6359_auxadc_read_raw(struct iio_dev *indio_dev,
scoped_guard(mutex, &adc_dev->lock) {
switch (chan->scan_index) {
case PMIC_AUXADC_CHAN_IBAT:
ret = adc_dev->chip_info->read_imp(adc_dev, NULL, val);
if (!adc_dev->chip_info->read_imp)
return -EOPNOTSUPP;
ret = adc_dev->chip_info->read_imp(adc_dev, chan, NULL, val);
break;
case PMIC_AUXADC_CHAN_VBAT:
ret = adc_dev->chip_info->read_imp(adc_dev, val, NULL);
if (!adc_dev->chip_info->read_imp)
return -EOPNOTSUPP;
ret = adc_dev->chip_info->read_imp(adc_dev, chan, val, NULL);
break;
default:
ret = mt6359_auxadc_read_adc(adc_dev, chan, val);
@@ -543,15 +825,36 @@ static const struct iio_info mt6359_auxadc_iio_info = {
static int mt6359_auxadc_probe(struct platform_device *pdev)
{
const struct mtk_pmic_auxadc_info *chip_info;
struct device *dev = &pdev->dev;
struct device *mt6397_mfd_dev = dev->parent;
struct device *mfd_dev = dev->parent;
struct mt6359_auxadc *adc_dev;
struct iio_dev *indio_dev;
struct device *regmap_dev;
struct regmap *regmap;
int ret;
chip_info = device_get_match_data(dev);
if (!chip_info)
return -EINVAL;
/*
* The regmap for this device has to be acquired differently for
* SoC PMIC Wrapper and SPMI PMIC cases:
*
* If this is under SPMI, the regmap comes from the direct parent of
* this driver: this_device->parent(mfd).
* ... or ...
* If this is under the SoC PMIC Wrapper, the regmap comes from the
* parent of the MT6397 MFD: this_device->parent(mfd)->parent(pwrap)
*/
if (chip_info->is_spmi)
regmap_dev = mfd_dev;
else
regmap_dev = mfd_dev->parent;
/* Regmap is from SoC PMIC Wrapper, parent of the mt6397 MFD */
regmap = dev_get_regmap(mt6397_mfd_dev->parent, NULL);
regmap = dev_get_regmap(regmap_dev, NULL);
if (!regmap)
return dev_err_probe(dev, -ENODEV, "Failed to get regmap\n");
@@ -562,10 +865,7 @@ static int mt6359_auxadc_probe(struct platform_device *pdev)
adc_dev = iio_priv(indio_dev);
adc_dev->regmap = regmap;
adc_dev->dev = dev;
adc_dev->chip_info = device_get_match_data(dev);
if (!adc_dev->chip_info)
return -EINVAL;
adc_dev->chip_info = chip_info;
mutex_init(&adc_dev->lock);
@@ -588,6 +888,8 @@ static const struct of_device_id mt6359_auxadc_of_match[] = {
{ .compatible = "mediatek,mt6357-auxadc", .data = &mt6357_chip_info },
{ .compatible = "mediatek,mt6358-auxadc", .data = &mt6358_chip_info },
{ .compatible = "mediatek,mt6359-auxadc", .data = &mt6359_chip_info },
{ .compatible = "mediatek,mt6363-auxadc", .data = &mt6363_chip_info },
{ .compatible = "mediatek,mt6373-auxadc", .data = &mt6373_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, mt6359_auxadc_of_match);

View File

@@ -264,10 +264,9 @@ static irqreturn_t mt6360_adc_trigger_handler(int irq, void *p)
struct {
u16 values[MT6360_CHAN_MAX];
aligned_s64 timestamp;
} data;
} data = { };
int i = 0, bit, val, ret;
memset(&data, 0, sizeof(data));
iio_for_each_active_channel(indio_dev, bit) {
ret = mt6360_adc_read_channel(mad, bit, &val);
if (ret < 0) {

View File

@@ -330,7 +330,7 @@ static int qcom_vadc7_scale_hw_calib_die_temp(
const struct adc5_data *data,
u16 adc_code, int *result_mdec);
static struct qcom_adc5_scale_type scale_adc5_fn[] = {
static const struct qcom_adc5_scale_type scale_adc5_fn[] = {
[SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt},
[SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm},
[SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm},

View File

@@ -404,12 +404,10 @@ static irqreturn_t rockchip_saradc_trigger_handler(int irq, void *p)
struct {
u16 values[SARADC_MAX_CHANNELS];
aligned_s64 timestamp;
} data;
} data = { };
int ret;
int i, j = 0;
memset(&data, 0, sizeof(data));
mutex_lock(&info->lock);
iio_for_each_active_channel(i_dev, i) {

View File

@@ -645,12 +645,10 @@ static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p)
struct {
u16 vals[RTQ6056_MAX_CHANNEL];
aligned_s64 timestamp;
} data;
} data = { };
unsigned int raw;
int i = 0, bit, ret;
memset(&data, 0, sizeof(data));
pm_runtime_get_sync(dev);
iio_for_each_active_channel(indio_dev, bit) {

View File

@@ -407,7 +407,6 @@ static const struct irq_domain_ops stm32_adc_domain_ops = {
static int stm32_adc_irq_probe(struct platform_device *pdev,
struct stm32_adc_priv *priv)
{
struct device_node *np = pdev->dev.of_node;
unsigned int i;
/*
@@ -421,7 +420,7 @@ static int stm32_adc_irq_probe(struct platform_device *pdev,
return priv->irq[i];
}
priv->domain = irq_domain_create_simple(of_fwnode_handle(np),
priv->domain = irq_domain_create_simple(dev_fwnode(&pdev->dev),
STM32_ADC_MAX_ADCS, 0,
&stm32_adc_domain_ops,
priv);

View File

@@ -216,7 +216,7 @@ struct stm32_adc;
struct stm32_adc_cfg {
const struct stm32_adc_regspec *regs;
const struct stm32_adc_info *adc_info;
struct stm32_adc_trig_info *trigs;
const struct stm32_adc_trig_info *trigs;
bool clk_required;
bool has_vregready;
bool has_boostmode;
@@ -383,7 +383,7 @@ static const struct stm32_adc_regs stm32f4_sq[STM32_ADC_MAX_SQ + 1] = {
};
/* STM32F4 external trigger sources for all instances */
static struct stm32_adc_trig_info stm32f4_adc_trigs[] = {
static const struct stm32_adc_trig_info stm32f4_adc_trigs[] = {
{ TIM1_CH1, STM32_EXT0 },
{ TIM1_CH2, STM32_EXT1 },
{ TIM1_CH3, STM32_EXT2 },
@@ -473,7 +473,7 @@ static const struct stm32_adc_regs stm32h7_sq[STM32_ADC_MAX_SQ + 1] = {
};
/* STM32H7 external trigger sources for all instances */
static struct stm32_adc_trig_info stm32h7_adc_trigs[] = {
static const struct stm32_adc_trig_info stm32h7_adc_trigs[] = {
{ TIM1_CH1, STM32_EXT0 },
{ TIM1_CH2, STM32_EXT1 },
{ TIM1_CH3, STM32_EXT2 },
@@ -2470,7 +2470,7 @@ static int stm32_adc_chan_fw_init(struct iio_dev *indio_dev, bool timestamping)
static int stm32_adc_dma_request(struct device *dev, struct iio_dev *indio_dev)
{
struct stm32_adc *adc = iio_priv(indio_dev);
struct dma_slave_config config;
struct dma_slave_config config = { };
int ret;
adc->dma_chan = dma_request_chan(dev, "rx");
@@ -2494,7 +2494,6 @@ static int stm32_adc_dma_request(struct device *dev, struct iio_dev *indio_dev)
}
/* Configure DMA channel to read data register */
memset(&config, 0, sizeof(config));
config.src_addr = (dma_addr_t)adc->common->phys_base;
config.src_addr += adc->offset + adc->cfg->regs->dr;
config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;

View File

@@ -8,6 +8,7 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/export.h>
#include <linux/iio/adc/stm32-dfsdm-adc.h>
#include <linux/iio/backend.h>
#include <linux/iio/buffer.h>

View File

@@ -8,6 +8,7 @@
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/export.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/interrupt.h>

View File

@@ -450,11 +450,9 @@ static irqreturn_t ads1015_trigger_handler(int irq, void *p)
struct {
s16 chan;
aligned_s64 timestamp;
} scan;
} scan = { };
int chan, ret, res;
memset(&scan, 0, sizeof(scan));
mutex_lock(&data->lock);
chan = find_first_bit(indio_dev->active_scan_mask,
iio_get_masklength(indio_dev));

View File

@@ -507,12 +507,10 @@ static irqreturn_t ads1119_trigger_handler(int irq, void *private)
struct {
s16 sample;
aligned_s64 timestamp;
} scan;
} scan = { };
unsigned int index;
int ret;
memset(&scan, 0, sizeof(scan));
if (!iio_trigger_using_own(indio_dev)) {
index = find_first_bit(indio_dev->active_scan_mask,
iio_get_masklength(indio_dev));

View File

@@ -625,7 +625,7 @@ static irqreturn_t ads131e08_trigger_handler(int irq, void *private)
* 16 bits of data into the buffer.
*/
unsigned int num_bytes = ADS131E08_NUM_DATA_BYTES(st->data_rate);
u8 tweek_offset = num_bytes == 2 ? 1 : 0;
u8 tweak_offset = num_bytes == 2 ? 1 : 0;
if (iio_trigger_using_own(indio_dev))
ret = ads131e08_read_data(st, st->readback_len);
@@ -640,25 +640,25 @@ static irqreturn_t ads131e08_trigger_handler(int irq, void *private)
dest = st->tmp_buf.data + i * ADS131E08_NUM_STORAGE_BYTES;
/*
* Tweek offset is 0:
* Tweak offset is 0:
* +---+---+---+---+
* |D0 |D1 |D2 | X | (3 data bytes)
* +---+---+---+---+
* a+0 a+1 a+2 a+3
*
* Tweek offset is 1:
* Tweak offset is 1:
* +---+---+---+---+
* |P0 |D0 |D1 | X | (one padding byte and 2 data bytes)
* +---+---+---+---+
* a+0 a+1 a+2 a+3
*/
memcpy(dest + tweek_offset, src, num_bytes);
memcpy(dest + tweak_offset, src, num_bytes);
/*
* Data conversion from 16 bits of data to 24 bits of data
* is done by sign extension (properly filling padding byte).
*/
if (tweek_offset)
if (tweak_offset)
*dest = *src & BIT(7) ? 0xff : 0x00;
i++;

View File

@@ -200,11 +200,9 @@ static irqreturn_t lmp92064_trigger_handler(int irq, void *p)
struct {
u16 values[2];
aligned_s64 timestamp;
} data;
} data = { };
int ret;
memset(&data, 0, sizeof(data));
ret = lmp92064_read_meas(priv, data.values);
if (ret)
goto err;

View File

@@ -276,7 +276,7 @@ static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx,
struct tsc2046_adc_ch_cfg *ch = &priv->ch_cfg[ch_idx];
unsigned int val, val_normalized = 0;
int ret, i, count_skip = 0, max_count;
struct spi_transfer xfer;
struct spi_transfer xfer = { };
struct spi_message msg;
u8 cmd;
@@ -314,7 +314,6 @@ static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx,
/* automatically power down on last sample */
tx_buf[i].cmd = tsc2046_adc_get_cmd(priv, ch_idx, false);
memset(&xfer, 0, sizeof(xfer));
xfer.tx_buf = tx_buf;
xfer.rx_buf = rx_buf;
xfer.len = sizeof(*tx_buf) * max_count;

View File

@@ -28,9 +28,6 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
/* This will be the driver name the kernel reports */
#define DRIVER_NAME "vf610-adc"
/* Vybrid/IMX ADC registers */
#define VF610_REG_ADC_HC0 0x00
#define VF610_REG_ADC_HC1 0x04
@@ -952,7 +949,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, vf610_adc_suspend,
static struct platform_driver vf610_adc_driver = {
.probe = vf610_adc_probe,
.driver = {
.name = DRIVER_NAME,
.name = "vf610-adc",
.of_match_table = vf610_adc_match,
.pm = pm_sleep_ptr(&vf610_adc_pm_ops),
},

View File

@@ -45,7 +45,7 @@ struct ad8366_state {
struct gpio_desc *reset_gpio;
unsigned char ch[2];
enum ad8366_type type;
struct ad8366_info *info;
const struct ad8366_info *info;
/*
* DMA (thus cache coherency maintenance) may require the
* transfer buffers to live in their own cache lines.
@@ -53,7 +53,7 @@ struct ad8366_state {
unsigned char data[2] __aligned(IIO_DMA_MINALIGN);
};
static struct ad8366_info ad8366_infos[] = {
static const struct ad8366_info ad8366_infos[] = {
[ID_AD8366] = {
.gain_min = 4500,
.gain_max = 20500,
@@ -163,7 +163,7 @@ static int ad8366_write_raw(struct iio_dev *indio_dev,
long mask)
{
struct ad8366_state *st = iio_priv(indio_dev);
struct ad8366_info *inf = st->info;
const struct ad8366_info *inf = st->info;
int code = 0, gain;
int ret;

View File

@@ -13,8 +13,8 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/unaligned.h>
#include <linux/types.h>
#include <linux/units.h>
/* ADA4250 Register Map */
#define ADA4250_REG_GAIN_MUX 0x00
@@ -56,13 +56,14 @@ enum ada4250_current_bias {
struct ada4250_state {
struct spi_device *spi;
struct regmap *regmap;
struct regulator *reg;
/* Protect against concurrent accesses to the device and data content */
struct mutex lock;
int avdd_uv;
int offset_uv;
u8 bias;
u8 gain;
int offset_uv;
bool refbuf_en;
__le16 reg_val_16 __aligned(IIO_DMA_MINALIGN);
};
/* ADA4250 Current Bias Source Settings: Disabled, Bandgap Reference, AVDD */
@@ -91,8 +92,7 @@ static int ada4250_set_offset_uv(struct iio_dev *indio_dev,
if (st->bias == 0 || st->bias == 3)
return -EINVAL;
voltage_v = regulator_get_voltage(st->reg);
voltage_v = DIV_ROUND_CLOSEST(voltage_v, 1000000);
voltage_v = DIV_ROUND_CLOSEST(st->avdd_uv, MICRO);
if (st->bias == ADA4250_BIAS_AVDD)
x[0] = voltage_v;
@@ -292,50 +292,33 @@ static const struct iio_chan_spec ada4250_channels[] = {
}
};
static void ada4250_reg_disable(void *data)
{
regulator_disable(data);
}
static int ada4250_init(struct ada4250_state *st)
{
struct device *dev = &st->spi->dev;
int ret;
u16 chip_id;
u8 data[2] __aligned(8) = {};
struct spi_device *spi = st->spi;
st->refbuf_en = device_property_read_bool(&spi->dev, "adi,refbuf-enable");
st->refbuf_en = device_property_read_bool(dev, "adi,refbuf-enable");
st->reg = devm_regulator_get(&spi->dev, "avdd");
if (IS_ERR(st->reg))
return dev_err_probe(&spi->dev, PTR_ERR(st->reg),
st->avdd_uv = devm_regulator_get_enable_read_voltage(dev, "avdd");
if (st->avdd_uv < 0)
return dev_err_probe(dev, st->avdd_uv,
"failed to get the AVDD voltage\n");
ret = regulator_enable(st->reg);
if (ret) {
dev_err(&spi->dev, "Failed to enable specified AVDD supply\n");
return ret;
}
ret = devm_add_action_or_reset(&spi->dev, ada4250_reg_disable, st->reg);
if (ret)
return ret;
ret = regmap_write(st->regmap, ADA4250_REG_RESET,
FIELD_PREP(ADA4250_RESET_MSK, 1));
if (ret)
return ret;
ret = regmap_bulk_read(st->regmap, ADA4250_REG_CHIP_ID, data, 2);
ret = regmap_bulk_read(st->regmap, ADA4250_REG_CHIP_ID, &st->reg_val_16,
sizeof(st->reg_val_16));
if (ret)
return ret;
chip_id = get_unaligned_le16(data);
chip_id = le16_to_cpu(st->reg_val_16);
if (chip_id != ADA4250_CHIP_ID) {
dev_err(&spi->dev, "Invalid chip ID.\n");
return -EINVAL;
}
if (chip_id != ADA4250_CHIP_ID)
dev_info(dev, "Invalid chip ID: 0x%02X.\n", chip_id);
return regmap_write(st->regmap, ADA4250_REG_REFBUF_EN,
FIELD_PREP(ADA4250_REFBUF_MSK, st->refbuf_en));
@@ -368,10 +351,8 @@ static int ada4250_probe(struct spi_device *spi)
mutex_init(&st->lock);
ret = ada4250_init(st);
if (ret) {
dev_err(&spi->dev, "ADA4250 init failed\n");
return ret;
}
if (ret)
return dev_err_probe(&spi->dev, ret, "ADA4250 init failed\n");
return devm_iio_device_register(&spi->dev, indio_dev);
}

View File

@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
/*
* Copyright (c) 2012 Analog Devices, Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*/

View File

@@ -82,7 +82,7 @@ static const struct iio_chan_spec atlas_hum_ezo_channels[] = {
},
};
static struct atlas_ezo_device atlas_ezo_devices[] = {
static const struct atlas_ezo_device atlas_ezo_devices[] = {
[ATLAS_CO2_EZO] = {
.channels = atlas_co2_ezo_channels,
.num_channels = 1,

View File

@@ -24,7 +24,6 @@
#include <linux/iio/triggered_buffer.h>
#include <linux/pm_runtime.h>
#define ATLAS_REGMAP_NAME "atlas_regmap"
#define ATLAS_DRV_NAME "atlas"
#define ATLAS_REG_DEV_TYPE 0x00
@@ -96,7 +95,7 @@ struct atlas_data {
};
static const struct regmap_config atlas_regmap_config = {
.name = ATLAS_REGMAP_NAME,
.name = "atlas_regmap",
.reg_bits = 8,
.val_bits = 8,
};

View File

@@ -158,7 +158,7 @@ const struct regmap_config bme680_regmap_config = {
.val_bits = 8,
.max_register = 0xef,
.volatile_table = &bme680_volatile_table,
.cache_type = REGCACHE_RBTREE,
.cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_NS(bme680_regmap_config, "IIO_BME680");

View File

@@ -587,7 +587,7 @@ static irqreturn_t scd30_trigger_handler(int irq, void *p)
struct {
int data[SCD30_MEAS_COUNT];
aligned_s64 ts;
} scan;
} scan = { };
int ret;
mutex_lock(&state->lock);
@@ -595,7 +595,6 @@ static irqreturn_t scd30_trigger_handler(int irq, void *p)
ret = scd30_read_poll(state);
else
ret = scd30_read_meas(state);
memset(&scan, 0, sizeof(scan));
memcpy(scan.data, state->meas, sizeof(state->meas));
mutex_unlock(&state->lock);
if (ret)

View File

@@ -665,10 +665,9 @@ static irqreturn_t scd4x_trigger_handler(int irq, void *p)
struct {
uint16_t data[3];
aligned_s64 ts;
} scan;
} scan = { };
int ret;
memset(&scan, 0, sizeof(scan));
mutex_lock(&state->lock);
ret = scd4x_read_poll(state, scan.data);
mutex_unlock(&state->lock);

View File

@@ -51,13 +51,12 @@ static int sunrise_regmap_read(void *context, const void *reg_buf,
{
struct i2c_client *client = context;
struct sunrise_dev *sunrise = i2c_get_clientdata(client);
union i2c_smbus_data data;
union i2c_smbus_data data = { };
int ret;
if (reg_size != 1 || !val_size)
return -EINVAL;
memset(&data, 0, sizeof(data));
data.block[0] = val_size;
/*
@@ -88,14 +87,13 @@ static int sunrise_regmap_write(void *context, const void *val_buf, size_t count
{
struct i2c_client *client = context;
struct sunrise_dev *sunrise = i2c_get_clientdata(client);
union i2c_smbus_data data;
union i2c_smbus_data data = { };
/* Discard reg address from values count. */
if (!count)
return -EINVAL;
count--;
memset(&data, 0, sizeof(data));
data.block[0] = count;
memcpy(&data.block[1], (u8 *)val_buf + 1, count);

View File

@@ -30,3 +30,12 @@ config IIO_CROS_EC_SENSORS_LID_ANGLE
convertible devices.
This module is loaded when the EC can calculate the angle between the base
and the lid.
config IIO_CROS_EC_ACTIVITY
tristate "ChromeOS EC Activity Sensors"
depends on IIO_CROS_EC_SENSORS_CORE
help
Module to handle activity events presented by the ChromeOS EC sensor hub.
Activities can be a proximity detector (on body/off body detection)
or a significant motion detector.
Creates an IIO device to manage all activities.

View File

@@ -7,3 +7,4 @@ cros-ec-sensors-core-objs += cros_ec_sensors_core.o cros_ec_sensors_trace.o
obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros-ec-sensors-core.o
obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o
obj-$(CONFIG_IIO_CROS_EC_SENSORS_LID_ANGLE) += cros_ec_lid_angle.o
obj-$(CONFIG_IIO_CROS_EC_ACTIVITY) += cros_ec_activity.o

View File

@@ -0,0 +1,307 @@
// SPDX-License-Identifier: GPL-2.0
/*
* cros_ec_activity - Driver for activities/gesture recognition.
*
* Copyright 2025 Google, Inc
*
* This driver uses the cros-ec interface to communicate with the ChromeOS
* EC about activity data.
*/
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/iio/common/cros_ec_sensors_core.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#define DRV_NAME "cros-ec-activity"
/* state data for ec_sensors iio driver. */
struct cros_ec_sensors_state {
/* Shared by all sensors */
struct cros_ec_sensors_core_state core;
struct iio_chan_spec *channels;
int body_detection_channel_index;
int sig_motion_channel_index;
};
static const struct iio_event_spec cros_ec_activity_single_shot[] = {
{
.type = IIO_EV_TYPE_CHANGE,
/* significant motion trigger when we get out of still. */
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
},
};
static const struct iio_event_spec cros_ec_body_detect_events[] = {
{
.type = IIO_EV_TYPE_CHANGE,
.dir = IIO_EV_DIR_EITHER,
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
},
};
static int cros_ec_activity_sensors_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct cros_ec_sensors_state *st = iio_priv(indio_dev);
int ret;
if (chan->type != IIO_PROXIMITY || mask != IIO_CHAN_INFO_RAW)
return -EINVAL;
guard(mutex)(&st->core.cmd_lock);
st->core.param.cmd = MOTIONSENSE_CMD_GET_ACTIVITY;
st->core.param.get_activity.activity =
MOTIONSENSE_ACTIVITY_BODY_DETECTION;
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
if (ret)
return ret;
/*
* EC actually report if a body is near (1) or far (0).
* Units for proximity sensor after scale is in meter,
* so invert the result to return 0m when near and 1m when far.
*/
*val = !st->core.resp->get_activity.state;
return IIO_VAL_INT;
}
static int cros_ec_activity_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir)
{
struct cros_ec_sensors_state *st = iio_priv(indio_dev);
int ret;
if (chan->type != IIO_ACTIVITY && chan->type != IIO_PROXIMITY)
return -EINVAL;
guard(mutex)(&st->core.cmd_lock);
st->core.param.cmd = MOTIONSENSE_CMD_LIST_ACTIVITIES;
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
if (ret)
return ret;
switch (chan->type) {
case IIO_PROXIMITY:
return !!(st->core.resp->list_activities.enabled &
(1 << MOTIONSENSE_ACTIVITY_BODY_DETECTION));
case IIO_ACTIVITY:
if (chan->channel2 == IIO_MOD_STILL) {
return !!(st->core.resp->list_activities.enabled &
(1 << MOTIONSENSE_ACTIVITY_SIG_MOTION));
}
dev_warn(&indio_dev->dev, "Unknown activity: %d\n",
chan->channel2);
return -EINVAL;
default:
dev_warn(&indio_dev->dev, "Unknown channel type: %d\n",
chan->type);
return -EINVAL;
}
}
static int cros_ec_activity_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
bool state)
{
struct cros_ec_sensors_state *st = iio_priv(indio_dev);
guard(mutex)(&st->core.cmd_lock);
st->core.param.cmd = MOTIONSENSE_CMD_SET_ACTIVITY;
switch (chan->type) {
case IIO_PROXIMITY:
st->core.param.set_activity.activity =
MOTIONSENSE_ACTIVITY_BODY_DETECTION;
break;
case IIO_ACTIVITY:
if (chan->channel2 == IIO_MOD_STILL) {
st->core.param.set_activity.activity =
MOTIONSENSE_ACTIVITY_SIG_MOTION;
break;
}
dev_warn(&indio_dev->dev, "Unknown activity: %d\n",
chan->channel2);
return -EINVAL;
default:
dev_warn(&indio_dev->dev, "Unknown channel type: %d\n",
chan->type);
return -EINVAL;
}
st->core.param.set_activity.enable = state;
return cros_ec_motion_send_host_cmd(&st->core, 0);
}
static int cros_ec_activity_push_data(struct iio_dev *indio_dev,
s16 *data, s64 timestamp)
{
struct ec_response_activity_data *activity_data =
(struct ec_response_activity_data *)data;
enum motionsensor_activity activity = activity_data->activity;
u8 state = activity_data->state;
const struct cros_ec_sensors_state *st = iio_priv(indio_dev);
const struct iio_chan_spec *chan;
enum iio_event_direction dir;
int index;
switch (activity) {
case MOTIONSENSE_ACTIVITY_BODY_DETECTION:
index = st->body_detection_channel_index;
dir = state ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING;
break;
case MOTIONSENSE_ACTIVITY_SIG_MOTION:
index = st->sig_motion_channel_index;
dir = IIO_EV_DIR_FALLING;
break;
default:
dev_warn(&indio_dev->dev, "Unknown activity: %d\n", activity);
return 0;
}
chan = &st->channels[index];
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(chan->type, index, chan->event_spec[0].type, dir),
timestamp);
return 0;
}
static irqreturn_t cros_ec_activity_capture(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
/*
* This callback would be called when a software trigger is
* used. But when this virtual sensor is present, it is guaranteed
* the sensor hub is advanced enough to not need a software trigger.
*/
dev_warn(&indio_dev->dev, "%s: Not Expected\n", __func__);
return IRQ_NONE;
}
static const struct iio_info ec_sensors_info = {
.read_raw = &cros_ec_activity_sensors_read_raw,
.read_event_config = cros_ec_activity_read_event_config,
.write_event_config = cros_ec_activity_write_event_config,
};
static int cros_ec_sensors_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct cros_ec_device *ec_device = dev_get_drvdata(dev->parent);
struct iio_dev *indio_dev;
struct cros_ec_sensors_state *st;
struct iio_chan_spec *channel;
unsigned long activities;
int i, index, ret, nb_activities;
if (!ec_device) {
dev_warn(dev, "No CROS EC device found.\n");
return -EINVAL;
}
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
cros_ec_activity_capture);
if (ret)
return ret;
indio_dev->info = &ec_sensors_info;
st = iio_priv(indio_dev);
st->core.type = st->core.resp->info.type;
st->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
st->core.param.cmd = MOTIONSENSE_CMD_LIST_ACTIVITIES;
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
if (ret)
return ret;
activities = st->core.resp->list_activities.enabled |
st->core.resp->list_activities.disabled;
if (!activities)
return -ENODEV;
/* Allocate a channel per activity and one for timestamp */
nb_activities = hweight_long(activities) + 1;
st->channels = devm_kcalloc(dev, nb_activities,
sizeof(*st->channels), GFP_KERNEL);
if (!st->channels)
return -ENOMEM;
channel = &st->channels[0];
index = 0;
for_each_set_bit(i, &activities, BITS_PER_LONG) {
/* List all available triggers */
if (i == MOTIONSENSE_ACTIVITY_BODY_DETECTION) {
channel->type = IIO_PROXIMITY;
channel->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
channel->event_spec = cros_ec_body_detect_events;
channel->num_event_specs =
ARRAY_SIZE(cros_ec_body_detect_events);
st->body_detection_channel_index = index;
} else {
channel->type = IIO_ACTIVITY;
channel->modified = 1;
channel->event_spec = cros_ec_activity_single_shot;
channel->num_event_specs =
ARRAY_SIZE(cros_ec_activity_single_shot);
if (i == MOTIONSENSE_ACTIVITY_SIG_MOTION) {
channel->channel2 = IIO_MOD_STILL;
st->sig_motion_channel_index = index;
} else {
dev_warn(dev, "Unknown activity: %d\n", i);
continue;
}
}
channel->ext_info = cros_ec_sensors_limited_info;
channel->scan_index = index++;
channel++;
}
/* Timestamp */
channel->scan_index = index;
channel->type = IIO_TIMESTAMP;
channel->channel = -1;
channel->scan_type.sign = 's';
channel->scan_type.realbits = 64;
channel->scan_type.storagebits = 64;
indio_dev->channels = st->channels;
indio_dev->num_channels = index + 1;
return cros_ec_sensors_core_register(dev, indio_dev,
cros_ec_activity_push_data);
}
static struct platform_driver cros_ec_sensors_platform_driver = {
.driver = {
.name = DRV_NAME,
},
.probe = cros_ec_sensors_probe,
};
module_platform_driver(cros_ec_sensors_platform_driver);
MODULE_DESCRIPTION("ChromeOS EC activity sensors driver");
MODULE_ALIAS("platform:" DRV_NAME);
MODULE_LICENSE("GPL v2");

View File

@@ -486,6 +486,16 @@ const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[] = {
};
EXPORT_SYMBOL_GPL(cros_ec_sensors_ext_info);
const struct iio_chan_spec_ext_info cros_ec_sensors_limited_info[] = {
{
.name = "id",
.shared = IIO_SHARED_BY_ALL,
.read = cros_ec_sensors_id
},
{ }
};
EXPORT_SYMBOL_GPL(cros_ec_sensors_limited_info);
/**
* cros_ec_sensors_idx_to_reg - convert index into offset in shared memory
* @st: pointer to state information for device

View File

@@ -11,7 +11,7 @@
#include <linux/hid-sensor-hub.h>
#include <linux/iio/iio.h>
static struct {
static const struct {
u32 usage_id;
int unit; /* 0 for default others from HID sensor spec */
int scale_val0; /* scale, whole number */

View File

@@ -293,10 +293,9 @@ 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_MAX_CH * AD3552R_MAX_REG_SIZE];
u8 buff[AD3552R_MAX_CH * AD3552R_MAX_REG_SIZE] = { };
int err;
memset(buff, 0, sizeof(buff));
err = iio_pop_from_buffer(buf, buff);
if (err)
goto end;

View File

@@ -426,7 +426,7 @@ static const struct regmap_config ad5380_regmap_config = {
.val_bits = 14,
.max_register = AD5380_REG_DATA(40),
.cache_type = REGCACHE_RBTREE,
.cache_type = REGCACHE_MAPLE,
.volatile_reg = ad5380_reg_false,
.readable_reg = ad5380_reg_false,

View File

@@ -155,7 +155,7 @@ struct ad5770r_output_modes {
int max;
};
static struct ad5770r_output_modes ad5770r_rng_tbl[] = {
static const struct ad5770r_output_modes ad5770r_rng_tbl[] = {
{ 0, AD5770R_CH0_0_300, 0, 300 },
{ 0, AD5770R_CH0_NEG_60_0, -60, 0 },
{ 0, AD5770R_CH0_NEG_60_300, -60, 300 },

View File

@@ -635,15 +635,26 @@ static int axi_dac_ddr_disable(struct iio_backend *back)
AXI_DAC_CNTRL_2_SDR_DDR_N);
}
static int axi_dac_wait_bus_free(struct axi_dac_state *st)
{
u32 val;
int ret;
ret = regmap_read_poll_timeout(st->regmap, AXI_DAC_UI_STATUS_REG, val,
FIELD_GET(AXI_DAC_UI_STATUS_IF_BUSY, val) == 0, 10,
100 * KILO);
if (ret == -ETIMEDOUT)
dev_err(st->dev, "AXI bus timeout\n");
return ret;
}
static int axi_dac_data_stream_enable(struct iio_backend *back)
{
struct axi_dac_state *st = iio_backend_get_priv(back);
int ret, val;
int ret;
ret = regmap_read_poll_timeout(st->regmap,
AXI_DAC_UI_STATUS_REG, val,
FIELD_GET(AXI_DAC_UI_STATUS_IF_BUSY, val) == 0,
10, 100 * KILO);
ret = axi_dac_wait_bus_free(st);
if (ret)
return ret;
@@ -734,12 +745,9 @@ static int __axi_dac_bus_reg_write(struct iio_backend *back, u32 reg,
if (ret)
return ret;
ret = regmap_read_poll_timeout(st->regmap,
AXI_DAC_UI_STATUS_REG, ival,
FIELD_GET(AXI_DAC_UI_STATUS_IF_BUSY, ival) == 0,
10, 100 * KILO);
if (ret == -ETIMEDOUT)
dev_err(st->dev, "AXI read timeout\n");
ret = axi_dac_wait_bus_free(st);
if (ret)
return ret;
/* Cleaning always AXI_DAC_CUSTOM_CTRL_TRANSFER_DATA */
return regmap_clear_bits(st->regmap, AXI_DAC_CUSTOM_CTRL_REG,
@@ -760,7 +768,6 @@ static int axi_dac_bus_reg_read(struct iio_backend *back, u32 reg, u32 *val,
{
struct axi_dac_state *st = iio_backend_get_priv(back);
int ret;
u32 ival;
guard(mutex)(&st->lock);
@@ -773,10 +780,7 @@ static int axi_dac_bus_reg_read(struct iio_backend *back, u32 reg, u32 *val,
if (ret)
return ret;
ret = regmap_read_poll_timeout(st->regmap,
AXI_DAC_UI_STATUS_REG, ival,
FIELD_GET(AXI_DAC_UI_STATUS_IF_BUSY, ival) == 0,
10, 100 * KILO);
ret = axi_dac_wait_bus_free(st);
if (ret)
return ret;
@@ -787,7 +791,7 @@ static int axi_dac_bus_set_io_mode(struct iio_backend *back,
enum ad3552r_io_mode mode)
{
struct axi_dac_state *st = iio_backend_get_priv(back);
int ival, ret;
int ret;
if (mode > AD3552R_IO_MODE_QSPI)
return -EINVAL;
@@ -800,9 +804,7 @@ static int axi_dac_bus_set_io_mode(struct iio_backend *back,
if (ret)
return ret;
return regmap_read_poll_timeout(st->regmap, AXI_DAC_UI_STATUS_REG, ival,
FIELD_GET(AXI_DAC_UI_STATUS_IF_BUSY, ival) == 0, 10,
100 * KILO);
return axi_dac_wait_bus_free(st);
}
static void axi_dac_child_remove(void *data)

View File

@@ -622,7 +622,7 @@ static const struct iio_chan_spec_ext_info ltc2688_toggle_ext_info[] = {
{ }
};
static struct iio_chan_spec_ext_info ltc2688_dither_ext_info[] = {
static const struct iio_chan_spec_ext_info ltc2688_dither_ext_info[] = {
LTC2688_CHAN_EXT_INFO("dither_raw", LTC2688_INPUT_B, IIO_SEPARATE,
ltc2688_dac_input_read, ltc2688_dac_input_write),
LTC2688_CHAN_EXT_INFO("dither_raw_available", LTC2688_INPUT_B_AVAIL,

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