IIO: 1st set of new device support, features and cleanup for 6.9

IIO Backend support
 ===================
 
 New approach from Nuno Sa to the problem of reuse of drivers with
 IIO devices that are actually the combination of a highspeed chip
 and an FPGA core handling the data capture and flows. It will hopefully
 also apply to some other split designs. The ad9467 and axi-adi drivers
 are converted over to this framework.
 
 New device support
 ==================
 
 adi,admfm2000
 - New driver for this dual microwave down converter.
 ams,as73211
 - Add support for as7331 UV sensor.
 richtek,rtq6056
 - Add support for related parts RTQ6053 and RTQ6059
 st,lsm6dsx
 - Add ASM330LHHXG1 accelerometer and gyro support (mainly IDs)
 ti,ads1298
 - New driver for this medical ADC.
 
 Features
 ========
 
 tests
 - Unit tests for the gain-time-scale helper library.
 bosch,bmi088
 - I2C support.
 bosh,bmi160
 - Add 10EC5280 ACPI ID. Used in a number of devices that won't get fixed.
   The ID is actually a PCI ID belonging to realtech. No response was received
   to earlier attempts to notify them of this.
   The manufacturers of some devices have replied to say they will not fix
   this incorrect ID. Add the ID and hope it isn't a problem.
 bosch,bmi323
 - Add BOSC0200 ACPI ID. Note this is a duplicate of one in the bmc150
   driver (it appears these parts share a windows driver).
   Both drivers perform an ID check that is safe on the other part before
   successfully probing.
 hid-sensors-als
 - Add color temperature and chromaticity support. Note this is a replacement
   for the series reverted in 6.8 that correctly handles all the potential
   channel combinations.
 honeywell,hsc030pa
 - Triggered buffer support (after driver cleanup).
 honeywell,mprls00025pa
 - Improved error handling.
 - New DT binding to allow use of part number triplet as provided in data sheet
   to specify equivalent of most of the binding more efficiently.
 - SPI support.
 memsic,mxc4005
 - ACPI ID MDA6655 as seen in the Chuwi Minibook X 2023
 ti,hdc3020
 - Add threshold event support (after some driver cleanup)
 veml,vcnl4000
 - Switch to high resolution proximity measurement.
 
 Cleanup
 =======
 Various minor typo fixes and better use of defines etc.
 
 Treewide
 - Stop using ACPI_PTR(). The savings in space are small and not worth
   the complexity of __maybe_unused of ifdef guards.  To avoid use in
   new IIO drivers based on copy and paste, clean it out.
 - cleanup.h based handling of iio_device_claim_direct_mode()/
   iio_device_release_direct_mode() using scope_cond_guard().
   In many drivers this is combined with other automated cleanup
   to give maximum simplifications.
   An initial set of drivers are converted over to this infrastructure.
 
 Tools
 - Use rewinddir() instead of seekdir() to return to start of file.
 
 core
 - Make iio_bus_type constant.
 
 adi,ad16475
 - Use irq_get_trigger_type() instead of opencoding.
 adi,ad16480
 - Use irq_get_trigger_type() instead of opencoding.
 adi,ad-sigma-delta
 - Avoid overwriting IRQ flags if provided by firmware.
 ams,as73211
 - Use IIO_VAL_FRACTIONAL for scales to simplify the code and potentially
   improve accuracy.
 gts-library
 - Use a div64_u64() instead of a loop to do a division.
 honeywell,mprls00025pa
 - Clean up dt-binding doc.
 - Drop defaults when DT binding not providing values. Very unlikely
   these were useful given they were wrong for vast majority of supported
   devices.
 - Whitespace cleanup
 miramems,da280
 - Use i2c_get_match_data() to replace hand rolled ACPI matching code.
 semtech,sx9324
 - Avoid unnecessary copying of property strings.
 st,lsm6dsx
 - Improve docs, particularly wrt to making addition of new device
   support less noisy.
 st,lsm9ds0
 - Use dev_err_probe() in all probe() error handling.
 - Improved header includes.
 - Tidy up termination of ID tables.
 ti,ads1014
 - Correct upper bound on PGA (wrong value had no actual impact)
 ti,afe4403/4404
 - devm_ useage to simplify error handling in probe() and allow() remove to
   be dropped.
 voltage-divider
 - Add dt-binding for io-channel-cells to allow such a device to be both
   an IIO consumer and IIO producer at the same time.
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAmXXmXERHGppYzIzQGtl
 cm5lbC5vcmcACgkQVIU0mcT0Foj8Bg/+OZgU0FOEhUXidF58ulMBPTmWG4qBsCCK
 nB8MhimNn9XcoUDkZwns5ABgRKJvYVqK3VXaGj7HXRcpHzjOAHDCD1aS9EDdwHuo
 prH9Q5L7iVMhOjKcK3UwtzbUOAjrrmE4Oa74RH4oDLKV9ws6mb1VQfoOgpRNTsTK
 rA0A3B4W78U+UxLZPWIuYDUS/BgDfSfB024VFtUhMvo0B6G52u6hoDk04//hQuW8
 IE0db4VxJLuRDYinBiTbtFtXWO8FNWd8r37MlQKx/llEKtjzI1ZttOK8DyQRudJ7
 rLP3nO8svCcQ9oJn0YrnFL9y6PlW/ASUaXH47xBfAiCdKgHlQTf3kDyzwddCqQRD
 3cqghbUbYp65Nm1o2TXYQio4zEInS/1ZMzpezb9K1+oiz/xGarjMCR+J4Za4cw76
 5jCfnp8vBPDX7JdGwYgbK9nXkXAJ5ewdm7ad03t3oPwfAQPlXfAHIWDT8evNDVlc
 L94RSmKeMKL1BG3jOr7mJbqIwivf799pgudVJMuBfnlYR+gkBBVvFWhXrgvsJWhQ
 gKhqfEYCjtJ/t483zcJporvWBmmmf6gUvNOB4x6oA0j85GPGoDlTdfK54dkD+98k
 KP83Wd4LqDOM6V6PE+L7yTW05sOgT2qDHYrw/qAPoEogThSSCoqRm/kWwPBv3toL
 0ykQId5/UBk=
 =xkgf
 -----END PGP SIGNATURE-----

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

Jonathan writes:

IIO: 1st set of new device support, features and cleanup for 6.9

IIO Backend support
===================

New approach from Nuno Sa to the problem of reuse of drivers with
IIO devices that are actually the combination of a highspeed chip
and an FPGA core handling the data capture and flows. It will hopefully
also apply to some other split designs. The ad9467 and axi-adi drivers
are converted over to this framework.

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

adi,admfm2000
- New driver for this dual microwave down converter.
ams,as73211
- Add support for as7331 UV sensor.
richtek,rtq6056
- Add support for related parts RTQ6053 and RTQ6059
st,lsm6dsx
- Add ASM330LHHXG1 accelerometer and gyro support (mainly IDs)
ti,ads1298
- New driver for this medical ADC.

Features
========

tests
- Unit tests for the gain-time-scale helper library.
bosch,bmi088
- I2C support.
bosh,bmi160
- Add 10EC5280 ACPI ID. Used in a number of devices that won't get fixed.
  The ID is actually a PCI ID belonging to realtech. No response was received
  to earlier attempts to notify them of this.
  The manufacturers of some devices have replied to say they will not fix
  this incorrect ID. Add the ID and hope it isn't a problem.
bosch,bmi323
- Add BOSC0200 ACPI ID. Note this is a duplicate of one in the bmc150
  driver (it appears these parts share a windows driver).
  Both drivers perform an ID check that is safe on the other part before
  successfully probing.
hid-sensors-als
- Add color temperature and chromaticity support. Note this is a replacement
  for the series reverted in 6.8 that correctly handles all the potential
  channel combinations.
honeywell,hsc030pa
- Triggered buffer support (after driver cleanup).
honeywell,mprls00025pa
- Improved error handling.
- New DT binding to allow use of part number triplet as provided in data sheet
  to specify equivalent of most of the binding more efficiently.
- SPI support.
memsic,mxc4005
- ACPI ID MDA6655 as seen in the Chuwi Minibook X 2023
ti,hdc3020
- Add threshold event support (after some driver cleanup)
veml,vcnl4000
- Switch to high resolution proximity measurement.

Cleanup
=======
Various minor typo fixes and better use of defines etc.

Treewide
- Stop using ACPI_PTR(). The savings in space are small and not worth
  the complexity of __maybe_unused of ifdef guards.  To avoid use in
  new IIO drivers based on copy and paste, clean it out.
- cleanup.h based handling of iio_device_claim_direct_mode()/
  iio_device_release_direct_mode() using scope_cond_guard().
  In many drivers this is combined with other automated cleanup
  to give maximum simplifications.
  An initial set of drivers are converted over to this infrastructure.

Tools
- Use rewinddir() instead of seekdir() to return to start of file.

core
- Make iio_bus_type constant.

adi,ad16475
- Use irq_get_trigger_type() instead of opencoding.
adi,ad16480
- Use irq_get_trigger_type() instead of opencoding.
adi,ad-sigma-delta
- Avoid overwriting IRQ flags if provided by firmware.
ams,as73211
- Use IIO_VAL_FRACTIONAL for scales to simplify the code and potentially
  improve accuracy.
gts-library
- Use a div64_u64() instead of a loop to do a division.
honeywell,mprls00025pa
- Clean up dt-binding doc.
- Drop defaults when DT binding not providing values. Very unlikely
  these were useful given they were wrong for vast majority of supported
  devices.
- Whitespace cleanup
miramems,da280
- Use i2c_get_match_data() to replace hand rolled ACPI matching code.
semtech,sx9324
- Avoid unnecessary copying of property strings.
st,lsm6dsx
- Improve docs, particularly wrt to making addition of new device
  support less noisy.
st,lsm9ds0
- Use dev_err_probe() in all probe() error handling.
- Improved header includes.
- Tidy up termination of ID tables.
ti,ads1014
- Correct upper bound on PGA (wrong value had no actual impact)
ti,afe4403/4404
- devm_ useage to simplify error handling in probe() and allow() remove to
  be dropped.
voltage-divider
- Add dt-binding for io-channel-cells to allow such a device to be both
  an IIO consumer and IIO producer at the same time.

* tag 'iio-for-6.9a' of http://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (106 commits)
  iio: imu: bmi323: Add ACPI Match Table
  iio: accel: bmc150: Document duplicate ACPI entries with bmi323 driver
  iio: adc: ti-ads1298: Add driver
  dt-bindings: iio: adc: ti-ads1298: Add bindings
  iio: pressure: hsc030pa add triggered buffer
  iio: pressure: hsc030pa add mandatory delay
  iio: pressure: hsc030pa: update datasheet URLs
  iio: pressure: hsc030pa: include cleanup
  iio: pressure: hsc030pa: use signed type to hold div_64() result
  dt-bindings: iio: pressure: honeywell,hsc030pa.yaml add spi props
  iio: st_sensors: lsm9ds0: Use common style for terminator in ID tables
  iio: st_sensors: lsm9ds0: Don't use "proxy" headers
  iio: st_sensors: lsm9ds0: Use dev_err_probe() everywhere
  iio: adc: adi-axi-adc: move to backend framework
  iio: adc: ad9467: convert to backend framework
  iio: add the IIO backend framework
  iio: buffer-dmaengine: export buffer alloc and free functions
  of: property: add device link support for io-backends
  dt-bindings: adc: axi-adc: update bindings for backend framework
  dt-bindings: adc: ad9467: add new io-backend property
  ...
This commit is contained in:
Greg Kroah-Hartman 2024-02-25 14:11:41 +01:00
commit d4551c189d
112 changed files with 5051 additions and 1888 deletions

View File

@ -44,6 +44,9 @@ properties:
Pin that controls the powerdown mode of the device.
maxItems: 1
io-backends:
maxItems: 1
reset-gpios:
description:
Reset pin for the device.
@ -68,6 +71,7 @@ examples:
reg = <0>;
clocks = <&adc_clk>;
clock-names = "adc-clk";
io-backends = <&iio_backend>;
};
};
...

View File

@ -39,12 +39,15 @@ properties:
$ref: /schemas/types.yaml#/definitions/phandle
description:
A reference to a the actual ADC to which this FPGA ADC interfaces to.
deprecated: true
'#io-backend-cells':
const: 0
required:
- compatible
- dmas
- reg
- adi,adc-dev
additionalProperties: false
@ -55,7 +58,6 @@ examples:
reg = <0x44a00000 0x10000>;
dmas = <&rx_dma 0>;
dma-names = "rx";
adi,adc-dev = <&spi_adc>;
#io-backend-cells = <0>;
};
...

View File

@ -25,7 +25,14 @@ description: |
properties:
compatible:
const: richtek,rtq6056
oneOf:
- enum:
- richtek,rtq6056
- richtek,rtq6059
- items:
- enum:
- richtek,rtq6053
- const: richtek,rtq6056
reg:
maxItems: 1

View File

@ -0,0 +1,80 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/ti,ads1298.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments' ads1298 medical ADC chips
description: |
Datasheet at: https://www.ti.com/product/ADS1298
Bindings for this chip aren't complete.
maintainers:
- Mike Looijmans <mike.looijmans@topic.nl>
properties:
compatible:
enum:
- ti,ads1298
reg:
maxItems: 1
spi-cpha: true
reset-gpios:
maxItems: 1
avdd-supply:
description:
Analog power supply, voltage between AVDD and AVSS. When providing a
symmetric +/- 2.5V, the regulator should report 5V.
vref-supply:
description:
Optional reference voltage. If omitted, internal reference is used,
which is 2.4V when analog supply is below 4.4V, 4V otherwise.
clocks:
description: Optional 2.048 MHz external source clock on CLK pin
maxItems: 1
interrupts:
description: Interrupt on DRDY pin, triggers on falling edge
maxItems: 1
label: true
required:
- compatible
- reg
- avdd-supply
- interrupts
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
spi {
#address-cells = <1>;
#size-cells = <0>;
adc@1 {
reg = <1>;
compatible = "ti,ads1298";
label = "ads1298-1-ecg";
avdd-supply = <&reg_iso_5v_a>;
clocks = <&clk_ads1298>;
interrupt-parent = <&gpio0>;
interrupts = <78 IRQ_TYPE_EDGE_FALLING>;
spi-max-frequency = <20000000>;
spi-cpha;
};
};
...

View File

@ -39,6 +39,17 @@ properties:
description: |
Channel node of a voltage io-channel.
'#io-channel-cells':
description:
In addition to consuming the measurement services of a voltage
output channel, the voltage divider can act as a provider of
measurement services to other devices. This is particularly
useful in scenarios wherein an ADC has an analog frontend,
such as a voltage divider, and then consuming its raw value
isn't interesting. In this case, the voltage before the divider
is desired.
const: 1
output-ohms:
description:
Resistance Rout over which the output voltage is measured. See full-ohms.

View File

@ -0,0 +1,127 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
# Copyright 2024 Analog Devices Inc.
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/frequency/adi,admfm2000.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ADMFM2000 Dual Microwave Down Converter
maintainers:
- Kim Seer Paller <kimseer.paller@analog.com>
description:
Dual microwave down converter module with input RF and LO frequency ranges
from 0.5 to 32 GHz and an output IF frequency range from 0.1 to 8 GHz.
It consists of a LNA, mixer, IF filter, DSA, and IF amplifier for each down
conversion path.
properties:
compatible:
enum:
- adi,admfm2000
'#address-cells':
const: 1
'#size-cells':
const: 0
patternProperties:
"^channel@[0-1]$":
type: object
description: Represents a channel of the device.
additionalProperties: false
properties:
reg:
description:
The channel number.
minimum: 0
maximum: 1
adi,mixer-mode:
description:
Enable mixer mode for the channel. It downconverts RF between 5 GHz
and 32 GHz to IF between 0.5 GHz and 8 GHz. If not present, the channel
is in direct IF mode which bypasses the mixer and downconverts RF
between 2 GHz and 8 GHz to IF between 0.5 GHz and 8 GHz.
type: boolean
switch-gpios:
description: |
GPIOs to select the RF path for the channel. The same state of CTRL-A
and CTRL-B GPIOs is not permitted.
CTRL-A CTRL-B CH1 Status CH2 Status
1 0 Direct IF mode Mixer mode
0 1 Mixer mode Direct IF mode
items:
- description: CTRL-A GPIO
- description: CTRL-B GPIO
attenuation-gpios:
description: |
Choice of attenuation:
DSA-V4 DSA-V3 DSA-V2 DSA-V1 DSA-V0
1 1 1 1 1 0 dB
1 1 1 1 0 -1 dB
1 1 1 0 1 -2 dB
1 1 0 1 1 -4 dB
1 0 1 1 1 -8 dB
0 1 1 1 1 -16 dB
0 0 0 0 0 -31 dB
items:
- description: DSA-V0 GPIO
- description: DSA-V1 GPIO
- description: DSA-V2 GPIO
- description: DSA-V3 GPIO
- description: DSA-V4 GPIO
required:
- reg
- switch-gpios
- attenuation-gpios
required:
- compatible
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
converter {
compatible = "adi,admfm2000";
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
switch-gpios = <&gpio 1 GPIO_ACTIVE_LOW>,
<&gpio 2 GPIO_ACTIVE_HIGH>;
attenuation-gpios = <&gpio 17 GPIO_ACTIVE_LOW>,
<&gpio 22 GPIO_ACTIVE_LOW>,
<&gpio 23 GPIO_ACTIVE_LOW>,
<&gpio 24 GPIO_ACTIVE_LOW>,
<&gpio 25 GPIO_ACTIVE_LOW>;
};
channel@1 {
reg = <1>;
adi,mixer-mode;
switch-gpios = <&gpio 3 GPIO_ACTIVE_LOW>,
<&gpio 4 GPIO_ACTIVE_HIGH>;
attenuation-gpios = <&gpio 0 GPIO_ACTIVE_LOW>,
<&gpio 5 GPIO_ACTIVE_LOW>,
<&gpio 6 GPIO_ACTIVE_LOW>,
<&gpio 16 GPIO_ACTIVE_LOW>,
<&gpio 26 GPIO_ACTIVE_LOW>;
};
};
...

View File

@ -43,6 +43,7 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
@ -51,5 +52,7 @@ examples:
compatible = "ti,hdc3021", "ti,hdc3020";
reg = <0x47>;
vdd-supply = <&vcc_3v3>;
interrupt-parent = <&gpio3>;
interrupts = <23 IRQ_TYPE_EDGE_RISING>;
};
};

View File

@ -35,7 +35,9 @@ properties:
- st,lsm6dsv
- st,lsm6dso16is
- items:
- const: st,asm330lhhx
- enum:
- st,asm330lhhx
- st,asm330lhhxg1
- const: st,lsm6dsr
- items:
- const: st,lsm6dstx

View File

@ -4,19 +4,22 @@
$id: http://devicetree.org/schemas/iio/light/ams,as73211.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: AMS AS73211 JENCOLOR(R) Digital XYZ Sensor
title: AMS AS73211 JENCOLOR(R) Digital XYZ Sensor and AMS AS7331 UV Sensor
maintainers:
- Christian Eggers <ceggers@arri.de>
description: |
XYZ True Color Sensor with I2C Interface
AMS AS73211 XYZ True Color Sensor with I2C Interface
https://ams.com/documents/20143/36005/AS73211_DS000556_3-01.pdf/a65474c0-b302-c2fd-e30a-c98df87616df
AMS AS7331 UVA, UVB and UVC Sensor with I2C Interface
https://ams.com/documents/20143/9106314/AS7331_DS001047_4-00.pdf
properties:
compatible:
enum:
- ams,as73211
- ams,as7331
reg:
description:

View File

@ -99,6 +99,9 @@ required:
- honeywell,transfer-function
- honeywell,pressure-triplet
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml
additionalProperties: false
dependentSchemas:

View File

@ -8,25 +8,28 @@ title: Honeywell mprls0025pa pressure sensor
maintainers:
- Andreas Klinger <ak@it-klinger.de>
- Petre Rodan <petre.rodan@subdimension.ro>
description: |
Honeywell pressure sensor of model mprls0025pa.
This sensor has an I2C and SPI interface. Only the I2C interface is
implemented.
This sensor has an I2C and SPI interface.
There are many models with different pressure ranges available. The vendor
calls them "mpr series". All of them have the identical programming model and
differ in the pressure range, unit and transfer function.
To support different models one need to specify the pressure range as well as
the transfer function. Pressure range needs to be converted from its unit to
pascal.
To support different models one need to specify its pressure triplet as well
as the transfer function.
For custom silicon chips not covered by the Honeywell MPR series datasheet,
the pressure values can be specified manually via honeywell,pmin-pascal and
honeywell,pmax-pascal.
The minimal range value stands for the minimum pressure and the maximum value
also for the maximum pressure with linear relation inside the range.
The transfer function defines the ranges of numerical values delivered by the
sensor. The minimal range value stands for the minimum pressure and the
maximum value also for the maximum pressure with linear relation inside the
range.
sensor.
Specifications about the devices can be found at:
https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/
@ -42,6 +45,10 @@ properties:
maxItems: 1
interrupts:
description:
Optional interrupt for indicating End-of-conversion.
If not present, the driver loops for a while until the received status
byte indicates correct measurement.
maxItems: 1
reset-gpios:
@ -50,6 +57,27 @@ properties:
If not present the device is not reset during the probe.
maxItems: 1
honeywell,transfer-function:
description: |
Transfer function which defines the range of valid values delivered by the
sensor.
1 - A, 10% to 90% of 2^24 (1677722 .. 15099494)
2 - B, 2.5% to 22.5% of 2^24 (419430 .. 3774874)
3 - C, 20% to 80% of 2^24 (3355443 .. 13421773)
enum: [1, 2, 3]
$ref: /schemas/types.yaml#/definitions/uint32
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: [0001BA, 01.6BA, 02.5BA, 0060MG, 0100MG, 0160MG, 0250MG, 0400MG,
0600MG, 0001BG, 01.6BG, 02.5BG, 0100KA, 0160KA, 0250KA, 0006KG,
0010KG, 0016KG, 0025KG, 0040KG, 0060KG, 0100KG, 0160KG, 0250KG,
0015PA, 0025PA, 0030PA, 0001PG, 0005PG, 0015PG, 0030PG, 0300YG]
$ref: /schemas/types.yaml#/definitions/string
honeywell,pmin-pascal:
description:
Minimum pressure value the sensor can measure in pascal.
@ -58,14 +86,8 @@ properties:
description:
Maximum pressure value the sensor can measure in pascal.
honeywell,transfer-function:
description: |
Transfer function which defines the range of valid values delivered by the
sensor.
1 - A, 10% to 90% of 2^24 (1677722 .. 15099494)
2 - B, 2.5% to 22.5% of 2^24 (419430 .. 3774874)
3 - C, 20% to 80% of 2^24 (3355443 .. 13421773)
$ref: /schemas/types.yaml#/definitions/uint32
spi-max-frequency:
maximum: 800000
vdd-supply:
description: provide VDD power to the sensor.
@ -73,11 +95,26 @@ properties:
required:
- compatible
- reg
- honeywell,pmin-pascal
- honeywell,pmax-pascal
- honeywell,transfer-function
- 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:
@ -93,10 +130,29 @@ examples:
reg = <0x18>;
reset-gpios = <&gpio3 19 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&gpio3>;
interrupts = <21 IRQ_TYPE_EDGE_FALLING>;
honeywell,pmin-pascal = <0>;
honeywell,pmax-pascal = <172369>;
interrupts = <21 IRQ_TYPE_EDGE_RISING>;
honeywell,pressure-triplet = "0025PA";
honeywell,transfer-function = <1>;
vdd-supply = <&vcc_3v3>;
};
};
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
pressure@0 {
compatible = "honeywell,mprls0025pa";
reg = <0>;
spi-max-frequency = <800000>;
reset-gpios = <&gpio1 28 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&gpio0>;
interrupts = <30 IRQ_TYPE_EDGE_RISING>;
honeywell,pressure-triplet = "0015PA";
honeywell,transfer-function = <1>;
vdd-supply = <&vcc_3v3>;
};
};
...

View File

@ -1144,7 +1144,7 @@ L: linux-iio@vger.kernel.org
S: Supported
W: http://ez.analog.com/community/linux-device-drivers
F: Documentation/devicetree/bindings/iio/adc/adi,ad7091r*
F: drivers/iio/adc/drivers/iio/adc/ad7091r*
F: drivers/iio/adc/ad7091r*
ANALOG DEVICES INC AD7192 DRIVER
M: Alexandru Tachici <alexandru.tachici@analog.com>
@ -1267,6 +1267,14 @@ W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml
F: drivers/hwmon/adm1177.c
ANALOG DEVICES INC ADMFM2000 DRIVER
M: Kim Seer Paller <kimseer.paller@analog.com>
L: linux-iio@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/iio/frequency/adi,admfm2000.yaml
F: drivers/iio/frequency/admfm2000.c
ANALOG DEVICES INC ADMV1013 DRIVER
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
L: linux-iio@vger.kernel.org
@ -9796,10 +9804,11 @@ F: drivers/iio/pressure/hsc030pa*
HONEYWELL MPRLS0025PA PRESSURE SENSOR SERIES IIO DRIVER
M: Andreas Klinger <ak@it-klinger.de>
M: Petre Rodan <petre.rodan@subdimension.ro>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/pressure/honeywell,mprls0025pa.yaml
F: drivers/iio/pressure/mprls0025pa.c
F: drivers/iio/pressure/mprls0025pa*
HP BIOSCFG DRIVER
M: Jorge Lopez <jorge.lopez2@hp.com>
@ -10388,6 +10397,14 @@ L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/rc/iguanair.c
IIO BACKEND FRAMEWORK
M: Nuno Sa <nuno.sa@analog.com>
R: Olivier Moysan <olivier.moysan@foss.st.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/industrialio-backend.c
F: include/linux/iio/backend.h
IIO DIGITAL POTENTIOMETER DAC
M: Peter Rosin <peda@axentia.se>
L: linux-iio@vger.kernel.org
@ -10410,6 +10427,7 @@ L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/industrialio-gts-helper.c
F: include/linux/iio/iio-gts-helper.h
F: drivers/iio/test/iio-test-gts.c
IIO MULTIPLEXER
M: Peter Rosin <peda@axentia.se>

View File

@ -71,6 +71,15 @@ config IIO_TRIGGERED_EVENT
help
Provides helper functions for setting up triggered events.
config IIO_BACKEND
tristate
help
Framework to handle complex IIO aggregate devices. The typical
architecture that can make use of this framework is to have one
device as the frontend device which can be "linked" against one or
multiple backend devices. The framework then makes it easy to get
and control such backend devices.
source "drivers/iio/accel/Kconfig"
source "drivers/iio/adc/Kconfig"
source "drivers/iio/addac/Kconfig"

View File

@ -13,6 +13,7 @@ obj-$(CONFIG_IIO_GTS_HELPER) += industrialio-gts-helper.o
obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o
obj-$(CONFIG_IIO_SW_TRIGGER) += industrialio-sw-trigger.o
obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o
obj-$(CONFIG_IIO_BACKEND) += industrialio-backend.o
obj-y += accel/
obj-y += adc/

View File

@ -256,11 +256,11 @@ config BMC150_ACCEL_SPI
config BMI088_ACCEL
tristate "Bosch BMI088 Accelerometer Driver"
depends on SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select REGMAP
select BMI088_ACCEL_SPI
select BMI088_ACCEL_SPI if SPI
select BMI088_ACCEL_I2C if I2C
help
Say yes here to build support for the following Bosch accelerometers:
BMI088, BMI085, BMI090L. Note that all of these are combo module that
@ -269,6 +269,10 @@ config BMI088_ACCEL
This driver only implements the accelerometer part, which has its own
address and register map. BMG160 provides the gyroscope driver.
config BMI088_ACCEL_I2C
tristate
select REGMAP_I2C
config BMI088_ACCEL_SPI
tristate
select REGMAP_SPI

View File

@ -30,6 +30,7 @@ obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
obj-$(CONFIG_BMI088_ACCEL) += bmi088-accel-core.o
obj-$(CONFIG_BMI088_ACCEL_I2C) += bmi088-accel-i2c.o
obj-$(CONFIG_BMI088_ACCEL_SPI) += bmi088-accel-spi.o
obj-$(CONFIG_DA280) += da280.o
obj-$(CONFIG_DA311) += da311.o

View File

