mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 21:38:32 +08:00
First set of new device support, features and cleanup for IIO in the 5.1 cycle
A number of interesting new devices supported plus a good set of staging cleanup including one graduation and one drop. New device support * ad56886 - Add support for AD5674R/AD5679R with some minor driver changes to support more channels. * ad7768 - New driver and dt bindings for this 24 bit ADC. * max44009 - New driver and dt bindings for this ambient light sensor. * mpu6050 - Support the ICM 20602 IMU. Minor tweaks due to slightly different register map. * NPCM adc - New driver and dt bindings for this BMC ADC. * Sensiron SGP30 - Modifiers for ethanol and H2. - New driver and dt bindings. - Follow patch added self cleaning support. * Sensiron SPS30 - New channel type for mass concentration. - New driver and bindings. - Minor tidy up patch followed (drop fmt specifier as unused) * st_pressure - lps22hh support. ID plus information structures and dt bindings. * ti-ads124s08 - Add binding doc and driver. Staging graduations * ad7606 driver and bindings. Staging drops * ad7152 CDC driver dropped. This part is near EoL and no one is known to be using it. If anyone surfaces obviously we can bring the driver back. If not, good to drop it to avoid wasting anyone's time cleaning it up. New features * bme680 - DT support and bindings doc. * isl29018 - Add regulator for VCC. * mag3110 - Add regulators for supplies. * meson-saradc - Support the temperature sensors of more SoCs. * mma8452 - Add regulators for power suplies and binding docs to reflect them. * st-accel - Support the undocumented but it seems fairly common _ONT ACPI method to specify orientation of the sensor. Cleanup, minor fixes and fixes for staging driver that have been broken a long time * ad5933 - Drop platform data alternative to specifying the reference voltage using a regulator. - Use the clock framework to contorl the reference clock. - Add a DT binding doc to cover the defacto binding. * ad7280a - Split up some big functions to improve readability. * ad7606 - Allow for timeout if interrupt never occurs. - Use devm functions to simplify probe and remove. - Use the find_closest macro to avoid need for precise values from userspace. - Add missing vendor prefixes for various DT properties. Note the driver is in staging still and there are no known devicetrees. - Add explict OF device ID table. - Simplify the Kconfig choices - Change to a threaded IRQ. - SPDX and simple stype fixes. * ad7816 - Drop unnecessary variable init. * ad9523 - Check a return value that was ignored. * ad9833 - Drop platform data. It was just setting most values to the hardware defaults. - Use the clock framework to provide the input clock. * adt7316 (lots of staging cleanup) - Fix some wrong register / bit definitions - Invert the logic of the check for an ldac pin so it actually makes sense. - Read the right register to get internal vref settings - Allow adt751x chips to use the internal vref for all DAC channels rather than a subset. - Remove dac vref bypass control from parts that don't have one. - Make the store DAC update mode function consistent with the show one. - Fix some spellings and other minor tidy up. - Avoid passing irq numbers around by putting all the irq logic in one place. - Fix an issue with the resolution of DAC control. - Fix support of the high resolution DAC mode (for temp proportional output) where supported. - Fix DAC read and write calculations. * st_lsm6dsx - Drop an unused variable (set but not read) * xilinx-xadc - Check an unhandled return value. -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAlxXWXwRHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0FogtPhAAtGoXJl9gwBywQXjykFBiN5PxBxKypq58 El7EQY7I0P/RkIVEBHgIc9p85lREVkIZQEcQZqsO5LI63cumCGtODyJg2CAF6+oz EIO0GCZFvb2zLiTyPg2NSuBhsYxiLtyHfu5MGPivbDyzVKZzf8LLjwQxkHYPaLzb Qa9D3wigoGHuvXpC9ULGQT36kMj6XPN9JGQsCRtbUQVgPu2XOPiuLv98vEpyaCPj 1C7D72LAEq1Z5A3rA17KpT576Yy1JGv1LkSz48xEtHlDoB2IgCkoZfx6ThTr1ehX eWav1NsD1IpbWKiSY19rSmjSkkbYk1WHyV+tTeTguFb6nfIA/y22kl9td8Rqjk2P L/O1tRHdXO8G+gtCfBhUdVmT1SQq/vkow2UBH59CllN81bsWxFEudE4VHJCBlmjr sgMKdb3d0aYekRis/bNQJMvT0nY2x+g2k+ey+kEnrYOZVjCx+EeNNS249CnUnFId ZZNGqJ2j0xK9BULqffcupCoERLIhf/9nlx5JO3n5YA5X3pdns+yzVkS/9oXpN7EK 9A41pU9zVWYN8B21MqfN0EPG41nkY36TOuTvL2/4/U2DfAD6VIf4vEYimrkTjsU0 n7vKqQL8DRicWDsbxP8h6u/nG0u+ZgOgY4SVzth0aT3/plCAwhWmoQpnkjVFKMBD HBQB0HlEg2M= =M03w -----END PGP SIGNATURE----- Merge tag 'iio-for-5.1a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: First set of new device support, features and cleanup for IIO in the 5.1 cycle A number of interesting new devices supported plus a good set of staging cleanup including one graduation and one drop. New device support * ad56886 - Add support for AD5674R/AD5679R with some minor driver changes to support more channels. * ad7768 - New driver and dt bindings for this 24 bit ADC. * max44009 - New driver and dt bindings for this ambient light sensor. * mpu6050 - Support the ICM 20602 IMU. Minor tweaks due to slightly different register map. * NPCM adc - New driver and dt bindings for this BMC ADC. * Sensiron SGP30 - Modifiers for ethanol and H2. - New driver and dt bindings. - Follow patch added self cleaning support. * Sensiron SPS30 - New channel type for mass concentration. - New driver and bindings. - Minor tidy up patch followed (drop fmt specifier as unused) * st_pressure - lps22hh support. ID plus information structures and dt bindings. * ti-ads124s08 - Add binding doc and driver. Staging graduations * ad7606 driver and bindings. Staging drops * ad7152 CDC driver dropped. This part is near EoL and no one is known to be using it. If anyone surfaces obviously we can bring the driver back. If not, good to drop it to avoid wasting anyone's time cleaning it up. New features * bme680 - DT support and bindings doc. * isl29018 - Add regulator for VCC. * mag3110 - Add regulators for supplies. * meson-saradc - Support the temperature sensors of more SoCs. * mma8452 - Add regulators for power suplies and binding docs to reflect them. * st-accel - Support the undocumented but it seems fairly common _ONT ACPI method to specify orientation of the sensor. Cleanup, minor fixes and fixes for staging driver that have been broken a long time * ad5933 - Drop platform data alternative to specifying the reference voltage using a regulator. - Use the clock framework to contorl the reference clock. - Add a DT binding doc to cover the defacto binding. * ad7280a - Split up some big functions to improve readability. * ad7606 - Allow for timeout if interrupt never occurs. - Use devm functions to simplify probe and remove. - Use the find_closest macro to avoid need for precise values from userspace. - Add missing vendor prefixes for various DT properties. Note the driver is in staging still and there are no known devicetrees. - Add explict OF device ID table. - Simplify the Kconfig choices - Change to a threaded IRQ. - SPDX and simple stype fixes. * ad7816 - Drop unnecessary variable init. * ad9523 - Check a return value that was ignored. * ad9833 - Drop platform data. It was just setting most values to the hardware defaults. - Use the clock framework to provide the input clock. * adt7316 (lots of staging cleanup) - Fix some wrong register / bit definitions - Invert the logic of the check for an ldac pin so it actually makes sense. - Read the right register to get internal vref settings - Allow adt751x chips to use the internal vref for all DAC channels rather than a subset. - Remove dac vref bypass control from parts that don't have one. - Make the store DAC update mode function consistent with the show one. - Fix some spellings and other minor tidy up. - Avoid passing irq numbers around by putting all the irq logic in one place. - Fix an issue with the resolution of DAC control. - Fix support of the high resolution DAC mode (for temp proportional output) where supported. - Fix DAC read and write calculations. * st_lsm6dsx - Drop an unused variable (set but not read) * xilinx-xadc - Check an unhandled return value. * tag 'iio-for-5.1a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (67 commits) iio: chemical: sps30: remove printk format specifier staging: iio: frequency: ad9833: Load clock using clock framework staging: iio: frequency: ad9833: Get frequency value statically dt-bindings: iio: light: Add max44009 iio: light: add driver for MAX44009 dt-bindings: iio: adc: Add docs for AD7768-1 iio: adc: Add AD7768-1 ADC basic support staging: iio: cdc: ad7152: remove driver completely iio: imu: mpu6050: Add support for the ICM 20602 IMU dt-bindings: iio: imu: add icm20602 bindings to mpu6050 dt-bindings: iio: pressure: add LPS22HH bindings iio: st_accel: use ACPI orientation data iio: adc: add NPCM ADC driver dt-binding: iio: add NPCM ADC documentation iio: chemical: sps30: allow changing self cleaning period dt-bindings: iio: chemical: Add bindings for bme680 iio: chemical: bme680: Add device-tree support iio:st_pressure:initial lps22hh sensor support iio: accell: mma8452: add vdd/vddio regulator operation support dt-bindings: iio: accel: mma8452: add power supplies property ...
This commit is contained in:
commit
1dcc3ed4a7
@ -1554,6 +1554,10 @@ What: /sys/bus/iio/devices/iio:deviceX/in_concentration_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_co2_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_ethanol_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_ethanol_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_h2_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_h2_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_voc_raw
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_voc_raw
|
||||
KernelVersion: 4.3
|
||||
@ -1684,4 +1688,19 @@ KernelVersion: 4.18
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Raw (unscaled) phase difference reading from channel Y
|
||||
that can be processed to radians.
|
||||
that can be processed to radians.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm1_input
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm1_input
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm2p5_input
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm2p5_input
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm4_input
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm4_input
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm10_input
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm10_input
|
||||
KernelVersion: 4.22
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Mass concentration reading of particulate matter in ug / m3.
|
||||
pmX consists of particles with aerodynamic diameter less or
|
||||
equal to X micrometers.
|
||||
|
28
Documentation/ABI/testing/sysfs-bus-iio-sps30
Normal file
28
Documentation/ABI/testing/sysfs-bus-iio-sps30
Normal file
@ -0,0 +1,28 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/start_cleaning
|
||||
Date: December 2018
|
||||
KernelVersion: 4.22
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Writing 1 starts sensor self cleaning. Internal fan accelerates
|
||||
to its maximum speed and keeps spinning for about 10 seconds in
|
||||
order to blow out accumulated dust.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/cleaning_period
|
||||
Date: January 2019
|
||||
KernelVersion: 5.1
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Sensor is capable of triggering self cleaning periodically.
|
||||
Period can be changed by writing a new value here. Upon reading
|
||||
the current one is returned. Units are seconds.
|
||||
|
||||
Writing 0 disables periodical self cleaning entirely.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/cleaning_period_available
|
||||
Date: January 2019
|
||||
KernelVersion: 5.1
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
The range of available values in seconds represented as the
|
||||
minimum value, the step and the maximum value, all enclosed in
|
||||
square brackets.
|
@ -20,6 +20,10 @@ Optional properties:
|
||||
- interrupt-names: should contain "INT1" and/or "INT2", the accelerometer's
|
||||
interrupt line in use.
|
||||
|
||||
- vdd-supply: phandle to the regulator that provides vdd power to the accelerometer.
|
||||
|
||||
- vddio-supply: phandle to the regulator that provides vddio power to the accelerometer.
|
||||
|
||||
Example:
|
||||
|
||||
mma8453fc@1d {
|
||||
|
65
Documentation/devicetree/bindings/iio/adc/adi,ad7606.txt
Normal file
65
Documentation/devicetree/bindings/iio/adc/adi,ad7606.txt
Normal file
@ -0,0 +1,65 @@
|
||||
Analog Devices AD7606 Simultaneous Sampling ADC
|
||||
|
||||
Required properties for the AD7606:
|
||||
|
||||
- compatible: Must be one of
|
||||
* "adi,ad7605-4"
|
||||
* "adi,ad7606-8"
|
||||
* "adi,ad7606-6"
|
||||
* "adi,ad7606-4"
|
||||
- reg: SPI chip select number for the device
|
||||
- spi-max-frequency: Max SPI frequency to use
|
||||
see: Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
- spi-cpha: See Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
- avcc-supply: phandle to the Avcc power supply
|
||||
- interrupts: IRQ line for the ADC
|
||||
see: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
- adi,conversion-start-gpios: must be the device tree identifier of the CONVST pin.
|
||||
This logic input is used to initiate conversions on the analog
|
||||
input channels. As the line is active high, it should be marked
|
||||
GPIO_ACTIVE_HIGH.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- reset-gpios: must be the device tree identifier of the RESET pin. If specified,
|
||||
it will be asserted during driver probe. As the line is active high,
|
||||
it should be marked GPIO_ACTIVE_HIGH.
|
||||
- standby-gpios: must be the device tree identifier of the STBY pin. This pin is used
|
||||
to place the AD7606 into one of two power-down modes, Standby mode or
|
||||
Shutdown mode. As the line is active low, it should be marked
|
||||
GPIO_ACTIVE_LOW.
|
||||
- adi,first-data-gpios: must be the device tree identifier of the FRSTDATA pin.
|
||||
The FRSTDATA output indicates when the first channel, V1, is
|
||||
being read back on either the parallel, byte or serial interface.
|
||||
As the line is active high, it should be marked GPIO_ACTIVE_HIGH.
|
||||
- adi,range-gpios: must be the device tree identifier of the RANGE pin. The polarity on
|
||||
this pin determines the input range of the analog input channels. If
|
||||
this pin is tied to a logic high, the analog input range is ±10V for
|
||||
all channels. If this pin is tied to a logic low, the analog input range
|
||||
is ±5V for all channels. As the line is active high, it should be marked
|
||||
GPIO_ACTIVE_HIGH.
|
||||
- adi,oversampling-ratio-gpios: must be the device tree identifier of the over-sampling
|
||||
mode pins. As the line is active high, it should be marked
|
||||
GPIO_ACTIVE_HIGH.
|
||||
|
||||
Example:
|
||||
|
||||
adc@0 {
|
||||
compatible = "adi,ad7606-8";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
spi-cpol;
|
||||
|
||||
avcc-supply = <&adc_vref>;
|
||||
|
||||
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-parent = <&gpio>;
|
||||
|
||||
adi,conversion-start-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>;
|
||||
reset-gpios = <&gpio 27 GPIO_ACTIVE_HIGH>;
|
||||
adi,first-data-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>;
|
||||
adi,oversampling-ratio-gpios = <&gpio 18 GPIO_ACTIVE_HIGH
|
||||
&gpio 23 GPIO_ACTIVE_HIGH
|
||||
&gpio 26 GPIO_ACTIVE_HIGH>;
|
||||
standby-gpios = <&gpio 24 GPIO_ACTIVE_LOW>;
|
||||
};
|
41
Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.txt
Normal file
41
Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.txt
Normal file
@ -0,0 +1,41 @@
|
||||
Analog Devices AD7768-1 ADC device driver
|
||||
|
||||
Required properties for the AD7768-1:
|
||||
|
||||
- compatible: Must be "adi,ad7768-1"
|
||||
- reg: SPI chip select number for the device
|
||||
- spi-max-frequency: Max SPI frequency to use
|
||||
see: Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
- clocks: phandle to the master clock (mclk)
|
||||
see: Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
- clock-names: Must be "mclk".
|
||||
- interrupts: IRQ line for the ADC
|
||||
see: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
- vref-supply: vref supply can be used as reference for conversion
|
||||
- adi,sync-in-gpios: must be the device tree identifier of the SYNC-IN pin. Enables
|
||||
synchronization of multiple devices that require simultaneous sampling.
|
||||
A pulse is always required if the configuration is changed in any way, for example
|
||||
if the filter decimation rate changes. As the line is active low, it should
|
||||
be marked GPIO_ACTIVE_LOW.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- reset-gpios : GPIO spec for the RESET pin. If specified, it will be asserted during
|
||||
driver probe. As the line is active low, it should be marked GPIO_ACTIVE_LOW.
|
||||
|
||||
Example:
|
||||
|
||||
adc@0 {
|
||||
compatible = "adi,ad7768-1";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <2000000>;
|
||||
spi-cpol;
|
||||
spi-cpha;
|
||||
vref-supply = <&adc_vref>;
|
||||
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-parent = <&gpio>;
|
||||
adi,sync-in-gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
|
||||
reset-gpios = <&gpio 27 GPIO_ACTIVE_LOW>;
|
||||
clocks = <&ad7768_mclk>;
|
||||
clock-names = "mclk";
|
||||
};
|
@ -23,6 +23,10 @@ Required properties:
|
||||
- #io-channel-cells: must be 1, see ../iio-bindings.txt
|
||||
|
||||
Optional properties:
|
||||
- amlogic,hhi-sysctrl: phandle to the syscon which contains the 5th bit
|
||||
of the TSC (temperature sensor coefficient) on
|
||||
Meson8b and Meson8m2 (which used to calibrate the
|
||||
temperature sensor)
|
||||
- nvmem-cells: phandle to the temperature_calib eFuse cells
|
||||
- nvmem-cell-names: if present (to enable the temperature sensor
|
||||
calibration) this must contain "temperature_calib"
|
||||
|
@ -0,0 +1,35 @@
|
||||
Nuvoton NPCM Analog to Digital Converter (ADC)
|
||||
|
||||
The NPCM ADC is a 10-bit converter for eight channel inputs.
|
||||
|
||||
Required properties:
|
||||
- compatible: "nuvoton,npcm750-adc" for the NPCM7XX BMC.
|
||||
- reg: specifies physical base address and size of the registers.
|
||||
- interrupts: Contain the ADC interrupt with flags for falling edge.
|
||||
|
||||
Optional properties:
|
||||
- clocks: phandle of ADC reference clock, in case the clock is not
|
||||
added the ADC will use the default ADC sample rate.
|
||||
- vref-supply: The regulator supply ADC reference voltage, in case the
|
||||
vref-supply is not added the ADC will use internal voltage
|
||||
reference.
|
||||
|
||||
Required Node in the NPCM7xx BMC:
|
||||
An additional register is present in the NPCM7xx SOC which is
|
||||
assumed to be in the same device tree, with and marked as
|
||||
compatible with "nuvoton,npcm750-rst".
|
||||
|
||||
Example:
|
||||
|
||||
adc: adc@f000c000 {
|
||||
compatible = "nuvoton,npcm750-adc";
|
||||
reg = <0xf000c000 0x8>;
|
||||
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clk NPCM7XX_CLK_ADC>;
|
||||
};
|
||||
|
||||
rst: rst@f0801000 {
|
||||
compatible = "nuvoton,npcm750-rst", "syscon",
|
||||
"simple-mfd";
|
||||
reg = <0xf0801000 0x6C>;
|
||||
};
|
25
Documentation/devicetree/bindings/iio/adc/ti-ads124s08.txt
Normal file
25
Documentation/devicetree/bindings/iio/adc/ti-ads124s08.txt
Normal file
@ -0,0 +1,25 @@
|
||||
* Texas Instruments' ads124s08 and ads124s06 ADC chip
|
||||
|
||||
Required properties:
|
||||
- compatible :
|
||||
"ti,ads124s08"
|
||||
"ti,ads124s06"
|
||||
- reg : spi chip select number for the device
|
||||
|
||||
Recommended properties:
|
||||
- spi-max-frequency : Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
- spi-cpha : Definition as per
|
||||
Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Optional properties:
|
||||
- reset-gpios : GPIO pin used to reset the device.
|
||||
|
||||
Example:
|
||||
adc@0 {
|
||||
compatible = "ti,ads124s08";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
spi-cpha;
|
||||
reset-gpios = <&gpio1 16 GPIO_ACTIVE_LOW>;
|
||||
};
|
11
Documentation/devicetree/bindings/iio/chemical/bme680.txt
Normal file
11
Documentation/devicetree/bindings/iio/chemical/bme680.txt
Normal file
@ -0,0 +1,11 @@
|
||||
Bosch Sensortec BME680 pressure/temperature/humidity/voc sensors
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "bosch,bme680"
|
||||
|
||||
Example:
|
||||
|
||||
bme680@76 {
|
||||
compatible = "bosch,bme680";
|
||||
reg = <0x76>;
|
||||
};
|
@ -0,0 +1,15 @@
|
||||
* Sensirion SGP30/SGPC3 multi-pixel Gas Sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be one of
|
||||
"sensirion,sgp30"
|
||||
"sensirion,sgpc3"
|
||||
- reg: the I2C address of the sensor
|
||||
|
||||
Example:
|
||||
|
||||
gas@58 {
|
||||
compatible = "sensirion,sgp30";
|
||||
reg = <0x58>;
|
||||
};
|
@ -0,0 +1,12 @@
|
||||
* Sensirion SPS30 particulate matter sensor
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "sensirion,sps30"
|
||||
- reg: the I2C address of the sensor
|
||||
|
||||
Example:
|
||||
|
||||
sps30@69 {
|
||||
compatible = "sensirion,sps30";
|
||||
reg = <0x69>;
|
||||
};
|
@ -0,0 +1,26 @@
|
||||
Analog Devices AD5933/AD5934 Impedance Converter, Network Analyzer
|
||||
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD5933.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD5934.pdf
|
||||
|
||||
Required properties:
|
||||
- compatible : should be one of
|
||||
"adi,ad5933"
|
||||
"adi,ad5934"
|
||||
- reg : the I2C address.
|
||||
- vdd-supply : The regulator supply for DVDD, AVDD1 and AVDD2 when they
|
||||
are connected together.
|
||||
|
||||
Optional properties:
|
||||
- clocks : external clock reference.
|
||||
- clock-names : must be "mclk" if clocks is set.
|
||||
|
||||
Example for a I2C device node:
|
||||
|
||||
impedance-analyzer@0d {
|
||||
compatible = "adi,adxl345";
|
||||
reg = <0x0d>;
|
||||
vdd-supply = <&vdd_supply>;
|
||||
clocks = <&ref_clk>;
|
||||
clock-names = "mclk";
|
||||
};
|
@ -11,6 +11,7 @@ Required properties:
|
||||
"invensense,mpu9250"
|
||||
"invensense,mpu9255"
|
||||
"invensense,icm20608"
|
||||
"invensense,icm20602"
|
||||
- reg : the I2C address of the sensor
|
||||
- interrupts: interrupt mapping for IRQ. It should be configured with flags
|
||||
IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or
|
||||
|
24
Documentation/devicetree/bindings/iio/light/max44009.txt
Normal file
24
Documentation/devicetree/bindings/iio/light/max44009.txt
Normal file
@ -0,0 +1,24 @@
|
||||
* MAX44009 Ambient Light Sensor
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: should be "maxim,max44009"
|
||||
- reg: the I2C address of the device (default is <0x4a>)
|
||||
|
||||
Optional properties:
|
||||
|
||||
- interrupts: interrupt mapping for GPIO IRQ. Should be configured with
|
||||
IRQ_TYPE_EDGE_FALLING.
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt client
|
||||
node bindings.
|
||||
|
||||
Example:
|
||||
|
||||
light-sensor@4a {
|
||||
compatible = "maxim,max44009";
|
||||
reg = <0x4a>;
|
||||
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <17 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
@ -77,3 +77,4 @@ Pressure sensors:
|
||||
- st,lps22hb-press
|
||||
- st,lps33hw
|
||||
- st,lps35hw
|
||||
- st,lps22hh
|
||||
|
16
MAINTAINERS
16
MAINTAINERS
@ -854,6 +854,22 @@ S: Supported
|
||||
F: drivers/iio/adc/ad7124.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7124.txt
|
||||
|
||||
ANALOG DEVICES INC AD7606 DRIVER
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
F: drivers/iio/adc/ad7606.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/ad7606.txt
|
||||
|
||||
ANALOG DEVICES INC AD7768-1 DRIVER
|
||||
M: Stefan Popa <stefan.popa@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
F: drivers/iio/adc/ad7768-1.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.txt
|
||||
|
||||
ANALOG DEVICES INC AD9389B DRIVER
|
||||
M: Hans Verkuil <hans.verkuil@cisco.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define MMA8452_STATUS 0x00
|
||||
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
|
||||
@ -107,6 +108,8 @@ struct mma8452_data {
|
||||
u8 data_cfg;
|
||||
const struct mma_chip_info *chip_info;
|
||||
int sleep_val;
|
||||
struct regulator *vdd_reg;
|
||||
struct regulator *vddio_reg;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1534,9 +1537,39 @@ static int mma8452_probe(struct i2c_client *client,
|
||||
mutex_init(&data->lock);
|
||||
data->chip_info = match->data;
|
||||
|
||||
data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
|
||||
if (IS_ERR(data->vdd_reg)) {
|
||||
if (PTR_ERR(data->vdd_reg) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
dev_err(&client->dev, "failed to get VDD regulator!\n");
|
||||
return PTR_ERR(data->vdd_reg);
|
||||
}
|
||||
|
||||
data->vddio_reg = devm_regulator_get(&client->dev, "vddio");
|
||||
if (IS_ERR(data->vddio_reg)) {
|
||||
if (PTR_ERR(data->vddio_reg) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
dev_err(&client->dev, "failed to get VDDIO regulator!\n");
|
||||
return PTR_ERR(data->vddio_reg);
|
||||
}
|
||||
|
||||
ret = regulator_enable(data->vdd_reg);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to enable VDD regulator!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_enable(data->vddio_reg);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to enable VDDIO regulator!\n");
|
||||
goto disable_regulator_vdd;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto disable_regulators;
|
||||
|
||||
switch (ret) {
|
||||
case MMA8451_DEVICE_ID:
|
||||
@ -1549,7 +1582,8 @@ static int mma8452_probe(struct i2c_client *client,
|
||||
break;
|
||||
/* else: fall through */
|
||||
default:
|
||||
return -ENODEV;
|
||||
ret = -ENODEV;
|
||||
goto disable_regulators;
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n",
|
||||
@ -1566,13 +1600,13 @@ static int mma8452_probe(struct i2c_client *client,
|
||||
|
||||
ret = mma8452_reset(client);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto disable_regulators;
|
||||
|
||||
data->data_cfg = MMA8452_DATA_CFG_FS_2G;
|
||||
ret = i2c_smbus_write_byte_data(client, MMA8452_DATA_CFG,
|
||||
data->data_cfg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto disable_regulators;
|
||||
|
||||
/*
|
||||
* By default set transient threshold to max to avoid events if
|
||||
@ -1581,7 +1615,7 @@ static int mma8452_probe(struct i2c_client *client,
|
||||
ret = i2c_smbus_write_byte_data(client, MMA8452_TRANSIENT_THS,
|
||||
MMA8452_TRANSIENT_THS_MASK);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto disable_regulators;
|
||||
|
||||
if (client->irq) {
|
||||
int irq2;
|
||||
@ -1595,7 +1629,7 @@ static int mma8452_probe(struct i2c_client *client,
|
||||
MMA8452_CTRL_REG5,
|
||||
data->chip_info->all_events);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto disable_regulators;
|
||||
|
||||
dev_dbg(&client->dev, "using interrupt line INT1\n");
|
||||
}
|
||||
@ -1604,11 +1638,11 @@ static int mma8452_probe(struct i2c_client *client,
|
||||
MMA8452_CTRL_REG4,
|
||||
data->chip_info->enabled_events);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto disable_regulators;
|
||||
|
||||
ret = mma8452_trigger_setup(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto disable_regulators;
|
||||
}
|
||||
|
||||
data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
|
||||
@ -1661,12 +1695,19 @@ buffer_cleanup:
|
||||
trigger_cleanup:
|
||||
mma8452_trigger_cleanup(indio_dev);
|
||||
|
||||
disable_regulators:
|
||||
regulator_disable(data->vddio_reg);
|
||||
|
||||
disable_regulator_vdd:
|
||||
regulator_disable(data->vdd_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mma8452_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
@ -1678,6 +1719,9 @@ static int mma8452_remove(struct i2c_client *client)
|
||||
mma8452_trigger_cleanup(indio_dev);
|
||||
mma8452_standby(iio_priv(indio_dev));
|
||||
|
||||
regulator_disable(data->vddio_reg);
|
||||
regulator_disable(data->vdd_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1696,6 +1740,18 @@ static int mma8452_runtime_suspend(struct device *dev)
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
ret = regulator_disable(data->vddio_reg);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to disable VDDIO regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_disable(data->vdd_reg);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to disable VDD regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1705,9 +1761,22 @@ static int mma8452_runtime_resume(struct device *dev)
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int ret, sleep_val;
|
||||
|
||||
ret = regulator_enable(data->vdd_reg);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable VDD regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_enable(data->vddio_reg);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable VDDIO regulator\n");
|
||||
regulator_disable(data->vdd_reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mma8452_active(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto runtime_resume_failed;
|
||||
|
||||
ret = mma8452_get_odr_index(data);
|
||||
sleep_val = 1000 / mma8452_samp_freq[ret][0];
|
||||
@ -1717,25 +1786,17 @@ static int mma8452_runtime_resume(struct device *dev)
|
||||
msleep_interruptible(sleep_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mma8452_suspend(struct device *dev)
|
||||
{
|
||||
return mma8452_standby(iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev))));
|
||||
}
|
||||
runtime_resume_failed:
|
||||
regulator_disable(data->vddio_reg);
|
||||
regulator_disable(data->vdd_reg);
|
||||
|
||||
static int mma8452_resume(struct device *dev)
|
||||
{
|
||||
return mma8452_active(iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev))));
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops mma8452_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(mma8452_suspend, mma8452_resume)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(mma8452_runtime_suspend,
|
||||
mma8452_runtime_resume, NULL)
|
||||
};
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
@ -918,12 +919,167 @@ static const struct iio_trigger_ops st_accel_trigger_ops = {
|
||||
#define ST_ACCEL_TRIGGER_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct iio_mount_matrix *
|
||||
get_mount_matrix(const struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
return adata->mount_matrix;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info mount_matrix_ext_info[] = {
|
||||
IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, get_mount_matrix),
|
||||
{ },
|
||||
};
|
||||
|
||||
/* Read ST-specific _ONT orientation data from ACPI and generate an
|
||||
* appropriate mount matrix.
|
||||
*/
|
||||
static int apply_acpi_orientation(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec *channels)
|
||||
{
|
||||
#ifdef CONFIG_ACPI
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
|
||||
struct acpi_device *adev;
|
||||
union acpi_object *ont;
|
||||
union acpi_object *elements;
|
||||
acpi_status status;
|
||||
int ret = -EINVAL;
|
||||
unsigned int val;
|
||||
int i, j;
|
||||
int final_ont[3][3] = { { 0 }, };
|
||||
|
||||
/* For some reason, ST's _ONT translation does not apply directly
|
||||
* to the data read from the sensor. Another translation must be
|
||||
* performed first, as described by the matrix below. Perhaps
|
||||
* ST required this specific translation for the first product
|
||||
* where the device was mounted?
|
||||
*/
|
||||
const int default_ont[3][3] = {
|
||||
{ 0, 1, 0 },
|
||||
{ -1, 0, 0 },
|
||||
{ 0, 0, -1 },
|
||||
};
|
||||
|
||||
|
||||
adev = ACPI_COMPANION(adata->dev);
|
||||
if (!adev)
|
||||
return 0;
|
||||
|
||||
/* Read _ONT data, which should be a package of 6 integers. */
|
||||
status = acpi_evaluate_object(adev->handle, "_ONT", NULL, &buffer);
|
||||
if (status == AE_NOT_FOUND) {
|
||||
return 0;
|
||||
} else if (ACPI_FAILURE(status)) {
|
||||
dev_warn(&indio_dev->dev, "failed to execute _ONT: %d\n",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
ont = buffer.pointer;
|
||||
if (ont->type != ACPI_TYPE_PACKAGE || ont->package.count != 6)
|
||||
goto out;
|
||||
|
||||
/* The first 3 integers provide axis order information.
|
||||
* e.g. 0 1 2 would indicate normal X,Y,Z ordering.
|
||||
* e.g. 1 0 2 indicates that data arrives in order Y,X,Z.
|
||||
*/
|
||||
elements = ont->package.elements;
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (elements[i].type != ACPI_TYPE_INTEGER)
|
||||
goto out;
|
||||
|
||||
val = elements[i].integer.value;
|
||||
if (val < 0 || val > 2)
|
||||
goto out;
|
||||
|
||||
/* Avoiding full matrix multiplication, we simply reorder the
|
||||
* columns in the default_ont matrix according to the
|
||||
* ordering provided by _ONT.
|
||||
*/
|
||||
final_ont[0][i] = default_ont[0][val];
|
||||
final_ont[1][i] = default_ont[1][val];
|
||||
final_ont[2][i] = default_ont[2][val];
|
||||
}
|
||||
|
||||
/* The final 3 integers provide sign flip information.
|
||||
* 0 means no change, 1 means flip.
|
||||
* e.g. 0 0 1 means that Z data should be sign-flipped.
|
||||
* This is applied after the axis reordering from above.
|
||||
*/
|
||||
elements += 3;
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (elements[i].type != ACPI_TYPE_INTEGER)
|
||||
goto out;
|
||||
|
||||
val = elements[i].integer.value;
|
||||
if (val != 0 && val != 1)
|
||||
goto out;
|
||||
if (!val)
|
||||
continue;
|
||||
|
||||
/* Flip the values in the indicated column */
|
||||
final_ont[0][i] *= -1;
|
||||
final_ont[1][i] *= -1;
|
||||
final_ont[2][i] *= -1;
|
||||
}
|
||||
|
||||
/* Convert our integer matrix to a string-based iio_mount_matrix */
|
||||
adata->mount_matrix = devm_kmalloc(&indio_dev->dev,
|
||||
sizeof(*adata->mount_matrix),
|
||||
GFP_KERNEL);
|
||||
if (!adata->mount_matrix) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
for (j = 0; j < 3; j++) {
|
||||
int matrix_val = final_ont[i][j];
|
||||
char *str_value;
|
||||
|
||||
switch (matrix_val) {
|
||||
case -1:
|
||||
str_value = "-1";
|
||||
break;
|
||||
case 0:
|
||||
str_value = "0";
|
||||
break;
|
||||
case 1:
|
||||
str_value = "1";
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
adata->mount_matrix->rotation[i * 3 + j] = str_value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Expose the mount matrix via ext_info */
|
||||
for (i = 0; i < indio_dev->num_channels; i++)
|
||||
channels[i].ext_info = mount_matrix_ext_info;
|
||||
|
||||
ret = 0;
|
||||
dev_info(&indio_dev->dev, "computed mount matrix from ACPI\n");
|
||||
|
||||
out:
|
||||
kfree(buffer.pointer);
|
||||
return ret;
|
||||
#else /* !CONFIG_ACPI */
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int st_accel_common_probe(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
struct st_sensors_platform_data *pdata =
|
||||
(struct st_sensors_platform_data *)adata->dev->platform_data;
|
||||
int irq = adata->get_irq_data_ready(indio_dev);
|
||||
struct iio_chan_spec *channels;
|
||||
size_t channels_size;
|
||||
int err;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
@ -942,9 +1098,22 @@ int st_accel_common_probe(struct iio_dev *indio_dev)
|
||||
|
||||
adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS;
|
||||
adata->multiread_bit = adata->sensor_settings->multi_read_bit;
|
||||
indio_dev->channels = adata->sensor_settings->ch;
|
||||
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
|
||||
|
||||
channels_size = indio_dev->num_channels * sizeof(struct iio_chan_spec);
|
||||
channels = devm_kmemdup(&indio_dev->dev,
|
||||
adata->sensor_settings->ch,
|
||||
channels_size, GFP_KERNEL);
|
||||
if (!channels) {
|
||||
err = -ENOMEM;
|
||||
goto st_accel_power_off;
|
||||
}
|
||||
|
||||
if (apply_acpi_orientation(indio_dev, channels))
|
||||
dev_warn(&indio_dev->dev,
|
||||
"failed to apply ACPI orientation data: %d\n", err);
|
||||
|
||||
indio_dev->channels = channels;
|
||||
adata->current_fullscale = (struct st_sensor_fullscale_avl *)
|
||||
&adata->sensor_settings->fs.fs_avl[0];
|
||||
adata->odr = adata->sensor_settings->odr.odr_avl[0].hz;
|
||||
|
@ -69,6 +69,33 @@ config AD7476
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7476.
|
||||
|
||||
config AD7606
|
||||
tristate
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
|
||||
config AD7606_IFACE_PARALLEL
|
||||
tristate "Analog Devices AD7606 ADC driver with parallel interface support"
|
||||
depends on HAS_IOMEM
|
||||
select AD7606
|
||||
help
|
||||
Say yes here to build parallel interface support for Analog Devices:
|
||||
ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC).
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7606_parallel.
|
||||
|
||||
config AD7606_IFACE_SPI
|
||||
tristate "Analog Devices AD7606 ADC driver with spi interface support"
|
||||
depends on SPI
|
||||
select AD7606
|
||||
help
|
||||
Say yes here to build spi interface support for Analog Devices:
|
||||
ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC).
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7606_spi.
|
||||
|
||||
config AD7766
|
||||
tristate "Analog Devices AD7766/AD7767 ADC driver"
|
||||
depends on SPI_MASTER
|
||||
@ -81,6 +108,19 @@ config AD7766
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ad7766.
|
||||
|
||||
config AD7768_1
|
||||
tristate "Analog Devices AD7768-1 ADC driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7768-1 SPI
|
||||
simultaneously sampling sigma-delta analog to digital converter (ADC).
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ad7768-1.
|
||||
|
||||
config AD7791
|
||||
tristate "Analog Devices AD7791 ADC driver"
|
||||
depends on SPI
|
||||
@ -576,6 +616,16 @@ config NAU7802
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called nau7802.
|
||||
|
||||
config NPCM_ADC
|
||||
tristate "Nuvoton NPCM ADC driver"
|
||||
depends on ARCH_NPCM || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Say yes here to build support for Nuvoton NPCM ADC.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called npcm_adc.
|
||||
|
||||
config PALMAS_GPADC
|
||||
tristate "TI Palmas General Purpose ADC"
|
||||
depends on MFD_PALMAS
|
||||
@ -908,6 +958,16 @@ config TI_ADS8688
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-ads8688.
|
||||
|
||||
config TI_ADS124S08
|
||||
tristate "Texas Instruments ADS124S08"
|
||||
depends on SPI && OF
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments ADS124S08
|
||||
and ADS124S06 ADC chips
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-ads124s08.
|
||||
|
||||
config TI_AM335X_ADC
|
||||
tristate "TI's AM335X ADC driver"
|
||||
depends on MFD_TI_AM335X_TSCADC && HAS_DMA
|
||||
|
@ -11,7 +11,11 @@ obj-$(CONFIG_AD7291) += ad7291.o
|
||||
obj-$(CONFIG_AD7298) += ad7298.o
|
||||
obj-$(CONFIG_AD7923) += ad7923.o
|
||||
obj-$(CONFIG_AD7476) += ad7476.o
|
||||
obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o
|
||||
obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o
|
||||
obj-$(CONFIG_AD7606) += ad7606.o
|
||||
obj-$(CONFIG_AD7766) += ad7766.o
|
||||
obj-$(CONFIG_AD7768_1) += ad7768-1.o
|
||||
obj-$(CONFIG_AD7791) += ad7791.o
|
||||
obj-$(CONFIG_AD7793) += ad7793.o
|
||||
obj-$(CONFIG_AD7887) += ad7887.o
|
||||
@ -55,6 +59,7 @@ obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
|
||||
obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
|
||||
obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
|
||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
|
||||
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
|
||||
obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
|
||||
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
|
||||
@ -81,6 +86,7 @@ obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
|
||||
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
|
||||
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
|
||||
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
|
||||
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
|
||||
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
|
||||
|
@ -1,28 +1,29 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* AD7606 SPI ADC driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/util_macros.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
#include "ad7606.h"
|
||||
|
||||
@ -30,8 +31,12 @@
|
||||
* Scales are computed as 5000/32768 and 10000/32768 respectively,
|
||||
* so that when applied to the raw values they provide mV values
|
||||
*/
|
||||
static const unsigned int scale_avail[2][2] = {
|
||||
{0, 152588}, {0, 305176}
|
||||
static const unsigned int scale_avail[2] = {
|
||||
152588, 305176
|
||||
};
|
||||
|
||||
static const unsigned int ad7606_oversampling_avail[7] = {
|
||||
1, 2, 4, 8, 16, 32, 64,
|
||||
};
|
||||
|
||||
static int ad7606_reset(struct ad7606_state *st)
|
||||
@ -82,36 +87,24 @@ static int ad7606_read_samples(struct ad7606_state *st)
|
||||
static irqreturn_t ad7606_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct ad7606_state *st = iio_priv(pf->indio_dev);
|
||||
|
||||
gpiod_set_value(st->gpio_convst, 1);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad7606_poll_bh_to_ring() bh of trigger launched polling to ring buffer
|
||||
* @work_s: the work struct through which this was scheduled
|
||||
*
|
||||
* Currently there is no option in this driver to disable the saving of
|
||||
* timestamps within the ring.
|
||||
* I think the one copy of this at a time was to avoid problems if the
|
||||
* trigger was set far too high and the reads then locked up the computer.
|
||||
**/
|
||||
static void ad7606_poll_bh_to_ring(struct work_struct *work_s)
|
||||
{
|
||||
struct ad7606_state *st = container_of(work_s, struct ad7606_state,
|
||||
poll_work);
|
||||
struct iio_dev *indio_dev = iio_priv_to_dev(st);
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
ret = ad7606_read_samples(st);
|
||||
if (ret == 0)
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->data,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
gpiod_set_value(st->gpio_convst, 0);
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
/* The rising edge of the CONVST signal starts a new conversion. */
|
||||
gpiod_set_value(st->gpio_convst, 1);
|
||||
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
|
||||
@ -119,12 +112,13 @@ static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
st->done = false;
|
||||
gpiod_set_value(st->gpio_convst, 1);
|
||||
|
||||
ret = wait_event_interruptible(st->wq_data_avail, st->done);
|
||||
if (ret)
|
||||
ret = wait_for_completion_timeout(&st->completion,
|
||||
msecs_to_jiffies(1000));
|
||||
if (!ret) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
ret = ad7606_read_samples(st);
|
||||
if (ret == 0)
|
||||
@ -159,8 +153,8 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
|
||||
*val = (short)ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = scale_avail[st->range][0];
|
||||
*val2 = scale_avail[st->range][1];
|
||||
*val = 0;
|
||||
*val2 = scale_avail[st->range];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
*val = st->oversampling;
|
||||
@ -176,8 +170,8 @@ static ssize_t in_voltage_scale_available_show(struct device *dev,
|
||||
int i, len = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(scale_avail); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",
|
||||
scale_avail[i][0], scale_avail[i][1]);
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
|
||||
scale_avail[i]);
|
||||
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
@ -186,18 +180,6 @@ static ssize_t in_voltage_scale_available_show(struct device *dev,
|
||||
|
||||
static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0);
|
||||
|
||||
static int ad7606_oversampling_get_index(unsigned int val)
|
||||
{
|
||||
unsigned char supported[] = {1, 2, 4, 8, 16, 32, 64};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(supported); i++)
|
||||
if (val == supported[i])
|
||||
return i;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad7606_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
@ -206,36 +188,29 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
DECLARE_BITMAP(values, 3);
|
||||
int ret, i;
|
||||
int i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = -EINVAL;
|
||||
mutex_lock(&st->lock);
|
||||
for (i = 0; i < ARRAY_SIZE(scale_avail); i++)
|
||||
if (val2 == scale_avail[i][1]) {
|
||||
gpiod_set_value(st->gpio_range, i);
|
||||
st->range = i;
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
i = find_closest(val2, scale_avail, ARRAY_SIZE(scale_avail));
|
||||
gpiod_set_value(st->gpio_range, i);
|
||||
st->range = i;
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
if (val2)
|
||||
return -EINVAL;
|
||||
ret = ad7606_oversampling_get_index(val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
i = find_closest(val, ad7606_oversampling_avail,
|
||||
ARRAY_SIZE(ad7606_oversampling_avail));
|
||||
|
||||
values[0] = ret;
|
||||
values[0] = i;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
gpiod_set_array_value(3, st->gpio_os->desc, st->gpio_os->info,
|
||||
values);
|
||||
st->oversampling = val;
|
||||
gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
|
||||
st->gpio_os->info, values);
|
||||
st->oversampling = ad7606_oversampling_avail[i];
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return 0;
|
||||
@ -274,8 +249,7 @@ static const struct attribute_group ad7606_attribute_group_range = {
|
||||
.attrs = ad7606_attributes_range,
|
||||
};
|
||||
|
||||
#define AD760X_CHANNEL(num, mask) \
|
||||
{ \
|
||||
#define AD760X_CHANNEL(num, mask) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = num, \
|
||||
@ -290,7 +264,7 @@ static const struct attribute_group ad7606_attribute_group_range = {
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
}
|
||||
}
|
||||
|
||||
#define AD7605_CHANNEL(num) \
|
||||
AD760X_CHANNEL(num, 0)
|
||||
@ -319,9 +293,7 @@ static const struct iio_chan_spec ad7606_channels[] = {
|
||||
};
|
||||
|
||||
static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
|
||||
/*
|
||||
* More devices added in future
|
||||
*/
|
||||
/* More devices added in future */
|
||||
[ID_AD7605_4] = {
|
||||
.channels = ad7605_channels,
|
||||
.num_channels = 5,
|
||||
@ -347,7 +319,7 @@ static int ad7606_request_gpios(struct ad7606_state *st)
|
||||
{
|
||||
struct device *dev = st->dev;
|
||||
|
||||
st->gpio_convst = devm_gpiod_get(dev, "conversion-start",
|
||||
st->gpio_convst = devm_gpiod_get(dev, "adi,conversion-start",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st->gpio_convst))
|
||||
return PTR_ERR(st->gpio_convst);
|
||||
@ -356,7 +328,8 @@ static int ad7606_request_gpios(struct ad7606_state *st)
|
||||
if (IS_ERR(st->gpio_reset))
|
||||
return PTR_ERR(st->gpio_reset);
|
||||
|
||||
st->gpio_range = devm_gpiod_get_optional(dev, "range", GPIOD_OUT_LOW);
|
||||
st->gpio_range = devm_gpiod_get_optional(dev, "adi,range",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st->gpio_range))
|
||||
return PTR_ERR(st->gpio_range);
|
||||
|
||||
@ -365,7 +338,7 @@ static int ad7606_request_gpios(struct ad7606_state *st)
|
||||
if (IS_ERR(st->gpio_standby))
|
||||
return PTR_ERR(st->gpio_standby);
|
||||
|
||||
st->gpio_frstdata = devm_gpiod_get_optional(dev, "first-data",
|
||||
st->gpio_frstdata = devm_gpiod_get_optional(dev, "adi,first-data",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(st->gpio_frstdata))
|
||||
return PTR_ERR(st->gpio_frstdata);
|
||||
@ -373,13 +346,17 @@ static int ad7606_request_gpios(struct ad7606_state *st)
|
||||
if (!st->chip_info->has_oversampling)
|
||||
return 0;
|
||||
|
||||
st->gpio_os = devm_gpiod_get_array_optional(dev, "oversampling-ratio",
|
||||
st->gpio_os = devm_gpiod_get_array_optional(dev,
|
||||
"adi,oversampling-ratio",
|
||||
GPIOD_OUT_LOW);
|
||||
return PTR_ERR_OR_ZERO(st->gpio_os);
|
||||
}
|
||||
|
||||
/**
|
||||
* Interrupt handler
|
||||
/*
|
||||
* The BUSY signal indicates when conversions are in progress, so when a rising
|
||||
* edge of CONVST is applied, BUSY goes logic high and transitions low at the
|
||||
* end of the entire conversion process. The falling edge of the BUSY signal
|
||||
* triggers this interrupt.
|
||||
*/
|
||||
static irqreturn_t ad7606_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
@ -387,37 +364,87 @@ static irqreturn_t ad7606_interrupt(int irq, void *dev_id)
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
schedule_work(&st->poll_work);
|
||||
gpiod_set_value(st->gpio_convst, 0);
|
||||
iio_trigger_poll_chained(st->trig);
|
||||
} else {
|
||||
st->done = true;
|
||||
wake_up_interruptible(&st->wq_data_avail);
|
||||
complete(&st->completion);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
};
|
||||
|
||||
static int ad7606_validate_trigger(struct iio_dev *indio_dev,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (st->trig != trig)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7606_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_triggered_buffer_postenable(indio_dev);
|
||||
gpiod_set_value(st->gpio_convst, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7606_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
gpiod_set_value(st->gpio_convst, 0);
|
||||
|
||||
return iio_triggered_buffer_predisable(indio_dev);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops ad7606_buffer_ops = {
|
||||
.postenable = &ad7606_buffer_postenable,
|
||||
.predisable = &ad7606_buffer_predisable,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7606_info_no_os_or_range = {
|
||||
.read_raw = &ad7606_read_raw,
|
||||
.validate_trigger = &ad7606_validate_trigger,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7606_info_os_and_range = {
|
||||
.read_raw = &ad7606_read_raw,
|
||||
.write_raw = &ad7606_write_raw,
|
||||
.attrs = &ad7606_attribute_group_os_and_range,
|
||||
.validate_trigger = &ad7606_validate_trigger,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7606_info_os = {
|
||||
.read_raw = &ad7606_read_raw,
|
||||
.write_raw = &ad7606_write_raw,
|
||||
.attrs = &ad7606_attribute_group_os,
|
||||
.validate_trigger = &ad7606_validate_trigger,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7606_info_range = {
|
||||
.read_raw = &ad7606_read_raw,
|
||||
.write_raw = &ad7606_write_raw,
|
||||
.attrs = &ad7606_attribute_group_range,
|
||||
.validate_trigger = &ad7606_validate_trigger,
|
||||
};
|
||||
|
||||
static const struct iio_trigger_ops ad7606_trigger_ops = {
|
||||
.validate_device = iio_trigger_validate_own_device,
|
||||
};
|
||||
|
||||
static void ad7606_regulator_disable(void *data)
|
||||
{
|
||||
struct ad7606_state *st = data;
|
||||
|
||||
regulator_disable(st->reg);
|
||||
}
|
||||
|
||||
int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
||||
const char *name, unsigned int id,
|
||||
const struct ad7606_bus_ops *bops)
|
||||
@ -431,6 +458,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
|
||||
st->dev = dev;
|
||||
mutex_init(&st->lock);
|
||||
@ -439,7 +467,6 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
||||
/* tied to logic low, analog input range is +/- 5V */
|
||||
st->range = 0;
|
||||
st->oversampling = 1;
|
||||
INIT_WORK(&st->poll_work, &ad7606_poll_bh_to_ring);
|
||||
|
||||
st->reg = devm_regulator_get(dev, "avcc");
|
||||
if (IS_ERR(st->reg))
|
||||
@ -451,11 +478,15 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(dev, ad7606_regulator_disable, st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->chip_info = &ad7606_chip_info_tbl[id];
|
||||
|
||||
ret = ad7606_request_gpios(st);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
return ret;
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
if (st->gpio_os) {
|
||||
@ -474,56 +505,45 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
|
||||
init_waitqueue_head(&st->wq_data_avail);
|
||||
init_completion(&st->completion);
|
||||
|
||||
ret = ad7606_reset(st);
|
||||
if (ret)
|
||||
dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
|
||||
|
||||
ret = request_irq(irq, ad7606_interrupt, IRQF_TRIGGER_FALLING, name,
|
||||
indio_dev);
|
||||
st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
|
||||
indio_dev->name, indio_dev->id);
|
||||
if (!st->trig)
|
||||
return -ENOMEM;
|
||||
|
||||
st->trig->ops = &ad7606_trigger_ops;
|
||||
st->trig->dev.parent = dev;
|
||||
iio_trigger_set_drvdata(st->trig, indio_dev);
|
||||
ret = devm_iio_trigger_register(dev, st->trig);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
return ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &ad7606_trigger_handler,
|
||||
NULL, NULL);
|
||||
indio_dev->trig = iio_trigger_get(st->trig);
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq,
|
||||
NULL,
|
||||
&ad7606_interrupt,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
name, indio_dev);
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
|
||||
&iio_pollfunc_store_time,
|
||||
&ad7606_trigger_handler,
|
||||
&ad7606_buffer_ops);
|
||||
if (ret)
|
||||
goto error_unregister_ring;
|
||||
return ret;
|
||||
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
|
||||
return 0;
|
||||
error_unregister_ring:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
error_free_irq:
|
||||
free_irq(irq, indio_dev);
|
||||
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
return ret;
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad7606_probe);
|
||||
|
||||
int ad7606_remove(struct device *dev, int irq)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad7606_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
free_irq(irq, indio_dev);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad7606_remove);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int ad7606_suspend(struct device *dev)
|
@ -1,9 +1,8 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* AD7606 ADC driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef IIO_ADC_AD7606_H_
|
||||
@ -15,7 +14,6 @@
|
||||
* @num_channels: number of channels
|
||||
* @has_oversampling: whether the device has oversampling support
|
||||
*/
|
||||
|
||||
struct ad7606_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
@ -27,13 +25,9 @@ struct ad7606_chip_info {
|
||||
* @dev pointer to kernel device
|
||||
* @chip_info entry in the table of chips that describes this device
|
||||
* @reg regulator info for the the power supply of the device
|
||||
* @poll_work work struct for continuously reading data from the device
|
||||
* into an IIO triggered buffer
|
||||
* @wq_data_avail wait queue struct for buffer mode
|
||||
* @bops bus operations (SPI or parallel)
|
||||
* @range voltage range selection, selects which scale to apply
|
||||
* @oversampling oversampling selection
|
||||
* @done marks whether reading data is done
|
||||
* @base_address address from where to read data in parallel operation
|
||||
* @lock protect sensor state from concurrent accesses to GPIOs
|
||||
* @gpio_convst GPIO descriptor for conversion start signal (CONVST)
|
||||
@ -44,19 +38,17 @@ struct ad7606_chip_info {
|
||||
* @gpio_frstdata GPIO descriptor for reading from device when data
|
||||
* is being read on the first channel
|
||||
* @gpio_os GPIO descriptors to control oversampling on the device
|
||||
* @complete completion to indicate end of conversion
|
||||
* @trig The IIO trigger associated with the device.
|
||||
* @data buffer for reading data from the device
|
||||
*/
|
||||
|
||||
struct ad7606_state {
|
||||
struct device *dev;
|
||||
const struct ad7606_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
struct work_struct poll_work;
|
||||
wait_queue_head_t wq_data_avail;
|
||||
const struct ad7606_bus_ops *bops;
|
||||
unsigned int range;
|
||||
unsigned int oversampling;
|
||||
bool done;
|
||||
void __iomem *base_address;
|
||||
|
||||
struct mutex lock; /* protect sensor state */
|
||||
@ -66,6 +58,8 @@ struct ad7606_state {
|
||||
struct gpio_desc *gpio_standby;
|
||||
struct gpio_desc *gpio_frstdata;
|
||||
struct gpio_descs *gpio_os;
|
||||
struct iio_trigger *trig;
|
||||
struct completion completion;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
@ -87,7 +81,6 @@ struct ad7606_bus_ops {
|
||||
int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
|
||||
const char *name, unsigned int id,
|
||||
const struct ad7606_bus_ops *bops);
|
||||
int ad7606_remove(struct device *dev, int irq);
|
||||
|
||||
enum ad7606_supported_device_ids {
|
||||
ID_AD7605_4,
|
@ -1,9 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* AD7606 Parallel Interface ADC driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -27,7 +26,7 @@ static int ad7606_par16_read_block(struct device *dev,
|
||||
}
|
||||
|
||||
static const struct ad7606_bus_ops ad7606_par16_bops = {
|
||||
.read_block = ad7606_par16_read_block,
|
||||
.read_block = ad7606_par16_read_block,
|
||||
};
|
||||
|
||||
static int ad7606_par8_read_block(struct device *dev,
|
||||
@ -42,7 +41,7 @@ static int ad7606_par8_read_block(struct device *dev,
|
||||
}
|
||||
|
||||
static const struct ad7606_bus_ops ad7606_par8_bops = {
|
||||
.read_block = ad7606_par8_read_block,
|
||||
.read_block = ad7606_par8_read_block,
|
||||
};
|
||||
|
||||
static int ad7606_par_probe(struct platform_device *pdev)
|
||||
@ -72,40 +71,33 @@ static int ad7606_par_probe(struct platform_device *pdev)
|
||||
&ad7606_par8_bops);
|
||||
}
|
||||
|
||||
static int ad7606_par_remove(struct platform_device *pdev)
|
||||
{
|
||||
return ad7606_remove(&pdev->dev, platform_get_irq(pdev, 0));
|
||||
}
|
||||
|
||||
static const struct platform_device_id ad7606_driver_ids[] = {
|
||||
{
|
||||
.name = "ad7605-4",
|
||||
.driver_data = ID_AD7605_4,
|
||||
}, {
|
||||
.name = "ad7606-8",
|
||||
.driver_data = ID_AD7606_8,
|
||||
}, {
|
||||
.name = "ad7606-6",
|
||||
.driver_data = ID_AD7606_6,
|
||||
}, {
|
||||
.name = "ad7606-4",
|
||||
.driver_data = ID_AD7606_4,
|
||||
},
|
||||
{ .name = "ad7605-4", .driver_data = ID_AD7605_4, },
|
||||
{ .name = "ad7606-4", .driver_data = ID_AD7606_4, },
|
||||
{ .name = "ad7606-6", .driver_data = ID_AD7606_6, },
|
||||
{ .name = "ad7606-8", .driver_data = ID_AD7606_8, },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(platform, ad7606_driver_ids);
|
||||
|
||||
static const struct of_device_id ad7606_of_match[] = {
|
||||
{ .compatible = "adi,ad7605-4" },
|
||||
{ .compatible = "adi,ad7606-4" },
|
||||
{ .compatible = "adi,ad7606-6" },
|
||||
{ .compatible = "adi,ad7606-8" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad7606_of_match);
|
||||
|
||||
static struct platform_driver ad7606_driver = {
|
||||
.probe = ad7606_par_probe,
|
||||
.remove = ad7606_par_remove,
|
||||
.id_table = ad7606_driver_ids,
|
||||
.driver = {
|
||||
.name = "ad7606",
|
||||
.pm = AD7606_PM_OPS,
|
||||
.name = "ad7606",
|
||||
.pm = AD7606_PM_OPS,
|
||||
.of_match_table = ad7606_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(ad7606_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
@ -1,9 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* AD7606 SPI ADC driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
@ -37,7 +36,7 @@ static int ad7606_spi_read_block(struct device *dev,
|
||||
}
|
||||
|
||||
static const struct ad7606_bus_ops ad7606_spi_bops = {
|
||||
.read_block = ad7606_spi_read_block,
|
||||
.read_block = ad7606_spi_read_block,
|
||||
};
|
||||
|
||||
static int ad7606_spi_probe(struct spi_device *spi)
|
||||
@ -49,28 +48,32 @@ static int ad7606_spi_probe(struct spi_device *spi)
|
||||
&ad7606_spi_bops);
|
||||
}
|
||||
|
||||
static int ad7606_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
return ad7606_remove(&spi->dev, spi->irq);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7606_id[] = {
|
||||
{"ad7605-4", ID_AD7605_4},
|
||||
{"ad7606-8", ID_AD7606_8},
|
||||
{"ad7606-6", ID_AD7606_6},
|
||||
{"ad7606-4", ID_AD7606_4},
|
||||
static const struct spi_device_id ad7606_id_table[] = {
|
||||
{ "ad7605-4", ID_AD7605_4 },
|
||||
{ "ad7606-4", ID_AD7606_4 },
|
||||
{ "ad7606-6", ID_AD7606_6 },
|
||||
{ "ad7606-8", ID_AD7606_8 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7606_id);
|
||||
MODULE_DEVICE_TABLE(spi, ad7606_id_table);
|
||||
|
||||
static const struct of_device_id ad7606_of_match[] = {
|
||||
{ .compatible = "adi,ad7605-4" },
|
||||
{ .compatible = "adi,ad7606-4" },
|
||||
{ .compatible = "adi,ad7606-6" },
|
||||
{ .compatible = "adi,ad7606-8" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad7606_of_match);
|
||||
|
||||
static struct spi_driver ad7606_driver = {
|
||||
.driver = {
|
||||
.name = "ad7606",
|
||||
.of_match_table = ad7606_of_match,
|
||||
.pm = AD7606_PM_OPS,
|
||||
},
|
||||
.probe = ad7606_spi_probe,
|
||||
.remove = ad7606_spi_remove,
|
||||
.id_table = ad7606_id,
|
||||
.id_table = ad7606_id_table,
|
||||
};
|
||||
module_spi_driver(ad7606_driver);
|
||||
|
459
drivers/iio/adc/ad7768-1.c
Normal file
459
drivers/iio/adc/ad7768-1.c
Normal file
@ -0,0 +1,459 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Analog Devices AD7768-1 SPI ADC driver
|
||||
*
|
||||
* Copyright 2017 Analog Devices Inc.
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
|
||||
/* AD7768 registers definition */
|
||||
#define AD7768_REG_CHIP_TYPE 0x3
|
||||
#define AD7768_REG_PROD_ID_L 0x4
|
||||
#define AD7768_REG_PROD_ID_H 0x5
|
||||
#define AD7768_REG_CHIP_GRADE 0x6
|
||||
#define AD7768_REG_SCRATCH_PAD 0x0A
|
||||
#define AD7768_REG_VENDOR_L 0x0C
|
||||
#define AD7768_REG_VENDOR_H 0x0D
|
||||
#define AD7768_REG_INTERFACE_FORMAT 0x14
|
||||
#define AD7768_REG_POWER_CLOCK 0x15
|
||||
#define AD7768_REG_ANALOG 0x16
|
||||
#define AD7768_REG_ANALOG2 0x17
|
||||
#define AD7768_REG_CONVERSION 0x18
|
||||
#define AD7768_REG_DIGITAL_FILTER 0x19
|
||||
#define AD7768_REG_SINC3_DEC_RATE_MSB 0x1A
|
||||
#define AD7768_REG_SINC3_DEC_RATE_LSB 0x1B
|
||||
#define AD7768_REG_DUTY_CYCLE_RATIO 0x1C
|
||||
#define AD7768_REG_SYNC_RESET 0x1D
|
||||
#define AD7768_REG_GPIO_CONTROL 0x1E
|
||||
#define AD7768_REG_GPIO_WRITE 0x1F
|
||||
#define AD7768_REG_GPIO_READ 0x20
|
||||
#define AD7768_REG_OFFSET_HI 0x21
|
||||
#define AD7768_REG_OFFSET_MID 0x22
|
||||
#define AD7768_REG_OFFSET_LO 0x23
|
||||
#define AD7768_REG_GAIN_HI 0x24
|
||||
#define AD7768_REG_GAIN_MID 0x25
|
||||
#define AD7768_REG_GAIN_LO 0x26
|
||||
#define AD7768_REG_SPI_DIAG_ENABLE 0x28
|
||||
#define AD7768_REG_ADC_DIAG_ENABLE 0x29
|
||||
#define AD7768_REG_DIG_DIAG_ENABLE 0x2A
|
||||
#define AD7768_REG_ADC_DATA 0x2C
|
||||
#define AD7768_REG_MASTER_STATUS 0x2D
|
||||
#define AD7768_REG_SPI_DIAG_STATUS 0x2E
|
||||
#define AD7768_REG_ADC_DIAG_STATUS 0x2F
|
||||
#define AD7768_REG_DIG_DIAG_STATUS 0x30
|
||||
#define AD7768_REG_MCLK_COUNTER 0x31
|
||||
|
||||
/* AD7768_REG_CONVERSION */
|
||||
#define AD7768_CONV_MODE_MSK GENMASK(2, 0)
|
||||
#define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x)
|
||||
|
||||
#define AD7768_RD_FLAG_MSK(x) (BIT(6) | ((x) & 0x3F))
|
||||
#define AD7768_WR_FLAG_MSK(x) ((x) & 0x3F)
|
||||
|
||||
enum ad7768_conv_mode {
|
||||
AD7768_CONTINUOUS,
|
||||
AD7768_ONE_SHOT,
|
||||
AD7768_SINGLE,
|
||||
AD7768_PERIODIC,
|
||||
AD7768_STANDBY
|
||||
};
|
||||
|
||||
enum ad7768_pwrmode {
|
||||
AD7768_ECO_MODE = 0,
|
||||
AD7768_MED_MODE = 2,
|
||||
AD7768_FAST_MODE = 3
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ad7768_channels[] = {
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 24,
|
||||
.storagebits = 32,
|
||||
.shift = 8,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
struct ad7768_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *vref;
|
||||
struct mutex lock;
|
||||
struct completion completion;
|
||||
struct iio_trigger *trig;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
union {
|
||||
__be32 d32;
|
||||
u8 d8[2];
|
||||
} data ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int addr,
|
||||
unsigned int len)
|
||||
{
|
||||
unsigned int shift;
|
||||
int ret;
|
||||
|
||||
shift = 32 - (8 * len);
|
||||
st->data.d8[0] = AD7768_RD_FLAG_MSK(addr);
|
||||
|
||||
ret = spi_write_then_read(st->spi, st->data.d8, 1,
|
||||
&st->data.d32, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (be32_to_cpu(st->data.d32) >> shift);
|
||||
}
|
||||
|
||||
static int ad7768_spi_reg_write(struct ad7768_state *st,
|
||||
unsigned int addr,
|
||||
unsigned int val)
|
||||
{
|
||||
st->data.d8[0] = AD7768_WR_FLAG_MSK(addr);
|
||||
st->data.d8[1] = val & 0xFF;
|
||||
|
||||
return spi_write(st->spi, st->data.d8, 2);
|
||||
}
|
||||
|
||||
static int ad7768_set_mode(struct ad7768_state *st,
|
||||
enum ad7768_conv_mode mode)
|
||||
{
|
||||
int regval;
|
||||
|
||||
regval = ad7768_spi_reg_read(st, AD7768_REG_CONVERSION, 1);
|
||||
if (regval < 0)
|
||||
return regval;
|
||||
|
||||
regval &= ~AD7768_CONV_MODE_MSK;
|
||||
regval |= AD7768_CONV_MODE(mode);
|
||||
|
||||
return ad7768_spi_reg_write(st, AD7768_REG_CONVERSION, regval);
|
||||
}
|
||||
|
||||
static int ad7768_scan_direct(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7768_state *st = iio_priv(indio_dev);
|
||||
int readval, ret;
|
||||
|
||||
reinit_completion(&st->completion);
|
||||
|
||||
ret = ad7768_set_mode(st, AD7768_ONE_SHOT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = wait_for_completion_timeout(&st->completion,
|
||||
msecs_to_jiffies(1000));
|
||||
if (!ret)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
readval = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
|
||||
if (readval < 0)
|
||||
return readval;
|
||||
/*
|
||||
* Any SPI configuration of the AD7768-1 can only be
|
||||
* performed in continuous conversion mode.
|
||||
*/
|
||||
ret = ad7768_set_mode(st, AD7768_CONTINUOUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return readval;
|
||||
}
|
||||
|
||||
static int ad7768_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg,
|
||||
unsigned int writeval,
|
||||
unsigned int *readval)
|
||||
{
|
||||
struct ad7768_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
if (readval) {
|
||||
ret = ad7768_spi_reg_read(st, reg, 1);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
*readval = ret;
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = ad7768_spi_reg_write(st, reg, writeval);
|
||||
}
|
||||
err_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7768_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long info)
|
||||
{
|
||||
struct ad7768_state *st = iio_priv(indio_dev);
|
||||
int scale_uv, ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad7768_scan_direct(indio_dev);
|
||||
if (ret >= 0)
|
||||
*val = ret;
|
||||
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_uv = regulator_get_voltage(st->vref);
|
||||
if (scale_uv < 0)
|
||||
return scale_uv;
|
||||
|
||||
*val = (scale_uv * 2) / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad7768_info = {
|
||||
.read_raw = &ad7768_read_raw,
|
||||
.debugfs_reg_access = &ad7768_reg_access,
|
||||
};
|
||||
|
||||
static int ad7768_setup(struct ad7768_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Two writes to the SPI_RESET[1:0] bits are required to initiate
|
||||
* a software reset. The bits must first be set to 11, and then
|
||||
* to 10. When the sequence is detected, the reset occurs.
|
||||
* See the datasheet, page 70.
|
||||
*/
|
||||
ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x3);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set power mode to fast */
|
||||
return ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK,
|
||||
AD7768_FAST_MODE);
|
||||
}
|
||||
|
||||
static irqreturn_t ad7768_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7768_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
ret = spi_read(st->spi, &st->data.d32, 3);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &st->data.d32,
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
err_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t ad7768_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_id;
|
||||
struct ad7768_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
iio_trigger_poll(st->trig);
|
||||
else
|
||||
complete(&st->completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
};
|
||||
|
||||
static int ad7768_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7768_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_triggered_buffer_postenable(indio_dev);
|
||||
/*
|
||||
* Write a 1 to the LSB of the INTERFACE_FORMAT register to enter
|
||||
* continuous read mode. Subsequent data reads do not require an
|
||||
* initial 8-bit write to query the ADC_DATA register.
|
||||
*/
|
||||
return ad7768_spi_reg_write(st, AD7768_REG_INTERFACE_FORMAT, 0x01);
|
||||
}
|
||||
|
||||
static int ad7768_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7768_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* To exit continuous read mode, perform a single read of the ADC_DATA
|
||||
* reg (0x2C), which allows further configuration of the device.
|
||||
*/
|
||||
ret = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return iio_triggered_buffer_predisable(indio_dev);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops ad7768_buffer_ops = {
|
||||
.postenable = &ad7768_buffer_postenable,
|
||||
.predisable = &ad7768_buffer_predisable,
|
||||
};
|
||||
|
||||
static const struct iio_trigger_ops ad7768_trigger_ops = {
|
||||
.validate_device = iio_trigger_validate_own_device,
|
||||
};
|
||||
|
||||
static void ad7768_regulator_disable(void *data)
|
||||
{
|
||||
struct ad7768_state *st = data;
|
||||
|
||||
regulator_disable(st->vref);
|
||||
}
|
||||
|
||||
static int ad7768_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7768_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
st->spi = spi;
|
||||
|
||||
st->vref = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(st->vref))
|
||||
return PTR_ERR(st->vref);
|
||||
|
||||
ret = regulator_enable(st->vref);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable specified vref supply\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(&spi->dev, ad7768_regulator_disable, st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
mutex_init(&st->lock);
|
||||
|
||||
indio_dev->channels = ad7768_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad7768_channels);
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad7768_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
|
||||
|
||||
ret = ad7768_setup(st);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "AD7768 setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
|
||||
indio_dev->name, indio_dev->id);
|
||||
if (!st->trig)
|
||||
return -ENOMEM;
|
||||
|
||||
st->trig->ops = &ad7768_trigger_ops;
|
||||
st->trig->dev.parent = &spi->dev;
|
||||
iio_trigger_set_drvdata(st->trig, indio_dev);
|
||||
ret = devm_iio_trigger_register(&spi->dev, st->trig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->trig = iio_trigger_get(st->trig);
|
||||
|
||||
init_completion(&st->completion);
|
||||
|
||||
ret = devm_request_irq(&spi->dev, spi->irq,
|
||||
&ad7768_interrupt,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
indio_dev->name, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
|
||||
&iio_pollfunc_store_time,
|
||||
&ad7768_trigger_handler,
|
||||
&ad7768_buffer_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7768_id_table[] = {
|
||||
{ "ad7768-1", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7768_id_table);
|
||||
|
||||
static const struct of_device_id ad7768_of_match[] = {
|
||||
{ .compatible = "adi,ad7768-1" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad7768_of_match);
|
||||
|
||||
static struct spi_driver ad7768_driver = {
|
||||
.driver = {
|
||||
.name = "ad7768-1",
|
||||
.of_match_table = ad7768_of_match,
|
||||
},
|
||||
.probe = ad7768_probe,
|
||||
.id_table = ad7768_id_table,
|
||||
};
|
||||
module_spi_driver(ad7768_driver);
|
||||
|
||||
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7768-1 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -26,6 +26,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
|
||||
#define MESON_SAR_ADC_REG0 0x00
|
||||
#define MESON_SAR_ADC_REG0_PANEL_DETECT BIT(31)
|
||||
@ -174,6 +175,9 @@
|
||||
#define MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL GENMASK(6, 0)
|
||||
#define MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED BIT(7)
|
||||
|
||||
#define MESON_HHI_DPLL_TOP_0 0x318
|
||||
#define MESON_HHI_DPLL_TOP_0_TSC_BIT4 BIT(9)
|
||||
|
||||
/* for use with IIO_VAL_INT_PLUS_MICRO */
|
||||
#define MILLION 1000000
|
||||
|
||||
@ -280,6 +284,7 @@ struct meson_sar_adc_priv {
|
||||
struct completion done;
|
||||
int calibbias;
|
||||
int calibscale;
|
||||
struct regmap *tsc_regmap;
|
||||
bool temperature_sensor_calibrated;
|
||||
u8 temperature_sensor_coefficient;
|
||||
u16 temperature_sensor_adc_val;
|
||||
@ -727,6 +732,15 @@ static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->tsc_regmap =
|
||||
syscon_regmap_lookup_by_phandle(indio_dev->dev.parent->of_node,
|
||||
"amlogic,hhi-sysctrl");
|
||||
if (IS_ERR(priv->tsc_regmap)) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
"failed to get amlogic,hhi-sysctrl regmap\n");
|
||||
return PTR_ERR(priv->tsc_regmap);
|
||||
}
|
||||
|
||||
read_len = MESON_SAR_ADC_EFUSE_BYTES;
|
||||
buf = nvmem_cell_read(temperature_calib, &read_len);
|
||||
if (IS_ERR(buf)) {
|
||||
@ -861,6 +875,22 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
|
||||
priv->temperature_sensor_coefficient);
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
|
||||
MESON_SAR_ADC_DELTA_10_TS_C_MASK, regval);
|
||||
|
||||
if (priv->param->temperature_trimming_bits == 5) {
|
||||
if (priv->temperature_sensor_coefficient & BIT(4))
|
||||
regval = MESON_HHI_DPLL_TOP_0_TSC_BIT4;
|
||||
else
|
||||
regval = 0;
|
||||
|
||||
/*
|
||||
* bit [4] (the 5th bit when starting to count at 1)
|
||||
* of the TSC is located in the HHI register area.
|
||||
*/
|
||||
regmap_update_bits(priv->tsc_regmap,
|
||||
MESON_HHI_DPLL_TOP_0,
|
||||
MESON_HHI_DPLL_TOP_0_TSC_BIT4,
|
||||
regval);
|
||||
}
|
||||
} else {
|
||||
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
|
||||
MESON_SAR_ADC_DELTA_10_TS_REVE1, 0);
|
||||
@ -1064,6 +1094,9 @@ static const struct meson_sar_adc_param meson_sar_adc_meson8b_param = {
|
||||
.bandgap_reg = MESON_SAR_ADC_DELTA_10,
|
||||
.regmap_config = &meson_sar_adc_regmap_config_meson8,
|
||||
.resolution = 10,
|
||||
.temperature_trimming_bits = 5,
|
||||
.temperature_multiplier = 10,
|
||||
.temperature_divider = 32,
|
||||
};
|
||||
|
||||
static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = {
|
||||
|
335
drivers/iio/adc/npcm_adc.c
Normal file
335
drivers/iio/adc/npcm_adc.c
Normal file
@ -0,0 +1,335 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Nuvoton Technology corporation.
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
struct npcm_adc {
|
||||
bool int_status;
|
||||
u32 adc_sample_hz;
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
struct clk *adc_clk;
|
||||
wait_queue_head_t wq;
|
||||
struct regulator *vref;
|
||||
struct regmap *rst_regmap;
|
||||
};
|
||||
|
||||
/* NPCM7xx reset module */
|
||||
#define NPCM7XX_IPSRST1_OFFSET 0x020
|
||||
#define NPCM7XX_IPSRST1_ADC_RST BIT(27)
|
||||
|
||||
/* ADC registers */
|
||||
#define NPCM_ADCCON 0x00
|
||||
#define NPCM_ADCDATA 0x04
|
||||
|
||||
/* ADCCON Register Bits */
|
||||
#define NPCM_ADCCON_ADC_INT_EN BIT(21)
|
||||
#define NPCM_ADCCON_REFSEL BIT(19)
|
||||
#define NPCM_ADCCON_ADC_INT_ST BIT(18)
|
||||
#define NPCM_ADCCON_ADC_EN BIT(17)
|
||||
#define NPCM_ADCCON_ADC_RST BIT(16)
|
||||
#define NPCM_ADCCON_ADC_CONV BIT(13)
|
||||
|
||||
#define NPCM_ADCCON_CH_MASK GENMASK(27, 24)
|
||||
#define NPCM_ADCCON_CH(x) ((x) << 24)
|
||||
#define NPCM_ADCCON_DIV_SHIFT 1
|
||||
#define NPCM_ADCCON_DIV_MASK GENMASK(8, 1)
|
||||
#define NPCM_ADC_DATA_MASK(x) ((x) & GENMASK(9, 0))
|
||||
|
||||
#define NPCM_ADC_ENABLE (NPCM_ADCCON_ADC_EN | NPCM_ADCCON_ADC_INT_EN)
|
||||
|
||||
/* ADC General Definition */
|
||||
#define NPCM_RESOLUTION_BITS 10
|
||||
#define NPCM_INT_VREF_MV 2000
|
||||
|
||||
#define NPCM_ADC_CHAN(ch) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = ch, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec npcm_adc_iio_channels[] = {
|
||||
NPCM_ADC_CHAN(0),
|
||||
NPCM_ADC_CHAN(1),
|
||||
NPCM_ADC_CHAN(2),
|
||||
NPCM_ADC_CHAN(3),
|
||||
NPCM_ADC_CHAN(4),
|
||||
NPCM_ADC_CHAN(5),
|
||||
NPCM_ADC_CHAN(6),
|
||||
NPCM_ADC_CHAN(7),
|
||||
};
|
||||
|
||||
static irqreturn_t npcm_adc_isr(int irq, void *data)
|
||||
{
|
||||
u32 regtemp;
|
||||
struct iio_dev *indio_dev = data;
|
||||
struct npcm_adc *info = iio_priv(indio_dev);
|
||||
|
||||
regtemp = ioread32(info->regs + NPCM_ADCCON);
|
||||
if (regtemp & NPCM_ADCCON_ADC_INT_ST) {
|
||||
iowrite32(regtemp, info->regs + NPCM_ADCCON);
|
||||
wake_up_interruptible(&info->wq);
|
||||
info->int_status = true;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int npcm_adc_read(struct npcm_adc *info, int *val, u8 channel)
|
||||
{
|
||||
int ret;
|
||||
u32 regtemp;
|
||||
|
||||
/* Select ADC channel */
|
||||
regtemp = ioread32(info->regs + NPCM_ADCCON);
|
||||
regtemp &= ~NPCM_ADCCON_CH_MASK;
|
||||
info->int_status = false;
|
||||
iowrite32(regtemp | NPCM_ADCCON_CH(channel) |
|
||||
NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
|
||||
|
||||
ret = wait_event_interruptible_timeout(info->wq, info->int_status,
|
||||
msecs_to_jiffies(10));
|
||||
if (ret == 0) {
|
||||
regtemp = ioread32(info->regs + NPCM_ADCCON);
|
||||
if ((regtemp & NPCM_ADCCON_ADC_CONV) && info->rst_regmap) {
|
||||
/* if conversion failed - reset ADC module */
|
||||
regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET,
|
||||
NPCM7XX_IPSRST1_ADC_RST);
|
||||
msleep(100);
|
||||
regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET,
|
||||
0x0);
|
||||
msleep(100);
|
||||
|
||||
/* Enable ADC and start conversion module */
|
||||
iowrite32(NPCM_ADC_ENABLE | NPCM_ADCCON_ADC_CONV,
|
||||
info->regs + NPCM_ADCCON);
|
||||
dev_err(info->dev, "RESET ADC Complete\n");
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = NPCM_ADC_DATA_MASK(ioread32(info->regs + NPCM_ADCDATA));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int npcm_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
int vref_uv;
|
||||
struct npcm_adc *info = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = npcm_adc_read(info, val, chan->channel);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret) {
|
||||
dev_err(info->dev, "NPCM ADC read failed\n");
|
||||
return ret;
|
||||
}
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (info->vref) {
|
||||
vref_uv = regulator_get_voltage(info->vref);
|
||||
*val = vref_uv / 1000;
|
||||
} else {
|
||||
*val = NPCM_INT_VREF_MV;
|
||||
}
|
||||
*val2 = NPCM_RESOLUTION_BITS;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = info->adc_sample_hz;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info npcm_adc_iio_info = {
|
||||
.read_raw = &npcm_adc_read_raw,
|
||||
};
|
||||
|
||||
static const struct of_device_id npcm_adc_match[] = {
|
||||
{ .compatible = "nuvoton,npcm750-adc", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, npcm_adc_match);
|
||||
|
||||
static int npcm_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
int irq;
|
||||
u32 div;
|
||||
u32 reg_con;
|
||||
struct resource *res;
|
||||
struct npcm_adc *info;
|
||||
struct iio_dev *indio_dev;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
info = iio_priv(indio_dev);
|
||||
|
||||
info->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
info->adc_clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(info->adc_clk)) {
|
||||
dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n");
|
||||
return PTR_ERR(info->adc_clk);
|
||||
}
|
||||
|
||||
/* calculate ADC clock sample rate */
|
||||
reg_con = ioread32(info->regs + NPCM_ADCCON);
|
||||
div = reg_con & NPCM_ADCCON_DIV_MASK;
|
||||
div = div >> NPCM_ADCCON_DIV_SHIFT;
|
||||
info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2);
|
||||
|
||||
if (of_device_is_compatible(np, "nuvoton,npcm750-adc")) {
|
||||
info->rst_regmap = syscon_regmap_lookup_by_compatible
|
||||
("nuvoton,npcm750-rst");
|
||||
if (IS_ERR(info->rst_regmap)) {
|
||||
dev_err(&pdev->dev, "Failed to find nuvoton,npcm750-rst\n");
|
||||
ret = PTR_ERR(info->rst_regmap);
|
||||
goto err_disable_clk;
|
||||
}
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(dev, "failed getting interrupt resource\n");
|
||||
ret = -EINVAL;
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0,
|
||||
"NPCM_ADC", indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed requesting interrupt\n");
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
reg_con = ioread32(info->regs + NPCM_ADCCON);
|
||||
info->vref = devm_regulator_get_optional(&pdev->dev, "vref");
|
||||
if (!IS_ERR(info->vref)) {
|
||||
ret = regulator_enable(info->vref);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Can't enable ADC reference voltage\n");
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
iowrite32(reg_con & ~NPCM_ADCCON_REFSEL,
|
||||
info->regs + NPCM_ADCCON);
|
||||
} else {
|
||||
/*
|
||||
* Any error which is not ENODEV indicates the regulator
|
||||
* has been specified and so is a failure case.
|
||||
*/
|
||||
if (PTR_ERR(info->vref) != -ENODEV) {
|
||||
ret = PTR_ERR(info->vref);
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
/* Use internal reference */
|
||||
iowrite32(reg_con | NPCM_ADCCON_REFSEL,
|
||||
info->regs + NPCM_ADCCON);
|
||||
}
|
||||
|
||||
init_waitqueue_head(&info->wq);
|
||||
|
||||
reg_con = ioread32(info->regs + NPCM_ADCCON);
|
||||
reg_con |= NPCM_ADC_ENABLE;
|
||||
|
||||
/* Enable the ADC Module */
|
||||
iowrite32(reg_con, info->regs + NPCM_ADCCON);
|
||||
|
||||
/* Start ADC conversion */
|
||||
iowrite32(reg_con | NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &npcm_adc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = npcm_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(npcm_adc_iio_channels);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Couldn't register the device.\n");
|
||||
goto err_iio_register;
|
||||
}
|
||||
|
||||
pr_info("NPCM ADC driver probed\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_iio_register:
|
||||
iowrite32(reg_con & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
|
||||
if (!IS_ERR(info->vref))
|
||||
regulator_disable(info->vref);
|
||||
err_disable_clk:
|
||||
clk_disable_unprepare(info->adc_clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int npcm_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct npcm_adc *info = iio_priv(indio_dev);
|
||||
u32 regtemp;
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
regtemp = ioread32(info->regs + NPCM_ADCCON);
|
||||
iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
|
||||
if (!IS_ERR(info->vref))
|
||||
regulator_disable(info->vref);
|
||||
clk_disable_unprepare(info->adc_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver npcm_adc_driver = {
|
||||
.probe = npcm_adc_probe,
|
||||
.remove = npcm_adc_remove,
|
||||
.driver = {
|
||||
.name = "npcm_adc",
|
||||
.of_match_table = npcm_adc_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(npcm_adc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Nuvoton NPCM ADC Driver");
|
||||
MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
376
drivers/iio/adc/ti-ads124s08.c
Normal file
376
drivers/iio/adc/ti-ads124s08.c
Normal file
@ -0,0 +1,376 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* TI ADS124S0X chip family driver
|
||||
* Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
/* Commands */
|
||||
#define ADS124S08_CMD_NOP 0x00
|
||||
#define ADS124S08_CMD_WAKEUP 0x02
|
||||
#define ADS124S08_CMD_PWRDWN 0x04
|
||||
#define ADS124S08_CMD_RESET 0x06
|
||||
#define ADS124S08_CMD_START 0x08
|
||||
#define ADS124S08_CMD_STOP 0x0a
|
||||
#define ADS124S08_CMD_SYOCAL 0x16
|
||||
#define ADS124S08_CMD_SYGCAL 0x17
|
||||
#define ADS124S08_CMD_SFOCAL 0x19
|
||||
#define ADS124S08_CMD_RDATA 0x12
|
||||
#define ADS124S08_CMD_RREG 0x20
|
||||
#define ADS124S08_CMD_WREG 0x40
|
||||
|
||||
/* Registers */
|
||||
#define ADS124S08_ID_REG 0x00
|
||||
#define ADS124S08_STATUS 0x01
|
||||
#define ADS124S08_INPUT_MUX 0x02
|
||||
#define ADS124S08_PGA 0x03
|
||||
#define ADS124S08_DATA_RATE 0x04
|
||||
#define ADS124S08_REF 0x05
|
||||
#define ADS124S08_IDACMAG 0x06
|
||||
#define ADS124S08_IDACMUX 0x07
|
||||
#define ADS124S08_VBIAS 0x08
|
||||
#define ADS124S08_SYS 0x09
|
||||
#define ADS124S08_OFCAL0 0x0a
|
||||
#define ADS124S08_OFCAL1 0x0b
|
||||
#define ADS124S08_OFCAL2 0x0c
|
||||
#define ADS124S08_FSCAL0 0x0d
|
||||
#define ADS124S08_FSCAL1 0x0e
|
||||
#define ADS124S08_FSCAL2 0x0f
|
||||
#define ADS124S08_GPIODAT 0x10
|
||||
#define ADS124S08_GPIOCON 0x11
|
||||
|
||||
/* ADS124S0x common channels */
|
||||
#define ADS124S08_AIN0 0x00
|
||||
#define ADS124S08_AIN1 0x01
|
||||
#define ADS124S08_AIN2 0x02
|
||||
#define ADS124S08_AIN3 0x03
|
||||
#define ADS124S08_AIN4 0x04
|
||||
#define ADS124S08_AIN5 0x05
|
||||
#define ADS124S08_AINCOM 0x0c
|
||||
/* ADS124S08 only channels */
|
||||
#define ADS124S08_AIN6 0x06
|
||||
#define ADS124S08_AIN7 0x07
|
||||
#define ADS124S08_AIN8 0x08
|
||||
#define ADS124S08_AIN9 0x09
|
||||
#define ADS124S08_AIN10 0x0a
|
||||
#define ADS124S08_AIN11 0x0b
|
||||
#define ADS124S08_MAX_CHANNELS 12
|
||||
|
||||
#define ADS124S08_POS_MUX_SHIFT 0x04
|
||||
#define ADS124S08_INT_REF 0x09
|
||||
|
||||
#define ADS124S08_START_REG_MASK 0x1f
|
||||
#define ADS124S08_NUM_BYTES_MASK 0x1f
|
||||
|
||||
#define ADS124S08_START_CONV 0x01
|
||||
#define ADS124S08_STOP_CONV 0x00
|
||||
|
||||
enum ads124s_id {
|
||||
ADS124S08_ID,
|
||||
ADS124S06_ID,
|
||||
};
|
||||
|
||||
struct ads124s_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
};
|
||||
|
||||
struct ads124s_private {
|
||||
const struct ads124s_chip_info *chip_info;
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct spi_device *spi;
|
||||
struct mutex lock;
|
||||
u8 data[5] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
#define ADS124S08_CHAN(index) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.scan_index = index, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 32, \
|
||||
.storagebits = 32, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ads124s06_channels[] = {
|
||||
ADS124S08_CHAN(0),
|
||||
ADS124S08_CHAN(1),
|
||||
ADS124S08_CHAN(2),
|
||||
ADS124S08_CHAN(3),
|
||||
ADS124S08_CHAN(4),
|
||||
ADS124S08_CHAN(5),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ads124s08_channels[] = {
|
||||
ADS124S08_CHAN(0),
|
||||
ADS124S08_CHAN(1),
|
||||
ADS124S08_CHAN(2),
|
||||
ADS124S08_CHAN(3),
|
||||
ADS124S08_CHAN(4),
|
||||
ADS124S08_CHAN(5),
|
||||
ADS124S08_CHAN(6),
|
||||
ADS124S08_CHAN(7),
|
||||
ADS124S08_CHAN(8),
|
||||
ADS124S08_CHAN(9),
|
||||
ADS124S08_CHAN(10),
|
||||
ADS124S08_CHAN(11),
|
||||
};
|
||||
|
||||
static const struct ads124s_chip_info ads124s_chip_info_tbl[] = {
|
||||
[ADS124S08_ID] = {
|
||||
.channels = ads124s08_channels,
|
||||
.num_channels = ARRAY_SIZE(ads124s08_channels),
|
||||
},
|
||||
[ADS124S06_ID] = {
|
||||
.channels = ads124s06_channels,
|
||||
.num_channels = ARRAY_SIZE(ads124s06_channels),
|
||||
},
|
||||
};
|
||||
|
||||
static int ads124s_write_cmd(struct iio_dev *indio_dev, u8 command)
|
||||
{
|
||||
struct ads124s_private *priv = iio_priv(indio_dev);
|
||||
|
||||
priv->data[0] = command;
|
||||
|
||||
return spi_write(priv->spi, &priv->data[0], 1);
|
||||
}
|
||||
|
||||
static int ads124s_write_reg(struct iio_dev *indio_dev, u8 reg, u8 data)
|
||||
{
|
||||
struct ads124s_private *priv = iio_priv(indio_dev);
|
||||
|
||||
priv->data[0] = ADS124S08_CMD_WREG | reg;
|
||||
priv->data[1] = 0x0;
|
||||
priv->data[2] = data;
|
||||
|
||||
return spi_write(priv->spi, &priv->data[0], 3);
|
||||
}
|
||||
|
||||
static int ads124s_reset(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ads124s_private *priv = iio_priv(indio_dev);
|
||||
|
||||
if (priv->reset_gpio) {
|
||||
gpiod_set_value(priv->reset_gpio, 0);
|
||||
udelay(200);
|
||||
gpiod_set_value(priv->reset_gpio, 1);
|
||||
} else {
|
||||
return ads124s_write_cmd(indio_dev, ADS124S08_CMD_RESET);
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int ads124s_read(struct iio_dev *indio_dev, unsigned int chan)
|
||||
{
|
||||
struct ads124s_private *priv = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u32 tmp;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &priv->data[0],
|
||||
.len = 4,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.tx_buf = &priv->data[1],
|
||||
.rx_buf = &priv->data[1],
|
||||
.len = 4,
|
||||
},
|
||||
};
|
||||
|
||||
priv->data[0] = ADS124S08_CMD_RDATA;
|
||||
memset(&priv->data[1], ADS124S08_CMD_NOP, sizeof(priv->data));
|
||||
|
||||
ret = spi_sync_transfer(priv->spi, t, ARRAY_SIZE(t));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tmp = priv->data[2] << 16 | priv->data[3] << 8 | priv->data[4];
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static int ads124s_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long m)
|
||||
{
|
||||
struct ads124s_private *priv = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX,
|
||||
chan->channel);
|
||||
if (ret) {
|
||||
dev_err(&priv->spi->dev, "Set ADC CH failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV);
|
||||
if (ret) {
|
||||
dev_err(&priv->spi->dev, "Start converions failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ads124s_read(indio_dev, chan->channel);
|
||||
if (ret < 0) {
|
||||
dev_err(&priv->spi->dev, "Read ADC failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
*val = ret;
|
||||
|
||||
ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV);
|
||||
if (ret) {
|
||||
dev_err(&priv->spi->dev, "Stop converions failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&priv->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ads124s_info = {
|
||||
.read_raw = &ads124s_read_raw,
|
||||
};
|
||||
|
||||
static irqreturn_t ads124s_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ads124s_private *priv = iio_priv(indio_dev);
|
||||
u32 buffer[ADS124S08_MAX_CHANNELS + sizeof(s64)/sizeof(u16)];
|
||||
int scan_index, j = 0;
|
||||
int ret;
|
||||
|
||||
for_each_set_bit(scan_index, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX,
|
||||
scan_index);
|
||||
if (ret)
|
||||
dev_err(&priv->spi->dev, "Set ADC CH failed\n");
|
||||
|
||||
ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV);
|
||||
if (ret)
|
||||
dev_err(&priv->spi->dev, "Start ADC converions failed\n");
|
||||
|
||||
buffer[j] = ads124s_read(indio_dev, scan_index);
|
||||
ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV);
|
||||
if (ret)
|
||||
dev_err(&priv->spi->dev, "Stop ADC converions failed\n");
|
||||
|
||||
j++;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
|
||||
pf->timestamp);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ads124s_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ads124s_private *ads124s_priv;
|
||||
struct iio_dev *indio_dev;
|
||||
const struct spi_device_id *spi_id = spi_get_device_id(spi);
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ads124s_priv));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ads124s_priv = iio_priv(indio_dev);
|
||||
|
||||
ads124s_priv->reset_gpio = devm_gpiod_get_optional(&spi->dev,
|
||||
"reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ads124s_priv->reset_gpio))
|
||||
dev_info(&spi->dev, "Reset GPIO not defined\n");
|
||||
|
||||
ads124s_priv->chip_info = &ads124s_chip_info_tbl[spi_id->driver_data];
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
ads124s_priv->spi = spi;
|
||||
|
||||
indio_dev->name = spi_id->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->dev.of_node = spi->dev.of_node;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ads124s_priv->chip_info->channels;
|
||||
indio_dev->num_channels = ads124s_priv->chip_info->num_channels;
|
||||
indio_dev->info = &ads124s_info;
|
||||
|
||||
mutex_init(&ads124s_priv->lock);
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
|
||||
ads124s_trigger_handler, NULL);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "iio triggered buffer setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ads124s_reset(indio_dev);
|
||||
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ads124s_id[] = {
|
||||
{ "ads124s06", ADS124S06_ID },
|
||||
{ "ads124s08", ADS124S08_ID },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ads124s_id);
|
||||
|
||||
static const struct of_device_id ads124s_of_table[] = {
|
||||
{ .compatible = "ti,ads124s06" },
|
||||
{ .compatible = "ti,ads124s08" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ads124s_of_table);
|
||||
|
||||
static struct spi_driver ads124s_driver = {
|
||||
.driver = {
|
||||
.name = "ads124s08",
|
||||
.of_match_table = ads124s_of_table,
|
||||
},
|
||||
.probe = ads124s_probe,
|
||||
.id_table = ads124s_id,
|
||||
};
|
||||
module_spi_driver(ads124s_driver);
|
||||
|
||||
MODULE_AUTHOR("Dan Murphy <dmuprhy@ti.com>");
|
||||
MODULE_DESCRIPTION("TI TI_ADS12S0X ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1273,8 +1273,10 @@ static int xadc_probe(struct platform_device *pdev)
|
||||
xadc->threshold[i] = 0xffff;
|
||||
else
|
||||
xadc->threshold[i] = 0;
|
||||
xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i),
|
||||
ret = xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i),
|
||||
xadc->threshold[i]);
|
||||
if (ret)
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
/* Go to non-buffered mode */
|
||||
|
@ -61,6 +61,17 @@ config IAQCORE
|
||||
iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds)
|
||||
sensors
|
||||
|
||||
config SPS30
|
||||
tristate "SPS30 particulate matter sensor"
|
||||
depends on I2C
|
||||
select CRC8
|
||||
help
|
||||
Say Y here to build support for the Sensirion SPS30 particulate
|
||||
matter sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called sps30.
|
||||
|
||||
config VZ89X
|
||||
tristate "SGX Sensortech MiCS VZ89X VOC sensor"
|
||||
depends on I2C
|
||||
|
@ -9,4 +9,6 @@ obj-$(CONFIG_BME680_I2C) += bme680_i2c.o
|
||||
obj-$(CONFIG_BME680_SPI) += bme680_spi.o
|
||||
obj-$(CONFIG_CCS811) += ccs811.o
|
||||
obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
|
||||
obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o
|
||||
obj-$(CONFIG_SPS30) += sps30.o
|
||||
obj-$(CONFIG_VZ89X) += vz89x.o
|
||||
|
@ -70,10 +70,17 @@ static const struct acpi_device_id bme680_acpi_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
|
||||
|
||||
static const struct of_device_id bme680_of_i2c_match[] = {
|
||||
{ .compatible = "bosch,bme680", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bme680_of_i2c_match);
|
||||
|
||||
static struct i2c_driver bme680_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "bme680_i2c",
|
||||
.acpi_match_table = ACPI_PTR(bme680_acpi_match),
|
||||
.of_match_table = bme680_of_i2c_match,
|
||||
},
|
||||
.probe = bme680_i2c_probe,
|
||||
.id_table = bme680_i2c_id,
|
||||
|
@ -6,6 +6,7 @@
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
@ -110,10 +111,17 @@ static const struct acpi_device_id bme680_acpi_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
|
||||
|
||||
static const struct of_device_id bme680_of_spi_match[] = {
|
||||
{ .compatible = "bosch,bme680", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bme680_of_spi_match);
|
||||
|
||||
static struct spi_driver bme680_spi_driver = {
|
||||
.driver = {
|
||||
.name = "bme680_spi",
|
||||
.acpi_match_table = ACPI_PTR(bme680_acpi_match),
|
||||
.of_match_table = bme680_of_spi_match,
|
||||
},
|
||||
.probe = bme680_spi_probe,
|
||||
.id_table = bme680_spi_id,
|
||||
|
591
drivers/iio/chemical/sgp30.c
Normal file
591
drivers/iio/chemical/sgp30.c
Normal file
@ -0,0 +1,591 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* sgp30.c - Support for Sensirion SGP Gas Sensors
|
||||
*
|
||||
* Copyright (C) 2018 Andreas Brauchli <andreas.brauchli@sensirion.com>
|
||||
*
|
||||
* I2C slave address: 0x58
|
||||
*
|
||||
* Datasheets:
|
||||
* https://www.sensirion.com/file/datasheet_sgp30
|
||||
* https://www.sensirion.com/file/datasheet_sgpc3
|
||||
*
|
||||
* TODO:
|
||||
* - baseline support
|
||||
* - humidity compensation
|
||||
* - power mode switching (SGPC3)
|
||||
*/
|
||||
|
||||
#include <linux/crc8.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define SGP_WORD_LEN 2
|
||||
#define SGP_CRC8_POLYNOMIAL 0x31
|
||||
#define SGP_CRC8_INIT 0xff
|
||||
#define SGP_CRC8_LEN 1
|
||||
#define SGP_CMD(cmd_word) cpu_to_be16(cmd_word)
|
||||
#define SGP_CMD_DURATION_US 12000
|
||||
#define SGP_MEASUREMENT_DURATION_US 50000
|
||||
#define SGP_CMD_LEN SGP_WORD_LEN
|
||||
#define SGP_CMD_MAX_BUF_SIZE (SGP_CMD_LEN + 2 * SGP_WORD_LEN)
|
||||
#define SGP_MEASUREMENT_LEN 2
|
||||
#define SGP30_MEASURE_INTERVAL_HZ 1
|
||||
#define SGPC3_MEASURE_INTERVAL_HZ 2
|
||||
#define SGP_VERS_PRODUCT(data) ((((data)->feature_set) & 0xf000) >> 12)
|
||||
#define SGP_VERS_RESERVED(data) ((((data)->feature_set) & 0x0800) >> 11)
|
||||
#define SGP_VERS_GEN(data) ((((data)->feature_set) & 0x0600) >> 9)
|
||||
#define SGP_VERS_ENG_BIT(data) ((((data)->feature_set) & 0x0100) >> 8)
|
||||
#define SGP_VERS_MAJOR(data) ((((data)->feature_set) & 0x00e0) >> 5)
|
||||
#define SGP_VERS_MINOR(data) (((data)->feature_set) & 0x001f)
|
||||
|
||||
DECLARE_CRC8_TABLE(sgp_crc8_table);
|
||||
|
||||
enum sgp_product_id {
|
||||
SGP30 = 0,
|
||||
SGPC3,
|
||||
};
|
||||
|
||||
enum sgp30_channel_idx {
|
||||
SGP30_IAQ_TVOC_IDX = 0,
|
||||
SGP30_IAQ_CO2EQ_IDX,
|
||||
SGP30_SIG_ETOH_IDX,
|
||||
SGP30_SIG_H2_IDX,
|
||||
};
|
||||
|
||||
enum sgpc3_channel_idx {
|
||||
SGPC3_IAQ_TVOC_IDX = 10,
|
||||
SGPC3_SIG_ETOH_IDX,
|
||||
};
|
||||
|
||||
enum sgp_cmd {
|
||||
SGP_CMD_IAQ_INIT = SGP_CMD(0x2003),
|
||||
SGP_CMD_IAQ_MEASURE = SGP_CMD(0x2008),
|
||||
SGP_CMD_GET_FEATURE_SET = SGP_CMD(0x202f),
|
||||
SGP_CMD_GET_SERIAL_ID = SGP_CMD(0x3682),
|
||||
|
||||
SGP30_CMD_MEASURE_SIGNAL = SGP_CMD(0x2050),
|
||||
|
||||
SGPC3_CMD_MEASURE_RAW = SGP_CMD(0x2046),
|
||||
};
|
||||
|
||||
struct sgp_version {
|
||||
u8 major;
|
||||
u8 minor;
|
||||
};
|
||||
|
||||
struct sgp_crc_word {
|
||||
__be16 value;
|
||||
u8 crc8;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
union sgp_reading {
|
||||
u8 start;
|
||||
struct sgp_crc_word raw_words[4];
|
||||
};
|
||||
|
||||
enum _iaq_buffer_state {
|
||||
IAQ_BUFFER_EMPTY = 0,
|
||||
IAQ_BUFFER_DEFAULT_VALS,
|
||||
IAQ_BUFFER_VALID,
|
||||
};
|
||||
|
||||
struct sgp_data {
|
||||
struct i2c_client *client;
|
||||
struct task_struct *iaq_thread;
|
||||
struct mutex data_lock;
|
||||
unsigned long iaq_init_start_jiffies;
|
||||
unsigned long iaq_defval_skip_jiffies;
|
||||
u16 product_id;
|
||||
u16 feature_set;
|
||||
unsigned long measure_interval_jiffies;
|
||||
enum sgp_cmd iaq_init_cmd;
|
||||
enum sgp_cmd measure_iaq_cmd;
|
||||
enum sgp_cmd measure_gas_signals_cmd;
|
||||
union sgp_reading buffer;
|
||||
union sgp_reading iaq_buffer;
|
||||
enum _iaq_buffer_state iaq_buffer_state;
|
||||
};
|
||||
|
||||
struct sgp_device {
|
||||
const struct iio_chan_spec *channels;
|
||||
int num_channels;
|
||||
};
|
||||
|
||||
static const struct sgp_version supported_versions_sgp30[] = {
|
||||
{
|
||||
.major = 1,
|
||||
.minor = 0,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct sgp_version supported_versions_sgpc3[] = {
|
||||
{
|
||||
.major = 0,
|
||||
.minor = 4,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec sgp30_channels[] = {
|
||||
{
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_VOC,
|
||||
.modified = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.address = SGP30_IAQ_TVOC_IDX,
|
||||
},
|
||||
{
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_CO2,
|
||||
.modified = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.address = SGP30_IAQ_CO2EQ_IDX,
|
||||
},
|
||||
{
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_ETHANOL,
|
||||
.modified = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.address = SGP30_SIG_ETOH_IDX,
|
||||
},
|
||||
{
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_H2,
|
||||
.modified = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.address = SGP30_SIG_H2_IDX,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec sgpc3_channels[] = {
|
||||
{
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_VOC,
|
||||
.modified = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.address = SGPC3_IAQ_TVOC_IDX,
|
||||
},
|
||||
{
|
||||
.type = IIO_CONCENTRATION,
|
||||
.channel2 = IIO_MOD_ETHANOL,
|
||||
.modified = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.address = SGPC3_SIG_ETOH_IDX,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct sgp_device sgp_devices[] = {
|
||||
[SGP30] = {
|
||||
.channels = sgp30_channels,
|
||||
.num_channels = ARRAY_SIZE(sgp30_channels),
|
||||
},
|
||||
[SGPC3] = {
|
||||
.channels = sgpc3_channels,
|
||||
.num_channels = ARRAY_SIZE(sgpc3_channels),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* sgp_verify_buffer() - verify the checksums of the data buffer words
|
||||
*
|
||||
* @data: SGP data
|
||||
* @buf: Raw data buffer
|
||||
* @word_count: Num data words stored in the buffer, excluding CRC bytes
|
||||
*
|
||||
* Return: 0 on success, negative error otherwise.
|
||||
*/
|
||||
static int sgp_verify_buffer(const struct sgp_data *data,
|
||||
union sgp_reading *buf, size_t word_count)
|
||||
{
|
||||
size_t size = word_count * (SGP_WORD_LEN + SGP_CRC8_LEN);
|
||||
int i;
|
||||
u8 crc;
|
||||
u8 *data_buf = &buf->start;
|
||||
|
||||
for (i = 0; i < size; i += SGP_WORD_LEN + SGP_CRC8_LEN) {
|
||||
crc = crc8(sgp_crc8_table, &data_buf[i], SGP_WORD_LEN,
|
||||
SGP_CRC8_INIT);
|
||||
if (crc != data_buf[i + SGP_WORD_LEN]) {
|
||||
dev_err(&data->client->dev, "CRC error\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sgp_read_cmd() - reads data from sensor after issuing a command
|
||||
* The caller must hold data->data_lock for the duration of the call.
|
||||
* @data: SGP data
|
||||
* @cmd: SGP Command to issue
|
||||
* @buf: Raw data buffer to use
|
||||
* @word_count: Num words to read, excluding CRC bytes
|
||||
*
|
||||
* Return: 0 on success, negative error otherwise.
|
||||
*/
|
||||
static int sgp_read_cmd(struct sgp_data *data, enum sgp_cmd cmd,
|
||||
union sgp_reading *buf, size_t word_count,
|
||||
unsigned long duration_us)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_client *client = data->client;
|
||||
size_t size = word_count * (SGP_WORD_LEN + SGP_CRC8_LEN);
|
||||
u8 *data_buf;
|
||||
|
||||
ret = i2c_master_send(client, (const char *)&cmd, SGP_CMD_LEN);
|
||||
if (ret != SGP_CMD_LEN)
|
||||
return -EIO;
|
||||
usleep_range(duration_us, duration_us + 1000);
|
||||
|
||||
if (word_count == 0)
|
||||
return 0;
|
||||
|
||||
data_buf = &buf->start;
|
||||
ret = i2c_master_recv(client, data_buf, size);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != size)
|
||||
return -EIO;
|
||||
|
||||
return sgp_verify_buffer(data, buf, word_count);
|
||||
}
|
||||
|
||||
/**
|
||||
* sgp_measure_iaq() - measure and retrieve IAQ values from sensor
|
||||
* The caller must hold data->data_lock for the duration of the call.
|
||||
* @data: SGP data
|
||||
*
|
||||
* Return: 0 on success, -EBUSY on default values, negative error
|
||||
* otherwise.
|
||||
*/
|
||||
|
||||
static int sgp_measure_iaq(struct sgp_data *data)
|
||||
{
|
||||
int ret;
|
||||
/* data contains default values */
|
||||
bool default_vals = !time_after(jiffies, data->iaq_init_start_jiffies +
|
||||
data->iaq_defval_skip_jiffies);
|
||||
|
||||
ret = sgp_read_cmd(data, data->measure_iaq_cmd, &data->iaq_buffer,
|
||||
SGP_MEASUREMENT_LEN, SGP_MEASUREMENT_DURATION_US);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->iaq_buffer_state = IAQ_BUFFER_DEFAULT_VALS;
|
||||
|
||||
if (default_vals)
|
||||
return -EBUSY;
|
||||
|
||||
data->iaq_buffer_state = IAQ_BUFFER_VALID;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sgp_iaq_thread_sleep_until(const struct sgp_data *data,
|
||||
unsigned long sleep_jiffies)
|
||||
{
|
||||
const long IAQ_POLL = 50000;
|
||||
|
||||
while (!time_after(jiffies, sleep_jiffies)) {
|
||||
usleep_range(IAQ_POLL, IAQ_POLL + 10000);
|
||||
if (kthread_should_stop() || data->iaq_init_start_jiffies == 0)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int sgp_iaq_threadfn(void *p)
|
||||
{
|
||||
struct sgp_data *data = (struct sgp_data *)p;
|
||||
unsigned long next_update_jiffies;
|
||||
int ret;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
mutex_lock(&data->data_lock);
|
||||
if (data->iaq_init_start_jiffies == 0) {
|
||||
ret = sgp_read_cmd(data, data->iaq_init_cmd, NULL, 0,
|
||||
SGP_CMD_DURATION_US);
|
||||
if (ret < 0)
|
||||
goto unlock_sleep_continue;
|
||||
data->iaq_init_start_jiffies = jiffies;
|
||||
}
|
||||
|
||||
ret = sgp_measure_iaq(data);
|
||||
if (ret && ret != -EBUSY) {
|
||||
dev_warn(&data->client->dev,
|
||||
"IAQ measurement error [%d]\n", ret);
|
||||
}
|
||||
unlock_sleep_continue:
|
||||
next_update_jiffies = jiffies + data->measure_interval_jiffies;
|
||||
mutex_unlock(&data->data_lock);
|
||||
sgp_iaq_thread_sleep_until(data, next_update_jiffies);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sgp_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct sgp_data *data = iio_priv(indio_dev);
|
||||
struct sgp_crc_word *words;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
mutex_lock(&data->data_lock);
|
||||
if (data->iaq_buffer_state != IAQ_BUFFER_VALID) {
|
||||
mutex_unlock(&data->data_lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
words = data->iaq_buffer.raw_words;
|
||||
switch (chan->address) {
|
||||
case SGP30_IAQ_TVOC_IDX:
|
||||
case SGPC3_IAQ_TVOC_IDX:
|
||||
*val = 0;
|
||||
*val2 = be16_to_cpu(words[1].value);
|
||||
ret = IIO_VAL_INT_PLUS_NANO;
|
||||
break;
|
||||
case SGP30_IAQ_CO2EQ_IDX:
|
||||
*val = 0;
|
||||
*val2 = be16_to_cpu(words[0].value);
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&data->data_lock);
|
||||
break;
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&data->data_lock);
|
||||
if (chan->address == SGPC3_SIG_ETOH_IDX) {
|
||||
if (data->iaq_buffer_state == IAQ_BUFFER_EMPTY)
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = 0;
|
||||
words = data->iaq_buffer.raw_words;
|
||||
} else {
|
||||
ret = sgp_read_cmd(data, data->measure_gas_signals_cmd,
|
||||
&data->buffer, SGP_MEASUREMENT_LEN,
|
||||
SGP_MEASUREMENT_DURATION_US);
|
||||
words = data->buffer.raw_words;
|
||||
}
|
||||
if (ret) {
|
||||
mutex_unlock(&data->data_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (chan->address) {
|
||||
case SGP30_SIG_ETOH_IDX:
|
||||
*val = be16_to_cpu(words[1].value);
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case SGPC3_SIG_ETOH_IDX:
|
||||
case SGP30_SIG_H2_IDX:
|
||||
*val = be16_to_cpu(words[0].value);
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&data->data_lock);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sgp_check_compat(struct sgp_data *data,
|
||||
unsigned int product_id)
|
||||
{
|
||||
const struct sgp_version *supported_versions;
|
||||
u16 ix, num_fs;
|
||||
u16 product, generation, major, minor;
|
||||
|
||||
/* driver does not match product */
|
||||
generation = SGP_VERS_GEN(data);
|
||||
if (generation != 0) {
|
||||
dev_err(&data->client->dev,
|
||||
"incompatible product generation %d != 0", generation);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
product = SGP_VERS_PRODUCT(data);
|
||||
if (product != product_id) {
|
||||
dev_err(&data->client->dev,
|
||||
"sensor reports a different product: 0x%04hx\n",
|
||||
product);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (SGP_VERS_RESERVED(data))
|
||||
dev_warn(&data->client->dev, "reserved bit is set\n");
|
||||
|
||||
/* engineering samples are not supported: no interface guarantees */
|
||||
if (SGP_VERS_ENG_BIT(data))
|
||||
return -ENODEV;
|
||||
|
||||
switch (product) {
|
||||
case SGP30:
|
||||
supported_versions = supported_versions_sgp30;
|
||||
num_fs = ARRAY_SIZE(supported_versions_sgp30);
|
||||
break;
|
||||
case SGPC3:
|
||||
supported_versions = supported_versions_sgpc3;
|
||||
num_fs = ARRAY_SIZE(supported_versions_sgpc3);
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
major = SGP_VERS_MAJOR(data);
|
||||
minor = SGP_VERS_MINOR(data);
|
||||
for (ix = 0; ix < num_fs; ix++) {
|
||||
if (major == supported_versions[ix].major &&
|
||||
minor >= supported_versions[ix].minor)
|
||||
return 0;
|
||||
}
|
||||
dev_err(&data->client->dev, "unsupported sgp version: %d.%d\n",
|
||||
major, minor);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void sgp_init(struct sgp_data *data)
|
||||
{
|
||||
data->iaq_init_cmd = SGP_CMD_IAQ_INIT;
|
||||
data->iaq_init_start_jiffies = 0;
|
||||
data->iaq_buffer_state = IAQ_BUFFER_EMPTY;
|
||||
switch (SGP_VERS_PRODUCT(data)) {
|
||||
case SGP30:
|
||||
data->measure_interval_jiffies = SGP30_MEASURE_INTERVAL_HZ * HZ;
|
||||
data->measure_iaq_cmd = SGP_CMD_IAQ_MEASURE;
|
||||
data->measure_gas_signals_cmd = SGP30_CMD_MEASURE_SIGNAL;
|
||||
data->product_id = SGP30;
|
||||
data->iaq_defval_skip_jiffies = 15 * HZ;
|
||||
break;
|
||||
case SGPC3:
|
||||
data->measure_interval_jiffies = SGPC3_MEASURE_INTERVAL_HZ * HZ;
|
||||
data->measure_iaq_cmd = SGPC3_CMD_MEASURE_RAW;
|
||||
data->measure_gas_signals_cmd = SGPC3_CMD_MEASURE_RAW;
|
||||
data->product_id = SGPC3;
|
||||
data->iaq_defval_skip_jiffies =
|
||||
43 * data->measure_interval_jiffies;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
static const struct iio_info sgp_info = {
|
||||
.read_raw = sgp_read_raw,
|
||||
};
|
||||
|
||||
static const struct of_device_id sgp_dt_ids[] = {
|
||||
{ .compatible = "sensirion,sgp30", .data = (void *)SGP30 },
|
||||
{ .compatible = "sensirion,sgpc3", .data = (void *)SGPC3 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int sgp_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct sgp_data *data;
|
||||
const struct of_device_id *of_id;
|
||||
unsigned long product_id;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
of_id = of_match_device(sgp_dt_ids, &client->dev);
|
||||
if (of_id)
|
||||
product_id = (unsigned long)of_id->data;
|
||||
else
|
||||
product_id = id->driver_data;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
crc8_populate_msb(sgp_crc8_table, SGP_CRC8_POLYNOMIAL);
|
||||
mutex_init(&data->data_lock);
|
||||
|
||||
/* get feature set version and write it to client data */
|
||||
ret = sgp_read_cmd(data, SGP_CMD_GET_FEATURE_SET, &data->buffer, 1,
|
||||
SGP_CMD_DURATION_US);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->feature_set = be16_to_cpu(data->buffer.raw_words[0].value);
|
||||
|
||||
ret = sgp_check_compat(data, product_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &sgp_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = sgp_devices[product_id].channels;
|
||||
indio_dev->num_channels = sgp_devices[product_id].num_channels;
|
||||
|
||||
sgp_init(data);
|
||||
|
||||
ret = devm_iio_device_register(&client->dev, indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to register iio device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->iaq_thread = kthread_run(sgp_iaq_threadfn, data,
|
||||
"%s-iaq", data->client->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sgp_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct sgp_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (data->iaq_thread)
|
||||
kthread_stop(data->iaq_thread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id sgp_id[] = {
|
||||
{ "sgp30", SGP30 },
|
||||
{ "sgpc3", SGPC3 },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, sgp_id);
|
||||
MODULE_DEVICE_TABLE(of, sgp_dt_ids);
|
||||
|
||||
static struct i2c_driver sgp_driver = {
|
||||
.driver = {
|
||||
.name = "sgp30",
|
||||
.of_match_table = of_match_ptr(sgp_dt_ids),
|
||||
},
|
||||
.probe = sgp_probe,
|
||||
.remove = sgp_remove,
|
||||
.id_table = sgp_id,
|
||||
};
|
||||
module_i2c_driver(sgp_driver);
|
||||
|
||||
MODULE_AUTHOR("Andreas Brauchli <andreas.brauchli@sensirion.com>");
|
||||
MODULE_AUTHOR("Pascal Sachs <pascal.sachs@sensirion.com>");
|
||||
MODULE_DESCRIPTION("Sensirion SGP gas sensors");
|
||||
MODULE_LICENSE("GPL v2");
|
545
drivers/iio/chemical/sps30.c
Normal file
545
drivers/iio/chemical/sps30.c
Normal file
@ -0,0 +1,545 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Sensirion SPS30 particulate matter sensor driver
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*
|
||||
* I2C slave address: 0x69
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/crc8.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define SPS30_CRC8_POLYNOMIAL 0x31
|
||||
/* max number of bytes needed to store PM measurements or serial string */
|
||||
#define SPS30_MAX_READ_SIZE 48
|
||||
/* sensor measures reliably up to 3000 ug / m3 */
|
||||
#define SPS30_MAX_PM 3000
|
||||
/* minimum and maximum self cleaning periods in seconds */
|
||||
#define SPS30_AUTO_CLEANING_PERIOD_MIN 0
|
||||
#define SPS30_AUTO_CLEANING_PERIOD_MAX 604800
|
||||
|
||||
/* SPS30 commands */
|
||||
#define SPS30_START_MEAS 0x0010
|
||||
#define SPS30_STOP_MEAS 0x0104
|
||||
#define SPS30_RESET 0xd304
|
||||
#define SPS30_READ_DATA_READY_FLAG 0x0202
|
||||
#define SPS30_READ_DATA 0x0300
|
||||
#define SPS30_READ_SERIAL 0xd033
|
||||
#define SPS30_START_FAN_CLEANING 0x5607
|
||||
#define SPS30_AUTO_CLEANING_PERIOD 0x8004
|
||||
/* not a sensor command per se, used only to distinguish write from read */
|
||||
#define SPS30_READ_AUTO_CLEANING_PERIOD 0x8005
|
||||
|
||||
enum {
|
||||
PM1,
|
||||
PM2P5,
|
||||
PM4,
|
||||
PM10,
|
||||
};
|
||||
|
||||
enum {
|
||||
RESET,
|
||||
MEASURING,
|
||||
};
|
||||
|
||||
struct sps30_state {
|
||||
struct i2c_client *client;
|
||||
/*
|
||||
* Guards against concurrent access to sensor registers.
|
||||
* Must be held whenever sequence of commands is to be executed.
|
||||
*/
|
||||
struct mutex lock;
|
||||
int state;
|
||||
};
|
||||
|
||||
DECLARE_CRC8_TABLE(sps30_crc8_table);
|
||||
|
||||
static int sps30_write_then_read(struct sps30_state *state, u8 *txbuf,
|
||||
int txsize, u8 *rxbuf, int rxsize)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Sensor does not support repeated start so instead of
|
||||
* sending two i2c messages in a row we just send one by one.
|
||||
*/
|
||||
ret = i2c_master_send(state->client, txbuf, txsize);
|
||||
if (ret != txsize)
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
if (!rxbuf)
|
||||
return 0;
|
||||
|
||||
ret = i2c_master_recv(state->client, rxbuf, rxsize);
|
||||
if (ret != rxsize)
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sps30_do_cmd(struct sps30_state *state, u16 cmd, u8 *data, int size)
|
||||
{
|
||||
/*
|
||||
* Internally sensor stores measurements in a following manner:
|
||||
*
|
||||
* PM1: upper two bytes, crc8, lower two bytes, crc8
|
||||
* PM2P5: upper two bytes, crc8, lower two bytes, crc8
|
||||
* PM4: upper two bytes, crc8, lower two bytes, crc8
|
||||
* PM10: upper two bytes, crc8, lower two bytes, crc8
|
||||
*
|
||||
* What follows next are number concentration measurements and
|
||||
* typical particle size measurement which we omit.
|
||||
*/
|
||||
u8 buf[SPS30_MAX_READ_SIZE] = { cmd >> 8, cmd };
|
||||
int i, ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case SPS30_START_MEAS:
|
||||
buf[2] = 0x03;
|
||||
buf[3] = 0x00;
|
||||
buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE);
|
||||
ret = sps30_write_then_read(state, buf, 5, NULL, 0);
|
||||
break;
|
||||
case SPS30_STOP_MEAS:
|
||||
case SPS30_RESET:
|
||||
case SPS30_START_FAN_CLEANING:
|
||||
ret = sps30_write_then_read(state, buf, 2, NULL, 0);
|
||||
break;
|
||||
case SPS30_READ_AUTO_CLEANING_PERIOD:
|
||||
buf[0] = SPS30_AUTO_CLEANING_PERIOD >> 8;
|
||||
buf[1] = (u8)SPS30_AUTO_CLEANING_PERIOD;
|
||||
case SPS30_READ_DATA_READY_FLAG:
|
||||
case SPS30_READ_DATA:
|
||||
case SPS30_READ_SERIAL:
|
||||
/* every two data bytes are checksummed */
|
||||
size += size / 2;
|
||||
ret = sps30_write_then_read(state, buf, 2, buf, size);
|
||||
break;
|
||||
case SPS30_AUTO_CLEANING_PERIOD:
|
||||
buf[2] = data[0];
|
||||
buf[3] = data[1];
|
||||
buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE);
|
||||
buf[5] = data[2];
|
||||
buf[6] = data[3];
|
||||
buf[7] = crc8(sps30_crc8_table, &buf[5], 2, CRC8_INIT_VALUE);
|
||||
ret = sps30_write_then_read(state, buf, 8, NULL, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* validate received data and strip off crc bytes */
|
||||
for (i = 0; i < size; i += 3) {
|
||||
u8 crc = crc8(sps30_crc8_table, &buf[i], 2, CRC8_INIT_VALUE);
|
||||
|
||||
if (crc != buf[i + 2]) {
|
||||
dev_err(&state->client->dev,
|
||||
"data integrity check failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*data++ = buf[i];
|
||||
*data++ = buf[i + 1];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static s32 sps30_float_to_int_clamped(const u8 *fp)
|
||||
{
|
||||
int val = get_unaligned_be32(fp);
|
||||
int mantissa = val & GENMASK(22, 0);
|
||||
/* this is fine since passed float is always non-negative */
|
||||
int exp = val >> 23;
|
||||
int fraction, shift;
|
||||
|
||||
/* special case 0 */
|
||||
if (!exp && !mantissa)
|
||||
return 0;
|
||||
|
||||
exp -= 127;
|
||||
if (exp < 0) {
|
||||
/* return values ranging from 1 to 99 */
|
||||
return ((((1 << 23) + mantissa) * 100) >> 23) >> (-exp);
|
||||
}
|
||||
|
||||
/* return values ranging from 100 to 300000 */
|
||||
shift = 23 - exp;
|
||||
val = (1 << exp) + (mantissa >> shift);
|
||||
if (val >= SPS30_MAX_PM)
|
||||
return SPS30_MAX_PM * 100;
|
||||
|
||||
fraction = mantissa & GENMASK(shift - 1, 0);
|
||||
|
||||
return val * 100 + ((fraction * 100) >> shift);
|
||||
}
|
||||
|
||||
static int sps30_do_meas(struct sps30_state *state, s32 *data, int size)
|
||||
{
|
||||
int i, ret, tries = 5;
|
||||
u8 tmp[16];
|
||||
|
||||
if (state->state == RESET) {
|
||||
ret = sps30_do_cmd(state, SPS30_START_MEAS, NULL, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
state->state = MEASURING;
|
||||
}
|
||||
|
||||
while (tries--) {
|
||||
ret = sps30_do_cmd(state, SPS30_READ_DATA_READY_FLAG, tmp, 2);
|
||||
if (ret)
|
||||
return -EIO;
|
||||
|
||||
/* new measurements ready to be read */
|
||||
if (tmp[1] == 1)
|
||||
break;
|
||||
|
||||
msleep_interruptible(300);
|
||||
}
|
||||
|
||||
if (!tries)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
ret = sps30_do_cmd(state, SPS30_READ_DATA, tmp, sizeof(int) * size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
data[i] = sps30_float_to_int_clamped(&tmp[4 * i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t sps30_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct sps30_state *state = iio_priv(indio_dev);
|
||||
int ret;
|
||||
s32 data[4 + 2]; /* PM1, PM2P5, PM4, PM10, timestamp */
|
||||
|
||||
mutex_lock(&state->lock);
|
||||
ret = sps30_do_meas(state, data, 4);
|
||||
mutex_unlock(&state->lock);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data,
|
||||
iio_get_time_ns(indio_dev));
|
||||
err:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sps30_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct sps30_state *state = iio_priv(indio_dev);
|
||||
int data[4], ret = -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
switch (chan->type) {
|
||||
case IIO_MASSCONCENTRATION:
|
||||
mutex_lock(&state->lock);
|
||||
/* read up to the number of bytes actually needed */
|
||||
switch (chan->channel2) {
|
||||
case IIO_MOD_PM1:
|
||||
ret = sps30_do_meas(state, data, 1);
|
||||
break;
|
||||
case IIO_MOD_PM2P5:
|
||||
ret = sps30_do_meas(state, data, 2);
|
||||
break;
|
||||
case IIO_MOD_PM4:
|
||||
ret = sps30_do_meas(state, data, 3);
|
||||
break;
|
||||
case IIO_MOD_PM10:
|
||||
ret = sps30_do_meas(state, data, 4);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&state->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = data[chan->address] / 100;
|
||||
*val2 = (data[chan->address] % 100) * 10000;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_MASSCONCENTRATION:
|
||||
switch (chan->channel2) {
|
||||
case IIO_MOD_PM1:
|
||||
case IIO_MOD_PM2P5:
|
||||
case IIO_MOD_PM4:
|
||||
case IIO_MOD_PM10:
|
||||
*val = 0;
|
||||
*val2 = 10000;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int sps30_do_cmd_reset(struct sps30_state *state)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sps30_do_cmd(state, SPS30_RESET, NULL, 0);
|
||||
msleep(300);
|
||||
/*
|
||||
* Power-on-reset causes sensor to produce some glitch on i2c bus and
|
||||
* some controllers end up in error state. Recover simply by placing
|
||||
* some data on the bus, for example STOP_MEAS command, which
|
||||
* is NOP in this case.
|
||||
*/
|
||||
sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0);
|
||||
state->state = RESET;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t start_cleaning_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct sps30_state *state = iio_priv(indio_dev);
|
||||
int val, ret;
|
||||
|
||||
if (kstrtoint(buf, 0, &val) || val != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&state->lock);
|
||||
ret = sps30_do_cmd(state, SPS30_START_FAN_CLEANING, NULL, 0);
|
||||
mutex_unlock(&state->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t cleaning_period_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct sps30_state *state = iio_priv(indio_dev);
|
||||
u8 tmp[4];
|
||||
int ret;
|
||||
|
||||
mutex_lock(&state->lock);
|
||||
ret = sps30_do_cmd(state, SPS30_READ_AUTO_CLEANING_PERIOD, tmp, 4);
|
||||
mutex_unlock(&state->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", get_unaligned_be32(tmp));
|
||||
}
|
||||
|
||||
static ssize_t cleaning_period_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct sps30_state *state = iio_priv(indio_dev);
|
||||
int val, ret;
|
||||
u8 tmp[4];
|
||||
|
||||
if (kstrtoint(buf, 0, &val))
|
||||
return -EINVAL;
|
||||
|
||||
if ((val < SPS30_AUTO_CLEANING_PERIOD_MIN) ||
|
||||
(val > SPS30_AUTO_CLEANING_PERIOD_MAX))
|
||||
return -EINVAL;
|
||||
|
||||
put_unaligned_be32(val, tmp);
|
||||
|
||||
mutex_lock(&state->lock);
|
||||
ret = sps30_do_cmd(state, SPS30_AUTO_CLEANING_PERIOD, tmp, 0);
|
||||
if (ret) {
|
||||
mutex_unlock(&state->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
|
||||
/*
|
||||
* sensor requires reset in order to return up to date self cleaning
|
||||
* period
|
||||
*/
|
||||
ret = sps30_do_cmd_reset(state);
|
||||
if (ret)
|
||||
dev_warn(dev,
|
||||
"period changed but reads will return the old value\n");
|
||||
|
||||
mutex_unlock(&state->lock);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t cleaning_period_available_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "[%d %d %d]\n",
|
||||
SPS30_AUTO_CLEANING_PERIOD_MIN, 1,
|
||||
SPS30_AUTO_CLEANING_PERIOD_MAX);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR_WO(start_cleaning, 0);
|
||||
static IIO_DEVICE_ATTR_RW(cleaning_period, 0);
|
||||
static IIO_DEVICE_ATTR_RO(cleaning_period_available, 0);
|
||||
|
||||
static struct attribute *sps30_attrs[] = {
|
||||
&iio_dev_attr_start_cleaning.dev_attr.attr,
|
||||
&iio_dev_attr_cleaning_period.dev_attr.attr,
|
||||
&iio_dev_attr_cleaning_period_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group sps30_attr_group = {
|
||||
.attrs = sps30_attrs,
|
||||
};
|
||||
|
||||
static const struct iio_info sps30_info = {
|
||||
.attrs = &sps30_attr_group,
|
||||
.read_raw = sps30_read_raw,
|
||||
};
|
||||
|
||||
#define SPS30_CHAN(_index, _mod) { \
|
||||
.type = IIO_MASSCONCENTRATION, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## _mod, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = _mod, \
|
||||
.scan_index = _index, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 19, \
|
||||
.storagebits = 32, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec sps30_channels[] = {
|
||||
SPS30_CHAN(0, PM1),
|
||||
SPS30_CHAN(1, PM2P5),
|
||||
SPS30_CHAN(2, PM4),
|
||||
SPS30_CHAN(3, PM10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
};
|
||||
|
||||
static void sps30_stop_meas(void *data)
|
||||
{
|
||||
struct sps30_state *state = data;
|
||||
|
||||
sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0);
|
||||
}
|
||||
|
||||
static const unsigned long sps30_scan_masks[] = { 0x0f, 0x00 };
|
||||
|
||||
static int sps30_probe(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct sps30_state *state;
|
||||
u8 buf[32];
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*state));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
state = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
state->client = client;
|
||||
state->state = RESET;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &sps30_info;
|
||||
indio_dev->name = client->name;
|
||||
indio_dev->channels = sps30_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(sps30_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->available_scan_masks = sps30_scan_masks;
|
||||
|
||||
mutex_init(&state->lock);
|
||||
crc8_populate_msb(sps30_crc8_table, SPS30_CRC8_POLYNOMIAL);
|
||||
|
||||
ret = sps30_do_cmd_reset(state);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to reset device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sps30_do_cmd(state, SPS30_READ_SERIAL, buf, sizeof(buf));
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to read serial number\n");
|
||||
return ret;
|
||||
}
|
||||
/* returned serial number is already NUL terminated */
|
||||
dev_info(&client->dev, "serial number: %s\n", buf);
|
||||
|
||||
ret = devm_add_action_or_reset(&client->dev, sps30_stop_meas, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL,
|
||||
sps30_trigger_handler, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id sps30_id[] = {
|
||||
{ "sps30" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sps30_id);
|
||||
|
||||
static const struct of_device_id sps30_of_match[] = {
|
||||
{ .compatible = "sensirion,sps30" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sps30_of_match);
|
||||
|
||||
static struct i2c_driver sps30_driver = {
|
||||
.driver = {
|
||||
.name = "sps30",
|
||||
.of_match_table = sps30_of_match,
|
||||
},
|
||||
.id_table = sps30_id,
|
||||
.probe_new = sps30_probe,
|
||||
};
|
||||
module_i2c_driver(sps30_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
|
||||
MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -148,9 +148,9 @@ config AD5686_SPI
|
||||
depends on SPI
|
||||
select AD5686
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5672R, AD5676,
|
||||
AD5676R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, AD5686R.
|
||||
Voltage Output Digital to Analog Converter.
|
||||
Say yes here to build support for Analog Devices AD5672R, AD5674R,
|
||||
AD5676, AD5676R, AD5679R, AD5684, AD5684R, AD5684R, AD5685R, AD5686,
|
||||
AD5686R Voltage Output Digital to Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5686.
|
||||
|
@ -1,7 +1,8 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* AD5672R, AD5676, AD5676R, AD5681R, AD5682R, AD5683, AD5683R,
|
||||
* AD5684, AD5684R, AD5685R, AD5686, AD5686R
|
||||
* AD5672R, AD5674R, AD5676, AD5676R, AD5679R,
|
||||
* AD5681R, AD5682R, AD5683, AD5683R, AD5684,
|
||||
* AD5684R, AD5685R, AD5686, AD5686R
|
||||
* Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2018 Analog Devices Inc.
|
||||
@ -102,8 +103,10 @@ static int ad5686_spi_remove(struct spi_device *spi)
|
||||
static const struct spi_device_id ad5686_spi_id[] = {
|
||||
{"ad5310r", ID_AD5310R},
|
||||
{"ad5672r", ID_AD5672R},
|
||||
{"ad5674r", ID_AD5674R},
|
||||
{"ad5676", ID_AD5676},
|
||||
{"ad5676r", ID_AD5676R},
|
||||
{"ad5679r", ID_AD5679R},
|
||||
{"ad5681r", ID_AD5681R},
|
||||
{"ad5682r", ID_AD5682R},
|
||||
{"ad5683", ID_AD5683},
|
||||
|
@ -71,7 +71,7 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
int ret;
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
unsigned int val, ref_bit_msk;
|
||||
u8 shift;
|
||||
u8 shift, address = 0;
|
||||
|
||||
ret = strtobool(buf, &readin);
|
||||
if (ret)
|
||||
@ -94,6 +94,9 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
case AD5686_REGMAP:
|
||||
shift = 0;
|
||||
ref_bit_msk = 0;
|
||||
/* AD5674R/AD5679R have 16 channels and 2 powerdown registers */
|
||||
if (chan->channel > 0x7)
|
||||
address = 0x8;
|
||||
break;
|
||||
case AD5693_REGMAP:
|
||||
shift = 13;
|
||||
@ -107,7 +110,8 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
if (!st->use_internal_vref)
|
||||
val |= ref_bit_msk;
|
||||
|
||||
ret = st->write(st, AD5686_CMD_POWERDOWN_DAC, 0, val);
|
||||
ret = st->write(st, AD5686_CMD_POWERDOWN_DAC,
|
||||
address, val >> (address * 2));
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
@ -226,10 +230,32 @@ static struct iio_chan_spec name[] = { \
|
||||
AD5868_CHANNEL(7, 7, bits, _shift), \
|
||||
}
|
||||
|
||||
#define DECLARE_AD5679_CHANNELS(name, bits, _shift) \
|
||||
static struct iio_chan_spec name[] = { \
|
||||
AD5868_CHANNEL(0, 0, bits, _shift), \
|
||||
AD5868_CHANNEL(1, 1, bits, _shift), \
|
||||
AD5868_CHANNEL(2, 2, bits, _shift), \
|
||||
AD5868_CHANNEL(3, 3, bits, _shift), \
|
||||
AD5868_CHANNEL(4, 4, bits, _shift), \
|
||||
AD5868_CHANNEL(5, 5, bits, _shift), \
|
||||
AD5868_CHANNEL(6, 6, bits, _shift), \
|
||||
AD5868_CHANNEL(7, 7, bits, _shift), \
|
||||
AD5868_CHANNEL(8, 8, bits, _shift), \
|
||||
AD5868_CHANNEL(9, 9, bits, _shift), \
|
||||
AD5868_CHANNEL(10, 10, bits, _shift), \
|
||||
AD5868_CHANNEL(11, 11, bits, _shift), \
|
||||
AD5868_CHANNEL(12, 12, bits, _shift), \
|
||||
AD5868_CHANNEL(13, 13, bits, _shift), \
|
||||
AD5868_CHANNEL(14, 14, bits, _shift), \
|
||||
AD5868_CHANNEL(15, 15, bits, _shift), \
|
||||
}
|
||||
|
||||
DECLARE_AD5693_CHANNELS(ad5310r_channels, 10, 2);
|
||||
DECLARE_AD5693_CHANNELS(ad5311r_channels, 10, 6);
|
||||
DECLARE_AD5676_CHANNELS(ad5672_channels, 12, 4);
|
||||
DECLARE_AD5679_CHANNELS(ad5674r_channels, 12, 4);
|
||||
DECLARE_AD5676_CHANNELS(ad5676_channels, 16, 0);
|
||||
DECLARE_AD5679_CHANNELS(ad5679r_channels, 16, 0);
|
||||
DECLARE_AD5686_CHANNELS(ad5684_channels, 12, 4);
|
||||
DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2);
|
||||
DECLARE_AD5686_CHANNELS(ad5686_channels, 16, 0);
|
||||
@ -262,6 +288,12 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = {
|
||||
.num_channels = 8,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5674R] = {
|
||||
.channels = ad5674r_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 16,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5675R] = {
|
||||
.channels = ad5676_channels,
|
||||
.int_vref_mv = 2500,
|
||||
@ -279,6 +311,12 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = {
|
||||
.num_channels = 8,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5679R] = {
|
||||
.channels = ad5679r_channels,
|
||||
.int_vref_mv = 2500,
|
||||
.num_channels = 16,
|
||||
.regmap_type = AD5686_REGMAP,
|
||||
},
|
||||
[ID_AD5681R] = {
|
||||
.channels = ad5691r_channels,
|
||||
.int_vref_mv = 2500,
|
||||
|
@ -54,9 +54,11 @@ enum ad5686_supported_device_ids {
|
||||
ID_AD5311R,
|
||||
ID_AD5671R,
|
||||
ID_AD5672R,
|
||||
ID_AD5674R,
|
||||
ID_AD5675R,
|
||||
ID_AD5676,
|
||||
ID_AD5676R,
|
||||
ID_AD5679R,
|
||||
ID_AD5681R,
|
||||
ID_AD5682R,
|
||||
ID_AD5683,
|
||||
|
@ -943,11 +943,14 @@ static int ad9523_setup(struct iio_dev *indio_dev)
|
||||
}
|
||||
}
|
||||
|
||||
for_each_clear_bit(i, &active_mask, AD9523_NUM_CHAN)
|
||||
ad9523_write(indio_dev,
|
||||
for_each_clear_bit(i, &active_mask, AD9523_NUM_CHAN) {
|
||||
ret = ad9523_write(indio_dev,
|
||||
AD9523_CHANNEL_CLOCK_DIST(i),
|
||||
AD9523_CLK_DIST_DRIVER_MODE(TRISTATE) |
|
||||
AD9523_CLK_DIST_PWR_DOWN_EN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ad9523_write(indio_dev, AD9523_POWER_DOWN_CTRL, 0);
|
||||
if (ret < 0)
|
||||
|
@ -13,8 +13,8 @@ config INV_MPU6050_I2C
|
||||
select INV_MPU6050_IIO
|
||||
select REGMAP_I2C
|
||||
help
|
||||
This driver supports the Invensense MPU6050/6500/9150 and ICM20608
|
||||
motion tracking devices over I2C.
|
||||
This driver supports the Invensense MPU6050/6500/9150 and
|
||||
ICM20608/20602 motion tracking devices over I2C.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050-i2c.
|
||||
|
||||
@ -24,7 +24,7 @@ config INV_MPU6050_SPI
|
||||
select INV_MPU6050_IIO
|
||||
select REGMAP_SPI
|
||||
help
|
||||
This driver supports the Invensense MPU6050/6500/9150 and ICM20608
|
||||
motion tracking devices over SPI.
|
||||
This driver supports the Invensense MPU6050/6500/9150 and
|
||||
ICM20608/20602 motion tracking devices over SPI.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050-spi.
|
||||
|
@ -38,6 +38,29 @@ static const int gyro_scale_6050[] = {133090, 266181, 532362, 1064724};
|
||||
*/
|
||||
static const int accel_scale[] = {598, 1196, 2392, 4785};
|
||||
|
||||
static const struct inv_mpu6050_reg_map reg_set_icm20602 = {
|
||||
.sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV,
|
||||
.lpf = INV_MPU6050_REG_CONFIG,
|
||||
.accel_lpf = INV_MPU6500_REG_ACCEL_CONFIG_2,
|
||||
.user_ctrl = INV_MPU6050_REG_USER_CTRL,
|
||||
.fifo_en = INV_MPU6050_REG_FIFO_EN,
|
||||
.gyro_config = INV_MPU6050_REG_GYRO_CONFIG,
|
||||
.accl_config = INV_MPU6050_REG_ACCEL_CONFIG,
|
||||
.fifo_count_h = INV_MPU6050_REG_FIFO_COUNT_H,
|
||||
.fifo_r_w = INV_MPU6050_REG_FIFO_R_W,
|
||||
.raw_gyro = INV_MPU6050_REG_RAW_GYRO,
|
||||
.raw_accl = INV_MPU6050_REG_RAW_ACCEL,
|
||||
.temperature = INV_MPU6050_REG_TEMPERATURE,
|
||||
.int_enable = INV_MPU6050_REG_INT_ENABLE,
|
||||
.int_status = INV_MPU6050_REG_INT_STATUS,
|
||||
.pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1,
|
||||
.pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2,
|
||||
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
|
||||
.accl_offset = INV_MPU6500_REG_ACCEL_OFFSET,
|
||||
.gyro_offset = INV_MPU6050_REG_GYRO_OFFSET,
|
||||
.i2c_if = INV_ICM20602_REG_I2C_IF,
|
||||
};
|
||||
|
||||
static const struct inv_mpu6050_reg_map reg_set_6500 = {
|
||||
.sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV,
|
||||
.lpf = INV_MPU6050_REG_CONFIG,
|
||||
@ -58,6 +81,7 @@ static const struct inv_mpu6050_reg_map reg_set_6500 = {
|
||||
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
|
||||
.accl_offset = INV_MPU6500_REG_ACCEL_OFFSET,
|
||||
.gyro_offset = INV_MPU6050_REG_GYRO_OFFSET,
|
||||
.i2c_if = 0,
|
||||
};
|
||||
|
||||
static const struct inv_mpu6050_reg_map reg_set_6050 = {
|
||||
@ -78,6 +102,7 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = {
|
||||
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
|
||||
.accl_offset = INV_MPU6050_REG_ACCEL_OFFSET,
|
||||
.gyro_offset = INV_MPU6050_REG_GYRO_OFFSET,
|
||||
.i2c_if = 0,
|
||||
};
|
||||
|
||||
static const struct inv_mpu6050_chip_config chip_config_6050 = {
|
||||
@ -140,6 +165,12 @@ static const struct inv_mpu6050_hw hw_info[] = {
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6050,
|
||||
},
|
||||
{
|
||||
.whoami = INV_ICM20602_WHOAMI_VALUE,
|
||||
.name = "ICM20602",
|
||||
.reg = ®_set_icm20602,
|
||||
.config = &chip_config_6050,
|
||||
},
|
||||
};
|
||||
|
||||
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
|
||||
|
@ -127,6 +127,7 @@ static int inv_mpu_probe(struct i2c_client *client,
|
||||
st = iio_priv(dev_get_drvdata(&client->dev));
|
||||
switch (st->chip_type) {
|
||||
case INV_ICM20608:
|
||||
case INV_ICM20602:
|
||||
/* no i2c auxiliary bus on the chip */
|
||||
break;
|
||||
default:
|
||||
@ -179,6 +180,7 @@ static const struct i2c_device_id inv_mpu_id[] = {
|
||||
{"mpu9250", INV_MPU9250},
|
||||
{"mpu9255", INV_MPU9255},
|
||||
{"icm20608", INV_ICM20608},
|
||||
{"icm20602", INV_ICM20602},
|
||||
{}
|
||||
};
|
||||
|
||||
@ -213,6 +215,10 @@ static const struct of_device_id inv_of_match[] = {
|
||||
.compatible = "invensense,icm20608",
|
||||
.data = (void *)INV_ICM20608
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,icm20602",
|
||||
.data = (void *)INV_ICM20602
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, inv_of_match);
|
||||
|
@ -44,6 +44,7 @@
|
||||
* @int_pin_cfg; Controls interrupt pin configuration.
|
||||
* @accl_offset: Controls the accelerometer calibration offset.
|
||||
* @gyro_offset: Controls the gyroscope calibration offset.
|
||||
* @i2c_if: Controls the i2c interface
|
||||
*/
|
||||
struct inv_mpu6050_reg_map {
|
||||
u8 sample_rate_div;
|
||||
@ -65,6 +66,7 @@ struct inv_mpu6050_reg_map {
|
||||
u8 int_pin_cfg;
|
||||
u8 accl_offset;
|
||||
u8 gyro_offset;
|
||||
u8 i2c_if;
|
||||
};
|
||||
|
||||
/*device enum */
|
||||
@ -77,6 +79,7 @@ enum inv_devices {
|
||||
INV_MPU9250,
|
||||
INV_MPU9255,
|
||||
INV_ICM20608,
|
||||
INV_ICM20602,
|
||||
INV_NUM_PARTS
|
||||
};
|
||||
|
||||
@ -195,6 +198,10 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38
|
||||
#define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07
|
||||
|
||||
/* ICM20602 register */
|
||||
#define INV_ICM20602_REG_I2C_IF 0x70
|
||||
#define INV_ICM20602_BIT_I2C_IF_DIS 0x40
|
||||
|
||||
#define INV_MPU6050_REG_FIFO_COUNT_H 0x72
|
||||
#define INV_MPU6050_REG_FIFO_R_W 0x74
|
||||
|
||||
@ -261,6 +268,7 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU9255_WHOAMI_VALUE 0x73
|
||||
#define INV_MPU6515_WHOAMI_VALUE 0x74
|
||||
#define INV_ICM20608_WHOAMI_VALUE 0xAF
|
||||
#define INV_ICM20602_WHOAMI_VALUE 0x12
|
||||
|
||||
/* scan element definition */
|
||||
enum inv_mpu6050_scan {
|
||||
|
@ -31,9 +31,14 @@ static int inv_mpu_i2c_disable(struct iio_dev *indio_dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->chip_config.user_ctrl |= INV_MPU6050_BIT_I2C_IF_DIS;
|
||||
ret = regmap_write(st->map, st->reg->user_ctrl,
|
||||
st->chip_config.user_ctrl);
|
||||
if (st->reg->i2c_if) {
|
||||
ret = regmap_write(st->map, st->reg->i2c_if,
|
||||
INV_ICM20602_BIT_I2C_IF_DIS);
|
||||
} else {
|
||||
st->chip_config.user_ctrl |= INV_MPU6050_BIT_I2C_IF_DIS;
|
||||
ret = regmap_write(st->map, st->reg->user_ctrl,
|
||||
st->chip_config.user_ctrl);
|
||||
}
|
||||
if (ret) {
|
||||
inv_mpu6050_set_power_itg(st, false);
|
||||
return ret;
|
||||
@ -81,6 +86,7 @@ static const struct spi_device_id inv_mpu_id[] = {
|
||||
{"mpu9250", INV_MPU9250},
|
||||
{"mpu9255", INV_MPU9255},
|
||||
{"icm20608", INV_ICM20608},
|
||||
{"icm20602", INV_ICM20602},
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -105,12 +105,10 @@ static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw)
|
||||
static int st_lsm6dsx_shub_read_reg(struct st_lsm6dsx_hw *hw, u8 addr,
|
||||
u8 *data, int len)
|
||||
{
|
||||
const struct st_lsm6dsx_shub_settings *hub_settings;
|
||||
int err;
|
||||
|
||||
mutex_lock(&hw->page_lock);
|
||||
|
||||
hub_settings = &hw->settings->shub_settings;
|
||||
err = st_lsm6dsx_set_page(hw, true);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
@ -87,6 +87,7 @@ static const char * const iio_chan_type_name_spec[] = {
|
||||
[IIO_GRAVITY] = "gravity",
|
||||
[IIO_POSITIONRELATIVE] = "positionrelative",
|
||||
[IIO_PHASE] = "phase",
|
||||
[IIO_MASSCONCENTRATION] = "massconcentration",
|
||||
};
|
||||
|
||||
static const char * const iio_modifier_names[] = {
|
||||
@ -127,6 +128,10 @@ static const char * const iio_modifier_names[] = {
|
||||
[IIO_MOD_Q] = "q",
|
||||
[IIO_MOD_CO2] = "co2",
|
||||
[IIO_MOD_VOC] = "voc",
|
||||
[IIO_MOD_PM1] = "pm1",
|
||||
[IIO_MOD_PM2P5] = "pm2p5",
|
||||
[IIO_MOD_PM4] = "pm4",
|
||||
[IIO_MOD_PM10] = "pm10",
|
||||
};
|
||||
|
||||
/* relies on pairs of these shared then separate */
|
||||
|
@ -299,6 +299,16 @@ config MAX44000
|
||||
To compile this driver as a module, choose M here:
|
||||
the module will be called max44000.
|
||||
|
||||
config MAX44009
|
||||
tristate "MAX44009 Ambient Light Sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to build support for Maxim Integrated's
|
||||
MAX44009 ambient light sensor device.
|
||||
|
||||
To compile this driver as a module, choose M here:
|
||||
the module will be called max44009.
|
||||
|
||||
config OPT3001
|
||||
tristate "Texas Instruments OPT3001 Light Sensor"
|
||||
depends on I2C
|
||||
|
@ -28,6 +28,7 @@ obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
|
||||
obj-$(CONFIG_LTR501) += ltr501.o
|
||||
obj-$(CONFIG_LV0104CS) += lv0104cs.o
|
||||
obj-$(CONFIG_MAX44000) += max44000.o
|
||||
obj-$(CONFIG_MAX44009) += max44009.o
|
||||
obj-$(CONFIG_OPT3001) += opt3001.o
|
||||
obj-$(CONFIG_PA12203001) += pa12203001.o
|
||||
obj-$(CONFIG_RPR0521) += rpr0521.o
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@ -95,6 +96,7 @@ struct isl29018_chip {
|
||||
struct isl29018_scale scale;
|
||||
int prox_scheme;
|
||||
bool suspended;
|
||||
struct regulator *vcc_reg;
|
||||
};
|
||||
|
||||
static int isl29018_set_integration_time(struct isl29018_chip *chip,
|
||||
@ -708,6 +710,16 @@ static const char *isl29018_match_acpi_device(struct device *dev, int *data)
|
||||
return dev_name(dev);
|
||||
}
|
||||
|
||||
static void isl29018_disable_regulator_action(void *_data)
|
||||
{
|
||||
struct isl29018_chip *chip = _data;
|
||||
int err;
|
||||
|
||||
err = regulator_disable(chip->vcc_reg);
|
||||
if (err)
|
||||
pr_err("failed to disable isl29018's VCC regulator!\n");
|
||||
}
|
||||
|
||||
static int isl29018_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@ -742,6 +754,27 @@ static int isl29018_probe(struct i2c_client *client,
|
||||
chip->scale = isl29018_scales[chip->int_time][0];
|
||||
chip->suspended = false;
|
||||
|
||||
chip->vcc_reg = devm_regulator_get(&client->dev, "vcc");
|
||||
if (IS_ERR(chip->vcc_reg)) {
|
||||
err = PTR_ERR(chip->vcc_reg);
|
||||
if (err != -EPROBE_DEFER)
|
||||
dev_err(&client->dev, "failed to get VCC regulator!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = regulator_enable(chip->vcc_reg);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "failed to enable VCC regulator!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = devm_add_action_or_reset(&client->dev, isl29018_disable_regulator_action,
|
||||
chip);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "failed to setup regulator cleanup action!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
chip->regmap = devm_regmap_init_i2c(client,
|
||||
isl29018_chip_info_tbl[dev_id].regmap_cfg);
|
||||
if (IS_ERR(chip->regmap)) {
|
||||
@ -768,6 +801,7 @@ static int isl29018_probe(struct i2c_client *client,
|
||||
static int isl29018_suspend(struct device *dev)
|
||||
{
|
||||
struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev));
|
||||
int ret;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
@ -777,10 +811,13 @@ static int isl29018_suspend(struct device *dev)
|
||||
* So we do not have much to do here.
|
||||
*/
|
||||
chip->suspended = true;
|
||||
ret = regulator_disable(chip->vcc_reg);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to disable VCC regulator\n");
|
||||
|
||||
mutex_unlock(&chip->lock);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int isl29018_resume(struct device *dev)
|
||||
@ -790,6 +827,13 @@ static int isl29018_resume(struct device *dev)
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
err = regulator_enable(chip->vcc_reg);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to enable VCC regulator\n");
|
||||
mutex_unlock(&chip->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = isl29018_chip_init(chip);
|
||||
if (!err)
|
||||
chip->suspended = false;
|
||||
|
555
drivers/iio/light/max44009.c
Normal file
555
drivers/iio/light/max44009.c
Normal file
@ -0,0 +1,555 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* max44009.c - Support for MAX44009 Ambient Light Sensor
|
||||
*
|
||||
* Copyright (c) 2019 Robert Eshleman <bobbyeshleman@gmail.com>
|
||||
*
|
||||
* Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX44009.pdf
|
||||
*
|
||||
* TODO: Support continuous mode and configuring from manual mode to
|
||||
* automatic mode.
|
||||
*
|
||||
* Default I2C address: 0x4a
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/util_macros.h>
|
||||
|
||||
#define MAX44009_DRV_NAME "max44009"
|
||||
|
||||
/* Registers in datasheet order */
|
||||
#define MAX44009_REG_INT_STATUS 0x0
|
||||
#define MAX44009_REG_INT_EN 0x1
|
||||
#define MAX44009_REG_CFG 0x2
|
||||
#define MAX44009_REG_LUX_HI 0x3
|
||||
#define MAX44009_REG_LUX_LO 0x4
|
||||
#define MAX44009_REG_UPPER_THR 0x5
|
||||
#define MAX44009_REG_LOWER_THR 0x6
|
||||
#define MAX44009_REG_THR_TIMER 0x7
|
||||
|
||||
#define MAX44009_CFG_TIM_MASK GENMASK(2, 0)
|
||||
#define MAX44009_CFG_MAN_MODE_MASK BIT(6)
|
||||
|
||||
/* The maximum rising threshold for the max44009 */
|
||||
#define MAX44009_MAXIMUM_THRESHOLD 7520256
|
||||
|
||||
#define MAX44009_THRESH_EXP_MASK (0xf << 4)
|
||||
#define MAX44009_THRESH_EXP_RSHIFT 4
|
||||
#define MAX44009_THRESH_MANT_LSHIFT 4
|
||||
#define MAX44009_THRESH_MANT_MASK 0xf
|
||||
|
||||
#define MAX44009_UPPER_THR_MINIMUM 15
|
||||
|
||||
/* The max44009 always scales raw readings by 0.045 and is non-configurable */
|
||||
#define MAX44009_SCALE_NUMERATOR 45
|
||||
#define MAX44009_SCALE_DENOMINATOR 1000
|
||||
|
||||
/* The fixed-point fractional multiplier for de-scaling threshold values */
|
||||
#define MAX44009_FRACT_MULT 1000000
|
||||
|
||||
static const u32 max44009_int_time_ns_array[] = {
|
||||
800000000,
|
||||
400000000,
|
||||
200000000,
|
||||
100000000,
|
||||
50000000, /* Manual mode only */
|
||||
25000000, /* Manual mode only */
|
||||
12500000, /* Manual mode only */
|
||||
6250000, /* Manual mode only */
|
||||
};
|
||||
|
||||
static const char max44009_int_time_str[] =
|
||||
"0.8 "
|
||||
"0.4 "
|
||||
"0.2 "
|
||||
"0.1 "
|
||||
"0.05 "
|
||||
"0.025 "
|
||||
"0.0125 "
|
||||
"0.00625";
|
||||
|
||||
struct max44009_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static const struct iio_event_spec max44009_event_spec[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec max44009_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
|
||||
BIT(IIO_CHAN_INFO_INT_TIME),
|
||||
.event_spec = max44009_event_spec,
|
||||
.num_event_specs = ARRAY_SIZE(max44009_event_spec),
|
||||
},
|
||||
};
|
||||
|
||||
static int max44009_read_int_time(struct max44009_data *data)
|
||||
{
|
||||
|
||||
int ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_CFG);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return max44009_int_time_ns_array[ret & MAX44009_CFG_TIM_MASK];
|
||||
}
|
||||
|
||||
static int max44009_write_int_time(struct max44009_data *data,
|
||||
int val, int val2)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int ret, int_time, config;
|
||||
s64 ns;
|
||||
|
||||
ns = val * NSEC_PER_SEC + val2;
|
||||
int_time = find_closest_descending(
|
||||
ns,
|
||||
max44009_int_time_ns_array,
|
||||
ARRAY_SIZE(max44009_int_time_ns_array));
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
config = ret;
|
||||
config &= int_time;
|
||||
|
||||
/*
|
||||
* To set the integration time, the device must also be in manual
|
||||
* mode.
|
||||
*/
|
||||
config |= MAX44009_CFG_MAN_MODE_MASK;
|
||||
|
||||
return i2c_smbus_write_byte_data(client, MAX44009_REG_CFG, config);
|
||||
}
|
||||
|
||||
static int max44009_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct max44009_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (mask == IIO_CHAN_INFO_INT_TIME && chan->type == IIO_LIGHT) {
|
||||
mutex_lock(&data->lock);
|
||||
ret = max44009_write_int_time(data, val, val2);
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int max44009_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
}
|
||||
|
||||
static int max44009_lux_raw(u8 hi, u8 lo)
|
||||
{
|
||||
int mantissa;
|
||||
int exponent;
|
||||
|
||||
/*
|
||||
* The mantissa consists of the low nibble of the Lux High Byte
|
||||
* and the low nibble of the Lux Low Byte.
|
||||
*/
|
||||
mantissa = ((hi & 0xf) << 4) | (lo & 0xf);
|
||||
|
||||
/* The exponent byte is just the upper nibble of the Lux High Byte */
|
||||
exponent = (hi >> 4) & 0xf;
|
||||
|
||||
/*
|
||||
* The exponent value is base 2 to the power of the raw exponent byte.
|
||||
*/
|
||||
exponent = 1 << exponent;
|
||||
|
||||
return exponent * mantissa;
|
||||
}
|
||||
|
||||
#define MAX44009_READ_LUX_XFER_LEN (4)
|
||||
|
||||
static int max44009_read_lux_raw(struct max44009_data *data)
|
||||
{
|
||||
int ret;
|
||||
u8 hireg = MAX44009_REG_LUX_HI;
|
||||
u8 loreg = MAX44009_REG_LUX_LO;
|
||||
u8 lo = 0;
|
||||
u8 hi = 0;
|
||||
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = data->client->addr,
|
||||
.flags = 0,
|
||||
.len = sizeof(hireg),
|
||||
.buf = &hireg,
|
||||
},
|
||||
{
|
||||
.addr = data->client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = sizeof(hi),
|
||||
.buf = &hi,
|
||||
},
|
||||
{
|
||||
.addr = data->client->addr,
|
||||
.flags = 0,
|
||||
.len = sizeof(loreg),
|
||||
.buf = &loreg,
|
||||
},
|
||||
{
|
||||
.addr = data->client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = sizeof(lo),
|
||||
.buf = &lo,
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Use i2c_transfer instead of smbus read because i2c_transfer
|
||||
* does NOT use a stop bit between address write and data read.
|
||||
* Using a stop bit causes disjoint upper/lower byte reads and
|
||||
* reduces accuracy.
|
||||
*/
|
||||
ret = i2c_transfer(data->client->adapter,
|
||||
msgs, MAX44009_READ_LUX_XFER_LEN);
|
||||
|
||||
if (ret != MAX44009_READ_LUX_XFER_LEN)
|
||||
return -EIO;
|
||||
|
||||
return max44009_lux_raw(hi, lo);
|
||||
}
|
||||
|
||||
static int max44009_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct max44009_data *data = iio_priv(indio_dev);
|
||||
int lux_raw;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
ret = max44009_read_lux_raw(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
lux_raw = ret;
|
||||
|
||||
*val = lux_raw * MAX44009_SCALE_NUMERATOR;
|
||||
*val2 = MAX44009_SCALE_DENOMINATOR;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_INT_TIME:
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
ret = max44009_read_int_time(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val2 = ret;
|
||||
*val = 0;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR(illuminance_integration_time_available,
|
||||
max44009_int_time_str);
|
||||
|
||||
static struct attribute *max44009_attributes[] = {
|
||||
&iio_const_attr_illuminance_integration_time_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group max44009_attribute_group = {
|
||||
.attrs = max44009_attributes,
|
||||
};
|
||||
|
||||
static int max44009_threshold_byte_from_fraction(int integral, int fractional)
|
||||
{
|
||||
int mantissa, exp;
|
||||
|
||||
if ((integral <= 0 && fractional <= 0) ||
|
||||
integral > MAX44009_MAXIMUM_THRESHOLD ||
|
||||
(integral == MAX44009_MAXIMUM_THRESHOLD && fractional != 0))
|
||||
return -EINVAL;
|
||||
|
||||
/* Reverse scaling of fixed-point integral */
|
||||
mantissa = integral * MAX44009_SCALE_DENOMINATOR;
|
||||
mantissa /= MAX44009_SCALE_NUMERATOR;
|
||||
|
||||
/* Reverse scaling of fixed-point fractional */
|
||||
mantissa += fractional / MAX44009_FRACT_MULT *
|
||||
(MAX44009_SCALE_DENOMINATOR / MAX44009_SCALE_NUMERATOR);
|
||||
|
||||
for (exp = 0; mantissa > 0xff; exp++)
|
||||
mantissa >>= 1;
|
||||
|
||||
mantissa >>= 4;
|
||||
mantissa &= 0xf;
|
||||
exp <<= 4;
|
||||
|
||||
return exp | mantissa;
|
||||
}
|
||||
|
||||
static int max44009_get_thr_reg(enum iio_event_direction dir)
|
||||
{
|
||||
switch (dir) {
|
||||
case IIO_EV_DIR_RISING:
|
||||
return MAX44009_REG_UPPER_THR;
|
||||
case IIO_EV_DIR_FALLING:
|
||||
return MAX44009_REG_LOWER_THR;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int max44009_write_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int val, int val2)
|
||||
{
|
||||
struct max44009_data *data = iio_priv(indio_dev);
|
||||
int reg, threshold;
|
||||
|
||||
if (info != IIO_EV_INFO_VALUE || chan->type != IIO_LIGHT)
|
||||
return -EINVAL;
|
||||
|
||||
threshold = max44009_threshold_byte_from_fraction(val, val2);
|
||||
if (threshold < 0)
|
||||
return threshold;
|
||||
|
||||
reg = max44009_get_thr_reg(dir);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
return i2c_smbus_write_byte_data(data->client, reg, threshold);
|
||||
}
|
||||
|
||||
static int max44009_read_threshold(struct iio_dev *indio_dev,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct max44009_data *data = iio_priv(indio_dev);
|
||||
int byte, reg;
|
||||
int mantissa, exponent;
|
||||
|
||||
reg = max44009_get_thr_reg(dir);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
byte = i2c_smbus_read_byte_data(data->client, reg);
|
||||
if (byte < 0)
|
||||
return byte;
|
||||
|
||||
mantissa = byte & MAX44009_THRESH_MANT_MASK;
|
||||
mantissa <<= MAX44009_THRESH_MANT_LSHIFT;
|
||||
|
||||
/*
|
||||
* To get the upper threshold, always adds the minimum upper threshold
|
||||
* value to the shifted byte value (see datasheet).
|
||||
*/
|
||||
if (dir == IIO_EV_DIR_RISING)
|
||||
mantissa += MAX44009_UPPER_THR_MINIMUM;
|
||||
|
||||
/*
|
||||
* Exponent is base 2 to the power of the threshold exponent byte
|
||||
* value
|
||||
*/
|
||||
exponent = byte & MAX44009_THRESH_EXP_MASK;
|
||||
exponent >>= MAX44009_THRESH_EXP_RSHIFT;
|
||||
|
||||
return (1 << exponent) * mantissa;
|
||||
}
|
||||
|
||||
static int max44009_read_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int *val, int *val2)
|
||||
{
|
||||
int ret;
|
||||
int threshold;
|
||||
|
||||
if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)
|
||||
return -EINVAL;
|
||||
|
||||
ret = max44009_read_threshold(indio_dev, dir);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
threshold = ret;
|
||||
|
||||
*val = threshold * MAX44009_SCALE_NUMERATOR;
|
||||
*val2 = MAX44009_SCALE_DENOMINATOR;
|
||||
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
}
|
||||
|
||||
static int max44009_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
int state)
|
||||
{
|
||||
struct max44009_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)
|
||||
return -EINVAL;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
MAX44009_REG_INT_EN, state);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Set device to trigger interrupt immediately upon exceeding
|
||||
* the threshold limit.
|
||||
*/
|
||||
return i2c_smbus_write_byte_data(data->client,
|
||||
MAX44009_REG_THR_TIMER, 0);
|
||||
}
|
||||
|
||||
static int max44009_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct max44009_data *data = iio_priv(indio_dev);
|
||||
|
||||
if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)
|
||||
return -EINVAL;
|
||||
|
||||
return i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_EN);
|
||||
}
|
||||
|
||||
static const struct iio_info max44009_info = {
|
||||
.read_raw = max44009_read_raw,
|
||||
.write_raw = max44009_write_raw,
|
||||
.write_raw_get_fmt = max44009_write_raw_get_fmt,
|
||||
.read_event_value = max44009_read_event_value,
|
||||
.read_event_config = max44009_read_event_config,
|
||||
.write_event_value = max44009_write_event_value,
|
||||
.write_event_config = max44009_write_event_config,
|
||||
.attrs = &max44009_attribute_group,
|
||||
};
|
||||
|
||||
static irqreturn_t max44009_threaded_irq_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_dev *indio_dev = p;
|
||||
struct max44009_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_STATUS);
|
||||
if (ret) {
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_EITHER),
|
||||
iio_get_time_ns(indio_dev));
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int max44009_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max44009_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &max44009_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->name = MAX44009_DRV_NAME;
|
||||
indio_dev->channels = max44009_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(max44009_channels);
|
||||
mutex_init(&data->lock);
|
||||
|
||||
/* Clear any stale interrupt bit */
|
||||
ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL,
|
||||
max44009_threaded_irq_handler,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT | IRQF_SHARED,
|
||||
"max44009_event",
|
||||
indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max44009_id[] = {
|
||||
{ "max44009", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max44009_id);
|
||||
|
||||
static struct i2c_driver max44009_driver = {
|
||||
.driver = {
|
||||
.name = MAX44009_DRV_NAME,
|
||||
},
|
||||
.probe = max44009_probe,
|
||||
.id_table = max44009_id,
|
||||
};
|
||||
module_i2c_driver(max44009_driver);
|
||||
|
||||
static const struct of_device_id max44009_of_match[] = {
|
||||
{ .compatible = "maxim,max44009" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max44009_of_match);
|
||||
|
||||
MODULE_AUTHOR("Robert Eshleman <bobbyeshleman@gmail.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("MAX44009 ambient light sensor driver");
|
@ -20,6 +20,7 @@
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define MAG3110_STATUS 0x00
|
||||
#define MAG3110_OUT_X 0x01 /* MSB first */
|
||||
@ -56,6 +57,8 @@ struct mag3110_data {
|
||||
struct mutex lock;
|
||||
u8 ctrl_reg1;
|
||||
int sleep_val;
|
||||
struct regulator *vdd_reg;
|
||||
struct regulator *vddio_reg;
|
||||
};
|
||||
|
||||
static int mag3110_request(struct mag3110_data *data)
|
||||
@ -469,17 +472,50 @@ static int mag3110_probe(struct i2c_client *client,
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, MAG3110_WHO_AM_I);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != MAG3110_DEVICE_ID)
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
|
||||
data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
|
||||
if (IS_ERR(data->vdd_reg)) {
|
||||
if (PTR_ERR(data->vdd_reg) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
dev_err(&client->dev, "failed to get VDD regulator!\n");
|
||||
return PTR_ERR(data->vdd_reg);
|
||||
}
|
||||
|
||||
data->vddio_reg = devm_regulator_get(&client->dev, "vddio");
|
||||
if (IS_ERR(data->vddio_reg)) {
|
||||
if (PTR_ERR(data->vddio_reg) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
dev_err(&client->dev, "failed to get VDDIO regulator!\n");
|
||||
return PTR_ERR(data->vddio_reg);
|
||||
}
|
||||
|
||||
ret = regulator_enable(data->vdd_reg);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to enable VDD regulator!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_enable(data->vddio_reg);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to enable VDDIO regulator!\n");
|
||||
goto disable_regulator_vdd;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, MAG3110_WHO_AM_I);
|
||||
if (ret < 0)
|
||||
goto disable_regulators;
|
||||
if (ret != MAG3110_DEVICE_ID) {
|
||||
ret = -ENODEV;
|
||||
goto disable_regulators;
|
||||
}
|
||||
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
@ -499,7 +535,7 @@ static int mag3110_probe(struct i2c_client *client,
|
||||
|
||||
ret = mag3110_change_config(data, MAG3110_CTRL_REG1, data->ctrl_reg1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto disable_regulators;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG2,
|
||||
MAG3110_CTRL_AUTO_MRST_EN);
|
||||
@ -520,16 +556,24 @@ buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
standby_on_error:
|
||||
mag3110_standby(iio_priv(indio_dev));
|
||||
disable_regulators:
|
||||
regulator_disable(data->vddio_reg);
|
||||
disable_regulator_vdd:
|
||||
regulator_disable(data->vdd_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mag3110_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct mag3110_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
mag3110_standby(iio_priv(indio_dev));
|
||||
regulator_disable(data->vddio_reg);
|
||||
regulator_disable(data->vdd_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -537,14 +581,48 @@ static int mag3110_remove(struct i2c_client *client)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mag3110_suspend(struct device *dev)
|
||||
{
|
||||
return mag3110_standby(iio_priv(i2c_get_clientdata(
|
||||
struct mag3110_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
int ret;
|
||||
|
||||
ret = mag3110_standby(iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev))));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_disable(data->vddio_reg);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to disable VDDIO regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_disable(data->vdd_reg);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to disable VDD regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mag3110_resume(struct device *dev)
|
||||
{
|
||||
struct mag3110_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(data->vdd_reg);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable VDD regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_enable(data->vddio_reg);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable VDDIO regulator\n");
|
||||
regulator_disable(data->vdd_reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
|
@ -165,7 +165,7 @@ config IIO_ST_PRESS
|
||||
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics pressure
|
||||
sensors: LPS001WP, LPS25H, LPS331AP, LPS22HB.
|
||||
sensors: LPS001WP, LPS25H, LPS331AP, LPS22HB, LPS22HH.
|
||||
|
||||
This driver can also be built as a module. If so, these modules
|
||||
will be created:
|
||||
|
@ -21,6 +21,7 @@ enum st_press_type {
|
||||
LPS22HB,
|
||||
LPS33HW,
|
||||
LPS35HW,
|
||||
LPS22HH,
|
||||
ST_PRESS_MAX,
|
||||
};
|
||||
|
||||
@ -30,6 +31,7 @@ enum st_press_type {
|
||||
#define LPS22HB_PRESS_DEV_NAME "lps22hb"
|
||||
#define LPS33HW_PRESS_DEV_NAME "lps33hw"
|
||||
#define LPS35HW_PRESS_DEV_NAME "lps35hw"
|
||||
#define LPS22HH_PRESS_DEV_NAME "lps22hh"
|
||||
|
||||
/**
|
||||
* struct st_sensors_platform_data - default press platform data
|
||||
|
@ -492,6 +492,75 @@ static const struct st_sensor_settings st_press_sensors_settings[] = {
|
||||
.multi_read_bit = false,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
/*
|
||||
* CUSTOM VALUES FOR LPS22HH SENSOR
|
||||
* See LPS22HH datasheet:
|
||||
* http://www2.st.com/resource/en/datasheet/lps22hh.pdf
|
||||
*/
|
||||
.wai = 0xb3,
|
||||
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
|
||||
.sensors_supported = {
|
||||
[0] = LPS22HH_PRESS_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_press_lps22hb_channels,
|
||||
.num_ch = ARRAY_SIZE(st_press_lps22hb_channels),
|
||||
.odr = {
|
||||
.addr = 0x10,
|
||||
.mask = 0x70,
|
||||
.odr_avl = {
|
||||
{ .hz = 1, .value = 0x01 },
|
||||
{ .hz = 10, .value = 0x02 },
|
||||
{ .hz = 25, .value = 0x03 },
|
||||
{ .hz = 50, .value = 0x04 },
|
||||
{ .hz = 75, .value = 0x05 },
|
||||
{ .hz = 100, .value = 0x06 },
|
||||
{ .hz = 200, .value = 0x07 },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = 0x10,
|
||||
.mask = 0x70,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.fs = {
|
||||
.fs_avl = {
|
||||
/*
|
||||
* Pressure and temperature sensitivity values
|
||||
* as defined in table 3 of LPS22HH datasheet.
|
||||
*/
|
||||
[0] = {
|
||||
.num = ST_PRESS_FS_AVL_1260MB,
|
||||
.gain = ST_PRESS_KPASCAL_NANO_SCALE,
|
||||
.gain2 = ST_PRESS_LPS22HB_LSB_PER_CELSIUS,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = 0x10,
|
||||
.mask = BIT(1),
|
||||
},
|
||||
.drdy_irq = {
|
||||
.int1 = {
|
||||
.addr = 0x12,
|
||||
.mask = BIT(2),
|
||||
.addr_od = 0x11,
|
||||
.mask_od = BIT(5),
|
||||
},
|
||||
.addr_ihl = 0x11,
|
||||
.mask_ihl = BIT(6),
|
||||
.stat_drdy = {
|
||||
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
|
||||
.mask = 0x03,
|
||||
},
|
||||
},
|
||||
.sim = {
|
||||
.addr = 0x10,
|
||||
.value = BIT(0),
|
||||
},
|
||||
.multi_read_bit = false,
|
||||
.bootime = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static int st_press_write_raw(struct iio_dev *indio_dev,
|
||||
|
@ -45,6 +45,10 @@ static const struct of_device_id st_press_of_match[] = {
|
||||
.compatible = "st,lps35hw",
|
||||
.data = LPS35HW_PRESS_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lps22hh",
|
||||
.data = LPS22HH_PRESS_DEV_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_press_of_match);
|
||||
@ -69,6 +73,7 @@ static const struct i2c_device_id st_press_id_table[] = {
|
||||
{ LPS22HB_PRESS_DEV_NAME, LPS22HB },
|
||||
{ LPS33HW_PRESS_DEV_NAME, LPS33HW },
|
||||
{ LPS35HW_PRESS_DEV_NAME, LPS35HW },
|
||||
{ LPS22HH_PRESS_DEV_NAME, LPS22HH },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_press_id_table);
|
||||
|
@ -49,6 +49,10 @@ static const struct of_device_id st_press_of_match[] = {
|
||||
.compatible = "st,lps35hw",
|
||||
.data = LPS35HW_PRESS_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lps22hh",
|
||||
.data = LPS22HH_PRESS_DEV_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_press_of_match);
|
||||
@ -93,6 +97,7 @@ static const struct spi_device_id st_press_id_table[] = {
|
||||
{ LPS22HB_PRESS_DEV_NAME },
|
||||
{ LPS33HW_PRESS_DEV_NAME },
|
||||
{ LPS35HW_PRESS_DEV_NAME },
|
||||
{ LPS22HH_PRESS_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_press_id_table);
|
||||
|
@ -3,40 +3,6 @@
|
||||
#
|
||||
menu "Analog to digital converters"
|
||||
|
||||
config AD7606
|
||||
tristate "Analog Devices AD7606 ADC driver"
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices:
|
||||
ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC).
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7606.
|
||||
|
||||
config AD7606_IFACE_PARALLEL
|
||||
tristate "parallel interface support"
|
||||
depends on AD7606
|
||||
help
|
||||
Say yes here to include parallel interface support on the AD7606
|
||||
ADC driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7606_parallel.
|
||||
|
||||
config AD7606_IFACE_SPI
|
||||
tristate "spi interface support"
|
||||
depends on AD7606
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to include parallel interface support on the AD7606
|
||||
ADC driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7606_spi.
|
||||
|
||||
config AD7780
|
||||
tristate "Analog Devices AD7780 and similar ADCs driver"
|
||||
depends on SPI
|
||||
|
@ -3,10 +3,6 @@
|
||||
# Makefile for industrial I/O ADC drivers
|
||||
#
|
||||
|
||||
obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o
|
||||
obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o
|
||||
obj-$(CONFIG_AD7606) += ad7606.o
|
||||
|
||||
obj-$(CONFIG_AD7780) += ad7780.o
|
||||
obj-$(CONFIG_AD7816) += ad7816.o
|
||||
obj-$(CONFIG_AD7192) += ad7192.o
|
||||
|
@ -97,6 +97,10 @@
|
||||
#define AD7280A_NUM_CH (AD7280A_AUX_ADC_6 - \
|
||||
AD7280A_CELL_VOLTAGE_1 + 1)
|
||||
|
||||
#define AD7280A_CALC_VOLTAGE_CHAN_NUM(d, c) ((d * AD7280A_CELLS_PER_DEV) + c)
|
||||
#define AD7280A_CALC_TEMP_CHAN_NUM(d, c) ((d * AD7280A_CELLS_PER_DEV) + \
|
||||
c - AD7280A_CELLS_PER_DEV)
|
||||
|
||||
#define AD7280A_DEVADDR_MASTER 0
|
||||
#define AD7280A_DEVADDR_ALL 0x1F
|
||||
/* 5-bit device address is sent LSB first */
|
||||
@ -496,72 +500,171 @@ static const struct attribute_group ad7280_attrs_group = {
|
||||
.attrs = ad7280_attributes,
|
||||
};
|
||||
|
||||
static void ad7280_voltage_channel_init(struct iio_chan_spec *chan, int i)
|
||||
{
|
||||
chan->type = IIO_VOLTAGE;
|
||||
chan->differential = 1;
|
||||
chan->channel = i;
|
||||
chan->channel2 = chan->channel + 1;
|
||||
}
|
||||
|
||||
static void ad7280_temp_channel_init(struct iio_chan_spec *chan, int i)
|
||||
{
|
||||
chan->type = IIO_TEMP;
|
||||
chan->channel = i;
|
||||
}
|
||||
|
||||
static void ad7280_common_fields_init(struct iio_chan_spec *chan, int addr,
|
||||
int cnt)
|
||||
{
|
||||
chan->indexed = 1;
|
||||
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
|
||||
chan->address = addr;
|
||||
chan->scan_index = cnt;
|
||||
chan->scan_type.sign = 'u';
|
||||
chan->scan_type.realbits = 12;
|
||||
chan->scan_type.storagebits = 32;
|
||||
}
|
||||
|
||||
static void ad7280_total_voltage_channel_init(struct iio_chan_spec *chan,
|
||||
int cnt, int dev)
|
||||
{
|
||||
chan->type = IIO_VOLTAGE;
|
||||
chan->differential = 1;
|
||||
chan->channel = 0;
|
||||
chan->channel2 = dev * AD7280A_CELLS_PER_DEV;
|
||||
chan->address = AD7280A_ALL_CELLS;
|
||||
chan->indexed = 1;
|
||||
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
|
||||
chan->scan_index = cnt;
|
||||
chan->scan_type.sign = 'u';
|
||||
chan->scan_type.realbits = 32;
|
||||
chan->scan_type.storagebits = 32;
|
||||
}
|
||||
|
||||
static void ad7280_timestamp_channel_init(struct iio_chan_spec *chan, int cnt)
|
||||
{
|
||||
chan->type = IIO_TIMESTAMP;
|
||||
chan->channel = -1;
|
||||
chan->scan_index = cnt;
|
||||
chan->scan_type.sign = 's';
|
||||
chan->scan_type.realbits = 64;
|
||||
chan->scan_type.storagebits = 64;
|
||||
}
|
||||
|
||||
static void ad7280_init_dev_channels(struct ad7280_state *st, int dev, int *cnt)
|
||||
{
|
||||
int addr, ch, i;
|
||||
struct iio_chan_spec *chan;
|
||||
|
||||
for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_AUX_ADC_6; ch++) {
|
||||
chan = &st->channels[*cnt];
|
||||
|
||||
if (ch < AD7280A_AUX_ADC_1) {
|
||||
i = AD7280A_CALC_VOLTAGE_CHAN_NUM(dev, ch);
|
||||
ad7280_voltage_channel_init(chan, i);
|
||||
} else {
|
||||
i = AD7280A_CALC_TEMP_CHAN_NUM(dev, ch);
|
||||
ad7280_temp_channel_init(chan, i);
|
||||
}
|
||||
|
||||
addr = ad7280a_devaddr(dev) << 8 | ch;
|
||||
ad7280_common_fields_init(chan, addr, *cnt);
|
||||
|
||||
(*cnt)++;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad7280_channel_init(struct ad7280_state *st)
|
||||
{
|
||||
int dev, ch, cnt;
|
||||
int dev, cnt = 0;
|
||||
|
||||
st->channels = devm_kcalloc(&st->spi->dev, (st->slave_num + 1) * 12 + 2,
|
||||
sizeof(*st->channels), GFP_KERNEL);
|
||||
if (!st->channels)
|
||||
return -ENOMEM;
|
||||
|
||||
for (dev = 0, cnt = 0; dev <= st->slave_num; dev++)
|
||||
for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_AUX_ADC_6;
|
||||
ch++, cnt++) {
|
||||
if (ch < AD7280A_AUX_ADC_1) {
|
||||
st->channels[cnt].type = IIO_VOLTAGE;
|
||||
st->channels[cnt].differential = 1;
|
||||
st->channels[cnt].channel = (dev * 6) + ch;
|
||||
st->channels[cnt].channel2 =
|
||||
st->channels[cnt].channel + 1;
|
||||
} else {
|
||||
st->channels[cnt].type = IIO_TEMP;
|
||||
st->channels[cnt].channel = (dev * 6) + ch - 6;
|
||||
}
|
||||
st->channels[cnt].indexed = 1;
|
||||
st->channels[cnt].info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW);
|
||||
st->channels[cnt].info_mask_shared_by_type =
|
||||
BIT(IIO_CHAN_INFO_SCALE);
|
||||
st->channels[cnt].address =
|
||||
ad7280a_devaddr(dev) << 8 | ch;
|
||||
st->channels[cnt].scan_index = cnt;
|
||||
st->channels[cnt].scan_type.sign = 'u';
|
||||
st->channels[cnt].scan_type.realbits = 12;
|
||||
st->channels[cnt].scan_type.storagebits = 32;
|
||||
st->channels[cnt].scan_type.shift = 0;
|
||||
}
|
||||
for (dev = 0; dev <= st->slave_num; dev++)
|
||||
ad7280_init_dev_channels(st, dev, &cnt);
|
||||
|
||||
st->channels[cnt].type = IIO_VOLTAGE;
|
||||
st->channels[cnt].differential = 1;
|
||||
st->channels[cnt].channel = 0;
|
||||
st->channels[cnt].channel2 = dev * 6;
|
||||
st->channels[cnt].address = AD7280A_ALL_CELLS;
|
||||
st->channels[cnt].indexed = 1;
|
||||
st->channels[cnt].info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
st->channels[cnt].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
|
||||
st->channels[cnt].scan_index = cnt;
|
||||
st->channels[cnt].scan_type.sign = 'u';
|
||||
st->channels[cnt].scan_type.realbits = 32;
|
||||
st->channels[cnt].scan_type.storagebits = 32;
|
||||
st->channels[cnt].scan_type.shift = 0;
|
||||
ad7280_total_voltage_channel_init(&st->channels[cnt], cnt, dev);
|
||||
cnt++;
|
||||
st->channels[cnt].type = IIO_TIMESTAMP;
|
||||
st->channels[cnt].channel = -1;
|
||||
st->channels[cnt].scan_index = cnt;
|
||||
st->channels[cnt].scan_type.sign = 's';
|
||||
st->channels[cnt].scan_type.realbits = 64;
|
||||
st->channels[cnt].scan_type.storagebits = 64;
|
||||
st->channels[cnt].scan_type.shift = 0;
|
||||
ad7280_timestamp_channel_init(&st->channels[cnt], cnt);
|
||||
|
||||
return cnt + 1;
|
||||
}
|
||||
|
||||
static int ad7280_balance_switch_attr_init(struct iio_dev_attr *attr,
|
||||
struct device *dev, int addr, int i)
|
||||
{
|
||||
attr->address = addr;
|
||||
attr->dev_attr.attr.mode = 0644;
|
||||
attr->dev_attr.show = ad7280_show_balance_sw;
|
||||
attr->dev_attr.store = ad7280_store_balance_sw;
|
||||
attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
|
||||
"in%d-in%d_balance_switch_en",
|
||||
i, i + 1);
|
||||
if (!attr->dev_attr.attr.name)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7280_balance_timer_attr_init(struct iio_dev_attr *attr,
|
||||
struct device *dev, int addr, int i)
|
||||
{
|
||||
attr->address = addr;
|
||||
attr->dev_attr.attr.mode = 0644;
|
||||
attr->dev_attr.show = ad7280_show_balance_timer;
|
||||
attr->dev_attr.store = ad7280_store_balance_timer;
|
||||
attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL,
|
||||
"in%d-in%d_balance_timer",
|
||||
i, i + 1);
|
||||
if (!attr->dev_attr.attr.name)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7280_init_dev_attrs(struct ad7280_state *st, int dev, int *cnt)
|
||||
{
|
||||
int addr, ch, i, ret;
|
||||
struct iio_dev_attr *iio_attr;
|
||||
struct device *sdev = &st->spi->dev;
|
||||
|
||||
for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_CELL_VOLTAGE_6; ch++) {
|
||||
iio_attr = &st->iio_attr[*cnt];
|
||||
addr = ad7280a_devaddr(dev) << 8 | ch;
|
||||
i = dev * AD7280A_CELLS_PER_DEV + ch;
|
||||
|
||||
ret = ad7280_balance_switch_attr_init(iio_attr, sdev, addr, i);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ad7280_attributes[*cnt] = &iio_attr->dev_attr.attr;
|
||||
|
||||
(*cnt)++;
|
||||
iio_attr = &st->iio_attr[*cnt];
|
||||
addr = ad7280a_devaddr(dev) << 8 | (AD7280A_CB1_TIMER + ch);
|
||||
|
||||
ret = ad7280_balance_timer_attr_init(iio_attr, sdev, addr, i);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ad7280_attributes[*cnt] = &iio_attr->dev_attr.attr;
|
||||
(*cnt)++;
|
||||
}
|
||||
|
||||
ad7280_attributes[*cnt] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7280_attr_init(struct ad7280_state *st)
|
||||
{
|
||||
int dev, ch, cnt;
|
||||
unsigned int index;
|
||||
struct iio_dev_attr *iio_attr;
|
||||
int dev, cnt = 0, ret;
|
||||
|
||||
st->iio_attr = devm_kcalloc(&st->spi->dev, 2, sizeof(*st->iio_attr) *
|
||||
(st->slave_num + 1) * AD7280A_CELLS_PER_DEV,
|
||||
@ -569,41 +672,11 @@ static int ad7280_attr_init(struct ad7280_state *st)
|
||||
if (!st->iio_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
for (dev = 0, cnt = 0; dev <= st->slave_num; dev++)
|
||||
for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_CELL_VOLTAGE_6;
|
||||
ch++, cnt++) {
|
||||
iio_attr = &st->iio_attr[cnt];
|
||||
index = dev * AD7280A_CELLS_PER_DEV + ch;
|
||||
iio_attr->address = ad7280a_devaddr(dev) << 8 | ch;
|
||||
iio_attr->dev_attr.attr.mode = 0644;
|
||||
iio_attr->dev_attr.show = ad7280_show_balance_sw;
|
||||
iio_attr->dev_attr.store = ad7280_store_balance_sw;
|
||||
iio_attr->dev_attr.attr.name =
|
||||
devm_kasprintf(&st->spi->dev, GFP_KERNEL,
|
||||
"in%d-in%d_balance_switch_en",
|
||||
index, index + 1);
|
||||
if (!iio_attr->dev_attr.attr.name)
|
||||
return -ENOMEM;
|
||||
|
||||
ad7280_attributes[cnt] = &iio_attr->dev_attr.attr;
|
||||
cnt++;
|
||||
iio_attr = &st->iio_attr[cnt];
|
||||
iio_attr->address = ad7280a_devaddr(dev) << 8 |
|
||||
(AD7280A_CB1_TIMER + ch);
|
||||
iio_attr->dev_attr.attr.mode = 0644;
|
||||
iio_attr->dev_attr.show = ad7280_show_balance_timer;
|
||||
iio_attr->dev_attr.store = ad7280_store_balance_timer;
|
||||
iio_attr->dev_attr.attr.name =
|
||||
devm_kasprintf(&st->spi->dev, GFP_KERNEL,
|
||||
"in%d-in%d_balance_timer",
|
||||
index, index + 1);
|
||||
if (!iio_attr->dev_attr.attr.name)
|
||||
return -ENOMEM;
|
||||
|
||||
ad7280_attributes[cnt] = &iio_attr->dev_attr.attr;
|
||||
}
|
||||
|
||||
ad7280_attributes[cnt] = NULL;
|
||||
for (dev = 0; dev <= st->slave_num; dev++) {
|
||||
ret = ad7280_init_dev_attrs(st, dev, &cnt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ enum ad7816_type {
|
||||
static int ad7816_spi_read(struct ad7816_chip_info *chip, u16 *data)
|
||||
{
|
||||
struct spi_device *spi_dev = chip->spi_dev;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
__be16 buf;
|
||||
|
||||
gpiod_set_value(chip->rdwr_pin, 1);
|
||||
@ -106,7 +106,7 @@ static int ad7816_spi_read(struct ad7816_chip_info *chip, u16 *data)
|
||||
static int ad7816_spi_write(struct ad7816_chip_info *chip, u8 data)
|
||||
{
|
||||
struct spi_device *spi_dev = chip->spi_dev;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
gpiod_set_value(chip->rdwr_pin, 1);
|
||||
gpiod_set_value(chip->rdwr_pin, 0);
|
||||
@ -354,8 +354,7 @@ static int ad7816_probe(struct spi_device *spi_dev)
|
||||
{
|
||||
struct ad7816_chip_info *chip;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret = 0;
|
||||
int i;
|
||||
int i, ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi_dev->dev, sizeof(*chip));
|
||||
if (!indio_dev)
|
||||
|
@ -43,7 +43,7 @@ static int adt7316_i2c_read(void *client, u8 reg, u8 *data)
|
||||
static int adt7316_i2c_write(void *client, u8 reg, u8 data)
|
||||
{
|
||||
struct i2c_client *cl = client;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(cl, reg, data);
|
||||
if (ret < 0)
|
||||
@ -55,7 +55,7 @@ static int adt7316_i2c_write(void *client, u8 reg, u8 data)
|
||||
static int adt7316_i2c_multi_read(void *client, u8 reg, u8 count, u8 *data)
|
||||
{
|
||||
struct i2c_client *cl = client;
|
||||
int i, ret = 0;
|
||||
int i, ret;
|
||||
|
||||
if (count > ADT7316_REG_MAX_ADDR)
|
||||
count = ADT7316_REG_MAX_ADDR;
|
||||
@ -74,7 +74,7 @@ static int adt7316_i2c_multi_read(void *client, u8 reg, u8 count, u8 *data)
|
||||
static int adt7316_i2c_multi_write(void *client, u8 reg, u8 count, u8 *data)
|
||||
{
|
||||
struct i2c_client *cl = client;
|
||||
int i, ret = 0;
|
||||
int i, ret;
|
||||
|
||||
if (count > ADT7316_REG_MAX_ADDR)
|
||||
count = ADT7316_REG_MAX_ADDR;
|
||||
|
@ -27,7 +27,7 @@ static int adt7316_spi_multi_read(void *client, u8 reg, u8 count, u8 *data)
|
||||
{
|
||||
struct spi_device *spi_dev = client;
|
||||
u8 cmd[2];
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
if (count > ADT7316_REG_MAX_ADDR)
|
||||
count = ADT7316_REG_MAX_ADDR;
|
||||
@ -56,7 +56,7 @@ static int adt7316_spi_multi_write(void *client, u8 reg, u8 count, u8 *data)
|
||||
{
|
||||
struct spi_device *spi_dev = client;
|
||||
u8 buf[ADT7316_REG_MAX_ADDR + 2];
|
||||
int i, ret = 0;
|
||||
int i, ret;
|
||||
|
||||
if (count > ADT7316_REG_MAX_ADDR)
|
||||
count = ADT7316_REG_MAX_ADDR;
|
||||
|
@ -47,6 +47,8 @@
|
||||
#define ADT7516_MSB_AIN3 0xA
|
||||
#define ADT7516_MSB_AIN4 0xB
|
||||
#define ADT7316_DA_DATA_BASE 0x10
|
||||
#define ADT7316_DA_10_BIT_LSB_SHIFT 6
|
||||
#define ADT7316_DA_12_BIT_LSB_SHIFT 4
|
||||
#define ADT7316_DA_MSB_DATA_REGS 4
|
||||
#define ADT7316_LSB_DAC_A 0x10
|
||||
#define ADT7316_MSB_DAC_A 0x11
|
||||
@ -59,8 +61,8 @@
|
||||
#define ADT7316_CONFIG1 0x18
|
||||
#define ADT7316_CONFIG2 0x19
|
||||
#define ADT7316_CONFIG3 0x1A
|
||||
#define ADT7316_LDAC_CONFIG 0x1B
|
||||
#define ADT7316_DAC_CONFIG 0x1C
|
||||
#define ADT7316_DAC_CONFIG 0x1B
|
||||
#define ADT7316_LDAC_CONFIG 0x1C
|
||||
#define ADT7316_INT_MASK1 0x1D
|
||||
#define ADT7316_INT_MASK2 0x1E
|
||||
#define ADT7316_IN_TEMP_OFFSET 0x1F
|
||||
@ -117,7 +119,7 @@
|
||||
*/
|
||||
#define ADT7316_ADCLK_22_5 0x1
|
||||
#define ADT7316_DA_HIGH_RESOLUTION 0x2
|
||||
#define ADT7316_DA_EN_VIA_DAC_LDCA 0x4
|
||||
#define ADT7316_DA_EN_VIA_DAC_LDAC 0x8
|
||||
#define ADT7516_AIN_IN_VREF 0x10
|
||||
#define ADT7316_EN_IN_TEMP_PROP_DACA 0x20
|
||||
#define ADT7316_EN_EX_TEMP_PROP_DACB 0x40
|
||||
@ -127,6 +129,7 @@
|
||||
*/
|
||||
#define ADT7316_DA_2VREF_CH_MASK 0xF
|
||||
#define ADT7316_DA_EN_MODE_MASK 0x30
|
||||
#define ADT7316_DA_EN_MODE_SHIFT 4
|
||||
#define ADT7316_DA_EN_MODE_SINGLE 0x00
|
||||
#define ADT7316_DA_EN_MODE_AB_CD 0x10
|
||||
#define ADT7316_DA_EN_MODE_ABCD 0x20
|
||||
@ -632,9 +635,7 @@ static ssize_t adt7316_show_da_high_resolution(struct device *dev,
|
||||
struct adt7316_chip_info *chip = iio_priv(dev_info);
|
||||
|
||||
if (chip->config3 & ADT7316_DA_HIGH_RESOLUTION) {
|
||||
if (chip->id == ID_ADT7316 || chip->id == ID_ADT7516)
|
||||
return sprintf(buf, "1 (12 bits)\n");
|
||||
if (chip->id == ID_ADT7317 || chip->id == ID_ADT7517)
|
||||
if (chip->id != ID_ADT7318 && chip->id != ID_ADT7519)
|
||||
return sprintf(buf, "1 (10 bits)\n");
|
||||
}
|
||||
|
||||
@ -651,17 +652,12 @@ static ssize_t adt7316_store_da_high_resolution(struct device *dev,
|
||||
u8 config3;
|
||||
int ret;
|
||||
|
||||
chip->dac_bits = 8;
|
||||
if (chip->id == ID_ADT7318 || chip->id == ID_ADT7519)
|
||||
return -EPERM;
|
||||
|
||||
if (buf[0] == '1') {
|
||||
config3 = chip->config3 | ADT7316_DA_HIGH_RESOLUTION;
|
||||
if (chip->id == ID_ADT7316 || chip->id == ID_ADT7516)
|
||||
chip->dac_bits = 12;
|
||||
else if (chip->id == ID_ADT7317 || chip->id == ID_ADT7517)
|
||||
chip->dac_bits = 10;
|
||||
} else {
|
||||
config3 = chip->config3 & (~ADT7316_DA_HIGH_RESOLUTION);
|
||||
}
|
||||
config3 = chip->config3 & (~ADT7316_DA_HIGH_RESOLUTION);
|
||||
if (buf[0] == '1')
|
||||
config3 |= ADT7316_DA_HIGH_RESOLUTION;
|
||||
|
||||
ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3);
|
||||
if (ret)
|
||||
@ -851,7 +847,7 @@ static ssize_t adt7316_show_DAC_update_mode(struct device *dev,
|
||||
struct iio_dev *dev_info = dev_to_iio_dev(dev);
|
||||
struct adt7316_chip_info *chip = iio_priv(dev_info);
|
||||
|
||||
if (!(chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA))
|
||||
if (!(chip->config3 & ADT7316_DA_EN_VIA_DAC_LDAC))
|
||||
return sprintf(buf, "manual\n");
|
||||
|
||||
switch (chip->dac_config & ADT7316_DA_EN_MODE_MASK) {
|
||||
@ -880,15 +876,15 @@ static ssize_t adt7316_store_DAC_update_mode(struct device *dev,
|
||||
u8 data;
|
||||
int ret;
|
||||
|
||||
if (!(chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA))
|
||||
if (!(chip->config3 & ADT7316_DA_EN_VIA_DAC_LDAC))
|
||||
return -EPERM;
|
||||
|
||||
ret = kstrtou8(buf, 10, &data);
|
||||
if (ret || data > ADT7316_DA_EN_MODE_MASK)
|
||||
if (ret || data > (ADT7316_DA_EN_MODE_MASK >> ADT7316_DA_EN_MODE_SHIFT))
|
||||
return -EINVAL;
|
||||
|
||||
dac_config = chip->dac_config & (~ADT7316_DA_EN_MODE_MASK);
|
||||
dac_config |= data;
|
||||
dac_config |= data << ADT7316_DA_EN_MODE_SHIFT;
|
||||
|
||||
ret = chip->bus.write(chip->bus.client, ADT7316_DAC_CONFIG, dac_config);
|
||||
if (ret)
|
||||
@ -911,7 +907,7 @@ static ssize_t adt7316_show_all_DAC_update_modes(struct device *dev,
|
||||
struct iio_dev *dev_info = dev_to_iio_dev(dev);
|
||||
struct adt7316_chip_info *chip = iio_priv(dev_info);
|
||||
|
||||
if (chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA)
|
||||
if (chip->config3 & ADT7316_DA_EN_VIA_DAC_LDAC)
|
||||
return sprintf(buf, "0 - auto at any MSB DAC writing\n"
|
||||
"1 - auto at MSB DAC AB and CD writing\n"
|
||||
"2 - auto at MSB DAC ABCD writing\n"
|
||||
@ -933,7 +929,7 @@ static ssize_t adt7316_store_update_DAC(struct device *dev,
|
||||
u8 data;
|
||||
int ret;
|
||||
|
||||
if (chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA) {
|
||||
if (chip->config3 & ADT7316_DA_EN_VIA_DAC_LDAC) {
|
||||
if ((chip->dac_config & ADT7316_DA_EN_MODE_MASK) !=
|
||||
ADT7316_DA_EN_MODE_LDAC)
|
||||
return -EPERM;
|
||||
@ -969,9 +965,6 @@ static ssize_t adt7316_show_DA_AB_Vref_bypass(struct device *dev,
|
||||
struct iio_dev *dev_info = dev_to_iio_dev(dev);
|
||||
struct adt7316_chip_info *chip = iio_priv(dev_info);
|
||||
|
||||
if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
|
||||
return -EPERM;
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
!!(chip->dac_config & ADT7316_VREF_BYPASS_DAC_AB));
|
||||
}
|
||||
@ -986,9 +979,6 @@ static ssize_t adt7316_store_DA_AB_Vref_bypass(struct device *dev,
|
||||
u8 dac_config;
|
||||
int ret;
|
||||
|
||||
if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
|
||||
return -EPERM;
|
||||
|
||||
dac_config = chip->dac_config & (~ADT7316_VREF_BYPASS_DAC_AB);
|
||||
if (buf[0] == '1')
|
||||
dac_config |= ADT7316_VREF_BYPASS_DAC_AB;
|
||||
@ -1014,9 +1004,6 @@ static ssize_t adt7316_show_DA_CD_Vref_bypass(struct device *dev,
|
||||
struct iio_dev *dev_info = dev_to_iio_dev(dev);
|
||||
struct adt7316_chip_info *chip = iio_priv(dev_info);
|
||||
|
||||
if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
|
||||
return -EPERM;
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
!!(chip->dac_config & ADT7316_VREF_BYPASS_DAC_CD));
|
||||
}
|
||||
@ -1031,9 +1018,6 @@ static ssize_t adt7316_store_DA_CD_Vref_bypass(struct device *dev,
|
||||
u8 dac_config;
|
||||
int ret;
|
||||
|
||||
if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
|
||||
return -EPERM;
|
||||
|
||||
dac_config = chip->dac_config & (~ADT7316_VREF_BYPASS_DAC_CD);
|
||||
if (buf[0] == '1')
|
||||
dac_config |= ADT7316_VREF_BYPASS_DAC_CD;
|
||||
@ -1061,10 +1045,10 @@ static ssize_t adt7316_show_DAC_internal_Vref(struct device *dev,
|
||||
|
||||
if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
|
||||
return sprintf(buf, "0x%x\n",
|
||||
(chip->dac_config & ADT7516_DAC_IN_VREF_MASK) >>
|
||||
(chip->ldac_config & ADT7516_DAC_IN_VREF_MASK) >>
|
||||
ADT7516_DAC_IN_VREF_OFFSET);
|
||||
return sprintf(buf, "%d\n",
|
||||
!!(chip->dac_config & ADT7316_DAC_IN_VREF));
|
||||
!!(chip->ldac_config & ADT7316_DAC_IN_VREF));
|
||||
}
|
||||
|
||||
static ssize_t adt7316_store_DAC_internal_Vref(struct device *dev,
|
||||
@ -1086,7 +1070,7 @@ static ssize_t adt7316_store_DAC_internal_Vref(struct device *dev,
|
||||
ldac_config = chip->ldac_config & (~ADT7516_DAC_IN_VREF_MASK);
|
||||
if (data & 0x1)
|
||||
ldac_config |= ADT7516_DAC_AB_IN_VREF;
|
||||
else if (data & 0x2)
|
||||
if (data & 0x2)
|
||||
ldac_config |= ADT7516_DAC_CD_IN_VREF;
|
||||
} else {
|
||||
ret = kstrtou8(buf, 16, &data);
|
||||
@ -1410,7 +1394,7 @@ static IIO_DEVICE_ATTR(ex_analog_temp_offset, 0644,
|
||||
static ssize_t adt7316_show_DAC(struct adt7316_chip_info *chip,
|
||||
int channel, char *buf)
|
||||
{
|
||||
u16 data;
|
||||
u16 data = 0;
|
||||
u8 msb, lsb, offset;
|
||||
int ret;
|
||||
|
||||
@ -1435,7 +1419,11 @@ static ssize_t adt7316_show_DAC(struct adt7316_chip_info *chip,
|
||||
if (ret)
|
||||
return -EIO;
|
||||
|
||||
data = (msb << offset) + (lsb & ((1 << offset) - 1));
|
||||
if (chip->dac_bits == 12)
|
||||
data = lsb >> ADT7316_DA_12_BIT_LSB_SHIFT;
|
||||
else if (chip->dac_bits == 10)
|
||||
data = lsb >> ADT7316_DA_10_BIT_LSB_SHIFT;
|
||||
data |= msb << offset;
|
||||
|
||||
return sprintf(buf, "%d\n", data);
|
||||
}
|
||||
@ -1443,7 +1431,7 @@ static ssize_t adt7316_show_DAC(struct adt7316_chip_info *chip,
|
||||
static ssize_t adt7316_store_DAC(struct adt7316_chip_info *chip,
|
||||
int channel, const char *buf, size_t len)
|
||||
{
|
||||
u8 msb, lsb, offset;
|
||||
u8 msb, lsb, lsb_reg, offset;
|
||||
u16 data;
|
||||
int ret;
|
||||
|
||||
@ -1461,9 +1449,13 @@ static ssize_t adt7316_store_DAC(struct adt7316_chip_info *chip,
|
||||
return -EINVAL;
|
||||
|
||||
if (chip->dac_bits > 8) {
|
||||
lsb = data & (1 << offset);
|
||||
lsb = data & ((1 << offset) - 1);
|
||||
if (chip->dac_bits == 12)
|
||||
lsb_reg = lsb << ADT7316_DA_12_BIT_LSB_SHIFT;
|
||||
else
|
||||
lsb_reg = lsb << ADT7316_DA_10_BIT_LSB_SHIFT;
|
||||
ret = chip->bus.write(chip->bus.client,
|
||||
ADT7316_DA_DATA_BASE + channel * 2, lsb);
|
||||
ADT7316_DA_DATA_BASE + channel * 2, lsb_reg);
|
||||
if (ret)
|
||||
return -EIO;
|
||||
}
|
||||
@ -1710,8 +1702,6 @@ static struct attribute *adt7516_attributes[] = {
|
||||
&iio_dev_attr_DAC_update_mode.dev_attr.attr,
|
||||
&iio_dev_attr_all_DAC_update_modes.dev_attr.attr,
|
||||
&iio_dev_attr_update_DAC.dev_attr.attr,
|
||||
&iio_dev_attr_DA_AB_Vref_bypass.dev_attr.attr,
|
||||
&iio_dev_attr_DA_CD_Vref_bypass.dev_attr.attr,
|
||||
&iio_dev_attr_DAC_internal_Vref.dev_attr.attr,
|
||||
&iio_dev_attr_VDD.dev_attr.attr,
|
||||
&iio_dev_attr_in_temp.dev_attr.attr,
|
||||
@ -1809,6 +1799,43 @@ static irqreturn_t adt7316_event_handler(int irq, void *private)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int adt7316_setup_irq(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adt7316_chip_info *chip = iio_priv(indio_dev);
|
||||
int irq_type, ret;
|
||||
|
||||
irq_type = irqd_get_trigger_type(irq_get_irq_data(chip->bus.irq));
|
||||
|
||||
switch (irq_type) {
|
||||
case IRQF_TRIGGER_HIGH:
|
||||
case IRQF_TRIGGER_RISING:
|
||||
break;
|
||||
case IRQF_TRIGGER_LOW:
|
||||
case IRQF_TRIGGER_FALLING:
|
||||
break;
|
||||
default:
|
||||
dev_info(&indio_dev->dev, "mode %d unsupported, using IRQF_TRIGGER_LOW\n",
|
||||
irq_type);
|
||||
irq_type = IRQF_TRIGGER_LOW;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&indio_dev->dev, chip->bus.irq,
|
||||
NULL, adt7316_event_handler,
|
||||
irq_type | IRQF_ONESHOT,
|
||||
indio_dev->name, indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "failed to request irq %d\n",
|
||||
chip->bus.irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (irq_type & IRQF_TRIGGER_HIGH)
|
||||
chip->config1 |= ADT7316_INT_POLARITY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Show mask of enabled interrupts in Hex.
|
||||
*/
|
||||
@ -2103,9 +2130,7 @@ int adt7316_probe(struct device *dev, struct adt7316_bus *bus,
|
||||
{
|
||||
struct adt7316_chip_info *chip;
|
||||
struct iio_dev *indio_dev;
|
||||
unsigned short *adt7316_platform_data = dev->platform_data;
|
||||
int irq_type = IRQF_TRIGGER_LOW;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*chip));
|
||||
if (!indio_dev)
|
||||
@ -2123,6 +2148,13 @@ int adt7316_probe(struct device *dev, struct adt7316_bus *bus,
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
if (chip->id == ID_ADT7316 || chip->id == ID_ADT7516)
|
||||
chip->dac_bits = 12;
|
||||
else if (chip->id == ID_ADT7317 || chip->id == ID_ADT7517)
|
||||
chip->dac_bits = 10;
|
||||
else
|
||||
chip->dac_bits = 8;
|
||||
|
||||
chip->ldac_pin = devm_gpiod_get_optional(dev, "adi,ldac", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(chip->ldac_pin)) {
|
||||
ret = PTR_ERR(chip->ldac_pin);
|
||||
@ -2130,8 +2162,8 @@ int adt7316_probe(struct device *dev, struct adt7316_bus *bus,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (chip->ldac_pin) {
|
||||
chip->config3 |= ADT7316_DA_EN_VIA_DAC_LDCA;
|
||||
if (!chip->ldac_pin) {
|
||||
chip->config3 |= ADT7316_DA_EN_VIA_DAC_LDAC;
|
||||
if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX)
|
||||
chip->config1 |= ADT7516_SEL_AIN3;
|
||||
}
|
||||
@ -2148,20 +2180,9 @@ int adt7316_probe(struct device *dev, struct adt7316_bus *bus,
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
if (chip->bus.irq > 0) {
|
||||
if (adt7316_platform_data[0])
|
||||
irq_type = adt7316_platform_data[0];
|
||||
|
||||
ret = devm_request_threaded_irq(dev, chip->bus.irq,
|
||||
NULL,
|
||||
adt7316_event_handler,
|
||||
irq_type | IRQF_ONESHOT,
|
||||
indio_dev->name,
|
||||
indio_dev);
|
||||
ret = adt7316_setup_irq(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (irq_type & IRQF_TRIGGER_HIGH)
|
||||
chip->config1 |= ADT7316_INT_POLARITY;
|
||||
}
|
||||
|
||||
ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, chip->config1);
|
||||
|
@ -13,16 +13,6 @@ config AD7150
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7150.
|
||||
|
||||
config AD7152
|
||||
tristate "Analog Devices ad7152/3 capacitive sensor driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for Analog Devices capacitive sensors.
|
||||
(ad7152, ad7153) Provides direct access via sysfs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7152.
|
||||
|
||||
config AD7746
|
||||
tristate "Analog Devices AD7745, AD7746 AD7747 capacitive sensor driver"
|
||||
depends on I2C
|
||||
|
@ -3,5 +3,4 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_AD7150) += ad7150.o
|
||||
obj-$(CONFIG_AD7152) += ad7152.o
|
||||
obj-$(CONFIG_AD7746) += ad7746.o
|
||||
|
@ -1,552 +0,0 @@
|
||||
/*
|
||||
* AD7152 capacitive sensor driver supporting AD7152/3
|
||||
*
|
||||
* Copyright 2010-2011a Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
/*
|
||||
* TODO: Check compliance of calibbias with abi (units)
|
||||
*/
|
||||
/*
|
||||
* AD7152 registers definition
|
||||
*/
|
||||
|
||||
#define AD7152_REG_STATUS 0
|
||||
#define AD7152_REG_CH1_DATA_HIGH 1
|
||||
#define AD7152_REG_CH2_DATA_HIGH 3
|
||||
#define AD7152_REG_CH1_OFFS_HIGH 5
|
||||
#define AD7152_REG_CH2_OFFS_HIGH 7
|
||||
#define AD7152_REG_CH1_GAIN_HIGH 9
|
||||
#define AD7152_REG_CH1_SETUP 11
|
||||
#define AD7152_REG_CH2_GAIN_HIGH 12
|
||||
#define AD7152_REG_CH2_SETUP 14
|
||||
#define AD7152_REG_CFG 15
|
||||
#define AD7152_REG_RESEVERD 16
|
||||
#define AD7152_REG_CAPDAC_POS 17
|
||||
#define AD7152_REG_CAPDAC_NEG 18
|
||||
#define AD7152_REG_CFG2 26
|
||||
|
||||
/* Status Register Bit Designations (AD7152_REG_STATUS) */
|
||||
#define AD7152_STATUS_RDY1 BIT(0)
|
||||
#define AD7152_STATUS_RDY2 BIT(1)
|
||||
#define AD7152_STATUS_C1C2 BIT(2)
|
||||
#define AD7152_STATUS_PWDN BIT(7)
|
||||
|
||||
/* Setup Register Bit Designations (AD7152_REG_CHx_SETUP) */
|
||||
#define AD7152_SETUP_CAPDIFF BIT(5)
|
||||
#define AD7152_SETUP_RANGE_2pF (0 << 6)
|
||||
#define AD7152_SETUP_RANGE_0_5pF (1 << 6)
|
||||
#define AD7152_SETUP_RANGE_1pF (2 << 6)
|
||||
#define AD7152_SETUP_RANGE_4pF (3 << 6)
|
||||
#define AD7152_SETUP_RANGE(x) ((x) << 6)
|
||||
|
||||
/* Config Register Bit Designations (AD7152_REG_CFG) */
|
||||
#define AD7152_CONF_CH2EN BIT(3)
|
||||
#define AD7152_CONF_CH1EN BIT(4)
|
||||
#define AD7152_CONF_MODE_IDLE (0 << 0)
|
||||
#define AD7152_CONF_MODE_CONT_CONV (1 << 0)
|
||||
#define AD7152_CONF_MODE_SINGLE_CONV (2 << 0)
|
||||
#define AD7152_CONF_MODE_OFFS_CAL (5 << 0)
|
||||
#define AD7152_CONF_MODE_GAIN_CAL (6 << 0)
|
||||
|
||||
/* Capdac Register Bit Designations (AD7152_REG_CAPDAC_XXX) */
|
||||
#define AD7152_CAPDAC_DACEN BIT(7)
|
||||
#define AD7152_CAPDAC_DACP(x) ((x) & 0x1F)
|
||||
|
||||
/* CFG2 Register Bit Designations (AD7152_REG_CFG2) */
|
||||
#define AD7152_CFG2_OSR(x) (((x) & 0x3) << 4)
|
||||
|
||||
enum {
|
||||
AD7152_DATA,
|
||||
AD7152_OFFS,
|
||||
AD7152_GAIN,
|
||||
AD7152_SETUP
|
||||
};
|
||||
|
||||
/*
|
||||
* struct ad7152_chip_info - chip specific information
|
||||
*/
|
||||
|
||||
struct ad7152_chip_info {
|
||||
struct i2c_client *client;
|
||||
/*
|
||||
* Capacitive channel digital filter setup;
|
||||
* conversion time/update rate setup per channel
|
||||
*/
|
||||
u8 filter_rate_setup;
|
||||
u8 setup[2];
|
||||
struct mutex state_lock; /* protect hardware state */
|
||||
};
|
||||
|
||||
static inline ssize_t ad7152_start_calib(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len,
|
||||
u8 regval)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7152_chip_info *chip = iio_priv(indio_dev);
|
||||
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
|
||||
bool doit;
|
||||
int ret, timeout = 10;
|
||||
|
||||
ret = strtobool(buf, &doit);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!doit)
|
||||
return 0;
|
||||
|
||||
if (this_attr->address == 0)
|
||||
regval |= AD7152_CONF_CH1EN;
|
||||
else
|
||||
regval |= AD7152_CONF_CH2EN;
|
||||
|
||||
mutex_lock(&chip->state_lock);
|
||||
ret = i2c_smbus_write_byte_data(chip->client, AD7152_REG_CFG, regval);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
do {
|
||||
mdelay(20);
|
||||
ret = i2c_smbus_read_byte_data(chip->client, AD7152_REG_CFG);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
} while ((ret == regval) && timeout--);
|
||||
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return len;
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ad7152_start_offset_calib(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
return ad7152_start_calib(dev, attr, buf, len,
|
||||
AD7152_CONF_MODE_OFFS_CAL);
|
||||
}
|
||||
|
||||
static ssize_t ad7152_start_gain_calib(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
return ad7152_start_calib(dev, attr, buf, len,
|
||||
AD7152_CONF_MODE_GAIN_CAL);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration,
|
||||
0200, NULL, ad7152_start_offset_calib, 0);
|
||||
static IIO_DEVICE_ATTR(in_capacitance1_calibbias_calibration,
|
||||
0200, NULL, ad7152_start_offset_calib, 1);
|
||||
static IIO_DEVICE_ATTR(in_capacitance0_calibscale_calibration,
|
||||
0200, NULL, ad7152_start_gain_calib, 0);
|
||||
static IIO_DEVICE_ATTR(in_capacitance1_calibscale_calibration,
|
||||
0200, NULL, ad7152_start_gain_calib, 1);
|
||||
|
||||
/* Values are Update Rate (Hz), Conversion Time (ms) + 1*/
|
||||
static const unsigned char ad7152_filter_rate_table[][2] = {
|
||||
{200, 5 + 1}, {50, 20 + 1}, {20, 50 + 1}, {17, 60 + 1},
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("200 50 20 17");
|
||||
|
||||
static IIO_CONST_ATTR(in_capacitance_scale_available,
|
||||
"0.000061050 0.000030525 0.000015263 0.000007631");
|
||||
|
||||
static struct attribute *ad7152_attributes[] = {
|
||||
&iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr,
|
||||
&iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr,
|
||||
&iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr,
|
||||
&iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr,
|
||||
&iio_const_attr_in_capacitance_scale_available.dev_attr.attr,
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group ad7152_attribute_group = {
|
||||
.attrs = ad7152_attributes,
|
||||
};
|
||||
|
||||
static const u8 ad7152_addresses[][4] = {
|
||||
{ AD7152_REG_CH1_DATA_HIGH, AD7152_REG_CH1_OFFS_HIGH,
|
||||
AD7152_REG_CH1_GAIN_HIGH, AD7152_REG_CH1_SETUP },
|
||||
{ AD7152_REG_CH2_DATA_HIGH, AD7152_REG_CH2_OFFS_HIGH,
|
||||
AD7152_REG_CH2_GAIN_HIGH, AD7152_REG_CH2_SETUP },
|
||||
};
|
||||
|
||||
/* Values are nano relative to pf base. */
|
||||
static const int ad7152_scale_table[] = {
|
||||
30525, 7631, 15263, 61050
|
||||
};
|
||||
|
||||
/**
|
||||
* read_raw handler for IIO_CHAN_INFO_SAMP_FREQ
|
||||
*
|
||||
* lock must be held
|
||||
**/
|
||||
static int ad7152_read_raw_samp_freq(struct device *dev, int *val)
|
||||
{
|
||||
struct ad7152_chip_info *chip = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
*val = ad7152_filter_rate_table[chip->filter_rate_setup][0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* write_raw handler for IIO_CHAN_INFO_SAMP_FREQ
|
||||
*
|
||||
* lock must be held
|
||||
**/
|
||||
static int ad7152_write_raw_samp_freq(struct device *dev, int val)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7152_chip_info *chip = iio_priv(indio_dev);
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ad7152_filter_rate_table); i++)
|
||||
if (val >= ad7152_filter_rate_table[i][0])
|
||||
break;
|
||||
|
||||
if (i >= ARRAY_SIZE(ad7152_filter_rate_table))
|
||||
i = ARRAY_SIZE(ad7152_filter_rate_table) - 1;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(chip->client,
|
||||
AD7152_REG_CFG2, AD7152_CFG2_OSR(i));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
chip->filter_rate_setup = i;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7152_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad7152_chip_info *chip = iio_priv(indio_dev);
|
||||
int ret, i;
|
||||
|
||||
mutex_lock(&chip->state_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (val != 1) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
val = (val2 * 1024) / 15625;
|
||||
|
||||
ret = i2c_smbus_write_word_data(chip->client,
|
||||
ad7152_addresses[chan->channel][AD7152_GAIN],
|
||||
swab16(val));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if ((val < 0) | (val > 0xFFFF)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ret = i2c_smbus_write_word_data(chip->client,
|
||||
ad7152_addresses[chan->channel][AD7152_OFFS],
|
||||
swab16(val));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (val) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(ad7152_scale_table); i++)
|
||||
if (val2 == ad7152_scale_table[i])
|
||||
break;
|
||||
|
||||
chip->setup[chan->channel] &= ~AD7152_SETUP_RANGE_4pF;
|
||||
chip->setup[chan->channel] |= AD7152_SETUP_RANGE(i);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(chip->client,
|
||||
ad7152_addresses[chan->channel][AD7152_SETUP],
|
||||
chip->setup[chan->channel]);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (val2) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ret = ad7152_write_raw_samp_freq(&indio_dev->dev, val);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7152_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad7152_chip_info *chip = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u8 regval = 0;
|
||||
|
||||
mutex_lock(&chip->state_lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
/* First set whether in differential mode */
|
||||
|
||||
regval = chip->setup[chan->channel];
|
||||
|
||||
if (chan->differential)
|
||||
chip->setup[chan->channel] |= AD7152_SETUP_CAPDIFF;
|
||||
else
|
||||
chip->setup[chan->channel] &= ~AD7152_SETUP_CAPDIFF;
|
||||
|
||||
if (regval != chip->setup[chan->channel]) {
|
||||
ret = i2c_smbus_write_byte_data(chip->client,
|
||||
ad7152_addresses[chan->channel][AD7152_SETUP],
|
||||
chip->setup[chan->channel]);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
/* Make sure the channel is enabled */
|
||||
if (chan->channel == 0)
|
||||
regval = AD7152_CONF_CH1EN;
|
||||
else
|
||||
regval = AD7152_CONF_CH2EN;
|
||||
|
||||
/* Trigger a single read */
|
||||
regval |= AD7152_CONF_MODE_SINGLE_CONV;
|
||||
ret = i2c_smbus_write_byte_data(chip->client, AD7152_REG_CFG,
|
||||
regval);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
msleep(ad7152_filter_rate_table[chip->filter_rate_setup][1]);
|
||||
/* Now read the actual register */
|
||||
ret = i2c_smbus_read_word_data(chip->client,
|
||||
ad7152_addresses[chan->channel][AD7152_DATA]);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
*val = swab16(ret);
|
||||
|
||||
if (chan->differential)
|
||||
*val -= 0x8000;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
|
||||
ret = i2c_smbus_read_word_data(chip->client,
|
||||
ad7152_addresses[chan->channel][AD7152_GAIN]);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
/* 1 + gain_val / 2^16 */
|
||||
*val = 1;
|
||||
*val2 = (15625 * swab16(ret)) / 1024;
|
||||
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
ret = i2c_smbus_read_word_data(chip->client,
|
||||
ad7152_addresses[chan->channel][AD7152_OFFS]);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
*val = swab16(ret);
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = i2c_smbus_read_byte_data(chip->client,
|
||||
ad7152_addresses[chan->channel][AD7152_SETUP]);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
*val = 0;
|
||||
*val2 = ad7152_scale_table[ret >> 6];
|
||||
|
||||
ret = IIO_VAL_INT_PLUS_NANO;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = ad7152_read_raw_samp_freq(&indio_dev->dev, val);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7152_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
default:
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info ad7152_info = {
|
||||
.attrs = &ad7152_attribute_group,
|
||||
.read_raw = ad7152_read_raw,
|
||||
.write_raw = ad7152_write_raw,
|
||||
.write_raw_get_fmt = ad7152_write_raw_get_fmt,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ad7152_channels[] = {
|
||||
{
|
||||
.type = IIO_CAPACITANCE,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
}, {
|
||||
.type = IIO_CAPACITANCE,
|
||||
.differential = 1,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.channel2 = 2,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
}, {
|
||||
.type = IIO_CAPACITANCE,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
}, {
|
||||
.type = IIO_CAPACITANCE,
|
||||
.differential = 1,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.channel2 = 3,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* device probe and remove
|
||||
*/
|
||||
|
||||
static int ad7152_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ad7152_chip_info *chip;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
chip = iio_priv(indio_dev);
|
||||
/* this is only used for device removal purposes */
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
chip->client = client;
|
||||
mutex_init(&chip->state_lock);
|
||||
|
||||
/* Establish that the iio_dev is a child of the i2c device */
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &ad7152_info;
|
||||
indio_dev->channels = ad7152_channels;
|
||||
if (id->driver_data == 0)
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad7152_channels);
|
||||
else
|
||||
indio_dev->num_channels = 2;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad7152_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_err(&client->dev, "%s capacitive sensor registered\n", id->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad7152_id[] = {
|
||||
{ "ad7152", 0 },
|
||||
{ "ad7153", 1 },
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, ad7152_id);
|
||||
|
||||
static struct i2c_driver ad7152_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
},
|
||||
.probe = ad7152_probe,
|
||||
.id_table = ad7152_id,
|
||||
};
|
||||
module_i2c_driver(ad7152_driver);
|
||||
|
||||
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7152/3 capacitive sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -6,6 +6,7 @@
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/device.h>
|
||||
@ -71,7 +72,7 @@
|
||||
struct ad9834_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
unsigned int mclk;
|
||||
struct clk *mclk;
|
||||
unsigned short control;
|
||||
unsigned short devid;
|
||||
struct spi_transfer xfer;
|
||||
@ -110,12 +111,15 @@ static unsigned int ad9834_calc_freqreg(unsigned long mclk, unsigned long fout)
|
||||
static int ad9834_write_frequency(struct ad9834_state *st,
|
||||
unsigned long addr, unsigned long fout)
|
||||
{
|
||||
unsigned long clk_freq;
|
||||
unsigned long regval;
|
||||
|
||||
if (fout > (st->mclk / 2))
|
||||
clk_freq = clk_get_rate(st->mclk);
|
||||
|
||||
if (fout > (clk_freq / 2))
|
||||
return -EINVAL;
|
||||
|
||||
regval = ad9834_calc_freqreg(st->mclk, fout);
|
||||
regval = ad9834_calc_freqreg(clk_freq, fout);
|
||||
|
||||
st->freq_data[0] = cpu_to_be16(addr | (regval &
|
||||
RES_MASK(AD9834_FREQ_BITS / 2)));
|
||||
@ -389,16 +393,11 @@ static const struct iio_info ad9833_info = {
|
||||
|
||||
static int ad9834_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad9834_platform_data *pdata = dev_get_platdata(&spi->dev);
|
||||
struct ad9834_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
struct regulator *reg;
|
||||
int ret;
|
||||
|
||||
if (!pdata) {
|
||||
dev_dbg(&spi->dev, "no platform data?\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
reg = devm_regulator_get(&spi->dev, "avdd");
|
||||
if (IS_ERR(reg))
|
||||
@ -418,7 +417,14 @@ static int ad9834_probe(struct spi_device *spi)
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st = iio_priv(indio_dev);
|
||||
mutex_init(&st->lock);
|
||||
st->mclk = pdata->mclk;
|
||||
st->mclk = devm_clk_get(&spi->dev, NULL);
|
||||
|
||||
ret = clk_prepare_enable(st->mclk);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable master clock\n");
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
st->spi = spi;
|
||||
st->devid = spi_get_device_id(spi)->driver_data;
|
||||
st->reg = reg;
|
||||
@ -454,42 +460,41 @@ static int ad9834_probe(struct spi_device *spi)
|
||||
spi_message_add_tail(&st->freq_xfer[1], &st->freq_msg);
|
||||
|
||||
st->control = AD9834_B28 | AD9834_RESET;
|
||||
st->control |= AD9834_DIV2;
|
||||
|
||||
if (!pdata->en_div2)
|
||||
st->control |= AD9834_DIV2;
|
||||
|
||||
if (!pdata->en_signbit_msb_out && (st->devid == ID_AD9834))
|
||||
if (st->devid == ID_AD9834)
|
||||
st->control |= AD9834_SIGN_PIB;
|
||||
|
||||
st->data = cpu_to_be16(AD9834_REG_CMD | st->control);
|
||||
ret = spi_sync(st->spi, &st->msg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "device init failed\n");
|
||||
goto error_disable_reg;
|
||||
goto error_clock_unprepare;
|
||||
}
|
||||
|
||||
ret = ad9834_write_frequency(st, AD9834_REG_FREQ0, pdata->freq0);
|
||||
ret = ad9834_write_frequency(st, AD9834_REG_FREQ0, 1000000);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
goto error_clock_unprepare;
|
||||
|
||||
ret = ad9834_write_frequency(st, AD9834_REG_FREQ1, pdata->freq1);
|
||||
ret = ad9834_write_frequency(st, AD9834_REG_FREQ1, 5000000);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
goto error_clock_unprepare;
|
||||
|
||||
ret = ad9834_write_phase(st, AD9834_REG_PHASE0, pdata->phase0);
|
||||
ret = ad9834_write_phase(st, AD9834_REG_PHASE0, 512);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
goto error_clock_unprepare;
|
||||
|
||||
ret = ad9834_write_phase(st, AD9834_REG_PHASE1, pdata->phase1);
|
||||
ret = ad9834_write_phase(st, AD9834_REG_PHASE1, 1024);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
goto error_clock_unprepare;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
goto error_clock_unprepare;
|
||||
|
||||
return 0;
|
||||
|
||||
error_clock_unprepare:
|
||||
clk_disable_unprepare(st->mclk);
|
||||
error_disable_reg:
|
||||
regulator_disable(reg);
|
||||
|
||||
@ -502,6 +507,7 @@ static int ad9834_remove(struct spi_device *spi)
|
||||
struct ad9834_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
clk_disable_unprepare(st->mclk);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
|
@ -8,32 +8,4 @@
|
||||
#ifndef IIO_DDS_AD9834_H_
|
||||
#define IIO_DDS_AD9834_H_
|
||||
|
||||
/*
|
||||
* TODO: struct ad7887_platform_data needs to go into include/linux/iio
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct ad9834_platform_data - platform specific information
|
||||
* @mclk: master clock in Hz
|
||||
* @freq0: power up freq0 tuning word in Hz
|
||||
* @freq1: power up freq1 tuning word in Hz
|
||||
* @phase0: power up phase0 value [0..4095] correlates with 0..2PI
|
||||
* @phase1: power up phase1 value [0..4095] correlates with 0..2PI
|
||||
* @en_div2: digital output/2 is passed to the SIGN BIT OUT pin
|
||||
* @en_signbit_msb_out: the MSB (or MSB/2) of the DAC data is connected to the
|
||||
* SIGN BIT OUT pin. en_div2 controls whether it is the MSB
|
||||
* or MSB/2 that is output. if en_signbit_msb_out=false,
|
||||
* the on-board comparator is connected to SIGN BIT OUT
|
||||
*/
|
||||
|
||||
struct ad9834_platform_data {
|
||||
unsigned int mclk;
|
||||
unsigned int freq0;
|
||||
unsigned int freq1;
|
||||
unsigned short phase0;
|
||||
unsigned short phase1;
|
||||
bool en_div2;
|
||||
bool en_signbit_msb_out;
|
||||
};
|
||||
|
||||
#endif /* IIO_DDS_AD9834_H_ */
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@ -82,21 +83,10 @@
|
||||
#define AD5933_POLL_TIME_ms 10
|
||||
#define AD5933_INIT_EXCITATION_TIME_ms 100
|
||||
|
||||
/**
|
||||
* struct ad5933_platform_data - platform specific data
|
||||
* @ext_clk_hz: the external clock frequency in Hz, if not set
|
||||
* the driver uses the internal clock (16.776 MHz)
|
||||
* @vref_mv: the external reference voltage in millivolt
|
||||
*/
|
||||
|
||||
struct ad5933_platform_data {
|
||||
unsigned long ext_clk_hz;
|
||||
unsigned short vref_mv;
|
||||
};
|
||||
|
||||
struct ad5933_state {
|
||||
struct i2c_client *client;
|
||||
struct regulator *reg;
|
||||
struct clk *mclk;
|
||||
struct delayed_work work;
|
||||
struct mutex lock; /* Protect sensor state */
|
||||
unsigned long mclk_hz;
|
||||
@ -112,10 +102,6 @@ struct ad5933_state {
|
||||
unsigned int poll_time_jiffies;
|
||||
};
|
||||
|
||||
static struct ad5933_platform_data ad5933_default_pdata = {
|
||||
.vref_mv = 3300,
|
||||
};
|
||||
|
||||
#define AD5933_CHANNEL(_type, _extend_name, _info_mask_separate, _address, \
|
||||
_scan_index, _realbits) { \
|
||||
.type = (_type), \
|
||||
@ -691,10 +677,10 @@ static void ad5933_work(struct work_struct *work)
|
||||
static int ad5933_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret, voltage_uv = 0;
|
||||
struct ad5933_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
int ret;
|
||||
struct ad5933_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
unsigned long ext_clk_hz = 0;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
@ -706,9 +692,6 @@ static int ad5933_probe(struct i2c_client *client,
|
||||
|
||||
mutex_init(&st->lock);
|
||||
|
||||
if (!pdata)
|
||||
pdata = &ad5933_default_pdata;
|
||||
|
||||
st->reg = devm_regulator_get(&client->dev, "vdd");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
@ -718,15 +701,28 @@ static int ad5933_probe(struct i2c_client *client,
|
||||
dev_err(&client->dev, "Failed to enable specified VDD supply\n");
|
||||
return ret;
|
||||
}
|
||||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
ret = regulator_get_voltage(st->reg);
|
||||
|
||||
if (voltage_uv)
|
||||
st->vref_mv = voltage_uv / 1000;
|
||||
else
|
||||
st->vref_mv = pdata->vref_mv;
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
if (pdata->ext_clk_hz) {
|
||||
st->mclk_hz = pdata->ext_clk_hz;
|
||||
st->vref_mv = ret / 1000;
|
||||
|
||||
st->mclk = devm_clk_get(&client->dev, "mclk");
|
||||
if (IS_ERR(st->mclk) && PTR_ERR(st->mclk) != -ENOENT) {
|
||||
ret = PTR_ERR(st->mclk);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
if (!IS_ERR(st->mclk)) {
|
||||
ret = clk_prepare_enable(st->mclk);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
ext_clk_hz = clk_get_rate(st->mclk);
|
||||
}
|
||||
|
||||
if (ext_clk_hz) {
|
||||
st->mclk_hz = ext_clk_hz;
|
||||
st->ctrl_lb = AD5933_CTRL_EXT_SYSCLK;
|
||||
} else {
|
||||
st->mclk_hz = AD5933_INT_OSC_FREQ_Hz;
|
||||
@ -746,7 +742,7 @@ static int ad5933_probe(struct i2c_client *client,
|
||||
|
||||
ret = ad5933_register_ring_funcs_and_init(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
goto error_disable_mclk;
|
||||
|
||||
ret = ad5933_setup(st);
|
||||
if (ret)
|
||||
@ -760,6 +756,8 @@ static int ad5933_probe(struct i2c_client *client,
|
||||
|
||||
error_unreg_ring:
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
error_disable_mclk:
|
||||
clk_disable_unprepare(st->mclk);
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
|
||||
@ -774,6 +772,7 @@ static int ad5933_remove(struct i2c_client *client)
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
regulator_disable(st->reg);
|
||||
clk_disable_unprepare(st->mclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -260,6 +260,7 @@ struct st_sensor_settings {
|
||||
struct st_sensor_data {
|
||||
struct device *dev;
|
||||
struct iio_trigger *trig;
|
||||
struct iio_mount_matrix *mount_matrix;
|
||||
struct st_sensor_settings *sensor_settings;
|
||||
struct st_sensor_fullscale_avl *current_fullscale;
|
||||
struct regulator *vdd;
|
||||
|
@ -46,6 +46,7 @@ enum iio_chan_type {
|
||||
IIO_GRAVITY,
|
||||
IIO_POSITIONRELATIVE,
|
||||
IIO_PHASE,
|
||||
IIO_MASSCONCENTRATION,
|
||||
};
|
||||
|
||||
enum iio_modifier {
|
||||
@ -87,6 +88,12 @@ enum iio_modifier {
|
||||
IIO_MOD_VOC,
|
||||
IIO_MOD_LIGHT_UV,
|
||||
IIO_MOD_LIGHT_DUV,
|
||||
IIO_MOD_PM1,
|
||||
IIO_MOD_PM2P5,
|
||||
IIO_MOD_PM4,
|
||||
IIO_MOD_PM10,
|
||||
IIO_MOD_ETHANOL,
|
||||
IIO_MOD_H2,
|
||||
};
|
||||
|
||||
enum iio_event_type {
|
||||
|
@ -60,6 +60,7 @@ static const char * const iio_chan_type_name_spec[] = {
|
||||
[IIO_GRAVITY] = "gravity",
|
||||
[IIO_POSITIONRELATIVE] = "positionrelative",
|
||||
[IIO_PHASE] = "phase",
|
||||
[IIO_MASSCONCENTRATION] = "massconcentration",
|
||||
};
|
||||
|
||||
static const char * const iio_ev_type_text[] = {
|
||||
@ -114,7 +115,13 @@ static const char * const iio_modifier_names[] = {
|
||||
[IIO_MOD_I] = "i",
|
||||
[IIO_MOD_Q] = "q",
|
||||
[IIO_MOD_CO2] = "co2",
|
||||
[IIO_MOD_ETHANOL] = "ethanol",
|
||||
[IIO_MOD_H2] = "h2",
|
||||
[IIO_MOD_VOC] = "voc",
|
||||
[IIO_MOD_PM1] = "pm1",
|
||||
[IIO_MOD_PM2P5] = "pm2p5",
|
||||
[IIO_MOD_PM4] = "pm4",
|
||||
[IIO_MOD_PM10] = "pm10",
|
||||
};
|
||||
|
||||
static bool event_is_known(struct iio_event_data *event)
|
||||
@ -156,6 +163,7 @@ static bool event_is_known(struct iio_event_data *event)
|
||||
case IIO_GRAVITY:
|
||||
case IIO_POSITIONRELATIVE:
|
||||
case IIO_PHASE:
|
||||
case IIO_MASSCONCENTRATION:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
@ -199,7 +207,13 @@ static bool event_is_known(struct iio_event_data *event)
|
||||
case IIO_MOD_I:
|
||||
case IIO_MOD_Q:
|
||||
case IIO_MOD_CO2:
|
||||
case IIO_MOD_ETHANOL:
|
||||
case IIO_MOD_H2:
|
||||
case IIO_MOD_VOC:
|
||||
case IIO_MOD_PM1:
|
||||
case IIO_MOD_PM2P5:
|
||||
case IIO_MOD_PM4:
|
||||
case IIO_MOD_PM10:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
|
Loading…
Reference in New Issue
Block a user