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:
commit
7050abeb8f
7
.mailmap
7
.mailmap
|
@ -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>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -25,6 +25,10 @@ properties:
|
|||
|
||||
spi-cpol: true
|
||||
|
||||
spi-cs-inactive-delay-ns:
|
||||
minimum: 16000
|
||||
default: 16000
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
|
|
|
@ -47,6 +47,10 @@ properties:
|
|||
spi-max-frequency:
|
||||
maximum: 2000000
|
||||
|
||||
spi-cs-inactive-delay-ns:
|
||||
minimum: 16000
|
||||
default: 16000
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
|
|
|
@ -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";
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
|
@ -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: |
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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,.*":
|
||||
|
|
56
MAINTAINERS
56
MAINTAINERS
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
/*
|
||||
* 3-axis accelerometer driver supporting following Bosch-Sensortec chips:
|
||||
* - BMI088
|
||||
* - BMI085
|
||||
* - BMI090L
|
||||
*
|
||||
* Copyright (c) 2018-2021, Topic Embedded Products
|
||||
*/
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
/*
|
||||
* 3-axis accelerometer driver supporting following Bosch-Sensortec chips:
|
||||
* - BMI088
|
||||
* - BMI085
|
||||
* - BMI090L
|
||||
*
|
||||
* Copyright (c) 2018-2020, Topic Embedded Products
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, ®, 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");
|
|
@ -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 |
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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");
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
@ -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);
|
|
@ -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);
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>");
|
|
@ -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, <r390_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 = <r390_info;
|
||||
indio_dev->channels = <r390_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");
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
|
@ -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
|
|
@ -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);
|
|
@ -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);
|
|
@ -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>
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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, ®_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, ®_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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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,
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -512,6 +512,7 @@ our $Attribute = qr{
|
|||
__ro_after_init|
|
||||
__kprobes|
|
||||
$InitAttribute|
|
||||
__aligned\s*\(.*\)|
|
||||
____cacheline_aligned|
|
||||
____cacheline_aligned_in_smp|
|
||||
____cacheline_internodealigned_in_smp|
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue