Char / Misc / IIO / other driver updates for 6.17-rc1

Here is the big set of char/misc/iio and other smaller driver subsystems
 for 6.17-rc1.  It's a big set this time around, with the huge majority
 being in the iio subsystem with new drivers and dts files being added
 there.
 
 Highlights include:
   - IIO driver updates, additions, and changes making more code const
     and cleaning up some init logic
   - bus_type constant conversion changes
   - misc device test functions added
   - rust miscdevice minor fixup
   - unused function removals for some drivers
   - mei driver updates
   - mhi driver updates
   - interconnect driver updates
   - Android binder updates and test infrastructure added
   - small cdx driver updates
   - small comedi fixes
   - small nvmem driver updates
   - small pps driver updates
   - some acrn virt driver fixes for printk messages
   - other small driver updates
 
 All of these have been in linux-next with no reported issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCaIeYDg8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+yk3dwCdF6xoEVZaDkLM5IF3XKWWgdYxjNsAoKUy2kUx
 YtmVh4S0tMmugfeRGac7
 =3Wxi
 -----END PGP SIGNATURE-----

Merge tag 'char-misc-6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char / misc / IIO / other driver updates from Greg KH:
 "Here is the big set of char/misc/iio and other smaller driver
  subsystems for 6.17-rc1. It's a big set this time around, with the
  huge majority being in the iio subsystem with new drivers and dts
  files being added there.

  Highlights include:
   - IIO driver updates, additions, and changes making more code const
     and cleaning up some init logic
   - bus_type constant conversion changes
   - misc device test functions added
   - rust miscdevice minor fixup
   - unused function removals for some drivers
   - mei driver updates
   - mhi driver updates
   - interconnect driver updates
   - Android binder updates and test infrastructure added
   - small cdx driver updates
   - small comedi fixes
   - small nvmem driver updates
   - small pps driver updates
   - some acrn virt driver fixes for printk messages
   - other small driver updates

  All of these have been in linux-next with no reported issues"

* tag 'char-misc-6.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (292 commits)
  binder: Use seq_buf in binder_alloc kunit tests
  binder: Add copyright notice to new kunit files
  misc: ti_fpc202: Switch to of_fwnode_handle()
  bus: moxtet: Use dev_fwnode()
  pc104: move PC104 option to drivers/Kconfig
  drivers: virt: acrn: Don't use %pK through printk
  comedi: fix race between polling and detaching
  interconnect: qcom: Add Milos interconnect provider driver
  dt-bindings: interconnect: document the RPMh Network-On-Chip Interconnect in Qualcomm Milos SoC
  mei: more prints with client prefix
  mei: bus: use cldev in prints
  bus: mhi: host: pci_generic: Add Telit FN990B40 modem support
  bus: mhi: host: Detect events pointing to unexpected TREs
  bus: mhi: host: pci_generic: Add Foxconn T99W696 modem
  bus: mhi: host: Use str_true_false() helper
  bus: mhi: host: pci_generic: Add support for EM929x and set MRU to 32768 for better performance.
  bus: mhi: host: Fix endianness of BHI vector table
  bus: mhi: host: pci_generic: Disable runtime PM for QDU100
  bus: mhi: host: pci_generic: Fix the modem name of Foxconn T99W640
  dt-bindings: interconnect: qcom,msm8998-bwmon: Allow 'nonposted-mmio'
  ...
This commit is contained in:
Linus Torvalds 2025-07-29 09:52:01 -07:00
commit 0d5ec7919f
281 changed files with 15268 additions and 2542 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
@ -250,6 +259,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:
@ -392,6 +420,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

@ -0,0 +1,136 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/interconnect/qcom,milos-rpmh.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Qualcomm RPMh Network-On-Chip Interconnect on Milos SoC
maintainers:
- Luca Weiss <luca.weiss@fairphone.com>
description: |
RPMh interconnect providers support system bandwidth requirements through
RPMh hardware accelerators known as Bus Clock Manager (BCM). The provider is
able to communicate with the BCM through the Resource State Coordinator (RSC)
associated with each execution environment. Provider nodes must point to at
least one RPMh device child node pertaining to their RSC and each provider
can map to multiple RPMh resources.
See also: include/dt-bindings/interconnect/qcom,milos-rpmh.h
properties:
compatible:
enum:
- qcom,milos-aggre1-noc
- qcom,milos-aggre2-noc
- qcom,milos-clk-virt
- qcom,milos-cnoc-cfg
- qcom,milos-cnoc-main
- qcom,milos-gem-noc
- qcom,milos-lpass-ag-noc
- qcom,milos-mc-virt
- qcom,milos-mmss-noc
- qcom,milos-nsp-noc
- qcom,milos-pcie-anoc
- qcom,milos-system-noc
reg:
maxItems: 1
clocks:
minItems: 1
maxItems: 2
required:
- compatible
allOf:
- $ref: qcom,rpmh-common.yaml#
- if:
properties:
compatible:
contains:
enum:
- qcom,milos-clk-virt
- qcom,milos-mc-virt
then:
properties:
reg: false
else:
required:
- reg
- if:
properties:
compatible:
contains:
enum:
- qcom,milos-pcie-anoc
then:
properties:
clocks:
items:
- description: aggre-NOC PCIe AXI clock
- description: cfg-NOC PCIe a-NOC AHB clock
- if:
properties:
compatible:
contains:
enum:
- qcom,milos-aggre1-noc
then:
properties:
clocks:
items:
- description: aggre USB3 PRIM AXI clock
- description: aggre UFS PHY AXI clock
- if:
properties:
compatible:
contains:
enum:
- qcom,milos-aggre2-noc
then:
properties:
clocks:
items:
- description: RPMH CC IPA clock
- if:
properties:
compatible:
contains:
enum:
- qcom,milos-aggre1-noc
- qcom,milos-aggre2-noc
- qcom,milos-pcie-anoc
then:
required:
- clocks
else:
properties:
clocks: false
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/qcom,milos-gcc.h>
interconnect-0 {
compatible = "qcom,milos-clk-virt";
#interconnect-cells = <2>;
qcom,bcm-voters = <&apps_bcm_voter>;
};
interconnect@16e0000 {
compatible = "qcom,milos-aggre1-noc";
reg = <0x016e0000 0x16400>;
#interconnect-cells = <2>;
clocks = <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
<&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>;
qcom,bcm-voters = <&apps_bcm_voter>;
};

View file

@ -76,6 +76,8 @@ properties:
minItems: 1
maxItems: 2
nonposted-mmio: true
required:
- compatible
- interconnects

View file

@ -36,6 +36,11 @@ properties:
- qcom,sm8350-epss-l3
- qcom,sm8650-epss-l3
- const: qcom,epss-l3
- items:
- enum:
- qcom,qcs8300-epss-l3
- const: qcom,sa8775p-epss-l3
- const: qcom,epss-l3
reg:
maxItems: 1

View file

@ -13,7 +13,7 @@ description: |
RPMh interconnect providers support system bandwidth requirements through
RPMh hardware accelerators known as Bus Clock Manager (BCM).
See also:: include/dt-bindings/interconnect/qcom,sa8775p.h
See also: include/dt-bindings/interconnect/qcom,sa8775p.h
properties:
compatible:

View file

@ -18,7 +18,7 @@ description: |
least one RPMh device child node pertaining to their RSC and each provider
can map to multiple RPMh resources.
See also:: include/dt-bindings/interconnect/qcom,sar2130p-rpmh.h
See also: include/dt-bindings/interconnect/qcom,sar2130p-rpmh.h
properties:
compatible:

View file

@ -14,7 +14,7 @@ description: |
RPMh interconnect providers support system bandwidth requirements through
RPMh hardware accelerators known as Bus Clock Manager (BCM).
See also:: include/dt-bindings/interconnect/qcom,sc7280.h
See also: include/dt-bindings/interconnect/qcom,sc7280.h
properties:
compatible:

View file

@ -14,7 +14,7 @@ description: |
RPMh interconnect providers support system bandwidth requirements through
RPMh hardware accelerators known as Bus Clock Manager (BCM).
See also:: include/dt-bindings/interconnect/qcom,sc8280xp.h
See also: include/dt-bindings/interconnect/qcom,sc8280xp.h
properties:
compatible:

View file

@ -13,7 +13,7 @@ description: |
RPMh interconnect providers support system bandwidth requirements through
RPMh hardware accelerators known as Bus Clock Manager (BCM).
See also:: include/dt-bindings/interconnect/qcom,sm7150-rpmh.h
See also: include/dt-bindings/interconnect/qcom,sm7150-rpmh.h
allOf:
- $ref: qcom,rpmh-common.yaml#

View file

@ -14,7 +14,7 @@ description: |
RPMh interconnect providers support system bandwidth requirements through
RPMh hardware accelerators known as Bus Clock Manager (BCM).
See also:: include/dt-bindings/interconnect/qcom,sm8450.h
See also: include/dt-bindings/interconnect/qcom,sm8450.h
properties:
compatible:

View file

@ -18,7 +18,7 @@ description: |
least one RPMh device child node pertaining to their RSC and each provider
can map to multiple RPMh resources.
See also:: include/dt-bindings/interconnect/qcom,sm8550-rpmh.h
See also: include/dt-bindings/interconnect/qcom,sm8550-rpmh.h
properties:
compatible:

View file

@ -18,7 +18,7 @@ description: |
least one RPMh device child node pertaining to their RSC and each provider
can map to multiple RPMh resources.
See also:: include/dt-bindings/interconnect/qcom,sm8650-rpmh.h
See also: include/dt-bindings/interconnect/qcom,sm8650-rpmh.h
properties:
compatible:

View file

@ -18,7 +18,7 @@ description: |
least one RPMh device child node pertaining to their RSC and each provider
can map to multiple RPMh resources.
See also:: include/dt-bindings/interconnect/qcom,sm8750-rpmh.h
See also: include/dt-bindings/interconnect/qcom,sm8750-rpmh.h
properties:
compatible:

View file

@ -18,7 +18,7 @@ description: |
least one RPMh device child node pertaining to their RSC and each provider
can map to multiple RPMh resources.
See also:: include/dt-bindings/interconnect/qcom,x1e80100-rpmh.h
See also: include/dt-bindings/interconnect/qcom,x1e80100-rpmh.h
properties:
compatible:

View file

@ -27,6 +27,7 @@ properties:
- enum:
- allwinner,sun50i-a100-sid
- allwinner,sun50i-h616-sid
- allwinner,sun55i-a523-sid
- const: allwinner,sun50i-a64-sid
- const: allwinner,sun50i-h5-sid
- const: allwinner,sun50i-h6-sid

View file

