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

New device support
 ------------------
 
 adi,hmc425a
   * Add support for ADRF5740 attenuators.  Minor changes to driver needed
     alongside new IDs.
 aosong,ags02ma
   * New driver for this volatile organic compounds sensor.
 bosch,bmp280
   * Add BMP390 (small amount of refactoring + ID)
 bosch,bmi323
   * New driver to support the BMI323 6-axis IMU.
 honeywell,hsc030pa
   * New driver supporting a huge number of SSC and HSC series pressure and
     temperature sensors.
 isil,isl76682
   * New driver for this simple Ambient Light sensor.
 liteon,ltr390
   * New driver for this ambient and ultraviolet light sensor.
 maxim,max34408
   * New driver to support the MAX34408 and MAX34409 current monitoring ADCs.
 melexis,mlx90635
   * New driver for this Infrared contactless temperature sensor.
 mirochip,mcp9600
   * New driver for this thermocouple EMF convertor.
 ti,hdc3020
   * New driver for this integrated relative humidity and temperature
     sensor.
 vishay,veml6075
   * New driver for this UVA and UVB light sensor.
 
 General features
 ----------------
 
 Device properties
   * Add fwnode_property_match_property_string() helper to allow matching
     single value property against an array of predefined strings.
   * Use fwnode_property_string_array_count() inside
     fwnode_property_match_string() instead of open coding the same.
 checkpatch.pl
   * Add exclusion of __aligned() from a warning reducing false positives
     on IIO drivers (and hopefully beyond)
 
 IIO Features
 ------------
 
 core
   * New light channel modifiers for UVA and UVB.
   * Add IIO_CHAN_INFO_TROUGH as counterpart to IIO_CHAN_INFO_PEAK so that
     we can support device that keep running track of the lowest value they
     have seen in similar fashion to the existing peak tracking.
 adi,adis library
   * Use spi cs inactive delay even when a burst reading is performed.
     As it's now used every time, can centralize the handling in the SPI
     setup code in the driver.
 adi,ad2s1210
   * Support for fixed-mode to this resolver driver where the A0 and A1
     pins are hard wired to config mode in which case position and config
     must be read from appropriate config registers.
   * Support reset GPIO if present.
 adi,ad5791
   * Allow configuration of presence of external amplifier in DT binding.
 adi,adis16400
   * Add spi-cs-inactive-delay-ns to bindings to allow it to be tweaked
     if default delays are not quite enough for a specific board.
 adi,adis16475
   * Add spi-cs-inactive-delay-ns to bindings to allow it to be tweaked
     if default delays are not quite enough for a specific board.
 bosch,bmp280
   * Enable multiple chip IDs per family of devices.
 rohm,bu27008
   * Add an illuminance channel calculated from RGB and IR data.
 
 Cleanup
 -------
 
 Minor white space, typos and tidy up not explicitly called out.
 
 Core
   * Check that the available_scan_masks array passed to the IIO core
     by a driver is sensible by ensuring the entries are ordered so the
     minimum number of channels is enabled in the earlier entries (as they
     will be selected if sufficient for the requested channels).
   * Document that the available_scan_masks infrastructure doesn't currently
     handle masks that don't fit in a long int.
   * Improve intensity documentation to reflect that there is no expectation
     of sensible units (it's dependent on a frequency sensitivity curve)
 Various
   * Use new device_property_match_property_string() to replace open coded
     versions of the same thing.
   * Fix a few MAINTAINERS filenames.
   * i2c_get_match_data() and spi_get_device_match_data() pushed into
     more drivers reducing boilerplate handling.
   * Some unnecessary headers removed.
   * ACPI_PTR() removals. It's rarely worth using this.
 adi,ad7091r (early part of a series adding device support - useful in
   their own right)
   * Pass iio_dev directly an event handler rather than relying
     on broken use of dev_get_drvdata() as drvdata is never set in this driver.
   * Make sure alert is turned on.
 adi,ad9467 (general driver fixing up as precursor to iio-backend proposal
   which is under review for 6.9)
   * Fix reset gpio handling to match expected polarity.
   * Always handle error codes from spi_writes.
   * Add a driver instance local mutex to avoid some races.
   * Fix scale setting to align with available scale values.
   * Split array of chip_info structures up into named individual elements.
   * Convert to regmap.
 honeywell,mprls0025pa
   * Drop now unnecessary type references in DT binding for properties in
     pascals.
 invensense,mpu6050
   * Don't eat a potentially useful return value from regmap_bulk_write()
 invensense,icm42600
   * Use max macro to improve code readability and save a few lines.
 liteon,ltrf216a
   * Improve prevision of light intensity.
 microchip,mcp3911
   * Use cleanup.h magic.
 qcom,spmi*
   * Fix wrong descriptions of SPMI reg fields in bindings.
 
 Other
 ----
 
 mailmap
   * Update for Matt Ranostay
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAmWDAIIRHGppYzIzQGtl
 cm5lbC5vcmcACgkQVIU0mcT0Foi37g//f9PEMOdLToWM6GoNidJmX+V8QTEKkDF5
 omUTSjMTAa7aXMt5dgoevrRrg4tIZs5TfheEfHg2io+Dg3aGQxxPpgyZnVQSC4MN
 bJwd+dVLjvi5sr7dEnwtJRfYCnt9LLI900xCg6b6YUFgJxY02HH6REPZVqU7M7Jl
 ANTAycSrYdLJlMReHsuDZT/kMJaWo4M0azSBxWtICTtLmlfii8dWTfSex4xzk1Pf
 nu13+ivz3FHpQOtLAHdtHf5CCDwcc+9DQw5O8Ihr8RQY9JEnwolaNgc0Fsmotfww
 sJ8bhFy8Zyc7bYwskGl1vEX/lJdk+drDt1LmbxxaQPKISpUx3A4ITVvwiG60KGI3
 gBwJmirxG9CFCa3MY4V608dvOkc58IyOX/GFq9l+5MlZvMdRqeRCFPOs8UT0G3zi
 Kd422wjVpAwjV70wOb9NjnkVxQD69FaMiSA1QBCp8PYams/okwqT3ZiBqdqahwjb
 77vx3gKL+Pa8hJJynA9eP8uRl6KpTxmallSuHsK4c1qdj+NaJfHPbSu0pmPALbE0
 N5fcv+vA0WS/J2fWVoJuqPQ/+yYk6WddjpcOn0imlW6Uo4CP6CQISnbAuNWO6TTe
 c8YAQiyoHMOk8slyU+++sPH6qS7hcyoPdBVJgCPwEN12PaZxnM7WaTN2WJX+TFXP
 KARwsZQ+rUE=
 =jH+G
 -----END PGP SIGNATURE-----

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

Jonathan writes:

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

New device support
------------------

adi,hmc425a
  * Add support for ADRF5740 attenuators.  Minor changes to driver needed
    alongside new IDs.
aosong,ags02ma
  * New driver for this volatile organic compounds sensor.
bosch,bmp280
  * Add BMP390 (small amount of refactoring + ID)
bosch,bmi323
  * New driver to support the BMI323 6-axis IMU.
honeywell,hsc030pa
  * New driver supporting a huge number of SSC and HSC series pressure and
    temperature sensors.
isil,isl76682
  * New driver for this simple Ambient Light sensor.
liteon,ltr390
  * New driver for this ambient and ultraviolet light sensor.
maxim,max34408
  * New driver to support the MAX34408 and MAX34409 current monitoring ADCs.
melexis,mlx90635
  * New driver for this Infrared contactless temperature sensor.
mirochip,mcp9600
  * New driver for this thermocouple EMF convertor.
ti,hdc3020
  * New driver for this integrated relative humidity and temperature
    sensor.
vishay,veml6075
  * New driver for this UVA and UVB light sensor.

General features
----------------

Device properties
  * Add fwnode_property_match_property_string() helper to allow matching
    single value property against an array of predefined strings.
  * Use fwnode_property_string_array_count() inside
    fwnode_property_match_string() instead of open coding the same.
checkpatch.pl
  * Add exclusion of __aligned() from a warning reducing false positives
    on IIO drivers (and hopefully beyond)

IIO Features
------------

core
  * New light channel modifiers for UVA and UVB.
  * Add IIO_CHAN_INFO_TROUGH as counterpart to IIO_CHAN_INFO_PEAK so that
    we can support device that keep running track of the lowest value they
    have seen in similar fashion to the existing peak tracking.
adi,adis library
  * Use spi cs inactive delay even when a burst reading is performed.
    As it's now used every time, can centralize the handling in the SPI
    setup code in the driver.
adi,ad2s1210
  * Support for fixed-mode to this resolver driver where the A0 and A1
    pins are hard wired to config mode in which case position and config
    must be read from appropriate config registers.
  * Support reset GPIO if present.
adi,ad5791
  * Allow configuration of presence of external amplifier in DT binding.
adi,adis16400
  * Add spi-cs-inactive-delay-ns to bindings to allow it to be tweaked
    if default delays are not quite enough for a specific board.
adi,adis16475
  * Add spi-cs-inactive-delay-ns to bindings to allow it to be tweaked
    if default delays are not quite enough for a specific board.
bosch,bmp280
  * Enable multiple chip IDs per family of devices.
rohm,bu27008
  * Add an illuminance channel calculated from RGB and IR data.

Cleanup
-------

Minor white space, typos and tidy up not explicitly called out.

Core
  * Check that the available_scan_masks array passed to the IIO core
    by a driver is sensible by ensuring the entries are ordered so the
    minimum number of channels is enabled in the earlier entries (as they
    will be selected if sufficient for the requested channels).
  * Document that the available_scan_masks infrastructure doesn't currently
    handle masks that don't fit in a long int.
  * Improve intensity documentation to reflect that there is no expectation
    of sensible units (it's dependent on a frequency sensitivity curve)
Various
  * Use new device_property_match_property_string() to replace open coded
    versions of the same thing.
  * Fix a few MAINTAINERS filenames.
  * i2c_get_match_data() and spi_get_device_match_data() pushed into
    more drivers reducing boilerplate handling.
  * Some unnecessary headers removed.
  * ACPI_PTR() removals. It's rarely worth using this.
adi,ad7091r (early part of a series adding device support - useful in
  their own right)
  * Pass iio_dev directly an event handler rather than relying
    on broken use of dev_get_drvdata() as drvdata is never set in this driver.
  * Make sure alert is turned on.
adi,ad9467 (general driver fixing up as precursor to iio-backend proposal
  which is under review for 6.9)
  * Fix reset gpio handling to match expected polarity.
  * Always handle error codes from spi_writes.
  * Add a driver instance local mutex to avoid some races.
  * Fix scale setting to align with available scale values.
  * Split array of chip_info structures up into named individual elements.
  * Convert to regmap.
honeywell,mprls0025pa
  * Drop now unnecessary type references in DT binding for properties in
    pascals.
invensense,mpu6050
  * Don't eat a potentially useful return value from regmap_bulk_write()
invensense,icm42600
  * Use max macro to improve code readability and save a few lines.
liteon,ltrf216a
  * Improve prevision of light intensity.
microchip,mcp3911
  * Use cleanup.h magic.
qcom,spmi*
  * Fix wrong descriptions of SPMI reg fields in bindings.

Other
----

mailmap
  * Update for Matt Ranostay

* tag 'iio-for-6.8a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (83 commits)
  iio: adc: ad7091r: Align arguments to function call parenthesis
  iio: adc: ad7091r: Set alert bit in config register
  iio: adc: ad7091r: Pass iio_dev to event handler
  scripts: checkpatch: Add __aligned to the list of attribute notes
  iio: chemical: add support for Aosong AGS02MA
  dt-bindings: iio: chemical: add aosong,ags02ma
  dt-bindings: vendor-prefixes: add aosong
  iio: accel: bmi088: update comments and Kconfig
  dt-bindings: iio: humidity: Add TI HDC302x support
  iio: humidity: Add driver for ti HDC302x humidity sensors
  iio: ABI: document temperature and humidity peak/trough raw attributes
  iio: core: introduce trough info element for minimum values
  iio: light: driver for Lite-On ltr390
  dt-bindings: iio: light: add ltr390
  iio: light: isl76682: remove unreachable code
  iio: pressure: driver for Honeywell HSC/SSC series
  dt-bindings: iio: pressure: add honeywell,hsc030
  doc: iio: Document intensity scale as poorly defined
  dt-bindings: iio: temperature: add MLX90635 device
  iio: temperature: mlx90635 MLX90635 IR Temperature sensor
  ...
This commit is contained in:
Greg Kroah-Hartman 2023-12-20 17:13:16 +01:00
commit 7050abeb8f
89 changed files with 8175 additions and 408 deletions

View File

@ -384,9 +384,10 @@ Matthias Fuchs <socketcan@esd.eu> <matthias.fuchs@esd.eu>
Matthieu Baerts <matttbe@kernel.org> <matthieu.baerts@tessares.net>
Matthieu CASTET <castet.matthieu@free.fr>
Matti Vaittinen <mazziesaccount@gmail.com> <matti.vaittinen@fi.rohmeurope.com>
Matt Ranostay <matt.ranostay@konsulko.com> <matt@ranostay.consulting>
Matt Ranostay <mranostay@gmail.com> Matthew Ranostay <mranostay@embeddedalley.com>
Matt Ranostay <mranostay@gmail.com> <matt.ranostay@intel.com>
Matt Ranostay <matt@ranostay.sg> <matt.ranostay@konsulko.com>
Matt Ranostay <matt@ranostay.sg> <matt@ranostay.consulting>
Matt Ranostay <matt@ranostay.sg> Matthew Ranostay <mranostay@embeddedalley.com>
Matt Ranostay <matt@ranostay.sg> <matt.ranostay@intel.com>
Matt Redfearn <matt.redfearn@mips.com> <matt.redfearn@imgtec.com>
Maulik Shah <quic_mkshah@quicinc.com> <mkshah@codeaurora.org>
Mauro Carvalho Chehab <mchehab@kernel.org> <maurochehab@gmail.com>

View File

@ -362,10 +362,21 @@ Description:
What: /sys/bus/iio/devices/iio:deviceX/in_accel_x_peak_raw
What: /sys/bus/iio/devices/iio:deviceX/in_accel_y_peak_raw
What: /sys/bus/iio/devices/iio:deviceX/in_accel_z_peak_raw
What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_peak_raw
What: /sys/bus/iio/devices/iio:deviceX/in_temp_peak_raw
KernelVersion: 2.6.36
Contact: linux-iio@vger.kernel.org
Description:
Highest value since some reset condition. These
Highest value since some reset condition. These
attributes allow access to this and are otherwise
the direct equivalent of the <type>Y[_name]_raw attributes.
What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_trough_raw
What: /sys/bus/iio/devices/iio:deviceX/in_temp_trough_raw
KernelVersion: 6.7
Contact: linux-iio@vger.kernel.org
Description:
Lowest value since some reset condition. These
attributes allow access to this and are otherwise
the direct equivalent of the <type>Y[_name]_raw attributes.
@ -618,7 +629,9 @@ KernelVersion: 2.6.35
Contact: linux-iio@vger.kernel.org
Description:
If a discrete set of scale values is available, they
are listed in this attribute.
are listed in this attribute. Unlike illumination,
multiplying intensity by intensity_scale does not
yield value with any standardized unit.
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_hardwaregain
What: /sys/bus/iio/devices/iio:deviceX/in_intensity_hardwaregain
@ -1574,6 +1587,8 @@ What: /sys/.../iio:deviceX/in_intensityY_raw
What: /sys/.../iio:deviceX/in_intensityY_ir_raw
What: /sys/.../iio:deviceX/in_intensityY_both_raw
What: /sys/.../iio:deviceX/in_intensityY_uv_raw
What: /sys/.../iio:deviceX/in_intensityY_uva_raw
What: /sys/.../iio:deviceX/in_intensityY_uvb_raw
What: /sys/.../iio:deviceX/in_intensityY_duv_raw
KernelVersion: 3.4
Contact: linux-iio@vger.kernel.org
@ -1582,8 +1597,9 @@ Description:
that measurements contain visible and infrared light
components or just infrared light, respectively. Modifier
uv indicates that measurements contain ultraviolet light
components. Modifier duv indicates that measurements
contain deep ultraviolet light components.
components. Modifiers uva, uvb and duv indicate that
measurements contain A, B or deep (C) ultraviolet light
components respectively.
What: /sys/.../iio:deviceX/in_uvindex_input
KernelVersion: 4.6
@ -2254,3 +2270,21 @@ Description:
If a label is defined for this event add that to the event
specific attributes. This is useful for userspace to be able to
better identify an individual event.
What: /sys/.../events/in_accel_gesture_tap_wait_timeout
KernelVersion: 6.7
Contact: linux-iio@vger.kernel.org
Description:
Enable tap gesture confirmation with timeout.
What: /sys/.../events/in_accel_gesture_tap_wait_dur
KernelVersion: 6.7
Contact: linux-iio@vger.kernel.org
Description:
Timeout value in seconds for tap gesture confirmation.
What: /sys/.../events/in_accel_gesture_tap_wait_dur_available
KernelVersion: 6.7
Contact: linux-iio@vger.kernel.org
Description:
List of available timeout value for tap gesture confirmation.

View File

@ -0,0 +1,139 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/adc/maxim,max34408.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Maxim MAX34408/MAX34409 current monitors with overcurrent control
maintainers:
- Ivan Mikhaylov <fr0st61te@gmail.com>
description: |
The MAX34408/MAX34409 are two- and four-channel current monitors that are
configured and monitored with a standard I2C/SMBus serial interface. Each
unidirectional current sensor offers precision high-side operation with a
low full-scale sense voltage. The devices automatically sequence through
two or four channels and collect the current-sense samples and average them
to reduce the effect of impulse noise. The raw ADC samples are compared to
user-programmable digital thresholds to indicate overcurrent conditions.
Overcurrent conditions trigger a hardware output to provide an immediate
indication to shut down any necessary external circuitry.
Specifications about the devices can be found at:
https://www.analog.com/media/en/technical-documentation/data-sheets/MAX34408-MAX34409.pdf
properties:
compatible:
enum:
- maxim,max34408
- maxim,max34409
"#address-cells":
const: 1
"#size-cells":
const: 0
reg:
maxItems: 1
interrupts:
maxItems: 1
powerdown-gpios:
description:
Shutdown Output. Open-drain output. This output transitions to high impedance
when any of the digital comparator thresholds are exceeded as long as the ENA
pin is high.
maxItems: 1
powerdown-status-gpios:
description:
SHTDN Enable Input. CMOS digital input. Connect to GND to clear the latch and
unconditionally deassert (force low) the SHTDN output and reset the shutdown
delay. Connect to VDD to enable normal latch operation of the SHTDN output.
maxItems: 1
vdd-supply: true
patternProperties:
"^channel@[0-3]$":
$ref: adc.yaml
type: object
description:
Represents the internal channels of the ADC.
properties:
reg:
items:
- minimum: 0
maximum: 3
maxim,rsense-val-micro-ohms:
description:
Adjust the Rsense value to monitor higher or lower current levels for
input.
enum: [250, 500, 1000, 5000, 10000, 50000, 100000, 200000, 500000]
default: 1000
required:
- reg
- maxim,rsense-val-micro-ohms
unevaluatedProperties: false
required:
- compatible
- reg
allOf:
- if:
properties:
compatible:
contains:
const: maxim,max34408
then:
patternProperties:
"^channel@[2-3]$": false
"^channel@[0-1]$":
properties:
reg:
maximum: 1
else:
patternProperties:
"^channel@[0-3]$":
properties:
reg:
maximum: 3
additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
adc@1e {
compatible = "maxim,max34409";
reg = <0x1e>;
powerdown-gpios = <&gpio0 1 GPIO_ACTIVE_LOW>;
powerdown-status-gpios = <&gpio0 2 GPIO_ACTIVE_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0x0>;
maxim,rsense-val-micro-ohms = <5000>;
};
channel@1 {
reg = <0x1>;
maxim,rsense-val-micro-ohms = <10000>;
};
};
};

View File

