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

Jonathan writes:

IIO: New device support, features and cleanup for the 6.20/7.0 cycle.

Slightly messier than normal unfortunately due to some conflicts
and build config bugs related to I3C drivers.

One last minute Kconfig fix right at the top after a linux-next report.
I've simplified the Kconfig and made it match other instances in the kernel
so that should be safe enough despite short soak time in front of build bots.

Merge of an immutable branch from I3C to get some stubs that were missing
and caused build issues with dual I2C / I3C drivers. This also brought in a
drop of some deprecated interfaces so there is also one patch to update a
new driver to not use those.

We are having another go at using cleanup.h magic with the IIO mode claim
functions after backing out last try at this. This time we have wrappers
around the new ACQUIRE() and ACQUIRE_ERR() macros.
Having been burnt once, we will be taking it a bit more slowly this time
wrt to wide adoption of these! Thanks in particular to Kurt for taking
on this core IIO work.

New Device Support
==================

adi,ad18113
- New driver to support the AD18113 amplifier - an interesting device due
  to the external bypass paths where we need to describe what gain those
  paths have in DT. Longer term it will be interesting to see if this
  simplistic description is enough for real deployments.
adi,ad4062
- New driver for the AD4060 and AD4052 SAR ADCs including trigger, event
  and GPIO controller support.  Follow up patch replaced use of some
  deprecated I3C interfaces prior to the I3C immutable branch merge as
  that includes dropping them.
adi,ad4134
- New driver for the AD4134 24bit 4 channel simultaneous sampling ADC.
adi,ad7768-1,
- Add support for the ADAQ767-1, ADAQ7768-1 and ADAQ7769-1 ADCs after some
  rework to enable the driver to support multiple device types.
adi,ad9467
- Add support for the similar ad9211 ADC to this existing driver.
- Make the selection of 2s comp mode explicit for normal operation and
  switch to offset binary when entering calibration mode.
honeywell,abp2
- New driver to support this huge family (100+) of board mount pressure and
  temperature sensors.
maxim,max22007
- New drier for this 4 channel DAC.
memsic,mmc5633
- New driver for this I2C/I3C magnetometer. Follow on patches fixed up
  issues related to single driver supporting both bus types.
microchip,mcp747feb02
- New driver for the Microchip MCP47F(E/V)B(0/1/2)1,
  MCP47F(E/V)B(0/1/2)2, MCP47F(E/V)B(0/1/2)4 and MCP47F(E/V)B(0/1/2)8
  buffered voltage output DACs.
nxp,sar-adc
- New driver support ADCs found on s32g2 and s32g3 platforms.
ti,ads1018
- New drier for the ADS1018 and ADS1118 SPI ADCs.
ti,ads131m02
- New driver supporting ADS131M(02/03/04/06/08)24-bit simultaneous sampling
  ADCs.

Features
========

iio-core
- New IIO_DEV_ACQUIRE_DIRECT_MODE() / IIO_DEV_ACQUIRE_FAILED() +
  equivalents for the much rarer case where the mode needs pinning
  whether or not it is in direct mode.  These use the ACQUIRE()
  / ACQUIRE_ERR() infrastructure underneath to provide both simple
  checks on whether we got the requested mode and to provide scope
  based release. Applied in a few initial drivers.
adi,ad9467
- Support calibbias control
adi,adf4377
- Add support to act as a clock provider.
adi,adxl380
- Support low power 1KHz sampling frequency mode. Required rework of
  how events and filters were configured, plus applying of constraints
  when in this mode.
rf-digital,rfd77402
- Add interrupt support as alternative to polling for completion.
st,lsm6dsx
- Tap event detection (after considerable driver rework)

Cleanup and Minor Fixes
=======================

More minor cleanup such as typos, white space etc not called out except
where they were applied to a lot of drivers.

Various drivers.
- Use of dev_err_probe() to cleanup error handling.
- Introduce local struct device and struct device_node variables to
  reduce duplication of getting them from containing structs.
- Ensure uses of iio_trigger_generic_data_rdy_poll() set IRQF_NO_THREAD
  as that function calls non threaded child interrupt handlers.
- Replace IRQF_ONESHOT in not thread interrupt handlers with
  IRQF_NO_THREAD to ensure they run as intended. Drop one unnecessary case.
iio-sw-device/trigger.
- Constify configs_group_operations structures.
iio-buffer-dma / buffer-dma-engine
- Use lockdep_assert_held() to replace WARN_ON() to check lock is
  correctly held.
- Make use of cleanup.h magic to simplify various code paths.
- Make iio_dma_buffer_init() return void rather than always success.

adi,ad7766
- Replace custom interrupt handler with iio_trigger_generic_data_rdy_poll()
adi,ad9832
- Drop legacy platform_data support.
adi,ade9000
- Add a maintainer entry.
adi,adt7316
- Move to EXPORT_GPL_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() so the compiler
  can cleanly drop unused pm structures and callbacks.
adi,adxl345
- Relax build constraint vs the driver that is in input so both may be
  built as modules and selection made at runtime.
adi,adxl380
- Make sure we don't read tail entries in the hardware fifo if a partial
  new scan has been written.
- Move to a single larger regmap_noinc_read() to read the hardware fifo.
aspeed,ast2600
- Add missing interrupts property to DT binding.
bosch,bmi270_i2c
- Add missing MODULE_DEVICE_TABLE() macros so auto probing of modules can
  work.
bosch,smi330
- Drop duplicate assignment of IIO_TYPE in smi330_read_avail()
- Use new common field_get() and field_prep() helpers to replace local
  version.
honeywell,mprls0025pa
  Fixes delayed to merge window as late in cycle and we didn't want to delay
  the rest of the series.
- Allow Kconfig selection of specific bus sub-drivers rather than tying that
  to the buses themselves being supported.
- Zero spi_transfer structure to avoid chance of unintentionally set fields
  effecting transfer.
- Fix a potential timing violation wrt to the chip select to first clock
  edge timing.
- As recent driver, take risk inherent in dropping interrupt direction from
  driver as that should be set by firmware.
- Fix wrong reported number of data bits for channel.
- Fix a pressure channel calculation bug.
- Rework to allow embedding the tx buffer in the iio_priv() structure rather
  than requiring separate allocation.
- Move the buffer clearing to the shared core bringing it into affect for
  SPI as well as I2C.
- Stricter checks for status byte.
- Greatly simplify the measurement sequence.
- Add a copyright entry to reflect Petre's continued work on this driver.
intersil,isl29018
- Switch from spritnf to sysfs_emit_at() to make it clear overflow can't
  occur.
invensense,icm42600
- Allow sysfs access to temperature when buffered capture in use as it
  does not impact other sensor data paths.
invensense,itg3200
- Check unused return value in read_raw() callback.
men,z188
- Drop now duplicated module alias.
rf-digital,rfd77402
- Add DT binding doc and explicit of_device_id table.
- Poll for timeout with times as on datasheet, then replace opencoded
  version with read_poll_timeout().
sensiron,scd4x
- Add missing timestamp channel. The code to push it to the buffer was there
  but there was no way to turn it on.
vti,sca3000
- Fix resource leak if iio_device_register() fails.

* tag 'iio-for-7.0a' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (144 commits)
  iio: magn: mmc5633: Fix Kconfig for combination of I3C as module and driver builtin
  iio: sca3000: Fix a resource leak in sca3000_probe()
  iio: proximity: rfd77402: Add interrupt handling support
  iio: proximity: rfd77402: Document device private data structure
  iio: proximity: rfd77402: Use devm-managed mutex initialization
  iio: proximity: rfd77402: Use kernel helper for result polling
  iio: proximity: rfd77402: Align polling timeout with datasheet
  iio: cros_ec: Allow enabling/disabling calibration mode
  iio: frequency: ad9523: correct kernel-doc bad line warning
  iio: buffer: buffer_impl.h: fix kernel-doc warnings
  iio: gyro: itg3200: Fix unchecked return value in read_raw
  MAINTAINERS: add entry for ADE9000 driver
  iio: accel: sca3000: remove unused last_timestamp field
  iio: accel: adxl372: remove unused int2_bitmask field
  iio: adc: ad7766: Use iio_trigger_generic_data_rdy_poll()
  iio: magnetometer: Remove IRQF_ONESHOT
  iio: Replace IRQF_ONESHOT with IRQF_NO_THREAD
  iio: Use IRQF_NO_THREAD
  iio: dac: Add MAX22007 DAC driver support
  dt-bindings: iio: dac: Add max22007
  ...
This commit is contained in:
Greg Kroah-Hartman
2026-02-02 17:08:30 +01:00
125 changed files with 12096 additions and 1227 deletions

View File

@@ -161,3 +161,14 @@ Contact: linux-i3c@vger.kernel.org
Description:
These directories are just symbolic links to
/sys/bus/i3c/devices/i3c-<bus-id>/<bus-id>-<device-pid>.
What: /sys/bus/i3c/devices/i3c-<bus-id>/<bus-id>-<device-pid>/dev_nack_retry_count
KernelVersion: 6.18
Contact: linux-i3c@vger.kernel.org
Description:
Expose the dev_nak_retry_count which controls the number of
automatic retries that will be performed by the controller when
the target device returns a NACK response. A value of 0 disables
the automatic retries. Exist only when I3C constroller supports
this retry on nack feature.

View File

@@ -3,9 +3,12 @@ Date: July 2015
KernelVersion: 4.7
Contact: linux-iio@vger.kernel.org
Description:
Writing '1' will perform a FOC (Fast Online Calibration). The
corresponding calibration offsets can be read from `*_calibbias`
entries.
Writing '1' either perform a FOC (Fast Online Calibration) or
enter calibration mode.
Writing '0` exits calibration mode. It is a NOP for FOC enabled
sensors.
The corresponding calibration offsets can be read from `*_calibbias`
entries.
What: /sys/bus/iio/devices/iio:deviceX/id
Date: September 2017

View File

@@ -0,0 +1,120 @@
# 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,ad4062.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD4062 ADC family device driver
maintainers:
- Jorge Marques <jorge.marques@analog.com>
description: |
Analog Devices AD4062 Single Channel Precision SAR ADC family
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4060.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4062.pdf
properties:
compatible:
enum:
- adi,ad4060
- adi,ad4062
reg:
maxItems: 1
interrupts:
description:
Two pins are available that can be configured as either a general purpose
digital output, device enable signal (used to synchronise other parts of
the signal chain with ADC sampling), device ready (GP1 only) or various
interrupt signals. If intended for use as a GPIO or device enable, will not
present here.
minItems: 1
items:
- description:
GP0 pin, cannot be configured as DEV_RDY.
- description:
GP1 pin, can be configured to any setting.
interrupt-names:
minItems: 1
items:
- const: gp0
- const: gp1
gpio-controller:
description:
Marks the device node as a GPIO controller. GPs not listed as interrupts
are exposed as a GPO.
'#gpio-cells':
const: 2
description:
The first cell is the GPIO number and the second cell specifies
GPIO flags, as defined in <dt-bindings/gpio/gpio.h>.
vdd-supply:
description: Analog power supply.
vio-supply:
description: Digital interface logic power supply.
ref-supply:
description:
Reference voltage to set the ADC full-scale range. If not present,
vdd-supply is used as the reference voltage.
required:
- compatible
- reg
- vdd-supply
- vio-supply
allOf:
- $ref: /schemas/i3c/i3c.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i3c {
#address-cells = <3>;
#size-cells = <0>;
adc@0,2ee007c0000 {
reg = <0x0 0x2ee 0x7c0000>;
vdd-supply = <&vdd>;
vio-supply = <&vio>;
ref-supply = <&ref>;
interrupt-parent = <&gpio>;
interrupts = <0 0 IRQ_TYPE_EDGE_RISING>,
<0 1 IRQ_TYPE_EDGE_FALLING>;
interrupt-names = "gp0", "gp1";
};
};
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i3c {
#address-cells = <3>;
#size-cells = <0>;
adc@0,2ee007c0000 {
reg = <0x0 0x2ee 0x7c0000>;
vdd-supply = <&vdd>;
vio-supply = <&vio>;
ref-supply = <&ref>;
gpio-controller;
#gpio-cells = <2>;
};
};

View File

@@ -0,0 +1,191 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/adi,ad4134.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD4134 ADC
maintainers:
- Marcelo Schmitt <marcelo.schmitt@analog.com>
description: |
The AD4134 is a quad channel, low noise, simultaneous sampling, precision
analog-to-digital converter (ADC).
Specifications can be found at:
https://www.analog.com/media/en/technical-documentation/data-sheets/ad4134.pdf
$ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
enum:
- adi,ad4134
reg:
maxItems: 1
spi-max-frequency:
maximum: 50000000
avdd5-supply:
description: A 5V supply that powers the chip's analog circuitry.
dvdd5-supply:
description: A 5V supply that powers the chip's digital circuitry.
iovdd-supply:
description:
A 1.8V supply that sets the logic levels for the digital interface pins.
refin-supply:
description:
A 4.096V or 5V supply that serves as reference for ADC conversions.
avdd1v8-supply:
description: A 1.8V supply used by the analog circuitry.
dvdd1v8-supply:
description: A 1.8V supply used by the digital circuitry.
clkvdd-supply:
description: A 1.8V supply for the chip's clock management circuit.
ldoin-supply:
description:
A 2.6V to 5.5V supply that generates 1.8V for AVDD1V8, DVDD1V8, and CLKVDD
pins.
clocks:
maxItems: 1
description:
Required external clock source. Can specify either a crystal or CMOS clock
source. If an external crystal is set, connect the CLKSEL pin to IOVDD.
Otherwise, connect the CLKSEL pin to IOGND and the external CMOS clock
signal to the XTAL2/CLKIN pin.
clock-names:
enum:
- xtal
- clkin
default: clkin
'#clock-cells':
const: 0
clock-output-names:
maxItems: 1
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
powerdown-gpios:
description:
Active low GPIO connected to the /PDN pin. Forces the device into full
power-down mode when brought low. Pull this input to IOVDD for normal
operation.
maxItems: 1
odr-gpios:
description:
GPIO connected to ODR pin. Used to sample ADC data in minimum I/O mode.
maxItems: 1
adi,asrc-mode:
$ref: /schemas/types.yaml#/definitions/string
description:
Asynchronous Sample Rate Converter (ASRC) operation mode control input.
Describes whether the MODE pin is set to a high level (for master mode
operation) or to a low level (for slave mode operation).
enum: [ high, low ]
default: low
adi,dclkio:
description:
DCLK pin I/O direction control for when the device operates in Pin Control
Slave Mode or in SPI Control Mode. Describes if DEC0/DCLKIO pin is at a
high level (which configures DCLK as an output) or to set to a low level
(configuring DCLK for input).
enum: [ out, in ]
default: in
adi,dclkmode:
description:
DCLK mode control for when the device operates in Pin Control Slave Mode
or in SPI Control Mode. Describes whether the DEC1/DCLKMODE pin is set to
a high level (configuring the DCLK to operate in free running mode) or
to a low level (to configure DCLK to operate in gated mode).
enum: [ free-running, gated ]
default: gated
required:
- compatible
- reg
- avdd5-supply
- dvdd5-supply
- iovdd-supply
- refin-supply
- clocks
- clock-names
oneOf:
- required:
- ldoin-supply
- required:
- avdd1v8-supply
- dvdd1v8-supply
- clkvdd-supply
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "adi,ad4134";
reg = <0>;
spi-max-frequency = <1000000>;
reset-gpios = <&gpio0 86 GPIO_ACTIVE_LOW>;
odr-gpios = <&gpio0 87 GPIO_ACTIVE_HIGH>;
powerdown-gpios = <&gpio0 88 GPIO_ACTIVE_LOW>;
clocks = <&sys_clk>;
clock-names = "clkin";
avdd5-supply = <&avdd5>;
dvdd5-supply = <&dvdd5>;
iovdd-supply = <&iovdd>;
refin-supply = <&refin>;
avdd1v8-supply = <&avdd1v8>;
dvdd1v8-supply = <&dvdd1v8>;
clkvdd-supply = <&clkvdd>;
regulators {
vcm_reg: vcm-output {
regulator-name = "ad4134-vcm";
};
};
};
};
...

View File

@@ -4,18 +4,26 @@
$id: http://devicetree.org/schemas/iio/adc/adi,ad7768-1.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices AD7768-1 ADC device driver
title: Analog Devices AD7768-1 ADC family
maintainers:
- Michael Hennerich <michael.hennerich@analog.com>
description: |
Datasheet at:
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7768-1.pdf
Analog Devices AD7768-1 24-Bit Single Channel Low Power sigma-delta ADC family
https://www.analog.com/media/en/technical-documentation/data-sheets/ad7768-1.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/adaq7767-1.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/adaq7768-1.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/adaq7769-1.pdf
properties:
compatible:
const: adi,ad7768-1
enum:
- adi,ad7768-1
- adi,adaq7767-1
- adi,adaq7768-1
- adi,adaq7769-1
reg:
maxItems: 1
@@ -58,6 +66,25 @@ properties:
description:
ADC reference voltage supply
adi,aaf-gain-bp:
description: |
Specifies the gain applied by the Analog Anti-Aliasing Filter (AAF)
to the ADC input in basis points (one hundredth of a percent).
The hardware gain is determined by which input pin(s) the signal goes
through into the AAF. The possible connections are:
* For the ADAQ7767-1: Input connected to IN1±, IN2± or IN3±.
* For the ADAQ7769-1: OUT_PGA pin connected to IN1_AAF+, IN2_AAF+,
or IN3_AAF+.
enum: [1430, 3640, 10000]
default: 10000
pga-gpios:
description:
GAIN 0, GAIN1 and GAIN2 pins for gain selection. For devices that have
PGA configuration input pins, pga-gpios must be defined.
minItems: 3
maxItems: 3
adi,sync-in-gpios:
maxItems: 1
description:
@@ -147,6 +174,35 @@ patternProperties:
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
# AAF Gain property only applies to ADAQ7767-1 and ADAQ7769-1 devices
- if:
properties:
compatible:
contains:
enum:
- adi,adaq7767-1
- adi,adaq7769-1
then:
required:
- adi,aaf-gain-bp
else:
properties:
adi,aaf-gain-bp: false
- if:
properties:
compatible:
contains:
enum:
- adi,adaq7768-1
- adi,adaq7769-1
then:
required:
- pga-gpios
else:
properties:
pga-gpios: false
unevaluatedProperties: false
examples:

View File

@@ -18,6 +18,7 @@ description: |
All the parts support the register map described by Application Note AN-877
https://www.analog.com/media/en/technical-documentation/application-notes/AN-877.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/AD9211.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/AD9265.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/AD9434.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/AD9467.pdf
@@ -25,6 +26,7 @@ description: |
properties:
compatible:
enum:
- adi,ad9211
- adi,ad9265
- adi,ad9434
- adi,ad9467

View File

@@ -44,6 +44,9 @@ properties:
Input clock used to derive the sample clock. Expected to be the
SoC's APB clock.
interrupts:
maxItems: 1
resets:
maxItems: 1

View File

@@ -0,0 +1,63 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/nxp,s32g2-sar-adc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP Successive Approximation ADC
description:
The NXP SAR ADC provides fast and accurate analog-to-digital
conversion using the Successive Approximation Register (SAR) method.
It has 12-bit resolution with 8 input channels. Conversions can be
launched in software or using hardware triggers. It supports
continuous and one-shot modes with separate registers.
maintainers:
- Daniel Lezcano <daniel.lezcano@kernel.org>
properties:
compatible:
oneOf:
- const: nxp,s32g2-sar-adc
- items:
- const: nxp,s32g3-sar-adc
- const: nxp,s32g2-sar-adc
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
dmas:
maxItems: 1
dma-names:
const: rx
required:
- compatible
- reg
- interrupts
- clocks
- dmas
- dma-names
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
adc@401f8000 {
compatible = "nxp,s32g2-sar-adc";
reg = <0x401f8000 0x1000>;
interrupts = <GIC_SPI 70 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 0x41>;
dmas = <&edma0 0 32>;
dma-names = "rx";
};

View File

@@ -0,0 +1,82 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/ti,ads1018.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TI ADS1018/ADS1118 SPI analog to digital converter
maintainers:
- Kurt Borja <kuurtb@gmail.com>
description: |
The ADS1018/ADS1118 is a precision, low-power, 12-bit/16-bit, analog to
digital converter (ADC). It integrates a programmable gain amplifier (PGA),
internal voltage reference, oscillator and high-accuracy temperature sensor.
Datasheets:
- ADS1018: https://www.ti.com/lit/ds/symlink/ads1018.pdf
- ADS1118: https://www.ti.com/lit/ds/symlink/ads1118.pdf
properties:
compatible:
enum:
- ti,ads1018
- ti,ads1118
reg:
maxItems: 1
vdd-supply: true
spi-max-frequency:
maximum: 4000000
spi-cpha: true
interrupts:
description: DOUT/DRDY (Data Out/Data Ready) line.
maxItems: 1
drdy-gpios:
description:
Extra GPIO line connected to DOUT/DRDY (Data Out/Data Ready). This allows
distinguishing between interrupts triggered by the data-ready signal and
interrupts triggered by an SPI transfer.
maxItems: 1
'#io-channel-cells':
const: 1
required:
- compatible
- reg
- vdd-supply
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "ti,ads1118";
reg = <0>;
spi-max-frequency = <4000000>;
spi-cpha;
vdd-supply = <&vdd_3v3_reg>;
interrupts-extended = <&gpio 14 IRQ_TYPE_EDGE_FALLING>;
drdy-gpios = <&gpio 14 GPIO_ACTIVE_LOW>;
};
};

View File

@@ -0,0 +1,208 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/ti,ads131m02.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments ADS131M0x 2-, 3-, 4-, 6- and 8-Channel ADCs
maintainers:
- Oleksij Rempel <o.rempel@pengutronix.de>
description: |
The ADS131M0x are a family of multichannel, simultaneous sampling,
24-bit, delta-sigma, analog-to-digital converters (ADCs) with a
built-in programmable gain amplifier (PGA) and internal reference.
Communication with the ADC chip is via SPI.
Datasheets:
- ADS131M02: https://www.ti.com/lit/ds/symlink/ads131m02.pdf
- ADS131M03: https://www.ti.com/lit/ds/symlink/ads131m03.pdf
- ADS131M04: https://www.ti.com/lit/ds/symlink/ads131m04.pdf
- ADS131M06: https://www.ti.com/lit/ds/symlink/ads131m06.pdf
- ADS131M08: https://www.ti.com/lit/ds/symlink/ads131m08.pdf
properties:
compatible:
enum:
- ti,ads131m02
- ti,ads131m03
- ti,ads131m04
- ti,ads131m06
- ti,ads131m08
reg:
description: SPI chip select number.
clocks:
description:
Phandle to the external clock source required by the ADC's CLKIN pin.
The datasheet recommends specific frequencies based on the desired power
mode (e.g., 8.192 MHz for High-Resolution mode).
maxItems: 1
avdd-supply:
description: Analog power supply (AVDD).
dvdd-supply:
description: Digital power supply (DVDD).
interrupts:
description: DRDY (Data Ready) output signal.
maxItems: 1
reset-gpios:
description: Optional RESET signal.
maxItems: 1
clock-names:
description:
Indicates if a crystal oscillator (XTAL) or CMOS signal is connected
(CLKIN). Note that XTAL mode is only supported on ADS131M06 and ADS131M08.
enum: [xtal, clkin]
refin-supply:
description: Optional external reference supply (REFIN).
'#address-cells':
const: 1
'#size-cells':
const: 0
required:
- compatible
- reg
- clocks
- clock-names
- avdd-supply
- dvdd-supply
patternProperties:
"^channel@[0-7]$":
type: object
$ref: /schemas/iio/adc/adc.yaml#
description: Properties for a single ADC channel.
properties:
reg:
description: The channel index (0-7).
minimum: 0
maximum: 7 # Max channels on ADS131M08
label: true
required:
- reg
unevaluatedProperties: false
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
- if:
# 20-pin devices: M02, M03, M04
# These do not support XTAL or REFIN.
properties:
compatible:
enum:
- ti,ads131m02
- ti,ads131m03
- ti,ads131m04
then:
properties:
clock-names:
const: clkin
refin-supply: false
- if:
# ADS131M02: 2 channels max (0-1)
properties:
compatible:
contains:
const: ti,ads131m02
then:
patternProperties:
"^channel@[0-1]$":
properties:
reg:
maximum: 1
"^channel@[2-7]$": false
- if:
# ADS131M03: 3 channels max (0-2)
properties:
compatible:
contains:
const: ti,ads131m03
then:
patternProperties:
"^channel@[0-2]$":
properties:
reg:
maximum: 2
"^channel@[3-7]$": false
- if:
# ADS131M04: 4 channels max (0-3)
properties:
compatible:
contains:
const: ti,ads131m04
then:
patternProperties:
"^channel@[0-3]$":
properties:
reg:
maximum: 3
"^channel@[4-7]$": false
- if:
# ADS131M06: 6 channels max (0-5)
properties:
compatible:
contains:
const: ti,ads131m06
then:
patternProperties:
"^channel@[0-5]$":
properties:
reg:
maximum: 5
"^channel@[6-7]$": false
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/stm32mp1-clks.h>
spi1 {
#address-cells = <1>;
#size-cells = <0>;
adc@0 {
compatible = "ti,ads131m02";
reg = <0>;
spi-max-frequency = <8000000>;
clocks = <&rcc CK_MCO2>;
clock-names = "clkin";
avdd-supply = <&vdd_ana>;
dvdd-supply = <&vdd_dig>;
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
label = "input_voltage";
};
channel@1 {
reg = <1>;
label = "input_current";
};
};
};

View File

@@ -0,0 +1,87 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/amplifiers/adi,adl8113.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices ADL8113 Low Noise Amplifier with integrated bypass switches
maintainers:
- Antoniu Miclaus <antoniu.miclaus@analog.com>
description: |
The ADL8113 is a 10MHz to 12GHz Low Noise Amplifier with integrated bypass
switches controlled by two GPIO pins (VA and VB). The device supports four
operation modes:
- Internal Amplifier: VA=0, VB=0 - Signal passes through the internal LNA
- Internal Bypass: VA=1, VB=1 - Signal bypasses through internal path
- External Bypass A: VA=0, VB=1 - Signal routes from RFIN to OUT_A and from IN_A to RFOUT
- External Bypass B: VA=1, VB=0 - Signal routes from RFIN to OUT_B and from IN_B to RFOUT
https://www.analog.com/en/products/adl8113.html
properties:
compatible:
const: adi,adl8113
vdd1-supply: true
vdd2-supply: true
vss2-supply: true
ctrl-gpios:
items:
- description: VA control pin
- description: VB control pin
adi,external-bypass-a-gain-db:
description:
Gain in dB of external amplifier connected to bypass path A (OUT_A/IN_A).
When specified, this gain value becomes selectable via the hardwaregain
attribute and automatically routes through the external A path.
adi,external-bypass-b-gain-db:
description:
Gain in dB of external amplifier connected to bypass path B (OUT_B/IN_B).
When specified, this gain value becomes selectable via the hardwaregain
attribute and automatically routes through the external B path.
required:
- compatible
- ctrl-gpios
- vdd1-supply
- vdd2-supply
- vss2-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
/* Basic configuration with only internal paths */
amplifier {
compatible = "adi,adl8113";
ctrl-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>,
<&gpio 23 GPIO_ACTIVE_HIGH>;
vdd1-supply = <&vdd1_5v>;
vdd2-supply = <&vdd2_3v3>;
vss2-supply = <&vss2_neg>;
};
- |
#include <dt-bindings/gpio/gpio.h>
/* Configuration with external bypass amplifiers */
amplifier {
compatible = "adi,adl8113";
ctrl-gpios = <&gpio 24 GPIO_ACTIVE_HIGH>,
<&gpio 25 GPIO_ACTIVE_HIGH>;
vdd1-supply = <&vdd1_5v>;
vdd2-supply = <&vdd2_3v3>;
vss2-supply = <&vss2_neg>;
adi,external-bypass-a-gain-db = <20>; /* 20dB external amp on path A */
adi,external-bypass-b-gain-db = <6>; /* 6dB external amp on path B */
};
...

View File

@@ -0,0 +1,120 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/dac/adi,max22007.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices MAX22007 DAC
maintainers:
- Janani Sunil <janani.sunil@analog.com>
description:
The MAX22007 is a quad-channel, 12-bit digital-to-analog converter (DAC)
with integrated precision output amplifiers and current output capability.
Each channel can be independently configured for voltage or current output.
Datasheet available at https://www.analog.com/en/products/max22007.html
$ref: /schemas/spi/spi-peripheral-props.yaml#
properties:
compatible:
const: adi,max22007
reg:
maxItems: 1
spi-max-frequency:
maximum: 500000
'#address-cells':
const: 1
'#size-cells':
const: 0
vdd-supply:
description: Low-Voltage Power Supply from +2.7V to +5.5V.
hvdd-supply:
description:
Positive High-Voltage Power Supply from +8V to (HVSS +24V) for
the Output Channels.
hvss-supply:
description:
Optional Negative High-Voltage Power Supply from -2V to 0V for the Output
Channels. For most applications HVSS can be connected to GND (0V), but for
applications requiring output down to true 0V or 0mA, connect to a -2V supply.
reset-gpios:
maxItems: 1
description:
Active low GPIO.
patternProperties:
"^channel@[0-3]$":
$ref: /schemas/iio/dac/dac.yaml#
type: object
description:
Represents the external channels which are connected to the DAC.
properties:
reg:
description: Channel number
items:
minimum: 0
maximum: 3
adi,ch-func:
description:
Channel output type. Use CH_FUNC_VOLTAGE_OUTPUT for voltage
output or CH_FUNC_CURRENT_OUTPUT for current output.
$ref: /schemas/types.yaml#/definitions/uint32
enum: [1, 2]
required:
- reg
- adi,ch-func
unevaluatedProperties: false
required:
- compatible
- reg
- vdd-supply
- hvdd-supply
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/iio/addac/adi,ad74413r.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
dac@0 {
compatible = "adi,max22007";
reg = <0>;
spi-max-frequency = <500000>;
reset-gpios = <&gpio 19 GPIO_ACTIVE_LOW>;
vdd-supply = <&vdd_reg>;
hvdd-supply = <&hvdd_reg>;
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
adi,ch-func = <CH_FUNC_VOLTAGE_OUTPUT>;
};
channel@1 {
reg = <1>;
adi,ch-func = <CH_FUNC_CURRENT_OUTPUT>;
};
};
};
...

View File

@@ -0,0 +1,302 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/dac/microchip,mcp47feb02.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip MCP47F(E/V)B(0/1/2)(1/2/4/8) DAC with I2C Interface Families
maintainers:
- Ariana Lazar <ariana.lazar@microchip.com>
description: |
Datasheet for MCP47FEB01, MCP47FEB11, MCP47FEB21, MCP47FEB02, MCP47FEB12,
MCP47FEB22 can be found here:
https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005375A.pdf
Datasheet for MCP47FVB01, MCP47FVB11, MCP47FVB21, MCP47FVB02, MCP47FVB12,
MCP47FVB22 can be found here:
https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005405A.pdf
Datasheet for MCP47FEB04, MCP47FEB14, MCP47FEB24, MCP47FEB08, MCP47FEB18,
MCP47FEB28, MCP47FVB04, MCP47FVB14, MCP47FVB24, MCP47FVB08, MCP47FVB18,
MCP47FVB28 can be found here:
https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP47FXBX48-Data-Sheet-DS200006368A.pdf
+------------+--------------+-------------+-------------+------------+
| Device | Resolution | Channels | Vref number | Memory |
|------------|--------------|-------------|-------------|------------|
| MCP47FEB01 | 8-bit | 1 | 1 | EEPROM |
| MCP47FEB11 | 10-bit | 1 | 1 | EEPROM |
| MCP47FEB21 | 12-bit | 1 | 1 | EEPROM |
|------------|--------------|-------------|-------------|------------|
| MCP47FEB02 | 8-bit | 2 | 1 | EEPROM |
| MCP47FEB12 | 10-bit | 2 | 1 | EEPROM |
| MCP47FEB22 | 12-bit | 2 | 1 | EEPROM |
|------------|--------------|-------------|-------------|------------|
| MCP47FVB01 | 8-bit | 1 | 1 | RAM |
| MCP47FVB11 | 10-bit | 1 | 1 | RAM |
| MCP47FVB21 | 12-bit | 1 | 1 | RAM |
|------------|--------------|-------------|-------------|------------|
| MCP47FVB02 | 8-bit | 2 | 1 | RAM |
| MCP47FVB12 | 10-bit | 2 | 1 | RAM |
| MCP47FVB22 | 12-bit | 2 | 1 | RAM |
|------------|--------------|-------------|-------------|------------|
| MCP47FVB04 | 8-bit | 4 | 2 | RAM |
| MCP47FVB14 | 10-bit | 4 | 2 | RAM |
| MCP47FVB24 | 12-bit | 4 | 2 | RAM |
|------------|--------------|-------------|-------------|------------|
| MCP47FVB08 | 8-bit | 8 | 2 | RAM |
| MCP47FVB18 | 10-bit | 8 | 2 | RAM |
| MCP47FVB28 | 12-bit | 8 | 2 | RAM |
|------------|--------------|-------------|-------------|------------|
| MCP47FEB04 | 8-bit | 4 | 2 | EEPROM |
| MCP47FEB14 | 10-bit | 4 | 2 | EEPROM |
| MCP47FEB24 | 12-bit | 4 | 2 | EEPROM |
|------------|--------------|-------------|-------------|------------|
| MCP47FEB08 | 8-bit | 8 | 2 | EEPROM |
| MCP47FEB18 | 10-bit | 8 | 2 | EEPROM |
| MCP47FEB28 | 12-bit | 8 | 2 | EEPROM |
+------------+--------------+-------------+-------------+------------+
properties:
compatible:
enum:
- microchip,mcp47feb01
- microchip,mcp47feb11
- microchip,mcp47feb21
- microchip,mcp47feb02
- microchip,mcp47feb12
- microchip,mcp47feb22
- microchip,mcp47fvb01
- microchip,mcp47fvb11
- microchip,mcp47fvb21
- microchip,mcp47fvb02
- microchip,mcp47fvb12
- microchip,mcp47fvb22
- microchip,mcp47fvb04
- microchip,mcp47fvb14
- microchip,mcp47fvb24
- microchip,mcp47fvb08
- microchip,mcp47fvb18
- microchip,mcp47fvb28
- microchip,mcp47feb04
- microchip,mcp47feb14
- microchip,mcp47feb24
- microchip,mcp47feb08
- microchip,mcp47feb18
- microchip,mcp47feb28
reg:
maxItems: 1
"#address-cells":
const: 1
"#size-cells":
const: 0
vdd-supply:
description:
Provides power to the chip and it could be used as reference voltage. The
voltage is used to calculate scale. For parts without EEPROM at powerup
this will be the selected as voltage reference.
vref-supply:
description: |
Vref pin (it could be found as Vref0 into the datasheet) may be used as a
voltage reference when this supply is specified. The internal reference
will be taken into account for voltage reference besides VDD if this supply
does not exist.
This supply will be voltage reference for the following outputs:
- for single-channel device: Vout0;
- for dual-channel device: Vout0, Vout1;
- for quad-channel device: Vout0, Vout2;
- for octal-channel device: Vout0, Vout2, Vout6, Vout8;
vref1-supply:
description: |
Vref1 pin may be used as a voltage reference when this supply is specified.
The internal reference will be taken into account for voltage reference
beside VDD if this supply does not exist.
This supply will be voltage reference for the following outputs:
- for quad-channel device: Vout1, Vout3;
- for octal-channel device: Vout1, Vout3, Vout5, Vout7;
lat-gpios:
description:
LAT pin to be used as a hardware trigger to synchronously update the DAC
channels. The pin is active Low. It could be also found as LAT0 in
datasheet.
maxItems: 1
lat1-gpios:
description:
LAT1 pin to be used as a hardware trigger to synchronously update the odd
DAC channels on devices with 4 and 8 channels. The pin is active Low.
maxItems: 1
microchip,vref-buffered:
type: boolean
description:
Enable buffering of the external Vref/Vref0 pin in cases where the
external reference voltage does not have sufficient current capability in
order not to drop its voltage when connected to the internal resistor
ladder circuit.
microchip,vref1-buffered:
type: boolean
description:
Enable buffering of the external Vref1 pin in cases where the external
reference voltage does not have sufficient current capability in order not
to drop its voltage when connected to the internal resistor ladder
circuit.
patternProperties:
"^channel@[0-7]$":
$ref: dac.yaml
type: object
description: Voltage output channel.
properties:
reg:
description: The channel number.
minItems: 1
maxItems: 8
label:
description: Unique name to identify which channel this is.
required:
- reg
unevaluatedProperties: false
required:
- compatible
- reg
- vdd-supply
allOf:
- if:
properties:
compatible:
contains:
enum:
- microchip,mcp47feb01
- microchip,mcp47feb11
- microchip,mcp47feb21
- microchip,mcp47fvb01
- microchip,mcp47fvb11
- microchip,mcp47fvb21
then:
properties:
lat1-gpios: false
vref1-supply: false
microchip,vref1-buffered: false
channel@0:
properties:
reg:
const: 0
patternProperties:
"^channel@[1-7]$": false
- if:
properties:
compatible:
contains:
enum:
- microchip,mcp47feb02
- microchip,mcp47feb12
- microchip,mcp47feb22
- microchip,mcp47fvb02
- microchip,mcp47fvb12
- microchip,mcp47fvb22
then:
properties:
lat1-gpios: false
vref1-supply: false
microchip,vref1-buffered: false
patternProperties:
"^channel@[0-1]$":
properties:
reg:
enum: [0, 1]
"^channel@[2-7]$": false
- if:
properties:
compatible:
contains:
enum:
- microchip,mcp47fvb04
- microchip,mcp47fvb14
- microchip,mcp47fvb24
- microchip,mcp47feb04
- microchip,mcp47feb14
- microchip,mcp47feb24
then:
patternProperties:
"^channel@[0-3]$":
properties:
reg:
enum: [0, 1, 2, 3]
"^channel@[4-7]$": false
- if:
properties:
compatible:
contains:
enum:
- microchip,mcp47fvb08
- microchip,mcp47fvb18
- microchip,mcp47fvb28
- microchip,mcp47feb08
- microchip,mcp47feb18
- microchip,mcp47feb28
then:
patternProperties:
"^channel@[0-7]$":
properties:
reg:
enum: [0, 1, 2, 3, 4, 5, 6, 7]
- if:
not:
required:
- vref-supply
then:
properties:
microchip,vref-buffered: false
- if:
not:
required:
- vref1-supply
then:
properties:
microchip,vref1-buffered: false
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
dac@0 {
compatible = "microchip,mcp47feb02";
reg = <0>;
vdd-supply = <&vdac_vdd>;
vref-supply = <&vref_reg>;
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
label = "Adjustable_voltage_ch0";
};
channel@1 {
reg = <0x1>;
label = "Adjustable_voltage_ch1";
};
};
};
...

View File

@@ -40,6 +40,12 @@ properties:
items:
- const: ref_in
'#clock-cells':
const: 0
clock-output-names:
maxItems: 1
chip-enable-gpios:
description:
GPIO that controls the Chip Enable Pin.
@@ -97,6 +103,8 @@ examples:
spi-max-frequency = <10000000>;
clocks = <&adf4377_ref_in>;
clock-names = "ref_in";
#clock-cells = <0>;
clock-output-names = "adf4377";
};
};
...

View File

@@ -0,0 +1,132 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/pressure/honeywell,abp2030pa.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Honeywell abp2030pa pressure sensor
maintainers:
- Petre Rodan <petre.rodan@subdimension.ro>
description: |
Honeywell pressure sensor of model abp2030pa.
This sensor has an I2C and SPI interface.
There are many models with different pressure ranges available. The vendor
calls them "ABP2 series". All of them have an identical programming model and
differ in the pressure range and measurement unit.
To support different models one needs to specify its pressure triplet.
For custom silicon chips not covered by the Honeywell ABP2 series datasheet,
the pressure values can be specified manually via honeywell,pmin-pascal and
honeywell,pmax-pascal.
Specifications about the devices can be found at:
https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/basic-abp2-series/documents/sps-siot-abp2-series-datasheet-32350268-en.pdf
properties:
compatible:
const: honeywell,abp2030pa
reg:
maxItems: 1
interrupts:
description:
Optional interrupt for indicating end of conversion.
SPI variants of ABP2 chips do not provide this feature.
maxItems: 1
honeywell,pressure-triplet:
description: |
Case-sensitive five character string that defines pressure range, unit
and type as part of the device nomenclature. In the unlikely case of a
custom chip, unset and provide pmin-pascal and pmax-pascal instead.
enum: [001BA, 1.6BA, 2.5BA, 004BA, 006BA, 008BA, 010BA, 012BA, 001BD,
1.6BD, 2.5BD, 004BD, 001BG, 1.6BG, 2.5BG, 004BG, 006BG, 008BG,
010BG, 012BG, 001GG, 1.2GG, 100KA, 160KA, 250KA, 001KD, 1.6KD,
2.5KD, 004KD, 006KD, 010KD, 016KD, 025KD, 040KD, 060KD, 100KD,
160KD, 250KD, 400KD, 001KG, 1.6KG, 2.5KG, 004KG, 006KG, 010KG,
016KG, 025KG, 040KG, 060KG, 100KG, 160KG, 250KG, 400KG, 600KG,
800KG, 250LD, 600LD, 600LG, 2.5MD, 006MD, 010MD, 016MD, 025MD,
040MD, 060MD, 100MD, 160MD, 250MD, 400MD, 600MD, 006MG, 010MG,
016MG, 025MG, 040MG, 060MG, 100MG, 160MG, 250MG, 400MG, 600MG,
001ND, 002ND, 004ND, 005ND, 010ND, 020ND, 030ND, 002NG, 004NG,
005NG, 010NG, 020NG, 030NG, 015PA, 030PA, 060PA, 100PA, 150PA,
175PA, 001PD, 005PD, 015PD, 030PD, 060PD, 001PG, 005PG, 015PG,
030PG, 060PG, 100PG, 150PG, 175PG]
$ref: /schemas/types.yaml#/definitions/string
honeywell,pmin-pascal:
description:
Minimum pressure value the sensor can measure in pascal.
honeywell,pmax-pascal:
description:
Maximum pressure value the sensor can measure in pascal.
spi-max-frequency:
maximum: 800000
vdd-supply: true
required:
- compatible
- reg
- vdd-supply
oneOf:
- required:
- honeywell,pressure-triplet
- required:
- honeywell,pmin-pascal
- honeywell,pmax-pascal
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml
- if:
required:
- honeywell,pressure-triplet
then:
properties:
honeywell,pmin-pascal: false
honeywell,pmax-pascal: false
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
pressure@18 {
compatible = "honeywell,abp2030pa";
reg = <0x18>;
interrupt-parent = <&gpio3>;
interrupts = <21 IRQ_TYPE_EDGE_RISING>;
honeywell,pressure-triplet = "001BA";
vdd-supply = <&vcc_3v3>;
};
};
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
pressure@0 {
compatible = "honeywell,abp2030pa";
reg = <0>;
spi-max-frequency = <800000>;
honeywell,pressure-triplet = "001PD";
vdd-supply = <&vcc_3v3>;
};
};
...

View File

@@ -0,0 +1,53 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/proximity/rfdigital,rfd77402.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: RF Digital RFD77402 ToF sensor
maintainers:
- Shrikant Raskar <raskar.shree97@gmail.com>
description:
The RF Digital RFD77402 is a Time-of-Flight (ToF) proximity and distance
sensor providing up to 200 mm range measurement over an I2C interface.
properties:
compatible:
const: rfdigital,rfd77402
reg:
maxItems: 1
interrupts:
maxItems: 1
description:
Interrupt asserted when a new distance measurement is available.
vdd-supply:
description: Regulator that provides power to the sensor.
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
proximity@4c {
compatible = "rfdigital,rfd77402";
reg = <0x4c>;
vdd-supply = <&vdd_3v3>;
interrupt-parent = <&gpio>;
interrupts = <4 IRQ_TYPE_EDGE_FALLING>;
};
};
...

View File

@@ -229,6 +229,10 @@ properties:
- meas,tsys01
# MEMSIC magnetometer
- memsic,mmc35240
# MEMSIC 3-axis magnetometer
- memsic,mmc5603
# MEMSIC 3-axis magnetometer (Support I3C HDR)
- memsic,mmc5633
# MEMSIC 3-axis accelerometer
- memsic,mxc4005
# MEMSIC 2-axis 8-bit digital accelerometer

View File

@@ -1361,6 +1361,8 @@ patternProperties:
description: Revolution Robotics, Inc. (Revotics)
"^rex,.*":
description: iMX6 Rex Project
"^rfdigital,.*":
description: RF Digital Corporation
"^richtek,.*":
description: Richtek Technology Corporation
"^ricoh,.*":

View File

@@ -0,0 +1,148 @@
.. SPDX-License-Identifier: GPL-2.0-only
=============
AD4062 driver
=============
ADC driver for Analog Devices Inc. AD4060/AD4062 devices. The module name is
``ad4062``.
Supported devices
=================
The following chips are supported by this driver:
* `AD4060 <https://www.analog.com/AD4060>`_
* `AD4062 <https://www.analog.com/AD4062>`_
Wiring modes
============
The ADC is interfaced through an I3C bus, and contains two programmable GPIOs.
The ADC convert-start happens on the SDA rising edge of the I3C stop (P) bit
at the end of the read command.
The two programmable GPIOS are optional and have a role assigned if present in
the devicetree ``interrupt-names`` property:
- GP0: Is assigned the role of Threshold Either signal.
- GP1: Is assigned the role of Data Ready signal.
If the property ``gpio-controller`` is present in the devicetree, then the GPO
not present in the ``interrupt-names`` is exposed as a GPO.
Device attributes
=================
The ADC contains only one channel with following attributes:
.. list-table:: Channel attributes
:header-rows: 1
* - Attribute
- Description
* - ``in_voltage_calibscale``
- Sets the gain scaling factor that the hardware applies to the sample,
to compensate for system gain error.
* - ``in_voltage_oversampling_ratio``
- Sets device's burst averaging mode to over sample using the
internal sample rate. Value 1 disable the burst averaging mode.
* - ``in_voltage_oversampling_ratio_available``
- List of available oversampling values.
* - ``in_voltage_raw``
- Returns the raw ADC voltage value.
* - ``in_voltage_scale``
- Returns the channel scale in reference to the reference voltage
``ref-supply`` or ``vdd-supply`` if the former not present.
Also contain the following device attributes:
.. list-table:: Device attributes
:header-rows: 1
* - Attribute
- Description
* - ``sampling_frequency``
- Sets the duration of a single scan, used in the burst averaging mode.
The duration is described by ``(n_avg - 1) / fosc + tconv``, where
``n_avg`` is the oversampling ratio, ``fosc`` is the internal sample
rate and ``tconv`` is the ADC conversion time.
* - ``sampling_frequency_available``
- Lists the available sampling frequencies, computed on the current
oversampling ratio. If the ratio is 1, the frequency is ``1/tconv``.
Interrupts
==========
The interrupts are mapped through the ``interrupt-names`` and ``interrupts``
properties.
The ``interrupt-names`` ``gp0`` entry sets the role of Threshold signal, and
entry ``gp1`` the role of Data Ready signal.
If each is not present, the driver fallback to enabling the same role as an
I3C IBI.
Low-power mode
==============
The device enters low-power mode on idle to save power. Enabling an event puts
the device out of the low-power since the ADC autonomously samples to assert
the event condition.
IIO trigger support
===================
An IIO trigger ``ad4062-devX`` is registered by the driver to be used by the
same device, to capture samples to a software buffer. It is required to attach
the trigger to the device by setting the ``current_trigger`` before enabling
and reading the buffer.
The acquisition is sequential and bounded by the protocol timings, software
latency and internal timings, the sample rate is not configurable. The burst
averaging mode does impact the effective sample rate, since it increases the
internal timing to output a single sample.
Threshold events
================
The ADC supports a monitoring mode to raise threshold events. The driver
supports a single interrupt for both rising and falling readings.
The feature is enabled/disabled by setting ``thresh_either_en``. During monitor
mode, the device continuously operates in autonomous mode. Any register access
puts the device back in configuration mode, due to this, any access disables
monitor mode.
The following event attributes are available:
.. list-table:: Event attributes
:header-rows: 1
* - Attribute
- Description
* - ``sampling_frequency``
- Frequency used in the monitoring mode, sets the device internal sample
rate when the mode is activated.
* - ``sampling_frequency_available``
- List of available sample rates.
* - ``thresh_either_en``
- Enable monitoring mode.
* - ``thresh_falling_hysteresis``
- Set the hysteresis value for the minimum threshold.
* - ``thresh_falling_value``
- Set the minimum threshold value.
* - ``thresh_rising_hysteresis``
- Set the hysteresis value for the maximum threshold.
* - ``thresh_rising_value``
- Set the maximum threshold value.
GPO controller support
======================
The device supports using GP0 and GP1 as GPOs. If the devicetree contains the
node ``gpio-controller```, the device is marked as a GPIO controller and the
GPs not listed in ``interrupt-names`` are exposed as a GPO. The GPIO index
matches the pin name, so if GP0 is not exposed but GP1 is, index 0 is masked
out and only index 1 can be set.

View File

@@ -22,6 +22,7 @@ Industrial I/O Kernel Drivers
ad3552r
ad4000
ad4030
ad4062
ad4695
ad7191
ad7380

View File

@@ -1435,6 +1435,14 @@ F: Documentation/devicetree/bindings/iio/adc/adi,ad4030.yaml
F: Documentation/iio/ad4030.rst
F: drivers/iio/adc/ad4030.c
ANALOG DEVICES INC AD4062 DRIVER
M: Jorge Marques <jorge.marques@analog.com>
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad4062.yaml
F: Documentation/iio/ad4062.rst
F: drivers/iio/adc/ad4062.c
ANALOG DEVICES INC AD4080 DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
@@ -1452,6 +1460,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 AD4134 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,ad4134.yaml
F: drivers/iio/adc/ad4134.c
ANALOG DEVICES INC AD4170-4 DRIVER
M: Marcelo Schmitt <marcelo.schmitt@analog.com>
L: linux-iio@vger.kernel.org
@@ -1596,6 +1612,14 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
F: drivers/iio/dac/ad9739a.c
ANALOG DEVICES INC MAX22007 DRIVER
M: Janani Sunil <janani.sunil@analog.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/dac/adi,max22007.yaml
F: drivers/iio/dac/max22007.c
ANALOG DEVICES INC ADA4250 DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
@@ -1604,6 +1628,14 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/amplifiers/adi,ada4250.yaml
F: drivers/iio/amplifiers/ada4250.c
ANALOG DEVICES INC ADE9000 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,ade9000.yaml
F: drivers/iio/adc/ade9000.c
ANALOG DEVICES INC ADF4377 DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
@@ -11515,6 +11547,13 @@ F: lib/test_hmm*
F: mm/hmm*
F: tools/testing/selftests/mm/*hmm*
HONEYWELL ABP2030PA PRESSURE SENSOR SERIES IIO DRIVER
M: Petre Rodan <petre.rodan@subdimension.ro>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/pressure/honeywell,abp2030pa.yaml
F: drivers/iio/pressure/abp2030pa*
HONEYWELL HSC030PA PRESSURE SENSOR SERIES IIO DRIVER
M: Petre Rodan <petre.rodan@subdimension.ro>
L: linux-iio@vger.kernel.org
@@ -15663,6 +15702,13 @@ F: Documentation/ABI/testing/sysfs-bus-iio-potentiometer-mcp4531
F: drivers/iio/potentiometer/mcp4018.c
F: drivers/iio/potentiometer/mcp4531.c
MCP47FEB02 MICROCHIP DAC DRIVER
M: Ariana Lazar <ariana.lazar@microchip.com>
L: linux-iio@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/iio/dac/microchip,mcp47feb02.yaml
F: drivers/iio/dac/mcp47feb02.c
MCP4821 DAC DRIVER
M: Anshul Dalal <anshulusr@gmail.com>
L: linux-iio@vger.kernel.org
@@ -26031,6 +26077,13 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/adc/ti,ads1119.yaml
F: drivers/iio/adc/ti-ads1119.c
TI ADS1018 ADC DRIVER
M: Kurt Borja <kuurtb@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/adc/ti,ads1018.yaml
F: drivers/iio/adc/ti-ads1018.c
TI ADS7924 ADC DRIVER
M: Hugo Villeneuve <hvilleneuve@dimonoff.com>
L: linux-iio@vger.kernel.org

View File

@@ -683,6 +683,39 @@ static ssize_t hotjoin_show(struct device *dev, struct device_attribute *da, cha
static DEVICE_ATTR_RW(hotjoin);
static ssize_t dev_nack_retry_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sysfs_emit(buf, "%u\n", dev_to_i3cmaster(dev)->dev_nack_retry_count);
}
static ssize_t dev_nack_retry_count_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct i3c_bus *i3cbus = dev_to_i3cbus(dev);
struct i3c_master_controller *master = dev_to_i3cmaster(dev);
unsigned long val;
int ret;
ret = kstrtoul(buf, 0, &val);
if (ret)
return ret;
i3c_bus_maintenance_lock(i3cbus);
ret = master->ops->set_dev_nack_retry(master, val);
i3c_bus_maintenance_unlock(i3cbus);
if (ret)
return ret;
master->dev_nack_retry_count = val;
return count;
}
static DEVICE_ATTR_RW(dev_nack_retry_count);
static struct attribute *i3c_masterdev_attrs[] = {
&dev_attr_mode.attr,
&dev_attr_current_master.attr,
@@ -2370,19 +2403,16 @@ static int of_populate_i3c_bus(struct i3c_master_controller *master)
{
struct device *dev = &master->dev;
struct device_node *i3cbus_np = dev->of_node;
struct device_node *node;
int ret;
u32 val;
if (!i3cbus_np)
return 0;
for_each_available_child_of_node(i3cbus_np, node) {
for_each_available_child_of_node_scoped(i3cbus_np, node) {
ret = of_i3c_master_add_dev(master, node);
if (ret) {
of_node_put(node);
if (ret)
return ret;
}
}
/*
@@ -2959,6 +2989,9 @@ int i3c_master_register(struct i3c_master_controller *master,
i3c_master_register_new_i3c_devs(master);
i3c_bus_normaluse_unlock(&master->bus);
if (master->ops->set_dev_nack_retry)
device_create_file(&master->dev, &dev_attr_dev_nack_retry_count);
return 0;
err_del_dev:
@@ -2984,6 +3017,9 @@ void i3c_master_unregister(struct i3c_master_controller *master)
{
i3c_bus_notify(&master->bus, I3C_NOTIFY_BUS_REMOVE);
if (master->ops->set_dev_nack_retry)
device_remove_file(&master->dev, &dev_attr_dev_nack_retry_count);
i3c_master_i2c_adapter_cleanup(master);
i3c_master_unregister_i3c_devs(master);
i3c_master_bus_cleanup(master);

View File

@@ -5,6 +5,7 @@
* Author: Vitor Soares <vitor.soares@synopsys.com>
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/completion.h>
@@ -204,11 +205,17 @@
#define EXTENDED_CAPABILITY 0xe8
#define SLAVE_CONFIG 0xec
#define DW_I3C_DEV_NACK_RETRY_CNT_MAX 0x3
#define DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK GENMASK(30, 29)
#define DEV_ADDR_TABLE_DYNAMIC_MASK GENMASK(23, 16)
#define DEV_ADDR_TABLE_STATIC_MASK GENMASK(6, 0)
#define DEV_ADDR_TABLE_IBI_MDB BIT(12)
#define DEV_ADDR_TABLE_SIR_REJECT BIT(13)
#define DEV_ADDR_TABLE_DEV_NACK_RETRY_CNT(x) \
FIELD_PREP(DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK, (x))
#define DEV_ADDR_TABLE_LEGACY_I2C_DEV BIT(31)
#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) (((x) << 16) & GENMASK(23, 16))
#define DEV_ADDR_TABLE_STATIC_ADDR(x) ((x) & GENMASK(6, 0))
#define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) FIELD_PREP(DEV_ADDR_TABLE_DYNAMIC_MASK, x)
#define DEV_ADDR_TABLE_STATIC_ADDR(x) FIELD_PREP(DEV_ADDR_TABLE_STATIC_MASK, x)
#define DEV_ADDR_TABLE_LOC(start, idx) ((start) + ((idx) << 2))
#define I3C_BUS_SDR1_SCL_RATE 8000000
@@ -1489,6 +1496,40 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
return IRQ_HANDLED;
}
static int dw_i3c_master_set_dev_nack_retry(struct i3c_master_controller *m,
unsigned long dev_nack_retry_cnt)
{
struct dw_i3c_master *master = to_dw_i3c_master(m);
u32 reg;
int i;
if (dev_nack_retry_cnt > DW_I3C_DEV_NACK_RETRY_CNT_MAX) {
dev_err(&master->base.dev,
"Value %ld exceeds maximum %d\n",
dev_nack_retry_cnt, DW_I3C_DEV_NACK_RETRY_CNT_MAX);
return -ERANGE;
}
/*
* Update DAT entries for all currently attached devices.
* We directly iterate through the master's device array.
*/
for (i = 0; i < master->maxdevs; i++) {
/* Skip free/empty slots */
if (master->free_pos & BIT(i))
continue;
reg = readl(master->regs +
DEV_ADDR_TABLE_LOC(master->datstartaddr, i));
reg &= ~DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK;
reg |= DEV_ADDR_TABLE_DEV_NACK_RETRY_CNT(dev_nack_retry_cnt);
writel(reg, master->regs +
DEV_ADDR_TABLE_LOC(master->datstartaddr, i));
}
return 0;
}
static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
.bus_init = dw_i3c_master_bus_init,
.bus_cleanup = dw_i3c_master_bus_cleanup,
@@ -1509,6 +1550,7 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
.recycle_ibi_slot = dw_i3c_master_recycle_ibi_slot,
.enable_hotjoin = dw_i3c_master_enable_hotjoin,
.disable_hotjoin = dw_i3c_master_disable_hotjoin,
.set_dev_nack_retry = dw_i3c_master_set_dev_nack_retry,
};
/* default platform ops implementations */
@@ -1676,11 +1718,16 @@ static void dw_i3c_master_restore_addrs(struct dw_i3c_master *master)
if (master->free_pos & BIT(pos))
continue;
if (master->devs[pos].is_i2c_addr)
reg_val = DEV_ADDR_TABLE_LEGACY_I2C_DEV |
reg_val = readl(master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
if (master->devs[pos].is_i2c_addr) {
reg_val &= ~DEV_ADDR_TABLE_STATIC_MASK;
reg_val |= DEV_ADDR_TABLE_LEGACY_I2C_DEV |
DEV_ADDR_TABLE_STATIC_ADDR(master->devs[pos].addr);
else
reg_val = DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr);
} else {
reg_val &= ~DEV_ADDR_TABLE_DYNAMIC_MASK;
reg_val |= DEV_ADDR_TABLE_DYNAMIC_ADDR(master->devs[pos].addr);
}
writel(reg_val, master->regs + DEV_ADDR_TABLE_LOC(master->datstartaddr, pos));
}

View File

@@ -533,8 +533,8 @@ static int svc_i3c_master_handle_ibi_won(struct svc_i3c_master *master, u32 msta
static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master)
{
struct svc_i3c_i2c_dev_data *data;
struct i3c_dev_desc *dev = NULL;
unsigned int ibitype, ibiaddr;
struct i3c_dev_desc *dev;
u32 status, val;
int ret;
@@ -627,7 +627,7 @@ static void svc_i3c_master_ibi_isr(struct svc_i3c_master *master)
* for the slave to interrupt again.
*/
if (svc_i3c_master_error(master)) {
if (master->ibi.tbq_slot) {
if (master->ibi.tbq_slot && dev) {
data = i3c_dev_get_master_data(dev);
i3c_generic_ibi_recycle_slot(data->ibi_pool,
master->ibi.tbq_slot);

View File

@@ -64,7 +64,7 @@ config ADXL345
config ADXL345_I2C
tristate "Analog Devices ADXL345 3-Axis Digital Accelerometer I2C Driver"
depends on INPUT_ADXL34X=n
depends on !INPUT_ADXL34X
depends on I2C
select ADXL345
select REGMAP_I2C
@@ -74,11 +74,12 @@ config ADXL345_I2C
To compile this driver as a module, choose M here: the module
will be called adxl345_i2c and you will also get adxl345_core
for the core module.
for the core module. INPUT_ADXL34X share compatibles with this
driver, do not add both modules to the kernel.
config ADXL345_SPI
tristate "Analog Devices ADXL345 3-Axis Digital Accelerometer SPI Driver"
depends on INPUT_ADXL34X=n
depends on !INPUT_ADXL34X
depends on SPI
select ADXL345
select REGMAP_SPI
@@ -88,7 +89,8 @@ config ADXL345_SPI
To compile this driver as a module, choose M here: the module
will be called adxl345_spi and you will also get adxl345_core
for the core module.
for the core module. INPUT_ADXL34X share compatibles with this
driver, do not add both modules to the kernel.
config ADXL355
tristate

View File

@@ -768,9 +768,8 @@ static int adxl355_probe_trigger(struct iio_dev *indio_dev, int irq)
data->dready_trig->ops = &adxl355_trigger_ops;
iio_trigger_set_drvdata(data->dready_trig, indio_dev);
ret = devm_request_irq(data->dev, irq,
&iio_trigger_generic_data_rdy_poll,
IRQF_ONESHOT, "adxl355_irq", data->dready_trig);
ret = devm_request_irq(data->dev, irq, &iio_trigger_generic_data_rdy_poll,
IRQF_NO_THREAD, "adxl355_irq", data->dready_trig);
if (ret)
return dev_err_probe(data->dev, ret, "request irq %d failed\n",
irq);

View File

@@ -295,7 +295,6 @@ struct adxl372_state {
u32 inact_time_ms;
u8 fifo_set_size;
unsigned long int1_bitmask;
unsigned long int2_bitmask;
u16 watermark;
__be16 fifo_buf[ADXL372_FIFO_SIZE];
bool peak_fifo_mode_en;
@@ -1247,11 +1246,10 @@ int adxl372_probe(struct device *dev, struct regmap *regmap,
indio_dev->trig = iio_trigger_get(st->dready_trig);
ret = devm_request_threaded_irq(dev, st->irq,
iio_trigger_generic_data_rdy_poll,
NULL,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
indio_dev->name, st->dready_trig);
ret = devm_request_irq(dev, st->irq,
iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
indio_dev->name, st->dready_trig);
if (ret < 0)
return ret;
}

View File

@@ -232,25 +232,46 @@ bool adxl380_readable_noinc_reg(struct device *dev, unsigned int reg)
}
EXPORT_SYMBOL_NS_GPL(adxl380_readable_noinc_reg, "IIO_ADXL380");
static int adxl380_act_inact_enabled(struct adxl380_state *st, bool *enabled)
{
unsigned int act_inact_ctl;
int ret;
if (!st->chip_info->has_low_power) {
*enabled = false;
return 0;
}
ret = regmap_read(st->regmap, ADXL380_ACT_INACT_CTL_REG, &act_inact_ctl);
if (ret)
return ret;
*enabled = FIELD_GET(ADXL380_ACT_EN_MSK, act_inact_ctl) ||
FIELD_GET(ADXL380_INACT_EN_MSK, act_inact_ctl);
return 0;
}
static int adxl380_set_measure_en(struct adxl380_state *st, bool en)
{
int ret;
unsigned int act_inact_ctl;
u8 op_mode = ADXL380_OP_MODE_STANDBY;
if (en) {
ret = regmap_read(st->regmap, ADXL380_ACT_INACT_CTL_REG, &act_inact_ctl);
bool act_inact_enabled;
ret = adxl380_act_inact_enabled(st, &act_inact_enabled);
if (ret)
return ret;
/*
* Activity/Inactivity detection available only in VLP/ULP
* mode and for devices that support low power modes. Otherwise
* go straight to measure mode (same bits as ADXL380_OP_MODE_HP).
* mode and for devices that support low power modes.
*/
if (st->chip_info->has_low_power &&
(FIELD_GET(ADXL380_ACT_EN_MSK, act_inact_ctl) ||
FIELD_GET(ADXL380_INACT_EN_MSK, act_inact_ctl)))
if (act_inact_enabled)
st->odr = ADXL380_ODR_VLP;
if (st->odr == ADXL380_ODR_VLP)
op_mode = ADXL380_OP_MODE_VLP;
else
op_mode = ADXL380_OP_MODE_HP;
@@ -417,17 +438,7 @@ static int adxl380_read_chn(struct adxl380_state *st, u8 addr)
static int adxl380_get_odr(struct adxl380_state *st, int *odr)
{
int ret;
unsigned int trig_cfg, odr_idx;
ret = regmap_read(st->regmap, ADXL380_TRIG_CFG_REG, &trig_cfg);
if (ret)
return ret;
odr_idx = (FIELD_GET(ADXL380_TRIG_CFG_SINC_RATE_MSK, trig_cfg) << 1) |
(FIELD_GET(ADXL380_TRIG_CFG_DEC_2X_MSK, trig_cfg) & 1);
*odr = st->chip_info->samp_freq_tbl[odr_idx];
*odr = st->chip_info->samp_freq_tbl[st->odr];
return 0;
}
@@ -488,18 +499,24 @@ static int adxl380_set_odr(struct adxl380_state *st, u8 odr)
if (ret)
return ret;
ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG,
ADXL380_TRIG_CFG_DEC_2X_MSK,
FIELD_PREP(ADXL380_TRIG_CFG_DEC_2X_MSK, odr & 1));
if (ret)
return ret;
if (odr >= ADXL380_ODR_DSM) {
u8 mul = odr - ADXL380_ODR_DSM;
u8 field;
ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG,
ADXL380_TRIG_CFG_SINC_RATE_MSK,
FIELD_PREP(ADXL380_TRIG_CFG_SINC_RATE_MSK, odr >> 1));
if (ret)
return ret;
field = FIELD_PREP(ADXL380_TRIG_CFG_DEC_2X_MSK, mul & 1);
ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG,
ADXL380_TRIG_CFG_DEC_2X_MSK, field);
if (ret)
return ret;
field = FIELD_PREP(ADXL380_TRIG_CFG_SINC_RATE_MSK, mul >> 1);
ret = regmap_update_bits(st->regmap, ADXL380_TRIG_CFG_REG,
ADXL380_TRIG_CFG_SINC_RATE_MSK, field);
if (ret)
return ret;
}
st->odr = odr;
ret = adxl380_set_measure_en(st, true);
if (ret)
return ret;
@@ -949,14 +966,13 @@ static irqreturn_t adxl380_irq_handler(int irq, void *p)
if (ret)
return IRQ_HANDLED;
for (i = 0; i < fifo_entries; i += st->fifo_set_size) {
ret = regmap_noinc_read(st->regmap, ADXL380_FIFO_DATA,
&st->fifo_buf[i],
2 * st->fifo_set_size);
if (ret)
return IRQ_HANDLED;
fifo_entries = rounddown(fifo_entries, st->fifo_set_size);
ret = regmap_noinc_read(st->regmap, ADXL380_FIFO_DATA, &st->fifo_buf,
sizeof(*st->fifo_buf) * fifo_entries);
if (ret)
return IRQ_HANDLED;
for (i = 0; i < fifo_entries; i += st->fifo_set_size)
iio_push_to_buffers(indio_dev, &st->fifo_buf[i]);
}
return IRQ_HANDLED;
}
@@ -1138,6 +1154,32 @@ static const struct iio_buffer_setup_ops adxl380_buffer_ops = {
.predisable = adxl380_buffer_predisable,
};
static int adxl380_samp_freq_avail(struct adxl380_state *st, const int **vals,
int *length)
{
bool act_inact_enabled;
int ret;
if (!st->chip_info->has_low_power) {
*vals = st->chip_info->samp_freq_tbl + ADXL380_ODR_DSM;
*length = ADXL380_ODR_MAX - ADXL380_ODR_DSM;
return 0;
}
ret = adxl380_act_inact_enabled(st, &act_inact_enabled);
if (ret)
return 0;
/*
* Motion detection is only functional in low-power mode, and this
* affects the available sampling frequencies.
*/
*vals = st->chip_info->samp_freq_tbl;
*length = act_inact_enabled ? ADXL380_ODR_DSM : ADXL380_ODR_MAX;
return 0;
}
static int adxl380_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long info)
@@ -1218,6 +1260,7 @@ static int adxl380_read_avail(struct iio_dev *indio_dev,
long mask)
{
struct adxl380_state *st = iio_priv(indio_dev);
int ret;
if (chan->type != IIO_ACCEL)
return -EINVAL;
@@ -1229,9 +1272,11 @@ static int adxl380_read_avail(struct iio_dev *indio_dev,
*length = ARRAY_SIZE(st->chip_info->scale_tbl) * 2;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SAMP_FREQ:
*vals = (const int *)st->chip_info->samp_freq_tbl;
ret = adxl380_samp_freq_avail(st, vals, length);
if (ret)
return ret;
*type = IIO_VAL_INT;
*length = ARRAY_SIZE(st->chip_info->samp_freq_tbl);
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
*vals = (const int *)st->lpf_tbl;
@@ -1254,12 +1299,16 @@ static int adxl380_write_raw(struct iio_dev *indio_dev,
int val, int val2, long info)
{
struct adxl380_state *st = iio_priv(indio_dev);
int odr_index, lpf_index, hpf_index, range_index;
const int *freq_vals;
int odr_index, lpf_index, hpf_index, range_index, freq_count, ret;
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ:
odr_index = adxl380_find_match_1d_tbl(st->chip_info->samp_freq_tbl,
ARRAY_SIZE(st->chip_info->samp_freq_tbl),
ret = adxl380_samp_freq_avail(st, &freq_vals, &freq_count);
if (ret)
return ret;
odr_index = adxl380_find_match_1d_tbl(freq_vals, freq_count,
val);
return adxl380_set_odr(st, odr_index);
case IIO_CHAN_INFO_CALIBBIAS:
@@ -1621,7 +1670,7 @@ const struct adxl380_chip_info adxl318_chip_info = {
[ADXL380_OP_MODE_8G_RANGE] = { 0, 2615434 },
[ADXL380_OP_MODE_16G_RANGE] = { 0, 5229886 },
},
.samp_freq_tbl = { 8000, 16000, 32000 },
.samp_freq_tbl = { 0, 8000, 16000, 32000 },
/*
* The datasheet defines an intercept of 550 LSB at 25 degC
* and a sensitivity of 10.2 LSB/C.
@@ -1639,7 +1688,7 @@ const struct adxl380_chip_info adxl319_chip_info = {
[ADXL382_OP_MODE_30G_RANGE] = { 0, 9806650 },
[ADXL382_OP_MODE_60G_RANGE] = { 0, 19613300 },
},
.samp_freq_tbl = { 16000, 32000, 64000 },
.samp_freq_tbl = { 0, 16000, 32000, 64000 },
/*
* The datasheet defines an intercept of 550 LSB at 25 degC
* and a sensitivity of 10.2 LSB/C.
@@ -1657,7 +1706,7 @@ const struct adxl380_chip_info adxl380_chip_info = {
[ADXL380_OP_MODE_8G_RANGE] = { 0, 2615434 },
[ADXL380_OP_MODE_16G_RANGE] = { 0, 5229886 },
},
.samp_freq_tbl = { 8000, 16000, 32000 },
.samp_freq_tbl = { 1000, 8000, 16000, 32000 },
/*
* The datasheet defines an intercept of 470 LSB at 25 degC
* and a sensitivity of 10.2 LSB/C.
@@ -1677,7 +1726,7 @@ const struct adxl380_chip_info adxl382_chip_info = {
[ADXL382_OP_MODE_30G_RANGE] = { 0, 9806650 },
[ADXL382_OP_MODE_60G_RANGE] = { 0, 19613300 },
},
.samp_freq_tbl = { 16000, 32000, 64000 },
.samp_freq_tbl = { 1000, 16000, 32000, 64000 },
/*
* The datasheet defines an intercept of 570 LSB at 25 degC
* and a sensitivity of 10.2 LSB/C.
@@ -1916,6 +1965,7 @@ int adxl380_probe(struct device *dev, struct regmap *regmap,
st->dev = dev;
st->regmap = regmap;
st->chip_info = chip_info;
st->odr = ADXL380_ODR_DSM;
mutex_init(&st->lock);

View File

@@ -8,10 +8,18 @@
#ifndef _ADXL380_H_
#define _ADXL380_H_
enum adxl380_odr {
ADXL380_ODR_VLP,
ADXL380_ODR_DSM,
ADXL380_ODR_DSM_2X,
ADXL380_ODR_DSM_4X,
ADXL380_ODR_MAX
};
struct adxl380_chip_info {
const char *name;
const int scale_tbl[3][2];
const int samp_freq_tbl[3];
const int samp_freq_tbl[ADXL380_ODR_MAX];
const struct iio_info *info;
const int temp_offset;
const u16 chip_id;

View File

@@ -986,8 +986,9 @@ static int bma180_probe(struct i2c_client *client)
}
ret = devm_request_irq(dev, client->irq,
iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_RISING,
"bma180_event", data->trig);
iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
"bma180_event", data->trig);
if (ret) {
dev_err(dev, "unable to request IRQ\n");
goto err_trigger_free;

View File

@@ -486,13 +486,10 @@ static int mxc4005_probe(struct i2c_client *client)
if (!data->dready_trig)
return -ENOMEM;
ret = devm_request_threaded_irq(&client->dev, client->irq,
iio_trigger_generic_data_rdy_poll,
NULL,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
"mxc4005_event",
data->dready_trig);
ret = devm_request_irq(&client->dev, client->irq,
iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_FALLING | IRQF_NO_THREAD,
"mxc4005_event", data->dready_trig);
if (ret) {
dev_err(&client->dev,
"failed to init threaded irq\n");

View File

@@ -153,7 +153,6 @@
* struct sca3000_state - device instance state information
* @us: the associated spi device
* @info: chip variant information
* @last_timestamp: the timestamp of the last event
* @mo_det_use_count: reference counter for the motion detection unit
* @lock: lock used to protect elements of sca3000_state
* and the underlying device state.
@@ -163,7 +162,6 @@
struct sca3000_state {
struct spi_device *us;
const struct sca3000_chip_info *info;
s64 last_timestamp;
int mo_det_use_count;
struct mutex lock;
/* Can these share a cacheline ? */
@@ -1489,7 +1487,11 @@ static int sca3000_probe(struct spi_device *spi)
if (ret)
goto error_free_irq;
return iio_device_register(indio_dev);
ret = iio_device_register(indio_dev);
if (ret)
goto error_free_irq;
return 0;
error_free_irq:
if (spi->irq)

View File

@@ -428,13 +428,10 @@ static int stk8ba50_probe(struct i2c_client *client)
}
if (client->irq > 0) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
stk8ba50_data_rdy_trig_poll,
NULL,
IRQF_TRIGGER_RISING |
IRQF_ONESHOT,
"stk8ba50_event",
indio_dev);
ret = devm_request_irq(&client->dev, client->irq,
stk8ba50_data_rdy_trig_poll,
IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
"stk8ba50_event", indio_dev);
if (ret < 0) {
dev_err(&client->dev, "request irq %d failed\n",
client->irq);

View File

@@ -70,6 +70,19 @@ config AD4030
To compile this driver as a module, choose M here: the module will be
called ad4030.
config AD4062
tristate "Analog Devices AD4062 Driver"
depends on I3C
select REGMAP_I3C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Analog Devices AD4062 I3C analog
to digital converters (ADC).
To compile this driver as a module, choose M here: the module will be
called ad4062.
config AD4080
tristate "Analog Devices AD4080 high speed ADC"
depends on SPI
@@ -99,6 +112,17 @@ config AD4130
To compile this driver as a module, choose M here: the module will be
called ad4130.
config AD4134
tristate "Analog Device AD4134 ADC Driver"
depends on SPI
select REGMAP_SPI
select CRC8
help
Say yes here to build support for Analog Devices AD4134 SPI analog to
digital converters (ADC).
To compile this driver as a module, choose M here: the module will be
called ad4134_spi.
config AD4170_4
tristate "Analog Device AD4170-4 ADC Driver"
@@ -387,6 +411,7 @@ config AD7768_1
depends on SPI
select REGULATOR
select REGMAP_SPI
select RATIONAL
select IIO_BUFFER
select IIO_TRIGGER
select IIO_TRIGGERED_BUFFER
@@ -1222,6 +1247,18 @@ config NPCM_ADC
This driver can also be built as a module. If so, the module
will be called npcm_adc.
config NXP_SAR_ADC
tristate "NXP S32G SAR-ADC driver"
depends on ARCH_S32 || COMPILE_TEST
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for S32G platforms
analog-to-digital converter.
This driver can also be built as a module. If so, the module will be
called nxp_sar_adc.
config PAC1921
tristate "Microchip Technology PAC1921 driver"
depends on I2C
@@ -1664,6 +1701,18 @@ config TI_ADS1015
This driver can also be built as a module. If so, the module will be
called ti-ads1015.
config TI_ADS1018
tristate "Texas Instruments ADS1018 ADC"
depends on SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
If you say yes here you get support for Texas Instruments ADS1018 and
ADS1118 ADC chips.
This driver can also be built as a module. If so, the module will be
called ti-ads1018.
config TI_ADS1100
tristate "Texas Instruments ADS1100 and ADS1000 ADC"
depends on I2C
@@ -1722,6 +1771,17 @@ config TI_ADS131E08
This driver can also be built as a module. If so, the module will be
called ti-ads131e08.
config TI_ADS131M02
tristate "Texas Instruments ADS131M02"
depends on SPI && REGULATOR
select CRC_ITU_T
help
Say yes here to get support for Texas Instruments ADS131M02, ADS131M03,
ADS131M04, ADS131M06 and ADS131M08 chips.
This driver can also be built as a module. If so, the module will be
called ti-ads131m02.
config TI_ADS7138
tristate "Texas Instruments ADS7128 and ADS7138 ADC driver"
depends on I2C

View File

@@ -11,8 +11,10 @@ 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_AD4062) += ad4062.o
obj-$(CONFIG_AD4080) += ad4080.o
obj-$(CONFIG_AD4130) += ad4130.o
obj-$(CONFIG_AD4134) += ad4134.o
obj-$(CONFIG_AD4170_4) += ad4170-4.o
obj-$(CONFIG_AD4695) += ad4695.o
obj-$(CONFIG_AD4851) += ad4851.o
@@ -108,6 +110,7 @@ obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_NCT7201) += nct7201.o
obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
obj-$(CONFIG_NXP_SAR_ADC) += nxp-sar-adc.o
obj-$(CONFIG_PAC1921) += pac1921.o
obj-$(CONFIG_PAC1934) += pac1934.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
@@ -145,11 +148,13 @@ obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
obj-$(CONFIG_TI_ADS1018) += ti-ads1018.o
obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o
obj-$(CONFIG_TI_ADS1119) += ti-ads1119.o
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o
obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o
obj-$(CONFIG_TI_ADS131M02) += ti-ads131m02.o
obj-$(CONFIG_TI_ADS7138) += ti-ads7138.o
obj-$(CONFIG_TI_ADS7924) += ti-ads7924.o
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o

1609
drivers/iio/adc/ad4062.c Normal file

File diff suppressed because it is too large Load Diff

500
drivers/iio/adc/ad4134.c Normal file
View File

@@ -0,0 +1,500 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2026 Analog Devices, Inc.
* Author: Marcelo Schmitt <marcelo.schmitt@analog.com>
*/
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/gpio/consumer.h>
#include <linux/iio/iio.h>
#include <linux/iio/types.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/spi/spi.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/unaligned.h>
#include <linux/units.h>
#define AD4134_RESET_TIME_US (10 * USEC_PER_SEC)
#define AD4134_REG_READ_MASK BIT(7)
#define AD4134_SPI_MAX_XFER_LEN 3
#define AD4134_EXT_CLOCK_MHZ (48 * HZ_PER_MHZ)
#define AD4134_NUM_CHANNELS 4
#define AD4134_CHAN_PRECISION_BITS 24
#define AD4134_IFACE_CONFIG_A_REG 0x00
#define AD4134_IFACE_CONFIG_B_REG 0x01
#define AD4134_IFACE_CONFIG_B_SINGLE_INSTR BIT(7)
#define AD4134_DEVICE_CONFIG_REG 0x02
#define AD4134_DEVICE_CONFIG_POWER_MODE_MASK BIT(0)
#define AD4134_POWER_MODE_HIGH_PERF 0x1
#define AD4134_SILICON_REV_REG 0x07
#define AD4134_SCRATCH_PAD_REG 0x0A
#define AD4134_STREAM_MODE_REG 0x0E
#define AD4134_SDO_PIN_SRC_SEL_REG 0x10
#define AD4134_SDO_PIN_SRC_SEL_SDO_SEL_MASK BIT(2)
#define AD4134_DATA_PACKET_CONFIG_REG 0x11
#define AD4134_DATA_PACKET_CONFIG_FRAME_MASK GENMASK(5, 4)
#define AD4134_DATA_PACKET_24BIT_FRAME 0x2
#define AD4134_DIG_IF_CFG_REG 0x12
#define AD4134_DIF_IF_CFG_FORMAT_MASK GENMASK(1, 0)
#define AD4134_DATA_FORMAT_SINGLE_CH_MODE 0x0
#define AD4134_PW_DOWN_CTRL_REG 0x13
#define AD4134_DEVICE_STATUS_REG 0x15
#define AD4134_ODR_VAL_INT_LSB_REG 0x16
#define AD4134_CH3_OFFSET_MSB_REG 0x3E
#define AD4134_AIN_OR_ERROR_REG 0x48
/*
* AD4134 register map ends at address 0x48 and there is no register for
* retrieving ADC sample data. Though, to make use of Linux regmap API both
* for register access and sample read, we define one virtual register for each
* ADC channel. AD4134_CH_VREG(x) maps a channel number to it's virtual register
* address while AD4134_VREG_CH(x) tells which channel given the address.
*/
#define AD4134_CH_VREG(x) ((x) + 0x50)
#define AD4134_VREG_CH(x) ((x) - 0x50)
#define AD4134_SPI_CRC_POLYNOM 0x07
#define AD4134_SPI_CRC_INIT_VALUE 0xA5
static unsigned char ad4134_spi_crc_table[CRC8_TABLE_SIZE];
#define AD4134_CHANNEL(_index) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = (_index), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec ad4134_chan_set[] = {
AD4134_CHANNEL(0),
AD4134_CHANNEL(1),
AD4134_CHANNEL(2),
AD4134_CHANNEL(3),
};
struct ad4134_state {
struct spi_device *spi;
struct regmap *regmap;
unsigned long sys_clk_hz;
struct gpio_desc *odr_gpio;
int refin_mv;
/*
* DMA (thus cache coherency maintenance) requires the transfer buffers
* to live in their own cache lines.
*/
u8 rx_buf[AD4134_SPI_MAX_XFER_LEN] __aligned(IIO_DMA_MINALIGN);
u8 tx_buf[AD4134_SPI_MAX_XFER_LEN];
};
static const struct regmap_range ad4134_regmap_rd_range[] = {
regmap_reg_range(AD4134_IFACE_CONFIG_A_REG, AD4134_SILICON_REV_REG),
regmap_reg_range(AD4134_SCRATCH_PAD_REG, AD4134_PW_DOWN_CTRL_REG),
regmap_reg_range(AD4134_DEVICE_STATUS_REG, AD4134_AIN_OR_ERROR_REG),
regmap_reg_range(AD4134_CH_VREG(0), AD4134_CH_VREG(AD4134_NUM_CHANNELS)),
};
static const struct regmap_range ad4134_regmap_wr_range[] = {
regmap_reg_range(AD4134_IFACE_CONFIG_A_REG, AD4134_DEVICE_CONFIG_REG),
regmap_reg_range(AD4134_SCRATCH_PAD_REG, AD4134_SCRATCH_PAD_REG),
regmap_reg_range(AD4134_STREAM_MODE_REG, AD4134_PW_DOWN_CTRL_REG),
regmap_reg_range(AD4134_ODR_VAL_INT_LSB_REG, AD4134_CH3_OFFSET_MSB_REG),
};
static const struct regmap_access_table ad4134_regmap_rd_table = {
.yes_ranges = ad4134_regmap_rd_range,
.n_yes_ranges = ARRAY_SIZE(ad4134_regmap_rd_range),
};
static const struct regmap_access_table ad4134_regmap_wr_table = {
.yes_ranges = ad4134_regmap_wr_range,
.n_yes_ranges = ARRAY_SIZE(ad4134_regmap_wr_range),
};
static int ad4134_calc_spi_crc(u8 inst, u8 data)
{
u8 buf[] = { inst, data };
return crc8(ad4134_spi_crc_table, buf, ARRAY_SIZE(buf),
AD4134_SPI_CRC_INIT_VALUE);
}
static void ad4134_prepare_spi_tx_buf(u8 inst, u8 data, u8 *buf)
{
buf[0] = inst;
buf[1] = data;
buf[2] = ad4134_calc_spi_crc(inst, data);
}
static int ad4134_reg_write(void *context, unsigned int reg, unsigned int val)
{
struct ad4134_state *st = context;
struct spi_transfer xfer = {
.tx_buf = st->tx_buf,
.rx_buf = st->rx_buf,
.len = AD4134_SPI_MAX_XFER_LEN,
};
int ret;
ad4134_prepare_spi_tx_buf(reg, val, st->tx_buf);
ret = spi_sync_transfer(st->spi, &xfer, 1);
if (ret)
return ret;
if (st->rx_buf[2] != st->tx_buf[2])
dev_dbg(&st->spi->dev, "reg write CRC check failed\n");
return 0;
}
static int ad4134_data_read(struct ad4134_state *st, unsigned int reg,
unsigned int *val)
{
unsigned int i;
int ret;
/*
* To be able to read data from all 4 channels through a single line, we
* set DOUTx output format to 0 in the digital interface config register
* (0x12). With that, data from all four channels is serialized and
* output on DOUT0. During the probe, we also set SDO_PIN_SRC_SEL in
* DEVICE_CONFIG_1 register to duplicate DOUT0 on the SDO pin. Combined,
* those configurations enable ADC data read through a conventional SPI
* interface. Now we read data from all channels but keep only the bits
* from the requested one.
*/
for (i = 0; i < ARRAY_SIZE(ad4134_chan_set); i++) {
ret = spi_write_then_read(st->spi, NULL, 0, st->rx_buf,
BITS_TO_BYTES(AD4134_CHAN_PRECISION_BITS));
if (ret)
return ret;
/*
* AD4134 has a built-in feature that flags when data transfers
* don't run enough clock cycles to read the entire data frame.
* Clock out data from all channels to avoid that.
*/
if (i == AD4134_VREG_CH(reg))
*val = get_unaligned_be24(st->rx_buf);
}
return 0;
}
static int ad4134_register_read(struct ad4134_state *st, unsigned int reg,
unsigned int *val)
{
struct spi_transfer xfer = {
.tx_buf = st->tx_buf,
.rx_buf = st->rx_buf,
.len = AD4134_SPI_MAX_XFER_LEN,
};
unsigned int inst;
int ret;
inst = AD4134_REG_READ_MASK | reg;
ad4134_prepare_spi_tx_buf(inst, 0, st->tx_buf);
ret = spi_sync_transfer(st->spi, &xfer, 1);
if (ret)
return ret;
*val = st->rx_buf[1];
/* Check CRC */
if (st->rx_buf[2] != st->tx_buf[2])
dev_dbg(&st->spi->dev, "reg read CRC check failed\n");
return 0;
}
static int ad4134_reg_read(void *context, unsigned int reg, unsigned int *val)
{
struct ad4134_state *st = context;
if (reg >= AD4134_CH_VREG(0))
return ad4134_data_read(st, reg, val);
return ad4134_register_read(st, reg, val);
}
static const struct regmap_config ad4134_regmap_config = {
.reg_read = ad4134_reg_read,
.reg_write = ad4134_reg_write,
.rd_table = &ad4134_regmap_rd_table,
.wr_table = &ad4134_regmap_wr_table,
.max_register = AD4134_CH_VREG(ARRAY_SIZE(ad4134_chan_set)),
};
static int ad4134_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long info)
{
struct ad4134_state *st = iio_priv(indio_dev);
int ret;
switch (info) {
case IIO_CHAN_INFO_RAW:
gpiod_set_value_cansleep(st->odr_gpio, 1);
/*
* For slave mode gated DCLK (data sheet page 11), the minimum
* ODR high time is 3 * tDIGCLK. The internal digital clock
* period is tDIGCLK = 1/fDIGCLK = 2/fSYSCLK.
* The System clock frequency (fSYSCLK) is typically 48 MHz.
* Thus, ODR high time = 3 * (2 / (48 * HZ_PER_MHZ))
* ODR high time = 0.000000125 s = 125 ns
* 1 micro second should be more than enough. Not worth it
* tweaking for shorter dealy since this is not a fast data path.
*/
fsleep(1);
gpiod_set_value_cansleep(st->odr_gpio, 0);
ret = regmap_read(st->regmap, AD4134_CH_VREG(chan->channel), val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = st->refin_mv;
*val2 = AD4134_CHAN_PRECISION_BITS - 1;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
}
static int ad4134_debugfs_reg_access(struct iio_dev *indio_dev,
unsigned int reg, unsigned int writeval,
unsigned int *readval)
{
struct ad4134_state *st = iio_priv(indio_dev);
if (readval)
return regmap_read(st->regmap, reg, readval);
return regmap_write(st->regmap, reg, writeval);
}
static int ad4134_min_io_mode_setup(struct ad4134_state *st)
{
struct device *dev = &st->spi->dev;
int ret;
st->odr_gpio = devm_gpiod_get(dev, "odr", GPIOD_OUT_LOW);
if (IS_ERR(st->odr_gpio))
return dev_err_probe(dev, PTR_ERR(st->odr_gpio),
"failed to get ODR GPIO\n");
ret = regmap_update_bits(st->regmap, AD4134_DIG_IF_CFG_REG,
AD4134_DIF_IF_CFG_FORMAT_MASK,
FIELD_PREP(AD4134_DIF_IF_CFG_FORMAT_MASK,
AD4134_DATA_FORMAT_SINGLE_CH_MODE));
if (ret)
return dev_err_probe(dev, ret,
"failed to set single channel mode\n");
ret = regmap_set_bits(st->regmap, AD4134_SDO_PIN_SRC_SEL_REG,
AD4134_SDO_PIN_SRC_SEL_SDO_SEL_MASK);
if (ret)
return dev_err_probe(dev, ret,
"failed to set SDO source selection\n");
return regmap_set_bits(st->regmap, AD4134_IFACE_CONFIG_B_REG,
AD4134_IFACE_CONFIG_B_SINGLE_INSTR);
}
static const struct iio_info ad4134_info = {
.read_raw = ad4134_read_raw,
.debugfs_reg_access = ad4134_debugfs_reg_access,
};
static const char * const ad4143_required_regulators[] = {
"avdd5", "dvdd5", "iovdd",
};
static const char * const ad4143_optional_regulators[] = {
"avdd1v8", "dvdd1v8", "clkvdd",
};
static int ad4134_regulator_setup(struct ad4134_state *st)
{
struct device *dev = &st->spi->dev;
int ret;
ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4143_required_regulators),
ad4143_required_regulators);
if (ret)
return dev_err_probe(dev, ret, "failed to enable power supplies\n");
/* Required regulator that we need to read the voltage */
ret = devm_regulator_get_enable_read_voltage(dev, "refin");
if (ret < 0)
return dev_err_probe(dev, ret, "failed to get REFIN voltage.\n");
st->refin_mv = ret / (MICRO / MILLI);
ret = devm_regulator_get_enable_optional(dev, "ldoin");
if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to enable ldoin supply\n");
/* If ldoin was provided, then use the use the internal LDO regulators */
if (ret == 0)
return 0;
/*
* If ldoin is not provided, then avdd1v8, dvdd1v8, and clkvdd are
* required.
*/
ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(ad4143_optional_regulators),
ad4143_optional_regulators);
if (ret)
return dev_err_probe(dev, ret, "failed to enable 1V8 power supplies\n");
return 0;
}
static int ad4134_clock_select(struct ad4134_state *st)
{
struct device *dev = &st->spi->dev;
struct clk *xtal_clk, *clkin_clk;
/*
* AD4134 requires one external clock source and only one external clock
* source can be provided at a time. Try to get a crystal provided clock.
* If that fails, try to get a CMOS clock.
*/
xtal_clk = devm_clk_get_optional_enabled(dev, "xtal");
if (!xtal_clk)
xtal_clk = devm_clk_get_optional_enabled(dev, "xtal");
if (IS_ERR(xtal_clk))
return dev_err_probe(dev, PTR_ERR(xtal_clk),
"failed to get xtal\n");
clkin_clk = devm_clk_get_optional_enabled(dev, "clkin");
if (!clkin_clk)
clkin_clk = devm_clk_get_optional_enabled(dev, "clkin");
if (IS_ERR(clkin_clk))
return dev_err_probe(dev, PTR_ERR(clkin_clk),
"failed to get clkin\n");
st->sys_clk_hz = clk_get_rate(xtal_clk) | clk_get_rate(clkin_clk);
if (st->sys_clk_hz != AD4134_EXT_CLOCK_MHZ)
dev_warn(dev, "invalid external clock frequency %lu\n",
st->sys_clk_hz);
return 0;
}
static int ad4134_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct reset_control *rst;
struct iio_dev *indio_dev;
struct ad4134_state *st;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
st->spi = spi;
indio_dev->name = "ad4134";
indio_dev->channels = ad4134_chan_set;
indio_dev->num_channels = ARRAY_SIZE(ad4134_chan_set);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &ad4134_info;
ret = ad4134_regulator_setup(st);
if (ret)
return ret;
ret = ad4134_clock_select(st);
if (ret)
return ret;
rst = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL);
if (IS_ERR(rst))
return dev_err_probe(dev, PTR_ERR(rst),
"failed to get and deassert reset\n");
crc8_populate_msb(ad4134_spi_crc_table, AD4134_SPI_CRC_POLYNOM);
st->regmap = devm_regmap_init(dev, NULL, st, &ad4134_regmap_config);
if (IS_ERR(st->regmap))
return dev_err_probe(dev, PTR_ERR(st->regmap),
"failed to initialize regmap");
ret = ad4134_min_io_mode_setup(st);
if (ret)
return dev_err_probe(dev, ret,
"failed to setup minimum I/O mode\n");
/* Bump precision to 24-bit */
ret = regmap_update_bits(st->regmap, AD4134_DATA_PACKET_CONFIG_REG,
AD4134_DATA_PACKET_CONFIG_FRAME_MASK,
FIELD_PREP(AD4134_DATA_PACKET_CONFIG_FRAME_MASK,
AD4134_DATA_PACKET_24BIT_FRAME));
if (ret)
return ret;
/* Set high performance power mode */
ret = regmap_update_bits(st->regmap, AD4134_DEVICE_CONFIG_REG,
AD4134_DEVICE_CONFIG_POWER_MODE_MASK,
FIELD_PREP(AD4134_DEVICE_CONFIG_POWER_MODE_MASK,
AD4134_POWER_MODE_HIGH_PERF));
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct spi_device_id ad4134_id[] = {
{ "ad4134" },
{ }
};
MODULE_DEVICE_TABLE(spi, ad4134_id);
static const struct of_device_id ad4134_of_match[] = {
{ .compatible = "adi,ad4134" },
{ }
};
MODULE_DEVICE_TABLE(of, ad4134_of_match);
static struct spi_driver ad4134_driver = {
.driver = {
.name = "ad4134",
.of_match_table = ad4134_of_match,
},
.probe = ad4134_probe,
.id_table = ad4134_id,
};
module_spi_driver(ad4134_driver);
MODULE_AUTHOR("Marcelo Schmitt <marcelo.schmitt@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD4134 SPI driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("IIO_AD4134");

View File

@@ -2973,7 +2973,7 @@ static int ad4170_probe(struct spi_device *spi)
if (spi->irq) {
ret = devm_request_irq(dev, spi->irq, &ad4170_irq_handler,
IRQF_ONESHOT, indio_dev->name, indio_dev);
IRQF_NO_THREAD, indio_dev->name, indio_dev);
if (ret)
return ret;

View File

@@ -16,7 +16,6 @@
#include <linux/gpio/consumer.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>

View File

@@ -345,7 +345,7 @@ static int ad7606_spi_update_scan_mode(struct iio_dev *indio_dev,
* has no way of demuxing the data to filter out unwanted
* channels.
*/
if (bitmap_weight(scan_mask, num_adc_ch) != num_adc_ch)
if (!bitmap_full(scan_mask, num_adc_ch))
return -EINVAL;
}

View File

@@ -184,12 +184,6 @@ static const struct iio_info ad7766_info = {
.read_raw = &ad7766_read_raw,
};
static irqreturn_t ad7766_irq(int irq, void *private)
{
iio_trigger_poll(private);
return IRQ_HANDLED;
}
static int ad7766_set_trigger_state(struct iio_trigger *trig, bool enable)
{
struct ad7766 *ad7766 = iio_trigger_get_drvdata(trig);
@@ -260,8 +254,8 @@ static int ad7766_probe(struct spi_device *spi)
* Some platforms might not allow the option to power it down so
* don't enable the interrupt to avoid extra load on the system
*/
ret = devm_request_irq(&spi->dev, spi->irq, ad7766_irq,
IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN,
ret = devm_request_irq(&spi->dev, spi->irq, iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN | IRQF_NO_THREAD,
dev_name(&spi->dev),
ad7766->trig);
if (ret < 0)

View File

@@ -6,6 +6,7 @@
*/
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/delay.h>
@@ -14,8 +15,12 @@
#include <linux/gpio/driver.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/limits.h>
#include <linux/math.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/rational.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/driver.h>
@@ -107,10 +112,15 @@
#define AD7768_VCM_OFF 0x07
#define ADAQ776X_GAIN_MAX_NANO (128 * NANO)
#define ADAQ776X_MAX_GAIN_MODES 8
#define AD7768_TRIGGER_SOURCE_SYNC_IDX 0
#define AD7768_MAX_CHANNELS 1
#define ADAQ7768_PGA_PINS 3
enum ad7768_conv_mode {
AD7768_CONTINUOUS,
AD7768_ONE_SHOT,
@@ -153,6 +163,51 @@ enum ad7768_scan_type {
AD7768_SCAN_TYPE_HIGH_SPEED,
};
enum {
AD7768_PGA_GAIN_0,
AD7768_PGA_GAIN_1,
AD7768_PGA_GAIN_2,
AD7768_PGA_GAIN_3,
AD7768_PGA_GAIN_4,
AD7768_PGA_GAIN_5,
AD7768_PGA_GAIN_6,
AD7768_PGA_GAIN_7,
};
enum {
AD7768_AAF_IN1,
AD7768_AAF_IN2,
AD7768_AAF_IN3,
};
/* PGA and AAF gains in V/V */
static const int adaq7768_gains[] = {
[AD7768_PGA_GAIN_0] = 325, /* 0.325 */
[AD7768_PGA_GAIN_1] = 650, /* 0.650 */
[AD7768_PGA_GAIN_2] = 1300, /* 1.300 */
[AD7768_PGA_GAIN_3] = 2600, /* 2.600 */
[AD7768_PGA_GAIN_4] = 5200, /* 5.200 */
[AD7768_PGA_GAIN_5] = 10400, /* 10.400 */
[AD7768_PGA_GAIN_6] = 20800, /* 20.800 */
};
static const int adaq7769_gains[] = {
[AD7768_PGA_GAIN_0] = 1000, /* 1.000 */
[AD7768_PGA_GAIN_1] = 2000, /* 2.000 */
[AD7768_PGA_GAIN_2] = 4000, /* 4.000 */
[AD7768_PGA_GAIN_3] = 8000, /* 8.000 */
[AD7768_PGA_GAIN_4] = 16000, /* 16.000 */
[AD7768_PGA_GAIN_5] = 32000, /* 32.000 */
[AD7768_PGA_GAIN_6] = 64000, /* 64.000 */
[AD7768_PGA_GAIN_7] = 128000, /* 128.000 */
};
static const int ad7768_aaf_gains_bp[] = {
[AD7768_AAF_IN1] = 10000, /* 1.000 */
[AD7768_AAF_IN2] = 3640, /* 0.364 */
[AD7768_AAF_IN3] = 1430, /* 0.143 */
};
/* -3dB cutoff frequency multipliers (relative to ODR) for each filter type. */
static const int ad7768_filter_3db_odr_multiplier[] = {
[AD7768_FILTER_SINC5] = 204, /* 0.204 */
@@ -213,6 +268,19 @@ static const struct iio_scan_type ad7768_scan_type[] = {
},
};
struct ad7768_chip_info {
const char *name;
const struct iio_chan_spec *channel_spec;
int num_channels;
const int *pga_gains;
int num_pga_modes;
int default_pga_mode;
int pgia_mode2pin_offset;
bool has_pga;
bool has_variable_aaf;
bool has_vcm_regulator;
};
struct ad7768_state {
struct spi_device *spi;
struct regmap *regmap;
@@ -228,13 +296,19 @@ struct ad7768_state {
unsigned int samp_freq;
unsigned int samp_freq_avail[ARRAY_SIZE(ad7768_mclk_div_rates)];
unsigned int samp_freq_avail_len;
unsigned int pga_gain_mode;
unsigned int aaf_gain;
int scale_tbl[ADAQ776X_MAX_GAIN_MODES][2];
struct completion completion;
struct iio_trigger *trig;
struct gpio_descs *pga_gpios;
struct gpio_desc *gpio_sync_in;
struct gpio_desc *gpio_reset;
const char *labels[AD7768_MAX_CHANNELS];
struct gpio_chip gpiochip;
const struct ad7768_chip_info *chip;
bool en_spi_sync;
struct mutex pga_lock; /* protect device internal state (PGA) */
/*
* DMA (thus cache coherency maintenance) may require the
* transfer buffers to live in their own cache lines.
@@ -457,6 +531,42 @@ static int ad7768_reg_access(struct iio_dev *indio_dev,
return ret;
}
static void ad7768_fill_scale_tbl(struct iio_dev *dev)
{
struct ad7768_state *st = iio_priv(dev);
const struct iio_scan_type *scan_type;
int val, val2, tmp0, tmp1, i;
struct u32_fract fract;
unsigned long n, d;
u64 tmp2;
scan_type = iio_get_current_scan_type(dev, &dev->channels[0]);
if (scan_type->sign == 's')
val2 = scan_type->realbits - 1;
else
val2 = scan_type->realbits;
for (i = 0; i < st->chip->num_pga_modes; i++) {
/* Convert gain to a fraction format */
fract.numerator = st->chip->pga_gains[i];
fract.denominator = MILLI;
if (st->chip->has_variable_aaf) {
fract.numerator *= ad7768_aaf_gains_bp[st->aaf_gain];
fract.denominator *= PERMYRIAD;
}
rational_best_approximation(fract.numerator, fract.denominator,
INT_MAX, INT_MAX, &n, &d);
val = mult_frac(st->vref_uv, d, n);
/* Would multiply by NANO here, but value is already in milli */
tmp2 = ((u64)val * MICRO) >> val2;
tmp0 = div_u64_rem(tmp2, NANO, &tmp1);
st->scale_tbl[i][0] = tmp0; /* Integer part */
st->scale_tbl[i][1] = abs(tmp1); /* Fractional part */
}
}
static int ad7768_set_sinc3_dec_rate(struct ad7768_state *st,
unsigned int dec_rate)
{
@@ -558,12 +668,66 @@ static int ad7768_configure_dig_fil(struct iio_dev *dev,
st->oversampling_ratio = ad7768_dec_rate_values[dec_rate_idx];
}
/* Update scale table: scale values vary according to the precision */
ad7768_fill_scale_tbl(dev);
ad7768_fill_samp_freq_tbl(st);
/* A sync-in pulse is required after every configuration change */
return ad7768_send_sync_pulse(st);
}
static int ad7768_setup_pga(struct device *dev, struct ad7768_state *st)
{
st->pga_gpios = devm_gpiod_get_array(dev, "pga", GPIOD_OUT_LOW);
if (IS_ERR(st->pga_gpios))
return dev_err_probe(dev, PTR_ERR(st->pga_gpios),
"Failed to get PGA gpios.\n");
if (st->pga_gpios->ndescs != ADAQ7768_PGA_PINS)
return dev_err_probe(dev, -EINVAL,
"Expected %d GPIOs for PGA control.\n",
ADAQ7768_PGA_PINS);
return 0;
}
static int ad7768_calc_pga_gain(struct ad7768_state *st, int gain_int,
int gain_fract, int precision)
{
u64 gain_nano;
u32 tmp;
gain_nano = gain_int * NANO + gain_fract;
gain_nano = clamp(gain_nano, 0, ADAQ776X_GAIN_MAX_NANO);
tmp = DIV_ROUND_CLOSEST_ULL(gain_nano << precision, NANO);
gain_nano = DIV_ROUND_CLOSEST(st->vref_uv, tmp);
if (st->chip->has_variable_aaf)
gain_nano = DIV_ROUND_CLOSEST_ULL(gain_nano * PERMYRIAD,
ad7768_aaf_gains_bp[st->aaf_gain]);
return find_closest(gain_nano, st->chip->pga_gains,
(int)st->chip->num_pga_modes);
}
static int ad7768_set_pga_gain(struct ad7768_state *st,
int gain_mode)
{
int pgia_pins_value = abs(gain_mode - st->chip->pgia_mode2pin_offset);
DECLARE_BITMAP(bitmap, ADAQ7768_PGA_PINS) = { };
int ret;
guard(mutex)(&st->pga_lock);
bitmap_write(bitmap, pgia_pins_value, 0, ADAQ7768_PGA_PINS);
ret = gpiod_multi_set_value_cansleep(st->pga_gpios, bitmap);
if (ret)
return ret;
st->pga_gain_mode = gain_mode;
return 0;
}
static int ad7768_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
{
struct iio_dev *indio_dev = gpiochip_get_data(chip);
@@ -735,6 +899,19 @@ static int ad7768_get_filter_type_attr(struct iio_dev *dev,
return ad7768_filter_regval_to_type[FIELD_GET(mask, mode)];
}
static int ad7768_update_dec_rate(struct iio_dev *dev, unsigned int dec_rate)
{
struct ad7768_state *st = iio_priv(dev);
int ret;
ret = ad7768_configure_dig_fil(dev, st->filter_type, dec_rate);
if (ret)
return ret;
/* Update sampling frequency */
return ad7768_set_freq(st, st->samp_freq);
}
static const struct iio_enum ad7768_filter_type_iio_enum = {
.items = ad7768_filter_enum,
.num_items = ARRAY_SIZE(ad7768_filter_enum),
@@ -748,24 +925,32 @@ static const struct iio_chan_spec_ext_info ad7768_ext_info[] = {
{ }
};
#define AD7768_CHAN(_idx, _msk_avail) \
{ \
.type = IIO_VOLTAGE, \
.info_mask_separate_available = _msk_avail, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.ext_info = ad7768_ext_info, \
.indexed = 1, \
.channel = _idx, \
.scan_index = _idx, \
.has_ext_scan_type = 1, \
.ext_scan_type = ad7768_scan_type, \
.num_ext_scan_type = ARRAY_SIZE(ad7768_scan_type), \
}
static const struct iio_chan_spec ad7768_channels[] = {
{
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.ext_info = ad7768_ext_info,
.indexed = 1,
.channel = 0,
.scan_index = 0,
.has_ext_scan_type = 1,
.ext_scan_type = ad7768_scan_type,
.num_ext_scan_type = ARRAY_SIZE(ad7768_scan_type),
},
AD7768_CHAN(0, 0),
};
static const struct iio_chan_spec adaq776x_channels[] = {
AD7768_CHAN(0, BIT(IIO_CHAN_INFO_SCALE)),
};
static int ad7768_read_raw(struct iio_dev *indio_dev,
@@ -795,7 +980,19 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = (st->vref_uv * 2) / 1000;
if (st->chip->has_pga) {
guard(mutex)(&st->pga_lock);
*val = st->scale_tbl[st->pga_gain_mode][0];
*val2 = st->scale_tbl[st->pga_gain_mode][1];
return IIO_VAL_INT_PLUS_NANO;
}
temp = (st->vref_uv * 2) / 1000;
if (st->chip->has_variable_aaf)
temp = (temp * PERMYRIAD) / ad7768_aaf_gains_bp[st->aaf_gain];
*val = temp;
*val2 = scan_type->realbits;
return IIO_VAL_FRACTIONAL_LOG2;
@@ -851,31 +1048,24 @@ static int ad7768_read_avail(struct iio_dev *indio_dev,
*length = st->samp_freq_avail_len;
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SCALE:
*vals = (int *)st->scale_tbl;
*length = st->chip->num_pga_modes * 2;
*type = IIO_VAL_INT_PLUS_NANO;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static int __ad7768_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long info)
static int ad7768_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, long mask)
{
struct ad7768_state *st = iio_priv(indio_dev);
int ret;
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ:
return ad7768_set_freq(st, val);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
ret = ad7768_configure_dig_fil(indio_dev, st->filter_type, val);
if (ret)
return ret;
/* Update sampling frequency */
return ad7768_set_freq(st, st->samp_freq);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
return IIO_VAL_INT_PLUS_MICRO;
}
}
@@ -883,15 +1073,47 @@ static int ad7768_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long info)
{
struct ad7768_state *st = iio_priv(indio_dev);
const struct iio_scan_type *scan_type;
int ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
scan_type = iio_get_current_scan_type(indio_dev, chan);
if (IS_ERR(scan_type))
return PTR_ERR(scan_type);
ret = __ad7768_write_raw(indio_dev, chan, val, val2, info);
iio_device_release_direct(indio_dev);
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
return ret;
ret = ad7768_set_freq(st, val);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ad7768_update_dec_rate(indio_dev, val);
iio_device_release_direct(indio_dev);
return ret;
case IIO_CHAN_INFO_SCALE: {
int gain_mode;
if (!st->chip->has_pga)
return -EOPNOTSUPP;
if (scan_type->sign == 's')
gain_mode = ad7768_calc_pga_gain(st, val, val2,
scan_type->realbits - 1);
else
gain_mode = ad7768_calc_pga_gain(st, val, val2,
scan_type->realbits);
return ad7768_set_pga_gain(st, gain_mode);
}
default:
return -EINVAL;
}
}
static int ad7768_read_label(struct iio_dev *indio_dev,
@@ -915,6 +1137,7 @@ static const struct iio_info ad7768_info = {
.read_raw = &ad7768_read_raw,
.read_avail = &ad7768_read_avail,
.write_raw = &ad7768_write_raw,
.write_raw_get_fmt = &ad7768_write_raw_get_fmt,
.read_label = ad7768_read_label,
.get_current_scan_type = &ad7768_get_current_scan_type,
.debugfs_reg_access = &ad7768_reg_access,
@@ -1298,8 +1521,9 @@ static const struct regulator_desc vcm_desc = {
.owner = THIS_MODULE,
};
static int ad7768_register_regulators(struct device *dev, struct ad7768_state *st,
struct iio_dev *indio_dev)
static int ad7768_register_vcm_regulator(struct device *dev,
struct ad7768_state *st,
struct iio_dev *indio_dev)
{
struct regulator_config config = {
.dev = dev,
@@ -1321,6 +1545,79 @@ static int ad7768_register_regulators(struct device *dev, struct ad7768_state *s
return 0;
}
static int ad7768_parse_aaf_gain(struct device *dev, struct ad7768_state *st)
{
u32 val;
int ret;
ret = device_property_read_u32(dev, "adi,aaf-gain-bp", &val);
if (ret == -EINVAL) {
/* If controllable, use default */
if (st->chip->has_variable_aaf)
st->aaf_gain = AD7768_AAF_IN1;
return 0;
}
if (ret)
return dev_err_probe(dev, ret, "Failed to get AAF gain value\n");
if (!st->chip->has_variable_aaf)
return dev_err_probe(dev, -EOPNOTSUPP,
"AAF gain provided, but not supported for %s\n", st->chip->name);
switch (val) {
case 10000:
st->aaf_gain = AD7768_AAF_IN1;
break;
case 3640:
st->aaf_gain = AD7768_AAF_IN2;
break;
case 1430:
st->aaf_gain = AD7768_AAF_IN3;
break;
default:
return dev_err_probe(dev, -EINVAL, "Invalid firmware provided AAF gain\n");
}
return 0;
}
static const struct ad7768_chip_info ad7768_chip_info = {
.name = "ad7768-1",
.channel_spec = ad7768_channels,
.num_channels = ARRAY_SIZE(ad7768_channels),
.has_vcm_regulator = true,
};
static const struct ad7768_chip_info adaq7767_chip_info = {
.name = "adaq7767-1",
.channel_spec = ad7768_channels,
.num_channels = ARRAY_SIZE(ad7768_channels),
.has_variable_aaf = true,
};
static const struct ad7768_chip_info adaq7768_chip_info = {
.name = "adaq7768-1",
.channel_spec = adaq776x_channels,
.num_channels = ARRAY_SIZE(adaq776x_channels),
.pga_gains = adaq7768_gains,
.default_pga_mode = AD7768_PGA_GAIN_2,
.num_pga_modes = ARRAY_SIZE(adaq7768_gains),
.pgia_mode2pin_offset = 6,
.has_pga = true,
};
static const struct ad7768_chip_info adaq7769_chip_info = {
.name = "adaq7769-1",
.channel_spec = adaq776x_channels,
.num_channels = ARRAY_SIZE(adaq776x_channels),
.pga_gains = adaq7769_gains,
.default_pga_mode = AD7768_PGA_GAIN_0,
.num_pga_modes = ARRAY_SIZE(adaq7769_gains),
.pgia_mode2pin_offset = 0,
.has_pga = true,
.has_variable_aaf = true,
};
static int ad7768_probe(struct spi_device *spi)
{
struct ad7768_state *st;
@@ -1347,6 +1644,7 @@ static int ad7768_probe(struct spi_device *spi)
return ret;
}
st->chip = spi_get_device_match_data(spi);
st->spi = spi;
st->regmap = devm_regmap_init_spi(spi, &ad7768_regmap_config);
@@ -1371,14 +1669,20 @@ static int ad7768_probe(struct spi_device *spi)
st->mclk_freq = clk_get_rate(st->mclk);
indio_dev->channels = ad7768_channels;
indio_dev->num_channels = ARRAY_SIZE(ad7768_channels);
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->channels = st->chip->channel_spec;
indio_dev->num_channels = st->chip->num_channels;
indio_dev->name = st->chip->name;
indio_dev->info = &ad7768_info;
indio_dev->modes = INDIO_DIRECT_MODE;
/* Register VCM output regulator */
ret = ad7768_register_regulators(&spi->dev, st, indio_dev);
if (st->chip->has_vcm_regulator) {
ret = ad7768_register_vcm_regulator(&spi->dev, st, indio_dev);
if (ret)
return ret;
}
ret = ad7768_parse_aaf_gain(&spi->dev, st);
if (ret)
return ret;
@@ -1389,14 +1693,26 @@ static int ad7768_probe(struct spi_device *spi)
}
init_completion(&st->completion);
ret = ad7768_set_channel_label(indio_dev, ARRAY_SIZE(ad7768_channels));
ret = devm_mutex_init(&spi->dev, &st->pga_lock);
if (ret)
return ret;
ret = devm_request_irq(&spi->dev, spi->irq,
&ad7768_interrupt,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
if (st->chip->has_pga) {
ret = ad7768_setup_pga(&spi->dev, st);
if (ret)
return ret;
ret = ad7768_set_pga_gain(st, st->chip->default_pga_mode);
if (ret)
return ret;
}
ret = ad7768_set_channel_label(indio_dev, st->chip->num_channels);
if (ret)
return ret;
ret = devm_request_irq(&spi->dev, spi->irq, &ad7768_interrupt,
IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
indio_dev->name, indio_dev);
if (ret)
return ret;
@@ -1409,13 +1725,19 @@ static int ad7768_probe(struct spi_device *spi)
}
static const struct spi_device_id ad7768_id_table[] = {
{ "ad7768-1", 0 },
{ "ad7768-1", (kernel_ulong_t)&ad7768_chip_info },
{ "adaq7767-1", (kernel_ulong_t)&adaq7767_chip_info },
{ "adaq7768-1", (kernel_ulong_t)&adaq7768_chip_info },
{ "adaq7769-1", (kernel_ulong_t)&adaq7769_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ad7768_id_table);
static const struct of_device_id ad7768_of_match[] = {
{ .compatible = "adi,ad7768-1" },
{ .compatible = "adi,ad7768-1", .data = &ad7768_chip_info },
{ .compatible = "adi,adaq7767-1", .data = &adaq7767_chip_info },
{ .compatible = "adi,adaq7768-1", .data = &adaq7768_chip_info },
{ .compatible = "adi,adaq7769-1", .data = &adaq7769_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, ad7768_of_match);

View File

@@ -840,7 +840,7 @@ static int ad7779_setup_without_backend(struct ad7779_state *st, struct iio_dev
iio_trigger_set_drvdata(st->trig, st);
ret = devm_request_irq(dev, st->spi->irq, iio_trigger_generic_data_rdy_poll,
IRQF_ONESHOT | IRQF_NO_AUTOEN, indio_dev->name,
IRQF_NO_THREAD | IRQF_NO_AUTOEN, indio_dev->name,
st->trig);
if (ret)
return dev_err_probe(dev, ret, "request IRQ %d failed\n",

View File

@@ -5,29 +5,29 @@
* Copyright 2012-2020 Analog Devices Inc.
*/
#include <linux/bitfield.h>
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/seq_file.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/units.h>
#include <linux/iio/backend.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/clk.h>
/*
* ADI High-Speed ADC common spi interface registers
* See Application-Note AN-877:
@@ -73,6 +73,7 @@
#define AN877_ADC_OUTPUT_MODE_OFFSET_BINARY 0x0
#define AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT 0x1
#define AN877_ADC_OUTPUT_MODE_GRAY_CODE 0x2
#define AN877_ADC_OUTPUT_MODE_MASK GENMASK(1, 0)
/* AN877_ADC_REG_OUTPUT_PHASE */
#define AN877_ADC_OUTPUT_EVEN_ODD_MODE_EN 0x20
@@ -81,12 +82,20 @@
/* AN877_ADC_REG_OUTPUT_DELAY */
#define AN877_ADC_DCO_DELAY_ENABLE 0x80
/*
* Analog Devices AD9211 10-Bit, 200/250/300 MSPS ADC
*/
#define CHIPID_AD9211 0x06
#define AD9211_DEF_OUTPUT_MODE 0x01
#define AD9211_REG_VREF_MASK GENMASK(4, 0)
/*
* Analog Devices AD9265 16-Bit, 125/105/80 MSPS ADC
*/
#define CHIPID_AD9265 0x64
#define AD9265_DEF_OUTPUT_MODE 0x40
#define AD9265_DEF_OUTPUT_MODE 0x41
#define AD9265_REG_VREF_MASK 0xC0
/*
@@ -94,7 +103,7 @@
*/
#define CHIPID_AD9434 0x6A
#define AD9434_DEF_OUTPUT_MODE 0x00
#define AD9434_DEF_OUTPUT_MODE 0x01
#define AD9434_REG_VREF_MASK GENMASK(4, 0)
/*
@@ -102,7 +111,7 @@
*/
#define CHIPID_AD9467 0x50
#define AD9467_DEF_OUTPUT_MODE 0x08
#define AD9467_DEF_OUTPUT_MODE 0x09
#define AD9467_REG_VREF_MASK 0x0F
/*
@@ -110,6 +119,7 @@
*/
#define CHIPID_AD9643 0x82
#define AD9643_DEF_OUTPUT_MODE 0x01
#define AD9643_REG_VREF_MASK 0x1F
/*
@@ -117,6 +127,7 @@
*/
#define CHIPID_AD9652 0xC1
#define AD9652_DEF_OUTPUT_MODE 0x01
#define AD9652_REG_VREF_MASK 0xC0
/*
@@ -124,6 +135,7 @@
*/
#define CHIPID_AD9649 0x6F
#define AD9649_DEF_OUTPUT_MODE 0x01
#define AD9649_TEST_POINTS 8
#define AD9647_MAX_TEST_POINTS 32
@@ -145,6 +157,7 @@ struct ad9467_chip_info {
unsigned int num_lanes;
unsigned int dco_en;
unsigned int test_points;
const int *offset_range;
/* data clock output */
bool has_dco;
bool has_dco_invert;
@@ -234,6 +247,21 @@ static int ad9467_reg_access(struct iio_dev *indio_dev, unsigned int reg,
return 0;
}
static const int ad9434_offset_range[] = {
-128, 1, 127,
};
static const unsigned int ad9211_scale_table[][2] = {
{980, 0x10}, {1000, 0x11}, {1020, 0x12}, {1040, 0x13},
{1060, 0x14}, {1080, 0x15}, {1100, 0x16}, {1120, 0x17},
{1140, 0x18}, {1160, 0x19}, {1180, 0x1A}, {1190, 0x1B},
{1200, 0x1C}, {1210, 0x1D}, {1220, 0x1E}, {1230, 0x1F},
{1250, 0x0}, {1270, 0x1}, {1290, 0x2}, {1310, 0x3},
{1330, 0x4}, {1350, 0x5}, {1370, 0x6}, {1390, 0x7},
{1410, 0x8}, {1430, 0x9}, {1450, 0xA}, {1460, 0xB},
{1470, 0xC}, {1480, 0xD}, {1490, 0xE}, {1500, 0xF},
};
static const unsigned int ad9265_scale_table[][2] = {
{1250, 0x00}, {1500, 0x40}, {1750, 0x80}, {2000, 0xC0},
};
@@ -297,8 +325,29 @@ static void __ad9467_get_scale(struct ad9467_state *st, int index,
}, \
}
static const struct iio_chan_spec ad9211_channels[] = {
AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 10, 's'),
};
static const struct iio_chan_spec ad9434_channels[] = {
AD9467_CHAN(0, BIT(IIO_CHAN_INFO_SCALE), 0, 12, 's'),
{
.type = IIO_VOLTAGE,
.indexed = 1,
.channel = 0,
.info_mask_shared_by_type =
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_CALIBBIAS),
.info_mask_shared_by_type_available =
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_CALIBBIAS),
.scan_index = 0,
.scan_type = {
.sign = 's',
.realbits = 12,
.storagebits = 16,
},
},
};
static const struct iio_chan_spec ad9467_channels[] = {
@@ -367,6 +416,24 @@ static const struct ad9467_chip_info ad9434_chip_tbl = {
.default_output_mode = AD9434_DEF_OUTPUT_MODE,
.vref_mask = AD9434_REG_VREF_MASK,
.num_lanes = 6,
.offset_range = ad9434_offset_range,
};
static const struct ad9467_chip_info ad9211_chip_tbl = {
.name = "ad9211",
.id = CHIPID_AD9211,
.max_rate = 300 * HZ_PER_MHZ,
.scale_table = ad9211_scale_table,
.num_scales = ARRAY_SIZE(ad9211_scale_table),
.channels = ad9211_channels,
.num_channels = ARRAY_SIZE(ad9211_channels),
.test_points = AD9647_MAX_TEST_POINTS,
.test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE,
AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1,
.default_output_mode = AD9211_DEF_OUTPUT_MODE,
.vref_mask = AD9211_REG_VREF_MASK,
.has_dco = true,
};
static const struct ad9467_chip_info ad9265_chip_tbl = {
@@ -399,6 +466,7 @@ static const struct ad9467_chip_info ad9643_chip_tbl = {
.test_mask = BIT(AN877_ADC_TESTMODE_RAMP) |
GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY, AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_RAMP + 1,
.default_output_mode = AD9643_DEF_OUTPUT_MODE,
.vref_mask = AD9643_REG_VREF_MASK,
.has_dco = true,
.has_dco_invert = true,
@@ -417,6 +485,7 @@ static const struct ad9467_chip_info ad9649_chip_tbl = {
.test_mask = GENMASK(AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY,
AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_MIXED_BIT_FREQUENCY + 1,
.default_output_mode = AD9649_DEF_OUTPUT_MODE,
.has_dco = true,
.has_dco_invert = true,
.dco_en = AN877_ADC_DCO_DELAY_ENABLE,
@@ -434,6 +503,7 @@ static const struct ad9467_chip_info ad9652_chip_tbl = {
.test_mask = GENMASK(AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE,
AN877_ADC_TESTMODE_OFF),
.test_mask_len = AN877_ADC_TESTMODE_ONE_ZERO_TOGGLE + 1,
.default_output_mode = AD9652_DEF_OUTPUT_MODE,
.vref_mask = AD9652_REG_VREF_MASK,
.has_dco = true,
};
@@ -499,6 +569,33 @@ static int ad9467_set_scale(struct ad9467_state *st, int val, int val2)
return -EINVAL;
}
static int ad9467_get_offset(struct ad9467_state *st, int *val)
{
int ret;
ret = ad9467_spi_read(st, AN877_ADC_REG_OFFSET);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
}
static int ad9467_set_offset(struct ad9467_state *st, int val)
{
int ret;
if (val < st->info->offset_range[0] || val > st->info->offset_range[2])
return -EINVAL;
ret = ad9467_spi_write(st, AN877_ADC_REG_OFFSET, val);
if (ret < 0)
return ret;
return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER,
AN877_ADC_TRANSFER_SYNC);
}
static int ad9467_outputmode_set(struct ad9467_state *st, unsigned int mode)
{
int ret;
@@ -582,10 +679,14 @@ static int ad9467_backend_testmode_off(struct ad9467_state *st,
static int ad9647_calibrate_prepare(struct ad9467_state *st)
{
unsigned int cmode;
unsigned int c;
int ret;
ret = ad9467_outputmode_set(st, st->info->default_output_mode);
cmode = st->info->default_output_mode;
FIELD_MODIFY(AN877_ADC_OUTPUT_MODE_MASK, &cmode,
AN877_ADC_OUTPUT_MODE_OFFSET_BINARY);
ret = ad9467_outputmode_set(st, cmode);
if (ret)
return ret;
@@ -689,7 +790,7 @@ static int ad9647_calibrate_stop(struct ad9467_state *st)
return ret;
}
mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
mode = st->info->default_output_mode;
return ad9467_outputmode_set(st, mode);
}
@@ -802,6 +903,8 @@ static int ad9467_read_raw(struct iio_dev *indio_dev,
struct ad9467_state *st = iio_priv(indio_dev);
switch (m) {
case IIO_CHAN_INFO_CALIBBIAS:
return ad9467_get_offset(st, val);
case IIO_CHAN_INFO_SCALE:
return ad9467_get_scale(st, val, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
@@ -836,6 +939,8 @@ static int ad9467_write_raw(struct iio_dev *indio_dev,
int ret;
switch (mask) {
case IIO_CHAN_INFO_CALIBBIAS:
return ad9467_set_offset(st, val);
case IIO_CHAN_INFO_SCALE:
return ad9467_set_scale(st, val, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
@@ -874,6 +979,10 @@ static int ad9467_read_avail(struct iio_dev *indio_dev,
const struct ad9467_chip_info *info = st->info;
switch (mask) {
case IIO_CHAN_INFO_CALIBBIAS:
*type = IIO_VAL_INT;
*vals = info->offset_range;
return IIO_AVAIL_RANGE;
case IIO_CHAN_INFO_SCALE:
*vals = (const int *)st->scales;
*type = IIO_VAL_INT_PLUS_MICRO;
@@ -1077,12 +1186,17 @@ static ssize_t ad9467_chan_test_mode_write(struct file *file,
if (ret)
return ret;
out_mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
out_mode = st->info->default_output_mode;
ret = ad9467_outputmode_set(st, out_mode);
if (ret)
return ret;
} else {
ret = ad9467_outputmode_set(st, st->info->default_output_mode);
unsigned int cmode;
cmode = st->info->default_output_mode;
FIELD_MODIFY(AN877_ADC_OUTPUT_MODE_MASK, &cmode,
AN877_ADC_OUTPUT_MODE_OFFSET_BINARY);
ret = ad9467_outputmode_set(st, cmode);
if (ret)
return ret;
@@ -1264,6 +1378,7 @@ static int ad9467_probe(struct spi_device *spi)
}
static const struct of_device_id ad9467_of_match[] = {
{ .compatible = "adi,ad9211", .data = &ad9211_chip_tbl, },
{ .compatible = "adi,ad9265", .data = &ad9265_chip_tbl, },
{ .compatible = "adi,ad9434", .data = &ad9434_chip_tbl, },
{ .compatible = "adi,ad9467", .data = &ad9467_chip_tbl, },
@@ -1275,6 +1390,7 @@ static const struct of_device_id ad9467_of_match[] = {
MODULE_DEVICE_TABLE(of, ad9467_of_match);
static const struct spi_device_id ad9467_ids[] = {
{ "ad9211", (kernel_ulong_t)&ad9211_chip_tbl },
{ "ad9265", (kernel_ulong_t)&ad9265_chip_tbl },
{ "ad9434", (kernel_ulong_t)&ad9434_chip_tbl },
{ "ad9467", (kernel_ulong_t)&ad9467_chip_tbl },

View File

@@ -964,7 +964,7 @@ static irqreturn_t ade9000_dready_thread(int irq, void *data)
struct iio_dev *indio_dev = data;
/* Handle data ready interrupt from C4/EVENT/DREADY pin */
if (!iio_device_claim_buffer_mode(indio_dev)) {
if (iio_device_try_claim_buffer_mode(indio_dev)) {
ade9000_iio_push_buffer(indio_dev);
iio_device_release_buffer_mode(indio_dev);
}

View File

@@ -591,17 +591,12 @@ static int axi_adc_create_platform_device(struct adi_axi_adc_state *st,
.size_data = st->info->pdata_sz,
};
struct platform_device *pdev;
int ret;
pdev = platform_device_register_full(&pi);
if (IS_ERR(pdev))
return PTR_ERR(pdev);
ret = devm_add_action_or_reset(st->dev, axi_adc_child_remove, pdev);
if (ret)
return ret;
return 0;
return devm_add_action_or_reset(st->dev, axi_adc_child_remove, pdev);
}
static const struct iio_backend_ops adi_axi_adc_ops = {
@@ -674,13 +669,14 @@ static const struct iio_backend_info axi_ad408x = {
static int adi_axi_adc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct adi_axi_adc_state *st;
void __iomem *base;
unsigned int ver;
struct clk *clk;
int ret;
st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
if (!st)
return -ENOMEM;
@@ -688,20 +684,19 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
st->dev = &pdev->dev;
st->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&axi_adc_regmap_config);
st->dev = dev;
st->regmap = devm_regmap_init_mmio(dev, base, &axi_adc_regmap_config);
if (IS_ERR(st->regmap))
return dev_err_probe(&pdev->dev, PTR_ERR(st->regmap),
return dev_err_probe(dev, PTR_ERR(st->regmap),
"failed to init register map\n");
st->info = device_get_match_data(&pdev->dev);
st->info = device_get_match_data(dev);
if (!st->info)
return -ENODEV;
clk = devm_clk_get_enabled(&pdev->dev, NULL);
clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
return dev_err_probe(&pdev->dev, PTR_ERR(clk),
return dev_err_probe(dev, PTR_ERR(clk),
"failed to get clock\n");
/*
@@ -716,47 +711,42 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
if (ret)
return ret;
if (ADI_AXI_PCORE_VER_MAJOR(ver) !=
ADI_AXI_PCORE_VER_MAJOR(st->info->version)) {
dev_err(&pdev->dev,
"Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
ADI_AXI_PCORE_VER_MAJOR(st->info->version),
ADI_AXI_PCORE_VER_MINOR(st->info->version),
ADI_AXI_PCORE_VER_PATCH(st->info->version),
ADI_AXI_PCORE_VER_MAJOR(ver),
ADI_AXI_PCORE_VER_MINOR(ver),
ADI_AXI_PCORE_VER_PATCH(ver));
return -ENODEV;
}
if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(st->info->version))
return dev_err_probe(dev, -ENODEV,
"Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
ADI_AXI_PCORE_VER_MAJOR(st->info->version),
ADI_AXI_PCORE_VER_MINOR(st->info->version),
ADI_AXI_PCORE_VER_PATCH(st->info->version),
ADI_AXI_PCORE_VER_MAJOR(ver),
ADI_AXI_PCORE_VER_MINOR(ver),
ADI_AXI_PCORE_VER_PATCH(ver));
ret = devm_iio_backend_register(&pdev->dev, st->info->backend_info, st);
ret = devm_iio_backend_register(dev, st->info->backend_info, st);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"failed to register iio backend\n");
return dev_err_probe(dev, ret, "failed to register iio backend\n");
device_for_each_child_node_scoped(&pdev->dev, child) {
device_for_each_child_node_scoped(dev, child) {
int val;
if (!st->info->has_child_nodes)
return dev_err_probe(&pdev->dev, -EINVAL,
return dev_err_probe(dev, -EINVAL,
"invalid fdt axi-dac compatible.");
/* Processing only reg 0 node */
ret = fwnode_property_read_u32(child, "reg", &val);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"invalid reg property.");
return dev_err_probe(dev, ret, "invalid reg property.");
if (val != 0)
return dev_err_probe(&pdev->dev, -EINVAL,
return dev_err_probe(dev, -EINVAL,
"invalid node address.");
ret = axi_adc_create_platform_device(st, child);
if (ret)
return dev_err_probe(&pdev->dev, -EINVAL,
return dev_err_probe(dev, -EINVAL,
"cannot create device.");
}
dev_info(&pdev->dev, "AXI ADC IP core (%d.%.2d.%c) probed\n",
dev_info(dev, "AXI ADC IP core (%d.%.2d.%c) probed\n",
ADI_AXI_PCORE_VER_MAJOR(ver),
ADI_AXI_PCORE_VER_MINOR(ver),
ADI_AXI_PCORE_VER_PATCH(ver));

View File

@@ -472,16 +472,18 @@ static int aspeed_adc_probe(struct platform_device *pdev)
struct aspeed_adc_data *data;
int ret;
u32 adc_engine_control_reg_val;
struct device *dev = &pdev->dev;
struct device_node *np = dev_of_node(dev);
unsigned long scaler_flags = 0;
char clk_name[32], clk_parent_name[32];
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data));
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->dev = &pdev->dev;
data->model_data = of_device_get_match_data(&pdev->dev);
data->dev = dev;
data->model_data = of_device_get_match_data(dev);
platform_set_drvdata(pdev, indio_dev);
data->base = devm_platform_ioremap_resource(pdev, 0);
@@ -491,16 +493,15 @@ static int aspeed_adc_probe(struct platform_device *pdev)
/* Register ADC clock prescaler with source specified by device tree. */
spin_lock_init(&data->clk_lock);
snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), "%s",
of_clk_get_parent_name(pdev->dev.of_node, 0));
of_clk_get_parent_name(np, 0));
snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-fixed-div",
data->model_data->model_name);
data->fixed_div_clk = clk_hw_register_fixed_factor(
&pdev->dev, clk_name, clk_parent_name, 0, 1, 2);
data->fixed_div_clk = clk_hw_register_fixed_factor(dev, clk_name,
clk_parent_name, 0, 1, 2);
if (IS_ERR(data->fixed_div_clk))
return PTR_ERR(data->fixed_div_clk);
ret = devm_add_action_or_reset(data->dev,
aspeed_adc_unregister_fixed_divider,
ret = devm_add_action_or_reset(dev, aspeed_adc_unregister_fixed_divider,
data->fixed_div_clk);
if (ret)
return ret;
@@ -510,7 +511,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler",
data->model_data->model_name);
data->clk_prescaler = devm_clk_hw_register_divider(
&pdev->dev, clk_name, clk_parent_name, 0,
dev, clk_name, clk_parent_name, 0,
data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0,
&data->clk_lock);
if (IS_ERR(data->clk_prescaler))
@@ -526,7 +527,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-scaler",
data->model_data->model_name);
data->clk_scaler = devm_clk_hw_register_divider(
&pdev->dev, clk_name, clk_parent_name, scaler_flags,
dev, clk_name, clk_parent_name, scaler_flags,
data->base + ASPEED_REG_CLOCK_CONTROL, 0,
data->model_data->scaler_bit_width,
data->model_data->need_prescaler ? CLK_DIVIDER_ONE_BASED : 0,
@@ -534,16 +535,14 @@ static int aspeed_adc_probe(struct platform_device *pdev)
if (IS_ERR(data->clk_scaler))
return PTR_ERR(data->clk_scaler);
data->rst = devm_reset_control_get_shared(&pdev->dev, NULL);
if (IS_ERR(data->rst)) {
dev_err(&pdev->dev,
"invalid or missing reset controller device tree entry");
return PTR_ERR(data->rst);
}
data->rst = devm_reset_control_get_shared(dev, NULL);
if (IS_ERR(data->rst))
return dev_err_probe(dev, PTR_ERR(data->rst),
"invalid or missing reset controller device tree entry");
reset_control_deassert(data->rst);
ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert,
data->rst);
ret = devm_add_action_or_reset(dev, aspeed_adc_reset_assert, data->rst);
if (ret)
return ret;
@@ -555,7 +554,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
if (ret)
return ret;
if (of_property_present(data->dev->of_node, "aspeed,battery-sensing")) {
if (of_property_present(np, "aspeed,battery-sensing")) {
if (data->model_data->bat_sense_sup) {
data->battery_sensing = 1;
if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) &
@@ -567,15 +566,13 @@ static int aspeed_adc_probe(struct platform_device *pdev)
data->battery_mode_gain.div = 2;
}
} else
dev_warn(&pdev->dev,
"Failed to enable battery-sensing mode\n");
dev_warn(dev, "Failed to enable battery-sensing mode\n");
}
ret = clk_prepare_enable(data->clk_scaler->clk);
if (ret)
return ret;
ret = devm_add_action_or_reset(data->dev,
aspeed_adc_clk_disable_unprepare,
ret = devm_add_action_or_reset(dev, aspeed_adc_clk_disable_unprepare,
data->clk_scaler->clk);
if (ret)
return ret;
@@ -593,8 +590,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
writel(adc_engine_control_reg_val,
data->base + ASPEED_REG_ENGINE_CONTROL);
ret = devm_add_action_or_reset(data->dev, aspeed_adc_power_down,
data);
ret = devm_add_action_or_reset(dev, aspeed_adc_power_down, data);
if (ret)
return ret;
@@ -626,8 +622,7 @@ static int aspeed_adc_probe(struct platform_device *pdev)
aspeed_adc_iio_channels;
indio_dev->num_channels = data->model_data->num_channels;
ret = devm_iio_device_register(data->dev, indio_dev);
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct aspeed_adc_trim_locate ast2500_adc_trim = {

View File

@@ -543,22 +543,21 @@ static const struct iio_chan_spec exynos_adc_iio_channels[] = {
static int exynos_adc_probe(struct platform_device *pdev)
{
struct exynos_adc *info = NULL;
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
struct iio_dev *indio_dev = NULL;
int ret;
int irq;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct exynos_adc));
indio_dev = devm_iio_device_alloc(dev, sizeof(struct exynos_adc));
if (!indio_dev)
return -ENOMEM;
info = iio_priv(indio_dev);
info->data = exynos_adc_get_data(pdev);
if (!info->data) {
dev_err(&pdev->dev, "failed getting exynos_adc_data\n");
return -EINVAL;
}
if (!info->data)
return dev_err_probe(dev, -EINVAL, "failed getting exynos_adc_data\n");
info->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(info->regs))
@@ -566,44 +565,34 @@ static int exynos_adc_probe(struct platform_device *pdev)
if (info->data->needs_adc_phy) {
info->pmu_map = syscon_regmap_lookup_by_phandle(
pdev->dev.of_node,
"samsung,syscon-phandle");
if (IS_ERR(info->pmu_map)) {
dev_err(&pdev->dev, "syscon regmap lookup failed.\n");
return PTR_ERR(info->pmu_map);
}
info->pmu_map = syscon_regmap_lookup_by_phandle(np, "samsung,syscon-phandle");
if (IS_ERR(info->pmu_map))
return dev_err_probe(dev, PTR_ERR(info->pmu_map),
"syscon regmap lookup failed.\n");
}
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
info->irq = irq;
info->dev = &pdev->dev;
info->dev = dev;
init_completion(&info->completion);
info->clk = devm_clk_get(&pdev->dev, "adc");
if (IS_ERR(info->clk)) {
dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
PTR_ERR(info->clk));
return PTR_ERR(info->clk);
}
info->clk = devm_clk_get(dev, "adc");
if (IS_ERR(info->clk))
return dev_err_probe(dev, PTR_ERR(info->clk), "failed getting clock\n");
if (info->data->needs_sclk) {
info->sclk = devm_clk_get(&pdev->dev, "sclk");
if (IS_ERR(info->sclk)) {
dev_err(&pdev->dev,
"failed getting sclk clock, err = %ld\n",
PTR_ERR(info->sclk));
return PTR_ERR(info->sclk);
}
info->sclk = devm_clk_get(dev, "sclk");
if (IS_ERR(info->sclk))
return dev_err_probe(dev, PTR_ERR(info->sclk),
"failed getting sclk clock\n");
}
info->vdd = devm_regulator_get(&pdev->dev, "vdd");
info->vdd = devm_regulator_get(dev, "vdd");
if (IS_ERR(info->vdd))
return dev_err_probe(&pdev->dev, PTR_ERR(info->vdd),
"failed getting regulator");
return dev_err_probe(dev, PTR_ERR(info->vdd), "failed getting regulator");
ret = regulator_enable(info->vdd);
if (ret)
@@ -619,7 +608,7 @@ static int exynos_adc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, indio_dev);
indio_dev->name = dev_name(&pdev->dev);
indio_dev->name = dev_name(dev);
indio_dev->info = &exynos_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = exynos_adc_iio_channels;
@@ -627,11 +616,9 @@ static int exynos_adc_probe(struct platform_device *pdev)
mutex_init(&info->lock);
ret = request_irq(info->irq, exynos_adc_isr,
0, dev_name(&pdev->dev), info);
ret = request_irq(info->irq, exynos_adc_isr, 0, dev_name(dev), info);
if (ret < 0) {
dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
info->irq);
dev_err(dev, "failed requesting irq, irq = %d\n", info->irq);
goto err_disable_clk;
}
@@ -644,7 +631,7 @@ static int exynos_adc_probe(struct platform_device *pdev)
ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev);
if (ret < 0) {
dev_err(&pdev->dev, "failed adding child nodes\n");
dev_err(dev, "failed adding child nodes\n");
goto err_of_populate;
}

View File

@@ -815,7 +815,7 @@ static int mcp3911_probe(struct spi_device *spi)
* don't enable the interrupt to avoid extra load on the system.
*/
ret = devm_request_irq(dev, spi->irq, &iio_trigger_generic_data_rdy_poll,
IRQF_NO_AUTOEN | IRQF_ONESHOT,
IRQF_NO_AUTOEN | IRQF_NO_THREAD,
indio_dev->name, adc->trig);
if (ret)
return ret;

View File

@@ -171,5 +171,4 @@ module_mcb_driver(men_z188_driver);
MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("IIO ADC driver for MEN 16z188 ADC Core");
MODULE_ALIAS("mcb:16z188");
MODULE_IMPORT_NS("MCB");

File diff suppressed because it is too large Load Diff

View File

@@ -934,20 +934,15 @@ static int rradc_probe(struct platform_device *pdev)
chip = iio_priv(indio_dev);
chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!chip->regmap) {
dev_err(dev, "Couldn't get parent's regmap\n");
return -EINVAL;
}
if (!chip->regmap)
return dev_err_probe(dev, -EINVAL, "Couldn't get parent's regmap\n");
chip->dev = dev;
mutex_init(&chip->conversion_lock);
ret = device_property_read_u32(dev, "reg", &chip->base);
if (ret < 0) {
dev_err(chip->dev, "Couldn't find reg address, ret = %d\n",
ret);
return ret;
}
if (ret < 0)
return dev_err_probe(dev, ret, "Couldn't find reg address\n");
batt_id_delay = -1;
ret = device_property_read_u32(dev, "qcom,batt-id-delay-ms",
@@ -975,10 +970,9 @@ static int rradc_probe(struct platform_device *pdev)
/* Get the PMIC revision, we need it to handle some varying coefficients */
chip->pmic = qcom_pmic_get(chip->dev);
if (IS_ERR(chip->pmic)) {
dev_err(chip->dev, "Unable to get reference to PMIC device\n");
return PTR_ERR(chip->pmic);
}
if (IS_ERR(chip->pmic))
return dev_err_probe(dev, PTR_ERR(chip->pmic),
"Unable to get reference to PMIC device\n");
switch (chip->pmic->subtype) {
case PMI8998_SUBTYPE:

View File

@@ -456,6 +456,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
{
const struct rockchip_saradc_data *match_data;
struct rockchip_saradc *info = NULL;
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
struct iio_dev *indio_dev = NULL;
int ret;
@@ -464,23 +465,21 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
if (!np)
return -ENODEV;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
if (!indio_dev)
return -ENOMEM;
info = iio_priv(indio_dev);
match_data = of_device_get_match_data(&pdev->dev);
match_data = of_device_get_match_data(dev);
if (!match_data)
return dev_err_probe(&pdev->dev, -ENODEV,
"failed to match device\n");
return dev_err_probe(dev, -ENODEV, "failed to match device\n");
info->data = match_data;
/* Sanity check for possible later IP variants with more channels */
if (info->data->num_channels > SARADC_MAX_CHANNELS)
return dev_err_probe(&pdev->dev, -EINVAL,
"max channels exceeded");
return dev_err_probe(dev, -EINVAL, "max channels exceeded");
info->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(info->regs))
@@ -490,12 +489,10 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
* The reset should be an optional property, as it should work
* with old devicetrees as well
*/
info->reset = devm_reset_control_get_optional_exclusive(&pdev->dev,
"saradc-apb");
if (IS_ERR(info->reset)) {
ret = PTR_ERR(info->reset);
return dev_err_probe(&pdev->dev, ret, "failed to get saradc-apb\n");
}
info->reset = devm_reset_control_get_optional_exclusive(dev, "saradc-apb");
if (IS_ERR(info->reset))
return dev_err_probe(dev, PTR_ERR(info->reset),
"failed to get saradc-apb\n");
init_completion(&info->completion);
@@ -503,16 +500,14 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,
ret = devm_request_irq(dev, irq, rockchip_saradc_isr,
0, dev_name(&pdev->dev), info);
if (ret < 0) {
dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
return ret;
}
if (ret < 0)
return dev_err_probe(dev, ret, "failed requesting irq %d\n", irq);
info->vref = devm_regulator_get(&pdev->dev, "vref");
info->vref = devm_regulator_get(dev, "vref");
if (IS_ERR(info->vref))
return dev_err_probe(&pdev->dev, PTR_ERR(info->vref),
return dev_err_probe(dev, PTR_ERR(info->vref),
"failed to get regulator\n");
if (info->reset)
@@ -520,11 +515,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
ret = regulator_enable(info->vref);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
"failed to enable vref regulator\n");
return dev_err_probe(dev, ret, "failed to enable vref regulator\n");
ret = devm_add_action_or_reset(&pdev->dev,
rockchip_saradc_regulator_disable, info);
ret = devm_add_action_or_reset(dev, rockchip_saradc_regulator_disable, info);
if (ret)
return ret;
@@ -534,14 +527,13 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
info->uv_vref = ret;
info->pclk = devm_clk_get_enabled(&pdev->dev, "apb_pclk");
info->pclk = devm_clk_get_enabled(dev, "apb_pclk");
if (IS_ERR(info->pclk))
return dev_err_probe(&pdev->dev, PTR_ERR(info->pclk),
"failed to get pclk\n");
return dev_err_probe(dev, PTR_ERR(info->pclk), "failed to get pclk\n");
info->clk = devm_clk_get_enabled(&pdev->dev, "saradc");
info->clk = devm_clk_get_enabled(dev, "saradc");
if (IS_ERR(info->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(info->clk),
return dev_err_probe(dev, PTR_ERR(info->clk),
"failed to get adc clock\n");
/*
* Use a default value for the converter clock.
@@ -549,18 +541,17 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
*/
ret = clk_set_rate(info->clk, info->data->clk_rate);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
"failed to set adc clk rate\n");
return dev_err_probe(dev, ret, "failed to set adc clk rate\n");
platform_set_drvdata(pdev, indio_dev);
indio_dev->name = dev_name(&pdev->dev);
indio_dev->name = dev_name(dev);
indio_dev->info = &rockchip_saradc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = info->data->channels;
indio_dev->num_channels = info->data->num_channels;
ret = devm_iio_triggered_buffer_setup(&indio_dev->dev, indio_dev, NULL,
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
rockchip_saradc_trigger_handler,
NULL);
if (ret)
@@ -571,7 +562,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
if (ret)
return ret;
ret = devm_add_action_or_reset(&pdev->dev,
ret = devm_add_action_or_reset(dev,
rockchip_saradc_regulator_unreg_notifier,
info);
if (ret)
@@ -579,7 +570,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
mutex_init(&info->lock);
return devm_iio_device_register(&pdev->dev, indio_dev);
return devm_iio_device_register(dev, indio_dev);
}
static int rockchip_saradc_suspend(struct device *dev)

View File

@@ -867,10 +867,8 @@ static int sc27xx_adc_probe(struct platform_device *pdev)
int ret;
pdata = of_device_get_match_data(dev);
if (!pdata) {
dev_err(dev, "No matching driver data found\n");
return -EINVAL;
}
if (!pdata)
return dev_err_probe(dev, -EINVAL, "No matching driver data found\n");
indio_dev = devm_iio_device_alloc(dev, sizeof(*sc27xx_data));
if (!indio_dev)
@@ -879,56 +877,43 @@ static int sc27xx_adc_probe(struct platform_device *pdev)
sc27xx_data = iio_priv(indio_dev);
sc27xx_data->regmap = dev_get_regmap(dev->parent, NULL);
if (!sc27xx_data->regmap) {
dev_err(dev, "failed to get ADC regmap\n");
return -ENODEV;
}
if (!sc27xx_data->regmap)
return dev_err_probe(dev, -ENODEV, "failed to get ADC regmap\n");
ret = of_property_read_u32(np, "reg", &sc27xx_data->base);
if (ret) {
dev_err(dev, "failed to get ADC base address\n");
return ret;
}
if (ret)
return dev_err_probe(dev, ret, "failed to get ADC base address\n");
sc27xx_data->irq = platform_get_irq(pdev, 0);
if (sc27xx_data->irq < 0)
return sc27xx_data->irq;
ret = of_hwspin_lock_get_id(np, 0);
if (ret < 0) {
dev_err(dev, "failed to get hwspinlock id\n");
return ret;
}
if (ret < 0)
return dev_err_probe(dev, ret, "failed to get hwspinlock id\n");
sc27xx_data->hwlock = devm_hwspin_lock_request_specific(dev, ret);
if (!sc27xx_data->hwlock) {
dev_err(dev, "failed to request hwspinlock\n");
return -ENXIO;
}
if (!sc27xx_data->hwlock)
return dev_err_probe(dev, -ENXIO, "failed to request hwspinlock\n");
sc27xx_data->dev = dev;
if (pdata->set_volref) {
sc27xx_data->volref = devm_regulator_get(dev, "vref");
if (IS_ERR(sc27xx_data->volref)) {
ret = PTR_ERR(sc27xx_data->volref);
return dev_err_probe(dev, ret, "failed to get ADC volref\n");
}
if (IS_ERR(sc27xx_data->volref))
return dev_err_probe(dev, PTR_ERR(sc27xx_data->volref),
"failed to get ADC volref\n");
}
sc27xx_data->var_data = pdata;
sc27xx_data->var_data->init_scale(sc27xx_data);
ret = sc27xx_adc_enable(sc27xx_data);
if (ret) {
dev_err(dev, "failed to enable ADC module\n");
return ret;
}
if (ret)
return dev_err_probe(dev, ret, "failed to enable ADC module\n");
ret = devm_add_action_or_reset(dev, sc27xx_adc_disable, sc27xx_data);
if (ret) {
dev_err(dev, "failed to add ADC disable action\n");
return ret;
}
if (ret)
return dev_err_probe(dev, ret, "failed to add ADC disable action\n");
indio_dev->name = dev_name(dev);
indio_dev->modes = INDIO_DIRECT_MODE;

View File

@@ -0,0 +1,739 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Texas Instruments ADS1018 ADC driver
*
* Copyright (C) 2025 Kurt Borja <kuurtb@gmail.com>
*/
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bitmap.h>
#include <linux/dev_printk.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/math.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/types.h>
#include <linux/units.h>
#include <asm/byteorder.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#define ADS1018_CFG_OS_TRIG BIT(15)
#define ADS1018_CFG_TS_MODE_EN BIT(4)
#define ADS1018_CFG_PULL_UP BIT(3)
#define ADS1018_CFG_NOP BIT(1)
#define ADS1018_CFG_VALID (ADS1018_CFG_PULL_UP | ADS1018_CFG_NOP)
#define ADS1018_CFG_MUX_MASK GENMASK(14, 12)
#define ADS1018_CFG_PGA_MASK GENMASK(11, 9)
#define ADS1018_PGA_DEFAULT 2
#define ADS1018_CFG_MODE_MASK BIT(8)
#define ADS1018_MODE_CONTINUOUS 0
#define ADS1018_MODE_ONESHOT 1
#define ADS1018_CFG_DRATE_MASK GENMASK(7, 5)
#define ADS1018_DRATE_DEFAULT 4
#define ADS1018_NUM_PGA_MODES 6
#define ADS1018_CHANNELS_MAX 10
struct ads1018_chan_data {
u8 pga_mode;
u8 data_rate_mode;
};
struct ads1018_chip_info {
const char *name;
const struct iio_chan_spec *channels;
unsigned long num_channels;
/* IIO_VAL_INT */
const u32 *data_rate_mode_to_hz;
unsigned long num_data_rate_mode_to_hz;
/*
* Let `res` be the chip's resolution and `fsr` (millivolts) be the
* full-scale range corresponding to the PGA mode given by the array
* index. Then, the gain is calculated using the following formula:
*
* gain = |fsr| / 2^(res - 1)
*
* This value then has to be represented in IIO_VAL_INT_PLUS_NANO
* format. For example if:
*
* gain = 6144 / 2^(16 - 1) = 0.1875
*
* ...then the formatted value is:
*
* { 0, 187500000 }
*/
const u32 pga_mode_to_gain[ADS1018_NUM_PGA_MODES][2];
/* IIO_VAL_INT_PLUS_MICRO */
const u32 temp_scale[2];
};
struct ads1018 {
struct spi_device *spi;
struct iio_trigger *indio_trig;
struct gpio_desc *drdy_gpiod;
int drdy_irq;
struct ads1018_chan_data chan_data[ADS1018_CHANNELS_MAX];
const struct ads1018_chip_info *chip_info;
struct spi_message msg_read;
struct spi_transfer xfer;
__be16 tx_buf[2] __aligned(IIO_DMA_MINALIGN);
__be16 rx_buf[2];
};
#define ADS1018_VOLT_DIFF_CHAN(_index, _chan, _chan2, _realbits) { \
.type = IIO_VOLTAGE, \
.channel = _chan, \
.channel2 = _chan2, \
.scan_index = _index, \
.scan_type = { \
.sign = 's', \
.realbits = _realbits, \
.storagebits = 16, \
.shift = 16 - _realbits, \
.endianness = IIO_BE, \
}, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.indexed = true, \
.differential = true, \
}
#define ADS1018_VOLT_CHAN(_index, _chan, _realbits) { \
.type = IIO_VOLTAGE, \
.channel = _chan, \
.scan_index = _index, \
.scan_type = { \
.sign = 's', \
.realbits = _realbits, \
.storagebits = 16, \
.shift = 16 - _realbits, \
.endianness = IIO_BE, \
}, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.indexed = true, \
}
#define ADS1018_TEMP_CHAN(_index, _realbits) { \
.type = IIO_TEMP, \
.scan_index = _index, \
.scan_type = { \
.sign = 's', \
.realbits = _realbits, \
.storagebits = 16, \
.shift = 16 - _realbits, \
.endianness = IIO_BE, \
}, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
}
static const struct iio_chan_spec ads1118_iio_channels[] = {
ADS1018_VOLT_DIFF_CHAN(0, 0, 1, 16),
ADS1018_VOLT_DIFF_CHAN(1, 0, 3, 16),
ADS1018_VOLT_DIFF_CHAN(2, 1, 3, 16),
ADS1018_VOLT_DIFF_CHAN(3, 2, 3, 16),
ADS1018_VOLT_CHAN(4, 0, 16),
ADS1018_VOLT_CHAN(5, 1, 16),
ADS1018_VOLT_CHAN(6, 2, 16),
ADS1018_VOLT_CHAN(7, 3, 16),
ADS1018_TEMP_CHAN(8, 14),
IIO_CHAN_SOFT_TIMESTAMP(9),
};
static const struct iio_chan_spec ads1018_iio_channels[] = {
ADS1018_VOLT_DIFF_CHAN(0, 0, 1, 12),
ADS1018_VOLT_DIFF_CHAN(1, 0, 3, 12),
ADS1018_VOLT_DIFF_CHAN(2, 1, 3, 12),
ADS1018_VOLT_DIFF_CHAN(3, 2, 3, 12),
ADS1018_VOLT_CHAN(4, 0, 12),
ADS1018_VOLT_CHAN(5, 1, 12),
ADS1018_VOLT_CHAN(6, 2, 12),
ADS1018_VOLT_CHAN(7, 3, 12),
ADS1018_TEMP_CHAN(8, 12),
IIO_CHAN_SOFT_TIMESTAMP(9),
};
/**
* ads1018_calc_delay - Calculates a suitable delay for a single-shot reading
* @hz: Sampling frequency
*
* Calculates an appropriate delay for a single shot reading given a sampling
* frequency.
*
* Return: Delay in microseconds (Always greater than 0).
*/
static u32 ads1018_calc_delay(unsigned int hz)
{
/*
* Calculate the worst-case sampling rate by subtracting 10% error
* specified in the datasheet...
*/
hz -= DIV_ROUND_UP(hz, 10);
/* ...Then calculate time per sample in microseconds. */
return DIV_ROUND_UP(HZ_PER_MHZ, hz);
}
/**
* ads1018_spi_read_exclusive - Reads a conversion value from the device
* @ads1018: Device data
* @cnv: ADC Conversion value (optional)
* @hold_cs: Keep CS line asserted after the SPI transfer
*
* Reads the most recent ADC conversion value, without updating the
* device's configuration.
*
* Context: Expects SPI bus *exclusive* use.
*
* Return: 0 on success, negative errno on error.
*/
static int ads1018_spi_read_exclusive(struct ads1018 *ads1018, __be16 *cnv,
bool hold_cs)
{
int ret;
ads1018->xfer.cs_change = hold_cs;
ret = spi_sync_locked(ads1018->spi, &ads1018->msg_read);
if (ret)
return ret;
if (cnv)
*cnv = ads1018->rx_buf[0];
return 0;
}
/**
* ads1018_single_shot - Performs a one-shot reading sequence
* @ads1018: Device data
* @chan: Channel specification
* @cnv: Conversion value
*
* Writes a new configuration, waits an appropriate delay, then reads the most
* recent conversion.
*
* Context: Expects iio_device_claim_direct() is held.
*
* Return: 0 on success, negative errno on error.
*/
static int ads1018_single_shot(struct ads1018 *ads1018,
struct iio_chan_spec const *chan, u16 *cnv)
{
u8 max_drate_mode = ads1018->chip_info->num_data_rate_mode_to_hz - 1;
u8 drate = ads1018->chip_info->data_rate_mode_to_hz[max_drate_mode];
u8 pga_mode = ads1018->chan_data[chan->scan_index].pga_mode;
struct spi_transfer xfer[2] = {
{
.tx_buf = ads1018->tx_buf,
.len = sizeof(ads1018->tx_buf[0]),
.delay = {
.value = ads1018_calc_delay(drate),
.unit = SPI_DELAY_UNIT_USECS,
},
.cs_change = 1, /* 16-bit mode requires CS de-assert */
},
{
.rx_buf = ads1018->rx_buf,
.len = sizeof(ads1018->rx_buf[0]),
},
};
u16 cfg;
int ret;
cfg = ADS1018_CFG_VALID | ADS1018_CFG_OS_TRIG;
cfg |= FIELD_PREP(ADS1018_CFG_MUX_MASK, chan->scan_index);
cfg |= FIELD_PREP(ADS1018_CFG_PGA_MASK, pga_mode);
cfg |= FIELD_PREP(ADS1018_CFG_MODE_MASK, ADS1018_MODE_ONESHOT);
cfg |= FIELD_PREP(ADS1018_CFG_DRATE_MASK, max_drate_mode);
if (chan->type == IIO_TEMP)
cfg |= ADS1018_CFG_TS_MODE_EN;
ads1018->tx_buf[0] = cpu_to_be16(cfg);
ret = spi_sync_transfer(ads1018->spi, xfer, ARRAY_SIZE(xfer));
if (ret)
return ret;
*cnv = be16_to_cpu(ads1018->rx_buf[0]);
return 0;
}
static int
ads1018_read_raw_direct_mode(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2,
long mask)
{
struct ads1018 *ads1018 = iio_priv(indio_dev);
const struct ads1018_chip_info *chip_info = ads1018->chip_info;
u8 addr = chan->scan_index;
u8 pga_mode, drate_mode;
u16 cnv;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = ads1018_single_shot(ads1018, chan, &cnv);
if (ret)
return ret;
cnv >>= chan->scan_type.shift;
*val = sign_extend32(cnv, chan->scan_type.realbits - 1);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_VOLTAGE:
pga_mode = ads1018->chan_data[addr].pga_mode;
*val = chip_info->pga_mode_to_gain[pga_mode][0];
*val2 = chip_info->pga_mode_to_gain[pga_mode][1];
return IIO_VAL_INT_PLUS_NANO;
case IIO_TEMP:
*val = chip_info->temp_scale[0];
*val2 = chip_info->temp_scale[1];
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EOPNOTSUPP;
}
case IIO_CHAN_INFO_SAMP_FREQ:
drate_mode = ads1018->chan_data[addr].data_rate_mode;
*val = chip_info->data_rate_mode_to_hz[drate_mode];
return IIO_VAL_INT;
default:
return -EOPNOTSUPP;
}
}
static int
ads1018_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
int ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ads1018_read_raw_direct_mode(indio_dev, chan, val, val2, mask);
iio_device_release_direct(indio_dev);
return ret;
}
static int
ads1018_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
const int **vals, int *type, int *length, long mask)
{
struct ads1018 *ads1018 = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
*type = IIO_VAL_INT_PLUS_NANO;
*vals = (const int *)ads1018->chip_info->pga_mode_to_gain;
*length = ADS1018_NUM_PGA_MODES * 2;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SAMP_FREQ:
*type = IIO_VAL_INT;
*vals = ads1018->chip_info->data_rate_mode_to_hz;
*length = ads1018->chip_info->num_data_rate_mode_to_hz;
return IIO_AVAIL_LIST;
default:
return -EOPNOTSUPP;
}
}
static int
ads1018_write_raw_direct_mode(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2,
long mask)
{
struct ads1018 *ads1018 = iio_priv(indio_dev);
const struct ads1018_chip_info *info = ads1018->chip_info;
unsigned int i;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
for (i = 0; i < ADS1018_NUM_PGA_MODES; i++) {
if (val == info->pga_mode_to_gain[i][0] &&
val2 == info->pga_mode_to_gain[i][1])
break;
}
if (i == ADS1018_NUM_PGA_MODES)
return -EINVAL;
ads1018->chan_data[chan->scan_index].pga_mode = i;
return 0;
case IIO_CHAN_INFO_SAMP_FREQ:
for (i = 0; i < info->num_data_rate_mode_to_hz; i++) {
if (val == info->data_rate_mode_to_hz[i])
break;
}
if (i == info->num_data_rate_mode_to_hz)
return -EINVAL;
ads1018->chan_data[chan->scan_index].data_rate_mode = i;
return 0;
default:
return -EOPNOTSUPP;
}
}
static int
ads1018_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
int ret;
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = ads1018_write_raw_direct_mode(indio_dev, chan, val, val2, mask);
iio_device_release_direct(indio_dev);
return ret;
}
static int
ads1018_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
return IIO_VAL_INT_PLUS_NANO;
default:
return IIO_VAL_INT_PLUS_MICRO;
}
}
static const struct iio_info ads1018_iio_info = {
.read_raw = ads1018_read_raw,
.read_avail = ads1018_read_avail,
.write_raw = ads1018_write_raw,
.write_raw_get_fmt = ads1018_write_raw_get_fmt,
};
static void ads1018_set_trigger_enable(struct ads1018 *ads1018)
{
spi_bus_lock(ads1018->spi->controller);
ads1018_spi_read_exclusive(ads1018, NULL, true);
enable_irq(ads1018->drdy_irq);
}
static void ads1018_set_trigger_disable(struct ads1018 *ads1018)
{
disable_irq(ads1018->drdy_irq);
ads1018_spi_read_exclusive(ads1018, NULL, false);
spi_bus_unlock(ads1018->spi->controller);
}
static int ads1018_set_trigger_state(struct iio_trigger *trig, bool state)
{
struct ads1018 *ads1018 = iio_trigger_get_drvdata(trig);
/*
* We need to lock the SPI bus and tie CS low (hold_cs) to catch
* data-ready interrupts, otherwise the MISO line enters a Hi-Z state.
*/
if (state)
ads1018_set_trigger_enable(ads1018);
else
ads1018_set_trigger_disable(ads1018);
return 0;
}
static const struct iio_trigger_ops ads1018_trigger_ops = {
.set_trigger_state = ads1018_set_trigger_state,
.validate_device = iio_trigger_validate_own_device,
};
static int ads1018_buffer_preenable(struct iio_dev *indio_dev)
{
struct ads1018 *ads1018 = iio_priv(indio_dev);
const struct ads1018_chip_info *chip_info = ads1018->chip_info;
unsigned int pga, drate, addr;
u16 cfg;
addr = find_first_bit(indio_dev->active_scan_mask,
iio_get_masklength(indio_dev));
pga = ads1018->chan_data[addr].pga_mode;
drate = ads1018->chan_data[addr].data_rate_mode;
cfg = ADS1018_CFG_VALID;
cfg |= FIELD_PREP(ADS1018_CFG_MUX_MASK, addr);
cfg |= FIELD_PREP(ADS1018_CFG_PGA_MASK, pga);
cfg |= FIELD_PREP(ADS1018_CFG_MODE_MASK, ADS1018_MODE_CONTINUOUS);
cfg |= FIELD_PREP(ADS1018_CFG_DRATE_MASK, drate);
if (chip_info->channels[addr].type == IIO_TEMP)
cfg |= ADS1018_CFG_TS_MODE_EN;
ads1018->tx_buf[0] = cpu_to_be16(cfg);
ads1018->tx_buf[1] = 0;
return spi_write(ads1018->spi, ads1018->tx_buf, sizeof(ads1018->tx_buf));
}
static int ads1018_buffer_postdisable(struct iio_dev *indio_dev)
{
struct ads1018 *ads1018 = iio_priv(indio_dev);
u16 cfg;
cfg = ADS1018_CFG_VALID;
cfg |= FIELD_PREP(ADS1018_CFG_MODE_MASK, ADS1018_MODE_ONESHOT);
ads1018->tx_buf[0] = cpu_to_be16(cfg);
ads1018->tx_buf[1] = 0;
return spi_write(ads1018->spi, ads1018->tx_buf, sizeof(ads1018->tx_buf));
}
static const struct iio_buffer_setup_ops ads1018_buffer_ops = {
.preenable = ads1018_buffer_preenable,
.postdisable = ads1018_buffer_postdisable,
.validate_scan_mask = iio_validate_scan_mask_onehot,
};
static irqreturn_t ads1018_irq_handler(int irq, void *dev_id)
{
struct ads1018 *ads1018 = dev_id;
/*
* We need to check if the "drdy" pin is actually active or if it's a
* pending interrupt triggered by the SPI transfer.
*/
if (!gpiod_get_value(ads1018->drdy_gpiod))
return IRQ_HANDLED;
iio_trigger_poll(ads1018->indio_trig);
return IRQ_HANDLED;
}
static irqreturn_t ads1018_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ads1018 *ads1018 = iio_priv(indio_dev);
struct {
__be16 conv;
aligned_s64 ts;
} scan = {};
int ret;
if (iio_trigger_using_own(indio_dev)) {
disable_irq(ads1018->drdy_irq);
ret = ads1018_spi_read_exclusive(ads1018, &scan.conv, true);
enable_irq(ads1018->drdy_irq);
} else {
ret = spi_read(ads1018->spi, ads1018->rx_buf, sizeof(ads1018->rx_buf));
scan.conv = ads1018->rx_buf[0];
}
if (!ret)
iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan),
pf->timestamp);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int ads1018_trigger_setup(struct iio_dev *indio_dev)
{
struct ads1018 *ads1018 = iio_priv(indio_dev);
struct spi_device *spi = ads1018->spi;
struct device *dev = &spi->dev;
const char *con_id = "drdy";
int ret;
ads1018->drdy_gpiod = devm_gpiod_get_optional(dev, con_id, GPIOD_IN);
if (IS_ERR(ads1018->drdy_gpiod))
return dev_err_probe(dev, PTR_ERR(ads1018->drdy_gpiod),
"Failed to get %s GPIO.\n", con_id);
/* First try to get IRQ from SPI core, then from GPIO */
if (spi->irq > 0)
ads1018->drdy_irq = spi->irq;
else if (ads1018->drdy_gpiod)
ads1018->drdy_irq = gpiod_to_irq(ads1018->drdy_gpiod);
if (ads1018->drdy_irq < 0)
return dev_err_probe(dev, ads1018->drdy_irq,
"Failed to get IRQ from %s GPIO.\n", con_id);
/* An IRQ line is only an optional requirement for the IIO trigger */
if (ads1018->drdy_irq == 0)
return 0;
ads1018->indio_trig = devm_iio_trigger_alloc(dev, "%s-dev%d-%s",
indio_dev->name,
iio_device_id(indio_dev),
con_id);
if (!ads1018->indio_trig)
return -ENOMEM;
iio_trigger_set_drvdata(ads1018->indio_trig, ads1018);
ads1018->indio_trig->ops = &ads1018_trigger_ops;
ret = devm_iio_trigger_register(dev, ads1018->indio_trig);
if (ret)
return ret;
/*
* The "data-ready" IRQ line is shared with the MOSI pin, thus we need
* to keep it disabled until we actually request data.
*/
return devm_request_irq(dev, ads1018->drdy_irq, ads1018_irq_handler,
IRQF_NO_AUTOEN, ads1018->chip_info->name, ads1018);
}
static int ads1018_spi_probe(struct spi_device *spi)
{
const struct ads1018_chip_info *info = spi_get_device_match_data(spi);
struct device *dev = &spi->dev;
struct iio_dev *indio_dev;
struct ads1018 *ads1018;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*ads1018));
if (!indio_dev)
return -ENOMEM;
ads1018 = iio_priv(indio_dev);
ads1018->spi = spi;
ads1018->chip_info = info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = info->name;
indio_dev->info = &ads1018_iio_info;
indio_dev->channels = info->channels;
indio_dev->num_channels = info->num_channels;
for (unsigned int i = 0; i < ADS1018_CHANNELS_MAX; i++) {
ads1018->chan_data[i].data_rate_mode = ADS1018_DRATE_DEFAULT;
ads1018->chan_data[i].pga_mode = ADS1018_PGA_DEFAULT;
}
ads1018->xfer.rx_buf = ads1018->rx_buf;
ads1018->xfer.len = sizeof(ads1018->rx_buf);
spi_message_init_with_transfers(&ads1018->msg_read, &ads1018->xfer, 1);
ret = ads1018_trigger_setup(indio_dev);
if (ret)
return ret;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
iio_pollfunc_store_time,
ads1018_trigger_handler,
&ads1018_buffer_ops);
if (ret)
return ret;
return devm_iio_device_register(&spi->dev, indio_dev);
}
static const unsigned int ads1018_data_rate_table[] = {
128, 250, 490, 920, 1600, 2400, 3300,
};
static const unsigned int ads1118_data_rate_table[] = {
8, 16, 32, 64, 128, 250, 475, 860,
};
static const struct ads1018_chip_info ads1018_chip_info = {
.name = "ads1018",
.channels = ads1018_iio_channels,
.num_channels = ARRAY_SIZE(ads1018_iio_channels),
.data_rate_mode_to_hz = ads1018_data_rate_table,
.num_data_rate_mode_to_hz = ARRAY_SIZE(ads1018_data_rate_table),
.pga_mode_to_gain = {
{ 3, 0 }, /* fsr = 6144 mV */
{ 2, 0 }, /* fsr = 4096 mV */
{ 1, 0 }, /* fsr = 2048 mV */
{ 0, 500000000 }, /* fsr = 1024 mV */
{ 0, 250000000 }, /* fsr = 512 mV */
{ 0, 125000000 }, /* fsr = 256 mV */
},
.temp_scale = { 125, 0 },
};
static const struct ads1018_chip_info ads1118_chip_info = {
.name = "ads1118",
.channels = ads1118_iio_channels,
.num_channels = ARRAY_SIZE(ads1118_iio_channels),
.data_rate_mode_to_hz = ads1118_data_rate_table,
.num_data_rate_mode_to_hz = ARRAY_SIZE(ads1118_data_rate_table),
.pga_mode_to_gain = {
{ 0, 187500000 }, /* fsr = 6144 mV */
{ 0, 125000000 }, /* fsr = 4096 mV */
{ 0, 62500000 }, /* fsr = 2048 mV */
{ 0, 31250000 }, /* fsr = 1024 mV */
{ 0, 15625000 }, /* fsr = 512 mV */
{ 0, 7812500 }, /* fsr = 256 mV */
},
.temp_scale = { 31, 250000 },
};
static const struct of_device_id ads1018_of_match[] = {
{ .compatible = "ti,ads1018", .data = &ads1018_chip_info },
{ .compatible = "ti,ads1118", .data = &ads1118_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, ads1018_of_match);
static const struct spi_device_id ads1018_spi_match[] = {
{ "ads1018", (kernel_ulong_t)&ads1018_chip_info },
{ "ads1118", (kernel_ulong_t)&ads1118_chip_info },
{ }
};
MODULE_DEVICE_TABLE(spi, ads1018_spi_match);
static struct spi_driver ads1018_spi_driver = {
.driver = {
.name = "ads1018",
.of_match_table = ads1018_of_match,
},
.probe = ads1018_spi_probe,
.id_table = ads1018_spi_match,
};
module_spi_driver(ads1018_spi_driver);
MODULE_DESCRIPTION("Texas Instruments ADS1018 ADC Driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kurt Borja <kuurtb@gmail.com>");

View File

@@ -827,7 +827,7 @@ static int ads131e08_probe(struct spi_device *spi)
if (spi->irq) {
ret = devm_request_irq(&spi->dev, spi->irq,
ads131e08_interrupt,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
IRQF_TRIGGER_FALLING | IRQF_NO_THREAD,
spi->dev.driver->name, indio_dev);
if (ret)
return dev_err_probe(&spi->dev, ret,

View File

@@ -0,0 +1,968 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for Texas Instruments ADS131M02 family ADC chips.
*
* Copyright (C) 2024 Protonic Holland
* Copyright (C) 2025 Oleksij Rempel <kernel@pengutronix.de>, Pengutronix
*
* Primary Datasheet Reference (used for citations):
* ADS131M08 8-Channel, Simultaneously-Sampling, 24-Bit, Delta-Sigma ADC
* Document SBAS950B, Revised February 2021
* https://www.ti.com/lit/ds/symlink/ads131m08.pdf
*/
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/crc-itu-t.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/device/devres.h>
#include <linux/err.h>
#include <linux/iio/iio.h>
#include <linux/lockdep.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/spi/spi.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/unaligned.h>
/* Max channels supported by the largest variant in the family (ADS131M08) */
#define ADS131M_MAX_CHANNELS 8
/* Section 6.7, t_REGACQ (min time after reset) is 5us */
#define ADS131M_RESET_DELAY_US 5
#define ADS131M_WORD_SIZE_BYTES 3
#define ADS131M_RESPONSE_WORDS 1
#define ADS131M_CRC_WORDS 1
/*
* SPI Frame word count calculation.
* Frame = N channel words + 1 response word + 1 CRC word.
* Word size depends on WLENGTH bits in MODE register (Default 24-bit).
*/
#define ADS131M_FRAME_WORDS(nch) \
((nch) + ADS131M_RESPONSE_WORDS + ADS131M_CRC_WORDS)
/*
* SPI Frame byte size calculation.
* Assumes default word size of 24 bits (3 bytes).
*/
#define ADS131M_FRAME_BYTES(nch) \
(ADS131M_FRAME_WORDS(nch) * ADS131M_WORD_SIZE_BYTES)
/*
* Index calculation for the start byte of channel 'x' data within the RX buffer.
* Assumes 24-bit words (3 bytes per word).
* The received frame starts with the response word (e.g., STATUS register
* content when NULL command was sent), followed by data for channels 0 to N-1,
* and finally the output CRC word.
* Response = index 0..2, Chan0 = index 3..5, Chan1 = index 6..8, ...
* Index for ChanX = 3 (response) + x * 3 (channel data size).
*/
#define ADS131M_CHANNEL_INDEX(x) \
((x) * ADS131M_WORD_SIZE_BYTES + ADS131M_WORD_SIZE_BYTES)
#define ADS131M_CMD_NULL 0x0000
#define ADS131M_CMD_RESET 0x0011
#define ADS131M_CMD_ADDR_MASK GENMASK(11, 7)
#define ADS131M_CMD_NUM_MASK GENMASK(6, 0)
#define ADS131M_CMD_RREG_OP 0xa000
#define ADS131M_CMD_WREG_OP 0x6000
#define ADS131M_CMD_RREG(a, n) \
(ADS131M_CMD_RREG_OP | \
FIELD_PREP(ADS131M_CMD_ADDR_MASK, a) | \
FIELD_PREP(ADS131M_CMD_NUM_MASK, n))
#define ADS131M_CMD_WREG(a, n) \
(ADS131M_CMD_WREG_OP | \
FIELD_PREP(ADS131M_CMD_ADDR_MASK, a) | \
FIELD_PREP(ADS131M_CMD_NUM_MASK, n))
/* STATUS Register (0x01h) bit definitions */
#define ADS131M_STATUS_CRC_ERR BIT(12) /* Input CRC error */
#define ADS131M_REG_MODE 0x02
#define ADS131M_MODE_RX_CRC_EN BIT(12) /* Enable Input CRC */
#define ADS131M_MODE_CRC_TYPE_ANSI BIT(11) /* 0 = CCITT, 1 = ANSI */
#define ADS131M_MODE_RESET_FLAG BIT(10)
#define ADS131M_REG_CLOCK 0x03
#define ADS131M_CLOCK_XTAL_DIS BIT(7)
#define ADS131M_CLOCK_EXTREF_EN BIT(6)
/* 1.2V internal reference, in millivolts, for IIO_VAL_FRACTIONAL_LOG2 */
#define ADS131M_VREF_INTERNAL_mV 1200
/* 24-bit resolution */
#define ADS131M_RESOLUTION_BITS 24
/* Signed data uses (RESOLUTION_BITS - 1) magnitude bits */
#define ADS131M_CODE_BITS (ADS131M_RESOLUTION_BITS - 1)
/* External ref FSR = Vref * 0.96 */
#define ADS131M_EXTREF_SCALE_NUM 96
#define ADS131M_EXTREF_SCALE_DEN 100
struct ads131m_configuration {
const struct iio_chan_spec *channels;
const char *name;
u16 reset_ack;
u8 num_channels;
u8 supports_extref:1;
u8 supports_xtal:1;
};
struct ads131m_priv {
struct iio_dev *indio_dev;
struct spi_device *spi;
const struct ads131m_configuration *config;
bool use_external_ref;
int scale_val;
int scale_val2;
struct spi_transfer xfer;
struct spi_message msg;
/*
* Protects the shared tx_buffer and rx_buffer. More importantly,
* this serializes all SPI communication to ensure the atomicity
* of multi-cycle command sequences (like WREG, RREG, or RESET).
*/
struct mutex lock;
/* DMA-safe buffers should be placed at the end of the struct. */
u8 tx_buffer[ADS131M_FRAME_BYTES(ADS131M_MAX_CHANNELS)]
__aligned(IIO_DMA_MINALIGN);
u8 rx_buffer[ADS131M_FRAME_BYTES(ADS131M_MAX_CHANNELS)];
};
/**
* ads131m_tx_frame_unlocked - Sends a command frame with Input CRC
* @priv: Device private data structure.
* @command: The 16-bit command to send (e.g., NULL, RREG, RESET).
*
* This function sends a command in Word 0, and its calculated 16-bit
* CRC in Word 1, as required when Input CRC is enabled.
*
* Return: 0 on success, or a negative error code.
*/
static int ads131m_tx_frame_unlocked(struct ads131m_priv *priv, u32 command)
{
struct iio_dev *indio_dev = priv->indio_dev;
u16 crc;
lockdep_assert_held(&priv->lock);
memset(priv->tx_buffer, 0, ADS131M_FRAME_BYTES(indio_dev->num_channels));
/* Word 0: 16-bit command, MSB-aligned in 24-bit word */
put_unaligned_be16(command, &priv->tx_buffer[0]);
/* Word 1: Input CRC. Calculated over the 3 bytes of Word 0. */
crc = crc_itu_t(0xffff, priv->tx_buffer, 3);
put_unaligned_be16(crc, &priv->tx_buffer[3]);
return spi_sync(priv->spi, &priv->msg);
}
/**
* ads131m_rx_frame_unlocked - Receives a full SPI data frame.
* @priv: Device private data structure.
*
* This function sends a NULL command (with its CRC) to clock out a
* full SPI frame from the device (e.g., response + channel data + CRC).
*
* Return: 0 on success, or a negative error code.
*/
static int ads131m_rx_frame_unlocked(struct ads131m_priv *priv)
{
return ads131m_tx_frame_unlocked(priv, ADS131M_CMD_NULL);
}
/**
* ads131m_check_status_crc_err - Checks for an Input CRC error.
* @priv: Device private data structure.
*
* Sends a NULL command to fetch the STATUS register and checks the
* CRC_ERR bit. This is used to verify the integrity of the previous
* command (like RREG or WREG).
*
* Return: 0 on success, -EIO if CRC_ERR bit is set.
*/
static int ads131m_check_status_crc_err(struct ads131m_priv *priv)
{
struct device *dev = &priv->spi->dev;
u16 status;
int ret;
lockdep_assert_held(&priv->lock);
ret = ads131m_rx_frame_unlocked(priv);
if (ret < 0) {
dev_err_ratelimited(dev,
"SPI error on STATUS read for CRC check\n");
return ret;
}
status = get_unaligned_be16(&priv->rx_buffer[0]);
if (status & ADS131M_STATUS_CRC_ERR) {
dev_err_ratelimited(dev,
"Input CRC error reported in STATUS = 0x%04x\n",
status);
return -EIO;
}
return 0;
}
/**
* ads131m_write_reg_unlocked - Writes a single register and verifies the ACK.
* @priv: Device private data structure.
* @reg: The 8-bit register address.
* @val: The 16-bit value to write.
*
* This function performs the full 3-cycle WREG operation with Input CRC:
* 1. (Cycle 1) Sends WREG command, data, and its calculated CRC.
* 2. (Cycle 2) Sends NULL+CRC to retrieve the response from Cycle 1.
* 3. Verifies the response is the correct ACK for the WREG.
* 4. (Cycle 3) Sends NULL+CRC to retrieve STATUS and check for CRC_ERR.
*
* Return: 0 on success, or a negative error code.
*/
static int ads131m_write_reg_unlocked(struct ads131m_priv *priv, u8 reg, u16 val)
{
struct iio_dev *indio_dev = priv->indio_dev;
u16 command, expected_ack, response, crc;
struct device *dev = &priv->spi->dev;
int ret_crc_err = 0;
int ret;
lockdep_assert_held(&priv->lock);
command = ADS131M_CMD_WREG(reg, 0); /* n = 0 for 1 register */
/*
* Per Table 8-11, WREG response is: 010a aaaa ammm mmmm
* For 1 reg (n = 0 -> m = 0): 010a aaaa a000 0000 = 0x4000 | (reg << 7)
*/
expected_ack = 0x4000 | (reg << 7);
/* Cycle 1: Send WREG Command + Data + Input CRC */
memset(priv->tx_buffer, 0, ADS131M_FRAME_BYTES(indio_dev->num_channels));
/* Word 0: WREG command, 1 reg (n = 0), MSB-aligned */
put_unaligned_be16(command, &priv->tx_buffer[0]);
/* Word 1: Data, MSB-aligned */
put_unaligned_be16(val, &priv->tx_buffer[3]);
/* Word 2: Input CRC. Calculated over Word 0 (Cmd) and Word 1 (Data). */
crc = crc_itu_t(0xffff, priv->tx_buffer, 6);
put_unaligned_be16(crc, &priv->tx_buffer[6]);
/* Ignore the RX buffer (it's from the previous command) */
ret = spi_sync(priv->spi, &priv->msg);
if (ret < 0) {
dev_err_ratelimited(dev, "SPI error on WREG (cycle 1)\n");
return ret;
}
/* Cycle 2: Send NULL Command to get the WREG response */
ret = ads131m_rx_frame_unlocked(priv);
if (ret < 0) {
dev_err_ratelimited(dev, "SPI error on WREG ACK (cycle 2)\n");
return ret;
}
/*
* Response is in the first 2 bytes of the RX buffer
* (MSB-aligned 16-bit response)
*/
response = get_unaligned_be16(&priv->rx_buffer[0]);
if (response != expected_ack) {
dev_err_ratelimited(dev, "WREG(0x%02x) failed, expected ACK 0x%04x, got 0x%04x\n",
reg, expected_ack, response);
ret_crc_err = -EIO;
/*
* Don't return yet, still need to do Cycle 3 to clear
* any potential CRC_ERR flag from this failed command.
*/
}
/*
* Cycle 3: Check STATUS for Input CRC error.
* This is necessary even if ACK was wrong, to clear the CRC_ERR flag.
*/
ret = ads131m_check_status_crc_err(priv);
if (ret < 0)
return ret;
return ret_crc_err;
}
/**
* ads131m_read_reg_unlocked - Reads a single register from the device.
* @priv: Device private data structure.
* @reg: The 8-bit register address.
* @val: Pointer to store the 16-bit register value.
*
* This function performs the full 3-cycle RREG operation with Input CRC:
* 1. (Cycle 1) Sends the RREG command + Input CRC.
* 2. (Cycle 2) Sends NULL+CRC to retrieve the register data.
* 3. (Cycle 3) Sends NULL+CRC to retrieve STATUS and check for CRC_ERR.
*
* Return: 0 on success, or a negative error code.
*/
static int ads131m_read_reg_unlocked(struct ads131m_priv *priv, u8 reg, u16 *val)
{
struct device *dev = &priv->spi->dev;
u16 command;
int ret;
lockdep_assert_held(&priv->lock);
command = ADS131M_CMD_RREG(reg, 0); /* n=0 for 1 register */
/*
* Cycle 1: Send RREG Command + Input CRC
* Ignore the RX buffer (it's from the previous command)
*/
ret = ads131m_tx_frame_unlocked(priv, command);
if (ret < 0) {
dev_err_ratelimited(dev, "SPI error on RREG (cycle 1)\n");
return ret;
}
/* Cycle 2: Send NULL Command to get the register data */
ret = ads131m_rx_frame_unlocked(priv);
if (ret < 0) {
dev_err_ratelimited(dev, "SPI error on RREG data (cycle 2)\n");
return ret;
}
/*
* Per datasheet, for a single reg read, the response is the data.
* It's in the first 2 bytes of the RX buffer (MSB-aligned 16-bit).
*/
*val = get_unaligned_be16(&priv->rx_buffer[0]);
/*
* Cycle 3: Check STATUS for Input CRC error.
* The RREG command does not execute if CRC is bad, but we read
* STATUS anyway to clear the flag in case it was set.
*/
return ads131m_check_status_crc_err(priv);
}
/**
* ads131m_rmw_reg - Reads, modifies, and writes a single register.
* @priv: Device private data structure.
* @reg: The 8-bit register address.
* @clear: Bitmask of bits to clear.
* @set: Bitmask of bits to set.
*
* This function performs an atomic read-modify-write operation on a register.
* It reads the register, applies the clear and set masks, and writes
* the new value back if it has changed.
*
* Return: 0 on success, or a negative error code.
*/
static int ads131m_rmw_reg(struct ads131m_priv *priv, u8 reg, u16 clear, u16 set)
{
u16 old_val, new_val;
int ret;
guard(mutex)(&priv->lock);
ret = ads131m_read_reg_unlocked(priv, reg, &old_val);
if (ret < 0)
return ret;
new_val = (old_val & ~clear) | set;
if (new_val == old_val)
return 0;
return ads131m_write_reg_unlocked(priv, reg, new_val);
}
/**
* ads131m_verify_output_crc - Verifies the CRC of the received SPI frame.
* @priv: Device private data structure.
*
* This function calculates the CRC-16-CCITT (Poly 0x1021, Seed 0xFFFF) over
* the received response and channel data, and compares it to the CRC word
* received at the end of the SPI frame.
*
* Return: 0 on success, -EIO on CRC mismatch.
*/
static int ads131m_verify_output_crc(struct ads131m_priv *priv)
{
struct iio_dev *indio_dev = priv->indio_dev;
struct device *dev = &priv->spi->dev;
u16 calculated_crc, received_crc;
size_t data_len;
lockdep_assert_held(&priv->lock);
/*
* Frame: [Response][Chan 0]...[Chan N-1][CRC Word]
* Data for CRC: [Response][Chan 0]...[Chan N-1]
* Data length = (N_channels + 1) * 3 bytes (at 24-bit word size)
*/
data_len = ADS131M_FRAME_BYTES(indio_dev->num_channels) - 3;
calculated_crc = crc_itu_t(0xffff, priv->rx_buffer, data_len);
/*
* The received 16-bit CRC is MSB-aligned in the last 24-bit word.
* We extract it from the first 2 bytes (BE) of that word.
*/
received_crc = get_unaligned_be16(&priv->rx_buffer[data_len]);
if (calculated_crc != received_crc) {
dev_err_ratelimited(dev, "Output CRC error. Got %04x, expected %04x\n",
received_crc, calculated_crc);
return -EIO;
}
return 0;
}
/**
* ads131m_adc_read - Reads channel data, checks input and output CRCs.
* @priv: Device private data structure.
* @channel: The channel number to read.
* @val: Pointer to store the raw 24-bit value.
*
* This function sends a NULL command (with Input CRC) to retrieve data.
* It checks the received STATUS word for any Input CRC errors from the
* previous command, and then verifies the Output CRC of the current
* data frame.
*
* Return: 0 on success, or a negative error code.
*/
static int ads131m_adc_read(struct ads131m_priv *priv, u8 channel, s32 *val)
{
struct device *dev = &priv->spi->dev;
u16 status;
int ret;
u8 *buf;
guard(mutex)(&priv->lock);
/* Send NULL command + Input CRC, and receive data frame */
ret = ads131m_rx_frame_unlocked(priv);
if (ret < 0)
return ret;
/*
* Check STATUS for Input CRC error from the previous command frame.
* Note: the STATUS word belongs to the frame before this NULL command.
*/
status = get_unaligned_be16(&priv->rx_buffer[0]);
if (status & ADS131M_STATUS_CRC_ERR) {
dev_err_ratelimited(dev,
"Previous input CRC error reported in STATUS (0x%04x)\n",
status);
}
ret = ads131m_verify_output_crc(priv);
if (ret < 0)
return ret;
buf = &priv->rx_buffer[ADS131M_CHANNEL_INDEX(channel)];
*val = sign_extend32(get_unaligned_be24(buf), ADS131M_CODE_BITS);
return 0;
}
static int ads131m_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *channel,
int *val, int *val2, long mask)
{
struct ads131m_priv *priv = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = ads131m_adc_read(priv, channel->channel, val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = priv->scale_val;
*val2 = priv->scale_val2;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
}
#define ADS131M_VOLTAGE_CHANNEL(num) \
{ \
.type = IIO_VOLTAGE, \
.differential = 1, \
.indexed = 1, \
.channel = (num), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec ads131m02_channels[] = {
ADS131M_VOLTAGE_CHANNEL(0),
ADS131M_VOLTAGE_CHANNEL(1),
};
static const struct iio_chan_spec ads131m03_channels[] = {
ADS131M_VOLTAGE_CHANNEL(0),
ADS131M_VOLTAGE_CHANNEL(1),
ADS131M_VOLTAGE_CHANNEL(2),
};
static const struct iio_chan_spec ads131m04_channels[] = {
ADS131M_VOLTAGE_CHANNEL(0),
ADS131M_VOLTAGE_CHANNEL(1),
ADS131M_VOLTAGE_CHANNEL(2),
ADS131M_VOLTAGE_CHANNEL(3),
};
static const struct iio_chan_spec ads131m06_channels[] = {
ADS131M_VOLTAGE_CHANNEL(0),
ADS131M_VOLTAGE_CHANNEL(1),
ADS131M_VOLTAGE_CHANNEL(2),
ADS131M_VOLTAGE_CHANNEL(3),
ADS131M_VOLTAGE_CHANNEL(4),
ADS131M_VOLTAGE_CHANNEL(5),
};
static const struct iio_chan_spec ads131m08_channels[] = {
ADS131M_VOLTAGE_CHANNEL(0),
ADS131M_VOLTAGE_CHANNEL(1),
ADS131M_VOLTAGE_CHANNEL(2),
ADS131M_VOLTAGE_CHANNEL(3),
ADS131M_VOLTAGE_CHANNEL(4),
ADS131M_VOLTAGE_CHANNEL(5),
ADS131M_VOLTAGE_CHANNEL(6),
ADS131M_VOLTAGE_CHANNEL(7),
};
static const struct ads131m_configuration ads131m02_config = {
.channels = ads131m02_channels,
.num_channels = ARRAY_SIZE(ads131m02_channels),
.reset_ack = 0xff22,
.name = "ads131m02",
};
static const struct ads131m_configuration ads131m03_config = {
.channels = ads131m03_channels,
.num_channels = ARRAY_SIZE(ads131m03_channels),
.reset_ack = 0xff23,
.name = "ads131m03",
};
static const struct ads131m_configuration ads131m04_config = {
.channels = ads131m04_channels,
.num_channels = ARRAY_SIZE(ads131m04_channels),
.reset_ack = 0xff24,
.name = "ads131m04",
};
static const struct ads131m_configuration ads131m06_config = {
.channels = ads131m06_channels,
.num_channels = ARRAY_SIZE(ads131m06_channels),
.reset_ack = 0xff26,
.supports_extref = true,
.supports_xtal = true,
.name = "ads131m06",
};
static const struct ads131m_configuration ads131m08_config = {
.channels = ads131m08_channels,
.num_channels = ARRAY_SIZE(ads131m08_channels),
.reset_ack = 0xff28,
.supports_extref = true,
.supports_xtal = true,
.name = "ads131m08",
};
static const struct iio_info ads131m_info = {
.read_raw = ads131m_read_raw,
};
/*
* Prepares the reusable SPI message structure for a full-duplex transfer.
* The ADS131M requires sending a command frame while simultaneously
* receiving the response/data frame from the previous command cycle.
*
* This message is optimized for the primary data acquisition workflow:
* sending a single-word command (like NULL) and receiving a full data
* frame (Response + N*Channels + CRC).
*
* This message is sized for a full data frame and is reused for all
* command/data cycles. The driver does not implement variable-length SPI
* messages.
*
* Return: 0 on success, or a negative error code.
*/
static int ads131m_prepare_message(struct ads131m_priv *priv)
{
struct iio_dev *indio_dev = priv->indio_dev;
struct device *dev = &priv->spi->dev;
int ret;
priv->xfer.tx_buf = priv->tx_buffer;
priv->xfer.rx_buf = priv->rx_buffer;
priv->xfer.len = ADS131M_FRAME_BYTES(indio_dev->num_channels);
spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1);
ret = devm_spi_optimize_message(dev, priv->spi, &priv->msg);
if (ret)
return dev_err_probe(dev, ret, "failed to optimize SPI message\n");
return 0;
}
/**
* ads131m_hw_reset - Pulses the optional hardware reset.
* @priv: Device private data structure.
* @rstc: Reset control for the /RESET line.
*
* Pulses the /RESET line to perform a hardware reset and waits the
* required t_REGACQ time for the device to be ready.
*
* Return: 0 on success, or a negative error code.
*/
static int ads131m_hw_reset(struct ads131m_priv *priv,
struct reset_control *rstc)
{
struct device *dev = &priv->spi->dev;
int ret;
/*
* Manually pulse the reset line using the framework.
* The reset-gpio provider does not implement the .reset op,
* so we must use .assert and .deassert.
*/
ret = reset_control_assert(rstc);
if (ret)
return dev_err_probe(dev, ret, "Failed to assert reset\n");
/* Datasheet: Hold /RESET low for > 2 f_CLKIN cycles. 1us is ample. */
fsleep(1);
ret = reset_control_deassert(rstc);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to deassert reset\n");
/* Wait t_REGACQ (5us) for registers to be accessible */
fsleep(ADS131M_RESET_DELAY_US);
return 0;
}
/**
* ads131m_sw_reset - Issues a software RESET and verifies ACK.
* @priv: Device private data structure.
*
* This function sends a RESET command (with Input CRC), waits t_REGACQ,
* reads back the RESET ACK, and then sends a final NULL to check for
* any input CRC errors.
*
* Return: 0 on success, or a negative error code.
*/
static int ads131m_sw_reset(struct ads131m_priv *priv)
{
u16 expected_ack = priv->config->reset_ack;
struct device *dev = &priv->spi->dev;
u16 response;
int ret;
guard(mutex)(&priv->lock);
ret = ads131m_tx_frame_unlocked(priv, ADS131M_CMD_RESET);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to send RESET command\n");
/* Wait t_REGACQ (5us) for device to be ready after reset */
fsleep(ADS131M_RESET_DELAY_US);
/* Cycle 2: Send NULL + CRC to retrieve the response to the RESET */
ret = ads131m_rx_frame_unlocked(priv);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to read RESET ACK\n");
response = get_unaligned_be16(&priv->rx_buffer[0]);
/* Check against the device-specific ACK value */
if (response != expected_ack)
return dev_err_probe(dev, -EIO,
"RESET ACK mismatch, got 0x%04x, expected 0x%04x\n",
response, expected_ack);
/* Cycle 3: Check STATUS for Input CRC error on the RESET command. */
return ads131m_check_status_crc_err(priv);
}
/**
* ads131m_reset - Resets the device using hardware or software.
* @priv: Device private data structure.
* @rstc: Optional reset control, or NULL for software reset.
*
* This function performs a hardware reset if supported (rstc provided),
* otherwise it issues a software RESET command via SPI.
*
* Note: The software reset path also validates the device's reset
* acknowledgment against the expected ID for the compatible string.
* The hardware reset path bypasses this ID check.
*
* Return: 0 on success, or a negative error code.
*/
static int ads131m_reset(struct ads131m_priv *priv, struct reset_control *rstc)
{
if (rstc)
return ads131m_hw_reset(priv, rstc);
return ads131m_sw_reset(priv);
}
static int ads131m_power_init(struct ads131m_priv *priv)
{
static const char * const supply_ids[] = { "avdd", "dvdd" };
struct device *dev = &priv->spi->dev;
int vref_uV;
int ret;
ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(supply_ids), supply_ids);
if (ret < 0)
return dev_err_probe(dev, ret, "failed to enable regulators\n");
/* Default to Internal 1.2V reference: 1200mV / 2^23 */
priv->scale_val = ADS131M_VREF_INTERNAL_mV;
priv->scale_val2 = BIT(ADS131M_CODE_BITS);
if (!priv->config->supports_extref)
return 0;
ret = devm_regulator_get_enable_read_voltage(dev, "refin");
if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to get refin supply\n");
if (ret == 0)
return dev_err_probe(dev, -EINVAL, "refin supply reports 0V\n");
if (ret == -ENODEV)
return 0;
vref_uV = ret;
/*
* External reference found: Scale(mV) = (vref_uV * 0.96) / 1000
* The denominator is 100 * 2^23 because of the 0.96 factor (96/100).
*/
priv->scale_val = div_s64((s64)vref_uV * ADS131M_EXTREF_SCALE_NUM, 1000);
priv->scale_val2 = ADS131M_EXTREF_SCALE_DEN * BIT(ADS131M_CODE_BITS);
priv->use_external_ref = true;
return 0;
}
/**
* ads131m_hw_init - Initialize the ADC hardware.
* @priv: Device private data structure.
* @rstc: Optional reset control, or NULL for software reset.
* @is_xtal: True if 'clock-names' is "xtal", false if "clkin".
*
* Return: 0 on success, or a negative error code.
*/
static int ads131m_hw_init(struct ads131m_priv *priv,
struct reset_control *rstc, bool is_xtal)
{
struct device *dev = &priv->spi->dev;
u16 mode_clear, mode_set;
int ret;
ret = ads131m_reset(priv, rstc);
if (ret < 0)
return ret;
/*
* Configure CLOCK register (0x03) based on DT properties.
* This register only needs configuration for 32-pin (M06/M08)
* variants, as the configurable bits (XTAL_DIS, EXTREF_EN)
* are reserved on 20-pin (M02/M03/M04) variants.
*/
if (priv->config->supports_xtal || priv->config->supports_extref) {
u16 clk_set = 0;
if (priv->config->supports_xtal && !is_xtal)
clk_set |= ADS131M_CLOCK_XTAL_DIS;
if (priv->config->supports_extref && priv->use_external_ref)
clk_set |= ADS131M_CLOCK_EXTREF_EN;
ret = ads131m_rmw_reg(priv, ADS131M_REG_CLOCK,
ADS131M_CLOCK_EXTREF_EN | ADS131M_CLOCK_XTAL_DIS,
clk_set);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to configure CLOCK register\n");
}
/*
* The RESET command sets all registers to default, which means:
* 1. The RESET bit (Bit 10) in MODE is set to '1'.
* 2. The CRC_TYPE bit (Bit 11) in MODE is '0' (CCITT).
* 3. The RX_CRC_EN bit (Bit 12) in MODE is '0' (Disabled).
*
* We must:
* 1. Clear the RESET bit.
* 2. Enable Input CRC (RX_CRC_EN).
* 3. Explicitly clear the ANSI CRC bit (for certainty).
*/
mode_clear = ADS131M_MODE_CRC_TYPE_ANSI | ADS131M_MODE_RESET_FLAG;
mode_set = ADS131M_MODE_RX_CRC_EN;
ret = ads131m_rmw_reg(priv, ADS131M_REG_MODE, mode_clear, mode_set);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to configure MODE register\n");
return 0;
}
/**
* ads131m_parse_clock - enable clock and detect "xtal" selection
* @priv: Device private data structure.
* @is_xtal: result flag (true if "xtal", false if default "clkin")
*
* Return: 0 on success, or a negative error code.
*/
static int ads131m_parse_clock(struct ads131m_priv *priv, bool *is_xtal)
{
struct device *dev = &priv->spi->dev;
struct clk *clk;
int ret;
clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR_OR_NULL(clk)) {
if (IS_ERR(clk))
ret = PTR_ERR(clk);
else
ret = -ENODEV;
return dev_err_probe(dev, ret, "clk get enabled failed\n");
}
ret = device_property_match_string(dev, "clock-names", "xtal");
if (ret > 0)
return dev_err_probe(dev, -EINVAL,
"'xtal' must be the only or first clock name");
if (ret < 0 && ret != -ENODATA)
return dev_err_probe(dev, ret,
"failed to read 'clock-names' property");
if (ret == 0 && !priv->config->supports_xtal)
return dev_err_probe(dev, -EINVAL,
"'xtal' clock not supported on this device");
*is_xtal = !ret;
return 0;
}
static int ads131m_probe(struct spi_device *spi)
{
const struct ads131m_configuration *config;
struct device *dev = &spi->dev;
struct reset_control *rstc;
struct iio_dev *indio_dev;
struct ads131m_priv *priv;
bool is_xtal;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
if (!indio_dev)
return -ENOMEM;
priv = iio_priv(indio_dev);
priv->indio_dev = indio_dev;
priv->spi = spi;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &ads131m_info;
config = spi_get_device_match_data(spi);
priv->config = config;
indio_dev->name = config->name;
indio_dev->channels = config->channels;
indio_dev->num_channels = config->num_channels;
rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(rstc))
return dev_err_probe(dev, PTR_ERR(rstc),
"Failed to get reset controller\n");
ret = devm_mutex_init(dev, &priv->lock);
if (ret < 0)
return ret;
ret = ads131m_prepare_message(priv);
if (ret < 0)
return ret;
ret = ads131m_power_init(priv);
if (ret < 0)
return ret;
/* Power must be applied and stable before the clock is enabled. */
ret = ads131m_parse_clock(priv, &is_xtal);
if (ret < 0)
return ret;
ret = ads131m_hw_init(priv, rstc, is_xtal);
if (ret < 0)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct of_device_id ads131m_of_match[] = {
{ .compatible = "ti,ads131m02", .data = &ads131m02_config },
{ .compatible = "ti,ads131m03", .data = &ads131m03_config },
{ .compatible = "ti,ads131m04", .data = &ads131m04_config },
{ .compatible = "ti,ads131m06", .data = &ads131m06_config },
{ .compatible = "ti,ads131m08", .data = &ads131m08_config },
{ }
};
MODULE_DEVICE_TABLE(of, ads131m_of_match);
static const struct spi_device_id ads131m_id[] = {
{ "ads131m02", (kernel_ulong_t)&ads131m02_config },
{ "ads131m03", (kernel_ulong_t)&ads131m03_config },
{ "ads131m04", (kernel_ulong_t)&ads131m04_config },
{ "ads131m06", (kernel_ulong_t)&ads131m06_config },
{ "ads131m08", (kernel_ulong_t)&ads131m08_config },
{ }
};
MODULE_DEVICE_TABLE(spi, ads131m_id);
static struct spi_driver ads131m_driver = {
.driver = {
.name = "ads131m02",
.of_match_table = ads131m_of_match,
},
.probe = ads131m_probe,
.id_table = ads131m_id,
};
module_spi_driver(ads131m_driver);
MODULE_AUTHOR("David Jander <david@protonic.nl>");
MODULE_DESCRIPTION("Texas Instruments ADS131M02 ADC driver");
MODULE_LICENSE("GPL");

View File

@@ -36,6 +36,18 @@ config ADA4250
To compile this driver as a module, choose M here: the
module will be called ada4250.
config ADL8113
tristate "Analog Devices ADL8113 Low Noise Amplifier"
depends on GPIOLIB
help
Say yes here to build support for Analog Devices ADL8113 Low Noise
Amplifier with integrated bypass switches. The device supports four
operation modes controlled by GPIO pins: internal amplifier,
internal bypass, and two external bypass modes.
To compile this driver as a module, choose M here: the
module will be called adl8113.
config HMC425
tristate "Analog Devices HMC425A and similar GPIO Gain Amplifiers"
depends on GPIOLIB

View File

@@ -6,4 +6,5 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AD8366) += ad8366.o
obj-$(CONFIG_ADA4250) += ada4250.o
obj-$(CONFIG_ADL8113) += adl8113.o
obj-$(CONFIG_HMC425) += hmc425a.o

View File

@@ -0,0 +1,269 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ADL8113 Low Noise Amplifier with integrated bypass switches
*
* Copyright 2025 Analog Devices Inc.
*/
#include <linux/array_size.h>
#include <linux/bitmap.h>
#include <linux/device/driver.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/iio/iio.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/types.h>
enum adl8113_signal_path {
ADL8113_INTERNAL_AMP,
ADL8113_INTERNAL_BYPASS,
ADL8113_EXTERNAL_A,
ADL8113_EXTERNAL_B,
};
struct adl8113_gain_config {
enum adl8113_signal_path path;
int gain_db;
};
struct adl8113_state {
struct gpio_descs *gpios;
struct adl8113_gain_config *gain_configs;
unsigned int num_gain_configs;
enum adl8113_signal_path current_path;
};
static const char * const adl8113_supply_names[] = {
"vdd1",
"vss2",
"vdd2",
};
static int adl8113_set_path(struct adl8113_state *st,
enum adl8113_signal_path path)
{
DECLARE_BITMAP(values, 2);
int ret;
/*
* Determine GPIO values based on signal path.
* Va: bit 0, Vb: bit 1.
*/
switch (path) {
case ADL8113_INTERNAL_AMP:
bitmap_write(values, 0x00, 0, 2);
break;
case ADL8113_INTERNAL_BYPASS:
bitmap_write(values, 0x03, 0, 2);
break;
case ADL8113_EXTERNAL_A:
bitmap_write(values, 0x02, 0, 2);
break;
case ADL8113_EXTERNAL_B:
bitmap_write(values, 0x01, 0, 2);
break;
default:
return -EINVAL;
}
ret = gpiod_set_array_value_cansleep(st->gpios->ndescs, st->gpios->desc,
st->gpios->info, values);
if (ret)
return ret;
st->current_path = path;
return 0;
}
static int adl8113_find_gain_config(struct adl8113_state *st, int gain_db)
{
unsigned int i;
for (i = 0; i < st->num_gain_configs; i++) {
if (st->gain_configs[i].gain_db == gain_db)
return i;
}
return -EINVAL;
}
static const struct iio_chan_spec adl8113_channels[] = {
{
.type = IIO_VOLTAGE,
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN),
},
};
static int adl8113_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct adl8113_state *st = iio_priv(indio_dev);
unsigned int i;
switch (mask) {
case IIO_CHAN_INFO_HARDWAREGAIN:
/* Find current gain configuration */
for (i = 0; i < st->num_gain_configs; i++) {
if (st->gain_configs[i].path == st->current_path) {
*val = st->gain_configs[i].gain_db;
*val2 = 0;
return IIO_VAL_INT_PLUS_MICRO_DB;
}
}
return -EINVAL;
default:
return -EINVAL;
}
}
static int adl8113_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct adl8113_state *st = iio_priv(indio_dev);
int config_idx;
switch (mask) {
case IIO_CHAN_INFO_HARDWAREGAIN:
if (val2 != 0)
return -EINVAL;
config_idx = adl8113_find_gain_config(st, val);
if (config_idx < 0)
return config_idx;
return adl8113_set_path(st, st->gain_configs[config_idx].path);
default:
return -EINVAL;
}
}
static const struct iio_info adl8113_info = {
.read_raw = adl8113_read_raw,
.write_raw = adl8113_write_raw,
};
static int adl8113_init_gain_configs(struct device *dev, struct adl8113_state *st)
{
int external_a_gain, external_b_gain;
unsigned int i;
/*
* Allocate for all 4 possible paths:
* - Internal amp and bypass (always present)
* - External bypass A and B (optional if configured)
*/
st->gain_configs = devm_kcalloc(dev, 4, sizeof(*st->gain_configs),
GFP_KERNEL);
if (!st->gain_configs)
return -ENOMEM;
/* Start filling the gain configurations with data */
i = 0;
/* Always include internal amplifier (14dB) */
st->gain_configs[i++] = (struct adl8113_gain_config) {
.path = ADL8113_INTERNAL_AMP,
.gain_db = 14,
};
/* Always include internal bypass (-2dB insertion loss) */
st->gain_configs[i++] = (struct adl8113_gain_config) {
.path = ADL8113_INTERNAL_BYPASS,
.gain_db = -2,
};
/* Add external bypass A if configured */
if (!device_property_read_u32(dev, "adi,external-bypass-a-gain-db",
&external_a_gain)) {
st->gain_configs[i++] = (struct adl8113_gain_config) {
.path = ADL8113_EXTERNAL_A,
.gain_db = external_a_gain,
};
}
/* Add external bypass B if configured */
if (!device_property_read_u32(dev, "adi,external-bypass-b-gain-db",
&external_b_gain)) {
st->gain_configs[i++] = (struct adl8113_gain_config) {
.path = ADL8113_EXTERNAL_B,
.gain_db = external_b_gain,
};
}
st->num_gain_configs = i;
return 0;
}
static int adl8113_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct adl8113_state *st;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
st->gpios = devm_gpiod_get_array(dev, "ctrl", GPIOD_OUT_LOW);
if (IS_ERR(st->gpios))
return dev_err_probe(dev, PTR_ERR(st->gpios),
"failed to get control GPIOs\n");
if (st->gpios->ndescs != 2)
return dev_err_probe(dev, -EINVAL,
"expected 2 control GPIOs, got %u\n",
st->gpios->ndescs);
ret = devm_regulator_bulk_get_enable(dev,
ARRAY_SIZE(adl8113_supply_names),
adl8113_supply_names);
if (ret)
return dev_err_probe(dev, ret,
"failed to get and enable supplies\n");
/* Initialize gain configurations from devicetree */
ret = adl8113_init_gain_configs(dev, st);
if (ret)
return ret;
/* Initialize to internal amplifier path (14dB) */
ret = adl8113_set_path(st, ADL8113_INTERNAL_AMP);
if (ret)
return ret;
indio_dev->info = &adl8113_info;
indio_dev->name = "adl8113";
indio_dev->channels = adl8113_channels;
indio_dev->num_channels = ARRAY_SIZE(adl8113_channels);
return devm_iio_device_register(dev, indio_dev);
}
static const struct of_device_id adl8113_of_match[] = {
{ .compatible = "adi,adl8113" },
{ }
};
MODULE_DEVICE_TABLE(of, adl8113_of_match);
static struct platform_driver adl8113_driver = {
.driver = {
.name = "adl8113",
.of_match_table = adl8113_of_match,
},
.probe = adl8113_probe,
};
module_platform_driver(adl8113_driver);
MODULE_AUTHOR("Antoniu Miclaus <antoniu.miclaus@analog.com>");
MODULE_DESCRIPTION("Analog Devices ADL8113 Low Noise Amplifier");
MODULE_LICENSE("GPL");

View File

@@ -6,6 +6,7 @@
#include <linux/atomic.h>
#include <linux/cleanup.h>
#include <linux/lockdep.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -135,9 +136,8 @@ static void iio_dma_buffer_cleanup_worker(struct work_struct *work)
struct iio_dma_buffer_block *block, *_block;
LIST_HEAD(block_list);
spin_lock_irq(&iio_dma_buffer_dead_blocks_lock);
list_splice_tail_init(&iio_dma_buffer_dead_blocks, &block_list);
spin_unlock_irq(&iio_dma_buffer_dead_blocks_lock);
scoped_guard(spinlock_irq, &iio_dma_buffer_dead_blocks_lock)
list_splice_tail_init(&iio_dma_buffer_dead_blocks, &block_list);
list_for_each_entry_safe(block, _block, &block_list, head)
iio_buffer_block_release(&block->kref);
@@ -147,13 +147,11 @@ static DECLARE_WORK(iio_dma_buffer_cleanup_work, iio_dma_buffer_cleanup_worker);
static void iio_buffer_block_release_atomic(struct kref *kref)
{
struct iio_dma_buffer_block *block;
unsigned long flags;
block = container_of(kref, struct iio_dma_buffer_block, kref);
spin_lock_irqsave(&iio_dma_buffer_dead_blocks_lock, flags);
list_add_tail(&block->head, &iio_dma_buffer_dead_blocks);
spin_unlock_irqrestore(&iio_dma_buffer_dead_blocks_lock, flags);
scoped_guard(spinlock_irqsave, &iio_dma_buffer_dead_blocks_lock)
list_add_tail(&block->head, &iio_dma_buffer_dead_blocks);
schedule_work(&iio_dma_buffer_cleanup_work);
}
@@ -171,22 +169,20 @@ static struct iio_dma_buffer_queue *iio_buffer_to_queue(struct iio_buffer *buf)
return container_of(buf, struct iio_dma_buffer_queue, buffer);
}
static struct iio_dma_buffer_block *iio_dma_buffer_alloc_block(
struct iio_dma_buffer_queue *queue, size_t size, bool fileio)
static struct iio_dma_buffer_block *
iio_dma_buffer_alloc_block(struct iio_dma_buffer_queue *queue, size_t size,
bool fileio)
{
struct iio_dma_buffer_block *block;
block = kzalloc(sizeof(*block), GFP_KERNEL);
struct iio_dma_buffer_block *block __free(kfree) =
kzalloc(sizeof(*block), GFP_KERNEL);
if (!block)
return NULL;
if (fileio) {
block->vaddr = dma_alloc_coherent(queue->dev, PAGE_ALIGN(size),
&block->phys_addr, GFP_KERNEL);
if (!block->vaddr) {
kfree(block);
if (!block->vaddr)
return NULL;
}
}
block->fileio = fileio;
@@ -201,7 +197,7 @@ static struct iio_dma_buffer_block *iio_dma_buffer_alloc_block(
if (!fileio)
atomic_inc(&queue->num_dmabufs);
return block;
return_ptr(block);
}
static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
@@ -232,14 +228,12 @@ static void iio_dma_buffer_queue_wake(struct iio_dma_buffer_queue *queue)
void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
{
struct iio_dma_buffer_queue *queue = block->queue;
unsigned long flags;
bool cookie;
cookie = dma_fence_begin_signalling();
spin_lock_irqsave(&queue->list_lock, flags);
_iio_dma_buffer_block_done(block);
spin_unlock_irqrestore(&queue->list_lock, flags);
scoped_guard(spinlock_irqsave, &queue->list_lock)
_iio_dma_buffer_block_done(block);
if (!block->fileio)
iio_buffer_signal_dmabuf_done(block->fence, 0);
@@ -261,25 +255,25 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_block_done, "IIO_DMA_BUFFER");
* hand the blocks back to the queue.
*/
void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue,
struct list_head *list)
struct list_head *list)
{
struct iio_dma_buffer_block *block, *_block;
unsigned long flags;
bool cookie;
cookie = dma_fence_begin_signalling();
spin_lock_irqsave(&queue->list_lock, flags);
list_for_each_entry_safe(block, _block, list, head) {
list_del(&block->head);
block->bytes_used = 0;
_iio_dma_buffer_block_done(block);
scoped_guard(spinlock_irqsave, &queue->list_lock) {
list_for_each_entry_safe(block, _block, list, head) {
list_del(&block->head);
block->bytes_used = 0;
_iio_dma_buffer_block_done(block);
if (!block->fileio)
iio_buffer_signal_dmabuf_done(block->fence, -EINTR);
iio_buffer_block_put_atomic(block);
if (!block->fileio)
iio_buffer_signal_dmabuf_done(block->fence,
-EINTR);
iio_buffer_block_put_atomic(block);
}
}
spin_unlock_irqrestore(&queue->list_lock, flags);
if (queue->fileio.enabled)
queue->fileio.enabled = false;
@@ -328,7 +322,6 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
struct iio_dma_buffer_block *block;
bool try_reuse = false;
size_t size;
int ret = 0;
int i;
/*
@@ -339,13 +332,13 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
size = DIV_ROUND_UP(queue->buffer.bytes_per_datum *
queue->buffer.length, 2);
mutex_lock(&queue->lock);
guard(mutex)(&queue->lock);
queue->fileio.enabled = iio_dma_buffer_can_use_fileio(queue);
/* If DMABUFs were created, disable fileio interface */
if (!queue->fileio.enabled)
goto out_unlock;
return 0;
/* Allocations are page aligned */
if (PAGE_ALIGN(queue->fileio.block_size) == PAGE_ALIGN(size))
@@ -354,22 +347,22 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
queue->fileio.block_size = size;
queue->fileio.active_block = NULL;
spin_lock_irq(&queue->list_lock);
for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
block = queue->fileio.blocks[i];
scoped_guard(spinlock_irq, &queue->list_lock) {
for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
block = queue->fileio.blocks[i];
/* If we can't re-use it free it */
if (block && (!iio_dma_block_reusable(block) || !try_reuse))
block->state = IIO_BLOCK_STATE_DEAD;
/* If we can't re-use it free it */
if (block && (!iio_dma_block_reusable(block) || !try_reuse))
block->state = IIO_BLOCK_STATE_DEAD;
}
/*
* At this point all blocks are either owned by the core or
* marked as dead. This means we can reset the lists without
* having to fear corruption.
*/
}
/*
* At this point all blocks are either owned by the core or marked as
* dead. This means we can reset the lists without having to fear
* corrution.
*/
spin_unlock_irq(&queue->list_lock);
INIT_LIST_HEAD(&queue->incoming);
for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
@@ -388,10 +381,9 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
if (!block) {
block = iio_dma_buffer_alloc_block(queue, size, true);
if (!block) {
ret = -ENOMEM;
goto out_unlock;
}
if (!block)
return -ENOMEM;
queue->fileio.blocks[i] = block;
}
@@ -415,10 +407,7 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
}
}
out_unlock:
mutex_unlock(&queue->lock);
return ret;
return 0;
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_request_update, "IIO_DMA_BUFFER");
@@ -426,13 +415,13 @@ static void iio_dma_buffer_fileio_free(struct iio_dma_buffer_queue *queue)
{
unsigned int i;
spin_lock_irq(&queue->list_lock);
for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
if (!queue->fileio.blocks[i])
continue;
queue->fileio.blocks[i]->state = IIO_BLOCK_STATE_DEAD;
scoped_guard(spinlock_irq, &queue->list_lock) {
for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
if (!queue->fileio.blocks[i])
continue;
queue->fileio.blocks[i]->state = IIO_BLOCK_STATE_DEAD;
}
}
spin_unlock_irq(&queue->list_lock);
INIT_LIST_HEAD(&queue->incoming);
@@ -446,7 +435,7 @@ static void iio_dma_buffer_fileio_free(struct iio_dma_buffer_queue *queue)
}
static void iio_dma_buffer_submit_block(struct iio_dma_buffer_queue *queue,
struct iio_dma_buffer_block *block)
struct iio_dma_buffer_block *block)
{
int ret;
@@ -490,19 +479,17 @@ static void iio_dma_buffer_submit_block(struct iio_dma_buffer_queue *queue,
*
* This will allocate the DMA buffers and start the DMA transfers.
*/
int iio_dma_buffer_enable(struct iio_buffer *buffer,
struct iio_dev *indio_dev)
int iio_dma_buffer_enable(struct iio_buffer *buffer, struct iio_dev *indio_dev)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
struct iio_dma_buffer_block *block, *_block;
mutex_lock(&queue->lock);
guard(mutex)(&queue->lock);
queue->active = true;
list_for_each_entry_safe(block, _block, &queue->incoming, head) {
list_del(&block->head);
iio_dma_buffer_submit_block(queue, block);
}
mutex_unlock(&queue->lock);
return 0;
}
@@ -516,24 +503,22 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_enable, "IIO_DMA_BUFFER");
* Needs to be called when the device that the buffer is attached to stops
* sampling. Typically should be the iio_buffer_access_ops disable callback.
*/
int iio_dma_buffer_disable(struct iio_buffer *buffer,
struct iio_dev *indio_dev)
int iio_dma_buffer_disable(struct iio_buffer *buffer, struct iio_dev *indio_dev)
{
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
mutex_lock(&queue->lock);
guard(mutex)(&queue->lock);
queue->active = false;
if (queue->ops && queue->ops->abort)
queue->ops->abort(queue);
mutex_unlock(&queue->lock);
return 0;
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_disable, "IIO_DMA_BUFFER");
static void iio_dma_buffer_enqueue(struct iio_dma_buffer_queue *queue,
struct iio_dma_buffer_block *block)
struct iio_dma_buffer_block *block)
{
if (block->state == IIO_BLOCK_STATE_DEAD) {
iio_buffer_block_put(block);
@@ -545,25 +530,22 @@ static void iio_dma_buffer_enqueue(struct iio_dma_buffer_queue *queue,
}
}
static struct iio_dma_buffer_block *iio_dma_buffer_dequeue(
struct iio_dma_buffer_queue *queue)
static struct iio_dma_buffer_block *
iio_dma_buffer_dequeue(struct iio_dma_buffer_queue *queue)
{
struct iio_dma_buffer_block *block;
unsigned int idx;
spin_lock_irq(&queue->list_lock);
guard(spinlock_irq)(&queue->list_lock);
idx = queue->fileio.next_dequeue;
block = queue->fileio.blocks[idx];
if (block->state == IIO_BLOCK_STATE_DONE) {
idx = (idx + 1) % ARRAY_SIZE(queue->fileio.blocks);
queue->fileio.next_dequeue = idx;
} else {
block = NULL;
}
if (block->state != IIO_BLOCK_STATE_DONE)
return NULL;
spin_unlock_irq(&queue->list_lock);
idx = (idx + 1) % ARRAY_SIZE(queue->fileio.blocks);
queue->fileio.next_dequeue = idx;
return block;
}
@@ -579,14 +561,13 @@ static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n,
if (n < buffer->bytes_per_datum)
return -EINVAL;
mutex_lock(&queue->lock);
guard(mutex)(&queue->lock);
if (!queue->fileio.active_block) {
block = iio_dma_buffer_dequeue(queue);
if (block == NULL) {
ret = 0;
goto out_unlock;
}
if (!block)
return 0;
queue->fileio.pos = 0;
queue->fileio.active_block = block;
} else {
@@ -602,10 +583,8 @@ static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n,
ret = copy_from_user(addr, user_buffer, n);
else
ret = copy_to_user(user_buffer, addr, n);
if (ret) {
ret = -EFAULT;
goto out_unlock;
}
if (ret)
return -EFAULT;
queue->fileio.pos += n;
@@ -614,12 +593,7 @@ static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n,
iio_dma_buffer_enqueue(queue, block);
}
ret = n;
out_unlock:
mutex_unlock(&queue->lock);
return ret;
return n;
}
/**
@@ -677,23 +651,19 @@ size_t iio_dma_buffer_usage(struct iio_buffer *buf)
* but won't increase since all blocks are in use.
*/
mutex_lock(&queue->lock);
guard(mutex)(&queue->lock);
if (queue->fileio.active_block)
data_available += queue->fileio.active_block->size;
spin_lock_irq(&queue->list_lock);
guard(spinlock_irq)(&queue->list_lock);
for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) {
block = queue->fileio.blocks[i];
if (block != queue->fileio.active_block
&& block->state == IIO_BLOCK_STATE_DONE)
if (block != queue->fileio.active_block && block->state == IIO_BLOCK_STATE_DONE)
data_available += block->size;
}
spin_unlock_irq(&queue->list_lock);
mutex_unlock(&queue->lock);
return data_available;
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_usage, "IIO_DMA_BUFFER");
@@ -764,7 +734,7 @@ int iio_dma_buffer_enqueue_dmabuf(struct iio_buffer *buffer,
bool cookie;
int ret;
WARN_ON(!mutex_is_locked(&queue->lock));
lockdep_assert_held(&queue->lock);
cookie = dma_fence_begin_signalling();
@@ -854,8 +824,8 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_set_length, "IIO_DMA_BUFFER");
* should refer to the device that will perform the DMA to ensure that
* allocations are done from a memory region that can be accessed by the device.
*/
int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue,
struct device *dev, const struct iio_dma_buffer_ops *ops)
void iio_dma_buffer_init(struct iio_dma_buffer_queue *queue, struct device *dev,
const struct iio_dma_buffer_ops *ops)
{
iio_buffer_init(&queue->buffer);
queue->buffer.length = PAGE_SIZE;
@@ -867,8 +837,6 @@ int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue,
mutex_init(&queue->lock);
spin_lock_init(&queue->list_lock);
return 0;
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_init, "IIO_DMA_BUFFER");
@@ -881,12 +849,10 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_init, "IIO_DMA_BUFFER");
*/
void iio_dma_buffer_exit(struct iio_dma_buffer_queue *queue)
{
mutex_lock(&queue->lock);
guard(mutex)(&queue->lock);
iio_dma_buffer_fileio_free(queue);
queue->ops = NULL;
mutex_unlock(&queue->lock);
}
EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_exit, "IIO_DMA_BUFFER");

View File

@@ -6,6 +6,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/cleanup.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
@@ -39,27 +40,24 @@ struct dmaengine_buffer {
size_t max_size;
};
static struct dmaengine_buffer *iio_buffer_to_dmaengine_buffer(
struct iio_buffer *buffer)
static struct dmaengine_buffer *iio_buffer_to_dmaengine_buffer(struct iio_buffer *buffer)
{
return container_of(buffer, struct dmaengine_buffer, queue.buffer);
}
static void iio_dmaengine_buffer_block_done(void *data,
const struct dmaengine_result *result)
const struct dmaengine_result *result)
{
struct iio_dma_buffer_block *block = data;
unsigned long flags;
spin_lock_irqsave(&block->queue->list_lock, flags);
list_del(&block->head);
spin_unlock_irqrestore(&block->queue->list_lock, flags);
scoped_guard(spinlock_irqsave, &block->queue->list_lock)
list_del(&block->head);
block->bytes_used -= result->residue;
iio_dma_buffer_block_done(block);
}
static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
struct iio_dma_buffer_block *block)
struct iio_dma_buffer_block *block)
{
struct dmaengine_buffer *dmaengine_buffer =
iio_buffer_to_dmaengine_buffer(&queue->buffer);
@@ -131,9 +129,8 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
if (dma_submit_error(cookie))
return dma_submit_error(cookie);
spin_lock_irq(&dmaengine_buffer->queue.list_lock);
list_add_tail(&block->head, &dmaengine_buffer->active);
spin_unlock_irq(&dmaengine_buffer->queue.list_lock);
scoped_guard(spinlock_irq, &dmaengine_buffer->queue.list_lock)
list_add_tail(&block->head, &dmaengine_buffer->active);
dma_async_issue_pending(dmaengine_buffer->chan);
@@ -189,7 +186,7 @@ static const struct iio_dma_buffer_ops iio_dmaengine_default_ops = {
};
static ssize_t iio_dmaengine_buffer_get_length_align(struct device *dev,
struct device_attribute *attr, char *buf)
struct device_attribute *attr, char *buf)
{
struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
struct dmaengine_buffer *dmaengine_buffer =
@@ -248,7 +245,7 @@ static struct iio_buffer *iio_dmaengine_buffer_alloc(struct dma_chan *chan)
dmaengine_buffer->max_size = dma_get_max_seg_size(chan->device->dev);
iio_dma_buffer_init(&dmaengine_buffer->queue, chan->device->dev,
&iio_dmaengine_default_ops);
&iio_dmaengine_default_ops);
dmaengine_buffer->queue.buffer.attrs = iio_dmaengine_buffer_attrs;
dmaengine_buffer->queue.buffer.access = &iio_dmaengine_buffer_ops;

View File

@@ -316,12 +316,9 @@ static int ens160_setup_trigger(struct iio_dev *indio_dev, int irq)
indio_dev->trig = iio_trigger_get(trig);
ret = devm_request_threaded_irq(dev, irq,
iio_trigger_generic_data_rdy_poll,
NULL,
IRQF_ONESHOT,
indio_dev->name,
indio_dev->trig);
ret = devm_request_irq(dev, irq, iio_trigger_generic_data_rdy_poll,
IRQF_NO_THREAD, indio_dev->name,
indio_dev->trig);
if (ret)
return dev_err_probe(dev, ret, "failed to request irq\n");

View File

@@ -59,6 +59,8 @@ enum scd4x_channel_idx {
SCD4X_CO2,
SCD4X_TEMP,
SCD4X_HR,
/* kernel timestamp, at the end of buffer */
SCD4X_TS,
};
struct scd4x_state {
@@ -615,6 +617,7 @@ static const struct iio_chan_spec scd4x_channels[] = {
.endianness = IIO_CPU,
},
},
IIO_CHAN_SOFT_TIMESTAMP(SCD4X_TS),
};
static int scd4x_suspend(struct device *dev)

View File

@@ -188,11 +188,8 @@ int cros_ec_sensors_push_data(struct iio_dev *indio_dev,
/*
* Ignore samples if the buffer is not set: it is needed if the ODR is
* set but the buffer is not enabled yet.
*
* Note: iio_device_claim_buffer_mode() returns -EBUSY if the buffer
* is not enabled.
*/
if (iio_device_claim_buffer_mode(indio_dev) < 0)
if (!iio_device_try_claim_buffer_mode(indio_dev))
return 0;
out = (s16 *)st->samples;
@@ -444,14 +441,14 @@ static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev,
ret = kstrtobool(buf, &calibrate);
if (ret < 0)
return ret;
if (!calibrate)
return -EINVAL;
mutex_lock(&st->cmd_lock);
st->param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB;
st->param.perform_calib.enable = calibrate;
ret = cros_ec_motion_send_host_cmd(st, 0);
if (ret != 0) {
dev_warn(&indio_dev->dev, "Unable to calibrate sensor\n");
dev_warn(&indio_dev->dev, "Unable to calibrate sensor: %d\n",
ret);
} else {
/* Save values */
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)

View File

@@ -482,6 +482,19 @@ config MAX517
This driver can also be built as a module. If so, the module
will be called max517.
config MAX22007
tristate "Analog Devices MAX22007 DAC Driver"
depends on SPI
select REGMAP_SPI
select CRC8
help
Say Y here if you want to build a driver for Analog Devices MAX22007.
MAX22007 is a quad-channel, 12-bit, voltage-output digital to
analog converter (DAC) with SPI interface.
If compiled as a module, it will be called max22007.
config MAX5522
tristate "Maxim MAX5522 DAC driver"
depends on SPI_MASTER
@@ -524,6 +537,26 @@ config MCP4728
To compile this driver as a module, choose M here: the module
will be called mcp4728.
config MCP47FEB02
tristate "MCP47F(E/V)B01/02/04/08/11/12/14/18/21/22/24/28 DAC driver"
depends on I2C
help
Say yes here if you want to build the driver for the Microchip:
- 8-bit DAC:
MCP47FEB01, MCP47FEB02, MCP47FEB04, MCP47FEB08,
MCP47FVB01, MCP47FVB02, MCP47FVB04, MCP47FVB08
- 10-bit DAC:
MCP47FEB11, MCP47FEB12, MCP47FEB14, MCP47FEB18,
MCP47FVB11, MCP47FVB12, MCP47FVB14, MCP47FVB18
- 12-bit DAC:
MCP47FEB21, MCP47FEB22, MCP47FEB24, MCP47FEB28,
MCP47FVB21, MCP47FVB22, MCP47FVB24, MCP47FVB28
having 1 to 8 channels, 8/10/12-bit digital-to-analog converter
(DAC) with I2C interface.
To compile this driver as a module, choose M here: the module
will be called mcp47feb02.
config MCP4821
tristate "MCP4801/02/11/12/21/22 DAC driver"
depends on SPI

View File

@@ -48,10 +48,12 @@ obj-$(CONFIG_LTC2664) += ltc2664.o
obj-$(CONFIG_LTC2688) += ltc2688.o
obj-$(CONFIG_M62332) += m62332.o
obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MAX22007) += max22007.o
obj-$(CONFIG_MAX5522) += max5522.o
obj-$(CONFIG_MAX5821) += max5821.o
obj-$(CONFIG_MCP4725) += mcp4725.o
obj-$(CONFIG_MCP4728) += mcp4728.o
obj-$(CONFIG_MCP47FEB02) += mcp47feb02.o
obj-$(CONFIG_MCP4821) += mcp4821.o
obj-$(CONFIG_MCP4922) += mcp4922.o
obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o

View File

@@ -885,34 +885,35 @@ static const struct regmap_config axi_dac_regmap_config = {
static int axi_dac_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct axi_dac_state *st;
void __iomem *base;
unsigned int ver;
struct clk *clk;
int ret;
st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
if (!st)
return -ENOMEM;
st->info = device_get_match_data(&pdev->dev);
st->info = device_get_match_data(dev);
if (!st->info)
return -ENODEV;
clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk");
clk = devm_clk_get_enabled(dev, "s_axi_aclk");
if (IS_ERR(clk)) {
/* Backward compat., old fdt versions without clock-names. */
clk = devm_clk_get_enabled(&pdev->dev, NULL);
clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
return dev_err_probe(&pdev->dev, PTR_ERR(clk),
"failed to get clock\n");
return dev_err_probe(dev, PTR_ERR(clk),
"failed to get clock\n");
}
if (st->info->has_dac_clk) {
struct clk *dac_clk;
dac_clk = devm_clk_get_enabled(&pdev->dev, "dac_clk");
dac_clk = devm_clk_get_enabled(dev, "dac_clk");
if (IS_ERR(dac_clk))
return dev_err_probe(&pdev->dev, PTR_ERR(dac_clk),
return dev_err_probe(dev, PTR_ERR(dac_clk),
"failed to get dac_clk clock\n");
/* We only care about the streaming mode rate */
@@ -923,11 +924,10 @@ static int axi_dac_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
st->dev = &pdev->dev;
st->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&axi_dac_regmap_config);
st->dev = dev;
st->regmap = devm_regmap_init_mmio(dev, base, &axi_dac_regmap_config);
if (IS_ERR(st->regmap))
return dev_err_probe(&pdev->dev, PTR_ERR(st->regmap),
return dev_err_probe(dev, PTR_ERR(st->regmap),
"failed to init register map\n");
/*
@@ -942,18 +942,15 @@ static int axi_dac_probe(struct platform_device *pdev)
if (ret)
return ret;
if (ADI_AXI_PCORE_VER_MAJOR(ver) !=
ADI_AXI_PCORE_VER_MAJOR(st->info->version)) {
dev_err(&pdev->dev,
"Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
ADI_AXI_PCORE_VER_MAJOR(st->info->version),
ADI_AXI_PCORE_VER_MINOR(st->info->version),
ADI_AXI_PCORE_VER_PATCH(st->info->version),
ADI_AXI_PCORE_VER_MAJOR(ver),
ADI_AXI_PCORE_VER_MINOR(ver),
ADI_AXI_PCORE_VER_PATCH(ver));
return -ENODEV;
}
if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(st->info->version))
return dev_err_probe(dev, -ENODEV,
"Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
ADI_AXI_PCORE_VER_MAJOR(st->info->version),
ADI_AXI_PCORE_VER_MINOR(st->info->version),
ADI_AXI_PCORE_VER_PATCH(st->info->version),
ADI_AXI_PCORE_VER_MAJOR(ver),
ADI_AXI_PCORE_VER_MINOR(ver),
ADI_AXI_PCORE_VER_PATCH(ver));
/* Let's get the core read only configuration */
ret = regmap_read(st->regmap, AXI_DAC_CONFIG_REG, &st->reg_config);
@@ -975,34 +972,33 @@ static int axi_dac_probe(struct platform_device *pdev)
mutex_init(&st->lock);
ret = devm_iio_backend_register(&pdev->dev, st->info->backend_info, st);
ret = devm_iio_backend_register(dev, st->info->backend_info, st);
if (ret)
return dev_err_probe(&pdev->dev, ret,
return dev_err_probe(dev, ret,
"failed to register iio backend\n");
device_for_each_child_node_scoped(&pdev->dev, child) {
device_for_each_child_node_scoped(dev, child) {
int val;
if (!st->info->has_child_nodes)
return dev_err_probe(&pdev->dev, -EINVAL,
return dev_err_probe(dev, -EINVAL,
"invalid fdt axi-dac compatible.");
/* Processing only reg 0 node */
ret = fwnode_property_read_u32(child, "reg", &val);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"invalid reg property.");
return dev_err_probe(dev, ret, "invalid reg property.");
if (val != 0)
return dev_err_probe(&pdev->dev, -EINVAL,
"invalid node address.");
return dev_err_probe(dev, -EINVAL,
"invalid node address.");
ret = axi_dac_create_platform_device(st, child);
if (ret)
return dev_err_probe(&pdev->dev, -EINVAL,
"cannot create device.");
return dev_err_probe(dev, -EINVAL,
"cannot create device.");
}
dev_info(&pdev->dev, "AXI DAC IP core (%d.%.2d.%c) probed\n",
dev_info(dev, "AXI DAC IP core (%d.%.2d.%c) probed\n",
ADI_AXI_PCORE_VER_MAJOR(ver),
ADI_AXI_PCORE_VER_MINOR(ver),
ADI_AXI_PCORE_VER_PATCH(ver));

View File

@@ -14,7 +14,6 @@
#include <linux/iio/iio.h>
#include <linux/iio/driver.h>
#include <linux/iio/machine.h>
#include <linux/iio/consumer.h>
#define DS4422_MAX_DAC_CHANNELS 2
#define DS4424_MAX_DAC_CHANNELS 4

491
drivers/iio/dac/max22007.c Normal file
View File

@@ -0,0 +1,491 @@
// SPDX-License-Identifier: GPL-2.0
/*
* max22007.c - MAX22007 DAC driver
*
* Driver for Analog Devices MAX22007 Digital to Analog Converter.
*
* Copyright (c) 2026 Analog Devices Inc.
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/device/devres.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/gpio/consumer.h>
#include <linux/iio/iio.h>
#include <linux/kstrtox.h>
#include <linux/minmax.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#include <dt-bindings/iio/addac/adi,ad74413r.h>
struct device;
#define MAX22007_NUM_CHANNELS 4
#define MAX22007_REV_ID_REG 0x00
#define MAX22007_STAT_INTR_REG 0x01
#define MAX22007_INTERRUPT_EN_REG 0x02
#define MAX22007_CONFIG_REG 0x03
#define MAX22007_CONTROL_REG 0x04
#define MAX22007_CHANNEL_MODE_REG 0x05
#define MAX22007_SOFT_RESET_REG 0x06
#define MAX22007_DAC_CHANNEL_REG(ch) (0x07 + (ch))
#define MAX22007_GPIO_CTRL_REG 0x0B
#define MAX22007_GPIO_DATA_REG 0x0C
#define MAX22007_GPI_EDGE_INT_CTRL_REG 0x0D
#define MAX22007_GPI_INT_STATUS_REG 0x0E
/* Channel mask definitions */
#define MAX22007_CH_MODE_CH_MASK(ch) BIT(12 + (ch))
#define MAX22007_CH_PWRON_CH_MASK(ch) BIT(8 + (ch))
#define MAX22007_DAC_LATCH_MODE_MASK(ch) BIT(12 + (ch))
#define MAX22007_LDAC_UPDATE_MASK(ch) BIT(12 + (ch))
#define MAX22007_SW_RST_MASK BIT(8)
#define MAX22007_SW_CLR_MASK BIT(12)
#define MAX22007_SOFT_RESET_BITS_MASK (MAX22007_SW_RST_MASK | \
MAX22007_SW_CLR_MASK)
#define MAX22007_DAC_DATA_MASK GENMASK(15, 4)
#define MAX22007_DAC_MAX_RAW GENMASK(11, 0)
#define MAX22007_CRC8_POLYNOMIAL 0x8C
#define MAX22007_CRC_EN_MASK BIT(0)
#define MAX22007_RW_MASK BIT(0)
#define MAX22007_CRC_OVERHEAD 1
#define MAX22007_NUM_SUPPLIES 3
#define MAX22007_REF_MV 2500
/* Field value preparation macros with masking */
#define MAX22007_CH_PWR_VAL(ch, val) (((val) & 0x1) << (8 + (ch)))
#define MAX22007_CH_MODE_VAL(ch, val) (((val) & 0x1) << (12 + (ch)))
#define MAX22007_DAC_LATCH_MODE_VAL(ch, val) (((val) & 0x1) << (12 + (ch)))
static u8 max22007_crc8_table[CRC8_TABLE_SIZE];
static const char * const max22007_supply_names[MAX22007_NUM_SUPPLIES] = {
"vdd",
"hvdd",
"hvss",
};
struct max22007_state {
struct spi_device *spi;
struct regmap *regmap;
struct iio_chan_spec *iio_chans;
u8 tx_buf[4] __aligned(IIO_DMA_MINALIGN);
u8 rx_buf[4];
};
static int max22007_spi_read(void *context, const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct max22007_state *st = context;
u8 calculated_crc, received_crc;
u8 rx_buf[4];
u8 reg_byte;
int ret;
if (reg_size != 1)
return -EINVAL;
if (val_size == 0 || val_size > 3)
return -EINVAL;
memcpy(&reg_byte, reg, 1);
ret = spi_write_then_read(st->spi, &reg_byte, 1, rx_buf,
val_size + MAX22007_CRC_OVERHEAD);
if (ret) {
dev_err(&st->spi->dev, "SPI transfer failed: %d\n", ret);
return ret;
}
calculated_crc = crc8(max22007_crc8_table, &reg_byte, 1, 0x00);
calculated_crc = crc8(max22007_crc8_table, rx_buf, 2, calculated_crc);
received_crc = rx_buf[val_size];
if (calculated_crc != received_crc) {
dev_err(&st->spi->dev, "CRC mismatch on read register %02x\n", reg_byte);
return -EIO;
}
memcpy(val, rx_buf, val_size);
return 0;
}
static int max22007_spi_write(void *context, const void *data, size_t count)
{
struct max22007_state *st = context;
struct spi_transfer xfer = {
.tx_buf = st->tx_buf,
.rx_buf = st->rx_buf,
};
if (count + MAX22007_CRC_OVERHEAD > sizeof(st->tx_buf))
return -EINVAL;
memset(st->tx_buf, 0, sizeof(st->tx_buf));
xfer.len = count + MAX22007_CRC_OVERHEAD;
memcpy(st->tx_buf, data, count);
st->tx_buf[count] = crc8(max22007_crc8_table, st->tx_buf,
sizeof(st->tx_buf) - 1, 0x00);
return spi_sync_transfer(st->spi, &xfer, 1);
}
static bool max22007_reg_readable(struct device *dev, unsigned int reg)
{
switch (reg) {
case MAX22007_REV_ID_REG:
case MAX22007_STAT_INTR_REG:
case MAX22007_CONFIG_REG:
case MAX22007_CONTROL_REG:
case MAX22007_CHANNEL_MODE_REG:
case MAX22007_SOFT_RESET_REG:
case MAX22007_GPIO_CTRL_REG:
case MAX22007_GPIO_DATA_REG:
case MAX22007_GPI_EDGE_INT_CTRL_REG:
case MAX22007_GPI_INT_STATUS_REG:
return true;
case MAX22007_DAC_CHANNEL_REG(0) ... MAX22007_DAC_CHANNEL_REG(MAX22007_NUM_CHANNELS - 1):
return true;
default:
return false;
}
}
static bool max22007_reg_writable(struct device *dev, unsigned int reg)
{
switch (reg) {
case MAX22007_CONFIG_REG:
case MAX22007_CONTROL_REG:
case MAX22007_CHANNEL_MODE_REG:
case MAX22007_SOFT_RESET_REG:
case MAX22007_GPIO_CTRL_REG:
case MAX22007_GPIO_DATA_REG:
case MAX22007_GPI_EDGE_INT_CTRL_REG:
return true;
case MAX22007_DAC_CHANNEL_REG(0) ... MAX22007_DAC_CHANNEL_REG(MAX22007_NUM_CHANNELS - 1):
return true;
default:
return false;
}
}
static const struct regmap_bus max22007_regmap_bus = {
.read = max22007_spi_read,
.write = max22007_spi_write,
.read_flag_mask = MAX22007_RW_MASK,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
};
static const struct regmap_config max22007_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.reg_shift = -1,
.readable_reg = max22007_reg_readable,
.writeable_reg = max22007_reg_writable,
.max_register = 0x0E,
};
static int max22007_write_channel_data(struct max22007_state *st,
unsigned int channel, int data)
{
unsigned int reg_val;
if (data < 0 || data > MAX22007_DAC_MAX_RAW)
return -EINVAL;
reg_val = FIELD_PREP(MAX22007_DAC_DATA_MASK, data);
return regmap_write(st->regmap, MAX22007_DAC_CHANNEL_REG(channel), reg_val);
}
static int max22007_read_channel_data(struct max22007_state *st,
unsigned int channel, int *data)
{
unsigned int reg_val;
int ret;
ret = regmap_read(st->regmap, MAX22007_DAC_CHANNEL_REG(channel), &reg_val);
if (ret)
return ret;
*data = FIELD_GET(MAX22007_DAC_DATA_MASK, reg_val);
return 0;
}
static int max22007_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct max22007_state *st = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = max22007_read_channel_data(st, chan->channel, val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_VOLTAGE)
*val = 5 * MAX22007_REF_MV; /* 5 * Vref in mV */
else
*val = 25; /* Vref / (2 * Rsense) = MAX22007_REF_MV / 100 */
*val2 = 12; /* 12-bit DAC resolution */
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
}
static int max22007_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct max22007_state *st = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
return max22007_write_channel_data(st, chan->channel, val);
default:
return -EINVAL;
}
}
static const struct iio_info max22007_info = {
.read_raw = max22007_read_raw,
.write_raw = max22007_write_raw,
};
static ssize_t max22007_read_dac_powerdown(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
char *buf)
{
struct max22007_state *st = iio_priv(indio_dev);
unsigned int reg_val;
bool powerdown;
int ret;
ret = regmap_read(st->regmap, MAX22007_CHANNEL_MODE_REG, &reg_val);
if (ret)
return ret;
powerdown = !(reg_val & MAX22007_CH_PWRON_CH_MASK(chan->channel));
return sysfs_emit(buf, "%d\n", powerdown);
}
static ssize_t max22007_write_dac_powerdown(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct max22007_state *st = iio_priv(indio_dev);
bool powerdown;
int ret;
ret = kstrtobool(buf, &powerdown);
if (ret)
return ret;
ret = regmap_update_bits(st->regmap, MAX22007_CHANNEL_MODE_REG,
MAX22007_CH_PWRON_CH_MASK(chan->channel),
MAX22007_CH_PWR_VAL(chan->channel, powerdown ? 0 : 1));
if (ret)
return ret;
return len;
}
static const struct iio_chan_spec_ext_info max22007_ext_info[] = {
{
.name = "powerdown",
.read = max22007_read_dac_powerdown,
.write = max22007_write_dac_powerdown,
.shared = IIO_SEPARATE,
},
{ }
};
static int max22007_parse_channel_cfg(struct max22007_state *st, u8 *num_channels)
{
struct device *dev = &st->spi->dev;
int ret, num_chan;
int i = 0;
u32 reg;
num_chan = device_get_child_node_count(dev);
if (!num_chan)
return dev_err_probe(dev, -ENODEV, "no channels configured\n");
st->iio_chans = devm_kcalloc(dev, num_chan, sizeof(*st->iio_chans), GFP_KERNEL);
if (!st->iio_chans)
return -ENOMEM;
device_for_each_child_node_scoped(dev, child) {
u32 ch_func;
enum iio_chan_type chan_type;
ret = fwnode_property_read_u32(child, "reg", &reg);
if (ret)
return dev_err_probe(dev, ret,
"failed to read reg property of %pfwP\n", child);
if (reg >= MAX22007_NUM_CHANNELS)
return dev_err_probe(dev, -EINVAL,
"reg out of range in %pfwP\n", child);
ret = fwnode_property_read_u32(child, "adi,ch-func", &ch_func);
if (ret)
return dev_err_probe(dev, ret,
"missing adi,ch-func property for %pfwP\n", child);
switch (ch_func) {
case CH_FUNC_VOLTAGE_OUTPUT:
chan_type = IIO_VOLTAGE;
break;
case CH_FUNC_CURRENT_OUTPUT:
chan_type = IIO_CURRENT;
break;
default:
return dev_err_probe(dev, -EINVAL,
"invalid adi,ch-func %u for %pfwP\n",
ch_func, child);
}
st->iio_chans[i++] = (struct iio_chan_spec) {
.output = 1,
.indexed = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.ext_info = max22007_ext_info,
.channel = reg,
.type = chan_type,
};
ret = regmap_update_bits(st->regmap, MAX22007_CHANNEL_MODE_REG,
MAX22007_CH_MODE_CH_MASK(reg),
MAX22007_CH_MODE_VAL(reg, ch_func - 1));
if (ret)
return ret;
/* Set DAC to transparent mode (immediate update) */
ret = regmap_update_bits(st->regmap, MAX22007_CONFIG_REG,
MAX22007_DAC_LATCH_MODE_MASK(reg),
MAX22007_DAC_LATCH_MODE_VAL(reg, 1));
if (ret)
return ret;
}
*num_channels = num_chan;
return 0;
}
static int max22007_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct gpio_desc *reset_gpio;
struct max22007_state *st;
struct iio_dev *indio_dev;
u8 num_channels;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
st->spi = spi;
crc8_populate_lsb(max22007_crc8_table, MAX22007_CRC8_POLYNOMIAL);
st->regmap = devm_regmap_init(dev, &max22007_regmap_bus, st,
&max22007_regmap_config);
if (IS_ERR(st->regmap))
return dev_err_probe(dev, PTR_ERR(st->regmap),
"Failed to initialize regmap\n");
ret = devm_regulator_bulk_get_enable(dev, MAX22007_NUM_SUPPLIES,
max22007_supply_names);
if (ret)
return dev_err_probe(dev, ret, "Failed to get and enable regulators\n");
reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(reset_gpio))
return dev_err_probe(dev, PTR_ERR(reset_gpio),
"Failed to get reset GPIO\n");
if (reset_gpio) {
gpiod_set_value_cansleep(reset_gpio, 1);
usleep_range(1000, 5000);
gpiod_set_value_cansleep(reset_gpio, 0);
usleep_range(1000, 5000);
} else {
ret = regmap_write(st->regmap, MAX22007_SOFT_RESET_REG,
MAX22007_SOFT_RESET_BITS_MASK);
if (ret)
return ret;
}
ret = regmap_set_bits(st->regmap, MAX22007_CONFIG_REG,
MAX22007_CRC_EN_MASK);
if (ret)
return ret;
ret = max22007_parse_channel_cfg(st, &num_channels);
if (ret)
return ret;
indio_dev->info = &max22007_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = st->iio_chans;
indio_dev->num_channels = num_channels;
indio_dev->name = "max22007";
return devm_iio_device_register(dev, indio_dev);
}
static const struct spi_device_id max22007_id[] = {
{ "max22007" },
{ }
};
MODULE_DEVICE_TABLE(spi, max22007_id);
static const struct of_device_id max22007_of_match[] = {
{ .compatible = "adi,max22007" },
{ }
};
MODULE_DEVICE_TABLE(of, max22007_of_match);
static struct spi_driver max22007_driver = {
.driver = {
.name = "max22007",
.of_match_table = max22007_of_match,
},
.probe = max22007_probe,
.id_table = max22007_id,
};
module_spi_driver(max22007_driver);
MODULE_AUTHOR("Janani Sunil <janani.sunil@analog.com>");
MODULE_DESCRIPTION("Analog Devices MAX22007 DAC");
MODULE_LICENSE("GPL");

1250
drivers/iio/dac/mcp47feb02.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,9 @@
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/container_of.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
@@ -435,9 +437,14 @@ struct adf4377_state {
struct gpio_desc *gpio_ce;
struct gpio_desc *gpio_enclk1;
struct gpio_desc *gpio_enclk2;
struct clk *clk;
struct clk *clkout;
struct clk_hw hw;
u8 buf[2] __aligned(IIO_DMA_MINALIGN);
};
#define to_adf4377_state(h) container_of(h, struct adf4377_state, hw)
static const char * const adf4377_muxout_modes[] = {
[ADF4377_MUXOUT_HIGH_Z] = "high_z",
[ADF4377_MUXOUT_LKDET] = "lock_detect",
@@ -929,6 +936,110 @@ static int adf4377_freq_change(struct notifier_block *nb, unsigned long action,
return NOTIFY_OK;
}
static unsigned long adf4377_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct adf4377_state *st = to_adf4377_state(hw);
u64 freq;
int ret;
ret = adf4377_get_freq(st, &freq);
if (ret)
return 0;
return freq;
}
static int adf4377_clk_set_rate(struct clk_hw *hw,
unsigned long rate,
unsigned long parent_rate)
{
struct adf4377_state *st = to_adf4377_state(hw);
return adf4377_set_freq(st, rate);
}
static int adf4377_clk_prepare(struct clk_hw *hw)
{
struct adf4377_state *st = to_adf4377_state(hw);
return regmap_update_bits(st->regmap, 0x1a, ADF4377_001A_PD_CLKOUT1_MSK |
ADF4377_001A_PD_CLKOUT2_MSK,
FIELD_PREP(ADF4377_001A_PD_CLKOUT1_MSK, 0) |
FIELD_PREP(ADF4377_001A_PD_CLKOUT2_MSK, 0));
}
static void adf4377_clk_unprepare(struct clk_hw *hw)
{
struct adf4377_state *st = to_adf4377_state(hw);
regmap_update_bits(st->regmap, 0x1a, ADF4377_001A_PD_CLKOUT1_MSK |
ADF4377_001A_PD_CLKOUT2_MSK,
FIELD_PREP(ADF4377_001A_PD_CLKOUT1_MSK, 1) |
FIELD_PREP(ADF4377_001A_PD_CLKOUT2_MSK, 1));
}
static int adf4377_clk_is_prepared(struct clk_hw *hw)
{
struct adf4377_state *st = to_adf4377_state(hw);
unsigned int readval;
int ret;
ret = regmap_read(st->regmap, 0x1a, &readval);
if (ret)
return ret;
return !(readval & (ADF4377_001A_PD_CLKOUT1_MSK | ADF4377_001A_PD_CLKOUT2_MSK));
}
static const struct clk_ops adf4377_clk_ops = {
.recalc_rate = adf4377_clk_recalc_rate,
.set_rate = adf4377_clk_set_rate,
.prepare = adf4377_clk_prepare,
.unprepare = adf4377_clk_unprepare,
.is_prepared = adf4377_clk_is_prepared,
};
static int adf4377_clk_register(struct adf4377_state *st)
{
struct spi_device *spi = st->spi;
struct device *dev = &spi->dev;
struct clk_init_data init;
struct clk_parent_data parent_data;
int ret;
if (!device_property_present(dev, "#clock-cells"))
return 0;
ret = device_property_read_string(dev, "clock-output-names", &init.name);
if (ret) {
init.name = devm_kasprintf(dev, GFP_KERNEL, "%pfw-clk",
dev_fwnode(dev));
if (!init.name)
return -ENOMEM;
}
parent_data.fw_name = "ref_in";
init.ops = &adf4377_clk_ops;
init.parent_data = &parent_data;
init.num_parents = 1;
init.flags = CLK_SET_RATE_PARENT;
st->hw.init = &init;
ret = devm_clk_hw_register(dev, &st->hw);
if (ret)
return ret;
ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &st->hw);
if (ret)
return ret;
st->clkout = st->hw.clk;
return 0;
}
static const struct adf4377_chip_info adf4377_chip_info = {
.name = "adf4377",
.has_gpio_enclk2 = true,
@@ -958,8 +1069,6 @@ static int adf4377_probe(struct spi_device *spi)
indio_dev->info = &adf4377_info;
indio_dev->name = "adf4377";
indio_dev->channels = adf4377_channels;
indio_dev->num_channels = ARRAY_SIZE(adf4377_channels);
st->regmap = regmap;
st->spi = spi;
@@ -979,6 +1088,15 @@ static int adf4377_probe(struct spi_device *spi)
if (ret)
return ret;
ret = adf4377_clk_register(st);
if (ret)
return ret;
if (!st->clkout) {
indio_dev->channels = adf4377_channels;
indio_dev->num_channels = ARRAY_SIZE(adf4377_channels);
}
return devm_iio_device_register(&spi->dev, indio_dev);
}

View File

@@ -597,7 +597,7 @@ static int adxrs290_probe_trigger(struct iio_dev *indio_dev)
ret = devm_request_irq(&st->spi->dev, st->spi->irq,
&iio_trigger_generic_data_rdy_poll,
IRQF_ONESHOT, "adxrs290_irq", st->dready_trig);
IRQF_NO_THREAD, "adxrs290_irq", st->dready_trig);
if (ret < 0)
return dev_err_probe(&st->spi->dev, ret,
"request irq %d failed\n", st->spi->irq);

View File

@@ -118,11 +118,9 @@ int itg3200_probe_trigger(struct iio_dev *indio_dev)
if (!st->trig)
return -ENOMEM;
ret = request_irq(st->i2c->irq,
&iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_RISING,
"itg3200_data_rdy",
st->trig);
ret = request_irq(st->i2c->irq, &iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
"itg3200_data_rdy", st->trig);
if (ret)
goto error_free_trig;

View File

@@ -93,6 +93,8 @@ static int itg3200_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
reg = (u8)chan->address;
ret = itg3200_read_reg_s16(indio_dev, reg, val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;

View File

@@ -1162,10 +1162,8 @@ int mpu3050_common_probe(struct device *dev,
mpu3050->regs[1].supply = mpu3050_reg_vlogic;
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(mpu3050->regs),
mpu3050->regs);
if (ret) {
dev_err(dev, "Cannot get regulators\n");
return ret;
}
if (ret)
return dev_err_probe(dev, ret, "Cannot get regulators\n");
ret = mpu3050_power_up(mpu3050);
if (ret)

View File

@@ -540,11 +540,10 @@ static int afe4403_probe(struct spi_device *spi)
return ret;
}
ret = devm_request_threaded_irq(dev, afe->irq,
iio_trigger_generic_data_rdy_poll,
NULL, IRQF_ONESHOT,
AFE4403_DRIVER_NAME,
afe->trig);
ret = devm_request_irq(dev, afe->irq,
iio_trigger_generic_data_rdy_poll,
IRQF_NO_THREAD, AFE4403_DRIVER_NAME,
afe->trig);
if (ret) {
dev_err(dev, "Unable to request IRQ\n");
return ret;

View File

@@ -547,11 +547,10 @@ static int afe4404_probe(struct i2c_client *client)
return ret;
}
ret = devm_request_threaded_irq(dev, afe->irq,
iio_trigger_generic_data_rdy_poll,
NULL, IRQF_ONESHOT,
AFE4404_DRIVER_NAME,
afe->trig);
ret = devm_request_irq(dev, afe->irq,
iio_trigger_generic_data_rdy_poll,
IRQF_NO_THREAD, AFE4404_DRIVER_NAME,
afe->trig);
if (ret) {
dev_err(dev, "Unable to request IRQ\n");
return ret;

View File

@@ -417,13 +417,7 @@ static int max30100_read_raw(struct iio_dev *indio_dev,
* Temperature reading can only be acquired while engine
* is running
*/
if (iio_device_claim_buffer_mode(indio_dev)) {
/*
* Replacing -EBUSY or other error code
* returned by iio_device_claim_buffer_mode()
* because user space may rely on the current
* one.
*/
if (!iio_device_try_claim_buffer_mode(indio_dev)) {
ret = -EAGAIN;
} else {
ret = max30100_get_temp(data, val);

View File

@@ -467,44 +467,29 @@ static int max30102_read_raw(struct iio_dev *indio_dev,
int *val, int *val2, long mask)
{
struct max30102_data *data = iio_priv(indio_dev);
int ret = -EINVAL;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
case IIO_CHAN_INFO_RAW: {
/*
* Temperature reading can only be acquired when not in
* shutdown; leave shutdown briefly when buffer not running
*/
any_mode_retry:
if (iio_device_claim_buffer_mode(indio_dev)) {
/*
* This one is a *bit* hacky. If we cannot claim buffer
* mode, then try direct mode so that we make sure
* things cannot concurrently change. And we just keep
* trying until we get one of the modes...
*/
if (!iio_device_claim_direct(indio_dev))
goto any_mode_retry;
IIO_DEV_GUARD_CURRENT_MODE(indio_dev);
ret = max30102_get_temp(data, val, true);
iio_device_release_direct(indio_dev);
} else {
ret = max30102_get_temp(data, val, false);
iio_device_release_buffer_mode(indio_dev);
}
ret = max30102_get_temp(data, val, !iio_buffer_enabled(indio_dev));
if (ret)
return ret;
ret = IIO_VAL_INT;
break;
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_SCALE:
*val = 1000; /* 62.5 */
*val2 = 16;
ret = IIO_VAL_FRACTIONAL;
break;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
return ret;
}
static const struct iio_info max30102_info = {

View File

@@ -37,6 +37,7 @@ static const struct i2c_device_id bmi270_i2c_id[] = {
{ "bmi270", (kernel_ulong_t)&bmi270_chip_info },
{ }
};
MODULE_DEVICE_TABLE(i2c, bmi270_i2c_id);
static const struct acpi_device_id bmi270_acpi_match[] = {
/* GPD Win Mini, Aya Neo AIR Pro, OXP Mini Pro, etc. */
@@ -45,12 +46,14 @@ static const struct acpi_device_id bmi270_acpi_match[] = {
{ "BMI0260", (kernel_ulong_t)&bmi260_chip_info },
{ }
};
MODULE_DEVICE_TABLE(acpi, bmi270_acpi_match);
static const struct of_device_id bmi270_of_match[] = {
{ .compatible = "bosch,bmi260", .data = &bmi260_chip_info },
{ .compatible = "bosch,bmi270", .data = &bmi270_chip_info },
{ }
};
MODULE_DEVICE_TABLE(of, bmi270_of_match);
static struct i2c_driver bmi270_i2c_driver = {
.driver = {

View File

@@ -59,10 +59,7 @@ int inv_icm42600_temp_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (!iio_device_claim_direct(indio_dev))
return -EBUSY;
ret = inv_icm42600_temp_read(st, &temp);
iio_device_release_direct(indio_dev);
if (ret)
return ret;
*val = temp;

View File

@@ -67,10 +67,6 @@
#define SMI330_CHIP_ID 0x42
#define SMI330_SOFT_RESET_DELAY 2000
/* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */
#define smi330_field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
#define smi330_field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
#define SMI330_ACCEL_CHANNEL(_axis) { \
.type = IIO_ACCEL, \
.modified = 1, \
@@ -361,7 +357,7 @@ static int smi330_get_sensor_config(struct smi330_data *data,
if (ret)
return ret;
reg_val = smi330_field_get(attr->mask, reg_val);
reg_val = field_get(attr->mask, reg_val);
if (attr->type == IIO_VAL_INT) {
for (i = 0; i < attr->len; i++) {
@@ -410,7 +406,7 @@ static int smi330_set_sensor_config(struct smi330_data *data,
if (ret)
return ret;
reg_val = smi330_field_prep(attr->mask, reg_val);
reg_val = field_prep(attr->mask, reg_val);
ret = regmap_update_bits(data->regmap, reg, attr->mask, reg_val);
if (ret)
return ret;
@@ -475,7 +471,6 @@ static int smi330_read_avail(struct iio_dev *indio_dev,
*vals = smi330_average_attr.vals;
*length = smi330_average_attr.len;
*type = smi330_average_attr.type;
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
*vals = smi330_bandwidth_attr.vals;

View File

@@ -79,10 +79,11 @@ enum st_lsm6dsx_hw_id {
#define ST_LSM6DSX_MAX_TAGGED_WORD_LEN ((32 / ST_LSM6DSX_TAGGED_SAMPLE_SIZE) \
* ST_LSM6DSX_TAGGED_SAMPLE_SIZE)
#define ST_LSM6DSX_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask))
#define st_lsm6dsx_field_get(mask, reg) ((reg & mask) >> __ffs(mask))
#define ST_LSM6DSX_CHANNEL_ACC(chan_type, addr, mod, scan_idx) \
#define ST_LSM6DSX_CHANNEL_ACC(addr, mod, scan_idx, events) \
{ \
.type = chan_type, \
.type = IIO_ACCEL, \
.address = addr, \
.modified = 1, \
.channel2 = mod, \
@@ -96,9 +97,9 @@ enum st_lsm6dsx_hw_id {
.storagebits = 16, \
.endianness = IIO_LE, \
}, \
.event_spec = &st_lsm6dsx_event, \
.event_spec = events, \
.num_event_specs = ARRAY_SIZE(events), \
.ext_info = st_lsm6dsx_ext_info, \
.num_event_specs = 1, \
}
#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \
@@ -260,14 +261,31 @@ struct st_lsm6dsx_shub_settings {
u8 pause;
};
enum st_lsm6dsx_event_id {
ST_LSM6DSX_EVENT_WAKEUP,
ST_LSM6DSX_EVENT_TAP,
ST_LSM6DSX_EVENT_MAX
};
struct st_lsm6dsx_event_src {
struct st_lsm6dsx_reg value;
struct st_lsm6dsx_reg x_value;
struct st_lsm6dsx_reg y_value;
struct st_lsm6dsx_reg z_value;
u8 enable_mask;
u8 enable_axis_reg;
u8 enable_x_mask;
u8 enable_y_mask;
u8 enable_z_mask;
struct st_lsm6dsx_reg status;
u8 status_x_mask;
u8 status_y_mask;
u8 status_z_mask;
};
struct st_lsm6dsx_event_settings {
struct st_lsm6dsx_reg enable_reg;
struct st_lsm6dsx_reg wakeup_reg;
u8 wakeup_src_reg;
u8 wakeup_src_status_mask;
u8 wakeup_src_z_mask;
u8 wakeup_src_y_mask;
u8 wakeup_src_x_mask;
struct st_lsm6dsx_event_src sources[ST_LSM6DSX_EVENT_MAX];
};
enum st_lsm6dsx_sensor_id {
@@ -353,8 +371,8 @@ struct st_lsm6dsx_settings {
struct {
struct st_lsm6dsx_reg irq1;
struct st_lsm6dsx_reg irq2;
struct st_lsm6dsx_reg irq1_func;
struct st_lsm6dsx_reg irq2_func;
u8 irq1_func;
u8 irq2_func;
struct st_lsm6dsx_reg lir;
struct st_lsm6dsx_reg clear_on_read;
struct st_lsm6dsx_reg hla;
@@ -430,7 +448,6 @@ struct st_lsm6dsx_sensor {
* @sip: Total number of samples (acc/gyro/ts) in a given pattern.
* @buff: Device read buffer.
* @irq_routing: pointer to interrupt routing configuration.
* @event_threshold: wakeup event threshold.
* @enable_event: enabled event bitmask.
* @iio_devs: Pointers to acc/gyro iio_dev instances.
* @settings: Pointer to the specific sensor settings in use.
@@ -453,9 +470,8 @@ struct st_lsm6dsx_hw {
u8 ts_sip;
u8 sip;
const struct st_lsm6dsx_reg *irq_routing;
u8 event_threshold;
u8 enable_event;
u8 irq_routing;
u8 enable_event[ST_LSM6DSX_EVENT_MAX];
u8 *buff;
@@ -471,13 +487,6 @@ struct st_lsm6dsx_hw {
} scan[ST_LSM6DSX_ID_MAX];
};
static __maybe_unused const struct iio_event_spec st_lsm6dsx_event = {
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_EITHER,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE)
};
static __maybe_unused const unsigned long st_lsm6dsx_available_scan_masks[] = {
0x7, 0x0,
};

View File

@@ -94,10 +94,41 @@
#define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f
static const struct iio_event_spec st_lsm6dsx_ev_motion[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_EITHER,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE),
},
};
static const struct iio_event_spec st_lsm6dsx_ev_motion_tap[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_EITHER,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE),
},
{
.type = IIO_EV_TYPE_GESTURE,
.dir = IIO_EV_DIR_SINGLETAP,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE),
},
};
static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x28, IIO_MOD_X, 0),
ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x2a, IIO_MOD_Y, 1),
ST_LSM6DSX_CHANNEL_ACC(IIO_ACCEL, 0x2c, IIO_MOD_Z, 2),
ST_LSM6DSX_CHANNEL_ACC(0x28, IIO_MOD_X, 0, st_lsm6dsx_ev_motion),
ST_LSM6DSX_CHANNEL_ACC(0x2a, IIO_MOD_Y, 1, st_lsm6dsx_ev_motion),
ST_LSM6DSX_CHANNEL_ACC(0x2c, IIO_MOD_Z, 2, st_lsm6dsx_ev_motion),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static const struct iio_chan_spec st_lsm6dsx_acc_tap_channels[] = {
ST_LSM6DSX_CHANNEL_ACC(0x28, IIO_MOD_X, 0, st_lsm6dsx_ev_motion_tap),
ST_LSM6DSX_CHANNEL_ACC(0x2a, IIO_MOD_Y, 1, st_lsm6dsx_ev_motion_tap),
ST_LSM6DSX_CHANNEL_ACC(0x2c, IIO_MOD_Z, 2, st_lsm6dsx_ev_motion_tap),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
@@ -326,14 +357,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(0),
},
.irq1_func = {
.addr = 0x5e,
.mask = BIT(5),
},
.irq2_func = {
.addr = 0x5f,
.mask = BIT(5),
},
.irq1_func = 0x5e,
.irq2_func = 0x5f,
.hla = {
.addr = 0x12,
.mask = BIT(5),
@@ -386,15 +411,22 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
},
.event_settings = {
.wakeup_reg = {
.addr = 0x5B,
.mask = GENMASK(5, 0),
.sources = {
[ST_LSM6DSX_EVENT_WAKEUP] = {
.value = {
.addr = 0x5b,
.mask = GENMASK(5, 0),
},
.enable_mask = BIT(5),
.status = {
.addr = 0x1b,
.mask = BIT(3),
},
.status_z_mask = BIT(0),
.status_y_mask = BIT(1),
.status_x_mask = BIT(2),
},
},
.wakeup_src_reg = 0x1b,
.wakeup_src_status_mask = BIT(3),
.wakeup_src_z_mask = BIT(0),
.wakeup_src_y_mask = BIT(1),
.wakeup_src_x_mask = BIT(2),
},
},
{
@@ -492,14 +524,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(0),
},
.irq1_func = {
.addr = 0x5e,
.mask = BIT(5),
},
.irq2_func = {
.addr = 0x5f,
.mask = BIT(5),
},
.irq1_func = 0x5e,
.irq2_func = 0x5f,
.hla = {
.addr = 0x12,
.mask = BIT(5),
@@ -552,15 +578,22 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
},
.event_settings = {
.wakeup_reg = {
.addr = 0x5B,
.mask = GENMASK(5, 0),
.sources = {
[ST_LSM6DSX_EVENT_WAKEUP] = {
.value = {
.addr = 0x5b,
.mask = GENMASK(5, 0),
},
.enable_mask = BIT(5),
.status = {
.addr = 0x1b,
.mask = BIT(3),
},
.status_z_mask = BIT(0),
.status_y_mask = BIT(1),
.status_x_mask = BIT(2),
},
},
.wakeup_src_reg = 0x1b,
.wakeup_src_status_mask = BIT(3),
.wakeup_src_z_mask = BIT(0),
.wakeup_src_y_mask = BIT(1),
.wakeup_src_x_mask = BIT(2),
},
},
{
@@ -688,14 +721,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(0),
},
.irq1_func = {
.addr = 0x5e,
.mask = BIT(5),
},
.irq2_func = {
.addr = 0x5f,
.mask = BIT(5),
},
.irq1_func = 0x5e,
.irq2_func = 0x5f,
.hla = {
.addr = 0x12,
.mask = BIT(5),
@@ -789,15 +816,22 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(7),
},
.wakeup_reg = {
.addr = 0x5B,
.mask = GENMASK(5, 0),
.sources = {
[ST_LSM6DSX_EVENT_WAKEUP] = {
.value = {
.addr = 0x5b,
.mask = GENMASK(5, 0),
},
.enable_mask = BIT(5),
.status = {
.addr = 0x1b,
.mask = BIT(3),
},
.status_z_mask = BIT(0),
.status_y_mask = BIT(1),
.status_x_mask = BIT(2),
},
},
.wakeup_src_reg = 0x1b,
.wakeup_src_status_mask = BIT(3),
.wakeup_src_z_mask = BIT(0),
.wakeup_src_y_mask = BIT(1),
.wakeup_src_x_mask = BIT(2),
},
},
{
@@ -937,14 +971,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x56,
.mask = BIT(6),
},
.irq1_func = {
.addr = 0x5e,
.mask = BIT(5),
},
.irq2_func = {
.addr = 0x5f,
.mask = BIT(5),
},
.irq1_func = 0x5e,
.irq2_func = 0x5f,
.hla = {
.addr = 0x12,
.mask = BIT(5),
@@ -1028,15 +1056,22 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(7),
},
.wakeup_reg = {
.addr = 0x5b,
.mask = GENMASK(5, 0),
.sources = {
[ST_LSM6DSX_EVENT_WAKEUP] = {
.value = {
.addr = 0x5b,
.mask = GENMASK(5, 0),
},
.enable_mask = BIT(5),
.status = {
.addr = 0x1b,
.mask = BIT(3),
},
.status_z_mask = BIT(0),
.status_y_mask = BIT(1),
.status_x_mask = BIT(2),
},
},
.wakeup_src_reg = 0x1b,
.wakeup_src_status_mask = BIT(3),
.wakeup_src_z_mask = BIT(0),
.wakeup_src_y_mask = BIT(1),
.wakeup_src_x_mask = BIT(2),
},
},
{
@@ -1152,14 +1187,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x56,
.mask = BIT(6),
},
.irq1_func = {
.addr = 0x5e,
.mask = BIT(5),
},
.irq2_func = {
.addr = 0x5f,
.mask = BIT(5),
},
.irq1_func = 0x5e,
.irq2_func = 0x5f,
.hla = {
.addr = 0x12,
.mask = BIT(5),
@@ -1211,15 +1240,22 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x58,
.mask = BIT(7),
},
.wakeup_reg = {
.addr = 0x5B,
.mask = GENMASK(5, 0),
.sources = {
[ST_LSM6DSX_EVENT_WAKEUP] = {
.value = {
.addr = 0x5b,
.mask = GENMASK(5, 0),
},
.enable_mask = BIT(5),
.status = {
.addr = 0x1b,
.mask = BIT(3),
},
.status_z_mask = BIT(0),
.status_y_mask = BIT(1),
.status_x_mask = BIT(2),
},
},
.wakeup_src_reg = 0x1b,
.wakeup_src_status_mask = BIT(3),
.wakeup_src_z_mask = BIT(0),
.wakeup_src_y_mask = BIT(1),
.wakeup_src_x_mask = BIT(2),
},
},
{
@@ -1248,8 +1284,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
},
.channels = {
[ST_LSM6DSX_ID_ACC] = {
.chan = st_lsm6dsx_acc_channels,
.len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
.chan = st_lsm6dsx_acc_tap_channels,
.len = ARRAY_SIZE(st_lsm6dsx_acc_tap_channels),
},
[ST_LSM6DSX_ID_GYRO] = {
.chan = st_lsm6dsx_gyro_channels,
@@ -1329,14 +1365,8 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x56,
.mask = BIT(0),
},
.irq1_func = {
.addr = 0x5e,
.mask = BIT(5),
},
.irq2_func = {
.addr = 0x5f,
.mask = BIT(5),
},
.irq1_func = 0x5e,
.irq2_func = 0x5f,
.hla = {
.addr = 0x03,
.mask = BIT(4),
@@ -1419,15 +1449,48 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.addr = 0x50,
.mask = BIT(7),
},
.wakeup_reg = {
.addr = 0x5b,
.mask = GENMASK(5, 0),
.sources = {
[ST_LSM6DSX_EVENT_WAKEUP] = {
.value = {
.addr = 0x5b,
.mask = GENMASK(5, 0),
},
.enable_mask = BIT(5),
.status = {
.addr = 0x45,
.mask = BIT(3),
},
.status_z_mask = BIT(0),
.status_y_mask = BIT(1),
.status_x_mask = BIT(2),
},
[ST_LSM6DSX_EVENT_TAP] = {
.x_value = {
.addr = 0x57,
.mask = GENMASK(4, 0),
},
.y_value = {
.addr = 0x58,
.mask = GENMASK(4, 0),
},
.z_value = {
.addr = 0x59,
.mask = GENMASK(4, 0),
},
.enable_mask = BIT(6),
.enable_axis_reg = 0x56,
.enable_x_mask = BIT(3),
.enable_y_mask = BIT(2),
.enable_z_mask = BIT(1),
.status = {
.addr = 0x46,
.mask = BIT(5),
},
.status_x_mask = BIT(2),
.status_y_mask = BIT(1),
.status_z_mask = BIT(0),
},
},
.wakeup_src_reg = 0x45,
.wakeup_src_status_mask = BIT(3),
.wakeup_src_z_mask = BIT(0),
.wakeup_src_y_mask = BIT(1),
.wakeup_src_x_mask = BIT(2),
},
},
{
@@ -1754,20 +1817,25 @@ __st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
}
static int
st_lsm6dsx_check_events(struct st_lsm6dsx_sensor *sensor, bool enable)
st_lsm6dsx_check_events(struct st_lsm6dsx_sensor *sensor)
{
struct st_lsm6dsx_hw *hw = sensor->hw;
int event;
if (sensor->id == ST_LSM6DSX_ID_GYRO || enable)
if (sensor->id != ST_LSM6DSX_ID_ACC)
return 0;
return hw->enable_event;
for (event = 0; event < ST_LSM6DSX_EVENT_MAX; event++) {
if (hw->enable_event[event])
return true;
}
return false;
}
int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
bool enable)
{
if (st_lsm6dsx_check_events(sensor, enable))
if (st_lsm6dsx_check_events(sensor))
return 0;
return __st_lsm6dsx_sensor_set_enable(sensor, enable);
@@ -1795,11 +1863,9 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
if (err < 0)
return err;
if (!hw->enable_event) {
err = st_lsm6dsx_sensor_set_enable(sensor, false);
if (err < 0)
return err;
}
err = st_lsm6dsx_sensor_set_enable(sensor, false);
if (err < 0)
return err;
*val = (s16)le16_to_cpu(data);
@@ -1876,28 +1942,106 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev,
return err;
}
static int st_lsm6dsx_event_setup(struct st_lsm6dsx_hw *hw, bool state)
static int st_lsm6dsx_event_setup(struct st_lsm6dsx_hw *hw,
enum st_lsm6dsx_event_id event, int axis,
bool state)
{
const struct st_lsm6dsx_reg *reg;
const struct st_lsm6dsx_event_src *src;
unsigned int data;
int err;
u8 old_enable, new_enable;
if (!hw->settings->irq_config.irq1_func.addr)
if (!hw->irq_routing)
return -ENOTSUPP;
reg = &hw->settings->event_settings.enable_reg;
if (reg->addr) {
data = ST_LSM6DSX_SHIFT_VAL(state, reg->mask);
err = st_lsm6dsx_update_bits_locked(hw, reg->addr,
reg->mask, data);
if (err < 0)
return err;
/* Enable/disable event interrupt */
src = &hw->settings->event_settings.sources[event];
if (src->enable_axis_reg) {
u8 enable_mask;
switch (axis) {
case IIO_MOD_X:
enable_mask = src->enable_x_mask;
break;
case IIO_MOD_Y:
enable_mask = src->enable_y_mask;
break;
case IIO_MOD_Z:
enable_mask = src->enable_z_mask;
break;
default:
enable_mask = 0;
}
if (enable_mask) {
data = ST_LSM6DSX_SHIFT_VAL(state, enable_mask);
err = st_lsm6dsx_update_bits_locked(hw,
src->enable_axis_reg,
enable_mask, data);
if (err < 0)
return err;
}
}
/* Enable wakeup interrupt */
data = ST_LSM6DSX_SHIFT_VAL(state, hw->irq_routing->mask);
return st_lsm6dsx_update_bits_locked(hw, hw->irq_routing->addr,
hw->irq_routing->mask, data);
/*
* If the set of axes for which the event source is enabled does not
* change from empty to non-empty or vice versa, there is nothing else
* to do.
*/
old_enable = hw->enable_event[event];
new_enable = state ? (old_enable | BIT(axis)) :
(old_enable & ~BIT(axis));
if (!old_enable == !new_enable)
return 0;
data = ST_LSM6DSX_SHIFT_VAL(state, src->enable_mask);
return st_lsm6dsx_update_bits_locked(hw, hw->irq_routing,
src->enable_mask, data);
}
static enum st_lsm6dsx_event_id
st_lsm6dsx_get_event_id(enum iio_event_type type)
{
switch (type) {
case IIO_EV_TYPE_THRESH:
return ST_LSM6DSX_EVENT_WAKEUP;
case IIO_EV_TYPE_GESTURE:
return ST_LSM6DSX_EVENT_TAP;
default:
return ST_LSM6DSX_EVENT_MAX;
}
}
static const struct st_lsm6dsx_reg *
st_lsm6dsx_get_event_reg(struct st_lsm6dsx_hw *hw,
enum st_lsm6dsx_event_id event,
const struct iio_chan_spec *chan)
{
const struct st_lsm6dsx_event_src *src;
const struct st_lsm6dsx_reg *reg;
src = &hw->settings->event_settings.sources[event];
switch (chan->channel2) {
case IIO_MOD_X:
reg = &src->x_value;
break;
case IIO_MOD_Y:
reg = &src->y_value;
break;
case IIO_MOD_Z:
reg = &src->z_value;
break;
default:
return NULL;
}
if (reg->addr)
return reg;
/*
* The sensor does not support configuring this event source on a per
* axis basis: return the register to configure the event source for all
* axes.
*/
return &src->value;
}
static int st_lsm6dsx_read_event(struct iio_dev *iio_dev,
@@ -1907,14 +2051,26 @@ static int st_lsm6dsx_read_event(struct iio_dev *iio_dev,
enum iio_event_info info,
int *val, int *val2)
{
enum st_lsm6dsx_event_id event = st_lsm6dsx_get_event_id(type);
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
struct st_lsm6dsx_hw *hw = sensor->hw;
const struct st_lsm6dsx_reg *reg;
u8 data;
int err;
if (type != IIO_EV_TYPE_THRESH)
if (event == ST_LSM6DSX_EVENT_MAX)
return -EINVAL;
reg = st_lsm6dsx_get_event_reg(hw, event, chan);
if (!reg)
return -EINVAL;
err = st_lsm6dsx_read_locked(hw, reg->addr, &data, sizeof(data));
if (err < 0)
return err;
*val2 = 0;
*val = hw->event_threshold;
*val = st_lsm6dsx_field_get(reg->mask, data);
return IIO_VAL_INT;
}
@@ -1927,27 +2083,29 @@ st_lsm6dsx_write_event(struct iio_dev *iio_dev,
enum iio_event_info info,
int val, int val2)
{
enum st_lsm6dsx_event_id event = st_lsm6dsx_get_event_id(type);
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
struct st_lsm6dsx_hw *hw = sensor->hw;
const struct st_lsm6dsx_reg *reg;
unsigned int data;
int err;
if (type != IIO_EV_TYPE_THRESH)
if (event == ST_LSM6DSX_EVENT_MAX)
return -EINVAL;
if (val < 0 || val > 31)
return -EINVAL;
reg = &hw->settings->event_settings.wakeup_reg;
reg = st_lsm6dsx_get_event_reg(hw, event, chan);
if (!reg)
return -EINVAL;
data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask);
err = st_lsm6dsx_update_bits_locked(hw, reg->addr,
reg->mask, data);
if (err < 0)
return -EINVAL;
hw->event_threshold = val;
return 0;
}
@@ -1957,13 +2115,56 @@ st_lsm6dsx_read_event_config(struct iio_dev *iio_dev,
enum iio_event_type type,
enum iio_event_direction dir)
{
enum st_lsm6dsx_event_id event = st_lsm6dsx_get_event_id(type);
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
struct st_lsm6dsx_hw *hw = sensor->hw;
if (type != IIO_EV_TYPE_THRESH)
if (event == ST_LSM6DSX_EVENT_MAX)
return -EINVAL;
return !!(hw->enable_event & BIT(chan->channel2));
return !!(hw->enable_event[event] & BIT(chan->channel2));
}
/**
* st_lsm6dsx_check_other_events - Check for enabled sensor events.
* @hw: Sensor hardware instance.
* @curr: Current event type.
*
* Return: whether any events other than @curr are enabled.
*/
static bool st_lsm6dsx_check_other_events(struct st_lsm6dsx_hw *hw,
enum st_lsm6dsx_event_id curr)
{
enum st_lsm6dsx_event_id other;
for (other = 0; other < ST_LSM6DSX_EVENT_MAX; other++) {
if (other != curr && hw->enable_event[other])
return true;
}
return false;
}
static int st_lsm6dsx_events_enable(struct st_lsm6dsx_sensor *sensor,
bool state)
{
struct st_lsm6dsx_hw *hw = sensor->hw;
const struct st_lsm6dsx_reg *reg;
reg = &hw->settings->event_settings.enable_reg;
if (reg->addr) {
int err;
err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
ST_LSM6DSX_SHIFT_VAL(state, reg->mask));
if (err)
return err;
}
if (state || !(hw->fifo_mask & BIT(sensor->id)))
return __st_lsm6dsx_sensor_set_enable(sensor, state);
return 0;
}
static int
@@ -1972,45 +2173,38 @@ st_lsm6dsx_write_event_config(struct iio_dev *iio_dev,
enum iio_event_type type,
enum iio_event_direction dir, bool state)
{
enum st_lsm6dsx_event_id event = st_lsm6dsx_get_event_id(type);
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
struct st_lsm6dsx_hw *hw = sensor->hw;
u8 enable_event;
int err;
if (type != IIO_EV_TYPE_THRESH)
if (event == ST_LSM6DSX_EVENT_MAX)
return -EINVAL;
if (state) {
enable_event = hw->enable_event | BIT(chan->channel2);
/* do not enable events if they are already enabled */
if (hw->enable_event)
goto out;
} else {
enable_event = hw->enable_event & ~BIT(chan->channel2);
/* only turn off sensor if no events is enabled */
if (enable_event)
goto out;
}
if (state)
enable_event = hw->enable_event[event] | BIT(chan->channel2);
else
enable_event = hw->enable_event[event] & ~BIT(chan->channel2);
/* stop here if no changes have been made */
if (hw->enable_event == enable_event)
if (hw->enable_event[event] == enable_event)
return 0;
err = st_lsm6dsx_event_setup(hw, state);
err = st_lsm6dsx_event_setup(hw, event, chan->channel2, state);
if (err < 0)
return err;
mutex_lock(&hw->conf_lock);
if (enable_event || !(hw->fifo_mask & BIT(sensor->id)))
err = __st_lsm6dsx_sensor_set_enable(sensor, state);
if (enable_event)
err = st_lsm6dsx_events_enable(sensor, true);
else if (!st_lsm6dsx_check_other_events(hw, event))
err = st_lsm6dsx_events_enable(sensor, false);
mutex_unlock(&hw->conf_lock);
if (err < 0)
return err;
out:
hw->enable_event = enable_event;
hw->enable_event[event] = enable_event;
return 0;
}
@@ -2151,11 +2345,11 @@ st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw,
switch (drdy_pin) {
case 1:
hw->irq_routing = &hw->settings->irq_config.irq1_func;
hw->irq_routing = hw->settings->irq_config.irq1_func;
*drdy_reg = &hw->settings->irq_config.irq1;
break;
case 2:
hw->irq_routing = &hw->settings->irq_config.irq2_func;
hw->irq_routing = hw->settings->irq_config.irq2_func;
*drdy_reg = &hw->settings->irq_config.irq2;
break;
default:
@@ -2414,53 +2608,70 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
}
static bool
st_lsm6dsx_report_motion_event(struct st_lsm6dsx_hw *hw)
st_lsm6dsx_report_events(struct st_lsm6dsx_hw *hw, enum st_lsm6dsx_event_id id,
enum iio_event_type type, enum iio_event_direction dir)
{
const struct st_lsm6dsx_event_settings *event_settings;
const struct st_lsm6dsx_event_src *src;
int err, data;
s64 timestamp;
if (!hw->enable_event)
if (!hw->enable_event[id])
return false;
event_settings = &hw->settings->event_settings;
err = st_lsm6dsx_read_locked(hw, event_settings->wakeup_src_reg,
src = &event_settings->sources[id];
err = st_lsm6dsx_read_locked(hw, src->status.addr,
&data, sizeof(data));
if (err < 0)
return false;
timestamp = iio_get_time_ns(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
if ((data & hw->settings->event_settings.wakeup_src_z_mask) &&
(hw->enable_event & BIT(IIO_MOD_Z)))
if ((data & src->status_z_mask) &&
(hw->enable_event[id] & BIT(IIO_MOD_Z)))
iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC],
IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
IIO_MOD_Z,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_EITHER),
type,
dir),
timestamp);
if ((data & hw->settings->event_settings.wakeup_src_y_mask) &&
(hw->enable_event & BIT(IIO_MOD_Y)))
if ((data & src->status_y_mask) &&
(hw->enable_event[id] & BIT(IIO_MOD_Y)))
iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC],
IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
IIO_MOD_Y,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_EITHER),
type,
dir),
timestamp);
if ((data & hw->settings->event_settings.wakeup_src_x_mask) &&
(hw->enable_event & BIT(IIO_MOD_X)))
if ((data & src->status_x_mask) &&
(hw->enable_event[id] & BIT(IIO_MOD_X)))
iio_push_event(hw->iio_devs[ST_LSM6DSX_ID_ACC],
IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
IIO_MOD_X,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_EITHER),
type,
dir),
timestamp);
return data & event_settings->wakeup_src_status_mask;
return data & src->status.mask;
}
static bool st_lsm6dsx_report_motion_event(struct st_lsm6dsx_hw *hw)
{
bool events_found;
events_found = st_lsm6dsx_report_events(hw, ST_LSM6DSX_EVENT_WAKEUP,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_EITHER);
events_found |= st_lsm6dsx_report_events(hw, ST_LSM6DSX_EVENT_TAP,
IIO_EV_TYPE_GESTURE,
IIO_EV_DIR_SINGLETAP);
return events_found;
}
static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private)
@@ -2749,7 +2960,7 @@ static int st_lsm6dsx_suspend(struct device *dev)
continue;
if (device_may_wakeup(dev) &&
sensor->id == ST_LSM6DSX_ID_ACC && hw->enable_event) {
st_lsm6dsx_check_events(sensor)) {
/* Enable wake from IRQ */
enable_irq_wake(hw->irq);
continue;
@@ -2780,7 +2991,7 @@ static int st_lsm6dsx_resume(struct device *dev)
sensor = iio_priv(hw->iio_devs[i]);
if (device_may_wakeup(dev) &&
sensor->id == ST_LSM6DSX_ID_ACC && hw->enable_event)
st_lsm6dsx_check_events(sensor))
disable_irq_wake(hw->irq);
if (!(hw->suspend_mask & BIT(sensor->id)))

View File

@@ -2174,88 +2174,34 @@ int __devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev,
EXPORT_SYMBOL_GPL(__devm_iio_device_register);
/**
* __iio_device_claim_direct - Keep device in direct mode
* @indio_dev: the iio_dev associated with the device
* __iio_dev_mode_lock() - Locks the current IIO device mode
* @indio_dev: the iio_dev associated with the device
*
* If the device is in direct mode it is guaranteed to stay
* that way until __iio_device_release_direct() is called.
* If the device is either in direct or buffer mode, it's guaranteed to stay
* that way until __iio_dev_mode_unlock() is called.
*
* Use with __iio_device_release_direct().
* This function is not meant to be used directly by drivers to protect internal
* state; a driver should have it's own mechanisms for that matter.
*
* Drivers should only call iio_device_claim_direct().
*
* Returns: true on success, false on failure.
* There are very few cases where a driver actually needs to lock the current
* mode unconditionally. It's recommended to use iio_device_claim_direct() or
* iio_device_try_claim_buffer_mode() pairs or related helpers instead.
*/
bool __iio_device_claim_direct(struct iio_dev *indio_dev)
void __iio_dev_mode_lock(struct iio_dev *indio_dev)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
mutex_lock(&iio_dev_opaque->mlock);
if (iio_buffer_enabled(indio_dev)) {
mutex_unlock(&iio_dev_opaque->mlock);
return false;
}
return true;
mutex_lock(&to_iio_dev_opaque(indio_dev)->mlock);
}
EXPORT_SYMBOL_GPL(__iio_device_claim_direct);
EXPORT_SYMBOL_GPL(__iio_dev_mode_lock);
/**
* __iio_device_release_direct - releases claim on direct mode
* @indio_dev: the iio_dev associated with the device
*
* Release the claim. Device is no longer guaranteed to stay
* in direct mode.
*
* Drivers should only call iio_device_release_direct().
*
* Use with __iio_device_claim_direct()
* __iio_dev_mode_unlock() - Unlocks the current IIO device mode
* @indio_dev: the iio_dev associated with the device
*/
void __iio_device_release_direct(struct iio_dev *indio_dev)
void __iio_dev_mode_unlock(struct iio_dev *indio_dev)
{
mutex_unlock(&to_iio_dev_opaque(indio_dev)->mlock);
}
EXPORT_SYMBOL_GPL(__iio_device_release_direct);
/**
* iio_device_claim_buffer_mode - Keep device in buffer mode
* @indio_dev: the iio_dev associated with the device
*
* If the device is in buffer mode it is guaranteed to stay
* that way until iio_device_release_buffer_mode() is called.
*
* Use with iio_device_release_buffer_mode().
*
* Returns: 0 on success, -EBUSY on failure.
*/
int iio_device_claim_buffer_mode(struct iio_dev *indio_dev)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
mutex_lock(&iio_dev_opaque->mlock);
if (iio_buffer_enabled(indio_dev))
return 0;
mutex_unlock(&iio_dev_opaque->mlock);
return -EBUSY;
}
EXPORT_SYMBOL_GPL(iio_device_claim_buffer_mode);
/**
* iio_device_release_buffer_mode - releases claim on buffer mode
* @indio_dev: the iio_dev associated with the device
*
* Release the claim. Device is no longer guaranteed to stay
* in buffer mode.
*
* Use with iio_device_claim_buffer_mode().
*/
void iio_device_release_buffer_mode(struct iio_dev *indio_dev)
{
mutex_unlock(&to_iio_dev_opaque(indio_dev)->mlock);
}
EXPORT_SYMBOL_GPL(iio_device_release_buffer_mode);
EXPORT_SYMBOL_GPL(__iio_dev_mode_unlock);
/**
* iio_device_get_current_mode() - helper function providing read-only access to

View File

@@ -148,7 +148,7 @@ static void device_drop_group(struct config_group *group,
config_item_put(item);
}
static struct configfs_group_operations device_ops = {
static const struct configfs_group_operations device_ops = {
.make_group = &device_make_group,
.drop_item = &device_drop_group,
};

View File

@@ -152,7 +152,7 @@ static void trigger_drop_group(struct config_group *group,
config_item_put(item);
}
static struct configfs_group_operations trigger_ops = {
static const struct configfs_group_operations trigger_ops = {
.make_group = &trigger_make_group,
.drop_item = &trigger_drop_group,
};

View File

@@ -273,9 +273,9 @@ static ssize_t in_illuminance_scale_available_show
mutex_lock(&chip->lock);
for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i)
len += sprintf(buf + len, "%d.%06d ",
isl29018_scales[chip->int_time][i].scale,
isl29018_scales[chip->int_time][i].uscale);
len += sysfs_emit_at(buf, len, "%d.%06d ",
isl29018_scales[chip->int_time][i].scale,
isl29018_scales[chip->int_time][i].uscale);
mutex_unlock(&chip->lock);
buf[len - 1] = '\n';
@@ -293,8 +293,8 @@ static ssize_t in_illuminance_integration_time_available_show
int len = 0;
for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i)
len += sprintf(buf + len, "0.%06d ",
isl29018_int_utimes[chip->type][i]);
len += sysfs_emit_at(buf, len, "0.%06d ",
isl29018_int_utimes[chip->type][i]);
buf[len - 1] = '\n';
@@ -330,7 +330,7 @@ static ssize_t proximity_on_chip_ambient_infrared_suppression_show
* Return the "proximity scheme" i.e. if the chip does on chip
* infrared suppression (1 means perform on chip suppression)
*/
return sprintf(buf, "%d\n", chip->prox_scheme);
return sysfs_emit(buf, "%d\n", chip->prox_scheme);
}
static ssize_t proximity_on_chip_ambient_infrared_suppression_store

View File

@@ -302,41 +302,23 @@ static int opt4060_set_driver_state(struct iio_dev *indio_dev,
bool continuous_irq)
{
struct opt4060_chip *chip = iio_priv(indio_dev);
int ret = 0;
any_mode_retry:
if (iio_device_claim_buffer_mode(indio_dev)) {
/*
* This one is a *bit* hacky. If we cannot claim buffer mode,
* then try direct mode so that we make sure things cannot
* concurrently change. And we just keep trying until we get one
* of the modes...
*/
if (!iio_device_claim_direct(indio_dev))
goto any_mode_retry;
/*
* This path means that we managed to claim direct mode. In
* this case the buffer isn't enabled and it's okay to leave
* continuous mode for sampling and/or irq.
*/
ret = opt4060_set_state_common(chip, continuous_sampling,
continuous_irq);
iio_device_release_direct(indio_dev);
return ret;
} else {
/*
* This path means that we managed to claim buffer mode. In
* this case the buffer is enabled and irq and sampling must go
* to or remain continuous, but only if the trigger is from this
* device.
*/
if (!iio_trigger_validate_own_device(indio_dev->trig, indio_dev))
ret = opt4060_set_state_common(chip, true, true);
else
ret = opt4060_set_state_common(chip, continuous_sampling,
continuous_irq);
iio_device_release_buffer_mode(indio_dev);
}
return ret;
IIO_DEV_GUARD_CURRENT_MODE(indio_dev);
/*
* If we manage to claim buffer mode and we are using our own trigger,
* IRQ and sampling must go to or remain continuous.
*/
if (iio_buffer_enabled(indio_dev) &&
iio_trigger_validate_own_device(indio_dev->trig, indio_dev))
return opt4060_set_state_common(chip, true, true);
/*
* This path means that we managed to claim direct mode. In this case
* the buffer isn't enabled and it's okay to leave continuous mode for
* sampling and/or irq.
*/
return opt4060_set_state_common(chip, continuous_sampling, continuous_irq);
}
/*

View File

@@ -1248,7 +1248,7 @@ static int si1145_probe_trigger(struct iio_dev *indio_dev)
ret = devm_request_irq(&client->dev, client->irq,
iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_FALLING,
IRQF_TRIGGER_FALLING | IRQF_NO_THREAD,
"si1145_irq",
trig);
if (ret < 0) {

View File

@@ -1078,20 +1078,17 @@ static int vcnl4010_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
case IIO_CHAN_INFO_SCALE:
if (!iio_device_claim_direct(indio_dev))
case IIO_CHAN_INFO_SCALE: {
IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
if (IIO_DEV_ACQUIRE_FAILED(claim))
return -EBUSY;
/* Protect against event capture. */
if (vcnl4010_is_in_periodic_mode(data)) {
ret = -EBUSY;
} else {
ret = vcnl4000_read_raw(indio_dev, chan, val, val2,
mask);
}
if (vcnl4010_is_in_periodic_mode(data))
return -EBUSY;
iio_device_release_direct(indio_dev);
return ret;
return vcnl4000_read_raw(indio_dev, chan, val, val2, mask);
}
case IIO_CHAN_INFO_SAMP_FREQ:
switch (chan->type) {
case IIO_PROXIMITY:
@@ -1148,36 +1145,27 @@ static int vcnl4010_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
int ret;
struct vcnl4000_data *data = iio_priv(indio_dev);
if (!iio_device_claim_direct(indio_dev))
IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
if (IIO_DEV_ACQUIRE_FAILED(claim))
return -EBUSY;
/* Protect against event capture. */
if (vcnl4010_is_in_periodic_mode(data)) {
ret = -EBUSY;
goto end;
}
if (vcnl4010_is_in_periodic_mode(data))
return -EBUSY;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
switch (chan->type) {
case IIO_PROXIMITY:
ret = vcnl4010_write_proxy_samp_freq(data, val, val2);
goto end;
return vcnl4010_write_proxy_samp_freq(data, val, val2);
default:
ret = -EINVAL;
goto end;
return -EINVAL;
}
default:
ret = -EINVAL;
goto end;
return -EINVAL;
}
end:
iio_device_release_direct(indio_dev);
return ret;
}
static int vcnl4010_read_event(struct iio_dev *indio_dev,
@@ -1438,14 +1426,13 @@ static int vcnl4010_config_threshold_disable(struct vcnl4000_data *data)
static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state)
{
struct vcnl4000_data *data = iio_priv(indio_dev);
int ret;
if (state) {
if (!iio_device_claim_direct(indio_dev))
IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
if (IIO_DEV_ACQUIRE_FAILED(claim))
return -EBUSY;
ret = vcnl4010_config_threshold_enable(data);
iio_device_release_direct(indio_dev);
return ret;
return vcnl4010_config_threshold_enable(data);
} else {
return vcnl4010_config_threshold_disable(data);
}

View File

@@ -139,6 +139,19 @@ config MMC35240
To compile this driver as a module, choose M here: the module
will be called mmc35240.
config MMC5633
tristate "MEMSIC MMC5633 3-axis magnetic sensor"
select REGMAP_I2C
select REGMAP_I3C if I3C
depends on I2C
depends on I3C || !I3C
help
Say yes here to build support for the MEMSIC MMC5633 3-axis
magnetic sensor.
To compile this driver as a module, choose M here: the module
will be called mmc5633
config IIO_ST_MAGN_3AXIS
tristate "STMicroelectronics magnetometers 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS

View File

@@ -15,6 +15,7 @@ obj-$(CONFIG_BMC150_MAGN_SPI) += bmc150_magn_spi.o
obj-$(CONFIG_MAG3110) += mag3110.o
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
obj-$(CONFIG_MMC35240) += mmc35240.o
obj-$(CONFIG_MMC5633) += mmc5633.o
obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o
st_magn-y := st_magn_core.o

View File

@@ -581,7 +581,7 @@ static int ak8975_setup_irq(struct ak8975_data *data)
irq = gpiod_to_irq(data->eoc_gpiod);
rc = devm_request_irq(&client->dev, irq, ak8975_irq_handler,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
IRQF_TRIGGER_RISING,
dev_name(&client->dev), data);
if (rc < 0) {
dev_err(&client->dev, "irq %d request failed: %d\n", irq, rc);

View File

@@ -906,12 +906,9 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap,
goto err_poweroff;
}
ret = request_threaded_irq(irq,
iio_trigger_generic_data_rdy_poll,
NULL,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
"bmc150_magn_event",
data->dready_trig);
ret = request_irq(irq, iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
"bmc150_magn_event", data->dready_trig);
if (ret < 0) {
dev_err(dev, "request irq %d failed\n", irq);
goto err_trigger_unregister;

View File

@@ -0,0 +1,586 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* MMC5633 - MEMSIC 3-axis Magnetic Sensor
*
* Copyright (c) 2015, Intel Corporation.
* Copyright (c) 2025, NXP
*
* IIO driver for MMC5633, base on mmc35240.c
*/
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/i3c/device.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/init.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/unaligned.h>
#define MMC5633_REG_XOUT0 0x00
#define MMC5633_REG_XOUT1 0x01
#define MMC5633_REG_YOUT0 0x02
#define MMC5633_REG_YOUT1 0x03
#define MMC5633_REG_ZOUT0 0x04
#define MMC5633_REG_ZOUT1 0x05
#define MMC5633_REG_XOUT2 0x06
#define MMC5633_REG_YOUT2 0x07
#define MMC5633_REG_ZOUT2 0x08
#define MMC5633_REG_TOUT 0x09
#define MMC5633_REG_STATUS1 0x18
#define MMC5633_REG_STATUS0 0x19
#define MMC5633_REG_CTRL0 0x1b
#define MMC5633_REG_CTRL1 0x1c
#define MMC5633_REG_CTRL2 0x1d
#define MMC5633_REG_ID 0x39
#define MMC5633_STATUS1_MEAS_T_DONE_BIT BIT(7)
#define MMC5633_STATUS1_MEAS_M_DONE_BIT BIT(6)
#define MMC5633_CTRL0_CMM_FREQ_EN BIT(7)
#define MMC5633_CTRL0_AUTO_ST_EN BIT(6)
#define MMC5633_CTRL0_AUTO_SR_EN BIT(5)
#define MMC5633_CTRL0_RESET BIT(4)
#define MMC5633_CTRL0_SET BIT(3)
#define MMC5633_CTRL0_MEAS_T BIT(1)
#define MMC5633_CTRL0_MEAS_M BIT(0)
#define MMC5633_CTRL1_BW_MASK GENMASK(1, 0)
#define MMC5633_WAIT_SET_RESET_US (1 * USEC_PER_MSEC)
#define MMC5633_HDR_CTRL0_MEAS_M 0x01
#define MMC5633_HDR_CTRL0_MEAS_T 0x03
#define MMC5633_HDR_CTRL0_SET 0x05
#define MMC5633_HDR_CTRL0_RESET 0x07
enum mmc5633_axis {
MMC5633_AXIS_X,
MMC5633_AXIS_Y,
MMC5633_AXIS_Z,
MMC5633_TEMPERATURE,
};
struct mmc5633_data {
struct regmap *regmap;
struct i3c_device *i3cdev;
struct mutex mutex; /* protect to finish one whole measurement */
};
static int mmc5633_samp_freq[][2] = {
{ 1, 200000 },
{ 2, 0 },
{ 3, 500000 },
{ 6, 600000 },
};
#define MMC5633_CHANNEL(_axis) { \
.type = IIO_MAGN, \
.modified = 1, \
.channel2 = IIO_MOD_ ## _axis, \
.address = MMC5633_AXIS_ ## _axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec mmc5633_channels[] = {
MMC5633_CHANNEL(X),
MMC5633_CHANNEL(Y),
MMC5633_CHANNEL(Z),
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.address = MMC5633_TEMPERATURE,
},
};
static int mmc5633_get_samp_freq_index(struct mmc5633_data *data,
int val, int val2)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(mmc5633_samp_freq); i++)
if (mmc5633_samp_freq[i][0] == val &&
mmc5633_samp_freq[i][1] == val2)
return i;
return -EINVAL;
}
static int mmc5633_init(struct mmc5633_data *data)
{
unsigned int reg_id;
int ret;
ret = regmap_read(data->regmap, MMC5633_REG_ID, &reg_id);
if (ret)
return dev_err_probe(regmap_get_device(data->regmap), ret,
"Error reading product id\n");
/*
* Make sure we restore sensor characteristics, by doing
* a SET/RESET sequence, the axis polarity being naturally
* aligned after RESET.
*/
ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, MMC5633_CTRL0_SET);
if (ret)
return ret;
/*
* Minimum time interval between SET or RESET to other operations is
* 1ms according to Operating Timing Diagram in datasheet.
*/
fsleep(MMC5633_WAIT_SET_RESET_US);
ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, MMC5633_CTRL0_RESET);
if (ret)
return ret;
/* set default sampling frequency */
return regmap_update_bits(data->regmap, MMC5633_REG_CTRL1,
MMC5633_CTRL1_BW_MASK,
FIELD_PREP(MMC5633_CTRL1_BW_MASK, 0));
}
static int mmc5633_take_measurement(struct mmc5633_data *data, int address)
{
unsigned int reg_status, val;
int ret;
val = (address == MMC5633_TEMPERATURE) ? MMC5633_CTRL0_MEAS_T : MMC5633_CTRL0_MEAS_M;
ret = regmap_write(data->regmap, MMC5633_REG_CTRL0, val);
if (ret < 0)
return ret;
val = (address == MMC5633_TEMPERATURE) ?
MMC5633_STATUS1_MEAS_T_DONE_BIT : MMC5633_STATUS1_MEAS_M_DONE_BIT;
ret = regmap_read_poll_timeout(data->regmap, MMC5633_REG_STATUS1, reg_status,
reg_status & val,
10 * USEC_PER_MSEC,
100 * 10 * USEC_PER_MSEC);
if (ret) {
dev_err(regmap_get_device(data->regmap), "data not ready\n");
return ret;
}
return 0;
}
static bool mmc5633_is_support_hdr(struct mmc5633_data *data)
{
if (!data->i3cdev)
return false;
return i3c_device_get_supported_xfer_mode(data->i3cdev) & BIT(I3C_HDR_DDR);
}
static int mmc5633_read_measurement(struct mmc5633_data *data, int address, void *buf, size_t sz)
{
struct device *dev = regmap_get_device(data->regmap);
u8 data_cmd[2], status[2];
unsigned int val, ready;
int ret;
if (mmc5633_is_support_hdr(data)) {
struct i3c_xfer xfers_wr_cmd[] = {
{
.cmd = 0x3b,
.len = 2,
.data.out = data_cmd,
}
};
struct i3c_xfer xfers_rd_sta_cmd[] = {
{
.cmd = 0x23 | BIT(7), /* RDSTA CMD */
.len = 2,
.data.in = status,
},
};
struct i3c_xfer xfers_rd_data_cmd[] = {
{
.cmd = 0x22 | BIT(7), /* RDLONG CMD */
.len = sz,
.data.in = buf,
},
};
data_cmd[0] = 0;
data_cmd[1] = (address == MMC5633_TEMPERATURE) ?
MMC5633_HDR_CTRL0_MEAS_T : MMC5633_HDR_CTRL0_MEAS_M;
ret = i3c_device_do_xfers(data->i3cdev, xfers_wr_cmd,
ARRAY_SIZE(xfers_wr_cmd), I3C_HDR_DDR);
if (ret < 0)
return ret;
ready = (address == MMC5633_TEMPERATURE) ?
MMC5633_STATUS1_MEAS_T_DONE_BIT : MMC5633_STATUS1_MEAS_M_DONE_BIT;
ret = read_poll_timeout(i3c_device_do_xfers, val,
val || (status[0] & ready),
10 * USEC_PER_MSEC,
100 * 10 * USEC_PER_MSEC, 0,
data->i3cdev, xfers_rd_sta_cmd,
ARRAY_SIZE(xfers_rd_sta_cmd), I3C_HDR_DDR);
if (ret) {
dev_err(dev, "data not ready\n");
return ret;
}
if (val) {
dev_err(dev, "i3c transfer error\n");
return val;
}
return i3c_device_do_xfers(data->i3cdev, xfers_rd_data_cmd,
ARRAY_SIZE(xfers_rd_data_cmd), I3C_HDR_DDR);
}
/* Fallback to use SDR/I2C mode */
ret = mmc5633_take_measurement(data, address);
if (ret < 0)
return ret;
if (address == MMC5633_TEMPERATURE)
/*
* Put tempeature to last byte of buff to align HDR case.
* I3C will early terminate data read if previous data is not
* available.
*/
return regmap_bulk_read(data->regmap, MMC5633_REG_TOUT, buf + sz - 1, 1);
return regmap_bulk_read(data->regmap, MMC5633_REG_XOUT0, buf, sz);
}
/* X,Y,Z 3 channels, each channel has 3 byte and TEMP */
#define MMC5633_ALL_SIZE (3 * 3 + 1)
static int mmc5633_get_raw(struct mmc5633_data *data, int index, unsigned char *buf, int *val)
{
if (index == MMC5633_TEMPERATURE) {
*val = buf[MMC5633_ALL_SIZE - 1];
return 0;
}
/*
* X[19..12] X[11..4] Y[19..12] Y[11..4] Z[19..12] Z[11..4] X[3..0] Y[3..0] Z[3..0]
*/
*val = get_unaligned_be16(buf + 2 * index) << 4;
*val |= buf[index + 6] >> 4;
return 0;
}
static int mmc5633_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct mmc5633_data *data = iio_priv(indio_dev);
char buf[MMC5633_ALL_SIZE];
unsigned int reg, i;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
scoped_guard(mutex, &data->mutex) {
ret = mmc5633_read_measurement(data, chan->address, buf, MMC5633_ALL_SIZE);
if (ret < 0)
return ret;
}
ret = mmc5633_get_raw(data, chan->address, buf, val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_MAGN) {
*val = 0;
*val2 = 62500;
} else {
*val = 0;
*val2 = 800000000; /* 0.8C */
}
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_OFFSET:
if (chan->type == IIO_TEMP) {
*val = -75;
return IIO_VAL_INT;
}
return -EINVAL;
case IIO_CHAN_INFO_SAMP_FREQ:
scoped_guard(mutex, &data->mutex) {
ret = regmap_read(data->regmap, MMC5633_REG_CTRL1, &reg);
if (ret < 0)
return ret;
}
i = FIELD_GET(MMC5633_CTRL1_BW_MASK, reg);
if (i >= ARRAY_SIZE(mmc5633_samp_freq))
return -EINVAL;
*val = mmc5633_samp_freq[i][0];
*val2 = mmc5633_samp_freq[i][1];
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static int mmc5633_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct mmc5633_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ: {
ret = mmc5633_get_samp_freq_index(data, val, val2);
if (ret < 0)
return ret;
guard(mutex)(&data->mutex);
return regmap_update_bits(data->regmap, MMC5633_REG_CTRL1,
MMC5633_CTRL1_BW_MASK,
FIELD_PREP(MMC5633_CTRL1_BW_MASK, ret));
}
default:
return -EINVAL;
}
}
static int mmc5633_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_SAMP_FREQ:
*vals = (const int *)mmc5633_samp_freq;
*length = ARRAY_SIZE(mmc5633_samp_freq) * 2;
*type = IIO_VAL_INT_PLUS_MICRO;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static const struct iio_info mmc5633_info = {
.read_raw = mmc5633_read_raw,
.write_raw = mmc5633_write_raw,
.read_avail = mmc5633_read_avail,
};
static bool mmc5633_is_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MMC5633_REG_CTRL0:
case MMC5633_REG_CTRL1:
return true;
default:
return false;
}
}
static bool mmc5633_is_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MMC5633_REG_XOUT0:
case MMC5633_REG_XOUT1:
case MMC5633_REG_YOUT0:
case MMC5633_REG_YOUT1:
case MMC5633_REG_ZOUT0:
case MMC5633_REG_ZOUT1:
case MMC5633_REG_XOUT2:
case MMC5633_REG_YOUT2:
case MMC5633_REG_ZOUT2:
case MMC5633_REG_TOUT:
case MMC5633_REG_STATUS1:
case MMC5633_REG_ID:
return true;
default:
return false;
}
}
static bool mmc5633_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MMC5633_REG_CTRL0:
case MMC5633_REG_CTRL1:
return false;
default:
return true;
}
}
static const struct reg_default mmc5633_reg_defaults[] = {
{ MMC5633_REG_CTRL0, 0x00 },
{ MMC5633_REG_CTRL1, 0x00 },
};
static const struct regmap_config mmc5633_regmap_config = {
.name = "mmc5633_regmap",
.reg_bits = 8,
.val_bits = 8,
.max_register = MMC5633_REG_ID,
.cache_type = REGCACHE_MAPLE,
.writeable_reg = mmc5633_is_writeable_reg,
.readable_reg = mmc5633_is_readable_reg,
.volatile_reg = mmc5633_is_volatile_reg,
.reg_defaults = mmc5633_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(mmc5633_reg_defaults),
};
static int mmc5633_common_probe(struct regmap *regmap, char *name,
struct i3c_device *i3cdev)
{
struct device *dev = regmap_get_device(regmap);
struct mmc5633_data *data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->regmap = regmap;
data->i3cdev = i3cdev;
ret = devm_mutex_init(dev, &data->mutex);
if (ret)
return ret;
indio_dev->info = &mmc5633_info;
indio_dev->name = name;
indio_dev->channels = mmc5633_channels;
indio_dev->num_channels = ARRAY_SIZE(mmc5633_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
ret = mmc5633_init(data);
if (ret < 0)
return dev_err_probe(dev, ret, "mmc5633 chip init failed\n");
return devm_iio_device_register(dev, indio_dev);
}
static int mmc5633_suspend(struct device *dev)
{
struct regmap *regmap = dev_get_regmap(dev, NULL);
regcache_cache_only(regmap, true);
return 0;
}
static int mmc5633_resume(struct device *dev)
{
struct regmap *regmap = dev_get_regmap(dev, NULL);
int ret;
regcache_mark_dirty(regmap);
ret = regcache_sync_region(regmap, MMC5633_REG_CTRL0, MMC5633_REG_CTRL1);
if (ret)
dev_err(dev, "Failed to restore control registers\n");
regcache_cache_only(regmap, false);
return 0;
}
static int mmc5633_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct regmap *regmap;
regmap = devm_regmap_init_i2c(client, &mmc5633_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");
return mmc5633_common_probe(regmap, client->name, NULL);
}
static DEFINE_SIMPLE_DEV_PM_OPS(mmc5633_pm_ops, mmc5633_suspend, mmc5633_resume);
static const struct of_device_id mmc5633_of_match[] = {
{ .compatible = "memsic,mmc5603" },
{ .compatible = "memsic,mmc5633" },
{ }
};
MODULE_DEVICE_TABLE(of, mmc5633_of_match);
static const struct i2c_device_id mmc5633_i2c_id[] = {
{ "mmc5603" },
{ "mmc5633" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mmc5633_i2c_id);
static struct i2c_driver mmc5633_i2c_driver = {
.driver = {
.name = "mmc5633_i2c",
.of_match_table = mmc5633_of_match,
.pm = pm_sleep_ptr(&mmc5633_pm_ops),
},
.probe = mmc5633_i2c_probe,
.id_table = mmc5633_i2c_id,
};
static const struct i3c_device_id mmc5633_i3c_ids[] = {
I3C_DEVICE(0x0251, 0x0000, NULL),
{ }
};
MODULE_DEVICE_TABLE(i3c, mmc5633_i3c_ids);
static int mmc5633_i3c_probe(struct i3c_device *i3cdev)
{
struct device *dev = i3cdev_to_dev(i3cdev);
struct regmap *regmap;
char *name;
name = devm_kasprintf(dev, GFP_KERNEL, "mmc5633_%s", dev_name(dev));
if (!name)
return -ENOMEM;
regmap = devm_regmap_init_i3c(i3cdev, &mmc5633_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap),
"Failed to register i3c regmap\n");
return mmc5633_common_probe(regmap, name, i3cdev);
}
static struct i3c_driver mmc5633_i3c_driver = {
.driver = {
.name = "mmc5633_i3c",
},
.probe = mmc5633_i3c_probe,
.id_table = mmc5633_i3c_ids,
};
module_i3c_i2c_driver(mmc5633_i3c_driver, &mmc5633_i2c_driver)
MODULE_AUTHOR("Frank Li <Frank.li@nxp.com>");
MODULE_DESCRIPTION("MEMSIC MMC5633 magnetic sensor driver");
MODULE_LICENSE("GPL");

View File

@@ -16,6 +16,35 @@ config ABP060MG
To compile this driver as a module, choose M here: the module
will be called abp060mg.
config ABP2030PA
tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
config ABP2030PA_I2C
tristate "Honeywell ABP2 pressure sensor series I2C driver"
depends on I2C
select ABP2030PA
help
Say Y here to build I2C bus support for the Honeywell ABP2
series pressure and temperature digital sensor.
To compile this driver as a module, choose M here: the module
will be called abp2030pa_i2c and you will also get abp2030pa
for the core module.
config ABP2030PA_SPI
tristate "Honeywell ABP2 pressure sensor series SPI driver"
depends on SPI_MASTER
select ABP2030PA
help
Say Y here to build SPI bus support for the Honeywell ABP2
series pressure and temperature digital sensor.
To compile this driver as a module, choose M here: the module
will be called abp2030pa_spi and you will also get abp2030pa
for the core module.
config ROHM_BM1390
tristate "ROHM BM1390GLV-Z pressure sensor driver"
depends on I2C
@@ -187,29 +216,33 @@ config MPL3115
will be called mpl3115.
config MPRLS0025PA
tristate "Honeywell MPRLS0025PA (MicroPressure sensors series)"
depends on (I2C || SPI_MASTER)
select MPRLS0025PA_I2C if I2C
select MPRLS0025PA_SPI if SPI_MASTER
tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say Y here to build support for the Honeywell MicroPressure pressure
sensor series. There are many different types with different pressure
range. These ranges can be set up in the device tree.
To compile this driver as a module, choose M here: the module will be
called mprls0025pa.
config MPRLS0025PA_I2C
tristate
depends on MPRLS0025PA
tristate "Honeywell MPR pressure sensor series I2C driver"
depends on I2C
select MPRLS0025PA
help
Say Y here to build I2C bus support for the Honeywell MicroPressure
series sensor.
To compile this driver as a module, choose M here: the module
will be called mprls0025pa_i2c and you will also get mprls0025pa
for the core module.
config MPRLS0025PA_SPI
tristate
depends on MPRLS0025PA
tristate "Honeywell MPR pressure sensor series SPI driver"
depends on SPI_MASTER
select MPRLS0025PA
help
Say Y here to build SPI bus support for the Honeywell MicroPressure
series sensor.
To compile this driver as a module, choose M here: the module
will be called mprls0025pa_spi and you will also get mprls0025pa
for the core module.
config MS5611
tristate "Measurement Specialties MS5611 pressure sensor driver"

View File

@@ -5,6 +5,9 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_ABP060MG) += abp060mg.o
obj-$(CONFIG_ABP2030PA) += abp2030pa.o
obj-$(CONFIG_ABP2030PA_I2C) += abp2030pa_i2c.o
obj-$(CONFIG_ABP2030PA_SPI) += abp2030pa_spi.o
obj-$(CONFIG_ADP810) += adp810.o
obj-$(CONFIG_ROHM_BM1390) += rohm-bm1390.o
obj-$(CONFIG_BMP280) += bmp280.o

View File

@@ -0,0 +1,544 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Honeywell ABP2 series pressure sensor driver
*
* Copyright (c) 2025 Petre Rodan <petre.rodan@subdimension.ro>
*
* Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/basic-abp2-series/documents/sps-siot-abp2-series-datasheet-32350268-en.pdf
*/
#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/string.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/unaligned.h>
#include <linux/units.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include "abp2030pa.h"
/* Status byte flags */
#define ABP2_ST_POWER BIT(6) /* 1 if device is powered */
#define ABP2_ST_BUSY BIT(5) /* 1 if device is busy */
#define ABP2_CMD_NOP 0xf0
#define ABP2_CMD_SYNC 0xaa
#define ABP2_PKT_SYNC_LEN 3
#define ABP2_PKT_NOP_LEN ABP2_MEASUREMENT_RD_SIZE
struct abp2_func_spec {
u32 output_min;
u32 output_max;
};
/* transfer function A: 10% to 90% of 2^24 */
static const struct abp2_func_spec abp2_func_spec[] = {
[ABP2_FUNCTION_A] = { .output_min = 1677722, .output_max = 15099494 },
};
enum abp2_variants {
ABP2001BA, ABP21_6BA, ABP22_5BA, ABP2004BA, ABP2006BA, ABP2008BA,
ABP2010BA, ABP2012BA, ABP2001BD, ABP21_6BD, ABP22_5BD, ABP2004BD,
ABP2001BG, ABP21_6BG, ABP22_5BG, ABP2004BG, ABP2006BG, ABP2008BG,
ABP2010BG, ABP2012BG, ABP2001GG, ABP21_2GG, ABP2100KA, ABP2160KA,
ABP2250KA, ABP2001KD, ABP21_6KD, ABP22_5KD, ABP2004KD, ABP2006KD,
ABP2010KD, ABP2016KD, ABP2025KD, ABP2040KD, ABP2060KD, ABP2100KD,
ABP2160KD, ABP2250KD, ABP2400KD, ABP2001KG, ABP21_6KG, ABP22_5KG,
ABP2004KG, ABP2006KG, ABP2010KG, ABP2016KG, ABP2025KG, ABP2040KG,
ABP2060KG, ABP2100KG, ABP2160KG, ABP2250KG, ABP2400KG, ABP2600KG,
ABP2800KG, ABP2250LD, ABP2600LD, ABP2600LG, ABP22_5MD, ABP2006MD,
ABP2010MD, ABP2016MD, ABP2025MD, ABP2040MD, ABP2060MD, ABP2100MD,
ABP2160MD, ABP2250MD, ABP2400MD, ABP2600MD, ABP2006MG, ABP2010MG,
ABP2016MG, ABP2025MG, ABP2040MG, ABP2060MG, ABP2100MG, ABP2160MG,
ABP2250MG, ABP2400MG, ABP2600MG, ABP2001ND, ABP2002ND, ABP2004ND,
ABP2005ND, ABP2010ND, ABP2020ND, ABP2030ND, ABP2002NG, ABP2004NG,
ABP2005NG, ABP2010NG, ABP2020NG, ABP2030NG, ABP2015PA, ABP2030PA,
ABP2060PA, ABP2100PA, ABP2150PA, ABP2175PA, ABP2001PD, ABP2005PD,
ABP2015PD, ABP2030PD, ABP2060PD, ABP2001PG, ABP2005PG, ABP2015PG,
ABP2030PG, ABP2060PG, ABP2100PG, ABP2150PG, ABP2175PG,
};
static const char * const abp2_triplet_variants[] = {
[ABP2001BA] = "001BA", [ABP21_6BA] = "1.6BA", [ABP22_5BA] = "2.5BA",
[ABP2004BA] = "004BA", [ABP2006BA] = "006BA", [ABP2008BA] = "008BA",
[ABP2010BA] = "010BA", [ABP2012BA] = "012BA", [ABP2001BD] = "001BD",
[ABP21_6BD] = "1.6BD", [ABP22_5BD] = "2.5BD", [ABP2004BD] = "004BD",
[ABP2001BG] = "001BG", [ABP21_6BG] = "1.6BG", [ABP22_5BG] = "2.5BG",
[ABP2004BG] = "004BG", [ABP2006BG] = "006BG", [ABP2008BG] = "008BG",
[ABP2010BG] = "010BG", [ABP2012BG] = "012BG", [ABP2001GG] = "001GG",
[ABP21_2GG] = "1.2GG", [ABP2100KA] = "100KA", [ABP2160KA] = "160KA",
[ABP2250KA] = "250KA", [ABP2001KD] = "001KD", [ABP21_6KD] = "1.6KD",
[ABP22_5KD] = "2.5KD", [ABP2004KD] = "004KD", [ABP2006KD] = "006KD",
[ABP2010KD] = "010KD", [ABP2016KD] = "016KD", [ABP2025KD] = "025KD",
[ABP2040KD] = "040KD", [ABP2060KD] = "060KD", [ABP2100KD] = "100KD",
[ABP2160KD] = "160KD", [ABP2250KD] = "250KD", [ABP2400KD] = "400KD",
[ABP2001KG] = "001KG", [ABP21_6KG] = "1.6KG", [ABP22_5KG] = "2.5KG",
[ABP2004KG] = "004KG", [ABP2006KG] = "006KG", [ABP2010KG] = "010KG",
[ABP2016KG] = "016KG", [ABP2025KG] = "025KG", [ABP2040KG] = "040KG",
[ABP2060KG] = "060KG", [ABP2100KG] = "100KG", [ABP2160KG] = "160KG",
[ABP2250KG] = "250KG", [ABP2400KG] = "400KG", [ABP2600KG] = "600KG",
[ABP2800KG] = "800KG", [ABP2250LD] = "250LD", [ABP2600LD] = "600LD",
[ABP2600LG] = "600LG", [ABP22_5MD] = "2.5MD", [ABP2006MD] = "006MD",
[ABP2010MD] = "010MD", [ABP2016MD] = "016MD", [ABP2025MD] = "025MD",
[ABP2040MD] = "040MD", [ABP2060MD] = "060MD", [ABP2100MD] = "100MD",
[ABP2160MD] = "160MD", [ABP2250MD] = "250MD", [ABP2400MD] = "400MD",
[ABP2600MD] = "600MD", [ABP2006MG] = "006MG", [ABP2010MG] = "010MG",
[ABP2016MG] = "016MG", [ABP2025MG] = "025MG", [ABP2040MG] = "040MG",
[ABP2060MG] = "060MG", [ABP2100MG] = "100MG", [ABP2160MG] = "160MG",
[ABP2250MG] = "250MG", [ABP2400MG] = "400MG", [ABP2600MG] = "600MG",
[ABP2001ND] = "001ND", [ABP2002ND] = "002ND", [ABP2004ND] = "004ND",
[ABP2005ND] = "005ND", [ABP2010ND] = "010ND", [ABP2020ND] = "020ND",
[ABP2030ND] = "030ND", [ABP2002NG] = "002NG", [ABP2004NG] = "004NG",
[ABP2005NG] = "005NG", [ABP2010NG] = "010NG", [ABP2020NG] = "020NG",
[ABP2030NG] = "030NG", [ABP2015PA] = "015PA", [ABP2030PA] = "030PA",
[ABP2060PA] = "060PA", [ABP2100PA] = "100PA", [ABP2150PA] = "150PA",
[ABP2175PA] = "175PA", [ABP2001PD] = "001PD", [ABP2005PD] = "005PD",
[ABP2015PD] = "015PD", [ABP2030PD] = "030PD", [ABP2060PD] = "060PD",
[ABP2001PG] = "001PG", [ABP2005PG] = "005PG", [ABP2015PG] = "015PG",
[ABP2030PG] = "030PG", [ABP2060PG] = "060PG", [ABP2100PG] = "100PG",
[ABP2150PG] = "150PG", [ABP2175PG] = "175PG",
};
/**
* struct abp2_range_config - list of pressure ranges based on nomenclature
* @pmin: lowest pressure that can be measured
* @pmax: highest pressure that can be measured
*/
struct abp2_range_config {
s32 pmin;
s32 pmax;
};
/* All min max limits have been converted to pascals */
static const struct abp2_range_config abp2_range_config[] = {
[ABP2001BA] = { .pmin = 0, .pmax = 100000 },
[ABP21_6BA] = { .pmin = 0, .pmax = 160000 },
[ABP22_5BA] = { .pmin = 0, .pmax = 250000 },
[ABP2004BA] = { .pmin = 0, .pmax = 400000 },
[ABP2006BA] = { .pmin = 0, .pmax = 600000 },
[ABP2008BA] = { .pmin = 0, .pmax = 800000 },
[ABP2010BA] = { .pmin = 0, .pmax = 1000000 },
[ABP2012BA] = { .pmin = 0, .pmax = 1200000 },
[ABP2001BD] = { .pmin = -100000, .pmax = 100000 },
[ABP21_6BD] = { .pmin = -160000, .pmax = 160000 },
[ABP22_5BD] = { .pmin = -250000, .pmax = 250000 },
[ABP2004BD] = { .pmin = -400000, .pmax = 400000 },
[ABP2001BG] = { .pmin = 0, .pmax = 100000 },
[ABP21_6BG] = { .pmin = 0, .pmax = 160000 },
[ABP22_5BG] = { .pmin = 0, .pmax = 250000 },
[ABP2004BG] = { .pmin = 0, .pmax = 400000 },
[ABP2006BG] = { .pmin = 0, .pmax = 600000 },
[ABP2008BG] = { .pmin = 0, .pmax = 800000 },
[ABP2010BG] = { .pmin = 0, .pmax = 1000000 },
[ABP2012BG] = { .pmin = 0, .pmax = 1200000 },
[ABP2001GG] = { .pmin = 0, .pmax = 1000000 },
[ABP21_2GG] = { .pmin = 0, .pmax = 1200000 },
[ABP2100KA] = { .pmin = 0, .pmax = 100000 },
[ABP2160KA] = { .pmin = 0, .pmax = 160000 },
[ABP2250KA] = { .pmin = 0, .pmax = 250000 },
[ABP2001KD] = { .pmin = -1000, .pmax = 1000 },
[ABP21_6KD] = { .pmin = -1600, .pmax = 1600 },
[ABP22_5KD] = { .pmin = -2500, .pmax = 2500 },
[ABP2004KD] = { .pmin = -4000, .pmax = 4000 },
[ABP2006KD] = { .pmin = -6000, .pmax = 6000 },
[ABP2010KD] = { .pmin = -10000, .pmax = 10000 },
[ABP2016KD] = { .pmin = -16000, .pmax = 16000 },
[ABP2025KD] = { .pmin = -25000, .pmax = 25000 },
[ABP2040KD] = { .pmin = -40000, .pmax = 40000 },
[ABP2060KD] = { .pmin = -60000, .pmax = 60000 },
[ABP2100KD] = { .pmin = -100000, .pmax = 100000 },
[ABP2160KD] = { .pmin = -160000, .pmax = 160000 },
[ABP2250KD] = { .pmin = -250000, .pmax = 250000 },
[ABP2400KD] = { .pmin = -400000, .pmax = 400000 },
[ABP2001KG] = { .pmin = 0, .pmax = 1000 },
[ABP21_6KG] = { .pmin = 0, .pmax = 1600 },
[ABP22_5KG] = { .pmin = 0, .pmax = 2500 },
[ABP2004KG] = { .pmin = 0, .pmax = 4000 },
[ABP2006KG] = { .pmin = 0, .pmax = 6000 },
[ABP2010KG] = { .pmin = 0, .pmax = 10000 },
[ABP2016KG] = { .pmin = 0, .pmax = 16000 },
[ABP2025KG] = { .pmin = 0, .pmax = 25000 },
[ABP2040KG] = { .pmin = 0, .pmax = 40000 },
[ABP2060KG] = { .pmin = 0, .pmax = 60000 },
[ABP2100KG] = { .pmin = 0, .pmax = 100000 },
[ABP2160KG] = { .pmin = 0, .pmax = 160000 },
[ABP2250KG] = { .pmin = 0, .pmax = 250000 },
[ABP2400KG] = { .pmin = 0, .pmax = 400000 },
[ABP2600KG] = { .pmin = 0, .pmax = 600000 },
[ABP2800KG] = { .pmin = 0, .pmax = 800000 },
[ABP2250LD] = { .pmin = -250, .pmax = 250 },
[ABP2600LD] = { .pmin = -600, .pmax = 600 },
[ABP2600LG] = { .pmin = 0, .pmax = 600 },
[ABP22_5MD] = { .pmin = -250, .pmax = 250 },
[ABP2006MD] = { .pmin = -600, .pmax = 600 },
[ABP2010MD] = { .pmin = -1000, .pmax = 1000 },
[ABP2016MD] = { .pmin = -1600, .pmax = 1600 },
[ABP2025MD] = { .pmin = -2500, .pmax = 2500 },
[ABP2040MD] = { .pmin = -4000, .pmax = 4000 },
[ABP2060MD] = { .pmin = -6000, .pmax = 6000 },
[ABP2100MD] = { .pmin = -10000, .pmax = 10000 },
[ABP2160MD] = { .pmin = -16000, .pmax = 16000 },
[ABP2250MD] = { .pmin = -25000, .pmax = 25000 },
[ABP2400MD] = { .pmin = -40000, .pmax = 40000 },
[ABP2600MD] = { .pmin = -60000, .pmax = 60000 },
[ABP2006MG] = { .pmin = 0, .pmax = 600 },
[ABP2010MG] = { .pmin = 0, .pmax = 1000 },
[ABP2016MG] = { .pmin = 0, .pmax = 1600 },
[ABP2025MG] = { .pmin = 0, .pmax = 2500 },
[ABP2040MG] = { .pmin = 0, .pmax = 4000 },
[ABP2060MG] = { .pmin = 0, .pmax = 6000 },
[ABP2100MG] = { .pmin = 0, .pmax = 10000 },
[ABP2160MG] = { .pmin = 0, .pmax = 16000 },
[ABP2250MG] = { .pmin = 0, .pmax = 25000 },
[ABP2400MG] = { .pmin = 0, .pmax = 40000 },
[ABP2600MG] = { .pmin = 0, .pmax = 60000 },
[ABP2001ND] = { .pmin = -249, .pmax = 249 },
[ABP2002ND] = { .pmin = -498, .pmax = 498 },
[ABP2004ND] = { .pmin = -996, .pmax = 996 },
[ABP2005ND] = { .pmin = -1245, .pmax = 1245 },
[ABP2010ND] = { .pmin = -2491, .pmax = 2491 },
[ABP2020ND] = { .pmin = -4982, .pmax = 4982 },
[ABP2030ND] = { .pmin = -7473, .pmax = 7473 },
[ABP2002NG] = { .pmin = 0, .pmax = 498 },
[ABP2004NG] = { .pmin = 0, .pmax = 996 },
[ABP2005NG] = { .pmin = 0, .pmax = 1245 },
[ABP2010NG] = { .pmin = 0, .pmax = 2491 },
[ABP2020NG] = { .pmin = 0, .pmax = 4982 },
[ABP2030NG] = { .pmin = 0, .pmax = 7473 },
[ABP2015PA] = { .pmin = 0, .pmax = 103421 },
[ABP2030PA] = { .pmin = 0, .pmax = 206843 },
[ABP2060PA] = { .pmin = 0, .pmax = 413685 },
[ABP2100PA] = { .pmin = 0, .pmax = 689476 },
[ABP2150PA] = { .pmin = 0, .pmax = 1034214 },
[ABP2175PA] = { .pmin = 0, .pmax = 1206583 },
[ABP2001PD] = { .pmin = -6895, .pmax = 6895 },
[ABP2005PD] = { .pmin = -34474, .pmax = 34474 },
[ABP2015PD] = { .pmin = -103421, .pmax = 103421 },
[ABP2030PD] = { .pmin = -206843, .pmax = 206843 },
[ABP2060PD] = { .pmin = -413685, .pmax = 413685 },
[ABP2001PG] = { .pmin = 0, .pmax = 6895 },
[ABP2005PG] = { .pmin = 0, .pmax = 34474 },
[ABP2015PG] = { .pmin = 0, .pmax = 103421 },
[ABP2030PG] = { .pmin = 0, .pmax = 206843 },
[ABP2060PG] = { .pmin = 0, .pmax = 413685 },
[ABP2100PG] = { .pmin = 0, .pmax = 689476 },
[ABP2150PG] = { .pmin = 0, .pmax = 1034214 },
[ABP2175PG] = { .pmin = 0, .pmax = 1206583 },
};
static_assert(ARRAY_SIZE(abp2_triplet_variants) == ARRAY_SIZE(abp2_range_config));
static int abp2_get_measurement(struct abp2_data *data)
{
struct device *dev = data->dev;
int ret;
reinit_completion(&data->completion);
ret = data->ops->write(data, ABP2_CMD_SYNC, ABP2_PKT_SYNC_LEN);
if (ret < 0)
return ret;
if (data->irq > 0) {
ret = wait_for_completion_timeout(&data->completion, HZ);
if (!ret) {
dev_err(dev, "timeout waiting for EOC interrupt\n");
return -ETIMEDOUT;
}
} else {
fsleep(5 * USEC_PER_MSEC);
}
memset(data->rx_buf, 0, sizeof(data->rx_buf));
ret = data->ops->read(data, ABP2_CMD_NOP, ABP2_PKT_NOP_LEN);
if (ret < 0)
return ret;
/*
* Status byte flags
* bit7 SANITY_CHK - must always be 0
* bit6 ABP2_ST_POWER - 1 if device is powered
* bit5 ABP2_ST_BUSY - 1 if device has no new conversion ready
* bit4 SANITY_CHK - must always be 0
* bit3 SANITY_CHK - must always be 0
* bit2 MEMORY_ERR - 1 if integrity test has failed
* bit1 SANITY_CHK - must always be 0
* bit0 MATH_ERR - 1 during internal math saturation error
*/
if (data->rx_buf[0] == (ABP2_ST_POWER | ABP2_ST_BUSY))
return -EBUSY;
/*
* The ABP2 sensor series seem to have a noticeable latch-up sensitivity.
* A partial latch-up condition manifests as either
* - output of invalid status bytes
* - zeroed out conversions (despite a normal status byte)
* - the MOSI line being pulled low randomly in sync with the SCLK
* signal (visible during the ABP2_CMD_NOP command).
* https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1588325/am3358-spi-tx-data-corruption
*/
if (data->rx_buf[0] != ABP2_ST_POWER) {
dev_err(data->dev,
"unexpected status byte 0x%02x\n", data->rx_buf[0]);
return -EIO;
}
return 0;
}
static irqreturn_t abp2_eoc_handler(int irq, void *private)
{
struct abp2_data *data = private;
complete(&data->completion);
return IRQ_HANDLED;
}
static irqreturn_t abp2_trigger_handler(int irq, void *private)
{
int ret;
struct iio_poll_func *pf = private;
struct iio_dev *indio_dev = pf->indio_dev;
struct abp2_data *data = iio_priv(indio_dev);
ret = abp2_get_measurement(data);
if (ret < 0)
goto out_notify_done;
data->scan.chan[0] = get_unaligned_be24(&data->rx_buf[1]);
data->scan.chan[1] = get_unaligned_be24(&data->rx_buf[4]);
iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan),
iio_get_time_ns(indio_dev));
out_notify_done:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
/*
* IIO ABI expects
* value = (conv + offset) * scale
*
* temp[C] = conv * a + b
* where a = 200/16777215; b = -50
*
* temp[C] = (conv + (b/a)) * a * (1000)
* =>
* scale = a * 1000 = .0000119209296 * 1000 = .01192092966562
* offset = b/a = -50 * 16777215 / 200 = -4194303.75
*
* pressure = (conv - Omin) * Q + Pmin =
* ((conv - Omin) + Pmin/Q) * Q
* =>
* scale = Q = (Pmax - Pmin) / (Omax - Omin)
* offset = Pmin/Q - Omin = Pmin * (Omax - Omin) / (Pmax - Pmin) - Omin
*/
static int abp2_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *channel, int *val,
int *val2, long mask)
{
struct abp2_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = abp2_get_measurement(data);
if (ret < 0)
return ret;
switch (channel->type) {
case IIO_PRESSURE:
*val = get_unaligned_be24(&data->rx_buf[1]);
return IIO_VAL_INT;
case IIO_TEMP:
*val = get_unaligned_be24(&data->rx_buf[4]);
return IIO_VAL_INT;
default:
return -EINVAL;
}
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
switch (channel->type) {
case IIO_TEMP:
*val = 0;
*val2 = 11920929;
return IIO_VAL_INT_PLUS_NANO;
case IIO_PRESSURE:
*val = data->p_scale;
*val2 = data->p_scale_dec;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_OFFSET:
switch (channel->type) {
case IIO_TEMP:
*val = -4194304;
return IIO_VAL_INT;
case IIO_PRESSURE:
*val = data->p_offset;
return IIO_VAL_INT;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static const struct iio_info abp2_info = {
.read_raw = &abp2_read_raw,
};
static const unsigned long abp2_scan_masks[] = {0x3, 0};
static const struct iio_chan_spec abp2_channels[] = {
{
.type = IIO_PRESSURE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.scan_index = 0,
.scan_type = {
.sign = 'u',
.realbits = 24,
.storagebits = 32,
.endianness = IIO_CPU,
},
},
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.scan_index = 1,
.scan_type = {
.sign = 'u',
.realbits = 24,
.storagebits = 32,
.endianness = IIO_CPU,
},
},
IIO_CHAN_SOFT_TIMESTAMP(2),
};
int abp2_common_probe(struct device *dev, const struct abp2_ops *ops, int irq)
{
int ret;
struct abp2_data *data;
struct iio_dev *indio_dev;
const char *triplet;
s32 tmp;
s64 odelta, pdelta;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->dev = dev;
data->ops = ops;
data->irq = irq;
init_completion(&data->completion);
indio_dev->name = "abp2030pa";
indio_dev->info = &abp2_info;
indio_dev->channels = abp2_channels;
indio_dev->num_channels = ARRAY_SIZE(abp2_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->available_scan_masks = abp2_scan_masks;
ret = devm_regulator_get_enable(dev, "vdd");
if (ret)
return dev_err_probe(dev, ret, "can't get and enable vdd supply\n");
ret = device_property_read_string(dev, "honeywell,pressure-triplet",
&triplet);
if (ret) {
ret = device_property_read_u32(dev, "honeywell,pmin-pascal",
&data->pmin);
if (ret)
return dev_err_probe(dev, ret,
"honeywell,pmin-pascal could not be read\n");
ret = device_property_read_u32(dev, "honeywell,pmax-pascal",
&data->pmax);
if (ret)
return dev_err_probe(dev, ret,
"honeywell,pmax-pascal could not be read\n");
} else {
ret = device_property_match_property_string(dev,
"honeywell,pressure-triplet",
abp2_triplet_variants,
ARRAY_SIZE(abp2_triplet_variants));
if (ret < 0)
return dev_err_probe(dev, -EINVAL, "honeywell,pressure-triplet is invalid\n");
data->pmin = abp2_range_config[ret].pmin;
data->pmax = abp2_range_config[ret].pmax;
}
if (data->pmin >= data->pmax)
return dev_err_probe(dev, -EINVAL, "pressure limits are invalid\n");
data->outmin = abp2_func_spec[data->function].output_min;
data->outmax = abp2_func_spec[data->function].output_max;
odelta = data->outmax - data->outmin;
pdelta = data->pmax - data->pmin;
data->p_scale = div_s64_rem(div_s64(pdelta * NANO, odelta), NANO, &tmp);
data->p_scale_dec = tmp;
data->p_offset = div_s64(odelta * data->pmin, pdelta) - data->outmin;
if (data->irq > 0) {
ret = devm_request_irq(dev, irq, abp2_eoc_handler, IRQF_ONESHOT,
dev_name(dev), data);
if (ret)
return ret;
}
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
abp2_trigger_handler, NULL);
if (ret)
return dev_err_probe(dev, ret, "iio triggered buffer setup failed\n");
ret = devm_iio_device_register(dev, indio_dev);
if (ret)
return dev_err_probe(dev, ret, "unable to register iio device\n");
return 0;
}
EXPORT_SYMBOL_NS_GPL(abp2_common_probe, "IIO_HONEYWELL_ABP2030PA");
MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
MODULE_DESCRIPTION("Honeywell ABP2 pressure sensor core driver");
MODULE_LICENSE("GPL");

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