@ -0,0 +1,47 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/nvmem/fsl,vf610-ocotp.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: On-Chip OTP Memory for Freescale Vybrid
maintainers:
- Frank Li <Frank.Li@nxp.com>
allOf:
- $ref: nvmem.yaml#
- $ref: nvmem-deprecated-cells.yaml
properties:
compatible:
items:
- enum:
- fsl,vf610-ocotp
- const: syscon
reg:
maxItems: 1
clocks:
items:
- description: ipg clock we associate with the OCOTP peripheral
required:
- compatible
- reg
- clocks
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/vf610-clock.h>
ocotp@400a5000 {
compatible = "fsl,vf610-ocotp", "syscon";
reg = <0x400a5000 0xcf0>;
#address-cells = <1>;
#size-cells = <1>;
clocks = <&clks VF610_CLK_OCOTP>;
};

View file

@ -27,7 +27,7 @@ properties:
const: 1
patternProperties:
"@[a-f0-9]+$":
"@[a-f0-9]+(,[0-7])?$":
type: object
$ref: fixed-cell.yaml
unevaluatedProperties: false

View file

@ -1,28 +0,0 @@
* NXP LPC18xx EEPROM memory NVMEM driver
Required properties:
- compatible: Should be "nxp,lpc1857-eeprom"
- reg: Must contain an entry with the physical base address and length
for each entry in reg-names.
- reg-names: Must include the following entries.
- reg: EEPROM registers.
- mem: EEPROM address space.
- clocks: Must contain an entry for each entry in clock-names.
- clock-names: Must include the following entries.
- eeprom: EEPROM operating clock.
- resets: Should contain a reference to the reset controller asserting
the EEPROM in reset.
- interrupts: Should contain EEPROM interrupt.
Example:
eeprom: eeprom@4000e000 {
compatible = "nxp,lpc1857-eeprom";
reg = <0x4000e000 0x1000>,
<0x20040000 0x4000>;
reg-names = "reg", "mem";
clocks = <&ccu1 CLK_CPU_EEPROM>;
clock-names = "eeprom";
resets = <&rgu 27>;
interrupts = <4>;
};

View file

@ -24,6 +24,21 @@ properties:
compatible:
oneOf:
- items:
- const: mediatek,mt8188-efuse
- const: mediatek,mt8186-efuse
- const: mediatek,mt8186-efuse
- items:
- enum:
- mediatek,mt8186-efuse
- mediatek,mt8188-efuse
- const: mediatek,efuse
deprecated: true
description: Some compatibles also imply a decoding scheme for the
"gpu-speedbin" cell, and thus are not backward compatible to the
generic "mediatek,efuse" compatible.
- items:
- enum:
- mediatek,mt7622-efuse
@ -33,8 +48,6 @@ properties:
- mediatek,mt7988-efuse
- mediatek,mt8173-efuse
- mediatek,mt8183-efuse
- mediatek,mt8186-efuse
- mediatek,mt8188-efuse
- mediatek,mt8192-efuse
- mediatek,mt8195-efuse
- mediatek,mt8516-efuse

View file

@ -0,0 +1,61 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/nvmem/nxp,lpc1857-eeprom.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP LPC18xx EEPROM memory
maintainers:
- Frank Li <Frank.Li@nxp.com>
properties:
compatible:
const: nxp,lpc1857-eeprom
reg:
maxItems: 2
reg-names:
items:
- const: reg
- const: mem
clocks:
maxItems: 1
clock-names:
items:
- const: eeprom
interrupts:
maxItems: 1
resets:
maxItems: 1
required:
- compatible
- reg
- reg-names
- clocks
- clock-names
- interrupts
- resets
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/lpc18xx-ccu.h>
eeprom@4000e000 {
compatible = "nxp,lpc1857-eeprom";
reg = <0x4000e000 0x1000>,
<0x20040000 0x4000>;
reg-names = "reg", "mem";
clocks = <&ccu1 CLK_CPU_EEPROM>;
clock-names = "eeprom";
resets = <&rgu 27>;
interrupts = <4>;
};

View file

@ -1,19 +0,0 @@
On-Chip OTP Memory for Freescale Vybrid
Required Properties:
compatible:
- "fsl,vf610-ocotp", "syscon" for VF5xx/VF6xx
#address-cells : Should be 1
#size-cells : Should be 1
reg : Address and length of OTP controller and fuse map registers
clocks : ipg clock we associate with the OCOTP peripheral
Example for Vybrid VF5xx/VF6xx:
ocotp: ocotp@400a5000 {
compatible = "fsl,vf610-ocotp", "syscon";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x400a5000 0xCF0>;
clocks = <&clks VF610_CLK_OCOTP>;
};

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

@ -1069,6 +1069,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

@ -1385,6 +1385,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
@ -1394,6 +1402,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>
@ -1492,6 +1508,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>
@ -9443,7 +9460,7 @@ K: \bunsafe_memcpy\b
K: \b__NO_FORTIFY\b
FPGA DFL DRIVERS
M: Wu Hao <hao.wu@intel.com>
M: Xu Yilun <yilun.xu@intel.com>
R: Tom Rix <trix@redhat.com>
L: linux-fpga@vger.kernel.org
S: Maintained
@ -9456,7 +9473,6 @@ F: include/uapi/linux/fpga-dfl.h
FPGA MANAGER FRAMEWORK
M: Moritz Fischer <mdf@kernel.org>
M: Wu Hao <hao.wu@intel.com>
M: Xu Yilun <yilun.xu@intel.com>
R: Tom Rix <trix@redhat.com>
L: linux-fpga@vger.kernel.org
@ -23611,7 +23627,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)
@ -25278,9 +25293,10 @@ M: David Lechner <dlechner@baylibre.com>
S: Maintained
F: Documentation/devicetree/bindings/trigger-source/adi,util-sigma-delta-spi.yaml
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

@ -10,6 +10,12 @@ source "drivers/cxl/Kconfig"
source "drivers/pcmcia/Kconfig"
source "drivers/rapidio/Kconfig"
config PC104
bool "PC/104 support" if EXPERT
help
Expose PC/104 form factor device drivers and options available for
selection and configuration. Enable this option if your target
machine has a PC/104 bus.
source "drivers/base/Kconfig"

View file

@ -37,14 +37,15 @@ config ANDROID_BINDER_DEVICES
created. Each binder device has its own context manager, and is
therefore logically separated from the other devices.
config ANDROID_BINDER_IPC_SELFTEST
bool "Android Binder IPC Driver Selftest"
depends on ANDROID_BINDER_IPC
config ANDROID_BINDER_ALLOC_KUNIT_TEST
tristate "KUnit Tests for Android Binder Alloc" if !KUNIT_ALL_TESTS
depends on ANDROID_BINDER_IPC && KUNIT
default KUNIT_ALL_TESTS
help
This feature allows binder selftest to run.
This feature builds the binder alloc KUnit tests.
Binder selftest checks the allocation and free of binder buffers
exhaustively with combinations of various buffer sizes and
alignments.
Each test case runs using a pared-down binder_alloc struct and
test-specific freelist, which allows this KUnit module to be loaded
for testing without interfering with a running system.
endmenu

View file

@ -3,4 +3,4 @@ ccflags-y += -I$(src) # needed for trace events
obj-$(CONFIG_ANDROID_BINDERFS) += binderfs.o
obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o binder_alloc.o
obj-$(CONFIG_ANDROID_BINDER_IPC_SELFTEST) += binder_alloc_selftest.o
obj-$(CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST) += tests/

View file