@ -339,22 +339,17 @@ static int adxl367_set_act_threshold(struct adxl367_state *st,
{
int ret;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
goto out;
return ret;
ret = _adxl367_set_act_threshold(st, act, threshold);
if (ret)
goto out;
return ret;
ret = adxl367_set_measure_en(st, true);
out:
mutex_unlock(&st->lock);
return ret;
return adxl367_set_measure_en(st, true);
}
static int adxl367_set_act_proc_mode(struct adxl367_state *st,
@ -482,51 +477,45 @@ static int adxl367_set_fifo_watermark(struct adxl367_state *st,
static int adxl367_set_range(struct iio_dev *indio_dev,
enum adxl367_range range)
{
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
guard(mutex)(&st->lock);
mutex_lock(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = adxl367_set_measure_en(st, false);
if (ret)
goto out;
ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL,
ADXL367_FILTER_CTL_RANGE_MASK,
FIELD_PREP(ADXL367_FILTER_CTL_RANGE_MASK,
range));
if (ret)
return ret;
ret = regmap_update_bits(st->regmap, ADXL367_REG_FILTER_CTL,
ADXL367_FILTER_CTL_RANGE_MASK,
FIELD_PREP(ADXL367_FILTER_CTL_RANGE_MASK,
range));
if (ret)
goto out;
adxl367_scale_act_thresholds(st, st->range, range);
adxl367_scale_act_thresholds(st, st->range, range);
/* Activity thresholds depend on range */
ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY,
st->act_threshold);
if (ret)
return ret;
/* Activity thresholds depend on range */
ret = _adxl367_set_act_threshold(st, ADXL367_ACTIVITY,
st->act_threshold);
if (ret)
goto out;
ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY,
st->inact_threshold);
if (ret)
return ret;
ret = _adxl367_set_act_threshold(st, ADXL367_INACTIVITY,
st->inact_threshold);
if (ret)
goto out;
ret = adxl367_set_measure_en(st, true);
if (ret)
return ret;
ret = adxl367_set_measure_en(st, true);
if (ret)
goto out;
st->range = range;
st->range = range;
out:
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
return ret;
return 0;
}
unreachable();
}
static int adxl367_time_ms_to_samples(struct adxl367_state *st, unsigned int ms)
@ -587,11 +576,11 @@ static int adxl367_set_act_time_ms(struct adxl367_state *st,
{
int ret;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
goto out;
return ret;
if (act == ADXL367_ACTIVITY)
ret = _adxl367_set_act_time_ms(st, ms);
@ -599,14 +588,9 @@ static int adxl367_set_act_time_ms(struct adxl367_state *st,
ret = _adxl367_set_inact_time_ms(st, ms);
if (ret)
goto out;
return ret;
ret = adxl367_set_measure_en(st, true);
out:
mutex_unlock(&st->lock);
return ret;
return adxl367_set_measure_en(st, true);
}
static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr)
@ -636,31 +620,23 @@ static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr)
static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr)
{
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
struct adxl367_state *st = iio_priv(indio_dev);;
int ret;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
guard(mutex)(&st->lock);
mutex_lock(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = adxl367_set_measure_en(st, false);
if (ret)
goto out;
ret = _adxl367_set_odr(st, odr);
if (ret)
return ret;
ret = _adxl367_set_odr(st, odr);
if (ret)
goto out;
ret = adxl367_set_measure_en(st, true);
out:
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
return ret;
return adxl367_set_measure_en(st, true);
}
unreachable();
}
static int adxl367_set_temp_adc_en(struct adxl367_state *st, unsigned int reg,
@ -749,36 +725,32 @@ static int adxl367_read_sample(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val)
{
struct adxl367_state *st = iio_priv(indio_dev);
u16 sample;
int ret;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
struct adxl367_state *st = iio_priv(indio_dev);
u16 sample;
int ret;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
guard(mutex)(&st->lock);
mutex_lock(&st->lock);
ret = adxl367_set_temp_adc_reg_en(st, chan->address, true);
if (ret)
return ret;
ret = adxl367_set_temp_adc_reg_en(st, chan->address, true);
if (ret)
goto out;
ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf,
sizeof(st->sample_buf));
if (ret)
return ret;
ret = regmap_bulk_read(st->regmap, chan->address, &st->sample_buf,
sizeof(st->sample_buf));
if (ret)
goto out;
sample = FIELD_GET(ADXL367_DATA_MASK, be16_to_cpu(st->sample_buf));
*val = sign_extend32(sample, chan->scan_type.realbits - 1);
sample = FIELD_GET(ADXL367_DATA_MASK, be16_to_cpu(st->sample_buf));
*val = sign_extend32(sample, chan->scan_type.realbits - 1);
ret = adxl367_set_temp_adc_reg_en(st, chan->address, false);
if (ret)
return ret;
ret = adxl367_set_temp_adc_reg_en(st, chan->address, false);
out:
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
return ret ?: IIO_VAL_INT;
return IIO_VAL_INT;
}
unreachable();
}
static int adxl367_get_status(struct adxl367_state *st, u8 *status,
@ -886,12 +858,12 @@ static int adxl367_read_raw(struct iio_dev *indio_dev,
return adxl367_read_sample(indio_dev, chan, val);
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_ACCEL:
mutex_lock(&st->lock);
case IIO_ACCEL: {
guard(mutex)(&st->lock);
*val = adxl367_range_scale_tbl[st->range][0];
*val2 = adxl367_range_scale_tbl[st->range][1];
mutex_unlock(&st->lock);
return IIO_VAL_INT_PLUS_NANO;
}
case IIO_TEMP:
*val = 1000;
*val2 = ADXL367_TEMP_PER_C;
@ -914,12 +886,12 @@ static int adxl367_read_raw(struct iio_dev *indio_dev,
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SAMP_FREQ:
mutex_lock(&st->lock);
case IIO_CHAN_INFO_SAMP_FREQ: {
guard(mutex)(&st->lock);
*val = adxl367_samp_freq_tbl[st->odr][0];
*val2 = adxl367_samp_freq_tbl[st->odr][1];
mutex_unlock(&st->lock);
return IIO_VAL_INT_PLUS_MICRO;
}
default:
return -EINVAL;
}
@ -1004,18 +976,15 @@ static int adxl367_read_event_value(struct iio_dev *indio_dev,
{
struct adxl367_state *st = iio_priv(indio_dev);
guard(mutex)(&st->lock);
switch (info) {
case IIO_EV_INFO_VALUE: {
switch (dir) {
case IIO_EV_DIR_RISING:
mutex_lock(&st->lock);
*val = st->act_threshold;
mutex_unlock(&st->lock);
return IIO_VAL_INT;
case IIO_EV_DIR_FALLING:
mutex_lock(&st->lock);
*val = st->inact_threshold;
mutex_unlock(&st->lock);
return IIO_VAL_INT;
default:
return -EINVAL;
@ -1024,15 +993,11 @@ static int adxl367_read_event_value(struct iio_dev *indio_dev,
case IIO_EV_INFO_PERIOD:
switch (dir) {
case IIO_EV_DIR_RISING:
mutex_lock(&st->lock);
*val = st->act_time_ms;
mutex_unlock(&st->lock);
*val2 = 1000;
return IIO_VAL_FRACTIONAL;
case IIO_EV_DIR_FALLING:
mutex_lock(&st->lock);
*val = st->inact_time_ms;
mutex_unlock(&st->lock);
*val2 = 1000;
return IIO_VAL_FRACTIONAL;
default:
@ -1110,9 +1075,7 @@ static int adxl367_write_event_config(struct iio_dev *indio_dev,
enum iio_event_direction dir,
int state)
{
struct adxl367_state *st = iio_priv(indio_dev);
enum adxl367_activity_type act;
int ret;
switch (dir) {
case IIO_EV_DIR_RISING:
@ -1125,33 +1088,28 @@ static int adxl367_write_event_config(struct iio_dev *indio_dev,
return -EINVAL;
}
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
goto out;
ret = adxl367_set_measure_en(st, false);
if (ret)
return ret;
ret = adxl367_set_act_interrupt_en(st, act, state);
if (ret)
goto out;
ret = adxl367_set_act_interrupt_en(st, act, state);
if (ret)
return ret;
ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED
: ADXL367_ACT_DISABLED);
if (ret)
goto out;
ret = adxl367_set_act_en(st, act, state ? ADCL367_ACT_REF_ENABLED
: ADXL367_ACT_DISABLED);
if (ret)
return ret;
ret = adxl367_set_measure_en(st, true);
out:
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
return ret;
return adxl367_set_measure_en(st, true);
}
unreachable();
}
static ssize_t adxl367_get_fifo_enabled(struct device *dev,
@ -1176,9 +1134,8 @@ static ssize_t adxl367_get_fifo_watermark(struct device *dev,
struct adxl367_state *st = iio_priv(dev_to_iio_dev(dev));
unsigned int fifo_watermark;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
fifo_watermark = st->fifo_watermark;
mutex_unlock(&st->lock);
return sysfs_emit(buf, "%d\n", fifo_watermark);
}
@ -1207,22 +1164,17 @@ static int adxl367_set_watermark(struct iio_dev *indio_dev, unsigned int val)
if (val > ADXL367_FIFO_MAX_WATERMARK)
return -EINVAL;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
goto out;
return ret;
ret = adxl367_set_fifo_watermark(st, val);
if (ret)
goto out;
return ret;
ret = adxl367_set_measure_en(st, true);
out:
mutex_unlock(&st->lock);
return ret;
return adxl367_set_measure_en(st, true);
}
static bool adxl367_find_mask_fifo_format(const unsigned long *scan_mask,
@ -1253,27 +1205,24 @@ static int adxl367_update_scan_mode(struct iio_dev *indio_dev,
if (!adxl367_find_mask_fifo_format(active_scan_mask, &fifo_format))
return -EINVAL;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
goto out;
return ret;
ret = adxl367_set_fifo_format(st, fifo_format);
if (ret)
goto out;
return ret;
ret = adxl367_set_measure_en(st, true);
if (ret)
goto out;
return ret;
st->fifo_set_size = bitmap_weight(active_scan_mask,
indio_dev->masklength);
out:
mutex_unlock(&st->lock);
return ret;
return 0;
}
static int adxl367_buffer_postenable(struct iio_dev *indio_dev)
@ -1281,31 +1230,26 @@ static int adxl367_buffer_postenable(struct iio_dev *indio_dev)
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
ret = adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask,
true);
if (ret)
goto out;
return ret;
ret = adxl367_set_measure_en(st, false);
if (ret)
goto out;
return ret;
ret = adxl367_set_fifo_watermark_interrupt_en(st, true);
if (ret)
goto out;
return ret;
ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_STREAM);
if (ret)
goto out;
return ret;
ret = adxl367_set_measure_en(st, true);
out:
mutex_unlock(&st->lock);
return ret;
return adxl367_set_measure_en(st, true);
}
static int adxl367_buffer_predisable(struct iio_dev *indio_dev)
@ -1313,31 +1257,26 @@ static int adxl367_buffer_predisable(struct iio_dev *indio_dev)
struct adxl367_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
ret = adxl367_set_measure_en(st, false);
if (ret)
goto out;
return ret;
ret = adxl367_set_fifo_mode(st, ADXL367_FIFO_MODE_DISABLED);
if (ret)
goto out;
return ret;
ret = adxl367_set_fifo_watermark_interrupt_en(st, false);
if (ret)
goto out;
return ret;
ret = adxl367_set_measure_en(st, true);
if (ret)
goto out;
return ret;
ret = adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask,
false);
out:
mutex_unlock(&st->lock);
return ret;
return adxl367_set_temp_adc_mask_en(st, indio_dev->active_scan_mask,
false);
}
static const struct iio_buffer_setup_ops adxl367_buffer_ops = {

View File

@ -224,6 +224,19 @@ static const struct acpi_device_id bmc150_accel_acpi_match[] = {
{"BMA250E"},
{"BMC150A"},
{"BMI055A"},
/*
* The "BOSC0200" identifier used here is not unique to devices using
* bmc150. The same "BOSC0200" identifier is found in the ACPI tables
* of the ASUS ROG ALLY and Ayaneo AIR Plus which both use a Bosch
* BMI323 chip. This creates a conflict with duplicate ACPI identifiers
* which multiple drivers want to use. Fortunately, when the bmc150
* driver starts to load on the ASUS ROG ALLY, the chip ID check
* portion fails (correctly) because the chip IDs received (via i2c)
* are unique between bmc150 and bmi323 and a dmesg output similar to
* this: "bmc150_accel_i2c i2c-BOSC0200:00: Invalid chip 0" can be
* seen. This allows the bmi323 driver to take over for ASUS ROG ALLY,
* and other devices using the bmi323 chip.
*/
{"BOSC0200"},
{"BSBA0150"},
{"DUAL250E"},
@ -266,7 +279,7 @@ static struct i2c_driver bmc150_accel_driver = {
.driver = {
.name = "bmc150_accel_i2c",
.of_match_table = bmc150_accel_of_match,
.acpi_match_table = ACPI_PTR(bmc150_accel_acpi_match),
.acpi_match_table = bmc150_accel_acpi_match,
.pm = &bmc150_accel_pm_ops,
},
.probe = bmc150_accel_probe,

View File

@ -7,7 +7,6 @@
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/acpi.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
@ -70,7 +69,7 @@ MODULE_DEVICE_TABLE(spi, bmc150_accel_id);
static struct spi_driver bmc150_accel_driver = {
.driver = {
.name = "bmc150_accel_spi",
.acpi_match_table = ACPI_PTR(bmc150_accel_acpi_match),
.acpi_match_table = bmc150_accel_acpi_match,
.pm = &bmc150_accel_pm_ops,
},
.probe = bmc150_accel_probe,

View File

@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0
/*
* 3-axis accelerometer driver supporting following Bosch-Sensortec chips:
* - BMI088
* - BMI085
* - BMI090L
*
* Copyright 2023 Jun Yan <jerrysteve1101@gmail.com>
*/
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include "bmi088-accel.h"
static int bmi088_accel_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
const struct i2c_device_id *id = i2c_client_get_device_id(i2c);
regmap = devm_regmap_init_i2c(i2c, &bmi088_regmap_conf);
if (IS_ERR(regmap)) {
dev_err(&i2c->dev, "Failed to initialize i2c regmap\n");
return PTR_ERR(regmap);
}
return bmi088_accel_core_probe(&i2c->dev, regmap, i2c->irq,
id->driver_data);
}
static void bmi088_accel_remove(struct i2c_client *i2c)
{
bmi088_accel_core_remove(&i2c->dev);
}
static const struct of_device_id bmi088_of_match[] = {
{ .compatible = "bosch,bmi085-accel" },
{ .compatible = "bosch,bmi088-accel" },
{ .compatible = "bosch,bmi090l-accel" },
{}
};
MODULE_DEVICE_TABLE(of, bmi088_of_match);
static const struct i2c_device_id bmi088_accel_id[] = {
{ "bmi085-accel", BOSCH_BMI085 },
{ "bmi088-accel", BOSCH_BMI088 },
{ "bmi090l-accel", BOSCH_BMI090L },
{}
};
MODULE_DEVICE_TABLE(i2c, bmi088_accel_id);
static struct i2c_driver bmi088_accel_driver = {
.driver = {
.name = "bmi088_accel_i2c",
.pm = pm_ptr(&bmi088_accel_pm_ops),
.of_match_table = bmi088_of_match,
},
.probe = bmi088_accel_probe,
.remove = bmi088_accel_remove,
.id_table = bmi088_accel_id,
};
module_i2c_driver(bmi088_accel_driver);
MODULE_AUTHOR("Jun Yan <jerrysteve1101@gmail.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("BMI088 accelerometer driver (I2C)");
MODULE_IMPORT_NS(IIO_BMI088);

View File

@ -23,8 +23,6 @@
#define DA280_MODE_ENABLE 0x1e
#define DA280_MODE_DISABLE 0x9e
enum da280_chipset { da217, da226, da280 };
/*
* a value of + or -4096 corresponds to + or - 1G
* scale = 9.81 / 4096 = 0.002395019
@ -47,6 +45,11 @@ static const struct iio_chan_spec da280_channels[] = {
DA280_CHANNEL(DA280_REG_ACC_Z_LSB, Z),
};
struct da280_match_data {
const char *name;
int num_channels;
};
struct da280_data {
struct i2c_client *client;
};
@ -89,17 +92,6 @@ static const struct iio_info da280_info = {
.read_raw = da280_read_raw,
};
static enum da280_chipset da280_match_acpi_device(struct device *dev)
{
const struct acpi_device_id *id;
id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id)
return -EINVAL;
return (enum da280_chipset) id->driver_data;
}
static void da280_disable(void *client)
{
da280_enable(client, false);
@ -107,16 +99,21 @@ static void da280_disable(void *client)
static int da280_probe(struct i2c_client *client)
{
const struct i2c_device_id *id = i2c_client_get_device_id(client);
int ret;
const struct da280_match_data *match_data;
struct iio_dev *indio_dev;
struct da280_data *data;
enum da280_chipset chip;
int ret;
ret = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID);
if (ret != DA280_CHIP_ID)
return (ret < 0) ? ret : -ENODEV;
match_data = i2c_get_match_data(client);
if (!match_data) {
dev_err(&client->dev, "Error match-data not set\n");
return -EINVAL;
}
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
@ -127,23 +124,8 @@ static int da280_probe(struct i2c_client *client)
indio_dev->info = &da280_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = da280_channels;
if (ACPI_HANDLE(&client->dev)) {
chip = da280_match_acpi_device(&client->dev);
} else {
chip = id->driver_data;
}
if (chip == da217) {
indio_dev->name = "da217";
indio_dev->num_channels = 3;
} else if (chip == da226) {
indio_dev->name = "da226";
indio_dev->num_channels = 2;
} else {
indio_dev->name = "da280";
indio_dev->num_channels = 3;
}
indio_dev->num_channels = match_data->num_channels;
indio_dev->name = match_data->name;
ret = da280_enable(client, true);
if (ret < 0)
@ -168,17 +150,21 @@ static int da280_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume);
static const struct da280_match_data da217_match_data = { "da217", 3 };
static const struct da280_match_data da226_match_data = { "da226", 2 };
static const struct da280_match_data da280_match_data = { "da280", 3 };
static const struct acpi_device_id da280_acpi_match[] = {
{"NSA2513", da217},
{"MIRAACC", da280},
{},
{ "NSA2513", (kernel_ulong_t)&da217_match_data },
{ "MIRAACC", (kernel_ulong_t)&da280_match_data },
{}
};
MODULE_DEVICE_TABLE(acpi, da280_acpi_match);
static const struct i2c_device_id da280_i2c_id[] = {
{ "da217", da217 },
{ "da226", da226 },
{ "da280", da280 },
{ "da217", (kernel_ulong_t)&da217_match_data },
{ "da226", (kernel_ulong_t)&da226_match_data },
{ "da280", (kernel_ulong_t)&da280_match_data },
{}
};
MODULE_DEVICE_TABLE(i2c, da280_i2c_id);
@ -186,7 +172,7 @@ MODULE_DEVICE_TABLE(i2c, da280_i2c_id);
static struct i2c_driver da280_driver = {
.driver = {
.name = "da280",
.acpi_match_table = ACPI_PTR(da280_acpi_match),
.acpi_match_table = da280_acpi_match,
.pm = pm_sleep_ptr(&da280_pm_ops),
},
.probe = da280_probe,

View File

@ -422,6 +422,23 @@ static int kiox010a_dsm(struct device *dev, int fn_index)
ACPI_FREE(obj);
return 0;
}
static const struct acpi_device_id kx_acpi_match[] = {
{"KXCJ1013", KXCJK1013},
{"KXCJ1008", KXCJ91008},
{"KXCJ9000", KXCJ91008},
{"KIOX0008", KXCJ91008},
{"KIOX0009", KXTJ21009},
{"KIOX000A", KXCJ91008},
{"KIOX010A", KXCJ91008}, /* KXCJ91008 in the display of a yoga 2-in-1 */
{"KIOX020A", KXCJ91008}, /* KXCJ91008 in the base of a yoga 2-in-1 */
{"KXTJ1009", KXTJ21009},
{"KXJ2109", KXTJ21009},
{"SMO8500", KXCJ91008},
{ }
};
MODULE_DEVICE_TABLE(acpi, kx_acpi_match);
#endif
static int kxcjk1013_set_mode(struct kxcjk1013_data *data,
@ -1687,22 +1704,6 @@ static const struct dev_pm_ops kxcjk1013_pm_ops = {
kxcjk1013_runtime_resume, NULL)
};
static const struct acpi_device_id kx_acpi_match[] = {
{"KXCJ1013", KXCJK1013},
{"KXCJ1008", KXCJ91008},
{"KXCJ9000", KXCJ91008},
{"KIOX0008", KXCJ91008},
{"KIOX0009", KXTJ21009},
{"KIOX000A", KXCJ91008},
{"KIOX010A", KXCJ91008}, /* KXCJ91008 in the display of a yoga 2-in-1 */
{"KIOX020A", KXCJ91008}, /* KXCJ91008 in the base of a yoga 2-in-1 */
{"KXTJ1009", KXTJ21009},
{"KXJ2109", KXTJ21009},
{"SMO8500", KXCJ91008},
{ },
};
MODULE_DEVICE_TABLE(acpi, kx_acpi_match);
static const struct i2c_device_id kxcjk1013_id[] = {
{"kxcjk1013", KXCJK1013},
{"kxcj91008", KXCJ91008},

View File

@ -604,9 +604,9 @@ MODULE_DEVICE_TABLE(i2c, mma9551_id);
static struct i2c_driver mma9551_driver = {
.driver = {
.name = MMA9551_DRV_NAME,
.acpi_match_table = ACPI_PTR(mma9551_acpi_match),
.acpi_match_table = mma9551_acpi_match,
.pm = pm_ptr(&mma9551_pm_ops),
},
},
.probe = mma9551_probe,
.remove = mma9551_remove,
.id_table = mma9551_id,

View File

@ -1243,9 +1243,9 @@ MODULE_DEVICE_TABLE(i2c, mma9553_id);
static struct i2c_driver mma9553_driver = {
.driver = {
.name = MMA9553_DRV_NAME,
.acpi_match_table = ACPI_PTR(mma9553_acpi_match),
.acpi_match_table = mma9553_acpi_match,
.pm = pm_ptr(&mma9553_pm_ops),
},
},
.probe = mma9553_probe,
.remove = mma9553_remove,
.id_table = mma9553_id,

View File

@ -8,7 +8,7 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/acpi.h>
#include <linux/mod_devicetable.h>
#include <linux/regmap.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
@ -472,6 +472,7 @@ static int mxc4005_probe(struct i2c_client *client)
static const struct acpi_device_id mxc4005_acpi_match[] = {
{"MXC4005", 0},
{"MXC6655", 0},
{"MDA6655", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, mxc4005_acpi_match);
@ -493,7 +494,7 @@ MODULE_DEVICE_TABLE(i2c, mxc4005_id);
static struct i2c_driver mxc4005_driver = {
.driver = {
.name = MXC4005_DRV_NAME,
.acpi_match_table = ACPI_PTR(mxc4005_acpi_match),
.acpi_match_table = mxc4005_acpi_match,
.of_match_table = mxc4005_of_match,
},
.probe = mxc4005_probe,

View File

@ -12,7 +12,7 @@
#include <linux/init.h>
#include <linux/iio/iio.h>
#include <linux/delay.h>
#include <linux/acpi.h>
#include <linux/mod_devicetable.h>
#include <linux/regmap.h>
#include <linux/iio/sysfs.h>
@ -181,7 +181,7 @@ MODULE_DEVICE_TABLE(i2c, mxc6255_id);
static struct i2c_driver mxc6255_driver = {
.driver = {
.name = MXC6255_DRV_NAME,
.acpi_match_table = ACPI_PTR(mxc6255_acpi_match),
.acpi_match_table = mxc6255_acpi_match,
},
.probe = mxc6255_probe,
.id_table = mxc6255_id,

View File

@ -10,7 +10,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
@ -127,14 +126,12 @@ static const struct of_device_id st_accel_of_match[] = {
};
MODULE_DEVICE_TABLE(of, st_accel_of_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id st_accel_acpi_match[] = {
{"SMO8840", (kernel_ulong_t)LIS2DH12_ACCEL_DEV_NAME},
{"SMO8A90", (kernel_ulong_t)LNG2DM_ACCEL_DEV_NAME},
{ },
};
MODULE_DEVICE_TABLE(acpi, st_accel_acpi_match);
#endif
static const struct i2c_device_id st_accel_id_table[] = {
{ LSM303DLH_ACCEL_DEV_NAME },
@ -204,7 +201,7 @@ static struct i2c_driver st_accel_driver = {
.driver = {
.name = "st-accel-i2c",
.of_match_table = st_accel_of_match,
.acpi_match_table = ACPI_PTR(st_accel_acpi_match),
.acpi_match_table = st_accel_acpi_match,
},
.probe = st_accel_i2c_probe,
.id_table = st_accel_id_table,

View File

@ -7,11 +7,11 @@
* STK8BA50 7-bit I2C address: 0x18.
*/
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@ -541,7 +541,7 @@ static struct i2c_driver stk8ba50_driver = {
.driver = {
.name = "stk8ba50",
.pm = pm_sleep_ptr(&stk8ba50_pm_ops),
.acpi_match_table = ACPI_PTR(stk8ba50_acpi_id),
.acpi_match_table = stk8ba50_acpi_id,
},
.probe = stk8ba50_probe,
.remove = stk8ba50_remove,

View File

@ -291,7 +291,7 @@ config AD799X
config AD9467
tristate "Analog Devices AD9467 High Speed ADC driver"
depends on SPI
depends on ADI_AXI_ADC
select IIO_BACKEND
help
Say yes here to build support for Analog Devices:
* AD9467 16-Bit, 200 MSPS/250 MSPS Analog-to-Digital Converter
@ -309,7 +309,7 @@ config ADI_AXI_ADC
select IIO_BUFFER_HW_CONSUMER
select IIO_BUFFER_DMAENGINE
select REGMAP_MMIO
depends on OF
select IIO_BACKEND
help
Say yes here to build support for Analog Devices Generic
AXI ADC IP core. The IP core is used for interfacing with
@ -1312,6 +1312,17 @@ config TI_ADS1100
This driver can also be built as a module. If so, the module will be
called ti-ads1100.
config TI_ADS1298
tristate "Texas Instruments ADS1298"
depends on SPI
select IIO_BUFFER
help
If you say yes here you get support for Texas Instruments ADS1298
medical ADC chips
This driver can also be built as a module. If so, the module will be
called ti-ads1298.
config TI_ADS7950
tristate "Texas Instruments ADS7950 ADC driver"
depends on SPI && GPIOLIB

View File

@ -116,6 +116,7 @@ obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
obj-$(CONFIG_TI_ADS1100) += ti-ads1100.o
obj-$(CONFIG_TI_ADS1298) += ti-ads1298.o
obj-$(CONFIG_TI_ADS7924) += ti-ads7924.o
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o

View File

@ -887,9 +887,9 @@ static int ad4130_set_filter_mode(struct iio_dev *indio_dev,
unsigned int old_fs;
int ret = 0;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
if (setup_info->filter_mode == val)
goto out;
return 0;
old_fs = setup_info->fs;
old_filter_mode = setup_info->filter_mode;
@ -911,12 +911,10 @@ static int ad4130_set_filter_mode(struct iio_dev *indio_dev,
if (ret) {
setup_info->fs = old_fs;
setup_info->filter_mode = old_filter_mode;
return ret;
}
out:
mutex_unlock(&st->lock);
return ret;
return 0;
}
static int ad4130_get_filter_mode(struct iio_dev *indio_dev,
@ -927,9 +925,8 @@ static int ad4130_get_filter_mode(struct iio_dev *indio_dev,
struct ad4130_setup_info *setup_info = &st->chans_info[channel].setup;
enum ad4130_filter_mode filter_mode;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
filter_mode = setup_info->filter_mode;
mutex_unlock(&st->lock);
return filter_mode;
}
@ -971,7 +968,7 @@ static int ad4130_set_channel_pga(struct ad4130_state *st, unsigned int channel,
struct ad4130_chan_info *chan_info = &st->chans_info[channel];
struct ad4130_setup_info *setup_info = &chan_info->setup;
unsigned int pga, old_pga;
int ret = 0;
int ret;
for (pga = 0; pga < AD4130_MAX_PGA; pga++)
if (val == st->scale_tbls[setup_info->ref_sel][pga][0] &&
@ -981,21 +978,20 @@ static int ad4130_set_channel_pga(struct ad4130_state *st, unsigned int channel,
if (pga == AD4130_MAX_PGA)
return -EINVAL;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
if (pga == setup_info->pga)
goto out;
return 0;
old_pga = setup_info->pga;
setup_info->pga = pga;
ret = ad4130_write_channel_setup(st, channel, false);
if (ret)
if (ret) {
setup_info->pga = old_pga;
return ret;
}
out:
mutex_unlock(&st->lock);
return ret;
return 0;
}
static int ad4130_set_channel_freq(struct ad4130_state *st,
@ -1004,26 +1000,25 @@ static int ad4130_set_channel_freq(struct ad4130_state *st,
struct ad4130_chan_info *chan_info = &st->chans_info[channel];
struct ad4130_setup_info *setup_info = &chan_info->setup;
unsigned int fs, old_fs;
int ret = 0;
int ret;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
old_fs = setup_info->fs;
ad4130_freq_to_fs(setup_info->filter_mode, val, val2, &fs);
if (fs == setup_info->fs)
goto out;
return 0;
setup_info->fs = fs;
ret = ad4130_write_channel_setup(st, channel, false);
if (ret)
if (ret) {
setup_info->fs = old_fs;
return ret;
}
out:
mutex_unlock(&st->lock);
return ret;
return 0;
}
static int _ad4130_read_sample(struct iio_dev *indio_dev, unsigned int channel,
@ -1065,20 +1060,13 @@ static int _ad4130_read_sample(struct iio_dev *indio_dev, unsigned int channel,
static int ad4130_read_sample(struct iio_dev *indio_dev, unsigned int channel,
int *val)
{
struct ad4130_state *st = iio_priv(indio_dev);
int ret;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
struct ad4130_state *st = iio_priv(indio_dev);
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
mutex_lock(&st->lock);
ret = _ad4130_read_sample(indio_dev, channel, val);
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
return ret;
guard(mutex)(&st->lock);
return _ad4130_read_sample(indio_dev, channel, val);
}
unreachable();
}
static int ad4130_read_raw(struct iio_dev *indio_dev,
@ -1092,24 +1080,24 @@ static int ad4130_read_raw(struct iio_dev *indio_dev,
switch (info) {
case IIO_CHAN_INFO_RAW:
return ad4130_read_sample(indio_dev, channel, val);
case IIO_CHAN_INFO_SCALE:
mutex_lock(&st->lock);
case IIO_CHAN_INFO_SCALE: {
guard(mutex)(&st->lock);
*val = st->scale_tbls[setup_info->ref_sel][setup_info->pga][0];
*val2 = st->scale_tbls[setup_info->ref_sel][setup_info->pga][1];
mutex_unlock(&st->lock);
return IIO_VAL_INT_PLUS_NANO;
}
case IIO_CHAN_INFO_OFFSET:
*val = st->bipolar ? -BIT(chan->scan_type.realbits - 1) : 0;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SAMP_FREQ:
mutex_lock(&st->lock);
case IIO_CHAN_INFO_SAMP_FREQ: {
guard(mutex)(&st->lock);
ad4130_fs_to_freq(setup_info->filter_mode, setup_info->fs,
val, val2);
mutex_unlock(&st->lock);
return IIO_VAL_INT_PLUS_NANO;
}
default:
return -EINVAL;
}
@ -1134,9 +1122,9 @@ static int ad4130_read_avail(struct iio_dev *indio_dev,
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_SAMP_FREQ:
mutex_lock(&st->lock);
filter_config = &ad4130_filter_configs[setup_info->filter_mode];
mutex_unlock(&st->lock);
scoped_guard(mutex, &st->lock) {
filter_config = &ad4130_filter_configs[setup_info->filter_mode];
}
*vals = (int *)filter_config->samp_freq_avail;
*length = filter_config->samp_freq_avail_len * 2;
@ -1197,21 +1185,18 @@ static int ad4130_update_scan_mode(struct iio_dev *indio_dev,
unsigned int val = 0;
int ret;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
for_each_set_bit(channel, scan_mask, indio_dev->num_channels) {
ret = ad4130_set_channel_enable(st, channel, true);
if (ret)
goto out;
return ret;
val++;
}
st->num_enabled_channels = val;
out:
mutex_unlock(&st->lock);
return 0;
}
@ -1232,22 +1217,19 @@ static int ad4130_set_fifo_watermark(struct iio_dev *indio_dev, unsigned int val
*/
eff = rounddown(AD4130_FIFO_SIZE, st->num_enabled_channels);
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
ret = regmap_update_bits(st->regmap, AD4130_FIFO_CONTROL_REG,
AD4130_FIFO_CONTROL_WM_MASK,
FIELD_PREP(AD4130_FIFO_CONTROL_WM_MASK,
ad4130_watermark_reg_val(eff)));
if (ret)
goto out;
return ret;
st->effective_watermark = eff;
st->watermark = val;
out:
mutex_unlock(&st->lock);
return ret;
return 0;
}
static const struct iio_info ad4130_info = {
@ -1265,26 +1247,21 @@ static int ad4130_buffer_postenable(struct iio_dev *indio_dev)
struct ad4130_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
ret = ad4130_set_watermark_interrupt_en(st, true);
if (ret)
goto out;
return ret;
ret = irq_set_irq_type(st->spi->irq, st->inv_irq_trigger);
if (ret)
goto out;
return ret;
ret = ad4130_set_fifo_mode(st, AD4130_FIFO_MODE_WM);
if (ret)
goto out;
return ret;
ret = ad4130_set_mode(st, AD4130_MODE_CONTINUOUS);
out:
mutex_unlock(&st->lock);
return ret;
return ad4130_set_mode(st, AD4130_MODE_CONTINUOUS);
}
static int ad4130_buffer_predisable(struct iio_dev *indio_dev)
@ -1293,23 +1270,23 @@ static int ad4130_buffer_predisable(struct iio_dev *indio_dev)
unsigned int i;
int ret;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
ret = ad4130_set_mode(st, AD4130_MODE_IDLE);
if (ret)
goto out;
return ret;
ret = irq_set_irq_type(st->spi->irq, st->irq_trigger);
if (ret)
goto out;
return ret;
ret = ad4130_set_fifo_mode(st, AD4130_FIFO_MODE_DISABLED);
if (ret)
goto out;
return ret;
ret = ad4130_set_watermark_interrupt_en(st, false);
if (ret)
goto out;
return ret;
/*
* update_scan_mode() is not called in the disable path, disable all
@ -1318,13 +1295,10 @@ static int ad4130_buffer_predisable(struct iio_dev *indio_dev)
for (i = 0; i < indio_dev->num_channels; i++) {
ret = ad4130_set_channel_enable(st, i, false);
if (ret)
goto out;
return ret;
}
out:
mutex_unlock(&st->lock);
return ret;
return 0;
}
static const struct iio_buffer_setup_ops ad4130_buffer_ops = {
@ -1338,9 +1312,8 @@ static ssize_t hwfifo_watermark_show(struct device *dev,
struct ad4130_state *st = iio_priv(dev_to_iio_dev(dev));
unsigned int val;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
val = st->watermark;
mutex_unlock(&st->lock);
return sysfs_emit(buf, "%d\n", val);
}

View File

@ -86,28 +86,25 @@ static int ad7091r_read_raw(struct iio_dev *iio_dev,
unsigned int read_val;
int ret;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
switch (m) {
case IIO_CHAN_INFO_RAW:
if (st->mode != AD7091R_MODE_COMMAND) {
ret = -EBUSY;
goto unlock;
}
if (st->mode != AD7091R_MODE_COMMAND)
return -EBUSY;
ret = ad7091r_read_one(iio_dev, chan->channel, &read_val);
if (ret)
goto unlock;
return ret;
*val = read_val;
ret = IIO_VAL_INT;
break;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
if (st->vref) {
ret = regulator_get_voltage(st->vref);
if (ret < 0)
goto unlock;
return ret;
*val = ret / 1000;
} else {
@ -115,17 +112,11 @@ static int ad7091r_read_raw(struct iio_dev *iio_dev,
}
*val2 = chan->scan_type.realbits;
ret = IIO_VAL_FRACTIONAL_LOG2;
break;
return IIO_VAL_FRACTIONAL_LOG2;
default:
ret = -EINVAL;
break;
return -EINVAL;
}
unlock:
mutex_unlock(&st->lock);
return ret;
}
static int ad7091r_read_event_config(struct iio_dev *indio_dev,

View File

@ -17,13 +17,12 @@
#include <linux/of.h>
#include <linux/iio/backend.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/clk.h>
#include <linux/iio/adc/adi-axi-adc.h>
/*
* ADI High-Speed ADC common spi interface registers
* See Application-Note AN-877:
@ -102,15 +101,20 @@
#define AD9467_REG_VREF_MASK 0x0F
struct ad9467_chip_info {
struct adi_axi_adc_chip_info axi_adc_info;
unsigned int default_output_mode;
unsigned int vref_mask;
const char *name;
unsigned int id;
const struct iio_chan_spec *channels;
unsigned int num_channels;
const unsigned int (*scale_table)[2];
int num_scales;
unsigned long max_rate;
unsigned int default_output_mode;
unsigned int vref_mask;
};
#define to_ad9467_chip_info(_info) \
container_of(_info, struct ad9467_chip_info, axi_adc_info)
struct ad9467_state {
const struct ad9467_chip_info *info;
struct iio_backend *back;
struct spi_device *spi;
struct clk *clk;
unsigned int output_mode;
@ -151,10 +155,10 @@ static int ad9467_spi_write(struct spi_device *spi, unsigned int reg,
return spi_write(spi, buf, ARRAY_SIZE(buf));
}
static int ad9467_reg_access(struct adi_axi_adc_conv *conv, unsigned int reg,
static int ad9467_reg_access(struct iio_dev *indio_dev, unsigned int reg,
unsigned int writeval, unsigned int *readval)
{
struct ad9467_state *st = adi_axi_adc_conv_priv(conv);
struct ad9467_state *st = iio_priv(indio_dev);
struct spi_device *spi = st->spi;
int ret;
@ -191,10 +195,10 @@ static const unsigned int ad9467_scale_table[][2] = {
{2300, 8}, {2400, 9}, {2500, 10},
};
static void __ad9467_get_scale(struct adi_axi_adc_conv *conv, int index,
static void __ad9467_get_scale(struct ad9467_state *st, int index,
unsigned int *val, unsigned int *val2)
{
const struct adi_axi_adc_chip_info *info = conv->chip_info;
const struct ad9467_chip_info *info = st->info;
const struct iio_chan_spec *chan = &info->channels[0];
unsigned int tmp;
@ -229,52 +233,44 @@ static const struct iio_chan_spec ad9467_channels[] = {
};
static const struct ad9467_chip_info ad9467_chip_tbl = {
.axi_adc_info = {
.name = "ad9467",
.id = CHIPID_AD9467,
.max_rate = 250000000UL,
.scale_table = ad9467_scale_table,
.num_scales = ARRAY_SIZE(ad9467_scale_table),
.channels = ad9467_channels,
.num_channels = ARRAY_SIZE(ad9467_channels),
},
.name = "ad9467",
.id = CHIPID_AD9467,
.max_rate = 250000000UL,
.scale_table = ad9467_scale_table,
.num_scales = ARRAY_SIZE(ad9467_scale_table),
.channels = ad9467_channels,
.num_channels = ARRAY_SIZE(ad9467_channels),
.default_output_mode = AD9467_DEF_OUTPUT_MODE,
.vref_mask = AD9467_REG_VREF_MASK,
};
static const struct ad9467_chip_info ad9434_chip_tbl = {
.axi_adc_info = {
.name = "ad9434",
.id = CHIPID_AD9434,
.max_rate = 500000000UL,
.scale_table = ad9434_scale_table,
.num_scales = ARRAY_SIZE(ad9434_scale_table),
.channels = ad9434_channels,
.num_channels = ARRAY_SIZE(ad9434_channels),
},
.name = "ad9434",
.id = CHIPID_AD9434,
.max_rate = 500000000UL,
.scale_table = ad9434_scale_table,
.num_scales = ARRAY_SIZE(ad9434_scale_table),
.channels = ad9434_channels,
.num_channels = ARRAY_SIZE(ad9434_channels),
.default_output_mode = AD9434_DEF_OUTPUT_MODE,
.vref_mask = AD9434_REG_VREF_MASK,
};
static const struct ad9467_chip_info ad9265_chip_tbl = {
.axi_adc_info = {
.name = "ad9265",
.id = CHIPID_AD9265,
.max_rate = 125000000UL,
.scale_table = ad9265_scale_table,
.num_scales = ARRAY_SIZE(ad9265_scale_table),
.channels = ad9467_channels,
.num_channels = ARRAY_SIZE(ad9467_channels),
},
.name = "ad9265",
.id = CHIPID_AD9265,
.max_rate = 125000000UL,
.scale_table = ad9265_scale_table,
.num_scales = ARRAY_SIZE(ad9265_scale_table),
.channels = ad9467_channels,
.num_channels = ARRAY_SIZE(ad9467_channels),
.default_output_mode = AD9265_DEF_OUTPUT_MODE,
.vref_mask = AD9265_REG_VREF_MASK,
};
static int ad9467_get_scale(struct adi_axi_adc_conv *conv, int *val, int *val2)
static int ad9467_get_scale(struct ad9467_state *st, int *val, int *val2)
{
const struct adi_axi_adc_chip_info *info = conv->chip_info;
const struct ad9467_chip_info *info1 = to_ad9467_chip_info(info);
struct ad9467_state *st = adi_axi_adc_conv_priv(conv);
const struct ad9467_chip_info *info = st->info;
unsigned int i, vref_val;
int ret;
@ -282,7 +278,7 @@ static int ad9467_get_scale(struct adi_axi_adc_conv *conv, int *val, int *val2)
if (ret < 0)
return ret;
vref_val = ret & info1->vref_mask;
vref_val = ret & info->vref_mask;
for (i = 0; i < info->num_scales; i++) {
if (vref_val == info->scale_table[i][1])
@ -292,15 +288,14 @@ static int ad9467_get_scale(struct adi_axi_adc_conv *conv, int *val, int *val2)
if (i == info->num_scales)
return -ERANGE;
__ad9467_get_scale(conv, i, val, val2);
__ad9467_get_scale(st, i, val, val2);
return IIO_VAL_INT_PLUS_MICRO;
}
static int ad9467_set_scale(struct adi_axi_adc_conv *conv, int val, int val2)
static int ad9467_set_scale(struct ad9467_state *st, int val, int val2)
{
const struct adi_axi_adc_chip_info *info = conv->chip_info;
struct ad9467_state *st = adi_axi_adc_conv_priv(conv);
const struct ad9467_chip_info *info = st->info;
unsigned int scale_val[2];
unsigned int i;
int ret;
@ -309,7 +304,7 @@ static int ad9467_set_scale(struct adi_axi_adc_conv *conv, int val, int val2)
return -EINVAL;
for (i = 0; i < info->num_scales; i++) {
__ad9467_get_scale(conv, i, &scale_val[0], &scale_val[1]);
__ad9467_get_scale(st, i, &scale_val[0], &scale_val[1]);
if (scale_val[0] != val || scale_val[1] != val2)
continue;
@ -326,15 +321,15 @@ static int ad9467_set_scale(struct adi_axi_adc_conv *conv, int val, int val2)
return -EINVAL;
}
static int ad9467_read_raw(struct adi_axi_adc_conv *conv,
static int ad9467_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long m)
{
struct ad9467_state *st = adi_axi_adc_conv_priv(conv);
struct ad9467_state *st = iio_priv(indio_dev);
switch (m) {
case IIO_CHAN_INFO_SCALE:
return ad9467_get_scale(conv, val, val2);
return ad9467_get_scale(st, val, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
*val = clk_get_rate(st->clk);
@ -344,17 +339,17 @@ static int ad9467_read_raw(struct adi_axi_adc_conv *conv,
}
}
static int ad9467_write_raw(struct adi_axi_adc_conv *conv,
static int ad9467_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
const struct adi_axi_adc_chip_info *info = conv->chip_info;
struct ad9467_state *st = adi_axi_adc_conv_priv(conv);
struct ad9467_state *st = iio_priv(indio_dev);
const struct ad9467_chip_info *info = st->info;
long r_clk;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
return ad9467_set_scale(conv, val, val2);
return ad9467_set_scale(st, val, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
r_clk = clk_round_rate(st->clk, val);
if (r_clk < 0 || r_clk > info->max_rate) {
@ -369,13 +364,13 @@ static int ad9467_write_raw(struct adi_axi_adc_conv *conv,
}
}
static int ad9467_read_avail(struct adi_axi_adc_conv *conv,
static int ad9467_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
const struct adi_axi_adc_chip_info *info = conv->chip_info;
struct ad9467_state *st = adi_axi_adc_conv_priv(conv);
struct ad9467_state *st = iio_priv(indio_dev);
const struct ad9467_chip_info *info = st->info;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
@ -389,6 +384,33 @@ static int ad9467_read_avail(struct adi_axi_adc_conv *conv,
}
}
static int ad9467_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct ad9467_state *st = iio_priv(indio_dev);
unsigned int c;
int ret;
for (c = 0; c < st->info->num_channels; c++) {
if (test_bit(c, scan_mask))
ret = iio_backend_chan_enable(st->back, c);
else
ret = iio_backend_chan_disable(st->back, c);
if (ret)
return ret;
}
return 0;
}
static const struct iio_info ad9467_info = {
.read_raw = ad9467_read_raw,
.write_raw = ad9467_write_raw,
.update_scan_mode = ad9467_update_scan_mode,
.debugfs_reg_access = ad9467_reg_access,
.read_avail = ad9467_read_avail,
};
static int ad9467_outputmode_set(struct spi_device *spi, unsigned int mode)
{
int ret;
@ -401,10 +423,9 @@ static int ad9467_outputmode_set(struct spi_device *spi, unsigned int mode)
AN877_ADC_TRANSFER_SYNC);
}
static int ad9467_scale_fill(struct adi_axi_adc_conv *conv)
static int ad9467_scale_fill(struct ad9467_state *st)
{
const struct adi_axi_adc_chip_info *info = conv->chip_info;
struct ad9467_state *st = adi_axi_adc_conv_priv(conv);
const struct ad9467_chip_info *info = st->info;
unsigned int i, val1, val2;
st->scales = devm_kmalloc_array(&st->spi->dev, info->num_scales,
@ -413,7 +434,7 @@ static int ad9467_scale_fill(struct adi_axi_adc_conv *conv)
return -ENOMEM;
for (i = 0; i < info->num_scales; i++) {
__ad9467_get_scale(conv, i, &val1, &val2);
__ad9467_get_scale(st, i, &val1, &val2);
st->scales[i][0] = val1;
st->scales[i][1] = val2;
}
@ -421,11 +442,27 @@ static int ad9467_scale_fill(struct adi_axi_adc_conv *conv)
return 0;
}
static int ad9467_preenable_setup(struct adi_axi_adc_conv *conv)
static int ad9467_setup(struct ad9467_state *st)
{
struct ad9467_state *st = adi_axi_adc_conv_priv(conv);
struct iio_backend_data_fmt data = {
.sign_extend = true,
.enable = true,
};
unsigned int c, mode;
int ret;
return ad9467_outputmode_set(st->spi, st->output_mode);
mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
ret = ad9467_outputmode_set(st->spi, mode);
if (ret)
return ret;
for (c = 0; c < st->info->num_channels; c++) {
ret = iio_backend_data_format_set(st->back, c, &data);
if (ret)
return ret;
}
return 0;
}
static int ad9467_reset(struct device *dev)
@ -443,25 +480,65 @@ static int ad9467_reset(struct device *dev)
return 0;
}
static int ad9467_iio_backend_get(struct ad9467_state *st)
{
struct device *dev = &st->spi->dev;
struct device_node *__back;
st->back = devm_iio_backend_get(dev, NULL);
if (!IS_ERR(st->back))
return 0;
/* If not found, don't error out as we might have legacy DT property */
if (PTR_ERR(st->back) != -ENOENT)
return PTR_ERR(st->back);
/*
* if we don't get the backend using the normal API's, use the legacy
* 'adi,adc-dev' property. So we get all nodes with that property, and
* look for the one pointing at us. Then we directly lookup that fwnode
* on the backend list of registered devices. This is done so we don't
* make io-backends mandatory which would break DT ABI.
*/
for_each_node_with_property(__back, "adi,adc-dev") {
struct device_node *__me;
__me = of_parse_phandle(__back, "adi,adc-dev", 0);
if (!__me)
continue;
if (!device_match_of_node(dev, __me)) {
of_node_put(__me);
continue;
}
of_node_put(__me);
st->back = __devm_iio_backend_get_from_fwnode_lookup(dev,
of_fwnode_handle(__back));
of_node_put(__back);
return PTR_ERR_OR_ZERO(st->back);
}
return -ENODEV;
}
static int ad9467_probe(struct spi_device *spi)
{
const struct ad9467_chip_info *info;
struct adi_axi_adc_conv *conv;
struct iio_dev *indio_dev;
struct ad9467_state *st;
unsigned int id;
int ret;
info = spi_get_device_match_data(spi);
if (!info)
return -ENODEV;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
conv = devm_adi_axi_adc_conv_register(&spi->dev, sizeof(*st));
if (IS_ERR(conv))
return PTR_ERR(conv);
st = adi_axi_adc_conv_priv(conv);
st = iio_priv(indio_dev);
st->spi = spi;
st->info = spi_get_device_match_data(spi);
if (!st->info)
return -ENODEV;
st->clk = devm_clk_get_enabled(&spi->dev, "adc-clk");
if (IS_ERR(st->clk))
return PTR_ERR(st->clk);
@ -475,29 +552,39 @@ static int ad9467_probe(struct spi_device *spi)
if (ret)
return ret;
conv->chip_info = &info->axi_adc_info;
ret = ad9467_scale_fill(conv);
ret = ad9467_scale_fill(st);
if (ret)
return ret;
id = ad9467_spi_read(spi, AN877_ADC_REG_CHIP_ID);
if (id != conv->chip_info->id) {
if (id != st->info->id) {
dev_err(&spi->dev, "Mismatch CHIP_ID, got 0x%X, expected 0x%X\n",
id, conv->chip_info->id);
id, st->info->id);
return -ENODEV;
}
conv->reg_access = ad9467_reg_access;
conv->write_raw = ad9467_write_raw;
conv->read_raw = ad9467_read_raw;
conv->read_avail = ad9467_read_avail;
conv->preenable_setup = ad9467_preenable_setup;
indio_dev->name = st->info->name;
indio_dev->channels = st->info->channels;
indio_dev->num_channels = st->info->num_channels;
indio_dev->info = &ad9467_info;
st->output_mode = info->default_output_mode |
AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT;
ret = ad9467_iio_backend_get(st);
if (ret)
return ret;
return 0;
ret = devm_iio_backend_request_buffer(&spi->dev, st->back, indio_dev);
if (ret)
return ret;
ret = devm_iio_backend_enable(&spi->dev, st->back);
if (ret)
return ret;
ret = ad9467_setup(st);
if (ret)
return ret;
return devm_iio_device_register(&spi->dev, indio_dev);
}
static const struct of_device_id ad9467_of_match[] = {
@ -529,4 +616,4 @@ module_spi_driver(ad9467_driver);
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD9467 ADC driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(IIO_ADI_AXI);
MODULE_IMPORT_NS(IIO_BACKEND);

View File

@ -568,6 +568,7 @@ EXPORT_SYMBOL_NS_GPL(ad_sd_validate_trigger, IIO_AD_SIGMA_DELTA);
static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_dev)
{
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
unsigned long irq_flags = irq_get_trigger_type(sigma_delta->spi->irq);
int ret;
if (dev != &sigma_delta->spi->dev) {
@ -588,9 +589,13 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de
/* the IRQ core clears IRQ_DISABLE_UNLAZY flag when freeing an IRQ */
irq_set_status_flags(sigma_delta->spi->irq, IRQ_DISABLE_UNLAZY);
/* Allow overwriting the flags from firmware */
if (!irq_flags)
irq_flags = sigma_delta->info->irq_flags;
ret = devm_request_irq(dev, sigma_delta->spi->irq,
ad_sd_data_rdy_trig_poll,
sigma_delta->info->irq_flags | IRQF_NO_AUTOEN,
irq_flags | IRQF_NO_AUTOEN,
indio_dev->name,
sigma_delta);
if (ret)

View File

@ -8,6 +8,7 @@
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/module.h>
@ -17,13 +18,12 @@
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/buffer-dmaengine.h>
#include <linux/fpga/adi-axi-common.h>
#include <linux/iio/adc/adi-axi-adc.h>
#include <linux/iio/backend.h>
#include <linux/iio/buffer-dmaengine.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
/*
* Register definitions:
@ -44,6 +44,7 @@
#define ADI_AXI_REG_CHAN_CTRL_PN_SEL_OWR BIT(10)
#define ADI_AXI_REG_CHAN_CTRL_IQCOR_EN BIT(9)
#define ADI_AXI_REG_CHAN_CTRL_DCFILT_EN BIT(8)
#define ADI_AXI_REG_CHAN_CTRL_FMT_MASK GENMASK(6, 4)
#define ADI_AXI_REG_CHAN_CTRL_FMT_SIGNEXT BIT(6)
#define ADI_AXI_REG_CHAN_CTRL_FMT_TYPE BIT(5)
#define ADI_AXI_REG_CHAN_CTRL_FMT_EN BIT(4)
@ -55,286 +56,100 @@
ADI_AXI_REG_CHAN_CTRL_FMT_EN | \
ADI_AXI_REG_CHAN_CTRL_ENABLE)
struct adi_axi_adc_core_info {
unsigned int version;
};
struct adi_axi_adc_state {
struct mutex lock;
struct adi_axi_adc_client *client;
struct regmap *regmap;
};
struct adi_axi_adc_client {
struct list_head entry;
struct adi_axi_adc_conv conv;
struct adi_axi_adc_state *state;
struct device *dev;
const struct adi_axi_adc_core_info *info;
};
static LIST_HEAD(registered_clients);
static DEFINE_MUTEX(registered_clients_lock);
static struct adi_axi_adc_client *conv_to_client(struct adi_axi_adc_conv *conv)
static int axi_adc_enable(struct iio_backend *back)
{
return container_of(conv, struct adi_axi_adc_client, conv);
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
int ret;
ret = regmap_set_bits(st->regmap, ADI_AXI_REG_RSTN,
ADI_AXI_REG_RSTN_MMCM_RSTN);
if (ret)
return ret;
fsleep(10000);
return regmap_set_bits(st->regmap, ADI_AXI_REG_RSTN,
ADI_AXI_REG_RSTN_RSTN | ADI_AXI_REG_RSTN_MMCM_RSTN);
}
void *adi_axi_adc_conv_priv(struct adi_axi_adc_conv *conv)
static void axi_adc_disable(struct iio_backend *back)
{
struct adi_axi_adc_client *cl = conv_to_client(conv);
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
return (char *)cl + ALIGN(sizeof(struct adi_axi_adc_client),
IIO_DMA_MINALIGN);
regmap_write(st->regmap, ADI_AXI_REG_RSTN, 0);
}
EXPORT_SYMBOL_NS_GPL(adi_axi_adc_conv_priv, IIO_ADI_AXI);
static int adi_axi_adc_config_dma_buffer(struct device *dev,
struct iio_dev *indio_dev)
static int axi_adc_data_format_set(struct iio_backend *back, unsigned int chan,
const struct iio_backend_data_fmt *data)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
u32 val;
if (!data->enable)
return regmap_clear_bits(st->regmap,
ADI_AXI_REG_CHAN_CTRL(chan),
ADI_AXI_REG_CHAN_CTRL_FMT_EN);
val = FIELD_PREP(ADI_AXI_REG_CHAN_CTRL_FMT_EN, true);
if (data->sign_extend)
val |= FIELD_PREP(ADI_AXI_REG_CHAN_CTRL_FMT_SIGNEXT, true);
if (data->type == IIO_BACKEND_OFFSET_BINARY)
val |= FIELD_PREP(ADI_AXI_REG_CHAN_CTRL_FMT_TYPE, true);
return regmap_update_bits(st->regmap, ADI_AXI_REG_CHAN_CTRL(chan),
ADI_AXI_REG_CHAN_CTRL_FMT_MASK, val);
}
static int axi_adc_chan_enable(struct iio_backend *back, unsigned int chan)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
return regmap_set_bits(st->regmap, ADI_AXI_REG_CHAN_CTRL(chan),
ADI_AXI_REG_CHAN_CTRL_ENABLE);
}
static int axi_adc_chan_disable(struct iio_backend *back, unsigned int chan)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
return regmap_clear_bits(st->regmap, ADI_AXI_REG_CHAN_CTRL(chan),
ADI_AXI_REG_CHAN_CTRL_ENABLE);
}
static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back,
struct iio_dev *indio_dev)
{
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
struct iio_buffer *buffer;
const char *dma_name;
int ret;
if (!device_property_present(dev, "dmas"))
return 0;
if (device_property_read_string(dev, "dma-names", &dma_name))
if (device_property_read_string(st->dev, "dma-names", &dma_name))
dma_name = "rx";
return devm_iio_dmaengine_buffer_setup(indio_dev->dev.parent,
indio_dev, dma_name);
}
static int adi_axi_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct adi_axi_adc_state *st = iio_priv(indio_dev);
struct adi_axi_adc_conv *conv = &st->client->conv;
if (!conv->read_raw)
return -EOPNOTSUPP;
return conv->read_raw(conv, chan, val, val2, mask);
}
static int adi_axi_adc_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct adi_axi_adc_state *st = iio_priv(indio_dev);
struct adi_axi_adc_conv *conv = &st->client->conv;
if (!conv->write_raw)
return -EOPNOTSUPP;
return conv->write_raw(conv, chan, val, val2, mask);
}
static int adi_axi_adc_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
struct adi_axi_adc_state *st = iio_priv(indio_dev);
struct adi_axi_adc_conv *conv = &st->client->conv;
if (!conv->read_avail)
return -EOPNOTSUPP;
return conv->read_avail(conv, chan, vals, type, length, mask);
}
static int adi_axi_adc_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct adi_axi_adc_state *st = iio_priv(indio_dev);
struct adi_axi_adc_conv *conv = &st->client->conv;
unsigned int i;
int ret;
for (i = 0; i < conv->chip_info->num_channels; i++) {
if (test_bit(i, scan_mask))
ret = regmap_set_bits(st->regmap,
ADI_AXI_REG_CHAN_CTRL(i),
ADI_AXI_REG_CHAN_CTRL_ENABLE);
else
ret = regmap_clear_bits(st->regmap,
ADI_AXI_REG_CHAN_CTRL(i),
ADI_AXI_REG_CHAN_CTRL_ENABLE);
if (ret)
return ret;
buffer = iio_dmaengine_buffer_alloc(st->dev, dma_name);
if (IS_ERR(buffer)) {
dev_err(st->dev, "Could not get DMA buffer, %ld\n",
PTR_ERR(buffer));
return ERR_CAST(buffer);
}
return 0;
}
static struct adi_axi_adc_conv *adi_axi_adc_conv_register(struct device *dev,
size_t sizeof_priv)
{
struct adi_axi_adc_client *cl;
size_t alloc_size;
alloc_size = ALIGN(sizeof(struct adi_axi_adc_client), IIO_DMA_MINALIGN);
if (sizeof_priv)
alloc_size += ALIGN(sizeof_priv, IIO_DMA_MINALIGN);
cl = kzalloc(alloc_size, GFP_KERNEL);
if (!cl)
return ERR_PTR(-ENOMEM);
mutex_lock(&registered_clients_lock);
cl->dev = get_device(dev);
list_add_tail(&cl->entry, &registered_clients);
mutex_unlock(&registered_clients_lock);
return &cl->conv;
}
static void adi_axi_adc_conv_unregister(struct adi_axi_adc_conv *conv)
{
struct adi_axi_adc_client *cl = conv_to_client(conv);
mutex_lock(&registered_clients_lock);
list_del(&cl->entry);
put_device(cl->dev);
mutex_unlock(&registered_clients_lock);
kfree(cl);
}
static void devm_adi_axi_adc_conv_release(void *conv)
{
adi_axi_adc_conv_unregister(conv);
}
struct adi_axi_adc_conv *devm_adi_axi_adc_conv_register(struct device *dev,
size_t sizeof_priv)
{
struct adi_axi_adc_conv *conv;
int ret;
conv = adi_axi_adc_conv_register(dev, sizeof_priv);
if (IS_ERR(conv))
return conv;
ret = devm_add_action_or_reset(dev, devm_adi_axi_adc_conv_release,
conv);
indio_dev->modes |= INDIO_BUFFER_HARDWARE;
ret = iio_device_attach_buffer(indio_dev, buffer);
if (ret)
return ERR_PTR(ret);
return conv;
}
EXPORT_SYMBOL_NS_GPL(devm_adi_axi_adc_conv_register, IIO_ADI_AXI);
static const struct iio_info adi_axi_adc_info = {
.read_raw = &adi_axi_adc_read_raw,
.write_raw = &adi_axi_adc_write_raw,
.update_scan_mode = &adi_axi_adc_update_scan_mode,
.read_avail = &adi_axi_adc_read_avail,
};
static const struct adi_axi_adc_core_info adi_axi_adc_10_0_a_info = {
.version = ADI_AXI_PCORE_VER(10, 0, 'a'),
};
static struct adi_axi_adc_client *adi_axi_adc_attach_client(struct device *dev)
{
const struct adi_axi_adc_core_info *info;
struct adi_axi_adc_client *cl;
struct device_node *cln;
info = of_device_get_match_data(dev);
if (!info)
return ERR_PTR(-ENODEV);
cln = of_parse_phandle(dev->of_node, "adi,adc-dev", 0);
if (!cln) {
dev_err(dev, "No 'adi,adc-dev' node defined\n");
return ERR_PTR(-ENODEV);
}
mutex_lock(&registered_clients_lock);
list_for_each_entry(cl, &registered_clients, entry) {
if (!cl->dev)
continue;
if (cl->dev->of_node != cln)
continue;
if (!try_module_get(cl->dev->driver->owner)) {
mutex_unlock(&registered_clients_lock);
of_node_put(cln);
return ERR_PTR(-ENODEV);
}
get_device(cl->dev);
cl->info = info;
mutex_unlock(&registered_clients_lock);
of_node_put(cln);
return cl;
}
mutex_unlock(&registered_clients_lock);
of_node_put(cln);
return ERR_PTR(-EPROBE_DEFER);
return buffer;
}
static int adi_axi_adc_setup_channels(struct device *dev,
struct adi_axi_adc_state *st)
static void axi_adc_free_buffer(struct iio_backend *back,
struct iio_buffer *buffer)
{
struct adi_axi_adc_conv *conv = &st->client->conv;
int i, ret;
if (conv->preenable_setup) {
ret = conv->preenable_setup(conv);
if (ret)
return ret;
}
for (i = 0; i < conv->chip_info->num_channels; i++) {
ret = regmap_write(st->regmap, ADI_AXI_REG_CHAN_CTRL(i),
ADI_AXI_REG_CHAN_CTRL_DEFAULTS);
if (ret)
return ret;
}
return 0;
}
static int axi_adc_reset(struct adi_axi_adc_state *st)
{
int ret;
ret = regmap_write(st->regmap, ADI_AXI_REG_RSTN, 0);
if (ret)
return ret;
mdelay(10);
ret = regmap_write(st->regmap, ADI_AXI_REG_RSTN,
ADI_AXI_REG_RSTN_MMCM_RSTN);
if (ret)
return ret;
mdelay(10);
return regmap_write(st->regmap, ADI_AXI_REG_RSTN,
ADI_AXI_REG_RSTN_RSTN | ADI_AXI_REG_RSTN_MMCM_RSTN);
}
static void adi_axi_adc_cleanup(void *data)
{
struct adi_axi_adc_client *cl = data;
put_device(cl->dev);
module_put(cl->dev->driver->owner);
iio_dmaengine_buffer_free(buffer);
}
static const struct regmap_config axi_adc_regmap_config = {
@ -344,45 +159,47 @@ static const struct regmap_config axi_adc_regmap_config = {
.max_register = 0x0800,
};
static const struct iio_backend_ops adi_axi_adc_generic = {
.enable = axi_adc_enable,
.disable = axi_adc_disable,
.data_format_set = axi_adc_data_format_set,
.chan_enable = axi_adc_chan_enable,
.chan_disable = axi_adc_chan_disable,
.request_buffer = axi_adc_request_buffer,
.free_buffer = axi_adc_free_buffer,
};
static int adi_axi_adc_probe(struct platform_device *pdev)
{
struct adi_axi_adc_conv *conv;
struct iio_dev *indio_dev;
struct adi_axi_adc_client *cl;
const unsigned int *expected_ver;
struct adi_axi_adc_state *st;
void __iomem *base;
unsigned int ver;
int ret;
cl = adi_axi_adc_attach_client(&pdev->dev);
if (IS_ERR(cl))
return PTR_ERR(cl);
ret = devm_add_action_or_reset(&pdev->dev, adi_axi_adc_cleanup, cl);
if (ret)
return ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
if (indio_dev == NULL)
st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
if (!st)
return -ENOMEM;
st = iio_priv(indio_dev);
st->client = cl;
cl->state = st;
mutex_init(&st->lock);
base = devm_platform_ioremap_resource(pdev, 0);
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);
if (IS_ERR(st->regmap))
return PTR_ERR(st->regmap);
conv = &st->client->conv;
expected_ver = device_get_match_data(&pdev->dev);
if (!expected_ver)
return -ENODEV;
ret = axi_adc_reset(st);
/*
* Force disable the core. Up to the frontend to enable us. And we can
* still read/write registers...
*/
ret = regmap_write(st->regmap, ADI_AXI_REG_RSTN, 0);
if (ret)
return ret;
@ -390,33 +207,19 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
if (ret)
return ret;
if (cl->info->version > ver) {
if (*expected_ver > ver) {
dev_err(&pdev->dev,
"IP core version is too old. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
ADI_AXI_PCORE_VER_MAJOR(cl->info->version),
ADI_AXI_PCORE_VER_MINOR(cl->info->version),
ADI_AXI_PCORE_VER_PATCH(cl->info->version),
ADI_AXI_PCORE_VER_MAJOR(*expected_ver),
ADI_AXI_PCORE_VER_MINOR(*expected_ver),
ADI_AXI_PCORE_VER_PATCH(*expected_ver),
ADI_AXI_PCORE_VER_MAJOR(ver),
ADI_AXI_PCORE_VER_MINOR(ver),
ADI_AXI_PCORE_VER_PATCH(ver));
return -ENODEV;
}
indio_dev->info = &adi_axi_adc_info;
indio_dev->name = "adi-axi-adc";
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->num_channels = conv->chip_info->num_channels;
indio_dev->channels = conv->chip_info->channels;
ret = adi_axi_adc_config_dma_buffer(&pdev->dev, indio_dev);
if (ret)
return ret;
ret = adi_axi_adc_setup_channels(&pdev->dev, st);
if (ret)
return ret;
ret = devm_iio_device_register(&pdev->dev, indio_dev);
ret = devm_iio_backend_register(&pdev->dev, &adi_axi_adc_generic, st);
if (ret)
return ret;
@ -428,6 +231,8 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
return 0;
}
static unsigned int adi_axi_adc_10_0_a_info = ADI_AXI_PCORE_VER(10, 0, 'a');
/* Match table for of_platform binding */
static const struct of_device_id adi_axi_adc_of_match[] = {
{ .compatible = "adi,axi-adc-10.0.a", .data = &adi_axi_adc_10_0_a_info },
@ -447,3 +252,5 @@ module_platform_driver(adi_axi_adc_driver);
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices Generic AXI ADC IP core driver");
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(IIO_DMAENGINE_BUFFER);
MODULE_IMPORT_NS(IIO_BACKEND);

View File

@ -357,62 +357,55 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev,
int *val,
long m)
{
int ret = 0;
s32 data;
u8 rxbuf[2];
struct max1363_state *st = iio_priv(indio_dev);
struct i2c_client *client = st->client;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
s32 data;
u8 rxbuf[2];
struct max1363_state *st = iio_priv(indio_dev);
struct i2c_client *client = st->client;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
/*
* If monitor mode is enabled, the method for reading a single
* channel will have to be rather different and has not yet
* been implemented.
*
* Also, cannot read directly if buffered capture enabled.
*/
if (st->monitor_on) {
ret = -EBUSY;
goto error_ret;
}
/*
* If monitor mode is enabled, the method for reading a single
* channel will have to be rather different and has not yet
* been implemented.
*
* Also, cannot read directly if buffered capture enabled.
*/
if (st->monitor_on)
return -EBUSY;
/* Check to see if current scan mode is correct */
if (st->current_mode != &max1363_mode_table[chan->address]) {
/* Update scan mode if needed */
st->current_mode = &max1363_mode_table[chan->address];
ret = max1363_set_scan_mode(st);
if (ret < 0)
goto error_ret;
}
if (st->chip_info->bits != 8) {
/* Get reading */
data = st->recv(client, rxbuf, 2);
if (data < 0) {
ret = data;
goto error_ret;
/* Check to see if current scan mode is correct */
if (st->current_mode != &max1363_mode_table[chan->address]) {
int ret;
/* Update scan mode if needed */
st->current_mode = &max1363_mode_table[chan->address];
ret = max1363_set_scan_mode(st);
if (ret < 0)
return ret;
}
data = (rxbuf[1] | rxbuf[0] << 8) &
((1 << st->chip_info->bits) - 1);
} else {
/* Get reading */
data = st->recv(client, rxbuf, 1);
if (data < 0) {
ret = data;
goto error_ret;
if (st->chip_info->bits != 8) {
/* Get reading */
data = st->recv(client, rxbuf, 2);
if (data < 0)
return data;
data = (rxbuf[1] | rxbuf[0] << 8) &
((1 << st->chip_info->bits) - 1);
} else {
/* Get reading */
data = st->recv(client, rxbuf, 1);
if (data < 0)
return data;
data = rxbuf[0];
}
data = rxbuf[0];
*val = data;
return 0;
}
*val = data;
error_ret:
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
return ret;
unreachable();
}
static int max1363_read_raw(struct iio_dev *indio_dev,
@ -710,9 +703,8 @@ static ssize_t max1363_monitor_store_freq(struct device *dev,
if (!found)
return -EINVAL;
mutex_lock(&st->lock);
st->monitor_speed = i;
mutex_unlock(&st->lock);
scoped_guard(mutex, &st->lock)
st->monitor_speed = i;
return 0;
}
@ -815,12 +807,11 @@ static int max1363_read_event_config(struct iio_dev *indio_dev,
int val;
int number = chan->channel;
mutex_lock(&st->lock);
guard(mutex)(&st->lock);
if (dir == IIO_EV_DIR_FALLING)
val = (1 << number) & st->mask_low;
else
val = (1 << number) & st->mask_high;
mutex_unlock(&st->lock);
return val;
}
@ -962,46 +953,42 @@ static int max1363_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan, enum iio_event_type type,
enum iio_event_direction dir, int state)
{
int ret = 0;
struct max1363_state *st = iio_priv(indio_dev);
u16 unifiedmask;
int number = chan->channel;
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
mutex_lock(&st->lock);
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
int number = chan->channel;
u16 unifiedmask;
int ret;
unifiedmask = st->mask_low | st->mask_high;
if (dir == IIO_EV_DIR_FALLING) {
guard(mutex)(&st->lock);
if (state == 0)
st->mask_low &= ~(1 << number);
else {
ret = __max1363_check_event_mask((1 << number),
unifiedmask);
if (ret)
goto error_ret;
st->mask_low |= (1 << number);
}
} else {
if (state == 0)
st->mask_high &= ~(1 << number);
else {
ret = __max1363_check_event_mask((1 << number),
unifiedmask);
if (ret)
goto error_ret;
st->mask_high |= (1 << number);
unifiedmask = st->mask_low | st->mask_high;
if (dir == IIO_EV_DIR_FALLING) {
if (state == 0)
st->mask_low &= ~(1 << number);
else {
ret = __max1363_check_event_mask((1 << number),
unifiedmask);
if (ret)
return ret;
st->mask_low |= (1 << number);
}
} else {
if (state == 0)
st->mask_high &= ~(1 << number);
else {
ret = __max1363_check_event_mask((1 << number),
unifiedmask);
if (ret)
return ret;
st->mask_high |= (1 << number);
}
}
}
max1363_monitor_mode_update(st, !!(st->mask_high | st->mask_low));
error_ret:
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
return ret;
return 0;
}
/*

View File

@ -39,6 +39,10 @@
#define RTQ6056_DEFAULT_CONFIG 0x4127
#define RTQ6056_CONT_ALLON 7
#define RTQ6059_DEFAULT_CONFIG 0x3C47
#define RTQ6059_VBUS_LSB_OFFSET 3
#define RTQ6059_AVG_BASE 8
enum {
RTQ6056_CH_VSHUNT = 0,
RTQ6056_CH_VBUS,
@ -47,19 +51,46 @@ enum {
RTQ6056_MAX_CHANNEL
};
/*
* The enum is to present the 0x00 CONFIG RG bitfield for the 16bit RG value
* field value order from LSB to MSB
* RTQ6053/6 is OPMODE->VSHUNTCT->VBUSCT->AVG->RESET
* RTQ6059 is OPMODE->SADC->BADC->PGA->RESET
*/
enum {
F_OPMODE = 0,
F_VSHUNTCT,
F_RTQ6059_SADC = F_VSHUNTCT,
F_VBUSCT,
F_RTQ6059_BADC = F_VBUSCT,
F_AVG,
F_RTQ6059_PGA = F_AVG,
F_RESET,
F_MAX_FIELDS
};
struct rtq6056_priv;
struct richtek_dev_data {
bool fixed_samp_freq;
u8 vbus_offset;
int default_conv_time_us;
unsigned int default_config;
unsigned int calib_coefficient;
const int *avg_sample_list;
int avg_sample_list_length;
const struct reg_field *reg_fields;
const struct iio_chan_spec *channels;
int num_channels;
int (*read_scale)(struct iio_chan_spec const *ch, int *val, int *val2);
int (*set_average)(struct rtq6056_priv *priv, int val);
};
struct rtq6056_priv {
struct device *dev;
struct regmap *regmap;
struct regmap_field *rm_fields[F_MAX_FIELDS];
const struct richtek_dev_data *devdata;
u32 shunt_resistor_uohm;
int vshuntct_us;
int vbusct_us;
@ -74,6 +105,14 @@ static const struct reg_field rtq6056_reg_fields[F_MAX_FIELDS] = {
[F_RESET] = REG_FIELD(RTQ6056_REG_CONFIG, 15, 15),
};
static const struct reg_field rtq6059_reg_fields[F_MAX_FIELDS] = {
[F_OPMODE] = REG_FIELD(RTQ6056_REG_CONFIG, 0, 2),
[F_RTQ6059_SADC] = REG_FIELD(RTQ6056_REG_CONFIG, 3, 6),
[F_RTQ6059_BADC] = REG_FIELD(RTQ6056_REG_CONFIG, 7, 10),
[F_RTQ6059_PGA] = REG_FIELD(RTQ6056_REG_CONFIG, 11, 12),
[F_RESET] = REG_FIELD(RTQ6056_REG_CONFIG, 15, 15),
};
static const struct iio_chan_spec rtq6056_channels[RTQ6056_MAX_CHANNEL + 1] = {
{
.type = IIO_VOLTAGE,
@ -151,10 +190,93 @@ static const struct iio_chan_spec rtq6056_channels[RTQ6056_MAX_CHANNEL + 1] = {
IIO_CHAN_SOFT_TIMESTAMP(RTQ6056_MAX_CHANNEL),
};
/*
* Difference between RTQ6056 and RTQ6059
* - Fixed sampling conversion time
* - Average sample numbers
* - Channel scale
* - calibration coefficient
*/
static const struct iio_chan_spec rtq6059_channels[RTQ6056_MAX_CHANNEL + 1] = {
{
.type = IIO_VOLTAGE,
.indexed = 1,
.channel = 0,
.address = RTQ6056_REG_SHUNTVOLT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.scan_index = 0,
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU,
},
},
{
.type = IIO_VOLTAGE,
.indexed = 1,
.channel = 1,
.address = RTQ6056_REG_BUSVOLT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.scan_index = 1,
.scan_type = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU,
},
},
{
.type = IIO_POWER,
.indexed = 1,
.channel = 2,
.address = RTQ6056_REG_POWER,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.scan_index = 2,
.scan_type = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU,
},
},
{
.type = IIO_CURRENT,
.indexed = 1,
.channel = 3,
.address = RTQ6056_REG_CURRENT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SAMP_FREQ),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
.scan_index = 3,
.scan_type = {
.sign = 's',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_CPU,
},
},
IIO_CHAN_SOFT_TIMESTAMP(RTQ6056_MAX_CHANNEL),
};
static int rtq6056_adc_read_channel(struct rtq6056_priv *priv,
struct iio_chan_spec const *ch,
int *val)
{
const struct richtek_dev_data *devdata = priv->devdata;
struct device *dev = priv->dev;
unsigned int addr = ch->address;
unsigned int regval;
@ -168,12 +290,21 @@ static int rtq6056_adc_read_channel(struct rtq6056_priv *priv,
return ret;
/* Power and VBUS is unsigned 16-bit, others are signed 16-bit */
if (addr == RTQ6056_REG_BUSVOLT || addr == RTQ6056_REG_POWER)
switch (addr) {
case RTQ6056_REG_BUSVOLT:
regval >>= devdata->vbus_offset;
*val = regval;
else
return IIO_VAL_INT;
case RTQ6056_REG_POWER:
*val = regval;
return IIO_VAL_INT;
case RTQ6056_REG_SHUNTVOLT:
case RTQ6056_REG_CURRENT:
*val = sign_extend32(regval, 16);
return IIO_VAL_INT;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int rtq6056_adc_read_scale(struct iio_chan_spec const *ch, int *val,
@ -199,6 +330,28 @@ static int rtq6056_adc_read_scale(struct iio_chan_spec const *ch, int *val,
}
}
static int rtq6059_adc_read_scale(struct iio_chan_spec const *ch, int *val,
int *val2)
{
switch (ch->address) {
case RTQ6056_REG_SHUNTVOLT:
/* VSHUNT lsb 10uV */
*val = 10000;
*val2 = 1000000;
return IIO_VAL_FRACTIONAL;
case RTQ6056_REG_BUSVOLT:
/* VBUS lsb 4mV */
*val = 4;
return IIO_VAL_INT;
case RTQ6056_REG_POWER:
/* Power lsb 20mW */
*val = 20;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
/*
* Sample frequency for channel VSHUNT and VBUS. The indices correspond
* with the bit value expected by the chip. And it can be found at
@ -248,6 +401,10 @@ static const int rtq6056_avg_sample_list[] = {
1, 4, 16, 64, 128, 256, 512, 1024,
};
static const int rtq6059_avg_sample_list[] = {
1, 2, 4, 8, 16, 32, 64, 128,
};
static int rtq6056_adc_set_average(struct rtq6056_priv *priv, int val)
{
unsigned int selector;
@ -268,6 +425,30 @@ static int rtq6056_adc_set_average(struct rtq6056_priv *priv, int val)
return 0;
}
static int rtq6059_adc_set_average(struct rtq6056_priv *priv, int val)
{
unsigned int selector;
int ret;
if (val > 128 || val < 1)
return -EINVAL;
/* The supported average sample is 2^x (x from 0 to 7) */
selector = fls(val) - 1;
ret = regmap_field_write(priv->rm_fields[F_RTQ6059_BADC],
RTQ6059_AVG_BASE + selector);
if (ret)
return ret;
ret = regmap_field_write(priv->rm_fields[F_RTQ6059_SADC],
RTQ6059_AVG_BASE + selector);
priv->avg_sample = BIT(selector);
return 0;
}
static int rtq6056_adc_get_sample_freq(struct rtq6056_priv *priv,
struct iio_chan_spec const *ch, int *val)
{
@ -292,12 +473,13 @@ static int rtq6056_adc_read_raw(struct iio_dev *indio_dev,
int *val2, long mask)
{
struct rtq6056_priv *priv = iio_priv(indio_dev);
const struct richtek_dev_data *devdata = priv->devdata;
switch (mask) {
case IIO_CHAN_INFO_RAW:
return rtq6056_adc_read_channel(priv, chan, val);
case IIO_CHAN_INFO_SCALE:
return rtq6056_adc_read_scale(chan, val, val2);
return devdata->read_scale(chan, val, val2);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*val = priv->avg_sample;
return IIO_VAL_INT;
@ -313,6 +495,9 @@ static int rtq6056_adc_read_avail(struct iio_dev *indio_dev,
const int **vals, int *type, int *length,
long mask)
{
struct rtq6056_priv *priv = iio_priv(indio_dev);
const struct richtek_dev_data *devdata = priv->devdata;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
*vals = rtq6056_samp_freq_list;
@ -320,9 +505,9 @@ static int rtq6056_adc_read_avail(struct iio_dev *indio_dev,
*length = ARRAY_SIZE(rtq6056_samp_freq_list);
return IIO_AVAIL_LIST;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*vals = rtq6056_avg_sample_list;
*vals = devdata->avg_sample_list;
*length = devdata->avg_sample_list_length;
*type = IIO_VAL_INT;
*length = ARRAY_SIZE(rtq6056_avg_sample_list);
return IIO_AVAIL_LIST;
default:
return -EINVAL;
@ -334,6 +519,7 @@ static int rtq6056_adc_write_raw(struct iio_dev *indio_dev,
int val2, long mask)
{
struct rtq6056_priv *priv = iio_priv(indio_dev);
const struct richtek_dev_data *devdata = priv->devdata;
int ret;
ret = iio_device_claim_direct_mode(indio_dev);
@ -342,10 +528,15 @@ static int rtq6056_adc_write_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
if (devdata->fixed_samp_freq) {
ret = -EINVAL;
break;
}
ret = rtq6056_adc_set_samp_freq(priv, chan, val);
break;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
ret = rtq6056_adc_set_average(priv, val);
ret = devdata->set_average(priv, val);
break;
default:
ret = -EINVAL;
@ -374,6 +565,7 @@ static int rtq6056_adc_read_label(struct iio_dev *indio_dev,
static int rtq6056_set_shunt_resistor(struct rtq6056_priv *priv,
int resistor_uohm)
{
const struct richtek_dev_data *devdata = priv->devdata;
unsigned int calib_val;
int ret;
@ -382,8 +574,8 @@ static int rtq6056_set_shunt_resistor(struct rtq6056_priv *priv,
return -EINVAL;
}
/* calibration = 5120000 / (Rshunt (uOhm) * current lsb (1mA)) */
calib_val = 5120000 / resistor_uohm;
/* calibration = coefficient / (Rshunt (uOhm) * current lsb (1mA)) */
calib_val = devdata->calib_coefficient / resistor_uohm;
ret = regmap_write(priv->regmap, RTQ6056_REG_CALIBRATION, calib_val);
if (ret)
return ret;
@ -450,6 +642,7 @@ static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct rtq6056_priv *priv = iio_priv(indio_dev);
const struct richtek_dev_data *devdata = priv->devdata;
struct device *dev = priv->dev;
struct {
u16 vals[RTQ6056_MAX_CHANNEL];
@ -469,6 +662,9 @@ static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p)
if (ret)
goto out;
if (addr == RTQ6056_REG_BUSVOLT)
raw >>= devdata->vbus_offset;
data.vals[i++] = raw;
}
@ -528,20 +724,26 @@ static int rtq6056_probe(struct i2c_client *i2c)
struct rtq6056_priv *priv;
struct device *dev = &i2c->dev;
struct regmap *regmap;
const struct richtek_dev_data *devdata;
unsigned int vendor_id, shunt_resistor_uohm;
int ret;
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -EOPNOTSUPP;
devdata = device_get_match_data(dev);
if (!devdata)
return dev_err_probe(dev, -EINVAL, "Invalid dev data\n");
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
if (!indio_dev)
return -ENOMEM;
priv = iio_priv(indio_dev);
priv->dev = dev;
priv->vshuntct_us = priv->vbusct_us = 1037;
priv->vshuntct_us = priv->vbusct_us = devdata->default_conv_time_us;
priv->avg_sample = 1;
priv->devdata = devdata;
i2c_set_clientdata(i2c, priv);
regmap = devm_regmap_init_i2c(i2c, &rtq6056_regmap_config);
@ -561,15 +763,11 @@ static int rtq6056_probe(struct i2c_client *i2c)
"Invalid vendor id 0x%04x\n", vendor_id);
ret = devm_regmap_field_bulk_alloc(dev, regmap, priv->rm_fields,
rtq6056_reg_fields, F_MAX_FIELDS);
devdata->reg_fields, F_MAX_FIELDS);
if (ret)
return dev_err_probe(dev, ret, "Failed to init regmap field\n");
/*
* By default, configure average sample as 1, bus and shunt conversion
* time as 1037 microsecond, and operating mode to all on.
*/
ret = regmap_write(regmap, RTQ6056_REG_CONFIG, RTQ6056_DEFAULT_CONFIG);
ret = regmap_write(regmap, RTQ6056_REG_CONFIG, devdata->default_config);
if (ret)
return dev_err_probe(dev, ret,
"Failed to enable continuous sensing\n");
@ -598,8 +796,8 @@ static int rtq6056_probe(struct i2c_client *i2c)
indio_dev->name = "rtq6056";
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = rtq6056_channels;
indio_dev->num_channels = ARRAY_SIZE(rtq6056_channels);
indio_dev->channels = devdata->channels;
indio_dev->num_channels = devdata->num_channels;
indio_dev->info = &rtq6056_info;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
@ -640,8 +838,45 @@ static int rtq6056_runtime_resume(struct device *dev)
static DEFINE_RUNTIME_DEV_PM_OPS(rtq6056_pm_ops, rtq6056_runtime_suspend,
rtq6056_runtime_resume, NULL);
static const struct richtek_dev_data rtq6056_devdata = {
.default_conv_time_us = 1037,
.calib_coefficient = 5120000,
/*
* By default, configure average sample as 1, bus and shunt conversion
* time as 1037 microsecond, and operating mode to all on.
*/
.default_config = RTQ6056_DEFAULT_CONFIG,
.avg_sample_list = rtq6056_avg_sample_list,
.avg_sample_list_length = ARRAY_SIZE(rtq6056_avg_sample_list),
.reg_fields = rtq6056_reg_fields,
.channels = rtq6056_channels,
.num_channels = ARRAY_SIZE(rtq6056_channels),
.read_scale = rtq6056_adc_read_scale,
.set_average = rtq6056_adc_set_average,
};
static const struct richtek_dev_data rtq6059_devdata = {
.fixed_samp_freq = true,
.vbus_offset = RTQ6059_VBUS_LSB_OFFSET,
.default_conv_time_us = 532,
.calib_coefficient = 40960000,
/*
* By default, configure average sample as 1, bus and shunt conversion
* time as 532 microsecond, and operating mode to all on.
*/
.default_config = RTQ6059_DEFAULT_CONFIG,
.avg_sample_list = rtq6059_avg_sample_list,
.avg_sample_list_length = ARRAY_SIZE(rtq6059_avg_sample_list),
.reg_fields = rtq6059_reg_fields,
.channels = rtq6059_channels,
.num_channels = ARRAY_SIZE(rtq6059_channels),
.read_scale = rtq6059_adc_read_scale,
.set_average = rtq6059_adc_set_average,
};
static const struct of_device_id rtq6056_device_match[] = {
{ .compatible = "richtek,rtq6056" },
{ .compatible = "richtek,rtq6056", .data = &rtq6056_devdata },
{ .compatible = "richtek,rtq6059", .data = &rtq6059_devdata },
{}
};
MODULE_DEVICE_TABLE(of, rtq6056_device_match);

View File

@ -293,13 +293,11 @@ static const struct of_device_id adc108s102_of_match[] = {
};
MODULE_DEVICE_TABLE(of, adc108s102_of_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id adc108s102_acpi_ids[] = {
{ "INT3495", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, adc108s102_acpi_ids);
#endif
static const struct spi_device_id adc108s102_id[] = {
{ "adc108s102", 0 },
@ -311,7 +309,7 @@ static struct spi_driver adc108s102_driver = {
.driver = {
.name = "adc108s102",
.of_match_table = adc108s102_of_match,
.acpi_match_table = ACPI_PTR(adc108s102_acpi_ids),
.acpi_match_table = adc108s102_acpi_ids,
},
.probe = adc108s102_probe,
.id_table = adc108s102_id,

View File

@ -925,7 +925,7 @@ static int ads1015_client_get_channels_config(struct i2c_client *client)
if (!fwnode_property_read_u32(node, "ti,gain", &pval)) {
pga = pval;
if (pga > 6) {
if (pga > 5) {
dev_err(dev, "invalid gain on %pfw\n", node);
fwnode_handle_put(node);
return -EINVAL;

View File

@ -0,0 +1,769 @@
// SPDX-License-Identifier: GPL-2.0
/* TI ADS1298 chip family driver
* Copyright (C) 2023 - 2024 Topic Embedded Products
*/
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/log2.h>
#include <linux/math.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/units.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>
#include <asm/unaligned.h>
/* Commands */
#define ADS1298_CMD_WAKEUP 0x02
#define ADS1298_CMD_STANDBY 0x04
#define ADS1298_CMD_RESET 0x06
#define ADS1298_CMD_START 0x08
#define ADS1298_CMD_STOP 0x0a
#define ADS1298_CMD_RDATAC 0x10
#define ADS1298_CMD_SDATAC 0x11
#define ADS1298_CMD_RDATA 0x12
#define ADS1298_CMD_RREG 0x20
#define ADS1298_CMD_WREG 0x40
/* Registers */
#define ADS1298_REG_ID 0x00
#define ADS1298_MASK_ID_FAMILY GENMASK(7, 3)
#define ADS1298_MASK_ID_CHANNELS GENMASK(2, 0)
#define ADS1298_ID_FAMILY_ADS129X 0x90
#define ADS1298_ID_FAMILY_ADS129XR 0xd0
#define ADS1298_REG_CONFIG1 0x01
#define ADS1298_MASK_CONFIG1_HR BIT(7)
#define ADS1298_MASK_CONFIG1_DR GENMASK(2, 0)
#define ADS1298_SHIFT_DR_HR 6
#define ADS1298_SHIFT_DR_LP 7
#define ADS1298_LOWEST_DR 0x06
#define ADS1298_REG_CONFIG2 0x02
#define ADS1298_MASK_CONFIG2_RESERVED BIT(6)
#define ADS1298_MASK_CONFIG2_WCT_CHOP BIT(5)
#define ADS1298_MASK_CONFIG2_INT_TEST BIT(4)
#define ADS1298_MASK_CONFIG2_TEST_AMP BIT(2)
#define ADS1298_MASK_CONFIG2_TEST_FREQ_DC GENMASK(1, 0)
#define ADS1298_MASK_CONFIG2_TEST_FREQ_SLOW 0
#define ADS1298_MASK_CONFIG2_TEST_FREQ_FAST BIT(0)
#define ADS1298_REG_CONFIG3 0x03
#define ADS1298_MASK_CONFIG3_PWR_REFBUF BIT(7)
#define ADS1298_MASK_CONFIG3_RESERVED BIT(6)
#define ADS1298_MASK_CONFIG3_VREF_4V BIT(5)
#define ADS1298_REG_LOFF 0x04
#define ADS1298_REG_CHnSET(n) (0x05 + n)
#define ADS1298_MASK_CH_PD BIT(7)
#define ADS1298_MASK_CH_PGA GENMASK(6, 4)
#define ADS1298_MASK_CH_MUX GENMASK(2, 0)
#define ADS1298_REG_LOFF_STATP 0x12
#define ADS1298_REG_LOFF_STATN 0x13
#define ADS1298_REG_CONFIG4 0x17
#define ADS1298_MASK_CONFIG4_SINGLE_SHOT BIT(3)
#define ADS1298_REG_WCT1 0x18
#define ADS1298_REG_WCT2 0x19
#define ADS1298_MAX_CHANNELS 8
#define ADS1298_BITS_PER_SAMPLE 24
#define ADS1298_CLK_RATE_HZ 2048000
#define ADS1298_CLOCKS_TO_USECS(x) \
(DIV_ROUND_UP((x) * MICROHZ_PER_HZ, ADS1298_CLK_RATE_HZ))
/*
* Read/write register commands require 4 clocks to decode, for speeds above
* 2x the clock rate, this would require extra time between the command byte and
* the data. Much simpler is to just limit the SPI transfer speed while doing
* register access.
*/
#define ADS1298_SPI_BUS_SPEED_SLOW ADS1298_CLK_RATE_HZ
/* For reading and writing registers, we need a 3-byte buffer */
#define ADS1298_SPI_CMD_BUFFER_SIZE 3
/* Outputs status word and 'n' 24-bit samples, plus the command byte */
#define ADS1298_SPI_RDATA_BUFFER_SIZE(n) (((n) + 1) * 3 + 1)
#define ADS1298_SPI_RDATA_BUFFER_SIZE_MAX \
ADS1298_SPI_RDATA_BUFFER_SIZE(ADS1298_MAX_CHANNELS)
struct ads1298_private {
const struct ads1298_chip_info *chip_info;
struct spi_device *spi;
struct regulator *reg_avdd;
struct regulator *reg_vref;
struct clk *clk;
struct regmap *regmap;
struct completion completion;
struct iio_trigger *trig;
struct spi_transfer rdata_xfer;
struct spi_message rdata_msg;
spinlock_t irq_busy_lock; /* Handshake between SPI and DRDY irqs */
/*
* rdata_xfer_busy increments when a DRDY occurs and decrements when SPI
* completion is reported. Hence its meaning is:
* 0 = Waiting for DRDY interrupt
* 1 = SPI transfer in progress
* 2 = DRDY during SPI transfer, start another transfer on completion
* >2 = Multiple DRDY during transfer, lost rdata_xfer_busy - 2 samples
*/
unsigned int rdata_xfer_busy;
/* Temporary storage for demuxing data after SPI transfer */
u32 bounce_buffer[ADS1298_MAX_CHANNELS];
/* For synchronous SPI exchanges (read/write registers) */
u8 cmd_buffer[ADS1298_SPI_CMD_BUFFER_SIZE] __aligned(IIO_DMA_MINALIGN);
/* Buffer used for incoming SPI data */
u8 rx_buffer[ADS1298_SPI_RDATA_BUFFER_SIZE_MAX];
/* Contains the RDATA command and zeroes to clock out */
u8 tx_buffer[ADS1298_SPI_RDATA_BUFFER_SIZE_MAX];
};
/* Three bytes per sample in RX buffer, starting at offset 4 */
#define ADS1298_OFFSET_IN_RX_BUFFER(index) (3 * (index) + 4)
#define ADS1298_CHAN(index) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = index, \
.address = ADS1298_OFFSET_IN_RX_BUFFER(index), \
.info_mask_separate = \
BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = \
BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.scan_index = index, \
.scan_type = { \
.sign = 's', \
.realbits = ADS1298_BITS_PER_SAMPLE, \
.storagebits = 32, \
.endianness = IIO_CPU, \
}, \
}
static const struct iio_chan_spec ads1298_channels[] = {
ADS1298_CHAN(0),
ADS1298_CHAN(1),
ADS1298_CHAN(2),
ADS1298_CHAN(3),
ADS1298_CHAN(4),
ADS1298_CHAN(5),
ADS1298_CHAN(6),
ADS1298_CHAN(7),
};
static int ads1298_write_cmd(struct ads1298_private *priv, u8 command)
{
struct spi_transfer xfer = {
.tx_buf = priv->cmd_buffer,
.rx_buf = priv->cmd_buffer,
.len = 1,
.speed_hz = ADS1298_SPI_BUS_SPEED_SLOW,
.delay = {
.value = 2,
.unit = SPI_DELAY_UNIT_USECS,
},
};
priv->cmd_buffer[0] = command;
return spi_sync_transfer(priv->spi, &xfer, 1);
}
static int ads1298_read_one(struct ads1298_private *priv, int chan_index)
{
int ret;
/* Enable the channel */
ret = regmap_update_bits(priv->regmap, ADS1298_REG_CHnSET(chan_index),
ADS1298_MASK_CH_PD, 0);
if (ret)
return ret;
/* Enable single-shot mode, so we don't need to send a STOP */
ret = regmap_update_bits(priv->regmap, ADS1298_REG_CONFIG4,
ADS1298_MASK_CONFIG4_SINGLE_SHOT,
ADS1298_MASK_CONFIG4_SINGLE_SHOT);
if (ret)
return ret;
reinit_completion(&priv->completion);
ret = ads1298_write_cmd(priv, ADS1298_CMD_START);
if (ret < 0) {
dev_err(&priv->spi->dev, "CMD_START error: %d\n", ret);
return ret;
}
/* Cannot take longer than 40ms (250Hz) */
ret = wait_for_completion_timeout(&priv->completion, msecs_to_jiffies(50));
if (!ret)
return -ETIMEDOUT;
return 0;
}
static int ads1298_get_samp_freq(struct ads1298_private *priv, int *val)
{
unsigned long rate;
unsigned int cfg;
int ret;
ret = regmap_read(priv->regmap, ADS1298_REG_CONFIG1, &cfg);
if (ret)
return ret;
if (priv->clk)
rate = clk_get_rate(priv->clk);
else
rate = ADS1298_CLK_RATE_HZ;
if (!rate)
return -EINVAL;
/* Data rate shift depends on HR/LP mode */
if (cfg & ADS1298_MASK_CONFIG1_HR)
rate >>= ADS1298_SHIFT_DR_HR;
else
rate >>= ADS1298_SHIFT_DR_LP;
*val = rate >> (cfg & ADS1298_MASK_CONFIG1_DR);
return IIO_VAL_INT;
}
static int ads1298_set_samp_freq(struct ads1298_private *priv, int val)
{
unsigned long rate;
unsigned int factor;
unsigned int cfg;
if (priv->clk)
rate = clk_get_rate(priv->clk);
else
rate = ADS1298_CLK_RATE_HZ;
if (!rate)
return -EINVAL;
factor = (rate >> ADS1298_SHIFT_DR_HR) / val;
if (factor >= BIT(ADS1298_SHIFT_DR_LP))
cfg = ADS1298_LOWEST_DR;
else if (factor)
cfg = ADS1298_MASK_CONFIG1_HR | ilog2(factor); /* Use HR mode */
else
cfg = ADS1298_MASK_CONFIG1_HR; /* Fastest possible */
return regmap_update_bits(priv->regmap, ADS1298_REG_CONFIG1,
ADS1298_MASK_CONFIG1_HR | ADS1298_MASK_CONFIG1_DR,
cfg);
}
static const u8 ads1298_pga_settings[] = { 6, 1, 2, 3, 4, 8, 12 };
static int ads1298_get_scale(struct ads1298_private *priv,
int channel, int *val, int *val2)
{
int ret;
unsigned int regval;
u8 gain;
if (priv->reg_vref) {
ret = regulator_get_voltage(priv->reg_vref);
if (ret < 0)
return ret;
*val = ret / MILLI; /* Convert to millivolts */
} else {
ret = regmap_read(priv->regmap, ADS1298_REG_CONFIG3, &regval);
if (ret)
return ret;
/* Refererence in millivolts */
*val = regval & ADS1298_MASK_CONFIG3_VREF_4V ? 4000 : 2400;
}
ret = regmap_read(priv->regmap, ADS1298_REG_CHnSET(channel), &regval);
if (ret)
return ret;
gain = ads1298_pga_settings[FIELD_GET(ADS1298_MASK_CH_PGA, regval)];
*val /= gain; /* Full scale is VREF / gain */
*val2 = ADS1298_BITS_PER_SAMPLE - 1; /* Signed, hence the -1 */
return IIO_VAL_FRACTIONAL_LOG2;
}
static int ads1298_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct ads1298_private *priv = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = ads1298_read_one(priv, chan->scan_index);
iio_device_release_direct_mode(indio_dev);
if (ret)
return ret;
*val = sign_extend32(get_unaligned_be24(priv->rx_buffer + chan->address),
ADS1298_BITS_PER_SAMPLE - 1);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
return ads1298_get_scale(priv, chan->channel, val, val2);
case IIO_CHAN_INFO_SAMP_FREQ:
return ads1298_get_samp_freq(priv, val);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
ret = regmap_read(priv->regmap, ADS1298_REG_CONFIG1, val);
if (ret)
return ret;
*val = 16 << (*val & ADS1298_MASK_CONFIG1_DR);
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int ads1298_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct ads1298_private *priv = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
return ads1298_set_samp_freq(priv, val);
default:
return -EINVAL;
}
}
static int ads1298_reg_write(void *context, unsigned int reg, unsigned int val)
{
struct ads1298_private *priv = context;
struct spi_transfer reg_write_xfer = {
.tx_buf = priv->cmd_buffer,
.rx_buf = priv->cmd_buffer,
.len = 3,
.speed_hz = ADS1298_SPI_BUS_SPEED_SLOW,
.delay = {
.value = 2,
.unit = SPI_DELAY_UNIT_USECS,
},
};
priv->cmd_buffer[0] = ADS1298_CMD_WREG | reg;
priv->cmd_buffer[1] = 0; /* Number of registers to be written - 1 */
priv->cmd_buffer[2] = val;
return spi_sync_transfer(priv->spi, &reg_write_xfer, 1);
}
static int ads1298_reg_read(void *context, unsigned int reg, unsigned int *val)
{
struct ads1298_private *priv = context;
struct spi_transfer reg_read_xfer = {
.tx_buf = priv->cmd_buffer,
.rx_buf = priv->cmd_buffer,
.len = 3,
.speed_hz = ADS1298_SPI_BUS_SPEED_SLOW,
.delay = {
.value = 2,
.unit = SPI_DELAY_UNIT_USECS,
},
};
int ret;
priv->cmd_buffer[0] = ADS1298_CMD_RREG | reg;
priv->cmd_buffer[1] = 0; /* Number of registers to be read - 1 */
priv->cmd_buffer[2] = 0;
ret = spi_sync_transfer(priv->spi, &reg_read_xfer, 1);
if (ret)
return ret;
*val = priv->cmd_buffer[2];
return 0;
}
static int ads1298_reg_access(struct iio_dev *indio_dev, unsigned int reg,
unsigned int writeval, unsigned int *readval)
{
struct ads1298_private *priv = iio_priv(indio_dev);
if (readval)
return regmap_read(priv->regmap, reg, readval);
return regmap_write(priv->regmap, reg, writeval);
}
static void ads1298_rdata_unmark_busy(struct ads1298_private *priv)
{
/* Notify we're no longer waiting for the SPI transfer to complete */
guard(spinlock_irqsave)(&priv->irq_busy_lock);
priv->rdata_xfer_busy = 0;
}
static int ads1298_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct ads1298_private *priv = iio_priv(indio_dev);
unsigned int val;
int ret;
int i;
/* Make the interrupt routines start with a clean slate */
ads1298_rdata_unmark_busy(priv);
/* Configure power-down bits to match scan mask */
for (i = 0; i < indio_dev->num_channels; i++) {
val = test_bit(i, scan_mask) ? 0 : ADS1298_MASK_CH_PD;
ret = regmap_update_bits(priv->regmap, ADS1298_REG_CHnSET(i),
ADS1298_MASK_CH_PD, val);
if (ret)
return ret;
}
return 0;
}
static const struct iio_info ads1298_info = {
.read_raw = &ads1298_read_raw,
.write_raw = &ads1298_write_raw,
.update_scan_mode = &ads1298_update_scan_mode,
.debugfs_reg_access = &ads1298_reg_access,
};
static void ads1298_rdata_release_busy_or_restart(struct ads1298_private *priv)
{
guard(spinlock_irqsave)(&priv->irq_busy_lock);
if (priv->rdata_xfer_busy > 1) {
/*
* DRDY interrupt occurred before SPI completion. Start a new
* SPI transaction now to retrieve the data that wasn't latched
* into the ADS1298 chip's transfer buffer yet.
*/
spi_async(priv->spi, &priv->rdata_msg);
/*
* If more than one DRDY took place, there was an overrun. Since
* the sample is already lost, reset the counter to 1 so that
* we will wait for a DRDY interrupt after this SPI transaction.
*/
priv->rdata_xfer_busy = 1;
} else {
/* No pending data, wait for DRDY */
priv->rdata_xfer_busy = 0;
}
}
/* Called from SPI completion interrupt handler */
static void ads1298_rdata_complete(void *context)
{
struct iio_dev *indio_dev = context;
struct ads1298_private *priv = iio_priv(indio_dev);
int scan_index;
u32 *bounce = priv->bounce_buffer;
if (!iio_buffer_enabled(indio_dev)) {
/*
* for a single transfer mode we're kept in direct_mode until
* completion, avoiding a race with buffered IO.
*/
ads1298_rdata_unmark_busy(priv);
complete(&priv->completion);
return;
}
/* Demux the channel data into our bounce buffer */
for_each_set_bit(scan_index, indio_dev->active_scan_mask,
indio_dev->masklength) {
const struct iio_chan_spec *scan_chan =
&indio_dev->channels[scan_index];
const u8 *data = priv->rx_buffer + scan_chan->address;
*bounce++ = get_unaligned_be24(data);
}
/* rx_buffer can be overwritten from this point on */
ads1298_rdata_release_busy_or_restart(priv);
iio_push_to_buffers(indio_dev, priv->bounce_buffer);
}
static irqreturn_t ads1298_interrupt(int irq, void *dev_id)
{
struct iio_dev *indio_dev = dev_id;
struct ads1298_private *priv = iio_priv(indio_dev);
unsigned int wasbusy;
guard(spinlock_irqsave)(&priv->irq_busy_lock);
wasbusy = priv->rdata_xfer_busy++;
/* When no SPI transfer in transit, start one now */
if (!wasbusy)
spi_async(priv->spi, &priv->rdata_msg);
return IRQ_HANDLED;
};
static int ads1298_buffer_postenable(struct iio_dev *indio_dev)
{
struct ads1298_private *priv = iio_priv(indio_dev);
int ret;
/* Disable single-shot mode */
ret = regmap_update_bits(priv->regmap, ADS1298_REG_CONFIG4,
ADS1298_MASK_CONFIG4_SINGLE_SHOT, 0);
if (ret)
return ret;
return ads1298_write_cmd(priv, ADS1298_CMD_START);
}
static int ads1298_buffer_predisable(struct iio_dev *indio_dev)
{
struct ads1298_private *priv = iio_priv(indio_dev);
return ads1298_write_cmd(priv, ADS1298_CMD_STOP);
}
static const struct iio_buffer_setup_ops ads1298_setup_ops = {
.postenable = &ads1298_buffer_postenable,
.predisable = &ads1298_buffer_predisable,
};
static void ads1298_reg_disable(void *reg)
{
regulator_disable(reg);
}
static const struct regmap_range ads1298_regmap_volatile_range[] = {
regmap_reg_range(ADS1298_REG_LOFF_STATP, ADS1298_REG_LOFF_STATN),
};
static const struct regmap_access_table ads1298_regmap_volatile = {
.yes_ranges = ads1298_regmap_volatile_range,
.n_yes_ranges = ARRAY_SIZE(ads1298_regmap_volatile_range),
};
static const struct regmap_config ads1298_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.reg_read = ads1298_reg_read,
.reg_write = ads1298_reg_write,
.max_register = ADS1298_REG_WCT2,
.volatile_table = &ads1298_regmap_volatile,
.cache_type = REGCACHE_MAPLE,
};
static int ads1298_init(struct iio_dev *indio_dev)
{
struct ads1298_private *priv = iio_priv(indio_dev);
struct device *dev = &priv->spi->dev;
const char *suffix;
unsigned int val;
int ret;
/* Device initializes into RDATAC mode, which we don't want */
ret = ads1298_write_cmd(priv, ADS1298_CMD_SDATAC);
if (ret)
return ret;
ret = regmap_read(priv->regmap, ADS1298_REG_ID, &val);
if (ret)
return ret;
/* Fill in name and channel count based on what the chip told us */
indio_dev->num_channels = 4 + 2 * (val & ADS1298_MASK_ID_CHANNELS);
switch (val & ADS1298_MASK_ID_FAMILY) {
case ADS1298_ID_FAMILY_ADS129X:
suffix = "";
break;
case ADS1298_ID_FAMILY_ADS129XR:
suffix = "r";
break;
default:
return dev_err_probe(dev, -ENODEV, "Unknown ID: 0x%x\n", val);
}
indio_dev->name = devm_kasprintf(dev, GFP_KERNEL, "ads129%u%s",
indio_dev->num_channels, suffix);
/* Enable internal test signal, double amplitude, double frequency */
ret = regmap_write(priv->regmap, ADS1298_REG_CONFIG2,
ADS1298_MASK_CONFIG2_RESERVED |
ADS1298_MASK_CONFIG2_INT_TEST |
ADS1298_MASK_CONFIG2_TEST_AMP |
ADS1298_MASK_CONFIG2_TEST_FREQ_FAST);
if (ret)
return ret;
val = ADS1298_MASK_CONFIG3_RESERVED; /* Must write 1 always */
if (!priv->reg_vref) {
/* Enable internal reference */
val |= ADS1298_MASK_CONFIG3_PWR_REFBUF;
/* Use 4V VREF when power supply is at least 4.4V */
if (regulator_get_voltage(priv->reg_avdd) >= 4400000)
val |= ADS1298_MASK_CONFIG3_VREF_4V;
}
return regmap_write(priv->regmap, ADS1298_REG_CONFIG3, val);
}
static int ads1298_probe(struct spi_device *spi)
{
struct ads1298_private *priv;
struct iio_dev *indio_dev;
struct device *dev = &spi->dev;
struct gpio_desc *reset_gpio;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
if (!indio_dev)
return -ENOMEM;
priv = iio_priv(indio_dev);
/* Reset to be asserted before enabling clock and power */
reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(reset_gpio))
return dev_err_probe(dev, PTR_ERR(reset_gpio),
"Cannot get reset GPIO\n");
/* VREF can be supplied externally, otherwise use internal reference */
priv->reg_vref = devm_regulator_get_optional(dev, "vref");
if (IS_ERR(priv->reg_vref)) {
if (PTR_ERR(priv->reg_vref) != -ENODEV)
return dev_err_probe(dev, PTR_ERR(priv->reg_avdd),
"Failed to get vref regulator\n");
priv->reg_vref = NULL;
} else {
ret = regulator_enable(priv->reg_vref);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, ads1298_reg_disable, priv->reg_vref);
if (ret)
return ret;
}
priv->clk = devm_clk_get_optional_enabled(dev, "clk");
if (IS_ERR(priv->clk))
return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get clk\n");
priv->reg_avdd = devm_regulator_get(dev, "avdd");
if (IS_ERR(priv->reg_avdd))
return dev_err_probe(dev, PTR_ERR(priv->reg_avdd),
"Failed to get avdd regulator\n");
ret = regulator_enable(priv->reg_avdd);
if (ret)
return dev_err_probe(dev, ret, "Failed to enable avdd regulator\n");
ret = devm_add_action_or_reset(dev, ads1298_reg_disable, priv->reg_avdd);
if (ret)
return ret;
priv->spi = spi;
init_completion(&priv->completion);
spin_lock_init(&priv->irq_busy_lock);
priv->regmap = devm_regmap_init(dev, NULL, priv, &ads1298_regmap_config);
if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap);
indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
indio_dev->channels = ads1298_channels;
indio_dev->info = &ads1298_info;
if (reset_gpio) {
/*
* Deassert reset now that clock and power are active.
* Minimum reset pulsewidth is 2 clock cycles.
*/
fsleep(ADS1298_CLOCKS_TO_USECS(2));
gpiod_set_value_cansleep(reset_gpio, 0);
} else {
ret = ads1298_write_cmd(priv, ADS1298_CMD_RESET);
if (ret)
return dev_err_probe(dev, ret, "RESET failed\n");
}
/* Wait 18 clock cycles for reset command to complete */
fsleep(ADS1298_CLOCKS_TO_USECS(18));
ret = ads1298_init(indio_dev);
if (ret)
return dev_err_probe(dev, ret, "Init failed\n");
priv->tx_buffer[0] = ADS1298_CMD_RDATA;
priv->rdata_xfer.tx_buf = priv->tx_buffer;
priv->rdata_xfer.rx_buf = priv->rx_buffer;
priv->rdata_xfer.len = ADS1298_SPI_RDATA_BUFFER_SIZE(indio_dev->num_channels);
/* Must keep CS low for 4 clocks */
priv->rdata_xfer.delay.value = 2;
priv->rdata_xfer.delay.unit = SPI_DELAY_UNIT_USECS;
spi_message_init_with_transfers(&priv->rdata_msg, &priv->rdata_xfer, 1);
priv->rdata_msg.complete = &ads1298_rdata_complete;
priv->rdata_msg.context = indio_dev;
ret = devm_request_irq(dev, spi->irq, &ads1298_interrupt,
IRQF_TRIGGER_FALLING, indio_dev->name,
indio_dev);
if (ret)
return ret;
ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, &ads1298_setup_ops);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct spi_device_id ads1298_id[] = {
{ "ads1298" },
{ }
};
MODULE_DEVICE_TABLE(spi, ads1298_id);
static const struct of_device_id ads1298_of_table[] = {
{ .compatible = "ti,ads1298" },
{ }
};
MODULE_DEVICE_TABLE(of, ads1298_of_table);
static struct spi_driver ads1298_driver = {
.driver = {
.name = "ads1298",
.of_match_table = ads1298_of_table,
},
.probe = ads1298_probe,
.id_table = ads1298_id,
};
module_spi_driver(ads1298_driver);
MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
MODULE_DESCRIPTION("TI ADS1298 ADC");
MODULE_LICENSE("GPL");

View File

@ -159,7 +159,7 @@ static const struct iio_dev_attr *iio_dmaengine_buffer_attrs[] = {
* Once done using the buffer iio_dmaengine_buffer_free() should be used to
* release it.
*/
static struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
const char *channel)
{
struct dmaengine_buffer *dmaengine_buffer;
@ -210,6 +210,7 @@ err_free:
kfree(dmaengine_buffer);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_alloc, IIO_DMAENGINE_BUFFER);
/**
* iio_dmaengine_buffer_free() - Free dmaengine buffer
@ -217,7 +218,7 @@ err_free:
*
* Frees a buffer previously allocated with iio_dmaengine_buffer_alloc().
*/
static void iio_dmaengine_buffer_free(struct iio_buffer *buffer)
void iio_dmaengine_buffer_free(struct iio_buffer *buffer)
{
struct dmaengine_buffer *dmaengine_buffer =
iio_buffer_to_dmaengine_buffer(buffer);
@ -227,6 +228,7 @@ static void iio_dmaengine_buffer_free(struct iio_buffer *buffer)
iio_buffer_put(buffer);
}
EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_free, IIO_DMAENGINE_BUFFER);
static void __devm_iio_dmaengine_buffer_free(void *buffer)
{
@ -279,8 +281,7 @@ int devm_iio_dmaengine_buffer_setup(struct device *dev,
{
struct iio_buffer *buffer;
buffer = devm_iio_dmaengine_buffer_alloc(indio_dev->dev.parent,
channel);
buffer = devm_iio_dmaengine_buffer_alloc(dev, channel);
if (IS_ERR(buffer))
return PTR_ERR(buffer);
@ -288,7 +289,7 @@ int devm_iio_dmaengine_buffer_setup(struct device *dev,
return iio_device_attach_buffer(indio_dev, buffer);
}
EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_setup);
EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup, IIO_DMAENGINE_BUFFER);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("DMA buffer for the IIO framework");

View File

@ -126,7 +126,7 @@ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts,
struct inv_sensors_timestamp_interval *it;
int64_t delta, interval;
const uint32_t fifo_mult = fifo_period / ts->chip.clock_period;
uint32_t period = ts->period;
uint32_t period;
bool valid = false;
if (fifo_nb == 0)

View File

@ -31,8 +31,6 @@
* @regs: irq regs we are faking
* @lock: protect the evgen state
* @inuse: mask of which irqs are connected
* @irq_sim: interrupt simulator
* @base: base of irq range
* @irq_sim_domain: irq simulator domain
*/
struct iio_dummy_eventgen {

View File

@ -283,65 +283,63 @@ static int iio_dummy_read_raw(struct iio_dev *indio_dev,
long mask)
{
struct iio_dummy_state *st = iio_priv(indio_dev);
int ret = -EINVAL;
mutex_lock(&st->lock);
switch (mask) {
case IIO_CHAN_INFO_RAW: /* magic value - channel value read */
switch (chan->type) {
case IIO_VOLTAGE:
if (chan->output) {
/* Set integer part to cached value */
*val = st->dac_val;
ret = IIO_VAL_INT;
} else if (chan->differential) {
if (chan->channel == 1)
*val = st->differential_adc_val[0];
else
*val = st->differential_adc_val[1];
ret = IIO_VAL_INT;
} else {
*val = st->single_ended_adc_val;
ret = IIO_VAL_INT;
}
break;
case IIO_ACCEL:
*val = st->accel_val;
ret = IIO_VAL_INT;
break;
default:
break;
}
break;
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_STEPS:
*val = st->steps;
ret = IIO_VAL_INT;
break;
case IIO_ACTIVITY:
switch (chan->channel2) {
case IIO_MOD_RUNNING:
*val = st->activity_running;
ret = IIO_VAL_INT;
break;
case IIO_MOD_WALKING:
*val = st->activity_walking;
ret = IIO_VAL_INT;
break;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
guard(mutex)(&st->lock);
switch (chan->type) {
case IIO_VOLTAGE:
if (chan->output) {
/* Set integer part to cached value */
*val = st->dac_val;
return IIO_VAL_INT;
} else if (chan->differential) {
if (chan->channel == 1)
*val = st->differential_adc_val[0];
else
*val = st->differential_adc_val[1];
return IIO_VAL_INT;
} else {
*val = st->single_ended_adc_val;
return IIO_VAL_INT;
}
case IIO_ACCEL:
*val = st->accel_val;
return IIO_VAL_INT;
default:
break;
return -EINVAL;
}
break;
default:
break;
}
break;
unreachable();
case IIO_CHAN_INFO_PROCESSED:
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
guard(mutex)(&st->lock);
switch (chan->type) {
case IIO_STEPS:
*val = st->steps;
return IIO_VAL_INT;
case IIO_ACTIVITY:
switch (chan->channel2) {
case IIO_MOD_RUNNING:
*val = st->activity_running;
return IIO_VAL_INT;
case IIO_MOD_WALKING:
*val = st->activity_walking;
return IIO_VAL_INT;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
unreachable();
case IIO_CHAN_INFO_OFFSET:
/* only single ended adc -> 7 */
*val = 7;
ret = IIO_VAL_INT;
break;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_VOLTAGE:
@ -350,60 +348,57 @@ static int iio_dummy_read_raw(struct iio_dev *indio_dev,
/* only single ended adc -> 0.001333 */
*val = 0;
*val2 = 1333;
ret = IIO_VAL_INT_PLUS_MICRO;
break;
return IIO_VAL_INT_PLUS_MICRO;
case 1:
/* all differential adc -> 0.000001344 */
*val = 0;
*val2 = 1344;
ret = IIO_VAL_INT_PLUS_NANO;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
break;
default:
break;
return -EINVAL;
}
break;
case IIO_CHAN_INFO_CALIBBIAS:
case IIO_CHAN_INFO_CALIBBIAS: {
guard(mutex)(&st->lock);
/* only the acceleration axis - read from cache */
*val = st->accel_calibbias;
ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_CALIBSCALE:
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_CALIBSCALE: {
guard(mutex)(&st->lock);
*val = st->accel_calibscale->val;
*val2 = st->accel_calibscale->val2;
ret = IIO_VAL_INT_PLUS_MICRO;
break;
return IIO_VAL_INT_PLUS_MICRO;
}
case IIO_CHAN_INFO_SAMP_FREQ:
*val = 3;
*val2 = 33;
ret = IIO_VAL_INT_PLUS_NANO;
break;
case IIO_CHAN_INFO_ENABLE:
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_ENABLE: {
guard(mutex)(&st->lock);
switch (chan->type) {
case IIO_STEPS:
*val = st->steps_enabled;
ret = IIO_VAL_INT;
break;
return IIO_VAL_INT;
default:
break;
return -EINVAL;
}
break;
case IIO_CHAN_INFO_CALIBHEIGHT:
}
case IIO_CHAN_INFO_CALIBHEIGHT: {
guard(mutex)(&st->lock);
switch (chan->type) {
case IIO_STEPS:
*val = st->height;
ret = IIO_VAL_INT;
break;
return IIO_VAL_INT;
default:
break;
return -EINVAL;
}
break;
default:
break;
}
mutex_unlock(&st->lock);
return ret;
default:
return -EINVAL;
}
}
/**
@ -426,7 +421,6 @@ static int iio_dummy_write_raw(struct iio_dev *indio_dev,
long mask)
{
int i;
int ret = 0;
struct iio_dummy_state *st = iio_priv(indio_dev);
switch (mask) {
@ -436,10 +430,10 @@ static int iio_dummy_write_raw(struct iio_dev *indio_dev,
if (chan->output == 0)
return -EINVAL;
/* Locking not required as writing single value */
mutex_lock(&st->lock);
st->dac_val = val;
mutex_unlock(&st->lock);
scoped_guard(mutex, &st->lock) {
/* Locking not required as writing single value */
st->dac_val = val;
}
return 0;
default:
return -EINVAL;
@ -447,9 +441,9 @@ static int iio_dummy_write_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_STEPS:
mutex_lock(&st->lock);
st->steps = val;
mutex_unlock(&st->lock);
scoped_guard(mutex, &st->lock) {
st->steps = val;
}
return 0;
case IIO_ACTIVITY:
if (val < 0)
@ -470,30 +464,29 @@ static int iio_dummy_write_raw(struct iio_dev *indio_dev,
default:
return -EINVAL;
}
case IIO_CHAN_INFO_CALIBSCALE:
mutex_lock(&st->lock);
case IIO_CHAN_INFO_CALIBSCALE: {
guard(mutex)(&st->lock);
/* Compare against table - hard matching here */
for (i = 0; i < ARRAY_SIZE(dummy_scales); i++)
if (val == dummy_scales[i].val &&
val2 == dummy_scales[i].val2)
break;
if (i == ARRAY_SIZE(dummy_scales))
ret = -EINVAL;
else
st->accel_calibscale = &dummy_scales[i];
mutex_unlock(&st->lock);
return ret;
return -EINVAL;
st->accel_calibscale = &dummy_scales[i];
return 0;
}
case IIO_CHAN_INFO_CALIBBIAS:
mutex_lock(&st->lock);
st->accel_calibbias = val;
mutex_unlock(&st->lock);
scoped_guard(mutex, &st->lock) {
st->accel_calibbias = val;
}
return 0;
case IIO_CHAN_INFO_ENABLE:
switch (chan->type) {
case IIO_STEPS:
mutex_lock(&st->lock);
st->steps_enabled = val;
mutex_unlock(&st->lock);
scoped_guard(mutex, &st->lock) {
st->steps_enabled = val;
}
return 0;
default:
return -EINVAL;

View File

@ -60,6 +60,16 @@ config ADF4377
To compile this driver as a module, choose M here: the
module will be called adf4377.
config ADMFM2000
tristate "Analog Devices ADMFM2000 Dual Microwave Down Converter"
depends on GPIOLIB
help
Say yes here to build support for Analog Devices ADMFM2000 Dual
Microwave Down Converter.
To compile this driver as a module, choose M here: the
module will be called admfm2000.
config ADMV1013
tristate "Analog Devices ADMV1013 Microwave Upconverter"
depends on SPI && COMMON_CLK

View File

@ -8,6 +8,7 @@ obj-$(CONFIG_AD9523) += ad9523.o
obj-$(CONFIG_ADF4350) += adf4350.o
obj-$(CONFIG_ADF4371) += adf4371.o
obj-$(CONFIG_ADF4377) += adf4377.o
obj-$(CONFIG_ADMFM2000) += admfm2000.o
obj-$(CONFIG_ADMV1013) += admv1013.o
obj-$(CONFIG_ADMV1014) += admv1014.o
obj-$(CONFIG_ADMV4420) += admv4420.o

View File

@ -0,0 +1,282 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ADMFM2000 Dual Microwave Down Converter
*
* Copyright 2024 Analog Devices Inc.
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/iio/iio.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#define ADMFM2000_MIXER_MODE 0
#define ADMFM2000_DIRECT_IF_MODE 1
#define ADMFM2000_DSA_GPIOS 5
#define ADMFM2000_MODE_GPIOS 2
#define ADMFM2000_MAX_GAIN 0
#define ADMFM2000_MIN_GAIN -31000
#define ADMFM2000_DEFAULT_GAIN -0x20
struct admfm2000_state {
struct mutex lock; /* protect sensor state */
struct gpio_desc *sw1_ch[2];
struct gpio_desc *sw2_ch[2];
struct gpio_desc *dsa1_gpios[5];
struct gpio_desc *dsa2_gpios[5];
u32 gain[2];
};
static int admfm2000_mode(struct iio_dev *indio_dev, u32 chan, u32 mode)
{
struct admfm2000_state *st = iio_priv(indio_dev);
int i;
switch (mode) {
case ADMFM2000_MIXER_MODE:
for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) {
gpiod_set_value_cansleep(st->sw1_ch[i], (chan == 0) ? 1 : 0);
gpiod_set_value_cansleep(st->sw2_ch[i], (chan == 0) ? 0 : 1);
}
return 0;
case ADMFM2000_DIRECT_IF_MODE:
for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) {
gpiod_set_value_cansleep(st->sw1_ch[i], (chan == 0) ? 0 : 1);
gpiod_set_value_cansleep(st->sw2_ch[i], (chan == 0) ? 1 : 0);
}
return 0;
default:
return -EINVAL;
}
}
static int admfm2000_attenuation(struct iio_dev *indio_dev, u32 chan, u32 value)
{
struct admfm2000_state *st = iio_priv(indio_dev);
int i;
switch (chan) {
case 0:
for (i = 0; i < ADMFM2000_DSA_GPIOS; i++)
gpiod_set_value_cansleep(st->dsa1_gpios[i], value & (1 << i));
return 0;
case 1:
for (i = 0; i < ADMFM2000_DSA_GPIOS; i++)
gpiod_set_value_cansleep(st->dsa2_gpios[i], value & (1 << i));
return 0;
default:
return -EINVAL;
}
}
static int admfm2000_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct admfm2000_state *st = iio_priv(indio_dev);
int gain;
switch (mask) {
case IIO_CHAN_INFO_HARDWAREGAIN:
mutex_lock(&st->lock);
gain = ~(st->gain[chan->channel]) * -1000;
*val = gain / 1000;
*val2 = (gain % 1000) * 1000;
mutex_unlock(&st->lock);
return IIO_VAL_INT_PLUS_MICRO_DB;
default:
return -EINVAL;
}
}
static int admfm2000_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct admfm2000_state *st = iio_priv(indio_dev);
int gain, ret;
if (val < 0)
gain = (val * 1000) - (val2 / 1000);
else
gain = (val * 1000) + (val2 / 1000);
if (gain > ADMFM2000_MAX_GAIN || gain < ADMFM2000_MIN_GAIN)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_HARDWAREGAIN:
mutex_lock(&st->lock);
st->gain[chan->channel] = ~((abs(gain) / 1000) & 0x1F);
ret = admfm2000_attenuation(indio_dev, chan->channel,
st->gain[chan->channel]);
mutex_unlock(&st->lock);
return ret;
default:
return -EINVAL;
}
}
static int admfm2000_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_HARDWAREGAIN:
return IIO_VAL_INT_PLUS_MICRO_DB;
default:
return -EINVAL;
}
}
static const struct iio_info admfm2000_info = {
.read_raw = &admfm2000_read_raw,
.write_raw = &admfm2000_write_raw,
.write_raw_get_fmt = &admfm2000_write_raw_get_fmt,
};
#define ADMFM2000_CHAN(_channel) { \
.type = IIO_VOLTAGE, \
.output = 1, \
.indexed = 1, \
.channel = _channel, \
.info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
}
static const struct iio_chan_spec admfm2000_channels[] = {
ADMFM2000_CHAN(0),
ADMFM2000_CHAN(1),
};
static int admfm2000_channel_config(struct admfm2000_state *st,
struct iio_dev *indio_dev)
{
struct platform_device *pdev = to_platform_device(indio_dev->dev.parent);
struct device *dev = &pdev->dev;
struct fwnode_handle *child;
struct gpio_desc **dsa;
struct gpio_desc **sw;
int ret, i;
bool mode;
u32 reg;
device_for_each_child_node(dev, child) {
ret = fwnode_property_read_u32(child, "reg", &reg);
if (ret) {
fwnode_handle_put(child);
return dev_err_probe(dev, ret,
"Failed to get reg property\n");
}
if (reg >= indio_dev->num_channels) {
fwnode_handle_put(child);
return dev_err_probe(dev, -EINVAL, "reg bigger than: %d\n",
indio_dev->num_channels);
}
if (fwnode_property_present(child, "adi,mixer-mode"))
mode = ADMFM2000_MIXER_MODE;
else
mode = ADMFM2000_DIRECT_IF_MODE;
switch (reg) {
case 0:
sw = st->sw1_ch;
dsa = st->dsa1_gpios;
break;
case 1:
sw = st->sw2_ch;
dsa = st->dsa2_gpios;
break;
default:
fwnode_handle_put(child);
return -EINVAL;
}
for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) {
sw[i] = devm_fwnode_gpiod_get_index(dev, child, "switch",
i, GPIOD_OUT_LOW, NULL);
if (IS_ERR(sw[i])) {
fwnode_handle_put(child);
return dev_err_probe(dev, PTR_ERR(sw[i]),
"Failed to get gpios\n");
}
}
for (i = 0; i < ADMFM2000_DSA_GPIOS; i++) {
dsa[i] = devm_fwnode_gpiod_get_index(dev, child,
"attenuation", i,
GPIOD_OUT_LOW, NULL);
if (IS_ERR(dsa[i])) {
fwnode_handle_put(child);
return dev_err_probe(dev, PTR_ERR(dsa[i]),
"Failed to get gpios\n");
}
}
ret = admfm2000_mode(indio_dev, reg, mode);
if (ret) {
fwnode_handle_put(child);
return ret;
}
}
return 0;
}
static int admfm2000_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct admfm2000_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);
indio_dev->name = "admfm2000";
indio_dev->num_channels = ARRAY_SIZE(admfm2000_channels);
indio_dev->channels = admfm2000_channels;
indio_dev->info = &admfm2000_info;
indio_dev->modes = INDIO_DIRECT_MODE;
st->gain[0] = ADMFM2000_DEFAULT_GAIN;
st->gain[1] = ADMFM2000_DEFAULT_GAIN;
mutex_init(&st->lock);
ret = admfm2000_channel_config(st, indio_dev);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
static const struct of_device_id admfm2000_of_match[] = {
{ .compatible = "adi,admfm2000" },
{ }
};
MODULE_DEVICE_TABLE(of, admfm2000_of_match);
static struct platform_driver admfm2000_driver = {
.driver = {
.name = "admfm2000",
.of_match_table = admfm2000_of_match,
},
.probe = admfm2000_probe,
};
module_platform_driver(admfm2000_driver);
MODULE_AUTHOR("Kim Seer Paller <kimseer.paller@analog.com>");
MODULE_DESCRIPTION("ADMFM2000 Dual Microwave Down Converter");
MODULE_LICENSE("GPL");

View File

@ -3,7 +3,7 @@
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/acpi.h>
#include <linux/mod_devicetable.h>
#include "bmg160.h"
@ -66,7 +66,7 @@ MODULE_DEVICE_TABLE(of, bmg160_of_match);
static struct i2c_driver bmg160_i2c_driver = {
.driver = {
.name = "bmg160_i2c",
.acpi_match_table = ACPI_PTR(bmg160_acpi_match),
.acpi_match_table = bmg160_acpi_match,
.of_match_table = bmg160_of_match,
.pm = &bmg160_pm_ops,
},

View File

@ -346,6 +346,13 @@ err:
return IRQ_HANDLED;
}
static void afe4403_regulator_disable(void *data)
{
struct regulator *regulator = data;
regulator_disable(regulator);
}
#define AFE4403_TIMING_PAIRS \
{ AFE440X_LED2STC, 0x000050 }, \
{ AFE440X_LED2ENDC, 0x0003e7 }, \
@ -495,19 +502,24 @@ static int afe4403_probe(struct spi_device *spi)
dev_err(afe->dev, "Unable to enable regulator\n");
return ret;
}
ret = devm_add_action_or_reset(afe->dev, afe4403_regulator_disable, afe->regulator);
if (ret) {
dev_err(afe->dev, "Unable to add regulator disable action\n");
return ret;
}
ret = regmap_write(afe->regmap, AFE440X_CONTROL0,
AFE440X_CONTROL0_SW_RESET);
if (ret) {
dev_err(afe->dev, "Unable to reset device\n");
goto err_disable_reg;
return ret;
}
ret = regmap_multi_reg_write(afe->regmap, afe4403_reg_sequences,
ARRAY_SIZE(afe4403_reg_sequences));
if (ret) {
dev_err(afe->dev, "Unable to set register defaults\n");
goto err_disable_reg;
return ret;
}
indio_dev->modes = INDIO_DIRECT_MODE;
@ -523,16 +535,15 @@ static int afe4403_probe(struct spi_device *spi)
iio_device_id(indio_dev));
if (!afe->trig) {
dev_err(afe->dev, "Unable to allocate IIO trigger\n");
ret = -ENOMEM;
goto err_disable_reg;
return -ENOMEM;
}
iio_trigger_set_drvdata(afe->trig, indio_dev);
ret = iio_trigger_register(afe->trig);
ret = devm_iio_trigger_register(afe->dev, afe->trig);
if (ret) {
dev_err(afe->dev, "Unable to register IIO trigger\n");
goto err_disable_reg;
return ret;
}
ret = devm_request_threaded_irq(afe->dev, afe->irq,
@ -542,52 +553,25 @@ static int afe4403_probe(struct spi_device *spi)
afe->trig);
if (ret) {
dev_err(afe->dev, "Unable to request IRQ\n");
goto err_trig;
return ret;
}
}
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
afe4403_trigger_handler, NULL);
ret = devm_iio_triggered_buffer_setup(afe->dev, indio_dev,
&iio_pollfunc_store_time,
afe4403_trigger_handler, NULL);
if (ret) {
dev_err(afe->dev, "Unable to setup buffer\n");
goto err_trig;
return ret;
}
ret = iio_device_register(indio_dev);
ret = devm_iio_device_register(afe->dev, indio_dev);
if (ret) {
dev_err(afe->dev, "Unable to register IIO device\n");
goto err_buff;
return ret;
}
return 0;
err_buff:
iio_triggered_buffer_cleanup(indio_dev);
err_trig:
if (afe->irq > 0)
iio_trigger_unregister(afe->trig);
err_disable_reg:
regulator_disable(afe->regulator);
return ret;
}
static void afe4403_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct afe4403_data *afe = iio_priv(indio_dev);
int ret;
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
if (afe->irq > 0)
iio_trigger_unregister(afe->trig);
ret = regulator_disable(afe->regulator);
if (ret)
dev_warn(afe->dev, "Unable to disable regulator\n");
}
static const struct spi_device_id afe4403_ids[] = {
@ -603,7 +587,6 @@ static struct spi_driver afe4403_spi_driver = {
.pm = pm_sleep_ptr(&afe4403_pm_ops),
},
.probe = afe4403_probe,
.remove = afe4403_remove,
.id_table = afe4403_ids,
};
module_spi_driver(afe4403_spi_driver);

View File

@ -349,6 +349,13 @@ err:
return IRQ_HANDLED;
}
static void afe4404_regulator_disable(void *data)
{
struct regulator *regulator = data;
regulator_disable(regulator);
}
/* Default timings from data-sheet */
#define AFE4404_TIMING_PAIRS \
{ AFE440X_PRPCOUNT, 39999 }, \
@ -502,19 +509,24 @@ static int afe4404_probe(struct i2c_client *client)
dev_err(afe->dev, "Unable to enable regulator\n");
return ret;
}
ret = devm_add_action_or_reset(afe->dev, afe4404_regulator_disable, afe->regulator);
if (ret) {
dev_err(afe->dev, "Unable to enable regulator\n");
return ret;
}
ret = regmap_write(afe->regmap, AFE440X_CONTROL0,
AFE440X_CONTROL0_SW_RESET);
if (ret) {
dev_err(afe->dev, "Unable to reset device\n");
goto disable_reg;
return ret;
}
ret = regmap_multi_reg_write(afe->regmap, afe4404_reg_sequences,
ARRAY_SIZE(afe4404_reg_sequences));
if (ret) {
dev_err(afe->dev, "Unable to set register defaults\n");
goto disable_reg;
return ret;
}
indio_dev->modes = INDIO_DIRECT_MODE;
@ -530,16 +542,15 @@ static int afe4404_probe(struct i2c_client *client)
iio_device_id(indio_dev));
if (!afe->trig) {
dev_err(afe->dev, "Unable to allocate IIO trigger\n");
ret = -ENOMEM;
goto disable_reg;
return -ENOMEM;
}
iio_trigger_set_drvdata(afe->trig, indio_dev);
ret = iio_trigger_register(afe->trig);
ret = devm_iio_trigger_register(afe->dev, afe->trig);
if (ret) {
dev_err(afe->dev, "Unable to register IIO trigger\n");
goto disable_reg;
return ret;
}
ret = devm_request_threaded_irq(afe->dev, afe->irq,
@ -549,52 +560,25 @@ static int afe4404_probe(struct i2c_client *client)
afe->trig);
if (ret) {
dev_err(afe->dev, "Unable to request IRQ\n");
goto disable_reg;
return ret;
}
}
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
afe4404_trigger_handler, NULL);
ret = devm_iio_triggered_buffer_setup(afe->dev, indio_dev,
&iio_pollfunc_store_time,
afe4404_trigger_handler, NULL);
if (ret) {
dev_err(afe->dev, "Unable to setup buffer\n");
goto unregister_trigger;
return ret;
}
ret = iio_device_register(indio_dev);
ret = devm_iio_device_register(afe->dev, indio_dev);
if (ret) {
dev_err(afe->dev, "Unable to register IIO device\n");
goto unregister_triggered_buffer;
return ret;
}
return 0;
unregister_triggered_buffer:
iio_triggered_buffer_cleanup(indio_dev);
unregister_trigger:
if (afe->irq > 0)
iio_trigger_unregister(afe->trig);
disable_reg:
regulator_disable(afe->regulator);
return ret;
}
static void afe4404_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct afe4404_data *afe = iio_priv(indio_dev);
int ret;
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
if (afe->irq > 0)
iio_trigger_unregister(afe->trig);
ret = regulator_disable(afe->regulator);
if (ret)
dev_err(afe->dev, "Unable to disable regulator\n");
}
static const struct i2c_device_id afe4404_ids[] = {
@ -610,7 +594,6 @@ static struct i2c_driver afe4404_i2c_driver = {
.pm = pm_sleep_ptr(&afe4404_pm_ops),
},
.probe = afe4404_probe,
.remove = afe4404_remove,
.id_table = afe4404_ids,
};
module_i2c_driver(afe4404_i2c_driver);

View File

@ -5,41 +5,66 @@
*
* Copyright (C) 2023
*
* Copyright (C) 2024 Liebherr-Electronics and Drives GmbH
*
* Datasheet: https://www.ti.com/lit/ds/symlink/hdc3020.pdf
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/cleanup.h>
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/units.h>
#include <asm/unaligned.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#define HDC3020_HEATER_CMD_MSB 0x30 /* shared by all heater commands */
#define HDC3020_HEATER_ENABLE 0x6D
#define HDC3020_HEATER_DISABLE 0x66
#define HDC3020_HEATER_CONFIG 0x6E
#define HDC3020_S_AUTO_10HZ_MOD0 0x2737
#define HDC3020_S_STATUS 0x3041
#define HDC3020_HEATER_DISABLE 0x3066
#define HDC3020_HEATER_ENABLE 0x306D
#define HDC3020_HEATER_CONFIG 0x306E
#define HDC3020_EXIT_AUTO 0x3093
#define HDC3020_S_T_RH_THRESH_LOW 0x6100
#define HDC3020_S_T_RH_THRESH_LOW_CLR 0x610B
#define HDC3020_S_T_RH_THRESH_HIGH_CLR 0x6116
#define HDC3020_S_T_RH_THRESH_HIGH 0x611D
#define HDC3020_R_T_RH_AUTO 0xE000
#define HDC3020_R_T_LOW_AUTO 0xE002
#define HDC3020_R_T_HIGH_AUTO 0xE003
#define HDC3020_R_RH_LOW_AUTO 0xE004
#define HDC3020_R_RH_HIGH_AUTO 0xE005
#define HDC3020_R_T_RH_THRESH_LOW 0xE102
#define HDC3020_R_T_RH_THRESH_LOW_CLR 0xE109
#define HDC3020_R_T_RH_THRESH_HIGH_CLR 0xE114
#define HDC3020_R_T_RH_THRESH_HIGH 0xE11F
#define HDC3020_R_STATUS 0xF32D
#define HDC3020_THRESH_TEMP_MASK GENMASK(8, 0)
#define HDC3020_THRESH_TEMP_TRUNC_SHIFT 7
#define HDC3020_THRESH_HUM_MASK GENMASK(15, 9)
#define HDC3020_THRESH_HUM_TRUNC_SHIFT 9
#define HDC3020_STATUS_T_LOW_ALERT BIT(6)
#define HDC3020_STATUS_T_HIGH_ALERT BIT(7)
#define HDC3020_STATUS_RH_LOW_ALERT BIT(8)
#define HDC3020_STATUS_RH_HIGH_ALERT BIT(9)
#define HDC3020_READ_RETRY_TIMES 10
#define HDC3020_BUSY_DELAY_MS 10
#define HDC3020_CRC8_POLYNOMIAL 0x31
static const u8 HDC3020_S_AUTO_10HZ_MOD0[2] = { 0x27, 0x37 };
static const u8 HDC3020_EXIT_AUTO[2] = { 0x30, 0x93 };
static const u8 HDC3020_R_T_RH_AUTO[2] = { 0xE0, 0x00 };
static const u8 HDC3020_R_T_LOW_AUTO[2] = { 0xE0, 0x02 };
static const u8 HDC3020_R_T_HIGH_AUTO[2] = { 0xE0, 0x03 };
static const u8 HDC3020_R_RH_LOW_AUTO[2] = { 0xE0, 0x04 };
static const u8 HDC3020_R_RH_HIGH_AUTO[2] = { 0xE0, 0x05 };
#define HDC3020_MIN_TEMP -40
#define HDC3020_MAX_TEMP 125
struct hdc3020_data {
struct i2c_client *client;
@ -54,18 +79,37 @@ struct hdc3020_data {
static const int hdc3020_heater_vals[] = {0, 1, 0x3FFF};
static const struct iio_event_spec hdc3020_t_rh_event[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_HYSTERESIS),
},
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_HYSTERESIS),
},
};
static const struct iio_chan_spec hdc3020_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_PEAK) |
BIT(IIO_CHAN_INFO_TROUGH) | BIT(IIO_CHAN_INFO_OFFSET),
.event_spec = hdc3020_t_rh_event,
.num_event_specs = ARRAY_SIZE(hdc3020_t_rh_event),
},
{
.type = IIO_HUMIDITYRELATIVE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_PEAK) |
BIT(IIO_CHAN_INFO_TROUGH),
.event_spec = hdc3020_t_rh_event,
.num_event_specs = ARRAY_SIZE(hdc3020_t_rh_event),
},
{
/*
@ -82,7 +126,7 @@ static const struct iio_chan_spec hdc3020_channels[] = {
DECLARE_CRC8_TABLE(hdc3020_crc8_table);
static int hdc3020_write_bytes(struct hdc3020_data *data, const u8 *buf, u8 len)
static int hdc3020_write_bytes(struct hdc3020_data *data, u8 *buf, u8 len)
{
struct i2c_client *client = data->client;
struct i2c_msg msg;
@ -90,7 +134,7 @@ static int hdc3020_write_bytes(struct hdc3020_data *data, const u8 *buf, u8 len)
msg.addr = client->addr;
msg.flags = 0;
msg.buf = (char *)buf;
msg.buf = buf;
msg.len = len;
/*
@ -109,26 +153,28 @@ static int hdc3020_write_bytes(struct hdc3020_data *data, const u8 *buf, u8 len)
return -ETIMEDOUT;
}
static int hdc3020_read_bytes(struct hdc3020_data *data, const u8 *buf,
void *val, int len)
static
int hdc3020_read_bytes(struct hdc3020_data *data, u16 reg, u8 *buf, int len)
{
u8 reg_buf[2];
int ret, cnt;
struct i2c_client *client = data->client;
struct i2c_msg msg[2] = {
[0] = {
.addr = client->addr,
.flags = 0,
.buf = (char *)buf,
.buf = reg_buf,
.len = 2,
},
[1] = {
.addr = client->addr,
.flags = I2C_M_RD,
.buf = val,
.buf = buf,
.len = len,
},
};
put_unaligned_be16(reg, reg_buf);
/*
* During the measurement process, HDC3020 will not return data.
* So wait for a while and try again
@ -145,6 +191,30 @@ static int hdc3020_read_bytes(struct hdc3020_data *data, const u8 *buf,
return -ETIMEDOUT;
}
static int hdc3020_read_be16(struct hdc3020_data *data, u16 reg)
{
u8 crc, buf[3];
int ret;
ret = hdc3020_read_bytes(data, reg, buf, 3);
if (ret < 0)
return ret;
crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE);
if (crc != buf[2])
return -EINVAL;
return get_unaligned_be16(buf);
}
static int hdc3020_exec_cmd(struct hdc3020_data *data, u16 reg)
{
u8 reg_buf[2];
put_unaligned_be16(reg, reg_buf);
return hdc3020_write_bytes(data, reg_buf, 2);
}
static int hdc3020_read_measurement(struct hdc3020_data *data,
enum iio_chan_type type, int *val)
{
@ -175,96 +245,6 @@ static int hdc3020_read_measurement(struct hdc3020_data *data,
return 0;
}
/*
* After exiting the automatic measurement mode or resetting, the peak
* value will be reset to the default value
* This method is used to get the highest temp measured during automatic
* measurement
*/
static int hdc3020_read_high_peak_t(struct hdc3020_data *data, int *val)
{
u8 crc, buf[3];
int ret;
ret = hdc3020_read_bytes(data, HDC3020_R_T_HIGH_AUTO, buf, 3);
if (ret < 0)
return ret;
crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE);
if (crc != buf[2])
return -EINVAL;
*val = get_unaligned_be16(buf);
return 0;
}
/*
* This method is used to get the lowest temp measured during automatic
* measurement
*/
static int hdc3020_read_low_peak_t(struct hdc3020_data *data, int *val)
{
u8 crc, buf[3];
int ret;
ret = hdc3020_read_bytes(data, HDC3020_R_T_LOW_AUTO, buf, 3);
if (ret < 0)
return ret;
crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE);
if (crc != buf[2])
return -EINVAL;
*val = get_unaligned_be16(buf);
return 0;
}
/*
* This method is used to get the highest humidity measured during automatic
* measurement
*/
static int hdc3020_read_high_peak_rh(struct hdc3020_data *data, int *val)
{
u8 crc, buf[3];
int ret;
ret = hdc3020_read_bytes(data, HDC3020_R_RH_HIGH_AUTO, buf, 3);
if (ret < 0)
return ret;
crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE);
if (crc != buf[2])
return -EINVAL;
*val = get_unaligned_be16(buf);
return 0;
}
/*
* This method is used to get the lowest humidity measured during automatic
* measurement
*/
static int hdc3020_read_low_peak_rh(struct hdc3020_data *data, int *val)
{
u8 crc, buf[3];
int ret;
ret = hdc3020_read_bytes(data, HDC3020_R_RH_LOW_AUTO, buf, 3);
if (ret < 0)
return ret;
crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE);
if (crc != buf[2])
return -EINVAL;
*val = get_unaligned_be16(buf);
return 0;
}
static int hdc3020_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
@ -286,28 +266,28 @@ static int hdc3020_read_raw(struct iio_dev *indio_dev,
}
case IIO_CHAN_INFO_PEAK: {
guard(mutex)(&data->lock);
if (chan->type == IIO_TEMP) {
ret = hdc3020_read_high_peak_t(data, val);
if (ret < 0)
return ret;
} else {
ret = hdc3020_read_high_peak_rh(data, val);
if (ret < 0)
return ret;
}
if (chan->type == IIO_TEMP)
ret = hdc3020_read_be16(data, HDC3020_R_T_HIGH_AUTO);
else
ret = hdc3020_read_be16(data, HDC3020_R_RH_HIGH_AUTO);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_TROUGH: {
guard(mutex)(&data->lock);
if (chan->type == IIO_TEMP) {
ret = hdc3020_read_low_peak_t(data, val);
if (ret < 0)
return ret;
} else {
ret = hdc3020_read_low_peak_rh(data, val);
if (ret < 0)
return ret;
}
if (chan->type == IIO_TEMP)
ret = hdc3020_read_be16(data, HDC3020_R_T_LOW_AUTO);
else
ret = hdc3020_read_be16(data, HDC3020_R_RH_LOW_AUTO);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_SCALE:
@ -352,23 +332,17 @@ static int hdc3020_update_heater(struct hdc3020_data *data, int val)
if (val < hdc3020_heater_vals[0] || val > hdc3020_heater_vals[2])
return -EINVAL;
buf[0] = HDC3020_HEATER_CMD_MSB;
if (!val)
hdc3020_exec_cmd(data, HDC3020_HEATER_DISABLE);
if (!val) {
buf[1] = HDC3020_HEATER_DISABLE;
return hdc3020_write_bytes(data, buf, 2);
}
buf[1] = HDC3020_HEATER_CONFIG;
put_unaligned_be16(HDC3020_HEATER_CONFIG, buf);
put_unaligned_be16(val & GENMASK(13, 0), &buf[2]);
buf[4] = crc8(hdc3020_crc8_table, buf + 2, 2, CRC8_INIT_VALUE);
ret = hdc3020_write_bytes(data, buf, 5);
if (ret < 0)
return ret;
buf[1] = HDC3020_HEATER_ENABLE;
return hdc3020_write_bytes(data, buf, 2);
return hdc3020_exec_cmd(data, HDC3020_HEATER_ENABLE);
}
static int hdc3020_write_raw(struct iio_dev *indio_dev,
@ -389,15 +363,197 @@ static int hdc3020_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
static int hdc3020_write_thresh(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int val, int val2)
{
struct hdc3020_data *data = iio_priv(indio_dev);
u8 buf[5];
u64 tmp;
u16 reg;
int ret;
/* Supported temperature range is from 40 to 125 degree celsius */
if (val < HDC3020_MIN_TEMP || val > HDC3020_MAX_TEMP)
return -EINVAL;
/* Select threshold register */
if (info == IIO_EV_INFO_VALUE) {
if (dir == IIO_EV_DIR_RISING)
reg = HDC3020_S_T_RH_THRESH_HIGH;
else
reg = HDC3020_S_T_RH_THRESH_LOW;
} else {
if (dir == IIO_EV_DIR_RISING)
reg = HDC3020_S_T_RH_THRESH_HIGH_CLR;
else
reg = HDC3020_S_T_RH_THRESH_LOW_CLR;
}
guard(mutex)(&data->lock);
ret = hdc3020_read_be16(data, reg);
if (ret < 0)
return ret;
switch (chan->type) {
case IIO_TEMP:
/*
* Calculate temperature threshold, shift it down to get the
* truncated threshold representation in the 9LSBs while keeping
* the current humidity threshold in the 7 MSBs.
*/
tmp = ((u64)(((val + 45) * MICRO) + val2)) * 65535ULL;
tmp = div_u64(tmp, MICRO * 175);
val = tmp >> HDC3020_THRESH_TEMP_TRUNC_SHIFT;
val = FIELD_PREP(HDC3020_THRESH_TEMP_MASK, val);
val |= (FIELD_GET(HDC3020_THRESH_HUM_MASK, ret) <<
HDC3020_THRESH_HUM_TRUNC_SHIFT);
break;
case IIO_HUMIDITYRELATIVE:
/*
* Calculate humidity threshold, shift it down and up to get the
* truncated threshold representation in the 7MSBs while keeping
* the current temperature threshold in the 9 LSBs.
*/
tmp = ((u64)((val * MICRO) + val2)) * 65535ULL;
tmp = div_u64(tmp, MICRO * 100);
val = tmp >> HDC3020_THRESH_HUM_TRUNC_SHIFT;
val = FIELD_PREP(HDC3020_THRESH_HUM_MASK, val);
val |= FIELD_GET(HDC3020_THRESH_TEMP_MASK, ret);
break;
default:
return -EOPNOTSUPP;
}
put_unaligned_be16(reg, buf);
put_unaligned_be16(val, buf + 2);
buf[4] = crc8(hdc3020_crc8_table, buf + 2, 2, CRC8_INIT_VALUE);
return hdc3020_write_bytes(data, buf, 5);
}
static int hdc3020_read_thresh(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int *val, int *val2)
{
struct hdc3020_data *data = iio_priv(indio_dev);
u16 reg;
int ret;
/* Select threshold register */
if (info == IIO_EV_INFO_VALUE) {
if (dir == IIO_EV_DIR_RISING)
reg = HDC3020_R_T_RH_THRESH_HIGH;
else
reg = HDC3020_R_T_RH_THRESH_LOW;
} else {
if (dir == IIO_EV_DIR_RISING)
reg = HDC3020_R_T_RH_THRESH_HIGH_CLR;
else
reg = HDC3020_R_T_RH_THRESH_LOW_CLR;
}
guard(mutex)(&data->lock);
ret = hdc3020_read_be16(data, reg);
if (ret < 0)
return ret;
switch (chan->type) {
case IIO_TEMP:
/*
* Get the temperature threshold from 9 LSBs, shift them to get
* the truncated temperature threshold representation and
* calculate the threshold according to the formula in the
* datasheet.
*/
*val = FIELD_GET(HDC3020_THRESH_TEMP_MASK, ret);
*val = *val << HDC3020_THRESH_TEMP_TRUNC_SHIFT;
*val = -2949075 + (175 * (*val));
*val2 = 65535;
return IIO_VAL_FRACTIONAL;
case IIO_HUMIDITYRELATIVE:
/*
* Get the humidity threshold from 7 MSBs, shift them to get the
* truncated humidity threshold representation and calculate the
* threshold according to the formula in the datasheet.
*/
*val = FIELD_GET(HDC3020_THRESH_HUM_MASK, ret);
*val = (*val << HDC3020_THRESH_HUM_TRUNC_SHIFT) * 100;
*val2 = 65535;
return IIO_VAL_FRACTIONAL;
default:
return -EOPNOTSUPP;
}
}
static irqreturn_t hdc3020_interrupt_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct hdc3020_data *data;
s64 time;
int ret;
data = iio_priv(indio_dev);
ret = hdc3020_read_be16(data, HDC3020_R_STATUS);
if (ret < 0)
return IRQ_HANDLED;
if (!(ret & (HDC3020_STATUS_T_HIGH_ALERT | HDC3020_STATUS_T_LOW_ALERT |
HDC3020_STATUS_RH_HIGH_ALERT | HDC3020_STATUS_RH_LOW_ALERT)))
return IRQ_NONE;
time = iio_get_time_ns(indio_dev);
if (ret & HDC3020_STATUS_T_HIGH_ALERT)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_TEMP, 0,
IIO_NO_MOD,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
time);
if (ret & HDC3020_STATUS_T_LOW_ALERT)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_TEMP, 0,
IIO_NO_MOD,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_FALLING),
time);
if (ret & HDC3020_STATUS_RH_HIGH_ALERT)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_HUMIDITYRELATIVE, 0,
IIO_NO_MOD,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_RISING),
time);
if (ret & HDC3020_STATUS_RH_LOW_ALERT)
iio_push_event(indio_dev,
IIO_MOD_EVENT_CODE(IIO_HUMIDITYRELATIVE, 0,
IIO_NO_MOD,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_FALLING),
time);
return IRQ_HANDLED;
}
static const struct iio_info hdc3020_info = {
.read_raw = hdc3020_read_raw,
.write_raw = hdc3020_write_raw,
.read_avail = hdc3020_read_available,
.read_event_value = hdc3020_read_thresh,
.write_event_value = hdc3020_write_thresh,
};
static void hdc3020_stop(void *data)
{
hdc3020_write_bytes((struct hdc3020_data *)data, HDC3020_EXIT_AUTO, 2);
hdc3020_exec_cmd((struct hdc3020_data *)data, HDC3020_EXIT_AUTO);
}
static int hdc3020_probe(struct i2c_client *client)
@ -424,8 +580,25 @@ static int hdc3020_probe(struct i2c_client *client)
indio_dev->info = &hdc3020_info;
indio_dev->channels = hdc3020_channels;
indio_dev->num_channels = ARRAY_SIZE(hdc3020_channels);
if (client->irq) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, hdc3020_interrupt_handler,
IRQF_ONESHOT, "hdc3020",
indio_dev);
if (ret)
return dev_err_probe(&client->dev, ret,
"Failed to request IRQ\n");
ret = hdc3020_write_bytes(data, HDC3020_S_AUTO_10HZ_MOD0, 2);
/*
* The alert output is activated by default upon power up,
* hardware reset, and soft reset. Clear the status register.
*/
ret = hdc3020_exec_cmd(data, HDC3020_S_STATUS);
if (ret)
return ret;
}
ret = hdc3020_exec_cmd(data, HDC3020_S_AUTO_10HZ_MOD0);
if (ret)
return dev_err_probe(&client->dev, ret,
"Unable to set up measurement\n");

View File

@ -9,7 +9,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/acpi.h>
#include <linux/mod_devicetable.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/regmap.h>
@ -63,7 +63,7 @@ static struct i2c_driver hts221_driver = {
.name = "hts221_i2c",
.pm = pm_sleep_ptr(&hts221_pm_ops),
.of_match_table = hts221_i2c_of_match,
.acpi_match_table = ACPI_PTR(hts221_acpi_match),
.acpi_match_table = hts221_acpi_match,
},
.probe = hts221_i2c_probe,
.id_table = hts221_i2c_id_table,

View File

@ -1363,22 +1363,16 @@ static int adis16475_config_sync_mode(struct adis16475 *st)
static int adis16475_config_irq_pin(struct adis16475 *st)
{
int ret;
struct irq_data *desc;
u32 irq_type;
u16 val = 0;
u8 polarity;
struct spi_device *spi = st->adis.spi;
desc = irq_get_irq_data(spi->irq);
if (!desc) {
dev_err(&spi->dev, "Could not find IRQ %d\n", spi->irq);
return -EINVAL;
}
/*
* It is possible to configure the data ready polarity. Furthermore, we
* need to update the adis struct if we want data ready as active low.
*/
irq_type = irqd_get_trigger_type(desc);
irq_type = irq_get_trigger_type(spi->irq);
if (irq_type == IRQ_TYPE_EDGE_RISING) {
polarity = 1;
st->adis.irq_flag = IRQF_TRIGGER_RISING;

View File

@ -1246,18 +1246,11 @@ static int adis16480_config_irq_pin(struct adis16480 *st)
{
struct device *dev = &st->adis.spi->dev;
struct fwnode_handle *fwnode = dev_fwnode(dev);
struct irq_data *desc;
enum adis16480_int_pin pin;
unsigned int irq_type;
uint16_t val;
int i, irq = 0;
desc = irq_get_irq_data(st->adis.spi->irq);
if (!desc) {
dev_err(dev, "Could not find IRQ %d\n", irq);
return -EINVAL;
}
/* Disable data ready since the default after reset is on */
val = ADIS16480_DRDY_EN(0);
@ -1285,7 +1278,7 @@ static int adis16480_config_irq_pin(struct adis16480 *st)
* configured as positive or negative, corresponding to
* IRQ_TYPE_EDGE_RISING or IRQ_TYPE_EDGE_FALLING respectively.
*/
irq_type = irqd_get_trigger_type(desc);
irq_type = irq_get_trigger_type(st->adis.spi->irq);
if (irq_type == IRQ_TYPE_EDGE_RISING) { /* Default */
val |= ADIS16480_DRDY_POL(1);
} else if (irq_type == IRQ_TYPE_EDGE_FALLING) {

View File

@ -43,6 +43,15 @@ static const struct i2c_device_id bmi160_i2c_id[] = {
MODULE_DEVICE_TABLE(i2c, bmi160_i2c_id);
static const struct acpi_device_id bmi160_acpi_match[] = {
/*
* FIRMWARE BUG WORKAROUND
* Some manufacturers like GPD, Lenovo or Aya used the incorrect
* ID "10EC5280" for bmi160 in their DSDT. A fixed firmware is not
* available as of Feb 2024 after trying to work with OEMs, and
* this is not expected to change anymore since at least some of
* the affected devices are from 2021/2022.
*/
{"10EC5280", 0},
{"BMI0160", 0},
{ },
};

View File

@ -1668,52 +1668,41 @@ static int bmi323_write_raw(struct iio_dev *indio_dev,
int val2, long mask)
{
struct bmi323_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = bmi323_set_odr(data, bmi323_iio_to_sensor(chan->type),
val, val2);
iio_device_release_direct_mode(indio_dev);
return ret;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev)
return bmi323_set_odr(data,
bmi323_iio_to_sensor(chan->type),
val, val2);
unreachable();
case IIO_CHAN_INFO_SCALE:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = bmi323_set_scale(data, bmi323_iio_to_sensor(chan->type),
val, val2);
iio_device_release_direct_mode(indio_dev);
return ret;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev)
return bmi323_set_scale(data,
bmi323_iio_to_sensor(chan->type),
val, val2);
unreachable();
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = bmi323_set_average(data, bmi323_iio_to_sensor(chan->type),
val);
iio_device_release_direct_mode(indio_dev);
return ret;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev)
return bmi323_set_average(data,
bmi323_iio_to_sensor(chan->type),
val);
unreachable();
case IIO_CHAN_INFO_ENABLE:
return bmi323_enable_steps(data, val);
case IIO_CHAN_INFO_PROCESSED:
scoped_guard(mutex, &data->mutex) {
if (val || !FIELD_GET(BMI323_FEAT_IO0_STP_CNT_MSK,
data->feature_events))
return -EINVAL;
case IIO_CHAN_INFO_PROCESSED: {
guard(mutex)(&data->mutex);
/* Clear step counter value */
ret = bmi323_update_ext_reg(data, BMI323_STEP_SC1_REG,
BMI323_STEP_SC1_RST_CNT_MSK,
FIELD_PREP(BMI323_STEP_SC1_RST_CNT_MSK,
1));
}
return ret;
if (val || !FIELD_GET(BMI323_FEAT_IO0_STP_CNT_MSK,
data->feature_events))
return -EINVAL;
/* Clear step counter value */
return bmi323_update_ext_reg(data, BMI323_STEP_SC1_REG,
BMI323_STEP_SC1_RST_CNT_MSK,
FIELD_PREP(BMI323_STEP_SC1_RST_CNT_MSK,
1));
}
default:
return -EINVAL;
}
@ -1724,7 +1713,6 @@ static int bmi323_read_raw(struct iio_dev *indio_dev,
int *val2, long mask)
{
struct bmi323_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
@ -1733,14 +1721,10 @@ static int bmi323_read_raw(struct iio_dev *indio_dev,
switch (chan->type) {
case IIO_ACCEL:
case IIO_ANGL_VEL:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = bmi323_read_axis(data, chan, val);
iio_device_release_direct_mode(indio_dev);
return ret;
iio_device_claim_direct_scoped(return -EBUSY,
indio_dev)
return bmi323_read_axis(data, chan, val);
unreachable();
case IIO_TEMP:
return bmi323_get_temp_data(data, val);
default:

View File

@ -93,6 +93,26 @@ static int bmi323_i2c_probe(struct i2c_client *i2c)
return bmi323_core_probe(dev);
}
static const struct acpi_device_id bmi323_acpi_match[] = {
/*
* The "BOSC0200" identifier used here is not unique to bmi323 devices.
* The same "BOSC0200" identifier is found in the ACPI tables of devices
* using the bmc150 chip. This creates a conflict with duplicate ACPI
* identifiers which multiple drivers want to use. If a non-bmi323
* device starts to load with this "BOSC0200" ACPI match here, then the
* chip ID check portion should fail because the chip IDs received (via
* i2c) are unique between bmc150 and bmi323 and the driver should
* relinquish the device. If and when a different driver (such as
* bmc150) starts to load with the "BOSC0200" ACPI match, a short reset
* should ensure that the device is not in a bad state during that
* driver initialization. This device reset does occur in both the
* bmi323 and bmc150 init sequences.
*/
{ "BOSC0200" },
{ }
};
MODULE_DEVICE_TABLE(acpi, bmi323_acpi_match);
static const struct i2c_device_id bmi323_i2c_ids[] = {
{ "bmi323" },
{ }
@ -109,6 +129,7 @@ static struct i2c_driver bmi323_i2c_driver = {
.driver = {
.name = "bmi323",
.of_match_table = bmi323_of_i2c_match,
.acpi_match_table = bmi323_acpi_match,
},
.probe = bmi323_i2c_probe,
.id_table = bmi323_i2c_ids,

View File

@ -10,7 +10,6 @@
* 1 | 0 | 0x1C
* 1 | 1 | 0x1F
*/
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
@ -57,7 +56,7 @@ MODULE_DEVICE_TABLE(of, fxos8700_of_match);
static struct i2c_driver fxos8700_i2c_driver = {
.driver = {
.name = "fxos8700_i2c",
.acpi_match_table = ACPI_PTR(fxos8700_acpi_match),
.acpi_match_table = fxos8700_acpi_match,
.of_match_table = fxos8700_of_match,
},
.probe = fxos8700_i2c_probe,

View File

@ -2,7 +2,6 @@
/*
* FXOS8700 - NXP IMU, SPI bits
*/
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/regmap.h>
@ -46,7 +45,7 @@ static struct spi_driver fxos8700_spi_driver = {
.probe = fxos8700_spi_probe,
.id_table = fxos8700_spi_id,
.driver = {
.acpi_match_table = ACPI_PTR(fxos8700_acpi_match),
.acpi_match_table = fxos8700_acpi_match,
.of_match_table = fxos8700_of_match,
.name = "fxos8700_spi",
},

View File

@ -1514,7 +1514,7 @@ MODULE_DEVICE_TABLE(i2c, kmx61_id);
static struct i2c_driver kmx61_driver = {
.driver = {
.name = KMX61_DRV_NAME,
.acpi_match_table = ACPI_PTR(kmx61_acpi_match),
.acpi_match_table = kmx61_acpi_match,
.pm = pm_ptr(&kmx61_pm_ops),
},
.probe = kmx61_probe,

View File

@ -11,11 +11,32 @@ config IIO_ST_LSM6DSX
select IIO_ST_LSM6DSX_I3C if (I3C)
help
Say yes here to build support for STMicroelectronics LSM6DSx imu
sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm,
ism330dlc, lsm6dso, lsm6dsox, asm330lhh, asm330lhhx, lsm6dsr,
lsm6ds3tr-c, ism330dhcx, lsm6dsrx, lsm6ds0, lsm6dsop, lsm6dstx,
lsm6dsv, lsm6dsv16x, lsm6dso16is, ism330is, asm330lhb, lsm6dst
and the accelerometer/gyroscope of lsm9ds1.
sensor.
Supported devices:
- asm330lhb
- asm330lhh
- asm330lhhx
- asm330lhhxg1
- ism330dhcx
- ism330dlc
- ism330is
- lsm6ds0
- lsm6ds3
- lsm6ds3h
- lsm6ds3tr-c
- lsm6dsl
- lsm6dsm
- lsm6dso
- lsm6dso16is
- lsm6dsop
- lsm6dsox
- lsm6dsr
- lsm6dsrx
- lsm6dst
- lsm6dstx
- lsm6dsv
- lsm6dsv16x
- lsm9ds1
To compile this driver as a module, choose M here: the module
will be called st_lsm6dsx.

View File

@ -38,6 +38,7 @@
#define ST_LSM6DSO16IS_DEV_NAME "lsm6dso16is"
#define ST_ISM330IS_DEV_NAME "ism330is"
#define ST_ASM330LHB_DEV_NAME "asm330lhb"
#define ST_ASM330LHHXG1_DEV_NAME "asm330lhhxg1"
enum st_lsm6dsx_hw_id {
ST_LSM6DS3_ID = 1,
@ -63,6 +64,7 @@ enum st_lsm6dsx_hw_id {
ST_LSM6DSO16IS_ID,
ST_ISM330IS_ID,
ST_ASM330LHB_ID,
ST_ASM330LHHXG1_ID,
ST_LSM6DSX_MAX_ID,
};

View File

@ -2,7 +2,7 @@
/*
* STMicroelectronics st_lsm6dsx FIFO buffer library driver
*
* LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC/LSM6DS3TR-C:
* Pattern FIFO:
* The FIFO buffer can be configured to store data from gyroscope and
* accelerometer. Samples are queued without any tag according to a
* specific pattern based on 'FIFO data sets' (6 bytes each):
@ -14,12 +14,34 @@
* (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the
* value of the decimation factor and ODR set for each FIFO data set.
*
* LSM6DSO/LSM6DSOX/ASM330LHH/ASM330LHHX/LSM6DSR/LSM6DSRX/ISM330DHCX/
* LSM6DST/LSM6DSOP/LSM6DSTX/LSM6DSV/ASM330LHB:
* Supported devices:
* - ISM330DLC
* - LSM6DS3
* - LSM6DS3H
* - LSM6DS3TR-C
* - LSM6DSL
* - LSM6DSM
*
* Tagged FIFO:
* The FIFO buffer can be configured to store data from gyroscope and
* accelerometer. Each sample is queued with a tag (1B) indicating data
* source (gyroscope, accelerometer, hw timer).
*
* Supported devices:
* - ASM330LHB
* - ASM330LHH
* - ASM330LHHX
* - ASM330LHHXG1
* - ISM330DHCX
* - LSM6DSO
* - LSM6DSOP
* - LSM6DSOX
* - LSM6DSR
* - LSM6DSRX
* - LSM6DST
* - LSM6DSTX
* - LSM6DSV
*
* FIFO supported modes:
* - BYPASS: FIFO disabled
* - CONTINUOUS: FIFO enabled. When the buffer is full, the FIFO index

View File

@ -14,34 +14,51 @@
* by a different driver.
*
* Supported sensors:
* - LSM6DS3:
*
* - LSM6DS3
* - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416
* - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
* - FIFO size: 8KB
*
* - LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC/LSM6DS3TR-C:
* - ISM330DLC
* - LSM6DS3H
* - LSM6DS3TR-C
* - LSM6DSL
* - LSM6DSM
* - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416
* - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
* - FIFO size: 4KB
*
* - LSM6DSO/LSM6DSOX/ASM330LHH/ASM330LHHX/LSM6DSR/ISM330DHCX/LSM6DST/LSM6DSOP/
* LSM6DSTX/LSM6DSO16IS/ISM330IS:
* - ASM330LHH
* - ASM330LHHX
* - ASM330LHHXG1
* - ISM330DHCX
* - ISM330IS
* - LSM6DSO
* - LSM6DSO16IS
* - LSM6DSOP
* - LSM6DSOX
* - LSM6DSR
* - LSM6DST
* - LSM6DSTX
* - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416,
* 833
* - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
* - FIFO size: 3KB
*
* - LSM6DSV/LSM6DSV16X:
* - LSM6DSV
* - LSM6DSV16X
* - Accelerometer/Gyroscope supported ODR [Hz]: 7.5, 15, 30, 60, 120, 240,
* 480, 960
* - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
* - Gyroscope supported full-scale [dps]: +-125/+-250/+-500/+-1000/+-2000
* - FIFO size: 3KB
*
* - LSM9DS1/LSM6DS0:
* - LSM6DS0
* - LSM9DS1
* - Accelerometer supported ODR [Hz]: 10, 50, 119, 238, 476, 952
* - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
* - Gyroscope supported ODR [Hz]: 15, 60, 119, 238, 476, 952
@ -820,6 +837,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.hw_id = ST_ASM330LHHX_ID,
.name = ST_ASM330LHHX_DEV_NAME,
.wai = 0x6b,
}, {
.hw_id = ST_ASM330LHHXG1_ID,
.name = ST_ASM330LHHXG1_DEV_NAME,
.wai = 0x6b,
}, {
.hw_id = ST_LSM6DSTX_ID,
.name = ST_LSM6DSTX_DEV_NAME,

View File

@ -134,6 +134,10 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = {
.compatible = "st,asm330lhb",
.data = (void *)ST_ASM330LHB_ID,
},
{
.compatible = "st,asm330lhhxg1",
.data = (void *)ST_ASM330LHHXG1_ID,
},
{},
};
MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match);
@ -168,6 +172,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = {
{ ST_LSM6DSO16IS_DEV_NAME, ST_LSM6DSO16IS_ID },
{ ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID },
{ ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID },
{ ST_ASM330LHHXG1_DEV_NAME, ST_ASM330LHHXG1_ID },
{},
};
MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table);

View File

@ -129,6 +129,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = {
.compatible = "st,asm330lhb",
.data = (void *)ST_ASM330LHB_ID,
},
{
.compatible = "st,asm330lhhxg1",
.data = (void *)ST_ASM330LHHXG1_ID,
},
{},
};
MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match);
@ -157,6 +161,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = {
{ ST_LSM6DSO16IS_DEV_NAME, ST_LSM6DSO16IS_ID },
{ ST_ISM330IS_DEV_NAME, ST_ISM330IS_ID },
{ ST_ASM330LHB_DEV_NAME, ST_ASM330LHB_ID },
{ ST_ASM330LHHXG1_DEV_NAME, ST_ASM330LHHXG1_ID },
{},
};
MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table);

View File

@ -4,9 +4,12 @@
#ifndef ST_LSM9DS0_H
#define ST_LSM9DS0_H
struct iio_dev;
struct device;
struct regmap;
struct regulator;
struct iio_dev;
struct st_lsm9ds0 {
struct device *dev;
const char *name;

View File

@ -7,10 +7,10 @@
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
*/
#include <linux/device.h>
#include <linux/array_size.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/iio/common/st_sensors.h>
@ -25,10 +25,9 @@ static int st_lsm9ds0_probe_accel(struct st_lsm9ds0 *lsm9ds0, struct regmap *reg
struct st_sensor_data *data;
settings = st_accel_get_settings(lsm9ds0->name);
if (!settings) {
dev_err(dev, "device name %s not recognized.\n", lsm9ds0->name);
return -ENODEV;
}
if (!settings)
return dev_err_probe(dev, -ENODEV, "device name %s not recognized.\n",
lsm9ds0->name);
lsm9ds0->accel = devm_iio_device_alloc(dev, sizeof(*data));
if (!lsm9ds0->accel)
@ -51,10 +50,9 @@ static int st_lsm9ds0_probe_magn(struct st_lsm9ds0 *lsm9ds0, struct regmap *regm
struct st_sensor_data *data;
settings = st_magn_get_settings(lsm9ds0->name);
if (!settings) {
dev_err(dev, "device name %s not recognized.\n", lsm9ds0->name);
return -ENODEV;
}
if (!settings)
return dev_err_probe(dev, -ENODEV, "device name %s not recognized.\n",
lsm9ds0->name);
lsm9ds0->magn = devm_iio_device_alloc(dev, sizeof(*data));
if (!lsm9ds0->magn)
@ -80,8 +78,7 @@ int st_lsm9ds0_probe(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap)
ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulator_names),
regulator_names);
if (ret)
return dev_err_probe(dev, ret,
"unable to enable Vdd supply\n");
return dev_err_probe(dev, ret, "unable to enable Vdd supply\n");
/* Setup accelerometer device */
ret = st_lsm9ds0_probe_accel(lsm9ds0, regmap);

View File

@ -7,8 +7,10 @@
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gfp_types.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/regmap.h>
@ -39,7 +41,7 @@ MODULE_DEVICE_TABLE(i2c, st_lsm9ds0_id_table);
static const struct acpi_device_id st_lsm9ds0_acpi_match[] = {
{"ACCL0001", (kernel_ulong_t)LSM303D_IMU_DEV_NAME},
{ },
{}
};
MODULE_DEVICE_TABLE(acpi, st_lsm9ds0_acpi_match);

View File

@ -7,7 +7,9 @@
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gfp_types.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/regmap.h>

View File

@ -0,0 +1,418 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Framework to handle complex IIO aggregate devices.
*
* The typical architecture is to have one device as the frontend device which
* can be "linked" against one or multiple backend devices. All the IIO and
* userspace interface is expected to be registers/managed by the frontend
* device which will callback into the backends when needed (to get/set some
* configuration that it does not directly control).
*
* -------------------------------------------------------
* ------------------ | ------------ ------------ ------- FPGA|
* | ADC |------------------------| | ADC CORE |---------| DMA CORE |------| RAM | |
* | (Frontend/IIO) | Serial Data (eg: LVDS) | |(backend) |---------| |------| | |
* | |------------------------| ------------ ------------ ------- |
* ------------------ -------------------------------------------------------
*
* The framework interface is pretty simple:
* - Backends should register themselves with devm_iio_backend_register()
* - Frontend devices should get backends with devm_iio_backend_get()
*
* Also to note that the primary target for this framework are converters like
* ADC/DACs so iio_backend_ops will have some operations typical of converter
* devices. On top of that, this is "generic" for all IIO which means any kind
* of device can make use of the framework. That said, If the iio_backend_ops
* struct begins to grow out of control, we can always refactor things so that
* the industrialio-backend.c is only left with the really generic stuff. Then,
* we can build on top of it depending on the needs.
*
* Copyright (C) 2023-2024 Analog Devices Inc.
*/
#define dev_fmt(fmt) "iio-backend: " fmt
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/iio/backend.h>
struct iio_backend {
struct list_head entry;
const struct iio_backend_ops *ops;
struct device *dev;
struct module *owner;
void *priv;
};
/*
* Helper struct for requesting buffers. This ensures that we have all data
* that we need to free the buffer in a device managed action.
*/
struct iio_backend_buffer_pair {
struct iio_backend *back;
struct iio_buffer *buffer;
};
static LIST_HEAD(iio_back_list);
static DEFINE_MUTEX(iio_back_lock);
/*
* Helper macros to call backend ops. Makes sure the option is supported.
*/
#define iio_backend_check_op(back, op) ({ \
struct iio_backend *____back = back; \
int ____ret = 0; \
\
if (!____back->ops->op) \
____ret = -EOPNOTSUPP; \
\
____ret; \
})
#define iio_backend_op_call(back, op, args...) ({ \
struct iio_backend *__back = back; \
int __ret; \
\
__ret = iio_backend_check_op(__back, op); \
if (!__ret) \
__ret = __back->ops->op(__back, ##args); \
\
__ret; \
})
#define iio_backend_ptr_op_call(back, op, args...) ({ \
struct iio_backend *__back = back; \
void *ptr_err; \
int __ret; \
\
__ret = iio_backend_check_op(__back, op); \
if (__ret) \
ptr_err = ERR_PTR(__ret); \
else \
ptr_err = __back->ops->op(__back, ##args); \
\
ptr_err; \
})
#define iio_backend_void_op_call(back, op, args...) { \
struct iio_backend *__back = back; \
int __ret; \
\
__ret = iio_backend_check_op(__back, op); \
if (!__ret) \
__back->ops->op(__back, ##args); \
}
/**
* iio_backend_chan_enable - Enable a backend channel
* @back: Backend device
* @chan: Channel number
*
* RETURNS:
* 0 on success, negative error number on failure.
*/
int iio_backend_chan_enable(struct iio_backend *back, unsigned int chan)
{
return iio_backend_op_call(back, chan_enable, chan);
}
EXPORT_SYMBOL_NS_GPL(iio_backend_chan_enable, IIO_BACKEND);
/**
* iio_backend_chan_disable - Disable a backend channel
* @back: Backend device
* @chan: Channel number
*
* RETURNS:
* 0 on success, negative error number on failure.
*/
int iio_backend_chan_disable(struct iio_backend *back, unsigned int chan)
{
return iio_backend_op_call(back, chan_disable, chan);
}
EXPORT_SYMBOL_NS_GPL(iio_backend_chan_disable, IIO_BACKEND);
static void __iio_backend_disable(void *back)
{
iio_backend_void_op_call(back, disable);
}
/**
* devm_iio_backend_enable - Device managed backend enable
* @dev: Consumer device for the backend
* @back: Backend device
*
* RETURNS:
* 0 on success, negative error number on failure.
*/
int devm_iio_backend_enable(struct device *dev, struct iio_backend *back)
{
int ret;
ret = iio_backend_op_call(back, enable);
if (ret)
return ret;
return devm_add_action_or_reset(dev, __iio_backend_disable, back);
}
EXPORT_SYMBOL_NS_GPL(devm_iio_backend_enable, IIO_BACKEND);
/**
* iio_backend_data_format_set - Configure the channel data format
* @back: Backend device
* @chan: Channel number
* @data: Data format
*
* Properly configure a channel with respect to the expected data format. A
* @struct iio_backend_data_fmt must be passed with the settings.
*
* RETURNS:
* 0 on success, negative error number on failure.
*/
int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan,
const struct iio_backend_data_fmt *data)
{
if (!data || data->type >= IIO_BACKEND_DATA_TYPE_MAX)
return -EINVAL;
return iio_backend_op_call(back, data_format_set, chan, data);
}
EXPORT_SYMBOL_NS_GPL(iio_backend_data_format_set, IIO_BACKEND);
static void iio_backend_free_buffer(void *arg)
{
struct iio_backend_buffer_pair *pair = arg;
iio_backend_void_op_call(pair->back, free_buffer, pair->buffer);
}
/**
* devm_iio_backend_request_buffer - Device managed buffer request
* @dev: Consumer device for the backend
* @back: Backend device
* @indio_dev: IIO device
*
* Request an IIO buffer from the backend. The type of the buffer (typically
* INDIO_BUFFER_HARDWARE) is up to the backend to decide. This is because,
* normally, the backend dictates what kind of buffering we can get.
*
* The backend .free_buffer() hooks is automatically called on @dev detach.
*
* RETURNS:
* 0 on success, negative error number on failure.
*/
int devm_iio_backend_request_buffer(struct device *dev,
struct iio_backend *back,
struct iio_dev *indio_dev)
{
struct iio_backend_buffer_pair *pair;
struct iio_buffer *buffer;
pair = devm_kzalloc(dev, sizeof(*pair), GFP_KERNEL);
if (!pair)
return -ENOMEM;
buffer = iio_backend_ptr_op_call(back, request_buffer, indio_dev);
if (IS_ERR(buffer))
return PTR_ERR(buffer);
/* weak reference should be all what we need */
pair->back = back;
pair->buffer = buffer;
return devm_add_action_or_reset(dev, iio_backend_free_buffer, pair);
}
EXPORT_SYMBOL_NS_GPL(devm_iio_backend_request_buffer, IIO_BACKEND);
static void iio_backend_release(void *arg)
{
struct iio_backend *back = arg;
module_put(back->owner);
}
static int __devm_iio_backend_get(struct device *dev, struct iio_backend *back)
{
struct device_link *link;
int ret;
/*
* Make sure the provider cannot be unloaded before the consumer module.
* Note that device_links would still guarantee that nothing is
* accessible (and breaks) but this makes it explicit that the consumer
* module must be also unloaded.
*/
if (!try_module_get(back->owner))
return dev_err_probe(dev, -ENODEV,
"Cannot get module reference\n");
ret = devm_add_action_or_reset(dev, iio_backend_release, back);
if (ret)
return ret;
link = device_link_add(dev, back->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
if (!link)
return dev_err_probe(dev, -EINVAL,
"Could not link to supplier(%s)\n",
dev_name(back->dev));
dev_dbg(dev, "Found backend(%s) device\n", dev_name(back->dev));
return 0;
}
/**
* devm_iio_backend_get - Device managed backend device get
* @dev: Consumer device for the backend
* @name: Backend name
*
* Get's the backend associated with @dev.
*
* RETURNS:
* A backend pointer, negative error pointer otherwise.
*/
struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name)
{
struct fwnode_handle *fwnode;
struct iio_backend *back;
unsigned int index;
int ret;
if (name) {
ret = device_property_match_string(dev, "io-backend-names",
name);
if (ret < 0)
return ERR_PTR(ret);
index = ret;
} else {
index = 0;
}
fwnode = fwnode_find_reference(dev_fwnode(dev), "io-backends", index);
if (IS_ERR(fwnode)) {
dev_err_probe(dev, PTR_ERR(fwnode),
"Cannot get Firmware reference\n");
return ERR_CAST(fwnode);
}
guard(mutex)(&iio_back_lock);
list_for_each_entry(back, &iio_back_list, entry) {
if (!device_match_fwnode(back->dev, fwnode))
continue;
fwnode_handle_put(fwnode);
ret = __devm_iio_backend_get(dev, back);
if (ret)
return ERR_PTR(ret);
return back;
}
fwnode_handle_put(fwnode);
return ERR_PTR(-EPROBE_DEFER);
}
EXPORT_SYMBOL_NS_GPL(devm_iio_backend_get, IIO_BACKEND);
/**
* __devm_iio_backend_get_from_fwnode_lookup - Device managed fwnode backend device get
* @dev: Consumer device for the backend
* @fwnode: Firmware node of the backend device
*
* Search the backend list for a device matching @fwnode.
* This API should not be used and it's only present for preventing the first
* user of this framework to break it's DT ABI.
*
* RETURNS:
* A backend pointer, negative error pointer otherwise.
*/
struct iio_backend *
__devm_iio_backend_get_from_fwnode_lookup(struct device *dev,
struct fwnode_handle *fwnode)
{
struct iio_backend *back;
int ret;
guard(mutex)(&iio_back_lock);
list_for_each_entry(back, &iio_back_list, entry) {
if (!device_match_fwnode(back->dev, fwnode))
continue;
ret = __devm_iio_backend_get(dev, back);
if (ret)
return ERR_PTR(ret);
return back;
}
return ERR_PTR(-EPROBE_DEFER);
}
EXPORT_SYMBOL_NS_GPL(__devm_iio_backend_get_from_fwnode_lookup, IIO_BACKEND);
/**
* iio_backend_get_priv - Get driver private data
* @back: Backend device
*/
void *iio_backend_get_priv(const struct iio_backend *back)
{
return back->priv;
}
EXPORT_SYMBOL_NS_GPL(iio_backend_get_priv, IIO_BACKEND);
static void iio_backend_unregister(void *arg)
{
struct iio_backend *back = arg;
guard(mutex)(&iio_back_lock);
list_del(&back->entry);
}
/**
* devm_iio_backend_register - Device managed backend device register
* @dev: Backend device being registered
* @ops: Backend ops
* @priv: Device private data
*
* @ops is mandatory. Not providing it results in -EINVAL.
*
* RETURNS:
* 0 on success, negative error number on failure.
*/
int devm_iio_backend_register(struct device *dev,
const struct iio_backend_ops *ops, void *priv)
{
struct iio_backend *back;
if (!ops)
return dev_err_probe(dev, -EINVAL, "No backend ops given\n");
/*
* Through device_links, we guarantee that a frontend device cannot be
* bound/exist if the backend driver is not around. Hence, we can bind
* the backend object lifetime with the device being passed since
* removing it will tear the frontend/consumer down.
*/
back = devm_kzalloc(dev, sizeof(*back), GFP_KERNEL);
if (!back)
return -ENOMEM;
back->ops = ops;
back->owner = dev->driver->owner;
back->dev = dev;
back->priv = priv;
scoped_guard(mutex, &iio_back_lock)
list_add(&back->entry, &iio_back_list);
return devm_add_action_or_reset(dev, iio_backend_unregister, back);
}
EXPORT_SYMBOL_NS_GPL(devm_iio_backend_register, IIO_BACKEND);
MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
MODULE_DESCRIPTION("Framework to handle complex IIO aggregate devices");
MODULE_LICENSE("GPL");

View File

@ -42,7 +42,7 @@ static DEFINE_IDA(iio_ida);
static dev_t iio_devt;
#define IIO_DEV_MAX 256
struct bus_type iio_bus_type = {
const struct bus_type iio_bus_type = {
.name = "iio",
};
EXPORT_SYMBOL(iio_bus_type);
@ -213,9 +213,7 @@ bool iio_buffer_enabled(struct iio_dev *indio_dev)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
return iio_dev_opaque->currentmode &
(INDIO_BUFFER_HARDWARE | INDIO_BUFFER_SOFTWARE |
INDIO_BUFFER_TRIGGERED);
return iio_dev_opaque->currentmode & INDIO_ALL_BUFFER_MODES;
}
EXPORT_SYMBOL_GPL(iio_buffer_enabled);

View File

@ -34,24 +34,11 @@
static int iio_gts_get_gain(const u64 max, const u64 scale)
{
u64 full = max;
int tmp = 1;
if (scale > full || !scale)
return -EINVAL;
if (U64_MAX - full < scale) {
/* Risk of overflow */
if (full - scale < scale)
return 1;
full -= scale;
tmp++;
}
while (full > scale * (u64)tmp)
tmp++;
return tmp;
return div64_u64(full, scale);
}
/**

View File

@ -87,13 +87,14 @@ config APDS9960
module will be called apds9960
config AS73211
tristate "AMS AS73211 XYZ color sensor"
tristate "AMS AS73211 XYZ color sensor and AMS AS7331 UV sensor"
depends on I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
If you say yes here you get support for the AMS AS73211
JENCOLOR(R) Digital XYZ Sensor.
JENCOLOR(R) Digital XYZ and the AMS AS7331 UVA, UVB and UVC
ultraviolet sensors.
For triggered measurements, you will need an additional trigger driver
like IIO_HRTIMER_TRIGGER or IIO_SYSFS_TRIGGER.

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Support for AMS AS73211 JENCOLOR(R) Digital XYZ Sensor
* Support for AMS AS73211 JENCOLOR(R) Digital XYZ Sensor and AMS AS7331
* UVA, UVB and UVC (DUV) Ultraviolet Sensor
*
* Author: Christian Eggers <ceggers@arri.de>
*
@ -9,7 +10,9 @@
* Color light sensor with 16-bit channels for x, y, z and temperature);
* 7-bit I2C slave address 0x74 .. 0x77.
*
* Datasheet: https://ams.com/documents/20143/36005/AS73211_DS000556_3-01.pdf
* Datasheets:
* AS73211: https://ams.com/documents/20143/36005/AS73211_DS000556_3-01.pdf
* AS7331: https://ams.com/documents/20143/9106314/AS7331_DS001047_4-00.pdf
*/
#include <linux/bitfield.h>
@ -84,6 +87,20 @@ static const int as73211_hardwaregain_avail[] = {
1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048,
};
struct as73211_data;
/**
* struct as73211_spec_dev_data - device-specific data
* @intensity_scale: Function to retrieve intensity scale values.
* @channels: Device channels.
* @num_channels: Number of channels of the device.
*/
struct as73211_spec_dev_data {
int (*intensity_scale)(struct as73211_data *data, int chan, int *val, int *val2);
struct iio_chan_spec const *channels;
int num_channels;
};
/**
* struct as73211_data - Instance data for one AS73211
* @client: I2C client.
@ -94,6 +111,7 @@ static const int as73211_hardwaregain_avail[] = {
* @mutex: Keeps cached registers in sync with the device.
* @completion: Completion to wait for interrupt.
* @int_time_avail: Available integration times (depend on sampling frequency).
* @spec_dev: device-specific configuration.
*/
struct as73211_data {
struct i2c_client *client;
@ -104,6 +122,7 @@ struct as73211_data {
struct mutex mutex;
struct completion completion;
int int_time_avail[AS73211_SAMPLE_TIME_NUM * 2];
const struct as73211_spec_dev_data *spec_dev;
};
#define AS73211_COLOR_CHANNEL(_color, _si, _addr) { \
@ -138,6 +157,10 @@ struct as73211_data {
#define AS73211_SCALE_Y 298384270 /* nW/m^2 */
#define AS73211_SCALE_Z 160241927 /* nW/m^2 */
#define AS7331_SCALE_UVA 340000 /* nW/cm^2 */
#define AS7331_SCALE_UVB 378000 /* nW/cm^2 */
#define AS7331_SCALE_UVC 166000 /* nW/cm^2 */
/* Channel order MUST match devices result register order */
#define AS73211_SCAN_INDEX_TEMP 0
#define AS73211_SCAN_INDEX_X 1
@ -176,6 +199,28 @@ static const struct iio_chan_spec as73211_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(AS73211_SCAN_INDEX_TS),
};
static const struct iio_chan_spec as7331_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
.address = AS73211_OUT_TEMP,
.scan_index = AS73211_SCAN_INDEX_TEMP,
.scan_type = {
.sign = 'u',
.realbits = 16,
.storagebits = 16,
.endianness = IIO_LE,
}
},
AS73211_COLOR_CHANNEL(LIGHT_UVA, AS73211_SCAN_INDEX_X, AS73211_OUT_MRES1),
AS73211_COLOR_CHANNEL(LIGHT_UVB, AS73211_SCAN_INDEX_Y, AS73211_OUT_MRES2),
AS73211_COLOR_CHANNEL(LIGHT_DUV, AS73211_SCAN_INDEX_Z, AS73211_OUT_MRES3),
IIO_CHAN_SOFT_TIMESTAMP(AS73211_SCAN_INDEX_TS),
};
static unsigned int as73211_integration_time_1024cyc(struct as73211_data *data)
{
/*
@ -316,6 +361,48 @@ static int as73211_req_data(struct as73211_data *data)
return 0;
}
static int as73211_intensity_scale(struct as73211_data *data, int chan,
int *val, int *val2)
{
switch (chan) {
case IIO_MOD_X:
*val = AS73211_SCALE_X;
break;
case IIO_MOD_Y:
*val = AS73211_SCALE_Y;
break;
case IIO_MOD_Z:
*val = AS73211_SCALE_Z;
break;
default:
return -EINVAL;
}
*val2 = as73211_integration_time_1024cyc(data) * as73211_gain(data);
return IIO_VAL_FRACTIONAL;
}
static int as7331_intensity_scale(struct as73211_data *data, int chan,
int *val, int *val2)
{
switch (chan) {
case IIO_MOD_LIGHT_UVA:
*val = AS7331_SCALE_UVA;
break;
case IIO_MOD_LIGHT_UVB:
*val = AS7331_SCALE_UVB;
break;
case IIO_MOD_LIGHT_DUV:
*val = AS7331_SCALE_UVC;
break;
default:
return -EINVAL;
}
*val2 = as73211_integration_time_1024cyc(data) * as73211_gain(data);
return IIO_VAL_FRACTIONAL;
}
static int as73211_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
@ -355,30 +442,13 @@ static int as73211_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec cons
*val2 = AS73211_SCALE_TEMP_MICRO;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_INTENSITY: {
unsigned int scale;
switch (chan->channel2) {
case IIO_MOD_X:
scale = AS73211_SCALE_X;
break;
case IIO_MOD_Y:
scale = AS73211_SCALE_Y;
break;
case IIO_MOD_Z:
scale = AS73211_SCALE_Z;
break;
default:
return -EINVAL;
}
scale /= as73211_gain(data);
scale /= as73211_integration_time_1024cyc(data);
*val = scale;
return IIO_VAL_INT;
case IIO_INTENSITY:
return data->spec_dev->intensity_scale(data, chan->channel2,
val, val2);
default:
return -EINVAL;
}}
}
case IIO_CHAN_INFO_SAMP_FREQ:
/* f_samp is configured in CREG3 in powers of 2 (x 1.024 MHz) */
@ -676,13 +746,17 @@ static int as73211_probe(struct i2c_client *client)
i2c_set_clientdata(client, indio_dev);
data->client = client;
data->spec_dev = i2c_get_match_data(client);
if (!data->spec_dev)
return -EINVAL;
mutex_init(&data->mutex);
init_completion(&data->completion);
indio_dev->info = &as73211_info;
indio_dev->name = AS73211_DRV_NAME;
indio_dev->channels = as73211_channels;
indio_dev->num_channels = ARRAY_SIZE(as73211_channels);
indio_dev->channels = data->spec_dev->channels;
indio_dev->num_channels = data->spec_dev->num_channels;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = i2c_smbus_read_byte_data(data->client, AS73211_REG_OSR);
@ -772,14 +846,28 @@ static int as73211_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(as73211_pm_ops, as73211_suspend,
as73211_resume);
static const struct as73211_spec_dev_data as73211_spec = {
.intensity_scale = as73211_intensity_scale,
.channels = as73211_channels,
.num_channels = ARRAY_SIZE(as73211_channels),
};
static const struct as73211_spec_dev_data as7331_spec = {
.intensity_scale = as7331_intensity_scale,
.channels = as7331_channels,
.num_channels = ARRAY_SIZE(as7331_channels),
};
static const struct of_device_id as73211_of_match[] = {
{ .compatible = "ams,as73211" },
{ .compatible = "ams,as73211", &as73211_spec },
{ .compatible = "ams,as7331", &as7331_spec },
{ }
};
MODULE_DEVICE_TABLE(of, as73211_of_match);
static const struct i2c_device_id as73211_id[] = {
{ "as73211", 0 },
{ "as73211", (kernel_ulong_t)&as73211_spec },
{ "as7331", (kernel_ulong_t)&as7331_spec },
{ }
};
MODULE_DEVICE_TABLE(i2c, as73211_id);

View File

@ -14,8 +14,11 @@
#include "../common/hid-sensors/hid-sensor-trigger.h"
enum {
CHANNEL_SCAN_INDEX_INTENSITY = 0,
CHANNEL_SCAN_INDEX_ILLUM = 1,
CHANNEL_SCAN_INDEX_INTENSITY,
CHANNEL_SCAN_INDEX_ILLUM,
CHANNEL_SCAN_INDEX_COLOR_TEMP,
CHANNEL_SCAN_INDEX_CHROMATICITY_X,
CHANNEL_SCAN_INDEX_CHROMATICITY_Y,
CHANNEL_SCAN_INDEX_MAX
};
@ -25,6 +28,7 @@ struct als_state {
struct hid_sensor_hub_callbacks callbacks;
struct hid_sensor_common common_attributes;
struct hid_sensor_hub_attribute_info als[CHANNEL_SCAN_INDEX_MAX];
struct iio_chan_spec channels[CHANNEL_SCAN_INDEX_MAX + 1];
struct {
u32 illum[CHANNEL_SCAN_INDEX_MAX];
u64 timestamp __aligned(8);
@ -33,7 +37,18 @@ struct als_state {
int scale_post_decml;
int scale_precision;
int value_offset;
int num_channels;
s64 timestamp;
unsigned long als_scan_mask[2];
};
/* The order of usage ids must match scan index starting from CHANNEL_SCAN_INDEX_INTENSITY */
static const u32 als_usage_ids[] = {
HID_USAGE_SENSOR_LIGHT_ILLUM,
HID_USAGE_SENSOR_LIGHT_ILLUM,
HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE,
HID_USAGE_SENSOR_LIGHT_CHROMATICITY_X,
HID_USAGE_SENSOR_LIGHT_CHROMATICITY_Y,
};
static const u32 als_sensitivity_addresses[] = {
@ -65,6 +80,40 @@ static const struct iio_chan_spec als_channels[] = {
BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE),
.scan_index = CHANNEL_SCAN_INDEX_ILLUM,
},
{
.type = IIO_COLORTEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS) |
BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE),
.scan_index = CHANNEL_SCAN_INDEX_COLOR_TEMP,
},
{
.type = IIO_CHROMATICITY,
.modified = 1,
.channel2 = IIO_MOD_X,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS) |
BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE),
.scan_index = CHANNEL_SCAN_INDEX_CHROMATICITY_X,
},
{
.type = IIO_CHROMATICITY,
.modified = 1,
.channel2 = IIO_MOD_Y,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_HYSTERESIS) |
BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE),
.scan_index = CHANNEL_SCAN_INDEX_CHROMATICITY_Y,
},
IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
};
@ -103,6 +152,21 @@ static int als_read_raw(struct iio_dev *indio_dev,
min = als_state->als[chan->scan_index].logical_minimum;
address = HID_USAGE_SENSOR_LIGHT_ILLUM;
break;
case CHANNEL_SCAN_INDEX_COLOR_TEMP:
report_id = als_state->als[chan->scan_index].report_id;
min = als_state->als[chan->scan_index].logical_minimum;
address = HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE;
break;
case CHANNEL_SCAN_INDEX_CHROMATICITY_X:
report_id = als_state->als[chan->scan_index].report_id;
min = als_state->als[chan->scan_index].logical_minimum;
address = HID_USAGE_SENSOR_LIGHT_CHROMATICITY_X;
break;
case CHANNEL_SCAN_INDEX_CHROMATICITY_Y:
report_id = als_state->als[chan->scan_index].report_id;
min = als_state->als[chan->scan_index].logical_minimum;
address = HID_USAGE_SENSOR_LIGHT_CHROMATICITY_Y;
break;
default:
report_id = -1;
break;
@ -223,6 +287,18 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev,
als_state->scan.illum[CHANNEL_SCAN_INDEX_ILLUM] = sample_data;
ret = 0;
break;
case HID_USAGE_SENSOR_LIGHT_COLOR_TEMPERATURE:
als_state->scan.illum[CHANNEL_SCAN_INDEX_COLOR_TEMP] = sample_data;
ret = 0;
break;
case HID_USAGE_SENSOR_LIGHT_CHROMATICITY_X:
als_state->scan.illum[CHANNEL_SCAN_INDEX_CHROMATICITY_X] = sample_data;
ret = 0;
break;
case HID_USAGE_SENSOR_LIGHT_CHROMATICITY_Y:
als_state->scan.illum[CHANNEL_SCAN_INDEX_CHROMATICITY_Y] = sample_data;
ret = 0;
break;
case HID_USAGE_SENSOR_TIME_TIMESTAMP:
als_state->timestamp = hid_sensor_convert_timestamp(&als_state->common_attributes,
*(s64 *)raw_data);
@ -238,27 +314,38 @@ static int als_capture_sample(struct hid_sensor_hub_device *hsdev,
/* Parse report which is specific to an usage id*/
static int als_parse_report(struct platform_device *pdev,
struct hid_sensor_hub_device *hsdev,
struct iio_chan_spec *channels,
unsigned usage_id,
struct als_state *st)
{
int ret;
struct iio_chan_spec *channels;
int ret, index = 0;
int i;
for (i = 0; i <= CHANNEL_SCAN_INDEX_ILLUM; ++i) {
channels = st->channels;
for (i = 0; i < CHANNEL_SCAN_INDEX_MAX; ++i) {
ret = sensor_hub_input_get_attribute_info(hsdev,
HID_INPUT_REPORT,
usage_id,
HID_USAGE_SENSOR_LIGHT_ILLUM,
als_usage_ids[i],
&st->als[i]);
if (ret < 0)
return ret;
als_adjust_channel_bit_mask(channels, i, st->als[i].size);
continue;
channels[index] = als_channels[i];
st->als_scan_mask[0] |= BIT(i);
als_adjust_channel_bit_mask(channels, index, st->als[i].size);
++index;
dev_dbg(&pdev->dev, "als %x:%x\n", st->als[i].index,
st->als[i].report_id);
}
st->num_channels = index;
/* Return success even if one usage id is present */
if (index)
ret = 0;
st->scale_precision = hid_sensor_format_scale(usage_id,
&st->als[CHANNEL_SCAN_INDEX_INTENSITY],
&st->scale_pre_decml, &st->scale_post_decml);
@ -294,15 +381,7 @@ static int hid_als_probe(struct platform_device *pdev)
return ret;
}
indio_dev->channels = devm_kmemdup(&pdev->dev, als_channels,
sizeof(als_channels), GFP_KERNEL);
if (!indio_dev->channels) {
dev_err(&pdev->dev, "failed to duplicate channels\n");
return -ENOMEM;
}
ret = als_parse_report(pdev, hsdev,
(struct iio_chan_spec *)indio_dev->channels,
hsdev->usage,
als_state);
if (ret) {
@ -310,8 +389,15 @@ static int hid_als_probe(struct platform_device *pdev)
return ret;
}
indio_dev->num_channels =
ARRAY_SIZE(als_channels);
/* Add timestamp channel */
als_state->channels[als_state->num_channels] = als_channels[CHANNEL_SCAN_INDEX_TIMESTAMP];
/* +1 for adding timestamp channel */
indio_dev->num_channels = als_state->num_channels + 1;
indio_dev->channels = als_state->channels;
indio_dev->available_scan_masks = als_state->als_scan_mask;
indio_dev->info = &als_info;
indio_dev->name = name;
indio_dev->modes = INDIO_DIRECT_MODE;

View File

@ -12,10 +12,10 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/acpi.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@ -438,7 +438,7 @@ static struct i2c_driver jsa1212_driver = {
.driver = {
.name = JSA1212_DRIVER_NAME,
.pm = pm_sleep_ptr(&jsa1212_pm_ops),
.acpi_match_table = ACPI_PTR(jsa1212_acpi_match),
.acpi_match_table = jsa1212_acpi_match,
},
.probe = jsa1212_probe,
.remove = jsa1212_remove,

View File

@ -10,6 +10,7 @@
*/
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/delay.h>
@ -1639,7 +1640,7 @@ static struct i2c_driver ltr501_driver = {
.name = LTR501_DRV_NAME,
.of_match_table = ltr501_of_match,
.pm = pm_sleep_ptr(&ltr501_pm_ops),
.acpi_match_table = ACPI_PTR(ltr_acpi_match),
.acpi_match_table = ltr_acpi_match,
},
.probe = ltr501_probe,
.remove = ltr501_remove,

View File

@ -10,6 +10,7 @@
*/
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
@ -19,7 +20,6 @@
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/acpi.h>
#define MAX44000_DRV_NAME "max44000"
@ -603,18 +603,16 @@ static const struct i2c_device_id max44000_id[] = {
};
MODULE_DEVICE_TABLE(i2c, max44000_id);
#ifdef CONFIG_ACPI
static const struct acpi_device_id max44000_acpi_match[] = {
{"MAX44000", 0},
{ }
};
MODULE_DEVICE_TABLE(acpi, max44000_acpi_match);
#endif
static struct i2c_driver max44000_driver = {
.driver = {
.name = MAX44000_DRV_NAME,
.acpi_match_table = ACPI_PTR(max44000_acpi_match),
.acpi_match_table = max44000_acpi_match,
},
.probe = max44000_probe,
.id_table = max44000_id,

View File

@ -10,11 +10,11 @@
*/
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/acpi.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
@ -1119,7 +1119,7 @@ static struct i2c_driver rpr0521_driver = {
.driver = {
.name = RPR0521_DRV_NAME,
.pm = pm_ptr(&rpr0521_pm_ops),
.acpi_match_table = ACPI_PTR(rpr0521_acpi_match),
.acpi_match_table = rpr0521_acpi_match,
},
.probe = rpr0521_probe,
.remove = rpr0521_remove,

View File

@ -7,11 +7,11 @@
* IIO driver for STK3310/STK3311. 7-bit I2C address: 0x48.
*/
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/regmap.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
@ -712,7 +712,7 @@ static struct i2c_driver stk3310_driver = {
.name = "stk3310",
.of_match_table = stk3310_of_match,
.pm = pm_sleep_ptr(&stk3310_pm_ops),
.acpi_match_table = ACPI_PTR(stk3310_acpi_id),
.acpi_match_table = stk3310_acpi_id,
},
.probe = stk3310_probe,
.remove = stk3310_remove,

View File

@ -9,7 +9,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/acpi.h>
#include <linux/mod_devicetable.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/iio/events.h>
@ -972,7 +972,7 @@ static struct i2c_driver us5182d_driver = {
.name = US5182D_DRV_NAME,
.pm = pm_ptr(&us5182d_pm_ops),
.of_match_table = us5182d_of_match,
.acpi_match_table = ACPI_PTR(us5182d_acpi_match),
.acpi_match_table = us5182d_acpi_match,
},
.probe = us5182d_probe,
.remove = us5182d_remove,

View File

@ -90,6 +90,7 @@
#define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0)
#define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */
#define VCNL4040_CONF1_PS_PERS GENMASK(5, 4) /* Proximity interrupt persistence setting */
#define VCNL4040_PS_CONF2_PS_HD BIT(11) /* Proximity high definition */
#define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */
#define VCNL4040_PS_CONF3_MPS GENMASK(6, 5) /* Proximity multi pulse number */
#define VCNL4040_PS_MS_LED_I GENMASK(10, 8) /* Proximity current */
@ -114,6 +115,13 @@
#define VCNL4010_INT_DRDY \
(BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS))
#define VCNL4040_CONF3_PS_MPS_16BITS 3 /* 8 multi pulses */
#define VCNL4040_CONF3_PS_LED_I_16BITS 3 /* 120 mA */
#define VCNL4040_CONF3_PS_SAMPLE_16BITS \
(FIELD_PREP(VCNL4040_PS_CONF3_MPS, VCNL4040_CONF3_PS_MPS_16BITS) | \
FIELD_PREP(VCNL4040_PS_MS_LED_I, VCNL4040_CONF3_PS_LED_I_16BITS))
static const int vcnl4010_prox_sampling_frequency[][2] = {
{1, 950000},
{3, 906250},
@ -195,6 +203,7 @@ struct vcnl4000_data {
enum vcnl4000_device_ids id;
int rev;
int al_scale;
int ps_scale;
u8 ps_int; /* proximity interrupt mode */
u8 als_int; /* ambient light interrupt mode*/
const struct vcnl4000_chip_spec *chip_spec;
@ -345,6 +354,7 @@ static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on)
static int vcnl4200_init(struct vcnl4000_data *data)
{
int ret, id;
u16 regval;
ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID);
if (ret < 0)
@ -386,9 +396,32 @@ static int vcnl4200_init(struct vcnl4000_data *data)
break;
}
data->al_scale = data->chip_spec->ulux_step;
data->ps_scale = 16;
mutex_init(&data->vcnl4200_al.lock);
mutex_init(&data->vcnl4200_ps.lock);
/* Use 16 bits proximity sensor readings */
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1);
if (ret < 0)
return ret;
regval = ret | VCNL4040_PS_CONF2_PS_HD;
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1,
regval);
if (ret < 0)
return ret;
/* Align proximity sensor sample rate to 16 bits data width */
ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF3);
if (ret < 0)
return ret;
regval = ret | VCNL4040_CONF3_PS_SAMPLE_16BITS;
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF3,
regval);
if (ret < 0)
return ret;
ret = data->chip_spec->set_power_state(data, true);
if (ret < 0)
return ret;
@ -901,8 +934,9 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
break;
case IIO_PROXIMITY:
ret = data->chip_spec->measure_proximity(data, val);
*val2 = data->ps_scale;
if (!ret)
ret = IIO_VAL_INT;
ret = IIO_VAL_FRACTIONAL;
break;
default:
ret = -EINVAL;

View File

@ -11,7 +11,6 @@
#include <linux/mod_devicetable.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/acpi.h>
#include <linux/regmap.h>
#include "bmc150_magn.h"
@ -68,7 +67,7 @@ static struct i2c_driver bmc150_magn_driver = {
.driver = {
.name = "bmc150_magn_i2c",
.of_match_table = bmc150_magn_of_match,
.acpi_match_table = ACPI_PTR(bmc150_magn_acpi_match),
.acpi_match_table = bmc150_magn_acpi_match,
.pm = &bmc150_magn_pm_ops,
},
.probe = bmc150_magn_i2c_probe,

View File

@ -10,7 +10,6 @@
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
#include <linux/regmap.h>
#include "bmc150_magn.h"
@ -55,7 +54,7 @@ static struct spi_driver bmc150_magn_spi_driver = {
.remove = bmc150_magn_spi_remove,
.id_table = bmc150_magn_spi_id,
.driver = {
.acpi_match_table = ACPI_PTR(bmc150_magn_acpi_match),
.acpi_match_table = bmc150_magn_acpi_match,
.name = "bmc150_magn_spi",
},
};

View File

@ -10,11 +10,11 @@
*/
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/regmap.h>
#include <linux/acpi.h>
#include <linux/pm.h>
#include <linux/iio/iio.h>
@ -573,7 +573,7 @@ static struct i2c_driver mmc35240_driver = {
.name = MMC35240_DRV_NAME,
.of_match_table = mmc35240_of_match,
.pm = pm_sleep_ptr(&mmc35240_pm_ops),
.acpi_match_table = ACPI_PTR(mmc35240_acpi_match),
.acpi_match_table = mmc35240_acpi_match,
},
.probe = mmc35240_probe,
.id_table = mmc35240_id,

View File

@ -5,8 +5,8 @@
* Copyright (C) 2016 Cristina-Gabriela Moraru <cristina.moraru09@gmail.com>
*/
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/iio.h>
@ -144,7 +144,7 @@ MODULE_DEVICE_TABLE(acpi, max5487_acpi_match);
static struct spi_driver max5487_driver = {
.driver = {
.name = "max5487",
.acpi_match_table = ACPI_PTR(max5487_acpi_match),
.acpi_match_table = max5487_acpi_match,
},
.id_table = max5487_id,
.probe = max5487_spi_probe,

View File

@ -114,6 +114,8 @@ config HSC030PA
depends on (I2C || SPI_MASTER)
select HSC030PA_I2C if I2C
select HSC030PA_SPI if SPI_MASTER
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say Y here to build support for the Honeywell TruStability
HSC and SSC pressure and temperature sensor series.
@ -181,7 +183,9 @@ config MPL3115
config MPRLS0025PA
tristate "Honeywell MPRLS0025PA (MicroPressure sensors series)"
depends on I2C
depends on (I2C || SPI_MASTER)
select MPRLS0025PA_I2C if I2C
select MPRLS0025PA_SPI if SPI_MASTER
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
@ -192,6 +196,16 @@ config MPRLS0025PA
To compile this driver as a module, choose M here: the module will be
called mprls0025pa.
config MPRLS0025PA_I2C
tristate
depends on MPRLS0025PA
depends on I2C
config MPRLS0025PA_SPI
tristate
depends on MPRLS0025PA
depends on SPI_MASTER
config MS5611
tristate "Measurement Specialties MS5611 pressure sensor driver"
select IIO_BUFFER

View File

@ -24,6 +24,8 @@ obj-$(CONFIG_MPL115_I2C) += mpl115_i2c.o
obj-$(CONFIG_MPL115_SPI) += mpl115_spi.o
obj-$(CONFIG_MPL3115) += mpl3115.o
obj-$(CONFIG_MPRLS0025PA) += mprls0025pa.o
obj-$(CONFIG_MPRLS0025PA_I2C) += mprls0025pa_i2c.o
obj-$(CONFIG_MPRLS0025PA_SPI) += mprls0025pa_spi.o
obj-$(CONFIG_MS5611) += ms5611_core.o
obj-$(CONFIG_MS5611_I2C) += ms5611_i2c.o
obj-$(CONFIG_MS5611_SPI) += ms5611_spi.o

View File

@ -11,12 +11,12 @@
*/
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/delay.h>
#include <linux/util_macros.h>
#include <linux/acpi.h>
#include <asm/unaligned.h>
@ -400,20 +400,18 @@ static const struct i2c_device_id hp206c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, hp206c_id);
#ifdef CONFIG_ACPI
static const struct acpi_device_id hp206c_acpi_match[] = {
{"HOP206C", 0},
{ },
};
MODULE_DEVICE_TABLE(acpi, hp206c_acpi_match);
#endif
static struct i2c_driver hp206c_driver = {
.probe = hp206c_probe,
.id_table = hp206c_id,
.driver = {
.name = "hp206c",
.acpi_match_table = ACPI_PTR(hp206c_acpi_match),
.acpi_match_table = hp206c_acpi_match,
},
};

View File

@ -22,8 +22,11 @@
#include <linux/types.h>
#include <linux/units.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <asm/unaligned.h>
@ -297,6 +300,29 @@ static int hsc_get_measurement(struct hsc_data *data)
return 0;
}
static irqreturn_t hsc_trigger_handler(int irq, void *private)
{
struct iio_poll_func *pf = private;
struct iio_dev *indio_dev = pf->indio_dev;
struct hsc_data *data = iio_priv(indio_dev);
int ret;
ret = hsc_get_measurement(data);
if (ret)
goto error;
memcpy(&data->scan.chan[0], &data->buffer[0], 2);
memcpy(&data->scan.chan[1], &data->buffer[2], 2);
iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
iio_get_time_ns(indio_dev));
error:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
/*
* IIO ABI expects
* value = (conv + offset) * scale
@ -382,13 +408,29 @@ static const struct iio_chan_spec hsc_channels[] = {
.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 = 14,
.storagebits = 16,
.endianness = IIO_BE,
},
},
{
.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 = 11,
.storagebits = 16,
.shift = 5,
.endianness = IIO_BE,
},
},
IIO_CHAN_SOFT_TIMESTAMP(2),
};
static const struct iio_info hsc_info = {
@ -406,7 +448,7 @@ int hsc_common_probe(struct device *dev, hsc_recv_fn recv)
struct hsc_data *hsc;
struct iio_dev *indio_dev;
const char *triplet;
u64 tmp;
s64 tmp;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*hsc));
@ -485,6 +527,11 @@ int hsc_common_probe(struct device *dev, hsc_recv_fn recv)
indio_dev->channels = hsc->chip->channels;
indio_dev->num_channels = hsc->chip->num_channels;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
hsc_trigger_handler, NULL);
if (ret)
return ret;
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS(hsc_common_probe, IIO_HONEYWELL_HSC030PA);

View File

@ -10,7 +10,10 @@
#include <linux/types.h>
#include <linux/iio/iio.h>
#define HSC_REG_MEASUREMENT_RD_SIZE 4
#define HSC_RESP_TIME_MS 2
struct device;
@ -53,6 +56,10 @@ struct hsc_data {
s32 p_scale_dec;
s64 p_offset;
s32 p_offset_dec;
struct {
__be16 chan[2];
s64 timestamp __aligned(8);
} scan;
u8 buffer[HSC_REG_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN);
};

View File

@ -4,14 +4,17 @@
*
* Copyright (c) 2023 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/trustability-hsc-series/documents/sps-siot-trustability-hsc-series-high-accuracy-board-mount-pressure-sensors-50099148-a-en-ciid-151133.pdf [hsc]
* Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/common/documents/sps-siot-i2c-comms-digital-output-pressure-sensors-tn-008201-3-en-ciid-45841.pdf [i2c related]
* Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/common/documents/sps-siot-i2c-comms-digital-output-pressure-sensors-tn-008201-3-en-ciid-45841.pdf
* Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/common/documents/sps-siot-sleep-mode-technical-note-008286-1-en-ciid-155793.pdf
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/iio/iio.h>
@ -23,6 +26,8 @@ static int hsc_i2c_recv(struct hsc_data *data)
struct i2c_msg msg;
int ret;
msleep_interruptible(HSC_RESP_TIME_MS);
msg.addr = client->addr;
msg.flags = client->flags | I2C_M_RD;
msg.len = HSC_REG_MEASUREMENT_RD_SIZE;

View File

@ -4,13 +4,17 @@
*
* Copyright (c) 2023 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/trustability-hsc-series/documents/sps-siot-trustability-hsc-series-high-accuracy-board-mount-pressure-sensors-50099148-a-en-ciid-151133.pdf
* Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/common/documents/sps-siot-spi-comms-digital-ouptu-pressure-sensors-tn-008202-3-en-ciid-45843.pdf
* Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/common/documents/sps-siot-sleep-mode-technical-note-008286-1-en-ciid-155793.pdf
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/iio/iio.h>
@ -25,6 +29,7 @@ static int hsc_spi_recv(struct hsc_data *data)
.len = HSC_REG_MEASUREMENT_RD_SIZE,
};
msleep_interruptible(HSC_RESP_TIME_MS);
return spi_sync_transfer(spi, &xfer, 1);
}

View File

@ -5,17 +5,13 @@
* Copyright (c) Andreas Klinger <ak@it-klinger.de>
*
* Data sheet:
* https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/
* products/sensors/pressure-sensors/board-mount-pressure-sensors/
* micropressure-mpr-series/documents/
* sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf
* https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf
*
* 7-bit I2C default slave address: 0x18
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/i2c.h>
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/math64.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
@ -25,7 +21,6 @@
#include <linux/gpio/consumer.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
@ -33,11 +28,15 @@
#include <asm/unaligned.h>
/* bits in i2c status byte */
#define MPR_I2C_POWER BIT(6) /* device is powered */
#define MPR_I2C_BUSY BIT(5) /* device is busy */
#define MPR_I2C_MEMORY BIT(2) /* integrity test passed */
#define MPR_I2C_MATH BIT(0) /* internal math saturation */
#include "mprls0025pa.h"
/* bits in status byte */
#define MPR_ST_POWER BIT(6) /* device is powered */
#define MPR_ST_BUSY BIT(5) /* device is busy */
#define MPR_ST_MEMORY BIT(2) /* integrity test passed */
#define MPR_ST_MATH BIT(0) /* internal math saturation */
#define MPR_ST_ERR_FLAG (MPR_ST_BUSY | MPR_ST_MEMORY | MPR_ST_MATH)
/*
* support _RAW sysfs interface:
@ -70,60 +69,87 @@
* transfer function B: 2.5% to 22.5% of 2^24
* transfer function C: 20% to 80% of 2^24
*/
enum mpr_func_id {
MPR_FUNCTION_A,
MPR_FUNCTION_B,
MPR_FUNCTION_C,
};
struct mpr_func_spec {
u32 output_min;
u32 output_max;
};
static const struct mpr_func_spec mpr_func_spec[] = {
[MPR_FUNCTION_A] = {.output_min = 1677722, .output_max = 15099494},
[MPR_FUNCTION_B] = {.output_min = 419430, .output_max = 3774874},
[MPR_FUNCTION_C] = {.output_min = 3355443, .output_max = 13421773},
[MPR_FUNCTION_A] = { .output_min = 1677722, .output_max = 15099494 },
[MPR_FUNCTION_B] = { .output_min = 419430, .output_max = 3774874 },
[MPR_FUNCTION_C] = { .output_min = 3355443, .output_max = 13421773 },
};
struct mpr_chan {
s32 pres; /* pressure value */
s64 ts; /* timestamp */
enum mpr_variants {
MPR0001BA = 0x00, MPR01_6BA = 0x01, MPR02_5BA = 0x02, MPR0060MG = 0x03,
MPR0100MG = 0x04, MPR0160MG = 0x05, MPR0250MG = 0x06, MPR0400MG = 0x07,
MPR0600MG = 0x08, MPR0001BG = 0x09, MPR01_6BG = 0x0a, MPR02_5BG = 0x0b,
MPR0100KA = 0x0c, MPR0160KA = 0x0d, MPR0250KA = 0x0e, MPR0006KG = 0x0f,
MPR0010KG = 0x10, MPR0016KG = 0x11, MPR0025KG = 0x12, MPR0040KG = 0x13,
MPR0060KG = 0x14, MPR0100KG = 0x15, MPR0160KG = 0x16, MPR0250KG = 0x17,
MPR0015PA = 0x18, MPR0025PA = 0x19, MPR0030PA = 0x1a, MPR0001PG = 0x1b,
MPR0005PG = 0x1c, MPR0015PG = 0x1d, MPR0030PG = 0x1e, MPR0300YG = 0x1f,
MPR_VARIANTS_MAX
};
struct mpr_data {
struct i2c_client *client;
struct mutex lock; /*
* access to device during read
*/
u32 pmin; /* minimal pressure in pascal */
u32 pmax; /* maximal pressure in pascal */
enum mpr_func_id function; /* transfer function */
u32 outmin; /*
* minimal numerical range raw
* value from sensor
*/
u32 outmax; /*
* maximal numerical range raw
* value from sensor
*/
int scale; /* int part of scale */
int scale2; /* nano part of scale */
int offset; /* int part of offset */
int offset2; /* nano part of offset */
struct gpio_desc *gpiod_reset; /* reset */
int irq; /*
* end of conversion irq;
* used to distinguish between
* irq mode and reading in a
* loop until data is ready
*/
struct completion completion; /* handshake from irq to read */
struct mpr_chan chan; /*
* channel values for buffered
* mode
*/
static const char * const mpr_triplet_variants[MPR_VARIANTS_MAX] = {
[MPR0001BA] = "0001BA", [MPR01_6BA] = "01.6BA", [MPR02_5BA] = "02.5BA",
[MPR0060MG] = "0060MG", [MPR0100MG] = "0100MG", [MPR0160MG] = "0160MG",
[MPR0250MG] = "0250MG", [MPR0400MG] = "0400MG", [MPR0600MG] = "0600MG",
[MPR0001BG] = "0001BG", [MPR01_6BG] = "01.6BG", [MPR02_5BG] = "02.5BG",
[MPR0100KA] = "0100KA", [MPR0160KA] = "0160KA", [MPR0250KA] = "0250KA",
[MPR0006KG] = "0006KG", [MPR0010KG] = "0010KG", [MPR0016KG] = "0016KG",
[MPR0025KG] = "0025KG", [MPR0040KG] = "0040KG", [MPR0060KG] = "0060KG",
[MPR0100KG] = "0100KG", [MPR0160KG] = "0160KG", [MPR0250KG] = "0250KG",
[MPR0015PA] = "0015PA", [MPR0025PA] = "0025PA", [MPR0030PA] = "0030PA",
[MPR0001PG] = "0001PG", [MPR0005PG] = "0005PG", [MPR0015PG] = "0015PG",
[MPR0030PG] = "0030PG", [MPR0300YG] = "0300YG"
};
/**
* struct mpr_range_config - list of pressure ranges based on nomenclature
* @pmin: lowest pressure that can be measured
* @pmax: highest pressure that can be measured
*/
struct mpr_range_config {
const s32 pmin;
const s32 pmax;
};
/* All min max limits have been converted to pascals */
static const struct mpr_range_config mpr_range_config[MPR_VARIANTS_MAX] = {
[MPR0001BA] = { .pmin = 0, .pmax = 100000 },
[MPR01_6BA] = { .pmin = 0, .pmax = 160000 },
[MPR02_5BA] = { .pmin = 0, .pmax = 250000 },
[MPR0060MG] = { .pmin = 0, .pmax = 6000 },
[MPR0100MG] = { .pmin = 0, .pmax = 10000 },
[MPR0160MG] = { .pmin = 0, .pmax = 16000 },
[MPR0250MG] = { .pmin = 0, .pmax = 25000 },
[MPR0400MG] = { .pmin = 0, .pmax = 40000 },
[MPR0600MG] = { .pmin = 0, .pmax = 60000 },
[MPR0001BG] = { .pmin = 0, .pmax = 100000 },
[MPR01_6BG] = { .pmin = 0, .pmax = 160000 },
[MPR02_5BG] = { .pmin = 0, .pmax = 250000 },
[MPR0100KA] = { .pmin = 0, .pmax = 100000 },
[MPR0160KA] = { .pmin = 0, .pmax = 160000 },
[MPR0250KA] = { .pmin = 0, .pmax = 250000 },
[MPR0006KG] = { .pmin = 0, .pmax = 6000 },
[MPR0010KG] = { .pmin = 0, .pmax = 10000 },
[MPR0016KG] = { .pmin = 0, .pmax = 16000 },
[MPR0025KG] = { .pmin = 0, .pmax = 25000 },
[MPR0040KG] = { .pmin = 0, .pmax = 40000 },
[MPR0060KG] = { .pmin = 0, .pmax = 60000 },
[MPR0100KG] = { .pmin = 0, .pmax = 100000 },
[MPR0160KG] = { .pmin = 0, .pmax = 160000 },
[MPR0250KG] = { .pmin = 0, .pmax = 250000 },
[MPR0015PA] = { .pmin = 0, .pmax = 103421 },
[MPR0025PA] = { .pmin = 0, .pmax = 172369 },
[MPR0030PA] = { .pmin = 0, .pmax = 206843 },
[MPR0001PG] = { .pmin = 0, .pmax = 6895 },
[MPR0005PG] = { .pmin = 0, .pmax = 34474 },
[MPR0015PG] = { .pmin = 0, .pmax = 103421 },
[MPR0030PG] = { .pmin = 0, .pmax = 206843 },
[MPR0300YG] = { .pmin = 0, .pmax = 39997 }
};
static const struct iio_chan_spec mpr_channels[] = {
@ -153,11 +179,11 @@ static void mpr_reset(struct mpr_data *data)
}
/**
* mpr_read_pressure() - Read pressure value from sensor via I2C
* mpr_read_pressure() - Read pressure value from sensor
* @data: Pointer to private data struct.
* @press: Output value read from sensor.
*
* Reading from the sensor by sending and receiving I2C telegrams.
* Reading from the sensor by sending and receiving telegrams.
*
* If there is an end of conversion (EOC) interrupt registered the function
* waits for a maximum of one second for the interrupt.
@ -170,25 +196,17 @@ static void mpr_reset(struct mpr_data *data)
*/
static int mpr_read_pressure(struct mpr_data *data, s32 *press)
{
struct device *dev = &data->client->dev;
struct device *dev = data->dev;
int ret, i;
u8 wdata[] = {0xAA, 0x00, 0x00};
s32 status;
int nloops = 10;
u8 buf[4];
reinit_completion(&data->completion);
ret = i2c_master_send(data->client, wdata, sizeof(wdata));
ret = data->ops->write(data, MPR_CMD_SYNC, MPR_PKT_SYNC_LEN);
if (ret < 0) {
dev_err(dev, "error while writing ret: %d\n", ret);
return ret;
}
if (ret != sizeof(wdata)) {
dev_err(dev, "received size doesn't fit - ret: %d / %u\n", ret,
(u32)sizeof(wdata));
return -EIO;
}
if (data->irq > 0) {
ret = wait_for_completion_timeout(&data->completion, HZ);
@ -206,14 +224,14 @@ static int mpr_read_pressure(struct mpr_data *data, s32 *press)
* quite long
*/
usleep_range(5000, 10000);
status = i2c_smbus_read_byte(data->client);
if (status < 0) {
ret = data->ops->read(data, MPR_CMD_NOP, 1);
if (ret < 0) {
dev_err(dev,
"error while reading, status: %d\n",
status);
return status;
ret);
return ret;
}
if (!(status & MPR_I2C_BUSY))
if (!(data->buffer[0] & MPR_ST_ERR_FLAG))
break;
}
if (i == nloops) {
@ -222,29 +240,19 @@ static int mpr_read_pressure(struct mpr_data *data, s32 *press)
}
}
ret = i2c_master_recv(data->client, buf, sizeof(buf));
if (ret < 0) {
dev_err(dev, "error in i2c_master_recv ret: %d\n", ret);
ret = data->ops->read(data, MPR_CMD_NOP, MPR_PKT_NOP_LEN);
if (ret < 0)
return ret;
}
if (ret != sizeof(buf)) {
dev_err(dev, "received size doesn't fit - ret: %d / %u\n", ret,
(u32)sizeof(buf));
return -EIO;
}
if (buf[0] & MPR_I2C_BUSY) {
/*
* it should never be the case that status still indicates
* business
*/
dev_err(dev, "data still not ready: %08x\n", buf[0]);
if (data->buffer[0] & MPR_ST_ERR_FLAG) {
dev_err(data->dev,
"unexpected status byte %02x\n", data->buffer[0]);
return -ETIMEDOUT;
}
*press = get_unaligned_be24(&buf[1]);
*press = get_unaligned_be24(&data->buffer[1]);
dev_dbg(dev, "received: %*ph cnt: %d\n", ret, buf, *press);
dev_dbg(dev, "received: %*ph cnt: %d\n", ret, data->buffer, *press);
return 0;
}
@ -271,7 +279,7 @@ static irqreturn_t mpr_trigger_handler(int irq, void *p)
goto err;
iio_push_to_buffers_with_timestamp(indio_dev, &data->chan,
iio_get_time_ns(indio_dev));
iio_get_time_ns(indio_dev));
err:
mutex_unlock(&data->lock);
@ -316,25 +324,23 @@ static const struct iio_info mpr_info = {
.read_raw = &mpr_read_raw,
};
static int mpr_probe(struct i2c_client *client)
int mpr_common_probe(struct device *dev, const struct mpr_ops *ops, int irq)
{
int ret;
struct mpr_data *data;
struct iio_dev *indio_dev;
struct device *dev = &client->dev;
const char *triplet;
s64 scale, offset;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE))
return dev_err_probe(dev, -EOPNOTSUPP,
"I2C functionality not supported\n");
u32 func;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return dev_err_probe(dev, -ENOMEM, "couldn't get iio_dev\n");
return -ENOMEM;
data = iio_priv(indio_dev);
data->client = client;
data->irq = client->irq;
data->dev = dev;
data->ops = ops;
data->irq = irq;
mutex_init(&data->lock);
init_completion(&data->completion);
@ -348,103 +354,102 @@ static int mpr_probe(struct i2c_client *client)
ret = devm_regulator_get_enable(dev, "vdd");
if (ret)
return dev_err_probe(dev, ret,
"can't get and enable vdd supply\n");
"can't get and enable vdd supply\n");
if (dev_fwnode(dev)) {
ret = data->ops->init(data->dev);
if (ret)
return ret;
ret = device_property_read_u32(dev,
"honeywell,transfer-function", &func);
if (ret)
return dev_err_probe(dev, ret,
"honeywell,transfer-function could not be read\n");
data->function = func - 1;
if (data->function > MPR_FUNCTION_C)
return dev_err_probe(dev, -EINVAL,
"honeywell,transfer-function %d invalid\n",
data->function);
ret = device_property_read_string(dev, "honeywell,pressure-triplet",
&triplet);
if (ret) {
ret = device_property_read_u32(dev, "honeywell,pmin-pascal",
&data->pmin);
&data->pmin);
if (ret)
return dev_err_probe(dev, ret,
"honeywell,pmin-pascal could not be read\n");
"honeywell,pmin-pascal could not be read\n");
ret = device_property_read_u32(dev, "honeywell,pmax-pascal",
&data->pmax);
&data->pmax);
if (ret)
return dev_err_probe(dev, ret,
"honeywell,pmax-pascal could not be read\n");
ret = device_property_read_u32(dev,
"honeywell,transfer-function", &data->function);
if (ret)
return dev_err_probe(dev, ret,
"honeywell,transfer-function could not be read\n");
if (data->function > MPR_FUNCTION_C)
return dev_err_probe(dev, -EINVAL,
"honeywell,transfer-function %d invalid\n",
data->function);
"honeywell,pmax-pascal could not be read\n");
} else {
/* when loaded as i2c device we need to use default values */
dev_notice(dev, "firmware node not found; using defaults\n");
data->pmin = 0;
data->pmax = 172369; /* 25 psi */
data->function = MPR_FUNCTION_A;
ret = device_property_match_property_string(dev,
"honeywell,pressure-triplet",
mpr_triplet_variants,
MPR_VARIANTS_MAX);
if (ret < 0)
return dev_err_probe(dev, -EINVAL,
"honeywell,pressure-triplet is invalid\n");
data->pmin = mpr_range_config[ret].pmin;
data->pmax = mpr_range_config[ret].pmax;
}
if (data->pmin >= data->pmax)
return dev_err_probe(dev, -EINVAL,
"pressure limits are invalid\n");
data->outmin = mpr_func_spec[data->function].output_min;
data->outmax = mpr_func_spec[data->function].output_max;
/* use 64 bit calculation for preserving a reasonable precision */
scale = div_s64(((s64)(data->pmax - data->pmin)) * NANO,
data->outmax - data->outmin);
data->outmax - data->outmin);
data->scale = div_s64_rem(scale, NANO, &data->scale2);
/*
* multiply with NANO before dividing by scale and later divide by NANO
* again.
*/
offset = ((-1LL) * (s64)data->outmin) * NANO -
div_s64(div_s64((s64)data->pmin * NANO, scale), NANO);
div_s64(div_s64((s64)data->pmin * NANO, scale), NANO);
data->offset = div_s64_rem(offset, NANO, &data->offset2);
if (data->irq > 0) {
ret = devm_request_irq(dev, data->irq, mpr_eoc_handler,
IRQF_TRIGGER_RISING, client->name, data);
IRQF_TRIGGER_RISING,
dev_name(dev),
data);
if (ret)
return dev_err_probe(dev, ret,
"request irq %d failed\n", data->irq);
"request irq %d failed\n", data->irq);
}
data->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_HIGH);
GPIOD_OUT_HIGH);
if (IS_ERR(data->gpiod_reset))
return dev_err_probe(dev, PTR_ERR(data->gpiod_reset),
"request reset-gpio failed\n");
"request reset-gpio failed\n");
mpr_reset(data);
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
mpr_trigger_handler, NULL);
mpr_trigger_handler, NULL);
if (ret)
return dev_err_probe(dev, ret,
"iio triggered buffer setup failed\n");
"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");
"unable to register iio device\n");
return 0;
}
static const struct of_device_id mpr_matches[] = {
{ .compatible = "honeywell,mprls0025pa" },
{ }
};
MODULE_DEVICE_TABLE(of, mpr_matches);
static const struct i2c_device_id mpr_id[] = {
{ "mprls0025pa" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mpr_id);
static struct i2c_driver mpr_driver = {
.probe = mpr_probe,
.id_table = mpr_id,
.driver = {
.name = "mprls0025pa",
.of_match_table = mpr_matches,
},
};
module_i2c_driver(mpr_driver);
EXPORT_SYMBOL_NS(mpr_common_probe, IIO_HONEYWELL_MPRLS0025PA);
MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
MODULE_DESCRIPTION("Honeywell MPRLS0025PA I2C driver");
MODULE_DESCRIPTION("Honeywell MPR pressure sensor core driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,102 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* MPRLS0025PA - Honeywell MicroPressure pressure sensor series driver
*
* Copyright (c) Andreas Klinger <ak@it-klinger.de>
*
* Data sheet:
* https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf
*/
#ifndef _MPRLS0025PA_H
#define _MPRLS0025PA_H
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include <linux/iio/iio.h>
#define MPR_MEASUREMENT_RD_SIZE 4
#define MPR_CMD_NOP 0xf0
#define MPR_CMD_SYNC 0xaa
#define MPR_PKT_NOP_LEN MPR_MEASUREMENT_RD_SIZE
#define MPR_PKT_SYNC_LEN 3
struct device;
struct iio_chan_spec;
struct iio_dev;
struct mpr_data;
struct mpr_ops;
/**
* struct mpr_chan
* @pres: pressure value
* @ts: timestamp
*/
struct mpr_chan {
s32 pres;
s64 ts;
};
enum mpr_func_id {
MPR_FUNCTION_A,
MPR_FUNCTION_B,
MPR_FUNCTION_C,
};
/**
* struct mpr_data
* @dev: current device structure
* @ops: functions that implement the sensor reads/writes, bus init
* @lock: access to device during read
* @pmin: minimal pressure in pascal
* @pmax: maximal pressure in pascal
* @function: transfer function
* @outmin: minimum raw pressure in counts (based on transfer function)
* @outmax: maximum raw pressure in counts (based on transfer function)
* @scale: pressure scale
* @scale2: pressure scale, decimal number
* @offset: pressure offset
* @offset2: pressure offset, decimal number
* @gpiod_reset: reset
* @irq: end of conversion irq. used to distinguish between irq mode and
* reading in a loop until data is ready
* @completion: handshake from irq to read
* @chan: channel values for buffered mode
* @buffer: raw conversion data
*/
struct mpr_data {
struct device *dev;
const struct mpr_ops *ops;
struct mutex lock;
u32 pmin;
u32 pmax;
enum mpr_func_id function;
u32 outmin;
u32 outmax;
int scale;
int scale2;
int offset;
int offset2;
struct gpio_desc *gpiod_reset;
int irq;
struct completion completion;
struct mpr_chan chan;
u8 buffer[MPR_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN);
};
struct mpr_ops {
int (*init)(struct device *dev);
int (*read)(struct mpr_data *data, const u8 cmd, const u8 cnt);
int (*write)(struct mpr_data *data, const u8 cmd, const u8 cnt);
};
int mpr_common_probe(struct device *dev, const struct mpr_ops *ops, int irq);
#endif

View File

@ -0,0 +1,100 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MPRLS0025PA - Honeywell MicroPressure pressure sensor series driver
*
* Copyright (c) Andreas Klinger <ak@it-klinger.de>
*
* Data sheet:
* https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/types.h>
#include "mprls0025pa.h"
static int mpr_i2c_init(struct device *unused)
{
return 0;
}
static int mpr_i2c_read(struct mpr_data *data, const u8 unused, const u8 cnt)
{
int ret;
struct i2c_client *client = to_i2c_client(data->dev);
if (cnt > MPR_MEASUREMENT_RD_SIZE)
return -EOVERFLOW;
memset(data->buffer, 0, MPR_MEASUREMENT_RD_SIZE);
ret = i2c_master_recv(client, data->buffer, cnt);
if (ret < 0)
return ret;
else if (ret != cnt)
return -EIO;
return 0;
}
static int mpr_i2c_write(struct mpr_data *data, const u8 cmd, const u8 unused)
{
int ret;
struct i2c_client *client = to_i2c_client(data->dev);
u8 wdata[MPR_PKT_SYNC_LEN];
memset(wdata, 0, sizeof(wdata));
wdata[0] = cmd;
ret = i2c_master_send(client, wdata, MPR_PKT_SYNC_LEN);
if (ret < 0)
return ret;
else if (ret != MPR_PKT_SYNC_LEN)
return -EIO;
return 0;
}
static const struct mpr_ops mpr_i2c_ops = {
.init = mpr_i2c_init,
.read = mpr_i2c_read,
.write = mpr_i2c_write,
};
static int mpr_i2c_probe(struct i2c_client *client)
{
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE))
return -EOPNOTSUPP;
return mpr_common_probe(&client->dev, &mpr_i2c_ops, client->irq);
}
static const struct of_device_id mpr_i2c_match[] = {
{ .compatible = "honeywell,mprls0025pa" },
{}
};
MODULE_DEVICE_TABLE(of, mpr_i2c_match);
static const struct i2c_device_id mpr_i2c_id[] = {
{ "mprls0025pa" },
{}
};
MODULE_DEVICE_TABLE(i2c, mpr_i2c_id);
static struct i2c_driver mpr_i2c_driver = {
.probe = mpr_i2c_probe,
.id_table = mpr_i2c_id,
.driver = {
.name = "mprls0025pa",
.of_match_table = mpr_i2c_match,
},
};
module_i2c_driver(mpr_i2c_driver);
MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
MODULE_DESCRIPTION("Honeywell MPR pressure sensor i2c driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(IIO_HONEYWELL_MPRLS0025PA);

View File

@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* MPRLS0025PA - Honeywell MicroPressure MPR series SPI sensor driver
*
* Copyright (c) 2024 Petre Rodan <petre.rodan@subdimension.ro>
*
* Data sheet:
* https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include "mprls0025pa.h"
struct mpr_spi_buf {
u8 tx[MPR_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN);
};
static int mpr_spi_init(struct device *dev)
{
struct spi_device *spi = to_spi_device(dev);
struct mpr_spi_buf *buf;
buf = devm_kzalloc(dev, sizeof(*buf), GFP_KERNEL);
if (!buf)
return -ENOMEM;
spi_set_drvdata(spi, buf);
return 0;
}
static int mpr_spi_xfer(struct mpr_data *data, const u8 cmd, const u8 pkt_len)
{
struct spi_device *spi = to_spi_device(data->dev);
struct mpr_spi_buf *buf = spi_get_drvdata(spi);
struct spi_transfer xfer;
if (pkt_len > MPR_MEASUREMENT_RD_SIZE)
return -EOVERFLOW;
buf->tx[0] = cmd;
xfer.tx_buf = buf->tx;
xfer.rx_buf = data->buffer;
xfer.len = pkt_len;
return spi_sync_transfer(spi, &xfer, 1);
}
static const struct mpr_ops mpr_spi_ops = {
.init = mpr_spi_init,
.read = mpr_spi_xfer,
.write = mpr_spi_xfer,
};
static int mpr_spi_probe(struct spi_device *spi)
{
return mpr_common_probe(&spi->dev, &mpr_spi_ops, spi->irq);
}
static const struct of_device_id mpr_spi_match[] = {
{ .compatible = "honeywell,mprls0025pa" },
{}
};
MODULE_DEVICE_TABLE(of, mpr_spi_match);
static const struct spi_device_id mpr_spi_id[] = {
{ "mprls0025pa" },
{}
};
MODULE_DEVICE_TABLE(spi, mpr_spi_id);
static struct spi_driver mpr_spi_driver = {
.driver = {
.name = "mprls0025pa",
.of_match_table = mpr_spi_match,
},
.probe = mpr_spi_probe,
.id_table = mpr_spi_id,
};
module_spi_driver(mpr_spi_driver);
MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
MODULE_DESCRIPTION("Honeywell MPR pressure sensor spi driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(IIO_HONEYWELL_MPRLS0025PA);

View File

@ -7,7 +7,6 @@
* Denis Ciocca <denis.ciocca@st.com>
*/
#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
@ -55,13 +54,11 @@ static const struct of_device_id st_press_of_match[] = {
};
MODULE_DEVICE_TABLE(of, st_press_of_match);
#ifdef CONFIG_ACPI
static const struct acpi_device_id st_press_acpi_match[] = {
{"SNO9210", LPS22HB},
{ },
};
MODULE_DEVICE_TABLE(acpi, st_press_acpi_match);
#endif
static const struct i2c_device_id st_press_id_table[] = {
{ LPS001WP_PRESS_DEV_NAME, LPS001WP },
@ -114,7 +111,7 @@ static struct i2c_driver st_press_driver = {
.driver = {
.name = "st-press-i2c",
.of_match_table = st_press_of_match,
.acpi_match_table = ACPI_PTR(st_press_acpi_match),
.acpi_match_table = st_press_acpi_match,
},
.probe = st_press_i2c_probe,
.id_table = st_press_id_table,

View File

@ -337,28 +337,19 @@ static int sx9310_read_raw(struct iio_dev *indio_dev,
int *val2, long mask)
{
struct sx_common_data *data = iio_priv(indio_dev);
int ret;
if (chan->type != IIO_PROXIMITY)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = sx_common_read_proximity(data, chan, val);
iio_device_release_direct_mode(indio_dev);
return ret;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev)
return sx_common_read_proximity(data, chan, val);
unreachable();
case IIO_CHAN_INFO_HARDWAREGAIN:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = sx9310_read_gain(data, chan, val);
iio_device_release_direct_mode(indio_dev);
return ret;
iio_device_claim_direct_scoped(return -EBUSY, indio_dev)
return sx9310_read_gain(data, chan, val);
unreachable();
case IIO_CHAN_INFO_SAMP_FREQ:
return sx9310_read_samp_freq(data, val, val2);
default:
@ -546,12 +537,10 @@ static int sx9310_write_thresh(struct sx_common_data *data,
return -EINVAL;
regval = FIELD_PREP(SX9310_REG_PROX_CTRL8_9_PTHRESH_MASK, regval);
mutex_lock(&data->mutex);
ret = regmap_update_bits(data->regmap, reg,
SX9310_REG_PROX_CTRL8_9_PTHRESH_MASK, regval);
mutex_unlock(&data->mutex);
return ret;
guard(mutex)(&data->mutex);
return regmap_update_bits(data->regmap, reg,
SX9310_REG_PROX_CTRL8_9_PTHRESH_MASK, regval);
}
static int sx9310_write_hysteresis(struct sx_common_data *data,
@ -576,17 +565,14 @@ static int sx9310_write_hysteresis(struct sx_common_data *data,
return -EINVAL;
hyst = FIELD_PREP(SX9310_REG_PROX_CTRL10_HYST_MASK, hyst);
mutex_lock(&data->mutex);
ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10,
SX9310_REG_PROX_CTRL10_HYST_MASK, hyst);
mutex_unlock(&data->mutex);
return ret;
guard(mutex)(&data->mutex);
return regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10,
SX9310_REG_PROX_CTRL10_HYST_MASK, hyst);
}
static int sx9310_write_far_debounce(struct sx_common_data *data, int val)
{
int ret;
unsigned int regval;
if (val > 0)
@ -596,18 +582,14 @@ static int sx9310_write_far_debounce(struct sx_common_data *data, int val)
regval = FIELD_PREP(SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK, val);
mutex_lock(&data->mutex);
ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10,
SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK,
regval);
mutex_unlock(&data->mutex);
return ret;
guard(mutex)(&data->mutex);
return regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10,
SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK,
regval);
}
static int sx9310_write_close_debounce(struct sx_common_data *data, int val)
{
int ret;
unsigned int regval;
if (val > 0)
@ -617,13 +599,10 @@ static int sx9310_write_close_debounce(struct sx_common_data *data, int val)
regval = FIELD_PREP(SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK, val);
mutex_lock(&data->mutex);
ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10,
SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK,
regval);
mutex_unlock(&data->mutex);
return ret;
guard(mutex)(&data->mutex);
return regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL10,
SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK,
regval);
}
static int sx9310_write_event_val(struct iio_dev *indio_dev,
@ -658,7 +637,7 @@ static int sx9310_write_event_val(struct iio_dev *indio_dev,
static int sx9310_set_samp_freq(struct sx_common_data *data, int val, int val2)
{
int i, ret;
int i;
for (i = 0; i < ARRAY_SIZE(sx9310_samp_freq_table); i++)
if (val == sx9310_samp_freq_table[i].val &&
@ -668,23 +647,17 @@ static int sx9310_set_samp_freq(struct sx_common_data *data, int val, int val2)
if (i == ARRAY_SIZE(sx9310_samp_freq_table))
return -EINVAL;
mutex_lock(&data->mutex);
ret = regmap_update_bits(
guard(mutex)(&data->mutex);
return regmap_update_bits(
data->regmap, SX9310_REG_PROX_CTRL0,
SX9310_REG_PROX_CTRL0_SCANPERIOD_MASK,
FIELD_PREP(SX9310_REG_PROX_CTRL0_SCANPERIOD_MASK, i));
mutex_unlock(&data->mutex);
return ret;
}
static int sx9310_write_gain(struct sx_common_data *data,
const struct iio_chan_spec *chan, int val)
{
unsigned int gain, mask;
int ret;
gain = ilog2(val);
@ -703,12 +676,9 @@ static int sx9310_write_gain(struct sx_common_data *data,
return -EINVAL;
}
mutex_lock(&data->mutex);
ret = regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL3, mask,
gain);
mutex_unlock(&data->mutex);
return ret;
guard(mutex)(&data->mutex);
return regmap_update_bits(data->regmap, SX9310_REG_PROX_CTRL3, mask,
gain);
}
static int sx9310_write_raw(struct iio_dev *indio_dev,
@ -969,22 +939,18 @@ static int sx9310_suspend(struct device *dev)
disable_irq_nosync(data->client->irq);
mutex_lock(&data->mutex);
guard(mutex)(&data->mutex);
ret = regmap_read(data->regmap, SX9310_REG_PROX_CTRL0,
&data->suspend_ctrl);
if (ret)
goto out;
return ret;
ctrl0 = data->suspend_ctrl & ~SX9310_REG_PROX_CTRL0_SENSOREN_MASK;
ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0, ctrl0);
if (ret)
goto out;
return ret;
ret = regmap_write(data->regmap, SX9310_REG_PAUSE, 0);
out:
mutex_unlock(&data->mutex);
return ret;
return regmap_write(data->regmap, SX9310_REG_PAUSE, 0);
}
static int sx9310_resume(struct device *dev)
@ -992,18 +958,16 @@ static int sx9310_resume(struct device *dev)
struct sx_common_data *data = iio_priv(dev_get_drvdata(dev));
int ret;
mutex_lock(&data->mutex);
ret = regmap_write(data->regmap, SX9310_REG_PAUSE, 1);
if (ret)
goto out;
scoped_guard(mutex, &data->mutex) {
ret = regmap_write(data->regmap, SX9310_REG_PAUSE, 1);
if (ret)
return ret;
ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0,
data->suspend_ctrl);
out:
mutex_unlock(&data->mutex);
if (ret)
return ret;
ret = regmap_write(data->regmap, SX9310_REG_PROX_CTRL0,
data->suspend_ctrl);
if (ret)
return ret;
}
enable_irq(data->client->irq);
return 0;

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