@ -25,7 +25,7 @@ properties:
- const: qcom,spmi-iadc
reg:
description: IADC base address and length in the SPMI PMIC register map
description: IADC base address in the SPMI PMIC register map
maxItems: 1
qcom,external-resistor-micro-ohms:
@ -50,15 +50,17 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
spmi {
pmic {
#address-cells = <1>;
#size-cells = <0>;
pmic_iadc: adc@3600 {
adc@3600 {
compatible = "qcom,pm8941-iadc", "qcom,spmi-iadc";
reg = <0x3600>;
interrupts = <0x0 0x36 0x0 IRQ_TYPE_EDGE_RISING>;
qcom,external-resistor-micro-ohms = <10000>;
#io-channel-cells = <1>;
#io-channel-cells = <1>;
};
};
...

View File

@ -43,9 +43,9 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
pmic_rradc: adc@4500 {
adc@4500 {
compatible = "qcom,pmi8998-rradc";
reg = <0x4500>;
#io-channel-cells = <1>;
#io-channel-cells = <1>;
};
};

View File

@ -236,11 +236,11 @@ additionalProperties: false
examples:
- |
spmi {
pmic {
#address-cells = <1>;
#size-cells = <0>;
/* VADC node */
pmic_vadc: adc@3100 {
adc@3100 {
compatible = "qcom,spmi-vadc";
reg = <0x3100>;
interrupts = <0x0 0x31 0x0 0x1>;
@ -281,9 +281,10 @@ examples:
#include <dt-bindings/iio/qcom,spmi-adc7-pm8350.h>
#include <dt-bindings/interrupt-controller/irq.h>
spmi {
pmic {
#address-cells = <1>;
#size-cells = <0>;
adc@3100 {
reg = <0x3100>;
compatible = "qcom,spmi-adc7";

View File

@ -67,19 +67,4 @@ required:
- compatible
- "#io-channel-cells"
examples:
- |
#include <dt-bindings/clock/mt8183-clk.h>
pmic {
compatible = "ti,twl6035-pmic", "ti,palmas-pmic";
adc {
compatible = "ti,palmas-gpadc";
interrupts = <18 0>,
<16 0>,
<17 0>;
#io-channel-cells = <1>;
ti,channel0-current-microamp = <5>;
ti,channel3-current-microamp = <10>;
};
};
...

View File

@ -12,6 +12,9 @@ maintainers:
description: |
Digital Step Attenuator IIO devices with gpio interface.
Offer various frequency and attenuation ranges.
ADRF5750 2 dB LSB, 4-Bit, Silicon Digital Attenuator, 10 MHz to 60 GHz
https://www.analog.com/media/en/technical-documentation/data-sheets/adrf5740.pdf
HMC425A 0.5 dB LSB GaAs MMIC 6-BIT DIGITAL POSITIVE CONTROL ATTENUATOR, 2.2 - 8.0 GHz
https://www.analog.com/media/en/technical-documentation/data-sheets/hmc425A.pdf
@ -22,6 +25,7 @@ description: |
properties:
compatible:
enum:
- adi,adrf5740
- adi,hmc425a
- adi,hmc540s

View File

@ -0,0 +1,47 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/chemical/aosong,ags02ma.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Aosong AGS02MA VOC Sensor
description: |
AGS02MA is an TVOC (Total Volatile Organic Compounds) i2c sensor with default
address of 0x1a.
Datasheet:
https://asairsensors.com/wp-content/uploads/2021/09/AGS02MA.pdf
maintainers:
- Anshul Dalal <anshulusr@gmail.com>
properties:
compatible:
enum:
- aosong,ags02ma
reg:
maxItems: 1
vdd-supply: true
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
voc-sensor@1a {
compatible = "aosong,ags02ma";
reg = <0x1a>;
vdd-supply = <&vdd_regulator>;
};
};

View File

@ -26,6 +26,11 @@ properties:
vdd-supply: true
vss-supply: true
adi,rbuf-gain2-en:
description: Specify to allow an external amplifier to be connected in a
gain of two configuration.
type: boolean
required:
- compatible
- reg

View File

@ -0,0 +1,55 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/humidity/ti,hdc3020.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: HDC3020/HDC3021/HDC3022 humidity and temperature iio sensors
maintainers:
- Li peiyu <579lpy@gmail.com>
- Javier Carrasco <javier.carrasco.cruz@gmail.com>
description:
https://www.ti.com/lit/ds/symlink/hdc3020.pdf
The HDC302x is an integrated capacitive based relative humidity (RH)
and temperature sensor.
properties:
compatible:
oneOf:
- items:
- enum:
- ti,hdc3021
- ti,hdc3022
- const: ti,hdc3020
- const: ti,hdc3020
interrupts:
maxItems: 1
vdd-supply: true
reg:
maxItems: 1
required:
- compatible
- reg
- vdd-supply
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
humidity-sensor@47 {
compatible = "ti,hdc3021", "ti,hdc3020";
reg = <0x47>;
vdd-supply = <&vcc_3v3>;
};
};

View File

@ -25,6 +25,10 @@ properties:
spi-cpol: true
spi-cs-inactive-delay-ns:
minimum: 16000
default: 16000
interrupts:
maxItems: 1

View File

@ -47,6 +47,10 @@ properties:
spi-max-frequency:
maximum: 2000000
spi-cs-inactive-delay-ns:
minimum: 16000
default: 16000
interrupts:
maxItems: 1

View File

@ -0,0 +1,77 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/imu/bosch,bmi323.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Bosch BMI323 6-Axis IMU
maintainers:
- Jagath Jog J <jagathjog1996@gmail.com>
description:
BMI323 is a 6-axis inertial measurement unit that supports acceleration and
gyroscopic measurements with hardware fifo buffering. Sensor also provides
events information such as motion, steps, orientation, single and double
tap detection.
properties:
compatible:
const: bosch,bmi323
reg:
maxItems: 1
vdd-supply: true
vddio-supply: true
interrupts:
minItems: 1
maxItems: 2
interrupt-names:
minItems: 1
maxItems: 2
items:
enum:
- INT1
- INT2
drive-open-drain:
description:
set if the specified interrupt pin should be configured as
open drain. If not set, defaults to push-pull.
mount-matrix:
description:
an optional 3x3 mounting rotation matrix.
required:
- compatible
- reg
- vdd-supply
- vddio-supply
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
unevaluatedProperties: false
examples:
- |
// Example for I2C
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
imu@68 {
compatible = "bosch,bmi323";
reg = <0x68>;
vddio-supply = <&vddio>;
vdd-supply = <&vdd>;
interrupt-parent = <&gpio1>;
interrupts = <29 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "INT1";
};
};

View File

@ -0,0 +1,56 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/light/liteon,ltr390.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Lite-On LTR390 ALS and UV Sensor
description: |
The Lite-On LTR390 is an ALS (Ambient Light Sensor) and a UV sensor in a
single package with i2c address of 0x53.
Datasheet:
https://optoelectronics.liteon.com/upload/download/DS86-2015-0004/LTR-390UV_Final_%20DS_V1%201.pdf
maintainers:
- Anshul Dalal <anshulusr@gmail.com>
properties:
compatible:
enum:
- liteon,ltr390
reg:
maxItems: 1
interrupts:
maxItems: 1
description: |
Level interrupt pin with open drain output.
The sensor pulls this pin low when the measured reading is greater than
some configured threshold.
vdd-supply: true
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
light-sensor@53 {
compatible = "liteon,ltr390";
reg = <0x53>;
interrupts = <18 IRQ_TYPE_EDGE_FALLING>;
vdd-supply = <&vdd_regulator>;
};
};

View File

@ -0,0 +1,39 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/light/vishay,veml6075.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Vishay VEML6075 UVA and UVB sensor
maintainers:
- Javier Carrasco <javier.carrasco.cruz@gmail.com>
properties:
compatible:
const: vishay,veml6075
reg:
maxItems: 1
vdd-supply: true
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
uv-sensor@10 {
compatible = "vishay,veml6075";
reg = <0x10>;
vdd-supply = <&vdd_reg>;
};
};
...

View File

@ -0,0 +1,142 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/pressure/honeywell,hsc030pa.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Honeywell TruStability HSC and SSC pressure sensor series
description: |
support for Honeywell TruStability HSC and SSC digital pressure sensor
series.
These sensors have either an I2C, an SPI or an analog interface. Only the
digital versions are supported by this driver.
There are 118 models with different pressure ranges available in each family.
The vendor calls them "HSC series" and "SSC series". All of them have an
identical programming model but differ in pressure range, unit and transfer
function.
To support different models one needs to specify the pressure range as well
as the transfer function. Pressure range can either be provided via
pressure-triplet (directly extracted from the part number) or in case it's
a custom chip via numerical range limits converted to pascals.
The transfer function defines the ranges of raw conversion values delivered
by the sensor. pmin-pascal and pmax-pascal corespond to the minimum and
maximum pressure that can be measured.
Please note that in case of an SPI-based sensor, the clock signal should not
exceed 800kHz and the MOSI signal is not required.
Specifications about the devices can be found at:
https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/trustability-hsc-series/documents/sps-siot-trustability-hsc-series-high-accuracy-board-mount-pressure-sensors-50099148-a-en-ciid-151133.pdf
https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/trustability-ssc-series/documents/sps-siot-trustability-ssc-series-standard-accuracy-board-mount-pressure-sensors-50099533-a-en-ciid-151134.pdf
maintainers:
- Petre Rodan <petre.rodan@subdimension.ro>
properties:
compatible:
const: honeywell,hsc030pa
reg:
maxItems: 1
honeywell,transfer-function:
description: |
Transfer function which defines the range of valid values delivered by
the sensor.
0 - A, 10% to 90% of 2^14
1 - B, 5% to 95% of 2^14
2 - C, 5% to 85% of 2^14
3 - F, 4% to 94% of 2^14
enum: [0, 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, set to "NA" and provide pmin-pascal and pmax-pascal.
enum: [001BA, 1.6BA, 2.5BA, 004BA, 006BA, 010BA, 1.6MD, 2.5MD, 004MD,
006MD, 010MD, 016MD, 025MD, 040MD, 060MD, 100MD, 160MD, 250MD,
400MD, 600MD, 001BD, 1.6BD, 2.5BD, 004BD, 2.5MG, 004MG, 006MG,
010MG, 016MG, 025MG, 040MG, 060MG, 100MG, 160MG, 250MG, 400MG,
600MG, 001BG, 1.6BG, 2.5BG, 004BG, 006BG, 010BG, 100KA, 160KA,
250KA, 400KA, 600KA, 001GA, 160LD, 250LD, 400LD, 600LD, 001KD,
1.6KD, 2.5KD, 004KD, 006KD, 010KD, 016KD, 025KD, 040KD, 060KD,
100KD, 160KD, 250KD, 400KD, 250LG, 400LG, 600LG, 001KG, 1.6KG,
2.5KG, 004KG, 006KG, 010KG, 016KG, 025KG, 040KG, 060KG, 100KG,
160KG, 250KG, 400KG, 600KG, 001GG, 015PA, 030PA, 060PA, 100PA,
150PA, 0.5ND, 001ND, 002ND, 004ND, 005ND, 010ND, 020ND, 030ND,
001PD, 005PD, 015PD, 030PD, 060PD, 001NG, 002NG, 004NG, 005NG,
010NG, 020NG, 030NG, 001PG, 005PG, 015PG, 030PG, 060PG, 100PG,
150PG, NA]
$ref: /schemas/types.yaml#/definitions/string
honeywell,pmin-pascal:
description: |
Minimum pressure value the sensor can measure in pascal.
To be specified only if honeywell,pressure-triplet is set to "NA".
honeywell,pmax-pascal:
description: |
Maximum pressure value the sensor can measure in pascal.
To be specified only if honeywell,pressure-triplet is set to "NA".
vdd-supply:
description:
Provide VDD power to the sensor (either 3.3V or 5V depending on the chip)
spi-max-frequency:
maximum: 800000
required:
- compatible
- reg
- honeywell,transfer-function
- honeywell,pressure-triplet
additionalProperties: false
dependentSchemas:
honeywell,pmin-pascal:
properties:
honeywell,pressure-triplet:
const: NA
honeywell,pmax-pascal:
properties:
honeywell,pressure-triplet:
const: NA
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
pressure@28 {
compatible = "honeywell,hsc030pa";
reg = <0x28>;
honeywell,transfer-function = <0>;
honeywell,pressure-triplet = "030PA";
};
};
- |
spi {
#address-cells = <1>;
#size-cells = <0>;
pressure@0 {
compatible = "honeywell,hsc030pa";
reg = <0>;
spi-max-frequency = <800000>;
honeywell,transfer-function = <0>;
honeywell,pressure-triplet = "NA";
honeywell,pmin-pascal = <0>;
honeywell,pmax-pascal = <200000>;
};
};
...

View File

@ -53,12 +53,10 @@ properties:
honeywell,pmin-pascal:
description:
Minimum pressure value the sensor can measure in pascal.
$ref: /schemas/types.yaml#/definitions/uint32
honeywell,pmax-pascal:
description:
Maximum pressure value the sensor can measure in pascal.
$ref: /schemas/types.yaml#/definitions/uint32
honeywell,transfer-function:
description: |

View File

@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/iio/temperature/melexis,mlx90632.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Melexis MLX90632 contactless Infra Red temperature sensor
title: Melexis MLX90632 and MLX90635 contactless Infra Red temperature sensor
maintainers:
- Crt Mori <cmo@melexis.com>
@ -27,9 +27,24 @@ description: |
Since measured object emissivity effects Infra Red energy emitted,
emissivity should be set before requesting the object temperature.
https://www.melexis.com/en/documents/documentation/datasheets/datasheet-mlx90635
MLX90635 is most suitable for consumer applications where
measured object temperature is in range between -20 to 100 degrees
Celsius with relative error of measurement 2 degree Celsius in
object temperature range for industrial applications, while just 0.2
degree Celsius for human body measurement applications. Since it can
operate and measure ambient temperature in range of -20 to 85 degrees
Celsius it is suitable also for outdoor use.
Since measured object emissivity effects Infra Red energy emitted,
emissivity should be set before requesting the object temperature.
properties:
compatible:
const: melexis,mlx90632
enum:
- melexis,mlx90632
- melexis,mlx90635
reg:
maxItems: 1

View File

@ -0,0 +1,70 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/iio/temperature/microchip,mcp9600.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip MCP9600 thermocouple EMF converter
maintainers:
- Andrew Hepp <andrew.hepp@ahepp.dev>
description:
https://ww1.microchip.com/downloads/en/DeviceDoc/MCP960X-Data-Sheet-20005426.pdf
properties:
compatible:
const: microchip,mcp9600
reg:
maxItems: 1
interrupts:
minItems: 1
maxItems: 6
interrupt-names:
minItems: 1
maxItems: 6
items:
enum:
- open-circuit
- short-circuit
- alert1
- alert2
- alert3
- alert4
thermocouple-type:
$ref: /schemas/types.yaml#/definitions/uint32
description:
Type of thermocouple (THERMOCOUPLE_TYPE_K if omitted).
Use defines in dt-bindings/iio/temperature/thermocouple.h.
Supported types are B, E, J, K, N, R, S, T.
vdd-supply: true
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/iio/temperature/thermocouple.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
temperature-sensor@60 {
compatible = "microchip,mcp9600";
reg = <0x60>;
interrupt-parent = <&gpio>;
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "open-circuit";
thermocouple-type = <THERMOCOUPLE_TYPE_K>;
vdd-supply = <&vdd>;
};
};

View File

@ -179,6 +179,8 @@ properties:
- isil,isl29030
# Intersil ISL68137 Digital Output Configurable PWM Controller
- isil,isl68137
# Intersil ISL76682 Ambient Light Sensor
- isil,isl76682
# Linear Technology LTC2488
- lineartechnology,ltc2488
# 5 Bit Programmable, Pulse-Width Modulator

View File

@ -119,6 +119,8 @@ patternProperties:
description: Andes Technology Corporation
"^anvo,.*":
description: Anvo-Systems Dresden GmbH
"^aosong,.*":
description: Guangzhou Aosong Electronic Co., Ltd.
"^apm,.*":
description: Applied Micro Circuits Corporation (APM)
"^apple,.*":

View File

@ -3059,6 +3059,13 @@ S: Supported
W: http://www.akm.com/
F: drivers/iio/magnetometer/ak8974.c
AOSONG AGS02MA TVOC SENSOR DRIVER
M: Anshul Dalal <anshulusr@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/chemical/aosong,ags02ma.yaml
F: drivers/iio/chemical/ags02ma.c
ASC7621 HARDWARE MONITOR DRIVER
M: George Joseph <george.joseph@fairview5.com>
L: linux-hwmon@vger.kernel.org
@ -3635,6 +3642,13 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/accel/bosch,bma400.yaml
F: drivers/iio/accel/bma400*
BOSCH SENSORTEC BMI323 IMU IIO DRIVER
M: Jagath Jog J <jagathjog1996@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/imu/bosch,bmi323.yaml
F: drivers/iio/imu/bmi323/
BPF JIT for ARM
M: Russell King <linux@armlinux.org.uk>
M: Puranjay Mohan <puranjay12@gmail.com>
@ -9647,6 +9661,13 @@ F: lib/test_hmm*
F: mm/hmm*
F: tools/testing/selftests/mm/*hmm*
HONEYWELL HSC030PA PRESSURE SENSOR SERIES IIO DRIVER
M: Petre Rodan <petre.rodan@subdimension.ro>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/pressure/honeywell,hsc030pa.yaml
F: drivers/iio/pressure/hsc030pa*
HONEYWELL MPRLS0025PA PRESSURE SENSOR SERIES IIO DRIVER
M: Andreas Klinger <ak@it-klinger.de>
L: linux-iio@vger.kernel.org
@ -10278,8 +10299,8 @@ IIO LIGHT SENSOR GAIN-TIME-SCALE HELPERS
M: Matti Vaittinen <mazziesaccount@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: drivers/iio/light/gain-time-scale-helper.c
F: drivers/iio/light/gain-time-scale-helper.h
F: drivers/iio/industrialio-gts-helper.c
F: include/linux/iio/iio-gts-helper.h
IIO MULTIPLEXER
M: Peter Rosin <peda@axentia.se>
@ -12596,6 +12617,13 @@ S: Maintained
W: http://linux-test-project.github.io/
T: git https://github.com/linux-test-project/ltp.git
LTR390 AMBIENT/UV LIGHT SENSOR DRIVER
M: Anshul Dalal <anshulusr@gmail.com>
L: linux-iio@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/iio/light/liteon,ltr390.yaml
F: drivers/iio/light/ltr390.c
LYNX 28G SERDES PHY DRIVER
M: Ioana Ciornei <ioana.ciornei@nxp.com>
L: netdev@vger.kernel.org
@ -13662,6 +13690,13 @@ S: Supported
W: http://www.melexis.com
F: drivers/iio/temperature/mlx90632.c
MELEXIS MLX90635 DRIVER
M: Crt Mori <cmo@melexis.com>
L: linux-iio@vger.kernel.org
S: Supported
W: http://www.melexis.com
F: drivers/iio/temperature/mlx90635.c
MELFAS MIP4 TOUCHSCREEN DRIVER
M: Sangwon Jee <jeesw@melfas.com>
S: Supported
@ -14132,11 +14167,12 @@ F: Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt
F: drivers/regulator/mcp16502.c
MICROCHIP MCP3564 ADC DRIVER
M: Marius Cristea <marius.cristea@microchip.com>
L: linux-iio@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/iio/adc/microchip,mcp3564.yaml
F: drivers/iio/adc/mcp3564.c
M: Marius Cristea <marius.cristea@microchip.com>
L: linux-iio@vger.kernel.org
S: Supported
F: Documentation/ABI/testing/sysfs-bus-iio-adc-mcp3564
F: Documentation/devicetree/bindings/iio/adc/microchip,mcp3564.yaml
F: drivers/iio/adc/mcp3564.c
MICROCHIP MCP3911 ADC DRIVER
M: Marcus Folkesson <marcus.folkesson@gmail.com>
@ -23113,6 +23149,12 @@ S: Maintained
F: drivers/input/serio/userio.c
F: include/uapi/linux/userio.h
VISHAY VEML6075 UVA AND UVB LIGHT SENSOR DRIVER
M: Javier Carrasco <javier.carrasco.cruz@gmail.com>
S: Maintained
F: Documentation/devicetree/bindings/iio/light/vishay,veml6075.yaml
F: drivers/iio/light/veml6075.c
VISL VIRTUAL STATELESS DECODER DRIVER
M: Daniel Almeida <daniel.almeida@collabora.com>
L: linux-media@vger.kernel.org

View File

@ -473,7 +473,7 @@ int fwnode_property_match_string(const struct fwnode_handle *fwnode,
const char **values;
int nval, ret;
nval = fwnode_property_read_string_array(fwnode, propname, NULL, 0);
nval = fwnode_property_string_array_count(fwnode, propname);
if (nval < 0)
return nval;
@ -498,6 +498,41 @@ out_free:
}
EXPORT_SYMBOL_GPL(fwnode_property_match_string);
/**
* fwnode_property_match_property_string - find a property string value in an array and return index
* @fwnode: Firmware node to get the property of
* @propname: Name of the property holding the string value
* @array: String array to search in
* @n: Size of the @array
*
* Find a property string value in a given @array and if it is found return
* the index back.
*
* Return: index, starting from %0, if the string value was found in the @array (success),
* %-ENOENT when the string value was not found in the @array,
* %-EINVAL if given arguments are not valid,
* %-ENODATA if the property does not have a value,
* %-EPROTO or %-EILSEQ if the property is not a string,
* %-ENXIO if no suitable firmware interface is present.
*/
int fwnode_property_match_property_string(const struct fwnode_handle *fwnode,
const char *propname, const char * const *array, size_t n)
{
const char *string;
int ret;
ret = fwnode_property_read_string(fwnode, propname, &string);
if (ret)
return ret;
ret = match_string(array, n, string);
if (ret < 0)
ret = -ENOENT;
return ret;
}
EXPORT_SYMBOL_GPL(fwnode_property_match_property_string);
/**
* fwnode_property_get_reference_args() - Find a reference with arguments
* @fwnode: Firmware node where to look for the reference

View File

@ -260,10 +260,11 @@ config BMI088_ACCEL
select REGMAP
select BMI088_ACCEL_SPI
help
Say yes here to build support for the Bosch BMI088 accelerometer.
Say yes here to build support for the following Bosch accelerometers:
BMI088, BMI085, BMI090L. Note that all of these are combo module that
include both accelerometer and gyroscope.
This is a combo module with both accelerometer and gyroscope. This
driver only implements the accelerometer part, which has its own
This driver only implements the accelerometer part, which has its own
address and register map. BMG160 provides the gyroscope driver.
config BMI088_ACCEL_SPI

View File

@ -2,6 +2,8 @@
/*
* 3-axis accelerometer driver supporting following Bosch-Sensortec chips:
* - BMI088
* - BMI085
* - BMI090L
*
* Copyright (c) 2018-2021, Topic Embedded Products
*/

View File

@ -2,6 +2,8 @@
/*
* 3-axis accelerometer driver supporting following Bosch-Sensortec chips:
* - BMI088
* - BMI085
* - BMI090L
*
* Copyright (c) 2018-2020, Topic Embedded Products
*/

View File

@ -292,7 +292,7 @@ config ADI_AXI_ADC
select IIO_BUFFER
select IIO_BUFFER_HW_CONSUMER
select IIO_BUFFER_DMAENGINE
depends on HAS_IOMEM
select REGMAP_MMIO
depends on OF
help
Say yes here to build support for Analog Devices Generic
@ -745,6 +745,17 @@ config MAX1363
To compile this driver as a module, choose M here: the module will be
called max1363.
config MAX34408
tristate "Maxim max34408/max344089 ADC driver"
depends on I2C
help
Say yes here to build support for Maxim max34408/max34409 current sense
monitor with 8-bits ADC interface with overcurrent delay/threshold and
shutdown delay.
To compile this driver as a module, choose M here: the module will be
called max34408.
config MAX77541_ADC
tristate "Analog Devices MAX77541 ADC driver"
depends on MFD_MAX77541

View File

@ -68,6 +68,7 @@ obj-$(CONFIG_MAX11205) += max11205.o
obj-$(CONFIG_MAX11410) += max11410.o
obj-$(CONFIG_MAX1241) += max1241.o
obj-$(CONFIG_MAX1363) += max1363.o
obj-$(CONFIG_MAX34408) += max34408.o
obj-$(CONFIG_MAX77541_ADC) += max77541-adc.o
obj-$(CONFIG_MAX9611) += max9611.o
obj-$(CONFIG_MCP320X) += mcp320x.o

View File

@ -28,6 +28,7 @@
#define AD7091R_REG_RESULT_CONV_RESULT(x) ((x) & 0xfff)
/* AD7091R_REG_CONF */
#define AD7091R_REG_CONF_ALERT_EN BIT(4)
#define AD7091R_REG_CONF_AUTO BIT(8)
#define AD7091R_REG_CONF_CMD BIT(10)
@ -174,8 +175,8 @@ static const struct iio_info ad7091r_info = {
static irqreturn_t ad7091r_event_handler(int irq, void *private)
{
struct ad7091r_state *st = (struct ad7091r_state *) private;
struct iio_dev *iio_dev = dev_get_drvdata(st->dev);
struct iio_dev *iio_dev = private;
struct ad7091r_state *st = iio_priv(iio_dev);
unsigned int i, read_val;
int ret;
s64 timestamp = iio_get_time_ns(iio_dev);
@ -232,9 +233,15 @@ int ad7091r_probe(struct device *dev, const char *name,
iio_dev->channels = chip_info->channels;
if (irq) {
ret = regmap_update_bits(st->map, AD7091R_REG_CONF,
AD7091R_REG_CONF_ALERT_EN, BIT(4));
if (ret)
return ret;
ret = devm_request_threaded_irq(dev, irq, NULL,
ad7091r_event_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, name, st);
ad7091r_event_handler,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT, name, iio_dev);
if (ret)
return ret;
}

View File

@ -4,8 +4,9 @@
*
* Copyright 2012-2020 Analog Devices Inc.
*/
#include <linux/cleanup.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@ -100,12 +101,6 @@
#define AD9467_DEF_OUTPUT_MODE 0x08
#define AD9467_REG_VREF_MASK 0x0F
enum {
ID_AD9265,
ID_AD9434,
ID_AD9467,
};
struct ad9467_chip_info {
struct adi_axi_adc_chip_info axi_adc_info;
unsigned int default_output_mode;
@ -119,9 +114,11 @@ struct ad9467_state {
struct spi_device *spi;
struct clk *clk;
unsigned int output_mode;
unsigned int (*scales)[2];
struct gpio_desc *pwrdown_gpio;
struct gpio_desc *reset_gpio;
/* ensure consistent state obtained on multiple related accesses */
struct mutex lock;
};
static int ad9467_spi_read(struct spi_device *spi, unsigned int reg)
@ -161,11 +158,13 @@ static int ad9467_reg_access(struct adi_axi_adc_conv *conv, unsigned int reg,
struct spi_device *spi = st->spi;
int ret;
if (readval == NULL) {
if (!readval) {
guard(mutex)(&st->lock);
ret = ad9467_spi_write(spi, reg, writeval);
ad9467_spi_write(spi, AN877_ADC_REG_TRANSFER,
AN877_ADC_TRANSFER_SYNC);
return ret;
if (ret)
return ret;
return ad9467_spi_write(spi, AN877_ADC_REG_TRANSFER,
AN877_ADC_TRANSFER_SYNC);
}
ret = ad9467_spi_read(spi, reg);
@ -212,6 +211,7 @@ static void __ad9467_get_scale(struct adi_axi_adc_conv *conv, int index,
.channel = _chan, \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = _si, \
.scan_type = { \
.sign = _sign, \
@ -228,43 +228,46 @@ static const struct iio_chan_spec ad9467_channels[] = {
AD9467_CHAN(0, 0, 16, 'S'),
};
static const struct ad9467_chip_info ad9467_chip_tbl[] = {
[ID_AD9265] = {
.axi_adc_info = {
.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 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),
},
[ID_AD9434] = {
.axi_adc_info = {
.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,
.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),
},
[ID_AD9467] = {
.axi_adc_info = {
.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,
.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),
},
.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)
@ -273,10 +276,13 @@ static int ad9467_get_scale(struct adi_axi_adc_conv *conv, int *val, int *val2)
const struct ad9467_chip_info *info1 = to_ad9467_chip_info(info);
struct ad9467_state *st = adi_axi_adc_conv_priv(conv);
unsigned int i, vref_val;
int ret;
vref_val = ad9467_spi_read(st->spi, AN877_ADC_REG_VREF);
ret = ad9467_spi_read(st->spi, AN877_ADC_REG_VREF);
if (ret < 0)
return ret;
vref_val &= info1->vref_mask;
vref_val = ret & info1->vref_mask;
for (i = 0; i < info->num_scales; i++) {
if (vref_val == info->scale_table[i][1])
@ -297,6 +303,7 @@ static int ad9467_set_scale(struct adi_axi_adc_conv *conv, int val, int val2)
struct ad9467_state *st = adi_axi_adc_conv_priv(conv);
unsigned int scale_val[2];
unsigned int i;
int ret;
if (val != 0)
return -EINVAL;
@ -306,11 +313,14 @@ static int ad9467_set_scale(struct adi_axi_adc_conv *conv, int val, int val2)
if (scale_val[0] != val || scale_val[1] != val2)
continue;
ad9467_spi_write(st->spi, AN877_ADC_REG_VREF,
info->scale_table[i][1]);
ad9467_spi_write(st->spi, AN877_ADC_REG_TRANSFER,
AN877_ADC_TRANSFER_SYNC);
return 0;
guard(mutex)(&st->lock);
ret = ad9467_spi_write(st->spi, AN877_ADC_REG_VREF,
info->scale_table[i][1]);
if (ret < 0)
return ret;
return ad9467_spi_write(st->spi, AN877_ADC_REG_TRANSFER,
AN877_ADC_TRANSFER_SYNC);
}
return -EINVAL;
@ -359,6 +369,26 @@ static int ad9467_write_raw(struct adi_axi_adc_conv *conv,
}
}
static int ad9467_read_avail(struct adi_axi_adc_conv *conv,
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);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
*vals = (const int *)st->scales;
*type = IIO_VAL_INT_PLUS_MICRO;
/* Values are stored in a 2D matrix */
*length = info->num_scales * 2;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static int ad9467_outputmode_set(struct spi_device *spi, unsigned int mode)
{
int ret;
@ -371,6 +401,26 @@ 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)
{
const struct adi_axi_adc_chip_info *info = conv->chip_info;
struct ad9467_state *st = adi_axi_adc_conv_priv(conv);
unsigned int i, val1, val2;
st->scales = devm_kmalloc_array(&st->spi->dev, info->num_scales,
sizeof(*st->scales), GFP_KERNEL);
if (!st->scales)
return -ENOMEM;
for (i = 0; i < info->num_scales; i++) {
__ad9467_get_scale(conv, i, &val1, &val2);
st->scales[i][0] = val1;
st->scales[i][1] = val2;
}
return 0;
}
static int ad9467_preenable_setup(struct adi_axi_adc_conv *conv)
{
struct ad9467_state *st = adi_axi_adc_conv_priv(conv);
@ -378,6 +428,21 @@ static int ad9467_preenable_setup(struct adi_axi_adc_conv *conv)
return ad9467_outputmode_set(st->spi, st->output_mode);
}
static int ad9467_reset(struct device *dev)
{
struct gpio_desc *gpio;
gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR_OR_NULL(gpio))
return PTR_ERR_OR_ZERO(gpio);
fsleep(1);
gpiod_set_value_cansleep(gpio, 0);
fsleep(10 * USEC_PER_MSEC);
return 0;
}
static int ad9467_probe(struct spi_device *spi)
{
const struct ad9467_chip_info *info;
@ -386,9 +451,7 @@ static int ad9467_probe(struct spi_device *spi)
unsigned int id;
int ret;
info = of_device_get_match_data(&spi->dev);
if (!info)
info = (void *)spi_get_device_id(spi)->driver_data;
info = spi_get_device_match_data(spi);
if (!info)
return -ENODEV;
@ -408,21 +471,16 @@ static int ad9467_probe(struct spi_device *spi)
if (IS_ERR(st->pwrdown_gpio))
return PTR_ERR(st->pwrdown_gpio);
st->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset",
GPIOD_OUT_LOW);
if (IS_ERR(st->reset_gpio))
return PTR_ERR(st->reset_gpio);
if (st->reset_gpio) {
udelay(1);
ret = gpiod_direction_output(st->reset_gpio, 1);
if (ret)
return ret;
mdelay(10);
}
ret = ad9467_reset(&spi->dev);
if (ret)
return ret;
conv->chip_info = &info->axi_adc_info;
ret = ad9467_scale_fill(conv);
if (ret)
return ret;
id = ad9467_spi_read(spi, AN877_ADC_REG_CHIP_ID);
if (id != conv->chip_info->id) {
dev_err(&spi->dev, "Mismatch CHIP_ID, got 0x%X, expected 0x%X\n",
@ -433,6 +491,7 @@ static int ad9467_probe(struct spi_device *spi)
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;
st->output_mode = info->default_output_mode |
@ -442,17 +501,17 @@ static int ad9467_probe(struct spi_device *spi)
}
static const struct of_device_id ad9467_of_match[] = {
{ .compatible = "adi,ad9265", .data = &ad9467_chip_tbl[ID_AD9265], },
{ .compatible = "adi,ad9434", .data = &ad9467_chip_tbl[ID_AD9434], },
{ .compatible = "adi,ad9467", .data = &ad9467_chip_tbl[ID_AD9467], },
{ .compatible = "adi,ad9265", .data = &ad9265_chip_tbl, },
{ .compatible = "adi,ad9434", .data = &ad9434_chip_tbl, },
{ .compatible = "adi,ad9467", .data = &ad9467_chip_tbl, },
{}
};
MODULE_DEVICE_TABLE(of, ad9467_of_match);
static const struct spi_device_id ad9467_ids[] = {
{ "ad9265", (kernel_ulong_t)&ad9467_chip_tbl[ID_AD9265] },
{ "ad9434", (kernel_ulong_t)&ad9467_chip_tbl[ID_AD9434] },
{ "ad9467", (kernel_ulong_t)&ad9467_chip_tbl[ID_AD9467] },
{ "ad9265", (kernel_ulong_t)&ad9265_chip_tbl },
{ "ad9434", (kernel_ulong_t)&ad9434_chip_tbl },
{ "ad9467", (kernel_ulong_t)&ad9467_chip_tbl },
{}
};
MODULE_DEVICE_TABLE(spi, ad9467_ids);

View File

@ -14,6 +14,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/iio/iio.h>
@ -62,7 +63,7 @@ struct adi_axi_adc_state {
struct mutex lock;
struct adi_axi_adc_client *client;
void __iomem *regs;
struct regmap *regmap;
};
struct adi_axi_adc_client {
@ -90,19 +91,6 @@ void *adi_axi_adc_conv_priv(struct adi_axi_adc_conv *conv)
}
EXPORT_SYMBOL_NS_GPL(adi_axi_adc_conv_priv, IIO_ADI_AXI);
static void adi_axi_adc_write(struct adi_axi_adc_state *st,
unsigned int reg,
unsigned int val)
{
iowrite32(val, st->regs + reg);
}
static unsigned int adi_axi_adc_read(struct adi_axi_adc_state *st,
unsigned int reg)
{
return ioread32(st->regs + reg);
}
static int adi_axi_adc_config_dma_buffer(struct device *dev,
struct iio_dev *indio_dev)
{
@ -144,22 +132,39 @@ static int adi_axi_adc_write_raw(struct iio_dev *indio_dev,
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, ctrl;
unsigned int i;
int ret;
for (i = 0; i < conv->chip_info->num_channels; i++) {
ctrl = adi_axi_adc_read(st, ADI_AXI_REG_CHAN_CTRL(i));
if (test_bit(i, scan_mask))
ctrl |= ADI_AXI_REG_CHAN_CTRL_ENABLE;
ret = regmap_set_bits(st->regmap,
ADI_AXI_REG_CHAN_CTRL(i),
ADI_AXI_REG_CHAN_CTRL_ENABLE);
else
ctrl &= ~ADI_AXI_REG_CHAN_CTRL_ENABLE;
adi_axi_adc_write(st, ADI_AXI_REG_CHAN_CTRL(i), ctrl);
ret = regmap_clear_bits(st->regmap,
ADI_AXI_REG_CHAN_CTRL(i),
ADI_AXI_REG_CHAN_CTRL_ENABLE);
if (ret)
return ret;
}
return 0;
@ -228,69 +233,11 @@ struct adi_axi_adc_conv *devm_adi_axi_adc_conv_register(struct device *dev,
}
EXPORT_SYMBOL_NS_GPL(devm_adi_axi_adc_conv_register, IIO_ADI_AXI);
static ssize_t in_voltage_scale_available_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct adi_axi_adc_state *st = iio_priv(indio_dev);
struct adi_axi_adc_conv *conv = &st->client->conv;
size_t len = 0;
int i;
for (i = 0; i < conv->chip_info->num_scales; i++) {
const unsigned int *s = conv->chip_info->scale_table[i];
len += scnprintf(buf + len, PAGE_SIZE - len,
"%u.%06u ", s[0], s[1]);
}
buf[len - 1] = '\n';
return len;
}
static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0);
enum {
ADI_AXI_ATTR_SCALE_AVAIL,
};
#define ADI_AXI_ATTR(_en_, _file_) \
[ADI_AXI_ATTR_##_en_] = &iio_dev_attr_##_file_.dev_attr.attr
static struct attribute *adi_axi_adc_attributes[] = {
ADI_AXI_ATTR(SCALE_AVAIL, in_voltage_scale_available),
NULL
};
static umode_t axi_adc_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
struct device *dev = kobj_to_dev(kobj);
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct adi_axi_adc_state *st = iio_priv(indio_dev);
struct adi_axi_adc_conv *conv = &st->client->conv;
switch (n) {
case ADI_AXI_ATTR_SCALE_AVAIL:
if (!conv->chip_info->num_scales)
return 0;
return attr->mode;
default:
return attr->mode;
}
}
static const struct attribute_group adi_axi_adc_attribute_group = {
.attrs = adi_axi_adc_attributes,
.is_visible = axi_adc_attr_is_visible,
};
static const struct iio_info adi_axi_adc_info = {
.read_raw = &adi_axi_adc_read_raw,
.write_raw = &adi_axi_adc_write_raw,
.attrs = &adi_axi_adc_attribute_group,
.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 = {
@ -354,21 +301,32 @@ static int adi_axi_adc_setup_channels(struct device *dev,
}
for (i = 0; i < conv->chip_info->num_channels; i++) {
adi_axi_adc_write(st, ADI_AXI_REG_CHAN_CTRL(i),
ADI_AXI_REG_CHAN_CTRL_DEFAULTS);
ret = regmap_write(st->regmap, ADI_AXI_REG_CHAN_CTRL(i),
ADI_AXI_REG_CHAN_CTRL_DEFAULTS);
if (ret)
return ret;
}
return 0;
}
static void axi_adc_reset(struct adi_axi_adc_state *st)
static int axi_adc_reset(struct adi_axi_adc_state *st)
{
adi_axi_adc_write(st, ADI_AXI_REG_RSTN, 0);
int ret;
ret = regmap_write(st->regmap, ADI_AXI_REG_RSTN, 0);
if (ret)
return ret;
mdelay(10);
adi_axi_adc_write(st, ADI_AXI_REG_RSTN, ADI_AXI_REG_RSTN_MMCM_RSTN);
ret = regmap_write(st->regmap, ADI_AXI_REG_RSTN,
ADI_AXI_REG_RSTN_MMCM_RSTN);
if (ret)
return ret;
mdelay(10);
adi_axi_adc_write(st, ADI_AXI_REG_RSTN,
ADI_AXI_REG_RSTN_RSTN | ADI_AXI_REG_RSTN_MMCM_RSTN);
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)
@ -379,12 +337,20 @@ static void adi_axi_adc_cleanup(void *data)
module_put(cl->dev->driver->owner);
}
static const struct regmap_config axi_adc_regmap_config = {
.val_bits = 32,
.reg_bits = 32,
.reg_stride = 4,
.max_register = 0x0800,
};
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;
struct adi_axi_adc_state *st;
void __iomem *base;
unsigned int ver;
int ret;
@ -405,15 +371,24 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
cl->state = st;
mutex_init(&st->lock);
st->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(st->regs))
return PTR_ERR(st->regs);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
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;
axi_adc_reset(st);
ret = axi_adc_reset(st);
if (ret)
return ret;
ver = adi_axi_adc_read(st, ADI_AXI_REG_VERSION);
ret = regmap_read(st->regmap, ADI_AXI_REG_VERSION, &ver);
if (ret)
return ret;
if (cl->info->version > ver) {
dev_err(&pdev->dev,

276
drivers/iio/adc/max34408.c Normal file
View File

@ -0,0 +1,276 @@
// SPDX-License-Identifier: GPL-2.0
/*
* IIO driver for Maxim MAX34409/34408 ADC, 4-Channels/2-Channels, 8bits, I2C
*
* Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/MAX34408-MAX34409.pdf
*
* TODO: ALERT interrupt, Overcurrent delay, Shutdown delay
*/
#include <linux/bitfield.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/types.h>
#define MAX34408_STATUS_REG 0x0
#define MAX34408_CONTROL_REG 0x1
#define MAX34408_OCDELAY_REG 0x2
#define MAX34408_SDDELAY_REG 0x3
#define MAX34408_ADC1_REG 0x4
#define MAX34408_ADC2_REG 0x5
/* ADC3 & ADC4 always returns 0x0 on 34408 */
#define MAX34409_ADC3_REG 0x6
#define MAX34409_ADC4_REG 0x7
#define MAX34408_OCT1_REG 0x8
#define MAX34408_OCT2_REG 0x9
#define MAX34409_OCT3_REG 0xA
#define MAX34409_OCT4_REG 0xB
#define MAX34408_DID_REG 0xC
#define MAX34408_DCYY_REG 0xD
#define MAX34408_DCWW_REG 0xE
/* Bit masks for status register */
#define MAX34408_STATUS_OC_MSK GENMASK(1, 0)
#define MAX34409_STATUS_OC_MSK GENMASK(3, 0)
#define MAX34408_STATUS_SHTDN BIT(4)
#define MAX34408_STATUS_ENA BIT(5)
/* Bit masks for control register */
#define MAX34408_CONTROL_AVG0 BIT(0)
#define MAX34408_CONTROL_AVG1 BIT(1)
#define MAX34408_CONTROL_AVG2 BIT(2)
#define MAX34408_CONTROL_ALERT BIT(3)
#define MAX34408_DEFAULT_AVG 0x4
/* Bit masks for over current delay */
#define MAX34408_OCDELAY_OCD_MSK GENMASK(6, 0)
#define MAX34408_OCDELAY_RESET BIT(7)
/* Bit masks for shutdown delay */
#define MAX34408_SDDELAY_SHD_MSK GENMASK(6, 0)
#define MAX34408_SDDELAY_RESET BIT(7)
#define MAX34408_DEFAULT_RSENSE 1000
/**
* struct max34408_data - max34408/max34409 specific data.
* @regmap: device register map.
* @dev: max34408 device.
* @lock: lock for protecting access to device hardware registers, mostly
* for read modify write cycles for control registers.
* @input_rsense: Rsense values in uOhm, will be overwritten by
* values from channel nodes.
*/
struct max34408_data {
struct regmap *regmap;
struct device *dev;
struct mutex lock;
u32 input_rsense[4];
};
static const struct regmap_config max34408_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX34408_DCWW_REG,
};
struct max34408_adc_model_data {
const char *model_name;
const struct iio_chan_spec *channels;
const int num_channels;
};
#define MAX34008_CHANNEL(_index, _address) \
{ \
.type = IIO_CURRENT, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_OFFSET), \
.channel = (_index), \
.address = (_address), \
.indexed = 1, \
}
static const struct iio_chan_spec max34408_channels[] = {
MAX34008_CHANNEL(0, MAX34408_ADC1_REG),
MAX34008_CHANNEL(1, MAX34408_ADC2_REG),
};
static const struct iio_chan_spec max34409_channels[] = {
MAX34008_CHANNEL(0, MAX34408_ADC1_REG),
MAX34008_CHANNEL(1, MAX34408_ADC2_REG),
MAX34008_CHANNEL(2, MAX34409_ADC3_REG),
MAX34008_CHANNEL(3, MAX34409_ADC4_REG),
};
static int max34408_read_adc_avg(struct max34408_data *max34408,
const struct iio_chan_spec *chan, int *val)
{
unsigned int ctrl;
int rc;
guard(mutex)(&max34408->lock);
rc = regmap_read(max34408->regmap, MAX34408_CONTROL_REG, (u32 *)&ctrl);
if (rc)
return rc;
/* set averaging (0b100) default values*/
rc = regmap_write(max34408->regmap, MAX34408_CONTROL_REG,
MAX34408_DEFAULT_AVG);
if (rc) {
dev_err(max34408->dev,
"Error (%d) writing control register\n", rc);
return rc;
}
rc = regmap_read(max34408->regmap, chan->address, val);
if (rc)
return rc;
/* back to old values */
rc = regmap_write(max34408->regmap, MAX34408_CONTROL_REG, ctrl);
if (rc)
dev_err(max34408->dev,
"Error (%d) writing control register\n", rc);
return rc;
}
static int max34408_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct max34408_data *max34408 = iio_priv(indio_dev);
int rc;
switch (mask) {
case IIO_CHAN_INFO_RAW:
rc = max34408_read_adc_avg(max34408, chan, val);
if (rc)
return rc;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
/*
* calcluate current for 8bit ADC with Rsense
* value.
* 10 mV * 1000 / Rsense uOhm = max current
* (max current * adc val * 1000) / (2^8 - 1) mA
*/
*val = 10000 / max34408->input_rsense[chan->channel];
*val2 = 8;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
}
static const struct iio_info max34408_info = {
.read_raw = max34408_read_raw,
};
static const struct max34408_adc_model_data max34408_model_data = {
.model_name = "max34408",
.channels = max34408_channels,
.num_channels = 2,
};
static const struct max34408_adc_model_data max34409_model_data = {
.model_name = "max34409",
.channels = max34409_channels,
.num_channels = 4,
};
static int max34408_probe(struct i2c_client *client)
{
const struct max34408_adc_model_data *model_data;
struct device *dev = &client->dev;
struct max34408_data *max34408;
struct fwnode_handle *node;
struct iio_dev *indio_dev;
struct regmap *regmap;
int rc, i = 0;
model_data = i2c_get_match_data(client);
if (!model_data)
return -EINVAL;
regmap = devm_regmap_init_i2c(client, &max34408_regmap_config);
if (IS_ERR(regmap)) {
dev_err_probe(dev, PTR_ERR(regmap),
"regmap_init failed\n");
return PTR_ERR(regmap);
}
indio_dev = devm_iio_device_alloc(dev, sizeof(*max34408));
if (!indio_dev)
return -ENOMEM;
max34408 = iio_priv(indio_dev);
max34408->regmap = regmap;
max34408->dev = dev;
mutex_init(&max34408->lock);
device_for_each_child_node(dev, node) {
fwnode_property_read_u32(node, "maxim,rsense-val-micro-ohms",
&max34408->input_rsense[i]);
i++;
}
/* disable ALERT and averaging */
rc = regmap_write(max34408->regmap, MAX34408_CONTROL_REG, 0x0);
if (rc)
return rc;
indio_dev->channels = model_data->channels;
indio_dev->num_channels = model_data->num_channels;
indio_dev->name = model_data->model_name;
indio_dev->info = &max34408_info;
indio_dev->modes = INDIO_DIRECT_MODE;
return devm_iio_device_register(dev, indio_dev);
}
static const struct of_device_id max34408_of_match[] = {
{
.compatible = "maxim,max34408",
.data = &max34408_model_data,
},
{
.compatible = "maxim,max34409",
.data = &max34409_model_data,
},
{}
};
MODULE_DEVICE_TABLE(of, max34408_of_match);
static const struct i2c_device_id max34408_id[] = {
{ "max34408", (kernel_ulong_t)&max34408_model_data },
{ "max34409", (kernel_ulong_t)&max34409_model_data },
{}
};
MODULE_DEVICE_TABLE(i2c, max34408_id);
static struct i2c_driver max34408_driver = {
.driver = {
.name = "max34408",
.of_match_table = max34408_of_match,
},
.probe = max34408_probe,
.id_table = max34408_id,
};
module_i2c_driver(max34408_driver);
MODULE_AUTHOR("Ivan Mikhaylov <fr0st61te@gmail.com>");
MODULE_DESCRIPTION("Maxim MAX34408/34409 ADC driver");
MODULE_LICENSE("GPL");

View File

@ -7,6 +7,7 @@
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
@ -316,47 +317,37 @@ static int mcp3911_read_raw(struct iio_dev *indio_dev,
int *val2, long mask)
{
struct mcp3911 *adc = iio_priv(indio_dev);
int ret = -EINVAL;
int ret;
mutex_lock(&adc->lock);
guard(mutex)(&adc->lock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = mcp3911_read(adc,
MCP3911_CHANNEL(channel->channel), val, 3);
if (ret)
goto out;
return ret;
*val = sign_extend32(*val, 23);
ret = IIO_VAL_INT;
break;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
ret = adc->chip->get_offset(adc, channel->channel, val);
if (ret)
goto out;
return ret;
ret = IIO_VAL_INT;
break;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
ret = adc->chip->get_osr(adc, val);
if (ret)
goto out;
ret = IIO_VAL_INT;
break;
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = mcp3911_scale_table[ilog2(adc->gain[channel->channel])][0];
*val2 = mcp3911_scale_table[ilog2(adc->gain[channel->channel])][1];
ret = IIO_VAL_INT_PLUS_NANO;
break;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
out:
mutex_unlock(&adc->lock);
return ret;
}
static int mcp3911_write_raw(struct iio_dev *indio_dev,
@ -364,9 +355,8 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev,
int val2, long mask)
{
struct mcp3911 *adc = iio_priv(indio_dev);
int ret = -EINVAL;
mutex_lock(&adc->lock);
guard(mutex)(&adc->lock);
switch (mask) {
case IIO_CHAN_INFO_SCALE:
for (int i = 0; i < MCP3911_NUM_SCALES; i++) {
@ -374,32 +364,25 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev,
val2 == mcp3911_scale_table[i][1]) {
adc->gain[channel->channel] = BIT(i);
ret = adc->chip->set_scale(adc, channel->channel, i);
return adc->chip->set_scale(adc, channel->channel, i);
}
}
break;
return -EINVAL;
case IIO_CHAN_INFO_OFFSET:
if (val2 != 0) {
ret = -EINVAL;
goto out;
}
ret = adc->chip->set_offset(adc, channel->channel, val);
break;
if (val2 != 0)
return -EINVAL;
return adc->chip->set_offset(adc, channel->channel, val);
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
for (int i = 0; i < ARRAY_SIZE(mcp3911_osr_table); i++) {
if (val == mcp3911_osr_table[i]) {
ret = adc->chip->set_osr(adc, i);
break;
return adc->chip->set_osr(adc, i);
}
}
break;
return -EINVAL;
default:
return -EINVAL;
}
out:
mutex_unlock(&adc->lock);
return ret;
}
static int mcp3911_calc_scale_table(struct mcp3911 *adc)
@ -532,7 +515,7 @@ static irqreturn_t mcp3911_trigger_handler(int irq, void *p)
int i = 0;
int ret;
mutex_lock(&adc->lock);
guard(mutex)(&adc->lock);
adc->tx_buf = MCP3911_REG_READ(MCP3911_CHANNEL(0), adc->dev_addr);
ret = spi_sync_transfer(adc->spi, xfer, ARRAY_SIZE(xfer));
if (ret < 0) {
@ -549,7 +532,6 @@ static irqreturn_t mcp3911_trigger_handler(int irq, void *p)
iio_push_to_buffers_with_timestamp(indio_dev, &adc->scan,
iio_get_time_ns(indio_dev));
out:
mutex_unlock(&adc->lock);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;

View File

@ -5,6 +5,7 @@
* Copyright 2020 Analog Devices Inc.
*/
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
@ -22,6 +23,7 @@
enum hmc425a_type {
ID_HMC425A,
ID_HMC540S,
ID_ADRF5740
};
struct hmc425a_chip_info {
@ -74,6 +76,10 @@ static int hmc425a_read_raw(struct iio_dev *indio_dev,
case ID_HMC540S:
gain = ~code * -1000;
break;
case ID_ADRF5740:
code = code & BIT(3) ? code & ~BIT(2) : code;
gain = code * -2000;
break;
}
*val = gain / 1000;
@ -113,6 +119,10 @@ static int hmc425a_write_raw(struct iio_dev *indio_dev,
case ID_HMC540S:
code = ~((abs(gain) / 1000) & 0xF);
break;
case ID_ADRF5740:
code = (abs(gain) / 2000) & 0xF;
code = code & BIT(3) ? code | BIT(2) : code;
break;
}
mutex_lock(&st->lock);
@ -165,6 +175,7 @@ static const struct iio_chan_spec hmc425a_channels[] = {
static const struct of_device_id hmc425a_of_match[] = {
{ .compatible = "adi,hmc425a", .data = (void *)ID_HMC425A },
{ .compatible = "adi,hmc540s", .data = (void *)ID_HMC540S },
{ .compatible = "adi,adrf5740", .data = (void *)ID_ADRF5740 },
{},
};
MODULE_DEVICE_TABLE(of, hmc425a_of_match);
@ -188,6 +199,15 @@ static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = {
.gain_max = 0,
.default_gain = -0x10, /* set default gain -15.0db*/
},
[ID_ADRF5740] = {
.name = "adrf5740",
.channels = hmc425a_channels,
.num_channels = ARRAY_SIZE(hmc425a_channels),
.num_gpios = 4,
.gain_min = -22000,
.gain_max = 0,
.default_gain = 0xF, /* set default gain -22.0db*/
},
};
static int hmc425a_probe(struct platform_device *pdev)
@ -229,6 +249,9 @@ static int hmc425a_probe(struct platform_device *pdev)
indio_dev->info = &hmc425a_info;
indio_dev->modes = INDIO_DIRECT_MODE;
/* Set default gain */
hmc425a_write(indio_dev, st->gain);
return devm_iio_device_register(&pdev->dev, indio_dev);
}

View File

@ -5,6 +5,17 @@
menu "Chemical Sensors"
config AOSONG_AGS02MA
tristate "Aosong AGS02MA TVOC sensor driver"
depends on I2C
select CRC8
help
Say Y here to build support for Aosong AGS02MA TVOC (Total Volatile
Organic Compounds) sensor.
To compile this driver as module, choose M here: the module will be
called ags02ma.
config ATLAS_PH_SENSOR
tristate "Atlas Scientific OEM SM sensors"
depends on I2C

View File

@ -4,6 +4,7 @@
#
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AOSONG_AGS02MA) += ags02ma.o
obj-$(CONFIG_ATLAS_PH_SENSOR) += atlas-sensor.o
obj-$(CONFIG_ATLAS_EZO_SENSOR) += atlas-ezo-sensor.o
obj-$(CONFIG_BME680) += bme680_core.o

View File

@ -0,0 +1,165 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com>
*
* Driver for Aosong AGS02MA
*
* Datasheet:
* https://asairsensors.com/wp-content/uploads/2021/09/AGS02MA.pdf
* Product Page:
* http://www.aosong.com/m/en/products-33.html
*/
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#define AGS02MA_TVOC_READ_REG 0x00
#define AGS02MA_VERSION_REG 0x11
#define AGS02MA_VERSION_PROCESSING_DELAY 30
#define AGS02MA_TVOC_READ_PROCESSING_DELAY 1500
#define AGS02MA_CRC8_INIT 0xff
#define AGS02MA_CRC8_POLYNOMIAL 0x31
DECLARE_CRC8_TABLE(ags02ma_crc8_table);
struct ags02ma_data {
struct i2c_client *client;
};
struct ags02ma_reading {
__be32 data;
u8 crc;
} __packed;
static int ags02ma_register_read(struct i2c_client *client, u8 reg, u16 delay,
u32 *val)
{
int ret;
u8 crc;
struct ags02ma_reading read_buffer;
ret = i2c_master_send(client, &reg, sizeof(reg));
if (ret < 0) {
dev_err(&client->dev,
"Failed to send data to register 0x%x: %d", reg, ret);
return ret;
}
/* Processing Delay, Check Table 7.7 in the datasheet */
msleep_interruptible(delay);
ret = i2c_master_recv(client, (u8 *)&read_buffer, sizeof(read_buffer));
if (ret < 0) {
dev_err(&client->dev,
"Failed to receive from register 0x%x: %d", reg, ret);
return ret;
}
crc = crc8(ags02ma_crc8_table, (u8 *)&read_buffer.data,
sizeof(read_buffer.data), AGS02MA_CRC8_INIT);
if (crc != read_buffer.crc) {
dev_err(&client->dev, "CRC error\n");
return -EIO;
}
*val = be32_to_cpu(read_buffer.data);
return 0;
}
static int ags02ma_read_raw(struct iio_dev *iio_device,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
int ret;
struct ags02ma_data *data = iio_priv(iio_device);
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = ags02ma_register_read(data->client, AGS02MA_TVOC_READ_REG,
AGS02MA_TVOC_READ_PROCESSING_DELAY,
val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
/* The sensor reads data as ppb */
*val = 0;
*val2 = 100;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static const struct iio_info ags02ma_info = {
.read_raw = ags02ma_read_raw,
};
static const struct iio_chan_spec ags02ma_channel = {
.type = IIO_CONCENTRATION,
.channel2 = IIO_MOD_VOC,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
};
static int ags02ma_probe(struct i2c_client *client)
{
int ret;
struct ags02ma_data *data;
struct iio_dev *indio_dev;
u32 version;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
crc8_populate_msb(ags02ma_crc8_table, AGS02MA_CRC8_POLYNOMIAL);
ret = ags02ma_register_read(client, AGS02MA_VERSION_REG,
AGS02MA_VERSION_PROCESSING_DELAY, &version);
if (ret < 0)
return dev_err_probe(&client->dev, ret,
"Failed to read device version\n");
dev_dbg(&client->dev, "Aosong AGS02MA, Version: 0x%x", version);
data = iio_priv(indio_dev);
data->client = client;
indio_dev->info = &ags02ma_info;
indio_dev->channels = &ags02ma_channel;
indio_dev->num_channels = 1;
indio_dev->name = "ags02ma";
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id ags02ma_id_table[] = {
{ "ags02ma" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, ags02ma_id_table);
static const struct of_device_id ags02ma_of_table[] = {
{ .compatible = "aosong,ags02ma" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, ags02ma_of_table);
static struct i2c_driver ags02ma_driver = {
.driver = {
.name = "ags02ma",
.of_match_table = ags02ma_of_table,
},
.id_table = ags02ma_id_table,
.probe = ags02ma_probe,
};
module_i2c_driver(ags02ma_driver);
MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>");
MODULE_DESCRIPTION("Aosong AGS02MA TVOC Driver");
MODULE_LICENSE("GPL");

View File

@ -345,6 +345,7 @@ static int ad5791_probe(struct spi_device *spi)
struct iio_dev *indio_dev;
struct ad5791_state *st;
int ret, pos_voltage_uv = 0, neg_voltage_uv = 0;
bool use_rbuf_gain2;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
@ -379,6 +380,12 @@ static int ad5791_probe(struct spi_device *spi)
st->pwr_down = true;
st->spi = spi;
if (pdata)
use_rbuf_gain2 = pdata->use_rbuf_gain2;
else
use_rbuf_gain2 = device_property_read_bool(&spi->dev,
"adi,rbuf-gain2-en");
if (!IS_ERR(st->reg_vss) && !IS_ERR(st->reg_vdd)) {
st->vref_mv = (pos_voltage_uv + neg_voltage_uv) / 1000;
st->vref_neg_mv = neg_voltage_uv / 1000;
@ -398,7 +405,7 @@ static int ad5791_probe(struct spi_device *spi)
st->ctrl = AD5761_CTRL_LINCOMP(st->chip_info->get_lin_comp(st->vref_mv))
| ((pdata && pdata->use_rbuf_gain2) ? 0 : AD5791_CTRL_RBUF) |
| (use_rbuf_gain2 ? 0 : AD5791_CTRL_RBUF) |
AD5791_CTRL_BIN2SC;
ret = ad5791_spi_write(st, AD5791_ADDR_CTRL, st->ctrl |

View File

@ -870,7 +870,6 @@ static const struct iio_chan_spec adf4377_channels[] = {
static int adf4377_properties_parse(struct adf4377_state *st)
{
struct spi_device *spi = st->spi;
const char *str;
int ret;
st->clkin = devm_clk_get_enabled(&spi->dev, "ref_in");
@ -896,16 +895,13 @@ static int adf4377_properties_parse(struct adf4377_state *st)
return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_enclk2),
"failed to get the CE GPIO\n");
ret = device_property_read_string(&spi->dev, "adi,muxout-select", &str);
if (ret) {
st->muxout_select = ADF4377_MUXOUT_HIGH_Z;
} else {
ret = match_string(adf4377_muxout_modes, ARRAY_SIZE(adf4377_muxout_modes), str);
if (ret < 0)
return ret;
ret = device_property_match_property_string(&spi->dev, "adi,muxout-select",
adf4377_muxout_modes,
ARRAY_SIZE(adf4377_muxout_modes));
if (ret >= 0)
st->muxout_select = ret;
}
else
st->muxout_select = ADF4377_MUXOUT_HIGH_Z;
return 0;
}

View File

@ -710,7 +710,6 @@ static int admv1014_init(struct admv1014_state *st)
static int admv1014_properties_parse(struct admv1014_state *st)
{
const char *str;
unsigned int i;
struct spi_device *spi = st->spi;
int ret;
@ -719,27 +718,21 @@ static int admv1014_properties_parse(struct admv1014_state *st)
st->p1db_comp = device_property_read_bool(&spi->dev, "adi,p1db-compensation-enable");
ret = device_property_read_string(&spi->dev, "adi,input-mode", &str);
if (ret) {
st->input_mode = ADMV1014_IQ_MODE;
} else {
ret = match_string(input_mode_names, ARRAY_SIZE(input_mode_names), str);
if (ret < 0)
return ret;
ret = device_property_match_property_string(&spi->dev, "adi,input-mode",
input_mode_names,
ARRAY_SIZE(input_mode_names));
if (ret >= 0)
st->input_mode = ret;
}
ret = device_property_read_string(&spi->dev, "adi,quad-se-mode", &str);
if (ret) {
st->quad_se_mode = ADMV1014_SE_MODE_POS;
} else {
ret = match_string(quad_se_mode_names, ARRAY_SIZE(quad_se_mode_names), str);
if (ret < 0)
return ret;
else
st->input_mode = ADMV1014_IQ_MODE;
ret = device_property_match_property_string(&spi->dev, "adi,quad-se-mode",
quad_se_mode_names,
ARRAY_SIZE(quad_se_mode_names));
if (ret >= 0)
st->quad_se_mode = ADMV1014_SE_MODE_POS + (ret * 3);
}
else
st->quad_se_mode = ADMV1014_SE_MODE_POS;
for (i = 0; i < ADMV1014_NUM_REGULATORS; ++i)
st->regulators[i].supply = admv1014_reg_name[i];

View File

@ -0,0 +1,473 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* hdc3020.c - Support for the TI HDC3020,HDC3021 and HDC3022
* temperature + relative humidity sensors
*
* Copyright (C) 2023
*
* Datasheet: https://www.ti.com/lit/ds/symlink/hdc3020.pdf
*/
#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/module.h>
#include <linux/mutex.h>
#include <asm/unaligned.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_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 };
struct hdc3020_data {
struct i2c_client *client;
/*
* Ensure that the sensor configuration (currently only heater is
* supported) will not be changed during the process of reading
* sensor data (this driver will try HDC3020_READ_RETRY_TIMES times
* if the device does not respond).
*/
struct mutex lock;
};
static const int hdc3020_heater_vals[] = {0, 1, 0x3FFF};
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),
},
{
.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),
},
{
/*
* For setting the internal heater, which can be switched on to
* prevent or remove any condensation that may develop when the
* ambient environment approaches its dew point temperature.
*/
.type = IIO_CURRENT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
.output = 1,
},
};
DECLARE_CRC8_TABLE(hdc3020_crc8_table);
static int hdc3020_write_bytes(struct hdc3020_data *data, const u8 *buf, u8 len)
{
struct i2c_client *client = data->client;
struct i2c_msg msg;
int ret, cnt;
msg.addr = client->addr;
msg.flags = 0;
msg.buf = (char *)buf;
msg.len = len;
/*
* During the measurement process, HDC3020 will not return data.
* So wait for a while and try again
*/
for (cnt = 0; cnt < HDC3020_READ_RETRY_TIMES; cnt++) {
ret = i2c_transfer(client->adapter, &msg, 1);
if (ret == 1)
return 0;
mdelay(HDC3020_BUSY_DELAY_MS);
}
dev_err(&client->dev, "Could not write sensor command\n");
return -ETIMEDOUT;
}
static int hdc3020_read_bytes(struct hdc3020_data *data, const u8 *buf,
void *val, int len)
{
int ret, cnt;
struct i2c_client *client = data->client;
struct i2c_msg msg[2] = {
[0] = {
.addr = client->addr,
.flags = 0,
.buf = (char *)buf,
.len = 2,
},
[1] = {
.addr = client->addr,
.flags = I2C_M_RD,
.buf = val,
.len = len,
},
};
/*
* During the measurement process, HDC3020 will not return data.
* So wait for a while and try again
*/
for (cnt = 0; cnt < HDC3020_READ_RETRY_TIMES; cnt++) {
ret = i2c_transfer(client->adapter, msg, 2);
if (ret == 2)
return 0;
mdelay(HDC3020_BUSY_DELAY_MS);
}
dev_err(&client->dev, "Could not read sensor data\n");
return -ETIMEDOUT;
}
static int hdc3020_read_measurement(struct hdc3020_data *data,
enum iio_chan_type type, int *val)
{
u8 crc, buf[6];
int ret;
ret = hdc3020_read_bytes(data, HDC3020_R_T_RH_AUTO, buf, 6);
if (ret < 0)
return ret;
/* CRC check of the temperature measurement */
crc = crc8(hdc3020_crc8_table, buf, 2, CRC8_INIT_VALUE);
if (crc != buf[2])
return -EINVAL;
/* CRC check of the relative humidity measurement */
crc = crc8(hdc3020_crc8_table, buf + 3, 2, CRC8_INIT_VALUE);
if (crc != buf[5])
return -EINVAL;
if (type == IIO_TEMP)
*val = get_unaligned_be16(buf);
else if (type == IIO_HUMIDITYRELATIVE)
*val = get_unaligned_be16(&buf[3]);
else
return -EINVAL;
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)
{
struct hdc3020_data *data = iio_priv(indio_dev);
int ret;
if (chan->type != IIO_TEMP && chan->type != IIO_HUMIDITYRELATIVE)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_RAW: {
guard(mutex)(&data->lock);
ret = hdc3020_read_measurement(data, chan->type, val);
if (ret < 0)
return ret;
return IIO_VAL_INT;
}
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;
}
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;
}
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_SCALE:
*val2 = 65536;
if (chan->type == IIO_TEMP)
*val = 175;
else
*val = 100;
return IIO_VAL_FRACTIONAL;
case IIO_CHAN_INFO_OFFSET:
if (chan->type != IIO_TEMP)
return -EINVAL;
*val = 16852;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int hdc3020_read_available(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals,
int *type, int *length, long mask)
{
if (mask != IIO_CHAN_INFO_RAW || chan->type != IIO_CURRENT)
return -EINVAL;
*vals = hdc3020_heater_vals;
*type = IIO_VAL_INT;
return IIO_AVAIL_RANGE;
}
static int hdc3020_update_heater(struct hdc3020_data *data, int val)
{
u8 buf[5];
int ret;
if (val < hdc3020_heater_vals[0] || val > hdc3020_heater_vals[2])
return -EINVAL;
buf[0] = HDC3020_HEATER_CMD_MSB;
if (!val) {
buf[1] = HDC3020_HEATER_DISABLE;
return hdc3020_write_bytes(data, buf, 2);
}
buf[1] = HDC3020_HEATER_CONFIG;
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);
}
static int hdc3020_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct hdc3020_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type != IIO_CURRENT)
return -EINVAL;
guard(mutex)(&data->lock);
return hdc3020_update_heater(data, val);
}
return -EINVAL;
}
static const struct iio_info hdc3020_info = {
.read_raw = hdc3020_read_raw,
.write_raw = hdc3020_write_raw,
.read_avail = hdc3020_read_available,
};
static void hdc3020_stop(void *data)
{
hdc3020_write_bytes((struct hdc3020_data *)data, HDC3020_EXIT_AUTO, 2);
}
static int hdc3020_probe(struct i2c_client *client)
{
struct iio_dev *indio_dev;
struct hdc3020_data *data;
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -EOPNOTSUPP;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->client = client;
mutex_init(&data->lock);
crc8_populate_msb(hdc3020_crc8_table, HDC3020_CRC8_POLYNOMIAL);
indio_dev->name = "hdc3020";
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &hdc3020_info;
indio_dev->channels = hdc3020_channels;
indio_dev->num_channels = ARRAY_SIZE(hdc3020_channels);
ret = hdc3020_write_bytes(data, HDC3020_S_AUTO_10HZ_MOD0, 2);
if (ret)
return dev_err_probe(&client->dev, ret,
"Unable to set up measurement\n");
ret = devm_add_action_or_reset(&data->client->dev, hdc3020_stop, data);
if (ret)
return ret;
ret = devm_iio_device_register(&data->client->dev, indio_dev);
if (ret)
return dev_err_probe(&client->dev, ret, "Failed to add device");
return 0;
}
static const struct i2c_device_id hdc3020_id[] = {
{ "hdc3020" },
{ "hdc3021" },
{ "hdc3022" },
{ }
};
MODULE_DEVICE_TABLE(i2c, hdc3020_id);
static const struct of_device_id hdc3020_dt_ids[] = {
{ .compatible = "ti,hdc3020" },
{ .compatible = "ti,hdc3021" },
{ .compatible = "ti,hdc3022" },
{ }
};
MODULE_DEVICE_TABLE(of, hdc3020_dt_ids);
static struct i2c_driver hdc3020_driver = {
.driver = {
.name = "hdc3020",
.of_match_table = hdc3020_dt_ids,
},
.probe = hdc3020_probe,
.id_table = hdc3020_id,
};
module_i2c_driver(hdc3020_driver);
MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gmail.com>");
MODULE_AUTHOR("Li peiyu <579lpy@gmail.com>");
MODULE_DESCRIPTION("TI HDC3020 humidity and temperature sensor driver");
MODULE_LICENSE("GPL");

View File

@ -53,6 +53,7 @@ config ADIS16480
ADIS16485, ADIS16488 inertial sensors.
source "drivers/iio/imu/bmi160/Kconfig"
source "drivers/iio/imu/bmi323/Kconfig"
source "drivers/iio/imu/bno055/Kconfig"
config FXOS8700

View File

@ -15,6 +15,7 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
obj-y += bmi160/
obj-y += bmi323/
obj-y += bno055/
obj-$(CONFIG_FXOS8700) += fxos8700_core.o

View File

@ -44,8 +44,6 @@ int __adis_write_reg(struct adis *adis, unsigned int reg, unsigned int value,
.cs_change = 1,
.delay.value = adis->data->write_delay,
.delay.unit = SPI_DELAY_UNIT_USECS,
.cs_change_delay.value = adis->data->cs_change_delay,
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
}, {
.tx_buf = adis->tx + 2,
.bits_per_word = 8,
@ -53,8 +51,6 @@ int __adis_write_reg(struct adis *adis, unsigned int reg, unsigned int value,
.cs_change = 1,
.delay.value = adis->data->write_delay,
.delay.unit = SPI_DELAY_UNIT_USECS,
.cs_change_delay.value = adis->data->cs_change_delay,
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
}, {
.tx_buf = adis->tx + 4,
.bits_per_word = 8,
@ -62,8 +58,6 @@ int __adis_write_reg(struct adis *adis, unsigned int reg, unsigned int value,
.cs_change = 1,
.delay.value = adis->data->write_delay,
.delay.unit = SPI_DELAY_UNIT_USECS,
.cs_change_delay.value = adis->data->cs_change_delay,
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
}, {
.tx_buf = adis->tx + 6,
.bits_per_word = 8,
@ -144,8 +138,6 @@ int __adis_read_reg(struct adis *adis, unsigned int reg, unsigned int *val,
.cs_change = 1,
.delay.value = adis->data->write_delay,
.delay.unit = SPI_DELAY_UNIT_USECS,
.cs_change_delay.value = adis->data->cs_change_delay,
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
}, {
.tx_buf = adis->tx + 2,
.bits_per_word = 8,
@ -153,8 +145,6 @@ int __adis_read_reg(struct adis *adis, unsigned int reg, unsigned int *val,
.cs_change = 1,
.delay.value = adis->data->read_delay,
.delay.unit = SPI_DELAY_UNIT_USECS,
.cs_change_delay.value = adis->data->cs_change_delay,
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
}, {
.tx_buf = adis->tx + 4,
.rx_buf = adis->rx,
@ -163,8 +153,6 @@ int __adis_read_reg(struct adis *adis, unsigned int reg, unsigned int *val,
.cs_change = 1,
.delay.value = adis->data->read_delay,
.delay.unit = SPI_DELAY_UNIT_USECS,
.cs_change_delay.value = adis->data->cs_change_delay,
.cs_change_delay.unit = SPI_DELAY_UNIT_USECS,
}, {
.rx_buf = adis->rx + 2,
.bits_per_word = 8,
@ -524,6 +512,12 @@ int adis_init(struct adis *adis, struct iio_dev *indio_dev,
}
mutex_init(&adis->state_lock);
if (!spi->cs_inactive.value) {
spi->cs_inactive.value = data->cs_change_delay;
spi->cs_inactive.unit = SPI_DELAY_UNIT_USECS;
}
adis->spi = spi;
adis->data = data;
iio_device_set_drvdata(indio_dev, adis);

View File

@ -0,0 +1,33 @@
# SPDX-License-Identifier: GPL-2.0
#
# BMI323 IMU driver
#
config BMI323
tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
config BMI323_I2C
tristate "Bosch BMI323 I2C driver"
depends on I2C
select BMI323
select REGMAP_I2C
help
Enable support for the Bosch BMI323 6-Axis IMU connected to I2C
interface.
This driver can also be built as a module. If so, the module will be
called bmi323_i2c.
config BMI323_SPI
tristate "Bosch BMI323 SPI driver"
depends on SPI
select BMI323
select REGMAP_SPI
help
Enable support for the Bosch BMI323 6-Axis IMU connected to SPI
interface.
This driver can also be built as a module. If so, the module will be
called bmi323_spi.

View File

@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for Bosch BMI323 IMU
#
obj-$(CONFIG_BMI323) += bmi323_core.o
obj-$(CONFIG_BMI323_I2C) += bmi323_i2c.o
obj-$(CONFIG_BMI323_SPI) += bmi323_spi.o

View File

@ -0,0 +1,209 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* IIO driver for Bosch BMI323 6-Axis IMU
*
* Copyright (C) 2023, Jagath Jog J <jagathjog1996@gmail.com>
*/
#ifndef _BMI323_H_
#define _BMI323_H_
#include <linux/bits.h>
#include <linux/regmap.h>
#include <linux/units.h>
#define BMI323_I2C_DUMMY 2
#define BMI323_SPI_DUMMY 1
/* Register map */
#define BMI323_CHIP_ID_REG 0x00
#define BMI323_CHIP_ID_VAL 0x0043
#define BMI323_CHIP_ID_MSK GENMASK(7, 0)
#define BMI323_ERR_REG 0x01
#define BMI323_STATUS_REG 0x02
#define BMI323_STATUS_POR_MSK BIT(0)
/* Accelero/Gyro/Temp data registers */
#define BMI323_ACCEL_X_REG 0x03
#define BMI323_GYRO_X_REG 0x06
#define BMI323_TEMP_REG 0x09
#define BMI323_ALL_CHAN_MSK GENMASK(5, 0)
/* Status registers */
#define BMI323_STATUS_INT1_REG 0x0D
#define BMI323_STATUS_INT2_REG 0x0E
#define BMI323_STATUS_NOMOTION_MSK BIT(0)
#define BMI323_STATUS_MOTION_MSK BIT(1)
#define BMI323_STATUS_STP_WTR_MSK BIT(5)
#define BMI323_STATUS_TAP_MSK BIT(8)
#define BMI323_STATUS_ERROR_MSK BIT(10)
#define BMI323_STATUS_TMP_DRDY_MSK BIT(11)
#define BMI323_STATUS_GYR_DRDY_MSK BIT(12)
#define BMI323_STATUS_ACC_DRDY_MSK BIT(13)
#define BMI323_STATUS_ACC_GYR_DRDY_MSK GENMASK(13, 12)
#define BMI323_STATUS_FIFO_WTRMRK_MSK BIT(14)
#define BMI323_STATUS_FIFO_FULL_MSK BIT(15)
/* Feature registers */
#define BMI323_FEAT_IO0_REG 0x10
#define BMI323_FEAT_IO0_XYZ_NOMOTION_MSK GENMASK(2, 0)
#define BMI323_FEAT_IO0_XYZ_MOTION_MSK GENMASK(5, 3)
#define BMI323_FEAT_XYZ_MSK GENMASK(2, 0)
#define BMI323_FEAT_IO0_STP_CNT_MSK BIT(9)
#define BMI323_FEAT_IO0_S_TAP_MSK BIT(12)
#define BMI323_FEAT_IO0_D_TAP_MSK BIT(13)
#define BMI323_FEAT_IO1_REG 0x11
#define BMI323_FEAT_IO1_ERR_MSK GENMASK(3, 0)
#define BMI323_FEAT_IO2_REG 0x12
#define BMI323_FEAT_IO_STATUS_REG 0x14
#define BMI323_FEAT_IO_STATUS_MSK BIT(0)
#define BMI323_FEAT_ENG_POLL 2000
#define BMI323_FEAT_ENG_TIMEOUT 10000
/* FIFO registers */
#define BMI323_FIFO_FILL_LEVEL_REG 0x15
#define BMI323_FIFO_DATA_REG 0x16
/* Accelero/Gyro config registers */
#define BMI323_ACC_CONF_REG 0x20
#define BMI323_GYRO_CONF_REG 0x21
#define BMI323_ACC_GYRO_CONF_MODE_MSK GENMASK(14, 12)
#define BMI323_ACC_GYRO_CONF_ODR_MSK GENMASK(3, 0)
#define BMI323_ACC_GYRO_CONF_SCL_MSK GENMASK(6, 4)
#define BMI323_ACC_GYRO_CONF_BW_MSK BIT(7)
#define BMI323_ACC_GYRO_CONF_AVG_MSK GENMASK(10, 8)
/* FIFO registers */
#define BMI323_FIFO_WTRMRK_REG 0x35
#define BMI323_FIFO_CONF_REG 0x36
#define BMI323_FIFO_CONF_STP_FUL_MSK BIT(0)
#define BMI323_FIFO_CONF_ACC_GYR_EN_MSK GENMASK(10, 9)
#define BMI323_FIFO_ACC_GYR_MSK GENMASK(1, 0)
#define BMI323_FIFO_CTRL_REG 0x37
#define BMI323_FIFO_FLUSH_MSK BIT(0)
/* Interrupt pin config registers */
#define BMI323_IO_INT_CTR_REG 0x38
#define BMI323_IO_INT1_LVL_MSK BIT(0)
#define BMI323_IO_INT1_OD_MSK BIT(1)
#define BMI323_IO_INT1_OP_EN_MSK BIT(2)
#define BMI323_IO_INT1_LVL_OD_OP_MSK GENMASK(2, 0)
#define BMI323_IO_INT2_LVL_MSK BIT(8)
#define BMI323_IO_INT2_OD_MSK BIT(9)
#define BMI323_IO_INT2_OP_EN_MSK BIT(10)
#define BMI323_IO_INT2_LVL_OD_OP_MSK GENMASK(10, 8)
#define BMI323_IO_INT_CONF_REG 0x39
#define BMI323_IO_INT_LTCH_MSK BIT(0)
#define BMI323_INT_MAP1_REG 0x3A
#define BMI323_INT_MAP2_REG 0x3B
#define BMI323_NOMOTION_MSK GENMASK(1, 0)
#define BMI323_MOTION_MSK GENMASK(3, 2)
#define BMI323_STEP_CNT_MSK GENMASK(11, 10)
#define BMI323_TAP_MSK GENMASK(1, 0)
#define BMI323_TMP_DRDY_MSK GENMASK(7, 6)
#define BMI323_GYR_DRDY_MSK GENMASK(9, 8)
#define BMI323_ACC_DRDY_MSK GENMASK(11, 10)
#define BMI323_FIFO_WTRMRK_MSK GENMASK(13, 12)
#define BMI323_FIFO_FULL_MSK GENMASK(15, 14)
/* Feature registers */
#define BMI323_FEAT_CTRL_REG 0x40
#define BMI323_FEAT_ENG_EN_MSK BIT(0)
#define BMI323_FEAT_DATA_ADDR 0x41
#define BMI323_FEAT_DATA_TX 0x42
#define BMI323_FEAT_DATA_STATUS 0x43
#define BMI323_FEAT_DATA_TX_RDY_MSK BIT(1)
#define BMI323_FEAT_EVNT_EXT_REG 0x47
#define BMI323_FEAT_EVNT_EXT_S_MSK BIT(3)
#define BMI323_FEAT_EVNT_EXT_D_MSK BIT(4)
#define BMI323_CMD_REG 0x7E
#define BMI323_RST_VAL 0xDEAF
#define BMI323_CFG_RES_REG 0x7F
/* Extended registers */
#define BMI323_GEN_SET1_REG 0x02
#define BMI323_GEN_SET1_MODE_MSK BIT(0)
#define BMI323_GEN_HOLD_DUR_MSK GENMASK(4, 1)
/* Any Motion/No Motion config registers */
#define BMI323_ANYMO1_REG 0x05
#define BMI323_NOMO1_REG 0x08
#define BMI323_MO2_OFFSET 0x01
#define BMI323_MO3_OFFSET 0x02
#define BMI323_MO1_REF_UP_MSK BIT(12)
#define BMI323_MO1_SLOPE_TH_MSK GENMASK(11, 0)
#define BMI323_MO2_HYSTR_MSK GENMASK(9, 0)
#define BMI323_MO3_DURA_MSK GENMASK(12, 0)
/* Step counter config registers */
#define BMI323_STEP_SC1_REG 0x10
#define BMI323_STEP_SC1_WTRMRK_MSK GENMASK(9, 0)
#define BMI323_STEP_SC1_RST_CNT_MSK BIT(10)
#define BMI323_STEP_SC1_REG 0x10
#define BMI323_STEP_LEN 2
/* Tap gesture config registers */
#define BMI323_TAP1_REG 0x1E
#define BMI323_TAP1_AXIS_SEL_MSK GENMASK(1, 0)
#define BMI323_AXIS_XYZ_MSK GENMASK(1, 0)
#define BMI323_TAP1_TIMOUT_MSK BIT(2)
#define BMI323_TAP1_MAX_PEAKS_MSK GENMASK(5, 3)
#define BMI323_TAP1_MODE_MSK GENMASK(7, 6)
#define BMI323_TAP2_REG 0x1F
#define BMI323_TAP2_THRES_MSK GENMASK(9, 0)
#define BMI323_TAP2_MAX_DUR_MSK GENMASK(15, 10)
#define BMI323_TAP3_REG 0x20
#define BMI323_TAP3_QUIET_TIM_MSK GENMASK(15, 12)
#define BMI323_TAP3_QT_BW_TAP_MSK GENMASK(11, 8)
#define BMI323_TAP3_QT_AFT_GES_MSK GENMASK(15, 12)
#define BMI323_MOTION_THRES_SCALE 512
#define BMI323_MOTION_HYSTR_SCALE 512
#define BMI323_MOTION_DURAT_SCALE 50
#define BMI323_TAP_THRES_SCALE 512
#define BMI323_DUR_BW_TAP_SCALE 200
#define BMI323_QUITE_TIM_GES_SCALE 25
#define BMI323_MAX_GES_DUR_SCALE 25
/*
* The formula to calculate temperature in C.
* See datasheet section 6.1.1, Register Map Overview
*
* T_C = (temp_raw / 512) + 23
*/
#define BMI323_TEMP_OFFSET 11776
#define BMI323_TEMP_SCALE 1953125
/*
* The BMI323 features a FIFO with a capacity of 2048 bytes. Each frame
* consists of accelerometer (X, Y, Z) data and gyroscope (X, Y, Z) data,
* totaling 6 words or 12 bytes. The FIFO buffer can hold a total of
* 170 frames.
*
* If a watermark interrupt is configured for 170 frames, the interrupt will
* trigger when the FIFO reaches 169 frames, so limit the maximum watermark
* level to 169 frames. In terms of data, 169 frames would equal 1014 bytes,
* which is approximately 2 frames before the FIFO reaches its full capacity.
* See datasheet section 5.7.3 FIFO Buffer Interrupts
*/
#define BMI323_BYTES_PER_SAMPLE 2
#define BMI323_FIFO_LENGTH_IN_BYTES 2048
#define BMI323_FIFO_FRAME_LENGTH 6
#define BMI323_FIFO_FULL_IN_FRAMES \
((BMI323_FIFO_LENGTH_IN_BYTES / \
(BMI323_BYTES_PER_SAMPLE * BMI323_FIFO_FRAME_LENGTH)) - 1)
#define BMI323_FIFO_FULL_IN_WORDS \
(BMI323_FIFO_FULL_IN_FRAMES * BMI323_FIFO_FRAME_LENGTH)
#define BMI323_INT_MICRO_TO_RAW(val, val2, scale) ((val) * (scale) + \
((val2) * (scale)) / MEGA)
#define BMI323_RAW_TO_MICRO(raw, scale) ((((raw) % (scale)) * MEGA) / scale)
struct device;
int bmi323_core_probe(struct device *dev);
extern const struct regmap_config bmi323_regmap_config;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,121 @@
// SPDX-License-Identifier: GPL-2.0
/*
* I2C driver for Bosch BMI323 6-Axis IMU.
*
* Copyright (C) 2023, Jagath Jog J <jagathjog1996@gmail.com>
*/
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "bmi323.h"
struct bmi323_i2c_priv {
struct i2c_client *i2c;
u8 i2c_rx_buffer[BMI323_FIFO_LENGTH_IN_BYTES + BMI323_I2C_DUMMY];
};
/*
* From BMI323 datasheet section 4: Notes on the Serial Interface Support.
* Each I2C register read operation requires to read two dummy bytes before
* the actual payload.
*/
static int bmi323_regmap_i2c_read(void *context, const void *reg_buf,
size_t reg_size, void *val_buf,
size_t val_size)
{
struct bmi323_i2c_priv *priv = context;
struct i2c_msg msgs[2];
int ret;
msgs[0].addr = priv->i2c->addr;
msgs[0].flags = priv->i2c->flags;
msgs[0].len = reg_size;
msgs[0].buf = (u8 *)reg_buf;
msgs[1].addr = priv->i2c->addr;
msgs[1].len = val_size + BMI323_I2C_DUMMY;
msgs[1].buf = priv->i2c_rx_buffer;
msgs[1].flags = priv->i2c->flags | I2C_M_RD;
ret = i2c_transfer(priv->i2c->adapter, msgs, ARRAY_SIZE(msgs));
if (ret < 0)
return -EIO;
memcpy(val_buf, priv->i2c_rx_buffer + BMI323_I2C_DUMMY, val_size);
return 0;
}
static int bmi323_regmap_i2c_write(void *context, const void *data,
size_t count)
{
struct bmi323_i2c_priv *priv = context;
u8 reg;
reg = *(u8 *)data;
return i2c_smbus_write_i2c_block_data(priv->i2c, reg,
count - sizeof(u8),
data + sizeof(u8));
}
static struct regmap_bus bmi323_regmap_bus = {
.read = bmi323_regmap_i2c_read,
.write = bmi323_regmap_i2c_write,
};
static const struct regmap_config bmi323_i2c_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.max_register = BMI323_CFG_RES_REG,
.val_format_endian = REGMAP_ENDIAN_LITTLE,
};
static int bmi323_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct bmi323_i2c_priv *priv;
struct regmap *regmap;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->i2c = i2c;
regmap = devm_regmap_init(dev, &bmi323_regmap_bus, priv,
&bmi323_i2c_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap),
"Failed to initialize I2C Regmap\n");
return bmi323_core_probe(dev);
}
static const struct i2c_device_id bmi323_i2c_ids[] = {
{ "bmi323" },
{ }
};
MODULE_DEVICE_TABLE(i2c, bmi323_i2c_ids);
static const struct of_device_id bmi323_of_i2c_match[] = {
{ .compatible = "bosch,bmi323" },
{ }
};
MODULE_DEVICE_TABLE(of, bmi323_of_i2c_match);
static struct i2c_driver bmi323_i2c_driver = {
.driver = {
.name = "bmi323",
.of_match_table = bmi323_of_i2c_match,
},
.probe = bmi323_i2c_probe,
.id_table = bmi323_i2c_ids,
};
module_i2c_driver(bmi323_i2c_driver);
MODULE_DESCRIPTION("Bosch BMI323 IMU driver");
MODULE_AUTHOR("Jagath Jog J <jagathjog1996@gmail.com>");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(IIO_BMI323);

View File

@ -0,0 +1,92 @@
// SPDX-License-Identifier: GPL-2.0
/*
* SPI driver for Bosch BMI323 6-Axis IMU.
*
* Copyright (C) 2023, Jagath Jog J <jagathjog1996@gmail.com>
*/
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include "bmi323.h"
/*
* From BMI323 datasheet section 4: Notes on the Serial Interface Support.
* Each SPI register read operation requires to read one dummy byte before
* the actual payload.
*/
static int bmi323_regmap_spi_read(void *context, const void *reg_buf,
size_t reg_size, void *val_buf,
size_t val_size)
{
struct spi_device *spi = context;
return spi_write_then_read(spi, reg_buf, reg_size, val_buf, val_size);
}
static int bmi323_regmap_spi_write(void *context, const void *data,
size_t count)
{
struct spi_device *spi = context;
u8 *data_buff = (u8 *)data;
data_buff[1] = data_buff[0];
return spi_write(spi, data_buff + 1, count - 1);
}
static struct regmap_bus bmi323_regmap_bus = {
.read = bmi323_regmap_spi_read,
.write = bmi323_regmap_spi_write,
};
static const struct regmap_config bmi323_spi_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.pad_bits = 8,
.read_flag_mask = BIT(7),
.max_register = BMI323_CFG_RES_REG,
.val_format_endian = REGMAP_ENDIAN_LITTLE,
};
static int bmi323_spi_probe(struct spi_device *spi)
{
struct device *dev = &spi->dev;
struct regmap *regmap;
regmap = devm_regmap_init(dev, &bmi323_regmap_bus, dev,
&bmi323_spi_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap),
"Failed to initialize SPI Regmap\n");
return bmi323_core_probe(dev);
}
static const struct spi_device_id bmi323_spi_ids[] = {
{ "bmi323" },
{ }
};
MODULE_DEVICE_TABLE(spi, bmi323_spi_ids);
static const struct of_device_id bmi323_of_spi_match[] = {
{ .compatible = "bosch,bmi323" },
{ }
};
MODULE_DEVICE_TABLE(of, bmi323_of_spi_match);
static struct spi_driver bmi323_spi_driver = {
.driver = {
.name = "bmi323",
.of_match_table = bmi323_of_spi_match,
},
.probe = bmi323_spi_probe,
.id_table = bmi323_spi_ids,
};
module_spi_driver(bmi323_spi_driver);
MODULE_DESCRIPTION("Bosch BMI323 IMU driver");
MODULE_AUTHOR("Jagath Jog J <jagathjog1996@gmail.com>");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(IIO_BMI323);

View File

@ -137,10 +137,7 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev,
out_unlock:
mutex_unlock(&st->lock);
/* sleep maximum required time */
if (sleep_accel > sleep_temp)
sleep = sleep_accel;
else
sleep = sleep_temp;
sleep = max(sleep_accel, sleep_temp);
if (sleep)
msleep(sleep);
return ret;

View File

@ -424,10 +424,7 @@ out_unlock:
mutex_unlock(&st->lock);
/* sleep maximum required time */
if (sleep_sensor > sleep_temp)
sleep = sleep_sensor;
else
sleep = sleep_temp;
sleep = max(sleep_sensor, sleep_temp);
if (sleep)
msleep(sleep);

View File

@ -137,10 +137,7 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev,
out_unlock:
mutex_unlock(&st->lock);
/* sleep maximum required time */
if (sleep_gyro > sleep_temp)
sleep = sleep_gyro;
else
sleep = sleep_temp;
sleep = max(sleep_gyro, sleep_temp);
if (sleep)
msleep(sleep);
return ret;

View File

@ -567,15 +567,12 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
static int inv_mpu6050_sensor_set(struct inv_mpu6050_state *st, int reg,
int axis, int val)
{
int ind, result;
int ind;
__be16 d = cpu_to_be16(val);
ind = (axis - IIO_MOD_X) * 2;
result = regmap_bulk_write(st->map, reg + ind, &d, sizeof(d));
if (result)
return -EINVAL;
return 0;
return regmap_bulk_write(st->map, reg + ind, &d, sizeof(d));
}
static int inv_mpu6050_sensor_show(struct inv_mpu6050_state *st, int reg,
@ -587,7 +584,7 @@ static int inv_mpu6050_sensor_show(struct inv_mpu6050_state *st, int reg,
ind = (axis - IIO_MOD_X) * 2;
result = regmap_bulk_read(st->map, reg + ind, &d, sizeof(d));
if (result)
return -EINVAL;
return result;
*val = (short)be16_to_cpup(&d);
return IIO_VAL_INT;

View File

@ -413,6 +413,22 @@ static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks,
{
if (bitmap_empty(mask, masklength))
return NULL;
/*
* The condition here do not handle multi-long masks correctly.
* It only checks the first long to be zero, and will use such mask
* as a terminator even if there was bits set after the first long.
*
* Correct check would require using:
* while (!bitmap_empty(av_masks, masklength))
* instead. This is potentially hazardous because the
* avaliable_scan_masks is a zero terminated array of longs - and
* using the proper bitmap_empty() check for multi-long wide masks
* would require the array to be terminated with multiple zero longs -
* which is not such an usual pattern.
*
* As writing of this no multi-long wide masks were found in-tree, so
* the simple while (*av_masks) check is working.
*/
while (*av_masks) {
if (strict) {
if (bitmap_equal(mask, av_masks, masklength))

View File

@ -117,6 +117,8 @@ static const char * const iio_modifier_names[] = {
[IIO_MOD_LIGHT_GREEN] = "green",
[IIO_MOD_LIGHT_BLUE] = "blue",
[IIO_MOD_LIGHT_UV] = "uv",
[IIO_MOD_LIGHT_UVA] = "uva",
[IIO_MOD_LIGHT_UVB] = "uvb",
[IIO_MOD_LIGHT_DUV] = "duv",
[IIO_MOD_QUATERNION] = "quaternion",
[IIO_MOD_TEMP_AMBIENT] = "ambient",
@ -182,6 +184,7 @@ static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_THERMOCOUPLE_TYPE] = "thermocouple_type",
[IIO_CHAN_INFO_CALIBAMBIENT] = "calibambient",
[IIO_CHAN_INFO_ZEROPOINT] = "zeropoint",
[IIO_CHAN_INFO_TROUGH] = "trough_raw",
};
/**
* iio_device_id() - query the unique ID for the device
@ -1896,6 +1899,66 @@ static int iio_check_extended_name(const struct iio_dev *indio_dev)
static const struct iio_buffer_setup_ops noop_ring_setup_ops;
static void iio_sanity_check_avail_scan_masks(struct iio_dev *indio_dev)
{
unsigned int num_masks, masklength, longs_per_mask;
const unsigned long *av_masks;
int i;
av_masks = indio_dev->available_scan_masks;
masklength = indio_dev->masklength;
longs_per_mask = BITS_TO_LONGS(masklength);
/*
* The code determining how many available_scan_masks is in the array
* will be assuming the end of masks when first long with all bits
* zeroed is encountered. This is incorrect for masks where mask
* consists of more than one long, and where some of the available masks
* has long worth of bits zeroed (but has subsequent bit(s) set). This
* is a safety measure against bug where array of masks is terminated by
* a single zero while mask width is greater than width of a long.
*/
if (longs_per_mask > 1)
dev_warn(indio_dev->dev.parent,
"multi long available scan masks not fully supported\n");
if (bitmap_empty(av_masks, masklength))
dev_warn(indio_dev->dev.parent, "empty scan mask\n");
for (num_masks = 0; *av_masks; num_masks++)
av_masks += longs_per_mask;
if (num_masks < 2)
return;
av_masks = indio_dev->available_scan_masks;
/*
* Go through all the masks from first to one before the last, and see
* that no mask found later from the available_scan_masks array is a
* subset of mask found earlier. If this happens, then the mask found
* later will never get used because scanning the array is stopped when
* the first suitable mask is found. Drivers should order the array of
* available masks in the order of preference (presumably the least
* costy to access masks first).
*/
for (i = 0; i < num_masks - 1; i++) {
const unsigned long *mask1;
int j;
mask1 = av_masks + i * longs_per_mask;
for (j = i + 1; j < num_masks; j++) {
const unsigned long *mask2;
mask2 = av_masks + j * longs_per_mask;
if (bitmap_subset(mask2, mask1, masklength))
dev_warn(indio_dev->dev.parent,
"available_scan_mask %d subset of %d. Never used\n",
j, i);
}
}
}
int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
@ -1934,6 +1997,9 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
goto error_unreg_debugfs;
}
if (indio_dev->available_scan_masks)
iio_sanity_check_avail_scan_masks(indio_dev);
ret = iio_device_register_sysfs(indio_dev);
if (ret) {
dev_err(indio_dev->dev.parent,

View File

@ -252,6 +252,21 @@ config ISL29125
To compile this driver as a module, choose M here: the module will be
called isl29125.
config ISL76682
tristate "Intersil ISL76682 Light Sensor"
depends on I2C
select REGMAP_I2C
help
Say Y here if you want to build a driver for the Intersil ISL76682
Ambient Light Sensor and IR Intensity sensor. This driver provides
the readouts via standard IIO sysfs and device interface. Both ALS
illuminance and IR illuminance are provided raw with separate scale
setting which can be configured via sysfs, the default scale is 1000
lux, other options are 4000/16000/64000 lux.
To compile this driver as a module, choose M here: the module will be
called isl76682.
config HID_SENSOR_ALS
depends on HID_SENSOR_HUB
select IIO_BUFFER
@ -347,6 +362,17 @@ config SENSORS_LM3533
changes. The ALS-control output values can be set per zone for the
three current output channels.
config LTR390
tristate "LTR-390UV-01 ambient light and UV sensor"
depends on I2C
select REGMAP_I2C
help
If you say yes here you get support for the Lite-On LTR-390UV-01
ambient light and UV sensor.
This driver can also be built as a module. If so, the module
will be called ltr390.
config LTR501
tristate "LTR-501ALS-01 light sensor"
depends on I2C
@ -637,6 +663,17 @@ config VEML6070
To compile this driver as a module, choose M here: the
module will be called veml6070.
config VEML6075
tristate "VEML6075 UVA and UVB light sensor"
select REGMAP_I2C
depends on I2C
help
Say Y here if you want to build a driver for the Vishay VEML6075 UVA
and UVB light sensor.
To compile this driver as a module, choose M here: the
module will be called veml6075.
config VL6180
tristate "VL6180 ALS, range and proximity sensor"
depends on I2C

View File

@ -28,8 +28,10 @@ obj-$(CONFIG_IQS621_ALS) += iqs621-als.o
obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o
obj-$(CONFIG_ISL29125) += isl29125.o
obj-$(CONFIG_ISL76682) += isl76682.o
obj-$(CONFIG_JSA1212) += jsa1212.o
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
obj-$(CONFIG_LTR390) += ltr390.o
obj-$(CONFIG_LTR501) += ltr501.o
obj-$(CONFIG_LTRF216A) += ltrf216a.o
obj-$(CONFIG_LV0104CS) += lv0104cs.o
@ -60,5 +62,6 @@ obj-$(CONFIG_VCNL4000) += vcnl4000.o
obj-$(CONFIG_VCNL4035) += vcnl4035.o
obj-$(CONFIG_VEML6030) += veml6030.o
obj-$(CONFIG_VEML6070) += veml6070.o
obj-$(CONFIG_VEML6075) += veml6075.o
obj-$(CONFIG_VL6180) += vl6180.o
obj-$(CONFIG_ZOPT2201) += zopt2201.o

View File

@ -0,0 +1,345 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* IIO driver for the light sensor ISL76682.
* ISL76682 is Ambient Light Sensor
*
* Copyright (c) 2023 Marek Vasut <marex@denx.de>
*/
#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/types.h>
#include <linux/iio/iio.h>
#define ISL76682_REG_COMMAND 0x00
#define ISL76682_COMMAND_EN BIT(7)
#define ISL76682_COMMAND_MODE_CONTINUOUS BIT(6)
#define ISL76682_COMMAND_LIGHT_IR BIT(5)
#define ISL76682_COMMAND_RANGE_LUX_1K 0x0
#define ISL76682_COMMAND_RANGE_LUX_4K 0x1
#define ISL76682_COMMAND_RANGE_LUX_16K 0x2
#define ISL76682_COMMAND_RANGE_LUX_64K 0x3
#define ISL76682_COMMAND_RANGE_LUX_MASK GENMASK(1, 0)
#define ISL76682_REG_ALSIR_L 0x01
#define ISL76682_REG_ALSIR_U 0x02
#define ISL76682_NUM_REGS (ISL76682_REG_ALSIR_U + 1)
#define ISL76682_CONV_TIME_MS 100
#define ISL76682_INT_TIME_US 90000
#define ISL76682_ADC_MAX (BIT(16) - 1)
struct isl76682_chip {
/*
* Lock to synchronize access to device command register
* and the content of range variable below.
*/
struct mutex lock;
struct regmap *regmap;
u8 range;
u8 command;
};
struct isl76682_range {
u8 range;
u32 als;
u32 ir;
};
static struct isl76682_range isl76682_range_table[] = {
{ ISL76682_COMMAND_RANGE_LUX_1K, 15000, 10500 },
{ ISL76682_COMMAND_RANGE_LUX_4K, 60000, 42000 },
{ ISL76682_COMMAND_RANGE_LUX_16K, 240000, 168000 },
{ ISL76682_COMMAND_RANGE_LUX_64K, 960000, 673000 }
};
static int isl76682_get(struct isl76682_chip *chip, bool mode_ir, int *data)
{
u8 command;
int ret;
command = ISL76682_COMMAND_EN | ISL76682_COMMAND_MODE_CONTINUOUS |
chip->range;
if (mode_ir)
command |= ISL76682_COMMAND_LIGHT_IR;
if (command != chip->command) {
ret = regmap_write(chip->regmap, ISL76682_REG_COMMAND, command);
if (ret)
return ret;
/* Need to wait for conversion time if ALS/IR mode enabled */
msleep(ISL76682_CONV_TIME_MS);
chip->command = command;
}
ret = regmap_bulk_read(chip->regmap, ISL76682_REG_ALSIR_L, data, 2);
*data &= ISL76682_ADC_MAX;
return ret;
}
static int isl76682_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct isl76682_chip *chip = iio_priv(indio_dev);
int i;
if (mask != IIO_CHAN_INFO_SCALE)
return -EINVAL;
if (val != 0)
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(isl76682_range_table); i++) {
if (chan->type == IIO_LIGHT && val2 != isl76682_range_table[i].als)
continue;
if (chan->type == IIO_INTENSITY && val2 != isl76682_range_table[i].ir)
continue;
scoped_guard(mutex, &chip->lock)
chip->range = isl76682_range_table[i].range;
return 0;
}
return -EINVAL;
}
static int isl76682_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct isl76682_chip *chip = iio_priv(indio_dev);
int ret;
int i;
guard(mutex)(&chip->lock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
switch (chan->type) {
case IIO_LIGHT:
ret = isl76682_get(chip, false, val);
return (ret < 0) ? ret : IIO_VAL_INT;
case IIO_INTENSITY:
ret = isl76682_get(chip, true, val);
return (ret < 0) ? ret : IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SCALE:
for (i = 0; i < ARRAY_SIZE(isl76682_range_table); i++) {
if (chip->range != isl76682_range_table[i].range)
continue;
*val = 0;
switch (chan->type) {
case IIO_LIGHT:
*val2 = isl76682_range_table[i].als;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_INTENSITY:
*val2 = isl76682_range_table[i].ir;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
return -EINVAL;
case IIO_CHAN_INFO_INT_TIME:
*val = 0;
*val2 = ISL76682_INT_TIME_US;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static int illuminance_scale_available[] = {
0, 15000,
0, 60000,
0, 240000,
0, 960000,
};
static int intensity_scale_available[] = {
0, 10500,
0, 42000,
0, 168000,
0, 673000,
};
static int isl76682_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type,
int *length, long mask)
{
switch (mask) {
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_LIGHT:
*vals = illuminance_scale_available;
*length = ARRAY_SIZE(illuminance_scale_available);
*type = IIO_VAL_INT_PLUS_MICRO;
return IIO_AVAIL_LIST;
case IIO_INTENSITY:
*vals = intensity_scale_available;
*length = ARRAY_SIZE(intensity_scale_available);
*type = IIO_VAL_INT_PLUS_MICRO;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static const struct iio_chan_spec isl76682_channels[] = {
{
.type = IIO_LIGHT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
}, {
.type = IIO_INTENSITY,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
}
};
static const struct iio_info isl76682_info = {
.read_avail = isl76682_read_avail,
.read_raw = isl76682_read_raw,
.write_raw = isl76682_write_raw,
};
static int isl76682_clear_configure_reg(struct isl76682_chip *chip)
{
struct device *dev = regmap_get_device(chip->regmap);
int ret;
ret = regmap_write(chip->regmap, ISL76682_REG_COMMAND, 0x0);
if (ret < 0)
dev_err(dev, "Error %d clearing the CONFIGURE register\n", ret);
/*
* In the success case, the command register was zeroed out.
*
* In the error case, we do not know in which state the command
* register is, so we assume it is zeroed out, so that it would
* be reprogrammed at the next data read out, and at that time
* we hope it would be reprogrammed successfully. That is very
* much a best effort approach.
*/
chip->command = 0;
return ret;
}
static void isl76682_reset_action(void *chip)
{
isl76682_clear_configure_reg(chip);
}
static bool isl76682_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case ISL76682_REG_ALSIR_L:
case ISL76682_REG_ALSIR_U:
return true;
default:
return false;
}
}
static const struct regmap_config isl76682_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.volatile_reg = isl76682_is_volatile_reg,
.max_register = ISL76682_NUM_REGS - 1,
.num_reg_defaults_raw = ISL76682_NUM_REGS,
.cache_type = REGCACHE_FLAT,
};
static int isl76682_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct isl76682_chip *chip;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
if (!indio_dev)
return -ENOMEM;
chip = iio_priv(indio_dev);
mutex_init(&chip->lock);
chip->regmap = devm_regmap_init_i2c(client, &isl76682_regmap_config);
ret = PTR_ERR_OR_ZERO(chip->regmap);
if (ret)
return dev_err_probe(dev, ret, "Error initializing regmap\n");
chip->range = ISL76682_COMMAND_RANGE_LUX_1K;
ret = isl76682_clear_configure_reg(chip);
if (ret < 0)
return ret;
ret = devm_add_action_or_reset(dev, isl76682_reset_action, chip);
if (ret)
return ret;
indio_dev->info = &isl76682_info;
indio_dev->channels = isl76682_channels;
indio_dev->num_channels = ARRAY_SIZE(isl76682_channels);
indio_dev->name = "isl76682";
indio_dev->modes = INDIO_DIRECT_MODE;
return devm_iio_device_register(dev, indio_dev);
}
static const struct i2c_device_id isl76682_id[] = {
{ "isl76682" },
{ }
};
MODULE_DEVICE_TABLE(i2c, isl76682_id);
static const struct of_device_id isl76682_of_match[] = {
{ .compatible = "isil,isl76682" },
{ }
};
MODULE_DEVICE_TABLE(of, isl76682_of_match);
static struct i2c_driver isl76682_driver = {
.driver = {
.name = "isl76682",
.of_match_table = isl76682_of_match,
},
.probe = isl76682_probe,
.id_table = isl76682_id,
};
module_i2c_driver(isl76682_driver);
MODULE_DESCRIPTION("ISL76682 Ambient Light Sensor driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marek Vasut <marex@denx.de>");

196
drivers/iio/light/ltr390.c Normal file
View File

@ -0,0 +1,196 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* IIO driver for Lite-On LTR390 ALS and UV sensor
* (7-bit I2C slave address 0x53)
*
* Based on the work of:
* Shreeya Patel and Shi Zhigang (LTRF216 Driver)
*
* Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com>
*
* Datasheet:
* https://optoelectronics.liteon.com/upload/download/DS86-2015-0004/LTR-390UV_Final_%20DS_V1%201.pdf
*
* TODO:
* - Support for configurable gain and resolution
* - Sensor suspend/resume support
* - Add support for reading the ALS
* - Interrupt support
*/
#include <linux/i2c.h>
#include <linux/math.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <asm/unaligned.h>
#define LTR390_MAIN_CTRL 0x00
#define LTR390_PART_ID 0x06
#define LTR390_UVS_DATA 0x10
#define LTR390_SW_RESET BIT(4)
#define LTR390_UVS_MODE BIT(3)
#define LTR390_SENSOR_ENABLE BIT(1)
#define LTR390_PART_NUMBER_ID 0xb
/*
* At 20-bit resolution (integration time: 400ms) and 18x gain, 2300 counts of
* the sensor are equal to 1 UV Index [Datasheet Page#8].
*
* For the default resolution of 18-bit (integration time: 100ms) and default
* gain of 3x, the counts/uvi are calculated as follows:
* 2300 / ((3/18) * (100/400)) = 95.83
*/
#define LTR390_COUNTS_PER_UVI 96
/*
* Window Factor is needed when the device is under Window glass with coated
* tinted ink. This is to compensate for the light loss due to the lower
* transmission rate of the window glass and helps * in calculating lux.
*/
#define LTR390_WINDOW_FACTOR 1
struct ltr390_data {
struct regmap *regmap;
struct i2c_client *client;
/* Protects device from simulataneous reads */
struct mutex lock;
};
static const struct regmap_config ltr390_regmap_config = {
.name = "ltr390",
.reg_bits = 8,
.reg_stride = 1,
.val_bits = 8,
};
static int ltr390_register_read(struct ltr390_data *data, u8 register_address)
{
struct device *dev = &data->client->dev;
int ret;
u8 recieve_buffer[3];
guard(mutex)(&data->lock);
ret = regmap_bulk_read(data->regmap, register_address, recieve_buffer,
sizeof(recieve_buffer));
if (ret) {
dev_err(dev, "failed to read measurement data");
return ret;
}
return get_unaligned_le24(recieve_buffer);
}
static int ltr390_read_raw(struct iio_dev *iio_device,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
int ret;
struct ltr390_data *data = iio_priv(iio_device);
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = ltr390_register_read(data, LTR390_UVS_DATA);
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = LTR390_WINDOW_FACTOR;
*val2 = LTR390_COUNTS_PER_UVI;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
}
static const struct iio_info ltr390_info = {
.read_raw = ltr390_read_raw,
};
static const struct iio_chan_spec ltr390_channel = {
.type = IIO_UVINDEX,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)
};
static int ltr390_probe(struct i2c_client *client)
{
struct ltr390_data *data;
struct iio_dev *indio_dev;
struct device *dev;
int ret, part_number;
dev = &client->dev;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->regmap = devm_regmap_init_i2c(client, &ltr390_regmap_config);
if (IS_ERR(data->regmap))
return dev_err_probe(dev, PTR_ERR(data->regmap),
"regmap initialization failed\n");
data->client = client;
mutex_init(&data->lock);
indio_dev->info = &ltr390_info;
indio_dev->channels = &ltr390_channel;
indio_dev->num_channels = 1;
indio_dev->name = "ltr390";
ret = regmap_read(data->regmap, LTR390_PART_ID, &part_number);
if (ret)
return dev_err_probe(dev, ret,
"failed to get sensor's part id\n");
/* Lower 4 bits of `part_number` change with hardware revisions */
if (part_number >> 4 != LTR390_PART_NUMBER_ID)
dev_info(dev, "received invalid product id: 0x%x", part_number);
dev_dbg(dev, "LTR390, product id: 0x%x\n", part_number);
/* reset sensor, chip fails to respond to this, so ignore any errors */
regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SW_RESET);
/* Wait for the registers to reset before proceeding */
usleep_range(1000, 2000);
ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL,
LTR390_SENSOR_ENABLE | LTR390_UVS_MODE);
if (ret)
return dev_err_probe(dev, ret, "failed to enable the sensor\n");
return devm_iio_device_register(dev, indio_dev);
}
static const struct i2c_device_id ltr390_id[] = {
{ "ltr390" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, ltr390_id);
static const struct of_device_id ltr390_of_table[] = {
{ .compatible = "liteon,ltr390" },
{ /* Sentinel */ }
};
MODULE_DEVICE_TABLE(of, ltr390_of_table);
static struct i2c_driver ltr390_driver = {
.driver = {
.name = "ltr390",
.of_match_table = ltr390_of_table,
},
.probe = ltr390_probe,
.id_table = ltr390_id,
};
module_i2c_driver(ltr390_driver);
MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>");
MODULE_DESCRIPTION("Lite-On LTR390 ALS and UV sensor Driver");
MODULE_LICENSE("GPL");

View File

@ -234,7 +234,7 @@ static int ltrf216a_read_data(struct ltrf216a_data *data, u8 addr)
static int ltrf216a_get_lux(struct ltrf216a_data *data)
{
int ret, greendata;
u64 lux, div;
u64 lux;
ret = ltrf216a_set_power_state(data, true);
if (ret)
@ -246,10 +246,9 @@ static int ltrf216a_get_lux(struct ltrf216a_data *data)
ltrf216a_set_power_state(data, false);
lux = greendata * 45 * LTRF216A_WIN_FAC * 100;
div = data->als_gain_fac * data->int_time_fac * 100;
lux = greendata * 45 * LTRF216A_WIN_FAC;
return div_u64(lux, div);
return lux;
}
static int ltrf216a_read_raw(struct iio_dev *indio_dev,
@ -279,7 +278,8 @@ static int ltrf216a_read_raw(struct iio_dev *indio_dev,
if (ret < 0)
return ret;
*val = ret;
return IIO_VAL_INT;
*val2 = data->als_gain_fac * data->int_time_fac;
return IIO_VAL_FRACTIONAL;
case IIO_CHAN_INFO_INT_TIME:
mutex_lock(&data->lock);
ret = ltrf216a_get_int_time(data, val, val2);

View File

@ -472,7 +472,7 @@ static struct i2c_driver pa12203001_driver = {
.driver = {
.name = PA12203001_DRIVER_NAME,
.pm = &pa12203001_pm_ops,
.acpi_match_table = ACPI_PTR(pa12203001_acpi_match),
.acpi_match_table = pa12203001_acpi_match,
},
.probe = pa12203001_probe,
.remove = pa12203001_remove,

View File

@ -130,6 +130,7 @@
* @BU27008_BLUE: Blue channel. Via data2 (when used).
* @BU27008_CLEAR: Clear channel. Via data2 or data3 (when used).
* @BU27008_IR: IR channel. Via data3 (when used).
* @BU27008_LUX: Illuminance channel, computed using RGB and IR.
* @BU27008_NUM_CHANS: Number of channel types.
*/
enum bu27008_chan_type {
@ -138,6 +139,7 @@ enum bu27008_chan_type {
BU27008_BLUE,
BU27008_CLEAR,
BU27008_IR,
BU27008_LUX,
BU27008_NUM_CHANS
};
@ -172,6 +174,8 @@ static const unsigned long bu27008_scan_masks[] = {
ALWAYS_SCANNABLE | BIT(BU27008_CLEAR) | BIT(BU27008_IR),
/* buffer is R, G, B, IR */
ALWAYS_SCANNABLE | BIT(BU27008_BLUE) | BIT(BU27008_IR),
/* buffer is R, G, B, IR, LUX */
ALWAYS_SCANNABLE | BIT(BU27008_BLUE) | BIT(BU27008_IR) | BIT(BU27008_LUX),
0
};
@ -331,6 +335,19 @@ static const struct iio_chan_spec bu27008_channels[] = {
* Hence we don't advertise available ones either.
*/
BU27008_CHAN(IR, DATA3, 0),
{
.type = IIO_LIGHT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.channel = BU27008_LUX,
.scan_index = BU27008_LUX,
.scan_type = {
.sign = 'u',
.realbits = 64,
.storagebits = 64,
.endianness = IIO_CPU,
},
},
IIO_CHAN_SOFT_TIMESTAMP(BU27008_NUM_CHANS),
};
@ -1004,6 +1021,169 @@ static int bu27008_read_one(struct bu27008_data *data, struct iio_dev *idev,
return ret;
}
#define BU27008_LUX_DATA_RED 0
#define BU27008_LUX_DATA_GREEN 1
#define BU27008_LUX_DATA_BLUE 2
#define BU27008_LUX_DATA_IR 3
#define LUX_DATA_SIZE (BU27008_NUM_HW_CHANS * sizeof(__le16))
static int bu27008_read_lux_chans(struct bu27008_data *data, unsigned int time,
__le16 *chan_data)
{
int ret, chan_sel, tmpret, valid;
chan_sel = BU27008_BLUE2_IR3 << (ffs(data->cd->chan_sel_mask) - 1);
ret = regmap_update_bits(data->regmap, data->cd->chan_sel_reg,
data->cd->chan_sel_mask, chan_sel);
if (ret)
return ret;
ret = bu27008_meas_set(data, true);
if (ret)
return ret;
msleep(time / USEC_PER_MSEC);
ret = regmap_read_poll_timeout(data->regmap, data->cd->valid_reg,
valid, (valid & BU27008_MASK_VALID),
BU27008_VALID_RESULT_WAIT_QUANTA_US,
BU27008_MAX_VALID_RESULT_WAIT_US);
if (ret)
goto out;
ret = regmap_bulk_read(data->regmap, BU27008_REG_DATA0_LO, chan_data,
LUX_DATA_SIZE);
if (ret)
goto out;
out:
tmpret = bu27008_meas_set(data, false);
if (tmpret)
dev_warn(data->dev, "Stopping measurement failed\n");
return ret;
}
/*
* Following equation for computing lux out of register values was given by
* ROHM HW colleagues;
*
* Red = RedData*1024 / Gain * 20 / meas_mode
* Green = GreenData* 1024 / Gain * 20 / meas_mode
* Blue = BlueData* 1024 / Gain * 20 / meas_mode
* IR = IrData* 1024 / Gain * 20 / meas_mode
*
* where meas_mode is the integration time in mS / 10
*
* IRratio = (IR > 0.18 * Green) ? 0 : 1
*
* Lx = max(c1*Red + c2*Green + c3*Blue,0)
*
* for
* IRratio 0: c1 = -0.00002237, c2 = 0.0003219, c3 = -0.000120371
* IRratio 1: c1 = -0.00001074, c2 = 0.000305415, c3 = -0.000129367
*/
/*
* The max chan data is 0xffff. When we multiply it by 1024 * 20, we'll get
* 0x4FFFB000 which still fits in 32-bit integer. This won't overflow.
*/
#define NORM_CHAN_DATA_FOR_LX_CALC(chan, gain, time) (le16_to_cpu(chan) * \
1024 * 20 / (gain) / (time))
static u64 bu27008_calc_nlux(struct bu27008_data *data, __le16 *lux_data,
unsigned int gain, unsigned int gain_ir, unsigned int time)
{
unsigned int red, green, blue, ir;
s64 c1, c2, c3, nlux;
time /= 10000;
ir = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_IR], gain_ir, time);
red = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_RED], gain, time);
green = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_GREEN], gain, time);
blue = NORM_CHAN_DATA_FOR_LX_CALC(lux_data[BU27008_LUX_DATA_BLUE], gain, time);
if ((u64)ir * 100LLU > (u64)green * 18LLU) {
c1 = -22370;
c2 = 321900;
c3 = -120371;
} else {
c1 = -10740;
c2 = 305415;
c3 = -129367;
}
nlux = c1 * red + c2 * green + c3 * blue;
return max_t(s64, 0, nlux);
}
static int bu27008_get_time_n_gains(struct bu27008_data *data,
unsigned int *gain, unsigned int *gain_ir, unsigned int *time)
{
int ret;
ret = bu27008_get_gain(data, &data->gts, gain);
if (ret < 0)
return ret;
ret = bu27008_get_gain(data, &data->gts_ir, gain_ir);
if (ret < 0)
return ret;
ret = bu27008_get_int_time_us(data);
if (ret < 0)
return ret;
/* Max integration time is 400000. Fits in signed int. */
*time = ret;
return 0;
}
struct bu27008_buf {
__le16 chan[BU27008_NUM_HW_CHANS];
u64 lux __aligned(8);
s64 ts __aligned(8);
};
static int bu27008_buffer_fill_lux(struct bu27008_data *data,
struct bu27008_buf *raw)
{
unsigned int gain, gain_ir, time;
int ret;
ret = bu27008_get_time_n_gains(data, &gain, &gain_ir, &time);
if (ret)
return ret;
raw->lux = bu27008_calc_nlux(data, raw->chan, gain, gain_ir, time);
return 0;
}
static int bu27008_read_lux(struct bu27008_data *data, struct iio_dev *idev,
struct iio_chan_spec const *chan,
int *val, int *val2)
{
__le16 lux_data[BU27008_NUM_HW_CHANS];
unsigned int gain, gain_ir, time;
u64 nlux;
int ret;
ret = bu27008_get_time_n_gains(data, &gain, &gain_ir, &time);
if (ret)
return ret;
ret = bu27008_read_lux_chans(data, time, lux_data);
if (ret)
return ret;
nlux = bu27008_calc_nlux(data, lux_data, gain, gain_ir, time);
*val = (int)nlux;
*val2 = nlux >> 32LLU;
return IIO_VAL_INT_64;
}
static int bu27008_read_raw(struct iio_dev *idev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
@ -1018,7 +1198,10 @@ static int bu27008_read_raw(struct iio_dev *idev,
return -EBUSY;
mutex_lock(&data->mutex);
ret = bu27008_read_one(data, idev, chan, val, val2);
if (chan->type == IIO_LIGHT)
ret = bu27008_read_lux(data, idev, chan, val, val2);
else
ret = bu27008_read_one(data, idev, chan, val, val2);
mutex_unlock(&data->mutex);
iio_device_release_direct_mode(idev);
@ -1026,6 +1209,11 @@ static int bu27008_read_raw(struct iio_dev *idev,
return ret;
case IIO_CHAN_INFO_SCALE:
if (chan->type == IIO_LIGHT) {
*val = 0;
*val2 = 1;
return IIO_VAL_INT_PLUS_NANO;
}
ret = bu27008_get_scale(data, chan->scan_index == BU27008_IR,
val, val2);
if (ret)
@ -1236,10 +1424,7 @@ static irqreturn_t bu27008_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *idev = pf->indio_dev;
struct bu27008_data *data = iio_priv(idev);
struct {
__le16 chan[BU27008_NUM_HW_CHANS];
s64 ts __aligned(8);
} raw;
struct bu27008_buf raw;
int ret, dummy;
memset(&raw, 0, sizeof(raw));
@ -1257,6 +1442,12 @@ static irqreturn_t bu27008_trigger_handler(int irq, void *p)
if (ret < 0)
goto err_read;
if (test_bit(BU27008_LUX, idev->active_scan_mask)) {
ret = bu27008_buffer_fill_lux(data, &raw);
if (ret)
goto err_read;
}
iio_push_to_buffers_with_timestamp(idev, &raw, pf->timestamp);
err_read:
iio_trigger_notify_done(idev->trig);

View File

@ -0,0 +1,474 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Vishay VEML6075 UVA and UVB light sensor
*
* Copyright 2023 Javier Carrasco <javier.carrasco.cruz@gmail.com>
*
* 7-bit I2C slave, address 0x10
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/units.h>
#include <linux/iio/iio.h>
#define VEML6075_CMD_CONF 0x00 /* configuration register */
#define VEML6075_CMD_UVA 0x07 /* UVA channel */
#define VEML6075_CMD_UVB 0x09 /* UVB channel */
#define VEML6075_CMD_COMP1 0x0A /* visible light compensation */
#define VEML6075_CMD_COMP2 0x0B /* infrarred light compensation */
#define VEML6075_CMD_ID 0x0C /* device ID */
#define VEML6075_CONF_IT GENMASK(6, 4) /* intregration time */
#define VEML6075_CONF_HD BIT(3) /* dynamic setting */
#define VEML6075_CONF_TRIG BIT(2) /* trigger */
#define VEML6075_CONF_AF BIT(1) /* active force enable */
#define VEML6075_CONF_SD BIT(0) /* shutdown */
#define VEML6075_IT_50_MS 0x00
#define VEML6075_IT_100_MS 0x01
#define VEML6075_IT_200_MS 0x02
#define VEML6075_IT_400_MS 0x03
#define VEML6075_IT_800_MS 0x04
#define VEML6075_AF_DISABLE 0x00
#define VEML6075_AF_ENABLE 0x01
#define VEML6075_SD_DISABLE 0x00
#define VEML6075_SD_ENABLE 0x01
/* Open-air coefficients and responsivity */
#define VEML6075_A_COEF 2220
#define VEML6075_B_COEF 1330
#define VEML6075_C_COEF 2950
#define VEML6075_D_COEF 1740
#define VEML6075_UVA_RESP 1461
#define VEML6075_UVB_RESP 2591
static const int veml6075_it_ms[] = { 50, 100, 200, 400, 800 };
struct veml6075_data {
struct i2c_client *client;
struct regmap *regmap;
/*
* prevent integration time modification and triggering
* measurements while a measurement is underway.
*/
struct mutex lock;
};
/* channel number */
enum veml6075_chan {
CH_UVA,
CH_UVB,
};
static const struct iio_chan_spec veml6075_channels[] = {
{
.type = IIO_INTENSITY,
.channel = CH_UVA,
.modified = 1,
.channel2 = IIO_MOD_LIGHT_UVA,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
},
{
.type = IIO_INTENSITY,
.channel = CH_UVB,
.modified = 1,
.channel2 = IIO_MOD_LIGHT_UVB,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
},
{
.type = IIO_UVINDEX,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
.info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME),
},
};
static int veml6075_request_measurement(struct veml6075_data *data)
{
int ret, conf, int_time;
ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf);
if (ret < 0)
return ret;
/* disable shutdown and trigger measurement */
ret = regmap_write(data->regmap, VEML6075_CMD_CONF,
(conf | VEML6075_CONF_TRIG) & ~VEML6075_CONF_SD);
if (ret < 0)
return ret;
/*
* A measurement requires between 1.30 and 1.40 times the integration
* time for all possible configurations. Using a 1.50 factor simplifies
* operations and ensures reliability under all circumstances.
*/
int_time = veml6075_it_ms[FIELD_GET(VEML6075_CONF_IT, conf)];
msleep(int_time + (int_time / 2));
/* shutdown again, data registers are still accessible */
return regmap_update_bits(data->regmap, VEML6075_CMD_CONF,
VEML6075_CONF_SD, VEML6075_CONF_SD);
}
static int veml6075_uva_comp(int raw_uva, int comp1, int comp2)
{
int comp1a_c, comp2a_c, uva_comp;
comp1a_c = (comp1 * VEML6075_A_COEF) / 1000U;
comp2a_c = (comp2 * VEML6075_B_COEF) / 1000U;
uva_comp = raw_uva - comp1a_c - comp2a_c;
return clamp_val(uva_comp, 0, U16_MAX);
}
static int veml6075_uvb_comp(int raw_uvb, int comp1, int comp2)
{
int comp1b_c, comp2b_c, uvb_comp;
comp1b_c = (comp1 * VEML6075_C_COEF) / 1000U;
comp2b_c = (comp2 * VEML6075_D_COEF) / 1000U;
uvb_comp = raw_uvb - comp1b_c - comp2b_c;
return clamp_val(uvb_comp, 0, U16_MAX);
}
static int veml6075_read_comp(struct veml6075_data *data, int *c1, int *c2)
{
int ret;
ret = regmap_read(data->regmap, VEML6075_CMD_COMP1, c1);
if (ret < 0)
return ret;
return regmap_read(data->regmap, VEML6075_CMD_COMP2, c2);
}
static int veml6075_read_uv_direct(struct veml6075_data *data, int chan,
int *val)
{
int c1, c2, ret;
guard(mutex)(&data->lock);
ret = veml6075_request_measurement(data);
if (ret < 0)
return ret;
ret = veml6075_read_comp(data, &c1, &c2);
if (ret < 0)
return ret;
switch (chan) {
case CH_UVA:
ret = regmap_read(data->regmap, VEML6075_CMD_UVA, val);
if (ret < 0)
return ret;
*val = veml6075_uva_comp(*val, c1, c2);
return IIO_VAL_INT;
case CH_UVB:
ret = regmap_read(data->regmap, VEML6075_CMD_UVB, val);
if (ret < 0)
return ret;
*val = veml6075_uvb_comp(*val, c1, c2);
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int veml6075_read_int_time_index(struct veml6075_data *data)
{
int ret, conf;
ret = regmap_read(data->regmap, VEML6075_CMD_CONF, &conf);
if (ret < 0)
return ret;
return FIELD_GET(VEML6075_CONF_IT, conf);
}
static int veml6075_read_int_time_ms(struct veml6075_data *data, int *val)
{
int int_index;
guard(mutex)(&data->lock);
int_index = veml6075_read_int_time_index(data);
if (int_index < 0)
return int_index;
*val = veml6075_it_ms[int_index];
return IIO_VAL_INT;
}
static int veml6075_get_uvi_micro(struct veml6075_data *data, int uva_comp,
int uvb_comp)
{
int uvia_micro = uva_comp * VEML6075_UVA_RESP;
int uvib_micro = uvb_comp * VEML6075_UVB_RESP;
int int_index;
int_index = veml6075_read_int_time_index(data);
if (int_index < 0)
return int_index;
switch (int_index) {
case VEML6075_IT_50_MS:
return uvia_micro + uvib_micro;
case VEML6075_IT_100_MS:
case VEML6075_IT_200_MS:
case VEML6075_IT_400_MS:
case VEML6075_IT_800_MS:
return (uvia_micro + uvib_micro) / (2 << int_index);
default:
return -EINVAL;
}
}
static int veml6075_read_uvi(struct veml6075_data *data, int *val, int *val2)
{
int ret, c1, c2, uva, uvb, uvi_micro;
guard(mutex)(&data->lock);
ret = veml6075_request_measurement(data);
if (ret < 0)
return ret;
ret = veml6075_read_comp(data, &c1, &c2);
if (ret < 0)
return ret;
ret = regmap_read(data->regmap, VEML6075_CMD_UVA, &uva);
if (ret < 0)
return ret;
ret = regmap_read(data->regmap, VEML6075_CMD_UVB, &uvb);
if (ret < 0)
return ret;
uvi_micro = veml6075_get_uvi_micro(data, veml6075_uva_comp(uva, c1, c2),
veml6075_uvb_comp(uvb, c1, c2));
if (uvi_micro < 0)
return uvi_micro;
*val = uvi_micro / MICRO;
*val2 = uvi_micro % MICRO;
return IIO_VAL_INT_PLUS_MICRO;
}
static int veml6075_read_responsivity(int chan, int *val, int *val2)
{
/* scale = 1 / resp */
switch (chan) {
case CH_UVA:
/* resp = 0.93 c/uW/cm2: scale = 1.75268817 */
*val = 1;
*val2 = 75268817;
return IIO_VAL_INT_PLUS_NANO;
case CH_UVB:
/* resp = 2.1 c/uW/cm2: scale = 0.476190476 */
*val = 0;
*val2 = 476190476;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
}
static int veml6075_read_avail(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
const int **vals, int *type, int *length,
long mask)
{
switch (mask) {
case IIO_CHAN_INFO_INT_TIME:
*length = ARRAY_SIZE(veml6075_it_ms);
*vals = veml6075_it_ms;
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
}
static int veml6075_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct veml6075_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
return veml6075_read_uv_direct(data, chan->channel, val);
case IIO_CHAN_INFO_PROCESSED:
return veml6075_read_uvi(data, val, val2);
case IIO_CHAN_INFO_INT_TIME:
return veml6075_read_int_time_ms(data, val);
case IIO_CHAN_INFO_SCALE:
return veml6075_read_responsivity(chan->channel, val, val2);
default:
return -EINVAL;
}
}
static int veml6075_write_int_time_ms(struct veml6075_data *data, int val)
{
int i = ARRAY_SIZE(veml6075_it_ms);
guard(mutex)(&data->lock);
while (i-- > 0) {
if (val == veml6075_it_ms[i])
break;
}
if (i < 0)
return -EINVAL;
return regmap_update_bits(data->regmap, VEML6075_CMD_CONF,
VEML6075_CONF_IT,
FIELD_PREP(VEML6075_CONF_IT, i));
}
static int veml6075_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct veml6075_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_INT_TIME:
return veml6075_write_int_time_ms(data, val);
default:
return -EINVAL;
}
}
static const struct iio_info veml6075_info = {
.read_avail = veml6075_read_avail,
.read_raw = veml6075_read_raw,
.write_raw = veml6075_write_raw,
};
static bool veml6075_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case VEML6075_CMD_CONF:
case VEML6075_CMD_UVA:
case VEML6075_CMD_UVB:
case VEML6075_CMD_COMP1:
case VEML6075_CMD_COMP2:
case VEML6075_CMD_ID:
return true;
default:
return false;
}
}
static bool veml6075_writable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case VEML6075_CMD_CONF:
return true;
default:
return false;
}
}
static const struct regmap_config veml6075_regmap_config = {
.name = "veml6075",
.reg_bits = 8,
.val_bits = 16,
.max_register = VEML6075_CMD_ID,
.readable_reg = veml6075_readable_reg,
.writeable_reg = veml6075_writable_reg,
.val_format_endian = REGMAP_ENDIAN_LITTLE,
};
static int veml6075_probe(struct i2c_client *client)
{
struct veml6075_data *data;
struct iio_dev *indio_dev;
struct regmap *regmap;
int config, ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
regmap = devm_regmap_init_i2c(client, &veml6075_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
data = iio_priv(indio_dev);
data->client = client;
data->regmap = regmap;
mutex_init(&data->lock);
indio_dev->name = "veml6075";
indio_dev->info = &veml6075_info;
indio_dev->channels = veml6075_channels;
indio_dev->num_channels = ARRAY_SIZE(veml6075_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
ret = devm_regulator_get_enable(&client->dev, "vdd");
if (ret < 0)
return ret;
/* default: 100ms integration time, active force enable, shutdown */
config = FIELD_PREP(VEML6075_CONF_IT, VEML6075_IT_100_MS) |
FIELD_PREP(VEML6075_CONF_AF, VEML6075_AF_ENABLE) |
FIELD_PREP(VEML6075_CONF_SD, VEML6075_SD_ENABLE);
ret = regmap_write(data->regmap, VEML6075_CMD_CONF, config);
if (ret < 0)
return ret;
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id veml6075_id[] = {
{ "veml6075" },
{ }
};
MODULE_DEVICE_TABLE(i2c, veml6075_id);
static const struct of_device_id veml6075_of_match[] = {
{ .compatible = "vishay,veml6075" },
{}
};
MODULE_DEVICE_TABLE(of, veml6075_of_match);
static struct i2c_driver veml6075_driver = {
.driver = {
.name = "veml6075",
.of_match_table = veml6075_of_match,
},
.probe = veml6075_probe,
.id_table = veml6075_id,
};
module_i2c_driver(veml6075_driver);
MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gmail.com>");
MODULE_DESCRIPTION("Vishay VEML6075 UVA and UVB light sensor driver");
MODULE_LICENSE("GPL");

View File

@ -497,17 +497,13 @@ static int tmag5273_set_operating_mode(struct tmag5273_data *data,
static void tmag5273_read_device_property(struct tmag5273_data *data)
{
struct device *dev = data->dev;
const char *str;
int ret;
data->angle_measurement = TMAG5273_ANGLE_EN_X_Y;
ret = device_property_read_string(dev, "ti,angle-measurement", &str);
if (ret)
return;
ret = match_string(tmag5273_angle_names,
ARRAY_SIZE(tmag5273_angle_names), str);
ret = device_property_match_property_string(dev, "ti,angle-measurement",
tmag5273_angle_names,
ARRAY_SIZE(tmag5273_angle_names));
if (ret >= 0)
data->angle_measurement = ret;
}

View File

@ -109,6 +109,28 @@ config HP03
To compile this driver as a module, choose M here: the module
will be called hp03.
config HSC030PA
tristate "Honeywell HSC/SSC TruStability pressure sensor series"
depends on (I2C || SPI_MASTER)
select HSC030PA_I2C if I2C
select HSC030PA_SPI if SPI_MASTER
help
Say Y here to build support for the Honeywell TruStability
HSC and SSC pressure and temperature sensor series.
To compile this driver as a module, choose M here: the module
will be called hsc030pa.
config HSC030PA_I2C
tristate
depends on HSC030PA
depends on I2C
config HSC030PA_SPI
tristate
depends on HSC030PA
depends on SPI_MASTER
config ICP10100
tristate "InvenSense ICP-101xx pressure and temperature sensor"
depends on I2C

View File

@ -15,6 +15,9 @@ obj-$(CONFIG_DPS310) += dps310.o
obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
obj-$(CONFIG_HP03) += hp03.o
obj-$(CONFIG_HSC030PA) += hsc030pa.o
obj-$(CONFIG_HSC030PA_I2C) += hsc030pa_i2c.o
obj-$(CONFIG_HSC030PA_SPI) += hsc030pa_spi.o
obj-$(CONFIG_ICP10100) += icp10100.o
obj-$(CONFIG_MPL115) += mpl115.o
obj-$(CONFIG_MPL115_I2C) += mpl115_i2c.o

View File

@ -13,6 +13,7 @@
* https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf
* https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf
* https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp388-ds001.pdf
* https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf
* https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp581-ds004.pdf
*
* Notice:
@ -794,10 +795,12 @@ static int bmp280_chip_config(struct bmp280_data *data)
}
static const int bmp280_oversampling_avail[] = { 1, 2, 4, 8, 16 };
static const u8 bmp280_chip_ids[] = { BMP280_CHIP_ID };
const struct bmp280_chip_info bmp280_chip_info = {
.id_reg = BMP280_REG_ID,
.chip_id = BMP280_CHIP_ID,
.chip_id = bmp280_chip_ids,
.num_chip_id = ARRAY_SIZE(bmp280_chip_ids),
.regmap_config = &bmp280_regmap_config,
.start_up_time = 2000,
.channels = bmp280_channels,
@ -846,9 +849,12 @@ static int bme280_chip_config(struct bmp280_data *data)
return bmp280_chip_config(data);
}
static const u8 bme280_chip_ids[] = { BME280_CHIP_ID };
const struct bmp280_chip_info bme280_chip_info = {
.id_reg = BMP280_REG_ID,
.chip_id = BME280_CHIP_ID,
.chip_id = bme280_chip_ids,
.num_chip_id = ARRAY_SIZE(bme280_chip_ids),
.regmap_config = &bmp280_regmap_config,
.start_up_time = 2000,
.channels = bmp280_channels,
@ -920,7 +926,7 @@ static int bmp380_cmd(struct bmp280_data *data, u8 cmd)
}
/*
* Returns temperature in Celsius dregrees, resolution is 0.01º C. Output value of
* Returns temperature in Celsius degrees, resolution is 0.01º C. Output value of
* "5123" equals 51.2º C. t_fine carries fine temperature as global value.
*
* Taken from datasheet, Section Appendix 9, "Compensation formula" and repo
@ -1220,10 +1226,12 @@ static int bmp380_chip_config(struct bmp280_data *data)
static const int bmp380_oversampling_avail[] = { 1, 2, 4, 8, 16, 32 };
static const int bmp380_iir_filter_coeffs_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128};
static const u8 bmp380_chip_ids[] = { BMP380_CHIP_ID, BMP390_CHIP_ID };
const struct bmp280_chip_info bmp380_chip_info = {
.id_reg = BMP380_REG_ID,
.chip_id = BMP380_CHIP_ID,
.chip_id = bmp380_chip_ids,
.num_chip_id = ARRAY_SIZE(bmp380_chip_ids),
.regmap_config = &bmp380_regmap_config,
.start_up_time = 2000,
.channels = bmp380_channels,
@ -1385,7 +1393,7 @@ static int bmp580_read_temp(struct bmp280_data *data, int *val, int *val2)
/*
* Temperature is returned in Celsius degrees in fractional
* form down 2^16. We reescale by x1000 to return milli Celsius
* form down 2^16. We rescale by x1000 to return milli Celsius
* to respect IIO ABI.
*/
*val = raw_temp * 1000;
@ -1412,7 +1420,7 @@ static int bmp580_read_press(struct bmp280_data *data, int *val, int *val2)
}
/*
* Pressure is returned in Pascals in fractional form down 2^16.
* We reescale /1000 to convert to kilopascal to respect IIO ABI.
* We rescale /1000 to convert to kilopascal to respect IIO ABI.
*/
*val = raw_press;
*val2 = 64000; /* 2^6 * 1000 */
@ -1720,10 +1728,12 @@ static int bmp580_chip_config(struct bmp280_data *data)
}
static const int bmp580_oversampling_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
static const u8 bmp580_chip_ids[] = { BMP580_CHIP_ID, BMP580_CHIP_ID_ALT };
const struct bmp280_chip_info bmp580_chip_info = {
.id_reg = BMP580_REG_CHIP_ID,
.chip_id = BMP580_CHIP_ID,
.chip_id = bmp580_chip_ids,
.num_chip_id = ARRAY_SIZE(bmp580_chip_ids),
.regmap_config = &bmp580_regmap_config,
.start_up_time = 2000,
.channels = bmp380_channels,
@ -1983,10 +1993,12 @@ static int bmp180_chip_config(struct bmp280_data *data)
static const int bmp180_oversampling_temp_avail[] = { 1 };
static const int bmp180_oversampling_press_avail[] = { 1, 2, 4, 8 };
static const u8 bmp180_chip_ids[] = { BMP180_CHIP_ID };
const struct bmp280_chip_info bmp180_chip_info = {
.id_reg = BMP280_REG_ID,
.chip_id = BMP180_CHIP_ID,
.chip_id = bmp180_chip_ids,
.num_chip_id = ARRAY_SIZE(bmp180_chip_ids),
.regmap_config = &bmp180_regmap_config,
.start_up_time = 2000,
.channels = bmp280_channels,
@ -2077,6 +2089,7 @@ int bmp280_common_probe(struct device *dev,
struct bmp280_data *data;
struct gpio_desc *gpiod;
unsigned int chip_id;
unsigned int i;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
@ -2142,12 +2155,17 @@ int bmp280_common_probe(struct device *dev,
ret = regmap_read(regmap, data->chip_info->id_reg, &chip_id);
if (ret < 0)
return ret;
if (chip_id != data->chip_info->chip_id) {
dev_err(dev, "bad chip id: expected %x got %x\n",
data->chip_info->chip_id, chip_id);
return -EINVAL;
for (i = 0; i < data->chip_info->num_chip_id; i++) {
if (chip_id == data->chip_info->chip_id[i]) {
dev_info(dev, "0x%x is a known chip id for %s\n", chip_id, name);
break;
}
}
if (i == data->chip_info->num_chip_id)
dev_warn(dev, "bad chip id: 0x%x is not a known chip id\n", chip_id);
if (data->chip_info->preinit) {
ret = data->chip_info->preinit(data);
if (ret)

View File

@ -7,13 +7,11 @@
static int bmp280_i2c_probe(struct i2c_client *client)
{
struct regmap *regmap;
const struct bmp280_chip_info *chip_info;
const struct i2c_device_id *id = i2c_client_get_device_id(client);
const struct bmp280_chip_info *chip_info;
struct regmap *regmap;
chip_info = device_get_match_data(&client->dev);
if (!chip_info)
chip_info = (const struct bmp280_chip_info *) id->driver_data;
chip_info = i2c_get_match_data(client);
regmap = devm_regmap_init_i2c(client, chip_info->regmap_config);
if (IS_ERR(regmap)) {

View File

@ -14,8 +14,7 @@
static int bmp280_regmap_spi_write(void *context, const void *data,
size_t count)
{
struct device *dev = context;
struct spi_device *spi = to_spi_device(dev);
struct spi_device *spi = to_spi_device(context);
u8 buf[2];
memcpy(buf, data, 2);
@ -31,8 +30,7 @@ static int bmp280_regmap_spi_write(void *context, const void *data,
static int bmp280_regmap_spi_read(void *context, const void *reg,
size_t reg_size, void *val, size_t val_size)
{
struct device *dev = context;
struct spi_device *spi = to_spi_device(dev);
struct spi_device *spi = to_spi_device(context);
return spi_write_then_read(spi, reg, reg_size, val, val_size);
}
@ -58,9 +56,7 @@ static int bmp280_spi_probe(struct spi_device *spi)
return ret;
}
chip_info = device_get_match_data(&spi->dev);
if (!chip_info)
chip_info = (const struct bmp280_chip_info *) id->driver_data;
chip_info = spi_get_device_match_data(spi);
regmap = devm_regmap_init(&spi->dev,
&bmp280_regmap_bus,

View File

@ -292,6 +292,7 @@
#define BMP580_CHIP_ID_ALT 0x51
#define BMP180_CHIP_ID 0x55
#define BMP280_CHIP_ID 0x58
#define BMP390_CHIP_ID 0x60
#define BME280_CHIP_ID 0x60
#define BMP280_SOFT_RESET_VAL 0xB6
@ -410,7 +411,7 @@ struct bmp280_data {
__le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2];
__be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / 2];
u8 bmp380_cal_buf[BMP380_CALIB_REG_COUNT];
/* Miscellaneous, endianess-aware data buffers */
/* Miscellaneous, endianness-aware data buffers */
__le16 le16;
__be16 be16;
} __aligned(IIO_DMA_MINALIGN);
@ -418,7 +419,8 @@ struct bmp280_data {
struct bmp280_chip_info {
unsigned int id_reg;
const unsigned int chip_id;
const u8 *chip_id;
int num_chip_id;
const struct regmap_config *regmap_config;

View File

@ -0,0 +1,494 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Honeywell TruStability HSC Series pressure/temperature sensor
*
* 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
*/
#include <linux/array_size.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/init.h>
#include <linux/math64.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/units.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <asm/unaligned.h>
#include "hsc030pa.h"
/*
* HSC_PRESSURE_TRIPLET_LEN - length for the string that defines the
* pressure range, measurement unit and type as per the part nomenclature.
* Consult honeywell,pressure-triplet in the bindings file for details.
*/
#define HSC_PRESSURE_TRIPLET_LEN 6
#define HSC_STATUS_MASK GENMASK(7, 6)
#define HSC_TEMPERATURE_MASK GENMASK(15, 5)
#define HSC_PRESSURE_MASK GENMASK(29, 16)
struct hsc_func_spec {
u32 output_min;
u32 output_max;
};
/*
* function A: 10% - 90% of 2^14
* function B: 5% - 95% of 2^14
* function C: 5% - 85% of 2^14
* function F: 4% - 94% of 2^14
*/
static const struct hsc_func_spec hsc_func_spec[] = {
[HSC_FUNCTION_A] = { .output_min = 1638, .output_max = 14746 },
[HSC_FUNCTION_B] = { .output_min = 819, .output_max = 15565 },
[HSC_FUNCTION_C] = { .output_min = 819, .output_max = 13926 },
[HSC_FUNCTION_F] = { .output_min = 655, .output_max = 15401 },
};
enum hsc_variants {
HSC001BA = 0x00, HSC1_6BA = 0x01, HSC2_5BA = 0x02, HSC004BA = 0x03,
HSC006BA = 0x04, HSC010BA = 0x05, HSC1_6MD = 0x06, HSC2_5MD = 0x07,
HSC004MD = 0x08, HSC006MD = 0x09, HSC010MD = 0x0a, HSC016MD = 0x0b,
HSC025MD = 0x0c, HSC040MD = 0x0d, HSC060MD = 0x0e, HSC100MD = 0x0f,
HSC160MD = 0x10, HSC250MD = 0x11, HSC400MD = 0x12, HSC600MD = 0x13,
HSC001BD = 0x14, HSC1_6BD = 0x15, HSC2_5BD = 0x16, HSC004BD = 0x17,
HSC2_5MG = 0x18, HSC004MG = 0x19, HSC006MG = 0x1a, HSC010MG = 0x1b,
HSC016MG = 0x1c, HSC025MG = 0x1d, HSC040MG = 0x1e, HSC060MG = 0x1f,
HSC100MG = 0x20, HSC160MG = 0x21, HSC250MG = 0x22, HSC400MG = 0x23,
HSC600MG = 0x24, HSC001BG = 0x25, HSC1_6BG = 0x26, HSC2_5BG = 0x27,
HSC004BG = 0x28, HSC006BG = 0x29, HSC010BG = 0x2a, HSC100KA = 0x2b,
HSC160KA = 0x2c, HSC250KA = 0x2d, HSC400KA = 0x2e, HSC600KA = 0x2f,
HSC001GA = 0x30, HSC160LD = 0x31, HSC250LD = 0x32, HSC400LD = 0x33,
HSC600LD = 0x34, HSC001KD = 0x35, HSC1_6KD = 0x36, HSC2_5KD = 0x37,
HSC004KD = 0x38, HSC006KD = 0x39, HSC010KD = 0x3a, HSC016KD = 0x3b,
HSC025KD = 0x3c, HSC040KD = 0x3d, HSC060KD = 0x3e, HSC100KD = 0x3f,
HSC160KD = 0x40, HSC250KD = 0x41, HSC400KD = 0x42, HSC250LG = 0x43,
HSC400LG = 0x44, HSC600LG = 0x45, HSC001KG = 0x46, HSC1_6KG = 0x47,
HSC2_5KG = 0x48, HSC004KG = 0x49, HSC006KG = 0x4a, HSC010KG = 0x4b,
HSC016KG = 0x4c, HSC025KG = 0x4d, HSC040KG = 0x4e, HSC060KG = 0x4f,
HSC100KG = 0x50, HSC160KG = 0x51, HSC250KG = 0x52, HSC400KG = 0x53,
HSC600KG = 0x54, HSC001GG = 0x55, HSC015PA = 0x56, HSC030PA = 0x57,
HSC060PA = 0x58, HSC100PA = 0x59, HSC150PA = 0x5a, HSC0_5ND = 0x5b,
HSC001ND = 0x5c, HSC002ND = 0x5d, HSC004ND = 0x5e, HSC005ND = 0x5f,
HSC010ND = 0x60, HSC020ND = 0x61, HSC030ND = 0x62, HSC001PD = 0x63,
HSC005PD = 0x64, HSC015PD = 0x65, HSC030PD = 0x66, HSC060PD = 0x67,
HSC001NG = 0x68, HSC002NG = 0x69, HSC004NG = 0x6a, HSC005NG = 0x6b,
HSC010NG = 0x6c, HSC020NG = 0x6d, HSC030NG = 0x6e, HSC001PG = 0x6f,
HSC005PG = 0x70, HSC015PG = 0x71, HSC030PG = 0x72, HSC060PG = 0x73,
HSC100PG = 0x74, HSC150PG = 0x75, HSC_VARIANTS_MAX
};
static const char * const hsc_triplet_variants[HSC_VARIANTS_MAX] = {
[HSC001BA] = "001BA", [HSC1_6BA] = "1.6BA", [HSC2_5BA] = "2.5BA",
[HSC004BA] = "004BA", [HSC006BA] = "006BA", [HSC010BA] = "010BA",
[HSC1_6MD] = "1.6MD", [HSC2_5MD] = "2.5MD", [HSC004MD] = "004MD",
[HSC006MD] = "006MD", [HSC010MD] = "010MD", [HSC016MD] = "016MD",
[HSC025MD] = "025MD", [HSC040MD] = "040MD", [HSC060MD] = "060MD",
[HSC100MD] = "100MD", [HSC160MD] = "160MD", [HSC250MD] = "250MD",
[HSC400MD] = "400MD", [HSC600MD] = "600MD", [HSC001BD] = "001BD",
[HSC1_6BD] = "1.6BD", [HSC2_5BD] = "2.5BD", [HSC004BD] = "004BD",
[HSC2_5MG] = "2.5MG", [HSC004MG] = "004MG", [HSC006MG] = "006MG",
[HSC010MG] = "010MG", [HSC016MG] = "016MG", [HSC025MG] = "025MG",
[HSC040MG] = "040MG", [HSC060MG] = "060MG", [HSC100MG] = "100MG",
[HSC160MG] = "160MG", [HSC250MG] = "250MG", [HSC400MG] = "400MG",
[HSC600MG] = "600MG", [HSC001BG] = "001BG", [HSC1_6BG] = "1.6BG",
[HSC2_5BG] = "2.5BG", [HSC004BG] = "004BG", [HSC006BG] = "006BG",
[HSC010BG] = "010BG", [HSC100KA] = "100KA", [HSC160KA] = "160KA",
[HSC250KA] = "250KA", [HSC400KA] = "400KA", [HSC600KA] = "600KA",
[HSC001GA] = "001GA", [HSC160LD] = "160LD", [HSC250LD] = "250LD",
[HSC400LD] = "400LD", [HSC600LD] = "600LD", [HSC001KD] = "001KD",
[HSC1_6KD] = "1.6KD", [HSC2_5KD] = "2.5KD", [HSC004KD] = "004KD",
[HSC006KD] = "006KD", [HSC010KD] = "010KD", [HSC016KD] = "016KD",
[HSC025KD] = "025KD", [HSC040KD] = "040KD", [HSC060KD] = "060KD",
[HSC100KD] = "100KD", [HSC160KD] = "160KD", [HSC250KD] = "250KD",
[HSC400KD] = "400KD", [HSC250LG] = "250LG", [HSC400LG] = "400LG",
[HSC600LG] = "600LG", [HSC001KG] = "001KG", [HSC1_6KG] = "1.6KG",
[HSC2_5KG] = "2.5KG", [HSC004KG] = "004KG", [HSC006KG] = "006KG",
[HSC010KG] = "010KG", [HSC016KG] = "016KG", [HSC025KG] = "025KG",
[HSC040KG] = "040KG", [HSC060KG] = "060KG", [HSC100KG] = "100KG",
[HSC160KG] = "160KG", [HSC250KG] = "250KG", [HSC400KG] = "400KG",
[HSC600KG] = "600KG", [HSC001GG] = "001GG", [HSC015PA] = "015PA",
[HSC030PA] = "030PA", [HSC060PA] = "060PA", [HSC100PA] = "100PA",
[HSC150PA] = "150PA", [HSC0_5ND] = "0.5ND", [HSC001ND] = "001ND",
[HSC002ND] = "002ND", [HSC004ND] = "004ND", [HSC005ND] = "005ND",
[HSC010ND] = "010ND", [HSC020ND] = "020ND", [HSC030ND] = "030ND",
[HSC001PD] = "001PD", [HSC005PD] = "005PD", [HSC015PD] = "015PD",
[HSC030PD] = "030PD", [HSC060PD] = "060PD", [HSC001NG] = "001NG",
[HSC002NG] = "002NG", [HSC004NG] = "004NG", [HSC005NG] = "005NG",
[HSC010NG] = "010NG", [HSC020NG] = "020NG", [HSC030NG] = "030NG",
[HSC001PG] = "001PG", [HSC005PG] = "005PG", [HSC015PG] = "015PG",
[HSC030PG] = "030PG", [HSC060PG] = "060PG", [HSC100PG] = "100PG",
[HSC150PG] = "150PG",
};
/**
* struct hsc_range_config - list of pressure ranges based on nomenclature
* @pmin: lowest pressure that can be measured
* @pmax: highest pressure that can be measured
*/
struct hsc_range_config {
const s32 pmin;
const s32 pmax;
};
/* All min max limits have been converted to pascals */
static const struct hsc_range_config hsc_range_config[HSC_VARIANTS_MAX] = {
[HSC001BA] = { .pmin = 0, .pmax = 100000 },
[HSC1_6BA] = { .pmin = 0, .pmax = 160000 },
[HSC2_5BA] = { .pmin = 0, .pmax = 250000 },
[HSC004BA] = { .pmin = 0, .pmax = 400000 },
[HSC006BA] = { .pmin = 0, .pmax = 600000 },
[HSC010BA] = { .pmin = 0, .pmax = 1000000 },
[HSC1_6MD] = { .pmin = -160, .pmax = 160 },
[HSC2_5MD] = { .pmin = -250, .pmax = 250 },
[HSC004MD] = { .pmin = -400, .pmax = 400 },
[HSC006MD] = { .pmin = -600, .pmax = 600 },
[HSC010MD] = { .pmin = -1000, .pmax = 1000 },
[HSC016MD] = { .pmin = -1600, .pmax = 1600 },
[HSC025MD] = { .pmin = -2500, .pmax = 2500 },
[HSC040MD] = { .pmin = -4000, .pmax = 4000 },
[HSC060MD] = { .pmin = -6000, .pmax = 6000 },
[HSC100MD] = { .pmin = -10000, .pmax = 10000 },
[HSC160MD] = { .pmin = -16000, .pmax = 16000 },
[HSC250MD] = { .pmin = -25000, .pmax = 25000 },
[HSC400MD] = { .pmin = -40000, .pmax = 40000 },
[HSC600MD] = { .pmin = -60000, .pmax = 60000 },
[HSC001BD] = { .pmin = -100000, .pmax = 100000 },
[HSC1_6BD] = { .pmin = -160000, .pmax = 160000 },
[HSC2_5BD] = { .pmin = -250000, .pmax = 250000 },
[HSC004BD] = { .pmin = -400000, .pmax = 400000 },
[HSC2_5MG] = { .pmin = 0, .pmax = 250 },
[HSC004MG] = { .pmin = 0, .pmax = 400 },
[HSC006MG] = { .pmin = 0, .pmax = 600 },
[HSC010MG] = { .pmin = 0, .pmax = 1000 },
[HSC016MG] = { .pmin = 0, .pmax = 1600 },
[HSC025MG] = { .pmin = 0, .pmax = 2500 },
[HSC040MG] = { .pmin = 0, .pmax = 4000 },
[HSC060MG] = { .pmin = 0, .pmax = 6000 },
[HSC100MG] = { .pmin = 0, .pmax = 10000 },
[HSC160MG] = { .pmin = 0, .pmax = 16000 },
[HSC250MG] = { .pmin = 0, .pmax = 25000 },
[HSC400MG] = { .pmin = 0, .pmax = 40000 },
[HSC600MG] = { .pmin = 0, .pmax = 60000 },
[HSC001BG] = { .pmin = 0, .pmax = 100000 },
[HSC1_6BG] = { .pmin = 0, .pmax = 160000 },
[HSC2_5BG] = { .pmin = 0, .pmax = 250000 },
[HSC004BG] = { .pmin = 0, .pmax = 400000 },
[HSC006BG] = { .pmin = 0, .pmax = 600000 },
[HSC010BG] = { .pmin = 0, .pmax = 1000000 },
[HSC100KA] = { .pmin = 0, .pmax = 100000 },
[HSC160KA] = { .pmin = 0, .pmax = 160000 },
[HSC250KA] = { .pmin = 0, .pmax = 250000 },
[HSC400KA] = { .pmin = 0, .pmax = 400000 },
[HSC600KA] = { .pmin = 0, .pmax = 600000 },
[HSC001GA] = { .pmin = 0, .pmax = 1000000 },
[HSC160LD] = { .pmin = -160, .pmax = 160 },
[HSC250LD] = { .pmin = -250, .pmax = 250 },
[HSC400LD] = { .pmin = -400, .pmax = 400 },
[HSC600LD] = { .pmin = -600, .pmax = 600 },
[HSC001KD] = { .pmin = -1000, .pmax = 1000 },
[HSC1_6KD] = { .pmin = -1600, .pmax = 1600 },
[HSC2_5KD] = { .pmin = -2500, .pmax = 2500 },
[HSC004KD] = { .pmin = -4000, .pmax = 4000 },
[HSC006KD] = { .pmin = -6000, .pmax = 6000 },
[HSC010KD] = { .pmin = -10000, .pmax = 10000 },
[HSC016KD] = { .pmin = -16000, .pmax = 16000 },
[HSC025KD] = { .pmin = -25000, .pmax = 25000 },
[HSC040KD] = { .pmin = -40000, .pmax = 40000 },
[HSC060KD] = { .pmin = -60000, .pmax = 60000 },
[HSC100KD] = { .pmin = -100000, .pmax = 100000 },
[HSC160KD] = { .pmin = -160000, .pmax = 160000 },
[HSC250KD] = { .pmin = -250000, .pmax = 250000 },
[HSC400KD] = { .pmin = -400000, .pmax = 400000 },
[HSC250LG] = { .pmin = 0, .pmax = 250 },
[HSC400LG] = { .pmin = 0, .pmax = 400 },
[HSC600LG] = { .pmin = 0, .pmax = 600 },
[HSC001KG] = { .pmin = 0, .pmax = 1000 },
[HSC1_6KG] = { .pmin = 0, .pmax = 1600 },
[HSC2_5KG] = { .pmin = 0, .pmax = 2500 },
[HSC004KG] = { .pmin = 0, .pmax = 4000 },
[HSC006KG] = { .pmin = 0, .pmax = 6000 },
[HSC010KG] = { .pmin = 0, .pmax = 10000 },
[HSC016KG] = { .pmin = 0, .pmax = 16000 },
[HSC025KG] = { .pmin = 0, .pmax = 25000 },
[HSC040KG] = { .pmin = 0, .pmax = 40000 },
[HSC060KG] = { .pmin = 0, .pmax = 60000 },
[HSC100KG] = { .pmin = 0, .pmax = 100000 },
[HSC160KG] = { .pmin = 0, .pmax = 160000 },
[HSC250KG] = { .pmin = 0, .pmax = 250000 },
[HSC400KG] = { .pmin = 0, .pmax = 400000 },
[HSC600KG] = { .pmin = 0, .pmax = 600000 },
[HSC001GG] = { .pmin = 0, .pmax = 1000000 },
[HSC015PA] = { .pmin = 0, .pmax = 103421 },
[HSC030PA] = { .pmin = 0, .pmax = 206843 },
[HSC060PA] = { .pmin = 0, .pmax = 413685 },
[HSC100PA] = { .pmin = 0, .pmax = 689476 },
[HSC150PA] = { .pmin = 0, .pmax = 1034214 },
[HSC0_5ND] = { .pmin = -125, .pmax = 125 },
[HSC001ND] = { .pmin = -249, .pmax = 249 },
[HSC002ND] = { .pmin = -498, .pmax = 498 },
[HSC004ND] = { .pmin = -996, .pmax = 996 },
[HSC005ND] = { .pmin = -1245, .pmax = 1245 },
[HSC010ND] = { .pmin = -2491, .pmax = 2491 },
[HSC020ND] = { .pmin = -4982, .pmax = 4982 },
[HSC030ND] = { .pmin = -7473, .pmax = 7473 },
[HSC001PD] = { .pmin = -6895, .pmax = 6895 },
[HSC005PD] = { .pmin = -34474, .pmax = 34474 },
[HSC015PD] = { .pmin = -103421, .pmax = 103421 },
[HSC030PD] = { .pmin = -206843, .pmax = 206843 },
[HSC060PD] = { .pmin = -413685, .pmax = 413685 },
[HSC001NG] = { .pmin = 0, .pmax = 249 },
[HSC002NG] = { .pmin = 0, .pmax = 498 },
[HSC004NG] = { .pmin = 0, .pmax = 996 },
[HSC005NG] = { .pmin = 0, .pmax = 1245 },
[HSC010NG] = { .pmin = 0, .pmax = 2491 },
[HSC020NG] = { .pmin = 0, .pmax = 4982 },
[HSC030NG] = { .pmin = 0, .pmax = 7473 },
[HSC001PG] = { .pmin = 0, .pmax = 6895 },
[HSC005PG] = { .pmin = 0, .pmax = 34474 },
[HSC015PG] = { .pmin = 0, .pmax = 103421 },
[HSC030PG] = { .pmin = 0, .pmax = 206843 },
[HSC060PG] = { .pmin = 0, .pmax = 413685 },
[HSC100PG] = { .pmin = 0, .pmax = 689476 },
[HSC150PG] = { .pmin = 0, .pmax = 1034214 },
};
/**
* hsc_measurement_is_valid() - validate last conversion via status bits
* @data: structure containing instantiated sensor data
* Return: true only if both status bits are zero
*
* the two MSB from the first transfered byte contain a status code
* 00 - normal operation, valid data
* 01 - device in factory programming mode
* 10 - stale data
* 11 - diagnostic condition
*/
static bool hsc_measurement_is_valid(struct hsc_data *data)
{
return !(data->buffer[0] & HSC_STATUS_MASK);
}
static int hsc_get_measurement(struct hsc_data *data)
{
const struct hsc_chip_data *chip = data->chip;
int ret;
ret = data->recv_cb(data);
if (ret < 0)
return ret;
data->is_valid = chip->valid(data);
if (!data->is_valid)
return -EAGAIN;
return 0;
}
/*
* IIO ABI expects
* value = (conv + offset) * scale
*
* datasheet provides the following formula for determining the temperature
* temp[C] = conv * a + b
* where a = 200/2047; b = -50
*
* temp[C] = (conv + (b/a)) * a * (1000)
* =>
* scale = a * 1000 = .097703957 * 1000 = 97.703957
* offset = b/a = -50 / .097703957 = -50000000 / 97704
*
* based on the datasheet
* pressure = (conv - Omin) * Q + Pmin =
* ((conv - Omin) + Pmin/Q) * Q
* =>
* scale = Q = (Pmax - Pmin) / (Omax - Omin)
* offset = Pmin/Q - Omin = Pmin * (Omax - Omin) / (Pmax - Pmin) - Omin
*/
static int hsc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *channel, int *val,
int *val2, long mask)
{
struct hsc_data *data = iio_priv(indio_dev);
int ret;
u32 recvd;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = hsc_get_measurement(data);
if (ret)
return ret;
recvd = get_unaligned_be32(data->buffer);
switch (channel->type) {
case IIO_PRESSURE:
*val = FIELD_GET(HSC_PRESSURE_MASK, recvd);
return IIO_VAL_INT;
case IIO_TEMP:
*val = FIELD_GET(HSC_TEMPERATURE_MASK, recvd);
return IIO_VAL_INT;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SCALE:
switch (channel->type) {
case IIO_TEMP:
*val = 97;
*val2 = 703957;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_PRESSURE:
*val = data->p_scale;
*val2 = data->p_scale_dec;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_OFFSET:
switch (channel->type) {
case IIO_TEMP:
*val = -50000000;
*val2 = 97704;
return IIO_VAL_FRACTIONAL;
case IIO_PRESSURE:
*val = data->p_offset;
*val2 = data->p_offset_dec;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static const struct iio_chan_spec hsc_channels[] = {
{
.type = IIO_PRESSURE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
},
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
},
};
static const struct iio_info hsc_info = {
.read_raw = hsc_read_raw,
};
static const struct hsc_chip_data hsc_chip = {
.valid = hsc_measurement_is_valid,
.channels = hsc_channels,
.num_channels = ARRAY_SIZE(hsc_channels),
};
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;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*hsc));
if (!indio_dev)
return -ENOMEM;
hsc = iio_priv(indio_dev);
hsc->chip = &hsc_chip;
hsc->recv_cb = recv;
hsc->dev = dev;
ret = device_property_read_u32(dev, "honeywell,transfer-function",
&hsc->function);
if (ret)
return dev_err_probe(dev, ret,
"honeywell,transfer-function could not be read\n");
if (hsc->function > HSC_FUNCTION_F)
return dev_err_probe(dev, -EINVAL,
"honeywell,transfer-function %d invalid\n",
hsc->function);
ret = device_property_read_string(dev, "honeywell,pressure-triplet",
&triplet);
if (ret)
return dev_err_probe(dev, ret,
"honeywell,pressure-triplet could not be read\n");
if (str_has_prefix(triplet, "NA")) {
ret = device_property_read_u32(dev, "honeywell,pmin-pascal",
&hsc->pmin);
if (ret)
return dev_err_probe(dev, ret,
"honeywell,pmin-pascal could not be read\n");
ret = device_property_read_u32(dev, "honeywell,pmax-pascal",
&hsc->pmax);
if (ret)
return dev_err_probe(dev, ret,
"honeywell,pmax-pascal could not be read\n");
} else {
ret = device_property_match_property_string(dev,
"honeywell,pressure-triplet",
hsc_triplet_variants,
HSC_VARIANTS_MAX);
if (ret < 0)
return dev_err_probe(dev, -EINVAL,
"honeywell,pressure-triplet is invalid\n");
hsc->pmin = hsc_range_config[ret].pmin;
hsc->pmax = hsc_range_config[ret].pmax;
}
if (hsc->pmin >= hsc->pmax)
return dev_err_probe(dev, -EINVAL,
"pressure limits are invalid\n");
ret = devm_regulator_get_enable(dev, "vdd");
if (ret)
return dev_err_probe(dev, ret, "can't get vdd supply\n");
hsc->outmin = hsc_func_spec[hsc->function].output_min;
hsc->outmax = hsc_func_spec[hsc->function].output_max;
tmp = div_s64(((s64)(hsc->pmax - hsc->pmin)) * MICRO,
hsc->outmax - hsc->outmin);
hsc->p_scale = div_s64_rem(tmp, NANO, &hsc->p_scale_dec);
tmp = div_s64(((s64)hsc->pmin * (s64)(hsc->outmax - hsc->outmin)) * MICRO,
hsc->pmax - hsc->pmin);
tmp -= (s64)hsc->outmin * MICRO;
hsc->p_offset = div_s64_rem(tmp, MICRO, &hsc->p_offset_dec);
indio_dev->name = "hsc030pa";
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &hsc_info;
indio_dev->channels = hsc->chip->channels;
indio_dev->num_channels = hsc->chip->num_channels;
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_NS(hsc_common_probe, IIO_HONEYWELL_HSC030PA);
MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
MODULE_DESCRIPTION("Honeywell HSC and SSC pressure sensor core driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,74 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Honeywell TruStability HSC Series pressure/temperature sensor
*
* Copyright (c) 2023 Petre Rodan <petre.rodan@subdimension.ro>
*/
#ifndef _HSC030PA_H
#define _HSC030PA_H
#include <linux/types.h>
#define HSC_REG_MEASUREMENT_RD_SIZE 4
struct device;
struct iio_chan_spec;
struct iio_dev;
struct hsc_data;
struct hsc_chip_data;
typedef int (*hsc_recv_fn)(struct hsc_data *);
/**
* struct hsc_data
* @dev: current device structure
* @chip: structure containing chip's channel properties
* @recv_cb: function that implements the chip reads
* @is_valid: true if last transfer has been validated
* @pmin: minimum measurable pressure limit
* @pmax: maximum measurable pressure limit
* @outmin: minimum raw pressure in counts (based on transfer function)
* @outmax: maximum raw pressure in counts (based on transfer function)
* @function: transfer function
* @p_scale: pressure scale
* @p_scale_dec: pressure scale, decimal places
* @p_offset: pressure offset
* @p_offset_dec: pressure offset, decimal places
* @buffer: raw conversion data
*/
struct hsc_data {
struct device *dev;
const struct hsc_chip_data *chip;
hsc_recv_fn recv_cb;
bool is_valid;
s32 pmin;
s32 pmax;
u32 outmin;
u32 outmax;
u32 function;
s64 p_scale;
s32 p_scale_dec;
s64 p_offset;
s32 p_offset_dec;
u8 buffer[HSC_REG_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN);
};
struct hsc_chip_data {
bool (*valid)(struct hsc_data *data);
const struct iio_chan_spec *channels;
u8 num_channels;
};
enum hsc_func_id {
HSC_FUNCTION_A,
HSC_FUNCTION_B,
HSC_FUNCTION_C,
HSC_FUNCTION_F,
};
int hsc_common_probe(struct device *dev, hsc_recv_fn recv);
#endif

View File

@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Honeywell TruStability HSC Series pressure/temperature sensor
*
* 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]
*/
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
#include "hsc030pa.h"
static int hsc_i2c_recv(struct hsc_data *data)
{
struct i2c_client *client = to_i2c_client(data->dev);
struct i2c_msg msg;
int ret;
msg.addr = client->addr;
msg.flags = client->flags | I2C_M_RD;
msg.len = HSC_REG_MEASUREMENT_RD_SIZE;
msg.buf = data->buffer;
ret = i2c_transfer(client->adapter, &msg, 1);
return (ret == 2) ? 0 : ret;
}
static int hsc_i2c_probe(struct i2c_client *client)
{
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -EOPNOTSUPP;
return hsc_common_probe(&client->dev, hsc_i2c_recv);
}
static const struct of_device_id hsc_i2c_match[] = {
{ .compatible = "honeywell,hsc030pa" },
{}
};
MODULE_DEVICE_TABLE(of, hsc_i2c_match);
static const struct i2c_device_id hsc_i2c_id[] = {
{ "hsc030pa" },
{}
};
MODULE_DEVICE_TABLE(i2c, hsc_i2c_id);
static struct i2c_driver hsc_i2c_driver = {
.driver = {
.name = "hsc030pa",
.of_match_table = hsc_i2c_match,
},
.probe = hsc_i2c_probe,
.id_table = hsc_i2c_id,
};
module_i2c_driver(hsc_i2c_driver);
MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
MODULE_DESCRIPTION("Honeywell HSC and SSC pressure sensor i2c driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(IIO_HONEYWELL_HSC030PA);

View File

@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Honeywell TruStability HSC Series pressure/temperature sensor
*
* 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
*/
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/stddef.h>
#include <linux/iio/iio.h>
#include "hsc030pa.h"
static int hsc_spi_recv(struct hsc_data *data)
{
struct spi_device *spi = to_spi_device(data->dev);
struct spi_transfer xfer = {
.tx_buf = NULL,
.rx_buf = data->buffer,
.len = HSC_REG_MEASUREMENT_RD_SIZE,
};
return spi_sync_transfer(spi, &xfer, 1);
}
static int hsc_spi_probe(struct spi_device *spi)
{
return hsc_common_probe(&spi->dev, hsc_spi_recv);
}
static const struct of_device_id hsc_spi_match[] = {
{ .compatible = "honeywell,hsc030pa" },
{}
};
MODULE_DEVICE_TABLE(of, hsc_spi_match);
static const struct spi_device_id hsc_spi_id[] = {
{ "hsc030pa" },
{}
};
MODULE_DEVICE_TABLE(spi, hsc_spi_id);
static struct spi_driver hsc_spi_driver = {
.driver = {
.name = "hsc030pa",
.of_match_table = hsc_spi_match,
},
.probe = hsc_spi_probe,
.id_table = hsc_spi_id,
};
module_spi_driver(hsc_spi_driver);
MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
MODULE_DESCRIPTION("Honeywell HSC and SSC pressure sensor spi driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(IIO_HONEYWELL_HSC030PA);

View File

@ -7,7 +7,6 @@
#include <asm/unaligned.h>
#include <linux/bitfield.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>

View File

@ -888,7 +888,6 @@ sx9324_get_default_reg(struct device *dev, int idx,
char prop[] = SX9324_PROXRAW_DEF;
u32 start = 0, raw = 0, pos = 0;
int ret, count, ph, pin;
const char *res;
memcpy(reg_def, &sx9324_default_regs[idx], sizeof(*reg_def));
@ -915,24 +914,21 @@ sx9324_get_default_reg(struct device *dev, int idx,
reg_def->def = raw;
break;
case SX9324_REG_AFE_CTRL0:
ret = device_property_read_string(dev,
"semtech,cs-idle-sleep", &res);
if (!ret)
ret = match_string(sx9324_csidle, ARRAY_SIZE(sx9324_csidle), res);
ret = device_property_match_property_string(dev, "semtech,cs-idle-sleep",
sx9324_csidle,
ARRAY_SIZE(sx9324_csidle));
if (ret >= 0) {
reg_def->def &= ~SX9324_REG_AFE_CTRL0_CSIDLE_MASK;
reg_def->def |= ret << SX9324_REG_AFE_CTRL0_CSIDLE_SHIFT;
}
ret = device_property_read_string(dev,
"semtech,int-comp-resistor", &res);
if (ret)
break;
ret = match_string(sx9324_rints, ARRAY_SIZE(sx9324_rints), res);
if (ret < 0)
break;
reg_def->def &= ~SX9324_REG_AFE_CTRL0_RINT_MASK;
reg_def->def |= ret << SX9324_REG_AFE_CTRL0_RINT_SHIFT;
ret = device_property_match_property_string(dev, "semtech,int-comp-resistor",
sx9324_rints,
ARRAY_SIZE(sx9324_rints));
if (ret >= 0) {
reg_def->def &= ~SX9324_REG_AFE_CTRL0_RINT_MASK;
reg_def->def |= ret << SX9324_REG_AFE_CTRL0_RINT_SHIFT;
}
break;
case SX9324_REG_AFE_CTRL4:
case SX9324_REG_AFE_CTRL7:

View File

@ -141,7 +141,7 @@ struct ad2s1210_state {
struct spi_device *sdev;
/** GPIO pin connected to SAMPLE line. */
struct gpio_desc *sample_gpio;
/** GPIO pins connected to A0 and A1 lines. */
/** GPIO pins connected to A0 and A1 lines (optional). */
struct gpio_descs *mode_gpios;
/** Used to access config registers. */
struct regmap *regmap;
@ -149,6 +149,8 @@ struct ad2s1210_state {
unsigned long clkin_hz;
/** Available raw hysteresis values based on resolution. */
int hysteresis_available[2];
/* adi,fixed-mode property - only valid when mode_gpios == NULL. */
enum ad2s1210_mode fixed_mode;
/** The selected resolution */
enum ad2s1210_resolution resolution;
/** Copy of fault register from the previous read. */
@ -175,6 +177,9 @@ static int ad2s1210_set_mode(struct ad2s1210_state *st, enum ad2s1210_mode mode)
struct gpio_descs *gpios = st->mode_gpios;
DECLARE_BITMAP(bitmap, 2);
if (!gpios)
return mode == st->fixed_mode ? 0 : -EOPNOTSUPP;
bitmap[0] = mode;
return gpiod_set_array_value(gpios->ndescs, gpios->desc, gpios->info,
@ -276,7 +281,8 @@ static int ad2s1210_regmap_reg_read(void *context, unsigned int reg,
* parity error. The fault register is read-only and the D7 bit means
* something else there.
*/
if (reg != AD2S1210_REG_FAULT && st->rx[1] & AD2S1210_ADDRESS_DATA)
if ((reg > AD2S1210_REG_VELOCITY_LSB && reg != AD2S1210_REG_FAULT)
&& st->rx[1] & AD2S1210_ADDRESS_DATA)
return -EBADMSG;
*val = st->rx[1];
@ -450,21 +456,53 @@ static int ad2s1210_single_conversion(struct iio_dev *indio_dev,
ad2s1210_toggle_sample_line(st);
timestamp = iio_get_time_ns(indio_dev);
switch (chan->type) {
case IIO_ANGL:
ret = ad2s1210_set_mode(st, MOD_POS);
break;
case IIO_ANGL_VEL:
ret = ad2s1210_set_mode(st, MOD_VEL);
break;
default:
return -EINVAL;
if (st->fixed_mode == MOD_CONFIG) {
unsigned int reg_val;
switch (chan->type) {
case IIO_ANGL:
ret = regmap_bulk_read(st->regmap,
AD2S1210_REG_POSITION_MSB,
&st->sample.raw, 2);
if (ret < 0)
return ret;
break;
case IIO_ANGL_VEL:
ret = regmap_bulk_read(st->regmap,
AD2S1210_REG_VELOCITY_MSB,
&st->sample.raw, 2);
if (ret < 0)
return ret;
break;
default:
return -EINVAL;
}
ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, &reg_val);
if (ret < 0)
return ret;
st->sample.fault = reg_val;
} else {
switch (chan->type) {
case IIO_ANGL:
ret = ad2s1210_set_mode(st, MOD_POS);
break;
case IIO_ANGL_VEL:
ret = ad2s1210_set_mode(st, MOD_VEL);
break;
default:
return -EINVAL;
}
if (ret < 0)
return ret;
ret = spi_read(st->sdev, &st->sample, 3);
if (ret < 0)
return ret;
}
if (ret < 0)
return ret;
ret = spi_read(st->sdev, &st->sample, 3);
if (ret < 0)
return ret;
switch (chan->type) {
case IIO_ANGL:
@ -1252,29 +1290,55 @@ static irqreturn_t ad2s1210_trigger_handler(int irq, void *p)
ad2s1210_toggle_sample_line(st);
if (test_bit(0, indio_dev->active_scan_mask)) {
ret = ad2s1210_set_mode(st, MOD_POS);
if (ret < 0)
goto error_ret;
if (st->fixed_mode == MOD_CONFIG) {
ret = regmap_bulk_read(st->regmap,
AD2S1210_REG_POSITION_MSB,
&st->sample.raw, 2);
if (ret < 0)
goto error_ret;
} else {
ret = ad2s1210_set_mode(st, MOD_POS);
if (ret < 0)
goto error_ret;
ret = spi_read(st->sdev, &st->sample, 3);
if (ret < 0)
goto error_ret;
ret = spi_read(st->sdev, &st->sample, 3);
if (ret < 0)
goto error_ret;
}
memcpy(&st->scan.chan[chan++], &st->sample.raw, 2);
}
if (test_bit(1, indio_dev->active_scan_mask)) {
ret = ad2s1210_set_mode(st, MOD_VEL);
if (ret < 0)
goto error_ret;
if (st->fixed_mode == MOD_CONFIG) {
ret = regmap_bulk_read(st->regmap,
AD2S1210_REG_VELOCITY_MSB,
&st->sample.raw, 2);
if (ret < 0)
goto error_ret;
} else {
ret = ad2s1210_set_mode(st, MOD_VEL);
if (ret < 0)
goto error_ret;
ret = spi_read(st->sdev, &st->sample, 3);
if (ret < 0)
goto error_ret;
ret = spi_read(st->sdev, &st->sample, 3);
if (ret < 0)
goto error_ret;
}
memcpy(&st->scan.chan[chan++], &st->sample.raw, 2);
}
if (st->fixed_mode == MOD_CONFIG) {
unsigned int reg_val;
ret = regmap_read(st->regmap, AD2S1210_REG_FAULT, &reg_val);
if (ret < 0)
return ret;
st->sample.fault = reg_val;
}
ad2s1210_push_events(indio_dev, st->sample.fault, pf->timestamp);
iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, pf->timestamp);
@ -1299,9 +1363,24 @@ static const struct iio_info ad2s1210_info = {
static int ad2s1210_setup_properties(struct ad2s1210_state *st)
{
struct device *dev = &st->sdev->dev;
const char *str_val;
u32 val;
int ret;
ret = device_property_read_string(dev, "adi,fixed-mode", &str_val);
if (ret == -EINVAL)
st->fixed_mode = -1;
else if (ret < 0)
return dev_err_probe(dev, ret,
"failed to read adi,fixed-mode property\n");
else {
if (strcmp(str_val, "config"))
return dev_err_probe(dev, -EINVAL,
"only adi,fixed-mode=\"config\" is supported\n");
st->fixed_mode = MOD_CONFIG;
}
ret = device_property_read_u32(dev, "assigned-resolution-bits", &val);
if (ret < 0)
return dev_err_probe(dev, ret,
@ -1347,6 +1426,7 @@ static int ad2s1210_setup_gpios(struct ad2s1210_state *st)
{
struct device *dev = &st->sdev->dev;
struct gpio_descs *resolution_gpios;
struct gpio_desc *reset_gpio;
DECLARE_BITMAP(bitmap, 2);
int ret;
@ -1357,12 +1437,21 @@ static int ad2s1210_setup_gpios(struct ad2s1210_state *st)
"failed to request sample GPIO\n");
/* both pins high means that we start in config mode */
st->mode_gpios = devm_gpiod_get_array(dev, "mode", GPIOD_OUT_HIGH);
st->mode_gpios = devm_gpiod_get_array_optional(dev, "mode",
GPIOD_OUT_HIGH);
if (IS_ERR(st->mode_gpios))
return dev_err_probe(dev, PTR_ERR(st->mode_gpios),
"failed to request mode GPIOs\n");
if (st->mode_gpios->ndescs != 2)
if (!st->mode_gpios && st->fixed_mode == -1)
return dev_err_probe(dev, -EINVAL,
"must specify either adi,fixed-mode or mode-gpios\n");
if (st->mode_gpios && st->fixed_mode != -1)
return dev_err_probe(dev, -EINVAL,
"must specify only one of adi,fixed-mode or mode-gpios\n");
if (st->mode_gpios && st->mode_gpios->ndescs != 2)
return dev_err_probe(dev, -EINVAL,
"requires exactly 2 mode-gpios\n");
@ -1393,6 +1482,17 @@ static int ad2s1210_setup_gpios(struct ad2s1210_state *st)
"failed to set resolution gpios\n");
}
/* If the optional reset GPIO is present, toggle it to do a hard reset. */
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),
"failed to request reset GPIO\n");
if (reset_gpio) {
udelay(10);
gpiod_set_value(reset_gpio, 0);
}
return 0;
}

View File

@ -76,6 +76,18 @@ config MLX90632
This driver can also be built as a module. If so, the module will
be called mlx90632.
config MLX90635
tristate "MLX90635 contact-less infrared sensor with medical accuracy"
depends on I2C
select REGMAP_I2C
help
If you say yes here you get support for the Melexis
MLX90635 contact-less infrared sensor with medical accuracy
connected with I2C.
This driver can also be built as a module. If so, the module will
be called mlx90635.
config TMP006
tristate "TMP006 infrared thermopile sensor"
depends on I2C
@ -158,4 +170,14 @@ config MAX31865
This driver can also be build as a module. If so, the module
will be called max31865.
config MCP9600
tristate "MCP9600 thermocouple EMF converter"
depends on I2C
help
If you say yes here you get support for MCP9600
thermocouple EMF converter connected via I2C.
This driver can also be built as a module. If so, the module
will be called mcp9600.
endmenu

View File

@ -10,8 +10,10 @@ obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o
obj-$(CONFIG_MAX30208) += max30208.o
obj-$(CONFIG_MAX31856) += max31856.o
obj-$(CONFIG_MAX31865) += max31865.o
obj-$(CONFIG_MCP9600) += mcp9600.o
obj-$(CONFIG_MLX90614) += mlx90614.o
obj-$(CONFIG_MLX90632) += mlx90632.o
obj-$(CONFIG_MLX90632) += mlx90635.o
obj-$(CONFIG_TMP006) += tmp006.o
obj-$(CONFIG_TMP007) += tmp007.o
obj-$(CONFIG_TMP117) += tmp117.o

View File

@ -0,0 +1,139 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* mcp9600.c - Support for Microchip MCP9600 thermocouple EMF converter
*
* Copyright (c) 2022 Andrew Hepp
* Author: <andrew.hepp@ahepp.dev>
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
/* MCP9600 registers */
#define MCP9600_HOT_JUNCTION 0x0
#define MCP9600_COLD_JUNCTION 0x2
#define MCP9600_DEVICE_ID 0x20
/* MCP9600 device id value */
#define MCP9600_DEVICE_ID_MCP9600 0x40
static const struct iio_chan_spec mcp9600_channels[] = {
{
.type = IIO_TEMP,
.address = MCP9600_HOT_JUNCTION,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
},
{
.type = IIO_TEMP,
.address = MCP9600_COLD_JUNCTION,
.channel2 = IIO_MOD_TEMP_AMBIENT,
.modified = 1,
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
},
};
struct mcp9600_data {
struct i2c_client *client;
};
static int mcp9600_read(struct mcp9600_data *data,
struct iio_chan_spec const *chan, int *val)
{
int ret;
ret = i2c_smbus_read_word_swapped(data->client, chan->address);
if (ret < 0)
return ret;
*val = ret;
return 0;
}
static int mcp9600_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct mcp9600_data *data = iio_priv(indio_dev);
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = mcp9600_read(data, chan, val);
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 62;
*val2 = 500000;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
}
static const struct iio_info mcp9600_info = {
.read_raw = mcp9600_read_raw,
};
static int mcp9600_probe(struct i2c_client *client)
{
struct iio_dev *indio_dev;
struct mcp9600_data *data;
int ret;
ret = i2c_smbus_read_byte_data(client, MCP9600_DEVICE_ID);
if (ret < 0)
return dev_err_probe(&client->dev, ret, "Failed to read device ID\n");
if (ret != MCP9600_DEVICE_ID_MCP9600)
dev_warn(&client->dev, "Expected ID %x, got %x\n",
MCP9600_DEVICE_ID_MCP9600, ret);
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->client = client;
indio_dev->info = &mcp9600_info;
indio_dev->name = "mcp9600";
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = mcp9600_channels;
indio_dev->num_channels = ARRAY_SIZE(mcp9600_channels);
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id mcp9600_id[] = {
{ "mcp9600" },
{}
};
MODULE_DEVICE_TABLE(i2c, mcp9600_id);
static const struct of_device_id mcp9600_of_match[] = {
{ .compatible = "microchip,mcp9600" },
{}
};
MODULE_DEVICE_TABLE(of, mcp9600_of_match);
static struct i2c_driver mcp9600_driver = {
.driver = {
.name = "mcp9600",
.of_match_table = mcp9600_of_match,
},
.probe = mcp9600_probe,
.id_table = mcp9600_id
};
module_i2c_driver(mcp9600_driver);
MODULE_AUTHOR("Andrew Hepp <andrew.hepp@ahepp.dev>");
MODULE_DESCRIPTION("Microchip MCP9600 thermocouple EMF converter driver");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

View File

@ -41,6 +41,7 @@ struct adi_axi_adc_chip_info {
* @reg_access IIO debugfs_reg_access hook for the client ADC
* @read_raw IIO read_raw hook for the client ADC
* @write_raw IIO write_raw hook for the client ADC
* @read_avail IIO read_avail hook for the client ADC
*/
struct adi_axi_adc_conv {
const struct adi_axi_adc_chip_info *chip_info;
@ -54,6 +55,9 @@ struct adi_axi_adc_conv {
int (*write_raw)(struct adi_axi_adc_conv *conv,
struct iio_chan_spec const *chan,
int val, int val2, long mask);
int (*read_avail)(struct adi_axi_adc_conv *conv,
struct iio_chan_spec const *chan,
const int **val, int *type, int *length, long mask);
};
struct adi_axi_adc_conv *devm_adi_axi_adc_conv_register(struct device *dev,

View File

@ -68,6 +68,7 @@ enum iio_chan_info_enum {
IIO_CHAN_INFO_THERMOCOUPLE_TYPE,
IIO_CHAN_INFO_CALIBAMBIENT,
IIO_CHAN_INFO_ZEROPOINT,
IIO_CHAN_INFO_TROUGH,
};
#endif /* _IIO_TYPES_H_ */

View File

@ -98,6 +98,18 @@ static inline bool device_is_compatible(const struct device *dev, const char *co
return fwnode_device_is_compatible(dev_fwnode(dev), compat);
}
int fwnode_property_match_property_string(const struct fwnode_handle *fwnode,
const char *propname,
const char * const *array, size_t n);
static inline
int device_property_match_property_string(const struct device *dev,
const char *propname,
const char * const *array, size_t n)
{
return fwnode_property_match_property_string(dev_fwnode(dev), propname, array, n);
}
int fwnode_property_get_reference_args(const struct fwnode_handle *fwnode,
const char *prop, const char *nargs_prop,
unsigned int nargs, unsigned int index,

View File

@ -91,6 +91,8 @@ enum iio_modifier {
IIO_MOD_CO2,
IIO_MOD_VOC,
IIO_MOD_LIGHT_UV,
IIO_MOD_LIGHT_UVA,
IIO_MOD_LIGHT_UVB,
IIO_MOD_LIGHT_DUV,
IIO_MOD_PM1,
IIO_MOD_PM2P5,

View File

@ -512,6 +512,7 @@ our $Attribute = qr{
__ro_after_init|
__kprobes|
$InitAttribute|
__aligned\s*\(.*\)|
____cacheline_aligned|
____cacheline_aligned_in_smp|
____cacheline_internodealigned_in_smp|

View File

@ -105,6 +105,8 @@ static const char * const iio_modifier_names[] = {
[IIO_MOD_LIGHT_GREEN] = "green",
[IIO_MOD_LIGHT_BLUE] = "blue",
[IIO_MOD_LIGHT_UV] = "uv",
[IIO_MOD_LIGHT_UVA] = "uva",
[IIO_MOD_LIGHT_UVB] = "uvb",
[IIO_MOD_LIGHT_DUV] = "duv",
[IIO_MOD_QUATERNION] = "quaternion",
[IIO_MOD_TEMP_AMBIENT] = "ambient",