@ -68,6 +68,8 @@
#include <linux/sizes.h>
#include <linux/ktime.h>
#include <kunit/visibility.h>
#include <uapi/linux/android/binder.h>
#include <linux/cacheflush.h>
@ -1585,11 +1587,10 @@ static struct binder_thread *binder_get_txn_from(
{
struct binder_thread *from;
spin_lock(&t->lock);
guard(spinlock)(&t->lock);
from = t->from;
if (from)
atomic_inc(&from->tmp_ref);
spin_unlock(&t->lock);
return from;
}
@ -3144,10 +3145,8 @@ static void binder_transaction(struct binder_proc *proc,
}
if (!target_node) {
binder_txn_error("%d:%d cannot find target node\n",
thread->pid, proc->pid);
/*
* return_error is set above
*/
proc->pid, thread->pid);
/* return_error is set above */
return_error_param = -EINVAL;
return_error_line = __LINE__;
goto err_dead_binder;
@ -5437,32 +5436,28 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp,
struct binder_node *new_node;
kuid_t curr_euid = current_euid();
mutex_lock(&context->context_mgr_node_lock);
guard(mutex)(&context->context_mgr_node_lock);
if (context->binder_context_mgr_node) {
pr_err("BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto out;
return -EBUSY;
}
ret = security_binder_set_context_mgr(proc->cred);
if (ret < 0)
goto out;
return ret;
if (uid_valid(context->binder_context_mgr_uid)) {
if (!uid_eq(context->binder_context_mgr_uid, curr_euid)) {
pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
from_kuid(&init_user_ns, curr_euid),
from_kuid(&init_user_ns,
context->binder_context_mgr_uid));
ret = -EPERM;
goto out;
return -EPERM;
}
} else {
context->binder_context_mgr_uid = curr_euid;
}
new_node = binder_new_node(proc, fbo);
if (!new_node) {
ret = -ENOMEM;
goto out;
}
if (!new_node)
return -ENOMEM;
binder_node_lock(new_node);
new_node->local_weak_refs++;
new_node->local_strong_refs++;
@ -5471,8 +5466,6 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp,
context->binder_context_mgr_node = new_node;
binder_node_unlock(new_node);
binder_put_node(new_node);
out:
mutex_unlock(&context->context_mgr_node_lock);
return ret;
}
@ -5708,11 +5701,6 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
struct binder_thread *thread;
void __user *ubuf = (void __user *)arg;
/*pr_info("binder_ioctl: %d:%d %x %lx\n",
proc->pid, current->pid, cmd, arg);*/
binder_selftest_alloc(&proc->alloc);
trace_binder_ioctl(cmd, arg);
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
@ -5948,10 +5936,11 @@ static void binder_vma_close(struct vm_area_struct *vma)
binder_alloc_vma_close(&proc->alloc);
}
static vm_fault_t binder_vm_fault(struct vm_fault *vmf)
VISIBLE_IF_KUNIT vm_fault_t binder_vm_fault(struct vm_fault *vmf)
{
return VM_FAULT_SIGBUS;
}
EXPORT_SYMBOL_IF_KUNIT(binder_vm_fault);
static const struct vm_operations_struct binder_vm_ops = {
.open = binder_vma_open,
@ -6314,14 +6303,13 @@ static DECLARE_WORK(binder_deferred_work, binder_deferred_func);
static void
binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer)
{
mutex_lock(&binder_deferred_lock);
guard(mutex)(&binder_deferred_lock);
proc->deferred_work |= defer;
if (hlist_unhashed(&proc->deferred_work_node)) {
hlist_add_head(&proc->deferred_work_node,
&binder_deferred_list);
schedule_work(&binder_deferred_work);
}
mutex_unlock(&binder_deferred_lock);
}
static void print_binder_transaction_ilocked(struct seq_file *m,
@ -6863,14 +6851,13 @@ static int proc_show(struct seq_file *m, void *unused)
struct binder_proc *itr;
int pid = (unsigned long)m->private;
mutex_lock(&binder_procs_lock);
guard(mutex)(&binder_procs_lock);
hlist_for_each_entry(itr, &binder_procs, proc_node) {
if (itr->pid == pid) {
seq_puts(m, "binder proc state:\n");
print_binder_proc(m, itr, true, false);
}
}
mutex_unlock(&binder_procs_lock);
return 0;
}
@ -6988,16 +6975,14 @@ const struct binder_debugfs_entry binder_debugfs_entries[] = {
void binder_add_device(struct binder_device *device)
{
spin_lock(&binder_devices_lock);
guard(spinlock)(&binder_devices_lock);
hlist_add_head(&device->hlist, &binder_devices);
spin_unlock(&binder_devices_lock);
}
void binder_remove_device(struct binder_device *device)
{
spin_lock(&binder_devices_lock);
guard(spinlock)(&binder_devices_lock);
hlist_del_init(&device->hlist);
spin_unlock(&binder_devices_lock);
}
static int __init init_binder_device(const char *name)

View file

@ -23,10 +23,11 @@
#include <linux/uaccess.h>
#include <linux/highmem.h>
#include <linux/sizes.h>
#include <kunit/visibility.h>
#include "binder_alloc.h"
#include "binder_trace.h"
struct list_lru binder_freelist;
static struct list_lru binder_freelist;
static DEFINE_MUTEX(binder_alloc_mmap_lock);
@ -57,13 +58,14 @@ static struct binder_buffer *binder_buffer_prev(struct binder_buffer *buffer)
return list_entry(buffer->entry.prev, struct binder_buffer, entry);
}
static size_t binder_alloc_buffer_size(struct binder_alloc *alloc,
struct binder_buffer *buffer)
VISIBLE_IF_KUNIT size_t binder_alloc_buffer_size(struct binder_alloc *alloc,
struct binder_buffer *buffer)
{
if (list_is_last(&buffer->entry, &alloc->buffers))
return alloc->vm_start + alloc->buffer_size - buffer->user_data;
return binder_buffer_next(buffer)->user_data - buffer->user_data;
}
EXPORT_SYMBOL_IF_KUNIT(binder_alloc_buffer_size);
static void binder_insert_free_buffer(struct binder_alloc *alloc,
struct binder_buffer *new_buffer)
@ -167,12 +169,8 @@ static struct binder_buffer *binder_alloc_prepare_to_free_locked(
struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc,
unsigned long user_ptr)
{
struct binder_buffer *buffer;
mutex_lock(&alloc->mutex);
buffer = binder_alloc_prepare_to_free_locked(alloc, user_ptr);
mutex_unlock(&alloc->mutex);
return buffer;
guard(mutex)(&alloc->mutex);
return binder_alloc_prepare_to_free_locked(alloc, user_ptr);
}
static inline void
@ -210,7 +208,7 @@ static void binder_lru_freelist_add(struct binder_alloc *alloc,
trace_binder_free_lru_start(alloc, index);
ret = list_lru_add(&binder_freelist,
ret = list_lru_add(alloc->freelist,
page_to_lru(page),
page_to_nid(page),
NULL);
@ -409,7 +407,7 @@ static void binder_lru_freelist_del(struct binder_alloc *alloc,
if (page) {
trace_binder_alloc_lru_start(alloc, index);
on_lru = list_lru_del(&binder_freelist,
on_lru = list_lru_del(alloc->freelist,
page_to_lru(page),
page_to_nid(page),
NULL);
@ -699,6 +697,7 @@ struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
out:
return buffer;
}
EXPORT_SYMBOL_IF_KUNIT(binder_alloc_new_buf);
static unsigned long buffer_start_page(struct binder_buffer *buffer)
{
@ -877,6 +876,7 @@ void binder_alloc_free_buf(struct binder_alloc *alloc,
binder_free_buf_locked(alloc, buffer);
mutex_unlock(&alloc->mutex);
}
EXPORT_SYMBOL_IF_KUNIT(binder_alloc_free_buf);
/**
* binder_alloc_mmap_handler() - map virtual address space for proc
@ -959,7 +959,7 @@ err_invalid_mm:
failure_string, ret);
return ret;
}
EXPORT_SYMBOL_IF_KUNIT(binder_alloc_mmap_handler);
void binder_alloc_deferred_release(struct binder_alloc *alloc)
{
@ -1007,7 +1007,7 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)
if (!page)
continue;
on_lru = list_lru_del(&binder_freelist,
on_lru = list_lru_del(alloc->freelist,
page_to_lru(page),
page_to_nid(page),
NULL);
@ -1028,6 +1028,7 @@ void binder_alloc_deferred_release(struct binder_alloc *alloc)
"%s: %d buffers %d, pages %d\n",
__func__, alloc->pid, buffers, page_count);
}
EXPORT_SYMBOL_IF_KUNIT(binder_alloc_deferred_release);
/**
* binder_alloc_print_allocated() - print buffer info
@ -1043,7 +1044,7 @@ void binder_alloc_print_allocated(struct seq_file *m,
struct binder_buffer *buffer;
struct rb_node *n;
mutex_lock(&alloc->mutex);
guard(mutex)(&alloc->mutex);
for (n = rb_first(&alloc->allocated_buffers); n; n = rb_next(n)) {
buffer = rb_entry(n, struct binder_buffer, rb_node);
seq_printf(m, " buffer %d: %lx size %zd:%zd:%zd %s\n",
@ -1053,7 +1054,6 @@ void binder_alloc_print_allocated(struct seq_file *m,
buffer->extra_buffers_size,
buffer->transaction ? "active" : "delivered");
}
mutex_unlock(&alloc->mutex);
}
/**
@ -1102,10 +1102,9 @@ int binder_alloc_get_allocated_count(struct binder_alloc *alloc)
struct rb_node *n;
int count = 0;
mutex_lock(&alloc->mutex);
guard(mutex)(&alloc->mutex);
for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n))
count++;
mutex_unlock(&alloc->mutex);
return count;
}
@ -1122,6 +1121,7 @@ void binder_alloc_vma_close(struct binder_alloc *alloc)
{
binder_alloc_set_mapped(alloc, false);
}
EXPORT_SYMBOL_IF_KUNIT(binder_alloc_vma_close);
/**
* binder_alloc_free_page() - shrinker callback to free pages
@ -1213,6 +1213,7 @@ err_mmap_read_lock_failed:
err_mmget:
return LRU_SKIP;
}
EXPORT_SYMBOL_IF_KUNIT(binder_alloc_free_page);
static unsigned long
binder_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
@ -1229,6 +1230,18 @@ binder_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
static struct shrinker *binder_shrinker;
VISIBLE_IF_KUNIT void __binder_alloc_init(struct binder_alloc *alloc,
struct list_lru *freelist)
{
alloc->pid = current->group_leader->pid;
alloc->mm = current->mm;
mmgrab(alloc->mm);
mutex_init(&alloc->mutex);
INIT_LIST_HEAD(&alloc->buffers);
alloc->freelist = freelist;
}
EXPORT_SYMBOL_IF_KUNIT(__binder_alloc_init);
/**
* binder_alloc_init() - called by binder_open() for per-proc initialization
* @alloc: binder_alloc for this proc
@ -1238,11 +1251,7 @@ static struct shrinker *binder_shrinker;
*/
void binder_alloc_init(struct binder_alloc *alloc)
{
alloc->pid = current->group_leader->pid;
alloc->mm = current->mm;
mmgrab(alloc->mm);
mutex_init(&alloc->mutex);
INIT_LIST_HEAD(&alloc->buffers);
__binder_alloc_init(alloc, &binder_freelist);
}
int binder_alloc_shrinker_init(void)

View file

@ -15,7 +15,6 @@
#include <linux/list_lru.h>
#include <uapi/linux/android/binder.h>
extern struct list_lru binder_freelist;
struct binder_transaction;
/**
@ -91,6 +90,7 @@ static inline struct list_head *page_to_lru(struct page *p)
* @free_async_space: VA space available for async buffers. This is
* initialized at mmap time to 1/2 the full VA space
* @pages: array of struct page *
* @freelist: lru list to use for free pages (invariant after init)
* @buffer_size: size of address space specified via mmap
* @pid: pid for associated binder_proc (invariant after init)
* @pages_high: high watermark of offset in @pages
@ -113,6 +113,7 @@ struct binder_alloc {
struct rb_root allocated_buffers;
size_t free_async_space;
struct page **pages;
struct list_lru *freelist;
size_t buffer_size;
int pid;
size_t pages_high;
@ -120,11 +121,6 @@ struct binder_alloc {
bool oneway_spam_detected;
};
#ifdef CONFIG_ANDROID_BINDER_IPC_SELFTEST
void binder_selftest_alloc(struct binder_alloc *alloc);
#else
static inline void binder_selftest_alloc(struct binder_alloc *alloc) {}
#endif
enum lru_status binder_alloc_free_page(struct list_head *item,
struct list_lru_one *lru,
void *cb_arg);
@ -160,12 +156,8 @@ void binder_alloc_print_pages(struct seq_file *m,
static inline size_t
binder_alloc_get_free_async_space(struct binder_alloc *alloc)
{
size_t free_async_space;
mutex_lock(&alloc->mutex);
free_async_space = alloc->free_async_space;
mutex_unlock(&alloc->mutex);
return free_async_space;
guard(mutex)(&alloc->mutex);
return alloc->free_async_space;
}
unsigned long
@ -187,5 +179,11 @@ int binder_alloc_copy_from_buffer(struct binder_alloc *alloc,
binder_size_t buffer_offset,
size_t bytes);
#if IS_ENABLED(CONFIG_KUNIT)
void __binder_alloc_init(struct binder_alloc *alloc, struct list_lru *freelist);
size_t binder_alloc_buffer_size(struct binder_alloc *alloc,
struct binder_buffer *buffer);
#endif
#endif /* _LINUX_BINDER_ALLOC_H */

View file

@ -1,306 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-only
/* binder_alloc_selftest.c
*
* Android IPC Subsystem
*
* Copyright (C) 2017 Google, Inc.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/mm_types.h>
#include <linux/err.h>
#include "binder_alloc.h"
#define BUFFER_NUM 5
#define BUFFER_MIN_SIZE (PAGE_SIZE / 8)
static bool binder_selftest_run = true;
static int binder_selftest_failures;
static DEFINE_MUTEX(binder_selftest_lock);
/**
* enum buf_end_align_type - Page alignment of a buffer
* end with regard to the end of the previous buffer.
*
* In the pictures below, buf2 refers to the buffer we
* are aligning. buf1 refers to previous buffer by addr.
* Symbol [ means the start of a buffer, ] means the end
* of a buffer, and | means page boundaries.
*/
enum buf_end_align_type {
/**
* @SAME_PAGE_UNALIGNED: The end of this buffer is on
* the same page as the end of the previous buffer and
* is not page aligned. Examples:
* buf1 ][ buf2 ][ ...
* buf1 ]|[ buf2 ][ ...
*/
SAME_PAGE_UNALIGNED = 0,
/**
* @SAME_PAGE_ALIGNED: When the end of the previous buffer
* is not page aligned, the end of this buffer is on the
* same page as the end of the previous buffer and is page
* aligned. When the previous buffer is page aligned, the
* end of this buffer is aligned to the next page boundary.
* Examples:
* buf1 ][ buf2 ]| ...
* buf1 ]|[ buf2 ]| ...
*/
SAME_PAGE_ALIGNED,
/**
* @NEXT_PAGE_UNALIGNED: The end of this buffer is on
* the page next to the end of the previous buffer and
* is not page aligned. Examples:
* buf1 ][ buf2 | buf2 ][ ...
* buf1 ]|[ buf2 | buf2 ][ ...
*/
NEXT_PAGE_UNALIGNED,
/**
* @NEXT_PAGE_ALIGNED: The end of this buffer is on
* the page next to the end of the previous buffer and
* is page aligned. Examples:
* buf1 ][ buf2 | buf2 ]| ...
* buf1 ]|[ buf2 | buf2 ]| ...
*/
NEXT_PAGE_ALIGNED,
/**
* @NEXT_NEXT_UNALIGNED: The end of this buffer is on
* the page that follows the page after the end of the
* previous buffer and is not page aligned. Examples:
* buf1 ][ buf2 | buf2 | buf2 ][ ...
* buf1 ]|[ buf2 | buf2 | buf2 ][ ...
*/
NEXT_NEXT_UNALIGNED,
/**
* @LOOP_END: The number of enum values in &buf_end_align_type.
* It is used for controlling loop termination.
*/
LOOP_END,
};
static void pr_err_size_seq(size_t *sizes, int *seq)
{
int i;
pr_err("alloc sizes: ");
for (i = 0; i < BUFFER_NUM; i++)
pr_cont("[%zu]", sizes[i]);
pr_cont("\n");
pr_err("free seq: ");
for (i = 0; i < BUFFER_NUM; i++)
pr_cont("[%d]", seq[i]);
pr_cont("\n");
}
static bool check_buffer_pages_allocated(struct binder_alloc *alloc,
struct binder_buffer *buffer,
size_t size)
{
unsigned long page_addr;
unsigned long end;
int page_index;
end = PAGE_ALIGN(buffer->user_data + size);
page_addr = buffer->user_data;
for (; page_addr < end; page_addr += PAGE_SIZE) {
page_index = (page_addr - alloc->vm_start) / PAGE_SIZE;
if (!alloc->pages[page_index] ||
!list_empty(page_to_lru(alloc->pages[page_index]))) {
pr_err("expect alloc but is %s at page index %d\n",
alloc->pages[page_index] ?
"lru" : "free", page_index);
return false;
}
}
return true;
}
static void binder_selftest_alloc_buf(struct binder_alloc *alloc,
struct binder_buffer *buffers[],
size_t *sizes, int *seq)
{
int i;
for (i = 0; i < BUFFER_NUM; i++) {
buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0);
if (IS_ERR(buffers[i]) ||
!check_buffer_pages_allocated(alloc, buffers[i],
sizes[i])) {
pr_err_size_seq(sizes, seq);
binder_selftest_failures++;
}
}
}
static void binder_selftest_free_buf(struct binder_alloc *alloc,
struct binder_buffer *buffers[],
size_t *sizes, int *seq, size_t end)
{
int i;
for (i = 0; i < BUFFER_NUM; i++)
binder_alloc_free_buf(alloc, buffers[seq[i]]);
for (i = 0; i < end / PAGE_SIZE; i++) {
/**
* Error message on a free page can be false positive
* if binder shrinker ran during binder_alloc_free_buf
* calls above.
*/
if (list_empty(page_to_lru(alloc->pages[i]))) {
pr_err_size_seq(sizes, seq);
pr_err("expect lru but is %s at page index %d\n",
alloc->pages[i] ? "alloc" : "free", i);
binder_selftest_failures++;
}
}
}
static void binder_selftest_free_page(struct binder_alloc *alloc)
{
int i;
unsigned long count;
while ((count = list_lru_count(&binder_freelist))) {
list_lru_walk(&binder_freelist, binder_alloc_free_page,
NULL, count);
}
for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) {
if (alloc->pages[i]) {
pr_err("expect free but is %s at page index %d\n",
list_empty(page_to_lru(alloc->pages[i])) ?
"alloc" : "lru", i);
binder_selftest_failures++;
}
}
}
static void binder_selftest_alloc_free(struct binder_alloc *alloc,
size_t *sizes, int *seq, size_t end)
{
struct binder_buffer *buffers[BUFFER_NUM];
binder_selftest_alloc_buf(alloc, buffers, sizes, seq);
binder_selftest_free_buf(alloc, buffers, sizes, seq, end);
/* Allocate from lru. */
binder_selftest_alloc_buf(alloc, buffers, sizes, seq);
if (list_lru_count(&binder_freelist))
pr_err("lru list should be empty but is not\n");
binder_selftest_free_buf(alloc, buffers, sizes, seq, end);
binder_selftest_free_page(alloc);
}
static bool is_dup(int *seq, int index, int val)
{
int i;
for (i = 0; i < index; i++) {
if (seq[i] == val)
return true;
}
return false;
}
/* Generate BUFFER_NUM factorial free orders. */
static void binder_selftest_free_seq(struct binder_alloc *alloc,
size_t *sizes, int *seq,
int index, size_t end)
{
int i;
if (index == BUFFER_NUM) {
binder_selftest_alloc_free(alloc, sizes, seq, end);
return;
}
for (i = 0; i < BUFFER_NUM; i++) {
if (is_dup(seq, index, i))
continue;
seq[index] = i;
binder_selftest_free_seq(alloc, sizes, seq, index + 1, end);
}
}
static void binder_selftest_alloc_size(struct binder_alloc *alloc,
size_t *end_offset)
{
int i;
int seq[BUFFER_NUM] = {0};
size_t front_sizes[BUFFER_NUM];
size_t back_sizes[BUFFER_NUM];
size_t last_offset, offset = 0;
for (i = 0; i < BUFFER_NUM; i++) {
last_offset = offset;
offset = end_offset[i];
front_sizes[i] = offset - last_offset;
back_sizes[BUFFER_NUM - i - 1] = front_sizes[i];
}
/*
* Buffers share the first or last few pages.
* Only BUFFER_NUM - 1 buffer sizes are adjustable since
* we need one giant buffer before getting to the last page.
*/
back_sizes[0] += alloc->buffer_size - end_offset[BUFFER_NUM - 1];
binder_selftest_free_seq(alloc, front_sizes, seq, 0,
end_offset[BUFFER_NUM - 1]);
binder_selftest_free_seq(alloc, back_sizes, seq, 0, alloc->buffer_size);
}
static void binder_selftest_alloc_offset(struct binder_alloc *alloc,
size_t *end_offset, int index)
{
int align;
size_t end, prev;
if (index == BUFFER_NUM) {
binder_selftest_alloc_size(alloc, end_offset);
return;
}
prev = index == 0 ? 0 : end_offset[index - 1];
end = prev;
BUILD_BUG_ON(BUFFER_MIN_SIZE * BUFFER_NUM >= PAGE_SIZE);
for (align = SAME_PAGE_UNALIGNED; align < LOOP_END; align++) {
if (align % 2)
end = ALIGN(end, PAGE_SIZE);
else
end += BUFFER_MIN_SIZE;
end_offset[index] = end;
binder_selftest_alloc_offset(alloc, end_offset, index + 1);
}
}
/**
* binder_selftest_alloc() - Test alloc and free of buffer pages.
* @alloc: Pointer to alloc struct.
*
* Allocate BUFFER_NUM buffers to cover all page alignment cases,
* then free them in all orders possible. Check that pages are
* correctly allocated, put onto lru when buffers are freed, and
* are freed when binder_alloc_free_page is called.
*/
void binder_selftest_alloc(struct binder_alloc *alloc)
{
size_t end_offset[BUFFER_NUM];
if (!binder_selftest_run)
return;
mutex_lock(&binder_selftest_lock);
if (!binder_selftest_run || !alloc->mapped)
goto done;
pr_info("STARTED\n");
binder_selftest_alloc_offset(alloc, end_offset, 0);
binder_selftest_run = false;
if (binder_selftest_failures > 0)
pr_info("%d tests FAILED\n", binder_selftest_failures);
else
pr_info("PASSED\n");
done:
mutex_unlock(&binder_selftest_lock);
}

View file

@ -590,4 +590,8 @@ void binder_add_device(struct binder_device *device);
*/
void binder_remove_device(struct binder_device *device);
#if IS_ENABLED(CONFIG_KUNIT)
vm_fault_t binder_vm_fault(struct vm_fault *vmf);
#endif
#endif /* _LINUX_BINDER_INTERNAL_H */

View file

@ -34,27 +34,6 @@ TRACE_EVENT(binder_ioctl,
TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg)
);
DECLARE_EVENT_CLASS(binder_lock_class,
TP_PROTO(const char *tag),
TP_ARGS(tag),
TP_STRUCT__entry(
__field(const char *, tag)
),
TP_fast_assign(
__entry->tag = tag;
),
TP_printk("tag=%s", __entry->tag)
);
#define DEFINE_BINDER_LOCK_EVENT(name) \
DEFINE_EVENT(binder_lock_class, name, \
TP_PROTO(const char *func), \
TP_ARGS(func))
DEFINE_BINDER_LOCK_EVENT(binder_lock);
DEFINE_BINDER_LOCK_EVENT(binder_locked);
DEFINE_BINDER_LOCK_EVENT(binder_unlock);
DECLARE_EVENT_CLASS(binder_function_return_class,
TP_PROTO(int ret),
TP_ARGS(ret),

View file

@ -117,7 +117,6 @@ static int binderfs_binder_device_create(struct inode *ref_inode,
struct dentry *dentry, *root;
struct binder_device *device;
char *name = NULL;
size_t name_len;
struct inode *inode = NULL;
struct super_block *sb = ref_inode->i_sb;
struct binderfs_info *info = sb->s_fs_info;
@ -161,9 +160,7 @@ static int binderfs_binder_device_create(struct inode *ref_inode,
inode->i_gid = info->root_gid;
req->name[BINDERFS_MAX_NAME] = '\0'; /* NUL-terminate */
name_len = strlen(req->name);
/* Make sure to include terminating NUL byte */
name = kmemdup(req->name, name_len + 1, GFP_KERNEL);
name = kstrdup(req->name, GFP_KERNEL);
if (!name)
goto err;

View file

@ -0,0 +1,7 @@
#
# Copyright 2025 Google LLC.
#
CONFIG_KUNIT=y
CONFIG_ANDROID_BINDER_IPC=y
CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST=y

View file

@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Copyright 2025 Google LLC.
#
obj-$(CONFIG_ANDROID_BINDER_ALLOC_KUNIT_TEST) += binder_alloc_kunit.o

View file

@ -0,0 +1,572 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Test cases for binder allocator code.
*
* Copyright 2025 Google LLC.
* Author: Tiffany Yang <ynaffit@google.com>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <kunit/test.h>
#include <linux/anon_inodes.h>
#include <linux/err.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/seq_buf.h>
#include <linux/sizes.h>
#include "../binder_alloc.h"
#include "../binder_internal.h"
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
#define BINDER_MMAP_SIZE SZ_128K
#define BUFFER_NUM 5
#define BUFFER_MIN_SIZE (PAGE_SIZE / 8)
#define FREESEQ_BUFLEN ((3 * BUFFER_NUM) + 1)
#define ALIGN_TYPE_STRLEN (12)
#define ALIGNMENTS_BUFLEN (((ALIGN_TYPE_STRLEN + 6) * BUFFER_NUM) + 1)
#define PRINT_ALL_CASES (0)
/* 5^5 alignment combinations * 2 places to share pages * 5! free sequences */
#define TOTAL_EXHAUSTIVE_CASES (3125 * 2 * 120)
/**
* enum buf_end_align_type - Page alignment of a buffer
* end with regard to the end of the previous buffer.
*
* In the pictures below, buf2 refers to the buffer we
* are aligning. buf1 refers to previous buffer by addr.
* Symbol [ means the start of a buffer, ] means the end
* of a buffer, and | means page boundaries.
*/
enum buf_end_align_type {
/**
* @SAME_PAGE_UNALIGNED: The end of this buffer is on
* the same page as the end of the previous buffer and
* is not page aligned. Examples:
* buf1 ][ buf2 ][ ...
* buf1 ]|[ buf2 ][ ...
*/
SAME_PAGE_UNALIGNED = 0,
/**
* @SAME_PAGE_ALIGNED: When the end of the previous buffer
* is not page aligned, the end of this buffer is on the
* same page as the end of the previous buffer and is page
* aligned. When the previous buffer is page aligned, the
* end of this buffer is aligned to the next page boundary.
* Examples:
* buf1 ][ buf2 ]| ...
* buf1 ]|[ buf2 ]| ...
*/
SAME_PAGE_ALIGNED,
/**
* @NEXT_PAGE_UNALIGNED: The end of this buffer is on
* the page next to the end of the previous buffer and
* is not page aligned. Examples:
* buf1 ][ buf2 | buf2 ][ ...
* buf1 ]|[ buf2 | buf2 ][ ...
*/
NEXT_PAGE_UNALIGNED,
/**
* @NEXT_PAGE_ALIGNED: The end of this buffer is on
* the page next to the end of the previous buffer and
* is page aligned. Examples:
* buf1 ][ buf2 | buf2 ]| ...
* buf1 ]|[ buf2 | buf2 ]| ...
*/
NEXT_PAGE_ALIGNED,
/**
* @NEXT_NEXT_UNALIGNED: The end of this buffer is on
* the page that follows the page after the end of the
* previous buffer and is not page aligned. Examples:
* buf1 ][ buf2 | buf2 | buf2 ][ ...
* buf1 ]|[ buf2 | buf2 | buf2 ][ ...
*/
NEXT_NEXT_UNALIGNED,
/**
* @LOOP_END: The number of enum values in &buf_end_align_type.
* It is used for controlling loop termination.
*/
LOOP_END,
};
static const char *const buf_end_align_type_strs[LOOP_END] = {
[SAME_PAGE_UNALIGNED] = "SP_UNALIGNED",
[SAME_PAGE_ALIGNED] = " SP_ALIGNED ",
[NEXT_PAGE_UNALIGNED] = "NP_UNALIGNED",
[NEXT_PAGE_ALIGNED] = " NP_ALIGNED ",
[NEXT_NEXT_UNALIGNED] = "NN_UNALIGNED",
};
struct binder_alloc_test_case_info {
char alignments[ALIGNMENTS_BUFLEN];
struct seq_buf alignments_sb;
size_t *buffer_sizes;
int *free_sequence;
bool front_pages;
};
static void stringify_free_seq(struct kunit *test, int *seq, struct seq_buf *sb)
{
int i;
for (i = 0; i < BUFFER_NUM; i++)
seq_buf_printf(sb, "[%d]", seq[i]);
KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(sb));
}
static void stringify_alignments(struct kunit *test, int *alignments,
struct seq_buf *sb)
{
int i;
for (i = 0; i < BUFFER_NUM; i++)
seq_buf_printf(sb, "[ %d:%s ]", i,
buf_end_align_type_strs[alignments[i]]);
KUNIT_EXPECT_FALSE(test, seq_buf_has_overflowed(sb));
}
static bool check_buffer_pages_allocated(struct kunit *test,
struct binder_alloc *alloc,
struct binder_buffer *buffer,
size_t size)
{
unsigned long page_addr;
unsigned long end;
int page_index;
end = PAGE_ALIGN(buffer->user_data + size);
page_addr = buffer->user_data;
for (; page_addr < end; page_addr += PAGE_SIZE) {
page_index = (page_addr - alloc->vm_start) / PAGE_SIZE;
if (!alloc->pages[page_index] ||
!list_empty(page_to_lru(alloc->pages[page_index]))) {
kunit_err(test, "expect alloc but is %s at page index %d\n",
alloc->pages[page_index] ?
"lru" : "free", page_index);
return false;
}
}
return true;
}
static unsigned long binder_alloc_test_alloc_buf(struct kunit *test,
struct binder_alloc *alloc,
struct binder_buffer *buffers[],
size_t *sizes, int *seq)
{
unsigned long failures = 0;
int i;
for (i = 0; i < BUFFER_NUM; i++) {
buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0);
if (IS_ERR(buffers[i]) ||
!check_buffer_pages_allocated(test, alloc, buffers[i], sizes[i]))
failures++;
}
return failures;
}
static unsigned long binder_alloc_test_free_buf(struct kunit *test,
struct binder_alloc *alloc,
struct binder_buffer *buffers[],
size_t *sizes, int *seq, size_t end)
{
unsigned long failures = 0;
int i;
for (i = 0; i < BUFFER_NUM; i++)
binder_alloc_free_buf(alloc, buffers[seq[i]]);
for (i = 0; i <= (end - 1) / PAGE_SIZE; i++) {
if (list_empty(page_to_lru(alloc->pages[i]))) {
kunit_err(test, "expect lru but is %s at page index %d\n",
alloc->pages[i] ? "alloc" : "free", i);
failures++;
}
}
return failures;
}
static unsigned long binder_alloc_test_free_page(struct kunit *test,
struct binder_alloc *alloc)
{
unsigned long failures = 0;
unsigned long count;
int i;
while ((count = list_lru_count(alloc->freelist))) {
list_lru_walk(alloc->freelist, binder_alloc_free_page,
NULL, count);
}
for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) {
if (alloc->pages[i]) {
kunit_err(test, "expect free but is %s at page index %d\n",
list_empty(page_to_lru(alloc->pages[i])) ?
"alloc" : "lru", i);
failures++;
}
}
return failures;
}
/* Executes one full test run for the given test case. */
static bool binder_alloc_test_alloc_free(struct kunit *test,
struct binder_alloc *alloc,
struct binder_alloc_test_case_info *tc,
size_t end)
{
unsigned long pages = PAGE_ALIGN(end) / PAGE_SIZE;
struct binder_buffer *buffers[BUFFER_NUM];
unsigned long failures;
bool failed = false;
failures = binder_alloc_test_alloc_buf(test, alloc, buffers,
tc->buffer_sizes,
tc->free_sequence);
failed = failed || failures;
KUNIT_EXPECT_EQ_MSG(test, failures, 0,
"Initial allocation failed: %lu/%u buffers with errors",
failures, BUFFER_NUM);
failures = binder_alloc_test_free_buf(test, alloc, buffers,
tc->buffer_sizes,
tc->free_sequence, end);
failed = failed || failures;
KUNIT_EXPECT_EQ_MSG(test, failures, 0,
"Initial buffers not freed correctly: %lu/%lu pages not on lru list",
failures, pages);
/* Allocate from lru. */
failures = binder_alloc_test_alloc_buf(test, alloc, buffers,
tc->buffer_sizes,
tc->free_sequence);
failed = failed || failures;
KUNIT_EXPECT_EQ_MSG(test, failures, 0,
"Reallocation failed: %lu/%u buffers with errors",
failures, BUFFER_NUM);
failures = list_lru_count(alloc->freelist);
failed = failed || failures;
KUNIT_EXPECT_EQ_MSG(test, failures, 0,
"lru list should be empty after reallocation but still has %lu pages",
failures);
failures = binder_alloc_test_free_buf(test, alloc, buffers,
tc->buffer_sizes,
tc->free_sequence, end);
failed = failed || failures;
KUNIT_EXPECT_EQ_MSG(test, failures, 0,
"Reallocated buffers not freed correctly: %lu/%lu pages not on lru list",
failures, pages);
failures = binder_alloc_test_free_page(test, alloc);
failed = failed || failures;
KUNIT_EXPECT_EQ_MSG(test, failures, 0,
"Failed to clean up allocated pages: %lu/%lu pages still installed",
failures, (alloc->buffer_size / PAGE_SIZE));
return failed;
}
static bool is_dup(int *seq, int index, int val)
{
int i;
for (i = 0; i < index; i++) {
if (seq[i] == val)
return true;
}
return false;
}
/* Generate BUFFER_NUM factorial free orders. */
static void permute_frees(struct kunit *test, struct binder_alloc *alloc,
struct binder_alloc_test_case_info *tc,
unsigned long *runs, unsigned long *failures,
int index, size_t end)
{
bool case_failed;
int i;
if (index == BUFFER_NUM) {
DECLARE_SEQ_BUF(freeseq_sb, FREESEQ_BUFLEN);
case_failed = binder_alloc_test_alloc_free(test, alloc, tc, end);
*runs += 1;
*failures += case_failed;
if (case_failed || PRINT_ALL_CASES) {
stringify_free_seq(test, tc->free_sequence,
&freeseq_sb);
kunit_err(test, "case %lu: [%s] | %s - %s - %s", *runs,
case_failed ? "FAILED" : "PASSED",
tc->front_pages ? "front" : "back ",
seq_buf_str(&tc->alignments_sb),
seq_buf_str(&freeseq_sb));
}
return;
}
for (i = 0; i < BUFFER_NUM; i++) {
if (is_dup(tc->free_sequence, index, i))
continue;
tc->free_sequence[index] = i;
permute_frees(test, alloc, tc, runs, failures, index + 1, end);
}
}
static void gen_buf_sizes(struct kunit *test,
struct binder_alloc *alloc,
struct binder_alloc_test_case_info *tc,
size_t *end_offset, unsigned long *runs,
unsigned long *failures)
{
size_t last_offset, offset = 0;
size_t front_sizes[BUFFER_NUM];
size_t back_sizes[BUFFER_NUM];
int seq[BUFFER_NUM] = {0};
int i;
tc->free_sequence = seq;
for (i = 0; i < BUFFER_NUM; i++) {
last_offset = offset;
offset = end_offset[i];
front_sizes[i] = offset - last_offset;
back_sizes[BUFFER_NUM - i - 1] = front_sizes[i];
}
back_sizes[0] += alloc->buffer_size - end_offset[BUFFER_NUM - 1];
/*
* Buffers share the first or last few pages.
* Only BUFFER_NUM - 1 buffer sizes are adjustable since
* we need one giant buffer before getting to the last page.
*/
tc->front_pages = true;
tc->buffer_sizes = front_sizes;
permute_frees(test, alloc, tc, runs, failures, 0,
end_offset[BUFFER_NUM - 1]);
tc->front_pages = false;
tc->buffer_sizes = back_sizes;
permute_frees(test, alloc, tc, runs, failures, 0, alloc->buffer_size);
}
static void gen_buf_offsets(struct kunit *test, struct binder_alloc *alloc,
size_t *end_offset, int *alignments,
unsigned long *runs, unsigned long *failures,
int index)
{
size_t end, prev;
int align;
if (index == BUFFER_NUM) {
struct binder_alloc_test_case_info tc = {0};
seq_buf_init(&tc.alignments_sb, tc.alignments,
ALIGNMENTS_BUFLEN);
stringify_alignments(test, alignments, &tc.alignments_sb);
gen_buf_sizes(test, alloc, &tc, end_offset, runs, failures);
return;
}
prev = index == 0 ? 0 : end_offset[index - 1];
end = prev;
BUILD_BUG_ON(BUFFER_MIN_SIZE * BUFFER_NUM >= PAGE_SIZE);
for (align = SAME_PAGE_UNALIGNED; align < LOOP_END; align++) {
if (align % 2)
end = ALIGN(end, PAGE_SIZE);
else
end += BUFFER_MIN_SIZE;
end_offset[index] = end;
alignments[index] = align;
gen_buf_offsets(test, alloc, end_offset, alignments, runs,
failures, index + 1);
}
}
struct binder_alloc_test {
struct binder_alloc alloc;
struct list_lru binder_test_freelist;
struct file *filp;
unsigned long mmap_uaddr;
};
static void binder_alloc_test_init_freelist(struct kunit *test)
{
struct binder_alloc_test *priv = test->priv;
KUNIT_EXPECT_PTR_EQ(test, priv->alloc.freelist,
&priv->binder_test_freelist);
}
static void binder_alloc_test_mmap(struct kunit *test)
{
struct binder_alloc_test *priv = test->priv;
struct binder_alloc *alloc = &priv->alloc;
struct binder_buffer *buf;
struct rb_node *n;
KUNIT_EXPECT_EQ(test, alloc->mapped, true);
KUNIT_EXPECT_EQ(test, alloc->buffer_size, BINDER_MMAP_SIZE);
n = rb_first(&alloc->allocated_buffers);
KUNIT_EXPECT_PTR_EQ(test, n, NULL);
n = rb_first(&alloc->free_buffers);
buf = rb_entry(n, struct binder_buffer, rb_node);
KUNIT_EXPECT_EQ(test, binder_alloc_buffer_size(alloc, buf),
BINDER_MMAP_SIZE);
KUNIT_EXPECT_TRUE(test, list_is_last(&buf->entry, &alloc->buffers));
}
/**
* binder_alloc_exhaustive_test() - Exhaustively test alloc and free of buffer pages.
* @test: The test context object.
*
* Allocate BUFFER_NUM buffers to cover all page alignment cases,
* then free them in all orders possible. Check that pages are
* correctly allocated, put onto lru when buffers are freed, and
* are freed when binder_alloc_free_page() is called.
*/
static void binder_alloc_exhaustive_test(struct kunit *test)
{
struct binder_alloc_test *priv = test->priv;
size_t end_offset[BUFFER_NUM];
int alignments[BUFFER_NUM];
unsigned long failures = 0;
unsigned long runs = 0;
gen_buf_offsets(test, &priv->alloc, end_offset, alignments, &runs,
&failures, 0);
KUNIT_EXPECT_EQ(test, runs, TOTAL_EXHAUSTIVE_CASES);
KUNIT_EXPECT_EQ(test, failures, 0);
}
/* ===== End test cases ===== */
static void binder_alloc_test_vma_close(struct vm_area_struct *vma)
{
struct binder_alloc *alloc = vma->vm_private_data;
binder_alloc_vma_close(alloc);
}
static const struct vm_operations_struct binder_alloc_test_vm_ops = {
.close = binder_alloc_test_vma_close,
.fault = binder_vm_fault,
};
static int binder_alloc_test_mmap_handler(struct file *filp,
struct vm_area_struct *vma)
{
struct binder_alloc *alloc = filp->private_data;
vm_flags_mod(vma, VM_DONTCOPY | VM_MIXEDMAP, VM_MAYWRITE);
vma->vm_ops = &binder_alloc_test_vm_ops;
vma->vm_private_data = alloc;
return binder_alloc_mmap_handler(alloc, vma);
}
static const struct file_operations binder_alloc_test_fops = {
.mmap = binder_alloc_test_mmap_handler,
};
static int binder_alloc_test_init(struct kunit *test)
{
struct binder_alloc_test *priv;
int ret;
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
test->priv = priv;
ret = list_lru_init(&priv->binder_test_freelist);
if (ret) {
kunit_err(test, "Failed to initialize test freelist\n");
return ret;
}
/* __binder_alloc_init requires mm to be attached */
ret = kunit_attach_mm();
if (ret) {
kunit_err(test, "Failed to attach mm\n");
return ret;
}
__binder_alloc_init(&priv->alloc, &priv->binder_test_freelist);
priv->filp = anon_inode_getfile("binder_alloc_kunit",
&binder_alloc_test_fops, &priv->alloc,
O_RDWR | O_CLOEXEC);
if (IS_ERR_OR_NULL(priv->filp)) {
kunit_err(test, "Failed to open binder alloc test driver file\n");
return priv->filp ? PTR_ERR(priv->filp) : -ENOMEM;
}
priv->mmap_uaddr = kunit_vm_mmap(test, priv->filp, 0, BINDER_MMAP_SIZE,
PROT_READ, MAP_PRIVATE | MAP_NORESERVE,
0);
if (!priv->mmap_uaddr) {
kunit_err(test, "Could not map the test's transaction memory\n");
return -ENOMEM;
}
return 0;
}
static void binder_alloc_test_exit(struct kunit *test)
{
struct binder_alloc_test *priv = test->priv;
/* Close the backing file to make sure binder_alloc_vma_close runs */
if (!IS_ERR_OR_NULL(priv->filp))
fput(priv->filp);
if (priv->alloc.mm)
binder_alloc_deferred_release(&priv->alloc);
/* Make sure freelist is empty */
KUNIT_EXPECT_EQ(test, list_lru_count(&priv->binder_test_freelist), 0);
list_lru_destroy(&priv->binder_test_freelist);
}
static struct kunit_case binder_alloc_test_cases[] = {
KUNIT_CASE(binder_alloc_test_init_freelist),
KUNIT_CASE(binder_alloc_test_mmap),
KUNIT_CASE(binder_alloc_exhaustive_test),
{}
};
static struct kunit_suite binder_alloc_test_suite = {
.name = "binder_alloc",
.test_cases = binder_alloc_test_cases,
.init = binder_alloc_test_init,
.exit = binder_alloc_test_exit,
};
kunit_test_suite(binder_alloc_test_suite);
MODULE_AUTHOR("Tiffany Yang <ynaffit@google.com>");
MODULE_DESCRIPTION("Binder Alloc KUnit tests");
MODULE_LICENSE("GPL");

View file

@ -31,8 +31,8 @@ int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
int ret;
for (i = 0; i < img_info->entries - 1; i++, mhi_buf++, bhi_vec++) {
bhi_vec->dma_addr = mhi_buf->dma_addr;
bhi_vec->size = mhi_buf->len;
bhi_vec->dma_addr = cpu_to_le64(mhi_buf->dma_addr);
bhi_vec->size = cpu_to_le64(mhi_buf->len);
}
dev_dbg(dev, "BHIe programming for RDDM\n");
@ -431,8 +431,8 @@ static void mhi_firmware_copy_bhie(struct mhi_controller *mhi_cntrl,
while (remainder) {
to_cpy = min(remainder, mhi_buf->len);
memcpy(mhi_buf->buf, buf, to_cpy);
bhi_vec->dma_addr = mhi_buf->dma_addr;
bhi_vec->size = to_cpy;
bhi_vec->dma_addr = cpu_to_le64(mhi_buf->dma_addr);
bhi_vec->size = cpu_to_le64(to_cpy);
buf += to_cpy;
remainder -= to_cpy;

View file

@ -10,6 +10,7 @@
#include <linux/list.h>
#include <linux/mhi.h>
#include <linux/module.h>
#include <linux/string_choices.h>
#include "internal.h"
static int mhi_debugfs_states_show(struct seq_file *m, void *d)
@ -22,7 +23,7 @@ static int mhi_debugfs_states_show(struct seq_file *m, void *d)
mhi_is_active(mhi_cntrl) ? "Active" : "Inactive",
mhi_state_str(mhi_cntrl->dev_state),
TO_MHI_EXEC_STR(mhi_cntrl->ee),
mhi_cntrl->wake_set ? "true" : "false");
str_true_false(mhi_cntrl->wake_set));
/* counters */
seq_printf(m, "M0: %u M2: %u M3: %u", mhi_cntrl->M0, mhi_cntrl->M2,

View file

@ -176,7 +176,7 @@ static int mhi_alloc_aligned_ring(struct mhi_controller *mhi_cntrl,
return 0;
}
void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl)
static void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl)
{
int i;
struct mhi_event *mhi_event = mhi_cntrl->mhi_event;
@ -191,7 +191,7 @@ void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl)
free_irq(mhi_cntrl->irq[0], mhi_cntrl);
}
int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl)
static int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl)
{
struct mhi_event *mhi_event = mhi_cntrl->mhi_event;
struct device *dev = &mhi_cntrl->mhi_dev->dev;
@ -254,7 +254,7 @@ error_request:
return ret;
}
void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl)
static void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl)
{
int i;
struct mhi_ctxt *mhi_ctxt = mhi_cntrl->mhi_ctxt;
@ -299,7 +299,7 @@ void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl)
mhi_cntrl->mhi_ctxt = NULL;
}
int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl)
static int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl)
{
struct mhi_ctxt *mhi_ctxt;
struct mhi_chan_ctxt *chan_ctxt;

View file

@ -25,8 +25,8 @@ struct mhi_ctxt {
};
struct bhi_vec_entry {
u64 dma_addr;
u64 size;
__le64 dma_addr;
__le64 size;
};
enum mhi_fw_load_type {
@ -383,19 +383,12 @@ void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl,
/* Initialization methods */
int mhi_init_mmio(struct mhi_controller *mhi_cntrl);
int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl);
void mhi_deinit_dev_ctxt(struct mhi_controller *mhi_cntrl);
int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl);
void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl);
int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl,
struct image_info *img_info);
void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl);
/* Automatically allocate and queue inbound buffers */
#define MHI_CH_INBOUND_ALLOC_BUFS BIT(0)
int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan, unsigned int flags);
int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan);
void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl,

View file

@ -602,7 +602,7 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
{
dma_addr_t ptr = MHI_TRE_GET_EV_PTR(event);
struct mhi_ring_element *local_rp, *ev_tre;
void *dev_rp;
void *dev_rp, *next_rp;
struct mhi_buf_info *buf_info;
u16 xfer_len;
@ -621,6 +621,16 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl,
result.dir = mhi_chan->dir;
local_rp = tre_ring->rp;
next_rp = local_rp + 1;
if (next_rp >= tre_ring->base + tre_ring->len)
next_rp = tre_ring->base;
if (dev_rp != next_rp && !MHI_TRE_DATA_GET_CHAIN(local_rp)) {
dev_err(&mhi_cntrl->mhi_dev->dev,
"Event element points to an unexpected TRE\n");
break;
}
while (local_rp != dev_rp) {
buf_info = buf_ring->rp;
/* If it's the last TRE, get length from the event */
@ -1435,7 +1445,7 @@ exit_unprepare_channel:
mutex_unlock(&mhi_chan->mutex);
}
int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
static int mhi_prepare_channel(struct mhi_controller *mhi_cntrl,
struct mhi_chan *mhi_chan, unsigned int flags)
{
int ret = 0;

View file

@ -43,6 +43,7 @@
* @mru_default: default MRU size for MBIM network packets
* @sideband_wake: Devices using dedicated sideband GPIO for wakeup instead
* of inband wake support (such as sdx24)
* @no_m3: M3 not supported
*/
struct mhi_pci_dev_info {
const struct mhi_controller_config *config;
@ -54,6 +55,7 @@ struct mhi_pci_dev_info {
unsigned int dma_data_width;
unsigned int mru_default;
bool sideband_wake;
bool no_m3;
};
#define MHI_CHANNEL_CONFIG_UL(ch_num, ch_name, el_count, ev_ring) \
@ -295,6 +297,7 @@ static const struct mhi_pci_dev_info mhi_qcom_qdu100_info = {
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
.sideband_wake = false,
.no_m3 = true,
};
static const struct mhi_channel_config mhi_qcom_sa8775p_channels[] = {
@ -490,6 +493,23 @@ static const struct mhi_channel_config mhi_foxconn_sdx55_channels[] = {
MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0_MBIM", 128, 3),
};
static const struct mhi_channel_config mhi_foxconn_sdx61_channels[] = {
MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 32, 0),
MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 32, 0),
MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 1),
MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 1),
MHI_CHANNEL_CONFIG_UL(12, "MBIM", 32, 0),
MHI_CHANNEL_CONFIG_DL(13, "MBIM", 32, 0),
MHI_CHANNEL_CONFIG_UL(32, "DUN", 32, 0),
MHI_CHANNEL_CONFIG_DL(33, "DUN", 32, 0),
MHI_CHANNEL_CONFIG_UL_FP(34, "FIREHOSE", 32, 0),
MHI_CHANNEL_CONFIG_DL_FP(35, "FIREHOSE", 32, 0),
MHI_CHANNEL_CONFIG_UL(50, "NMEA", 32, 0),
MHI_CHANNEL_CONFIG_DL(51, "NMEA", 32, 0),
MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0_MBIM", 128, 2),
MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0_MBIM", 128, 3),
};
static struct mhi_event_config mhi_foxconn_sdx55_events[] = {
MHI_EVENT_CONFIG_CTRL(0, 128),
MHI_EVENT_CONFIG_DATA(1, 128),
@ -506,6 +526,15 @@ static const struct mhi_controller_config modem_foxconn_sdx55_config = {
.event_cfg = mhi_foxconn_sdx55_events,
};
static const struct mhi_controller_config modem_foxconn_sdx61_config = {
.max_channels = 128,
.timeout_ms = 20000,
.num_channels = ARRAY_SIZE(mhi_foxconn_sdx61_channels),
.ch_cfg = mhi_foxconn_sdx61_channels,
.num_events = ARRAY_SIZE(mhi_foxconn_sdx55_events),
.event_cfg = mhi_foxconn_sdx55_events,
};
static const struct mhi_controller_config modem_foxconn_sdx72_config = {
.max_channels = 128,
.timeout_ms = 20000,
@ -593,8 +622,8 @@ static const struct mhi_pci_dev_info mhi_foxconn_dw5932e_info = {
.sideband_wake = false,
};
static const struct mhi_pci_dev_info mhi_foxconn_t99w515_info = {
.name = "foxconn-t99w515",
static const struct mhi_pci_dev_info mhi_foxconn_t99w640_info = {
.name = "foxconn-t99w640",
.edl = "qcom/sdx72m/foxconn/edl.mbn",
.edl_trigger = true,
.config = &modem_foxconn_sdx72_config,
@ -615,6 +644,17 @@ static const struct mhi_pci_dev_info mhi_foxconn_dw5934e_info = {
.sideband_wake = false,
};
static const struct mhi_pci_dev_info mhi_foxconn_t99w696_info = {
.name = "foxconn-t99w696",
.edl = "qcom/sdx61/foxconn/prog_firehose_lite.elf",
.edl_trigger = true,
.config = &modem_foxconn_sdx61_config,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
.mru_default = 32768,
.sideband_wake = false,
};
static const struct mhi_channel_config mhi_mv3x_channels[] = {
MHI_CHANNEL_CONFIG_UL(0, "LOOPBACK", 64, 0),
MHI_CHANNEL_CONFIG_DL(1, "LOOPBACK", 64, 0),
@ -695,6 +735,7 @@ static const struct mhi_pci_dev_info mhi_sierra_em919x_info = {
.config = &modem_sierra_em919x_config,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
.mru_default = 32768,
.sideband_wake = false,
};
@ -818,6 +859,16 @@ static const struct mhi_pci_dev_info mhi_telit_fn920c04_info = {
.edl_trigger = true,
};
static const struct mhi_pci_dev_info mhi_telit_fn990b40_info = {
.name = "telit-fn990b40",
.config = &modem_telit_fn920c04_config,
.bar_num = MHI_PCI_DEFAULT_BAR_NUM,
.dma_data_width = 32,
.sideband_wake = false,
.mru_default = 32768,
.edl_trigger = true,
};
static const struct mhi_pci_dev_info mhi_netprisma_lcur57_info = {
.name = "netprisma-lcur57",
.edl = "qcom/prog_firehose_sdx24.mbn",
@ -852,6 +903,9 @@ static const struct pci_device_id mhi_pci_id_table[] = {
/* EM919x (sdx55), use the same vid:pid as qcom-sdx55m */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, 0x18d7, 0x0200),
.driver_data = (kernel_ulong_t) &mhi_sierra_em919x_info },
/* EM929x (sdx65), use the same configuration as EM919x */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x18d7, 0x0301),
.driver_data = (kernel_ulong_t) &mhi_sierra_em919x_info },
/* Telit FN980 hardware revision v1 */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, 0x1C5D, 0x2000),
.driver_data = (kernel_ulong_t) &mhi_telit_fn980_hw_v1_info },
@ -863,8 +917,26 @@ static const struct pci_device_id mhi_pci_id_table[] = {
/* Telit FE990A */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, 0x1c5d, 0x2015),
.driver_data = (kernel_ulong_t) &mhi_telit_fe990a_info },
/* Foxconn T99W696.01, Lenovo Generic SKU */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe142),
.driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info },
/* Foxconn T99W696.02, Lenovo X1 Carbon SKU */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe143),
.driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info },
/* Foxconn T99W696.03, Lenovo X1 2in1 SKU */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe144),
.driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info },
/* Foxconn T99W696.04, Lenovo PRC SKU */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe145),
.driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info },
/* Foxconn T99W696.00, Foxconn SKU */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0308, PCI_VENDOR_ID_FOXCONN, 0xe146),
.driver_data = (kernel_ulong_t) &mhi_foxconn_t99w696_info },
{ PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0308),
.driver_data = (kernel_ulong_t) &mhi_qcom_sdx65_info },
/* Telit FN990B40 (sdx72) */
{ PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0309, 0x1c5d, 0x201a),
.driver_data = (kernel_ulong_t) &mhi_telit_fn990b40_info },
{ PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0309),
.driver_data = (kernel_ulong_t) &mhi_qcom_sdx75_info },
/* QDU100, x100-DU */
@ -920,9 +992,9 @@ static const struct pci_device_id mhi_pci_id_table[] = {
/* DW5932e (sdx62), Non-eSIM */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0f9),
.driver_data = (kernel_ulong_t) &mhi_foxconn_dw5932e_info },
/* T99W515 (sdx72) */
/* T99W640 (sdx72) */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe118),
.driver_data = (kernel_ulong_t) &mhi_foxconn_t99w515_info },
.driver_data = (kernel_ulong_t) &mhi_foxconn_t99w640_info },
/* DW5934e(sdx72), With eSIM */
{ PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe11d),
.driver_data = (kernel_ulong_t) &mhi_foxconn_dw5934e_info },
@ -1306,8 +1378,8 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* start health check */
mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD);
/* Only allow runtime-suspend if PME capable (for wakeup) */
if (pci_pme_capable(pdev, PCI_D3hot)) {
/* Allow runtime suspend only if both PME from D3Hot and M3 are supported */
if (pci_pme_capable(pdev, PCI_D3hot) && !(info->no_m3)) {
pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_mark_last_busy(&pdev->dev);

View file

@ -737,8 +737,7 @@ static int moxtet_irq_setup(struct moxtet *moxtet)
{
int i, ret;
moxtet->irq.domain = irq_domain_create_simple(of_fwnode_handle(moxtet->dev->of_node),
MOXTET_NIRQS, 0,
moxtet->irq.domain = irq_domain_create_simple(dev_fwnode(moxtet->dev), MOXTET_NIRQS, 0,
&moxtet_irq_domain, moxtet);
if (moxtet->irq.domain == NULL) {
dev_err(moxtet->dev, "Could not add IRQ domain\n");

View file

@ -7,7 +7,8 @@
config CDX_BUS
bool "CDX Bus driver"
depends on OF && ARM64
depends on OF && ARM64 || COMPILE_TEST
select GENERIC_MSI_IRQ
help
Driver to enable Composable DMA Transfer(CDX) Bus. CDX bus
exposes Fabric devices which uses composable DMA IP to the

View file

@ -9,6 +9,7 @@ if CDX_BUS
config CDX_CONTROLLER
tristate "CDX bus controller"
depends on HAS_DMA
select GENERIC_MSI_IRQ
select REMOTEPROC
select RPMSG

View file

@ -195,19 +195,16 @@ static int xlnx_cdx_probe(struct platform_device *pdev)
/* Create MSI domain */
cdx->msi_domain = cdx_msi_domain_init(&pdev->dev);
if (!cdx->msi_domain) {
dev_err(&pdev->dev, "cdx_msi_domain_init() failed");
ret = -ENODEV;
ret = dev_err_probe(&pdev->dev, -ENODEV, "cdx_msi_domain_init() failed");
goto cdx_msi_fail;
}
ret = cdx_setup_rpmsg(pdev);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "Failed to register CDX RPMsg transport\n");
dev_err_probe(&pdev->dev, ret, "Failed to register CDX RPMsg transport\n");
goto cdx_rpmsg_fail;
}
dev_info(&pdev->dev, "Successfully registered CDX controller with RPMsg as transport\n");
return 0;
cdx_rpmsg_fail:
@ -246,31 +243,13 @@ MODULE_DEVICE_TABLE(of, cdx_match_table);
static struct platform_driver cdx_pdriver = {
.driver = {
.name = "cdx-controller",
.pm = NULL,
.of_match_table = cdx_match_table,
},
.probe = xlnx_cdx_probe,
.remove = xlnx_cdx_remove,
};
static int __init cdx_controller_init(void)
{
int ret;
ret = platform_driver_register(&cdx_pdriver);
if (ret)
pr_err("platform_driver_register() failed: %d\n", ret);
return ret;
}
static void __exit cdx_controller_exit(void)
{
platform_driver_unregister(&cdx_pdriver);
}
module_init(cdx_controller_init);
module_exit(cdx_controller_exit);
module_platform_driver(cdx_pdriver);
MODULE_AUTHOR("AMD Inc.");
MODULE_DESCRIPTION("CDX controller for AMD devices");

View file

@ -237,7 +237,7 @@ config APPLICOM
config SONYPI
tristate "Sony Vaio Programmable I/O Control Device support"
depends on X86_32 && PCI && INPUT
depends on X86_32 && PCI && INPUT && HAS_IOPORT
depends on ACPI_EC || !ACPI
help
This driver enables access to the Sony Programmable I/O Control

View file

@ -289,15 +289,15 @@ EXPORT_SYMBOL(misc_deregister);
static int __init misc_init(void)
{
int err;
struct proc_dir_entry *ret;
struct proc_dir_entry *misc_proc_file;
ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops);
misc_proc_file = proc_create_seq("misc", 0, NULL, &misc_seq_ops);
err = class_register(&misc_class);
if (err)
goto fail_remove;
err = -EIO;
if (__register_chrdev(MISC_MAJOR, 0, MINORMASK + 1, "misc", &misc_fops))
err = __register_chrdev(MISC_MAJOR, 0, MINORMASK + 1, "misc", &misc_fops);
if (err < 0)
goto fail_printk;
return 0;
@ -305,7 +305,7 @@ fail_printk:
pr_err("unable to get major %d for misc devices\n", MISC_MAJOR);
class_unregister(&misc_class);
fail_remove:
if (ret)
if (misc_proc_file)
remove_proc_entry("misc", NULL);
return err;
}

View file

@ -787,6 +787,7 @@ static int is_device_busy(struct comedi_device *dev)
struct comedi_subdevice *s;
int i;
lockdep_assert_held_write(&dev->attach_lock);
lockdep_assert_held(&dev->mutex);
if (!dev->attached)
return 0;
@ -795,7 +796,16 @@ static int is_device_busy(struct comedi_device *dev)
s = &dev->subdevices[i];
if (s->busy)
return 1;
if (s->async && comedi_buf_is_mmapped(s))
if (!s->async)
continue;
if (comedi_buf_is_mmapped(s))
return 1;
/*
* There may be tasks still waiting on the subdevice's wait
* queue, although they should already be about to be removed
* from it since the subdevice has no active async command.
*/
if (wq_has_sleeper(&s->async->wait_head))
return 1;
}
@ -825,15 +835,22 @@ static int do_devconfig_ioctl(struct comedi_device *dev,
return -EPERM;
if (!arg) {
if (is_device_busy(dev))
return -EBUSY;
if (dev->attached) {
struct module *driver_module = dev->driver->module;
int rc = 0;
comedi_device_detach(dev);
module_put(driver_module);
if (dev->attached) {
down_write(&dev->attach_lock);
if (is_device_busy(dev)) {
rc = -EBUSY;
} else {
struct module *driver_module =
dev->driver->module;
comedi_device_detach_locked(dev);
module_put(driver_module);
}
up_write(&dev->attach_lock);
}
return 0;
return rc;
}
if (copy_from_user(&it, arg, sizeof(it)))

View file

@ -50,6 +50,7 @@ extern struct mutex comedi_drivers_list_lock;
int insn_inval(struct comedi_device *dev, struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data);
void comedi_device_detach_locked(struct comedi_device *dev);
void comedi_device_detach(struct comedi_device *dev);
int comedi_device_attach(struct comedi_device *dev,
struct comedi_devconfig *it);

View file

@ -158,7 +158,7 @@ static void comedi_device_detach_cleanup(struct comedi_device *dev)
int i;
struct comedi_subdevice *s;
lockdep_assert_held(&dev->attach_lock);
lockdep_assert_held_write(&dev->attach_lock);
lockdep_assert_held(&dev->mutex);
if (dev->subdevices) {
for (i = 0; i < dev->n_subdevices; i++) {
@ -196,16 +196,23 @@ static void comedi_device_detach_cleanup(struct comedi_device *dev)
comedi_clear_hw_dev(dev);
}
void comedi_device_detach(struct comedi_device *dev)
void comedi_device_detach_locked(struct comedi_device *dev)
{
lockdep_assert_held_write(&dev->attach_lock);
lockdep_assert_held(&dev->mutex);
comedi_device_cancel_all(dev);
down_write(&dev->attach_lock);
dev->attached = false;
dev->detach_count++;
if (dev->driver)
dev->driver->detach(dev);
comedi_device_detach_cleanup(dev);
}
void comedi_device_detach(struct comedi_device *dev)
{
lockdep_assert_held(&dev->mutex);
down_write(&dev->attach_lock);
comedi_device_detach_locked(dev);
up_write(&dev->attach_lock);
}

View file

@ -406,7 +406,7 @@ static int zynq_fpga_ops_write(struct fpga_manager *mgr, struct sg_table *sgt)
}
priv->dma_nelms =
dma_map_sg(mgr->dev.parent, sgt->sgl, sgt->nents, DMA_TO_DEVICE);
dma_map_sgtable(mgr->dev.parent, sgt, DMA_TO_DEVICE, 0);
if (priv->dma_nelms == 0) {
dev_err(&mgr->dev, "Unable to DMA map (TO_DEVICE)\n");
return -ENOMEM;
@ -478,7 +478,7 @@ out_clk:
clk_disable(priv->clk);
out_free:
dma_unmap_sg(mgr->dev.parent, sgt->sgl, sgt->nents, DMA_TO_DEVICE);
dma_unmap_sgtable(mgr->dev.parent, sgt, DMA_TO_DEVICE, 0);
return err;
}

View file

@ -1404,7 +1404,7 @@ void fsi_driver_unregister(struct fsi_driver *fsi_drv)
}
EXPORT_SYMBOL_GPL(fsi_driver_unregister);
struct bus_type fsi_bus_type = {
const struct bus_type fsi_bus_type = {
.name = "fsi",
.match = fsi_bus_match,
};

View file

@ -13,13 +13,13 @@
#include <linux/irqflags.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/regmap.h>
#include <linux/firmware.h>
#include <linux/gpio/aspeed.h>
#include <linux/mfd/syscon.h>
#include <linux/of_address.h>
#include <linux/genalloc.h>
#include "fsi-master.h"
@ -1285,14 +1285,7 @@ static int fsi_master_acf_probe(struct platform_device *pdev)
master->gpio_mux = gpio;
/* Grab the reserved memory region (use DMA API instead ?) */
np = of_parse_phandle(mnode, "memory-region", 0);
if (!np) {
dev_err(&pdev->dev, "Didn't find reserved memory\n");
rc = -EINVAL;
goto err_free;
}
rc = of_address_to_resource(np, 0, &res);
of_node_put(np);
rc = of_reserved_mem_region_to_resource(mnode, 0, &res);
if (rc) {
dev_err(&pdev->dev, "Couldn't address to resource for reserved memory\n");
rc = -ENOMEM;

View file

@ -1039,9 +1039,12 @@ static const struct fw_upload_ops cc1352_bootloader_ops = {
.cleanup = cc1352_cleanup
};
/*
* Must only be called from probe() as the devres resources allocated here
* will only be released on driver detach.
*/
static int gb_fw_init(struct gb_beagleplay *bg)
{
int ret;
struct fw_upload *fwl;
struct gpio_desc *desc;
@ -1060,29 +1063,17 @@ static int gb_fw_init(struct gb_beagleplay *bg)
bg->bootloader_backdoor_gpio = desc;
desc = devm_gpiod_get(&bg->sd->dev, "reset", GPIOD_IN);
if (IS_ERR(desc)) {
ret = PTR_ERR(desc);
goto free_boot;
}
if (IS_ERR(desc))
return PTR_ERR(desc);
bg->rst_gpio = desc;
fwl = firmware_upload_register(THIS_MODULE, &bg->sd->dev, "cc1352p7",
&cc1352_bootloader_ops, bg);
if (IS_ERR(fwl)) {
ret = PTR_ERR(fwl);
goto free_reset;
}
if (IS_ERR(fwl))
return PTR_ERR(fwl);
bg->fwl = fwl;
return 0;
free_reset:
devm_gpiod_put(&bg->sd->dev, bg->rst_gpio);
bg->rst_gpio = NULL;
free_boot:
devm_gpiod_put(&bg->sd->dev, bg->bootloader_backdoor_gpio);
bg->bootloader_backdoor_gpio = NULL;
return ret;
}
static void gb_fw_deinit(struct gb_beagleplay *bg)

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 @@ err:
}
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;
}

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