mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
Second set of new device support, features and cleanup for IIO in the 5.1 cycle.
There are a few late breaking fixes in here that weren't worth trying to rush into 5.0 as they have been with us for quite a while. New device support * ad7476 - add support for TI ADS786X parts that are compatible with this Analog Devices driver. Good to see some simple devices are so similar. * Ingenic jz47xx SoC ADCs - new driver and bindings * Plantower PMS7003 partical sensor - new driver and bindings including vendor prefix. * TI DAC7612 - new driver and bindings for this dual DAC. New features * ad7768-1 - Sampling frequency control * bmi160 - Data ready trigger support, including open-drain dt binding. Cleanup / minor fixes. * Analog Device DACs - Fix some inconsistent licenses. These are only ones where there were two different license marked in the same file, and hence were previously unclear. * ads124s08 - Spelling fix. * adxl345 - Parameter alignement tidy up. * bmi160 - SPDX - correct a note on the types of supported interrupts which was too strict. - use iio_pollfunc_store_time to grab an earlier timestamp. - use if (ret) instead of if (ret < 0) to be consistent whilst simplifying some handling where ret was effectively getting written to 0 even though it was always already 0. * exynos_adc - Fix a null pointer dereference on unbind. - Fix number of channels on Exynos4x12 devices to be 4 rather than 8. * lpc32xx-adc - Move DT bindings doc out of staging. Oops, I missed this one when moving the driver. - SPDX. * npcm-adc - drop documentation of reset node as going to be done differently. It's a new driver this cycle so no need to support the previous binding going forwards. * sps30 - Fix an issue with a loop timeout test that meant it would never identify a timeout. - Mark deliberate switch fall throughs. -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAlxjNmgRHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0FogooQ/9HDDuw4hrYJqH2cdUtKV29+a+kvMtrC1i X9+VstbMQNLo4DM3lYKi4VOeh6P5htRrD9ZJ4I2lh6PfnABjr3lb1AequlxwWNQZ 9EHEY3BA0G33757LQEkqxl7h8Cqvo2y6Wl6OcUund0jP+h3F3EYkI4XWNcq5Yht4 uWTkyTRYVZqFnlXGvfPz+53tEZ6p5RijbhOdYcL8R/0yWYzZzgzut7eYZn8Qn+mR LzSCBoEyAOUELYyRoczY2EkEO+u8H7lcU43i5TPPKji/c+w4OXu2ktuGVucXaHBs E1NLp0psLdqR2ef8fNYTs3FO2kxI7jV5qMlR91Sa2lDRyPhYeMF+JYBQlpqm5H2U xp8WwFrfT4KZ1yvioNeW+aNlPOd6ljDMg1z/iLWpAcUqx65QArmogL64m/Fc5GQD jrYzw68FO6fqKh3ik7VdPKIUS0p3Dz8BdWOqvI68+C/Mr/TgML51frf1NVbdd36L qgzMN6N53bykwN2w51O0Af4U3ZednN7BDDFkUbucutoglU+K8yRjFj583wM8QYsG GOZ3sPVZm+ItWCGc7nTJowe6+EQNgo/md3IEmmZNPrfWPHoMEebqnNcOuqcYvIlj wXgsJBNlWyQp5bqE9LmgClwAaWkXIoUvjUHt0cK5043ueLrYaGJ698sg0N/UO0JC T0/PJEMjLPE= =Se+F -----END PGP SIGNATURE----- Merge tag 'iio-for-5.1b' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: Second set of new device support, features and cleanup for IIO in the 5.1 cycle. There are a few late breaking fixes in here that weren't worth trying to rush into 5.0 as they have been with us for quite a while. New device support * ad7476 - add support for TI ADS786X parts that are compatible with this Analog Devices driver. Good to see some simple devices are so similar. * Ingenic jz47xx SoC ADCs - new driver and bindings * Plantower PMS7003 partical sensor - new driver and bindings including vendor prefix. * TI DAC7612 - new driver and bindings for this dual DAC. New features * ad7768-1 - Sampling frequency control * bmi160 - Data ready trigger support, including open-drain dt binding. Cleanup / minor fixes. * Analog Device DACs - Fix some inconsistent licenses. These are only ones where there were two different license marked in the same file, and hence were previously unclear. * ads124s08 - Spelling fix. * adxl345 - Parameter alignement tidy up. * bmi160 - SPDX - correct a note on the types of supported interrupts which was too strict. - use iio_pollfunc_store_time to grab an earlier timestamp. - use if (ret) instead of if (ret < 0) to be consistent whilst simplifying some handling where ret was effectively getting written to 0 even though it was always already 0. * exynos_adc - Fix a null pointer dereference on unbind. - Fix number of channels on Exynos4x12 devices to be 4 rather than 8. * lpc32xx-adc - Move DT bindings doc out of staging. Oops, I missed this one when moving the driver. - SPDX. * npcm-adc - drop documentation of reset node as going to be done differently. It's a new driver this cycle so no need to support the previous binding going forwards. * sps30 - Fix an issue with a loop timeout test that meant it would never identify a timeout. - Mark deliberate switch fall throughs. * tag 'iio-for-5.1b' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (26 commits) iio: adc: exynos-adc: Use proper number of channels for Exynos4x12 dt-binding: iio: remove rst node from NPCM ADC document dt-bindings: iio: chemical: pms7003: add device tree support dt-bindings: add Plantower to the vendor prefixes iio: chemical: add support for Plantower PMS7003 sensor iio:chemical:sps30 Supress some switch fallthrough warnings. iio:adc:lpc32xx use SPDX-License-Identifier dt-bindings: iio: adc: move lpc32xx-adc out of staging iio: adc: ads124s08: fix spelling mistake "converions" -> "conversions" iio: adc: exynos-adc: Fix NULL pointer exception on unbind iio: chemical: sps30: fix a loop timeout test iio:accel:adxl345: Change alignment to match paranthesis iio:dac:dac7612: device tree bindings iio:dac:ti-dac7612: Add driver for Texas Instruments DAC7612 iio: adc: ad7476: Add support for TI ADS786X ADCs iio: adc: ad7768-1: Add support for setting the sampling frequency drivers: iio: dac: Fix wrong license for ADI drivers IIO: add Ingenic JZ47xx ADC driver. dt-bindings: iio/adc: Add bindings for Ingenic JZ47xx SoCs ADC. dt-bindings: iio/adc: Add docs for Ingenic JZ47xx SoCs ADC. ...
This commit is contained in:
commit
277c8e8b81
48
Documentation/devicetree/bindings/iio/adc/ingenic,adc.txt
Normal file
48
Documentation/devicetree/bindings/iio/adc/ingenic,adc.txt
Normal file
@ -0,0 +1,48 @@
|
||||
* Ingenic JZ47xx ADC controller IIO bindings
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Should be one of:
|
||||
* ingenic,jz4725b-adc
|
||||
* ingenic,jz4740-adc
|
||||
- reg: ADC controller registers location and length.
|
||||
- clocks: phandle to the SoC's ADC clock.
|
||||
- clock-names: Must be set to "adc".
|
||||
- #io-channel-cells: Must be set to <1> to indicate channels are selected
|
||||
by index.
|
||||
|
||||
ADC clients must use the format described in iio-bindings.txt, giving
|
||||
a phandle and IIO specifier pair ("io-channels") to the ADC controller.
|
||||
|
||||
Example:
|
||||
|
||||
#include <dt-bindings/iio/adc/ingenic,adc.h>
|
||||
|
||||
adc: adc@10070000 {
|
||||
compatible = "ingenic,jz4740-adc";
|
||||
#io-channel-cells = <1>;
|
||||
|
||||
reg = <0x10070000 0x30>;
|
||||
|
||||
clocks = <&cgu JZ4740_CLK_ADC>;
|
||||
clock-names = "adc";
|
||||
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <18>;
|
||||
};
|
||||
|
||||
adc-keys {
|
||||
...
|
||||
compatible = "adc-keys";
|
||||
io-channels = <&adc INGENIC_ADC_AUX>;
|
||||
io-channel-names = "buttons";
|
||||
...
|
||||
};
|
||||
|
||||
battery {
|
||||
...
|
||||
compatible = "ingenic,jz4740-battery";
|
||||
io-channels = <&adc INGENIC_ADC_BATTERY>;
|
||||
io-channel-names = "battery";
|
||||
...
|
||||
};
|
@ -14,11 +14,6 @@ Optional properties:
|
||||
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 {
|
||||
@ -27,9 +22,3 @@ adc: adc@f000c000 {
|
||||
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>;
|
||||
};
|
||||
|
@ -11,11 +11,13 @@ New driver handles the following
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "samsung,exynos-adc-v1"
|
||||
for exynos4412/5250 controllers.
|
||||
for Exynos5250 controllers.
|
||||
Must be "samsung,exynos-adc-v2" for
|
||||
future controllers.
|
||||
Must be "samsung,exynos3250-adc" for
|
||||
controllers compatible with ADC of Exynos3250.
|
||||
Must be "samsung,exynos4212-adc" for
|
||||
controllers compatible with ADC of Exynos4212 and Exynos4412.
|
||||
Must be "samsung,exynos7-adc" for
|
||||
the ADC in Exynos7 and compatibles
|
||||
Must be "samsung,s3c2410-adc" for
|
||||
|
@ -0,0 +1,20 @@
|
||||
* Plantower PMS7003 particulate matter sensor
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "plantower,pms7003"
|
||||
- vcc-supply: phandle to the regulator that provides power to the sensor
|
||||
|
||||
Optional properties:
|
||||
- plantower,set-gpios: phandle to the GPIO connected to the SET line
|
||||
- reset-gpios: phandle to the GPIO connected to the RESET line
|
||||
|
||||
Refer to serial/slave-device.txt for generic serial attached device bindings.
|
||||
|
||||
Example:
|
||||
|
||||
&uart0 {
|
||||
air-pollution-sensor {
|
||||
compatible = "plantower,pms7003";
|
||||
vcc-supply = <®_vcc5v0>;
|
||||
};
|
||||
};
|
28
Documentation/devicetree/bindings/iio/dac/ti,dac7612.txt
Normal file
28
Documentation/devicetree/bindings/iio/dac/ti,dac7612.txt
Normal file
@ -0,0 +1,28 @@
|
||||
* Texas Instruments Dual, 12-Bit Serial Input Digital-to-Analog Converter
|
||||
|
||||
The DAC7612 is a dual, 12-bit digital-to-analog converter (DAC) with guaranteed
|
||||
12-bit monotonicity performance over the industrial temperature range.
|
||||
Is is programmable through an SPI interface.
|
||||
|
||||
The internal DACs are loaded when the LOADDACS pin is pulled down.
|
||||
|
||||
http://www.ti.com/lit/ds/sbas106/sbas106.pdf
|
||||
|
||||
Required Properties:
|
||||
- compatible: Should be one of:
|
||||
"ti,dac7612"
|
||||
"ti,dac7612u"
|
||||
"ti,dac7612ub"
|
||||
- reg: Definition as per Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Optional Properties:
|
||||
- ti,loaddacs-gpios: GPIO descriptor for the LOADDACS pin.
|
||||
- spi-*: Definition as per Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
|
||||
Example:
|
||||
|
||||
dac@1 {
|
||||
compatible = "ti,dac7612";
|
||||
reg = <0x1>;
|
||||
ti,loaddacs-gpios = <&msmgpio 25 GPIO_ACTIVE_LOW>;
|
||||
};
|
@ -9,9 +9,11 @@ Required properties:
|
||||
- spi-max-frequency : set maximum clock frequency (only for SPI)
|
||||
|
||||
Optional properties:
|
||||
- interrupts : interrupt mapping for IRQ, must be IRQ_TYPE_LEVEL_LOW
|
||||
- interrupts : interrupt mapping for IRQ
|
||||
- interrupt-names : set to "INT1" if INT1 pin should be used as interrupt
|
||||
input, set to "INT2" if INT2 pin should be used instead
|
||||
- drive-open-drain : set if the specified interrupt pin should be configured as
|
||||
open drain. If not set, defaults to push-pull.
|
||||
|
||||
Examples:
|
||||
|
||||
@ -20,7 +22,7 @@ bmi160@68 {
|
||||
reg = <0x68>;
|
||||
|
||||
interrupt-parent = <&gpio4>;
|
||||
interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
|
||||
interrupts = <12 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "INT1";
|
||||
};
|
||||
|
||||
|
@ -304,6 +304,7 @@ phytec PHYTEC Messtechnik GmbH
|
||||
picochip Picochip Ltd
|
||||
pine64 Pine64
|
||||
pixcir PIXCIR MICROELECTRONICS Co., Ltd
|
||||
plantower Plantower Co., Ltd
|
||||
plathome Plat'Home Co., Ltd.
|
||||
plda PLDA
|
||||
plx Broadcom Corporation (formerly PLX Technology)
|
||||
|
@ -15105,6 +15105,13 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: sound/soc/ti/
|
||||
|
||||
Texas Instruments' DAC7612 DAC Driver
|
||||
M: Ricardo Ribalda <ricardo@ribalda.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/iio/dac/ti-dac7612.c
|
||||
F: Documentation/devicetree/bindings/iio/dac/ti,dac7612.txt
|
||||
|
||||
THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -150,8 +150,8 @@ static int adxl345_read_raw(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
static int adxl345_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct adxl345_data *data = iio_priv(indio_dev);
|
||||
s64 n;
|
||||
|
@ -57,14 +57,17 @@ config AD7298
|
||||
module will be called ad7298.
|
||||
|
||||
config AD7476
|
||||
tristate "Analog Devices AD7476 and similar 1-channel ADCs driver"
|
||||
tristate "Analog Devices AD7476 1-channel ADCs driver and other similar devices from AD an TI"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7273, AD7274, AD7276,
|
||||
AD7277, AD7278, AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468,
|
||||
AD7495, AD7910, AD7920, AD7920 SPI analog to digital converters (ADC).
|
||||
Say yes here to build support for the following SPI analog to
|
||||
digital converters (ADCs):
|
||||
Analog Devices: AD7273, AD7274, AD7276, AD7277, AD7278, AD7475,
|
||||
AD7476, AD7477, AD7478, AD7466, AD7467, AD7468, AD7495, AD7910,
|
||||
AD7920.
|
||||
Texas Instruments: ADS7866, ADS7867, ADS7868.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7476.
|
||||
@ -407,6 +410,15 @@ config INA2XX_ADC
|
||||
Say yes here to build support for TI INA2xx family of Power Monitors.
|
||||
This driver is mutually exclusive with the HWMON version.
|
||||
|
||||
config INGENIC_ADC
|
||||
tristate "Ingenic JZ47xx SoCs ADC driver"
|
||||
depends on MIPS || COMPILE_TEST
|
||||
help
|
||||
Say yes here to build support for the Ingenic JZ47xx SoCs ADC unit.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ingenic_adc.
|
||||
|
||||
config IMX7D_ADC
|
||||
tristate "Freescale IMX7D ADC driver"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
|
@ -40,6 +40,7 @@ obj-$(CONFIG_HI8435) += hi8435.o
|
||||
obj-$(CONFIG_HX711) += hx711.o
|
||||
obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
|
||||
obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
|
||||
obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o
|
||||
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
||||
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
|
||||
obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
|
||||
|
@ -59,6 +59,9 @@ enum ad7476_supported_device_ids {
|
||||
ID_ADC081S,
|
||||
ID_ADC101S,
|
||||
ID_ADC121S,
|
||||
ID_ADS7866,
|
||||
ID_ADS7867,
|
||||
ID_ADS7868,
|
||||
};
|
||||
|
||||
static irqreturn_t ad7476_trigger_handler(int irq, void *p)
|
||||
@ -157,6 +160,8 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
|
||||
#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
|
||||
BIT(IIO_CHAN_INFO_RAW))
|
||||
#define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0)
|
||||
#define ADS786X_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \
|
||||
BIT(IIO_CHAN_INFO_RAW))
|
||||
|
||||
static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
|
||||
[ID_AD7091R] = {
|
||||
@ -209,6 +214,18 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
|
||||
.channel[0] = ADC081S_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_ADS7866] = {
|
||||
.channel[0] = ADS786X_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_ADS7867] = {
|
||||
.channel[0] = ADS786X_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_ADS7868] = {
|
||||
.channel[0] = ADS786X_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_info ad7476_info = {
|
||||
@ -314,6 +331,9 @@ static const struct spi_device_id ad7476_id[] = {
|
||||
{"adc081s", ID_ADC081S},
|
||||
{"adc101s", ID_ADC101S},
|
||||
{"adc121s", ID_ADC121S},
|
||||
{"ads7866", ID_ADS7866},
|
||||
{"ads7867", ID_ADS7867},
|
||||
{"ads7868", ID_ADS7868},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7476_id);
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
@ -59,6 +60,18 @@
|
||||
#define AD7768_REG_DIG_DIAG_STATUS 0x30
|
||||
#define AD7768_REG_MCLK_COUNTER 0x31
|
||||
|
||||
/* AD7768_REG_POWER_CLOCK */
|
||||
#define AD7768_PWR_MCLK_DIV_MSK GENMASK(5, 4)
|
||||
#define AD7768_PWR_MCLK_DIV(x) FIELD_PREP(AD7768_PWR_MCLK_DIV_MSK, x)
|
||||
#define AD7768_PWR_PWRMODE_MSK GENMASK(1, 0)
|
||||
#define AD7768_PWR_PWRMODE(x) FIELD_PREP(AD7768_PWR_PWRMODE_MSK, x)
|
||||
|
||||
/* AD7768_REG_DIGITAL_FILTER */
|
||||
#define AD7768_DIG_FIL_FIL_MSK GENMASK(6, 4)
|
||||
#define AD7768_DIG_FIL_FIL(x) FIELD_PREP(AD7768_DIG_FIL_FIL_MSK, x)
|
||||
#define AD7768_DIG_FIL_DEC_MSK GENMASK(2, 0)
|
||||
#define AD7768_DIG_FIL_DEC_RATE(x) FIELD_PREP(AD7768_DIG_FIL_DEC_MSK, x)
|
||||
|
||||
/* AD7768_REG_CONVERSION */
|
||||
#define AD7768_CONV_MODE_MSK GENMASK(2, 0)
|
||||
#define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x)
|
||||
@ -80,11 +93,51 @@ enum ad7768_pwrmode {
|
||||
AD7768_FAST_MODE = 3
|
||||
};
|
||||
|
||||
enum ad7768_mclk_div {
|
||||
AD7768_MCLK_DIV_16,
|
||||
AD7768_MCLK_DIV_8,
|
||||
AD7768_MCLK_DIV_4,
|
||||
AD7768_MCLK_DIV_2
|
||||
};
|
||||
|
||||
enum ad7768_dec_rate {
|
||||
AD7768_DEC_RATE_32 = 0,
|
||||
AD7768_DEC_RATE_64 = 1,
|
||||
AD7768_DEC_RATE_128 = 2,
|
||||
AD7768_DEC_RATE_256 = 3,
|
||||
AD7768_DEC_RATE_512 = 4,
|
||||
AD7768_DEC_RATE_1024 = 5,
|
||||
AD7768_DEC_RATE_8 = 9,
|
||||
AD7768_DEC_RATE_16 = 10
|
||||
};
|
||||
|
||||
struct ad7768_clk_configuration {
|
||||
enum ad7768_mclk_div mclk_div;
|
||||
enum ad7768_dec_rate dec_rate;
|
||||
unsigned int clk_div;
|
||||
enum ad7768_pwrmode pwrmode;
|
||||
};
|
||||
|
||||
static const struct ad7768_clk_configuration ad7768_clk_config[] = {
|
||||
{ AD7768_MCLK_DIV_2, AD7768_DEC_RATE_8, 16, AD7768_FAST_MODE },
|
||||
{ AD7768_MCLK_DIV_2, AD7768_DEC_RATE_16, 32, AD7768_FAST_MODE },
|
||||
{ AD7768_MCLK_DIV_2, AD7768_DEC_RATE_32, 64, AD7768_FAST_MODE },
|
||||
{ AD7768_MCLK_DIV_2, AD7768_DEC_RATE_64, 128, AD7768_FAST_MODE },
|
||||
{ AD7768_MCLK_DIV_2, AD7768_DEC_RATE_128, 256, AD7768_FAST_MODE },
|
||||
{ AD7768_MCLK_DIV_4, AD7768_DEC_RATE_128, 512, AD7768_MED_MODE },
|
||||
{ AD7768_MCLK_DIV_4, AD7768_DEC_RATE_256, 1024, AD7768_MED_MODE },
|
||||
{ AD7768_MCLK_DIV_4, AD7768_DEC_RATE_512, 2048, AD7768_MED_MODE },
|
||||
{ AD7768_MCLK_DIV_4, AD7768_DEC_RATE_1024, 4096, AD7768_MED_MODE },
|
||||
{ AD7768_MCLK_DIV_8, AD7768_DEC_RATE_1024, 8192, AD7768_MED_MODE },
|
||||
{ AD7768_MCLK_DIV_16, AD7768_DEC_RATE_1024, 16384, AD7768_ECO_MODE },
|
||||
};
|
||||
|
||||
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),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.scan_index = 0,
|
||||
@ -102,8 +155,12 @@ struct ad7768_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *vref;
|
||||
struct mutex lock;
|
||||
struct clk *mclk;
|
||||
unsigned int mclk_freq;
|
||||
unsigned int samp_freq;
|
||||
struct completion completion;
|
||||
struct iio_trigger *trig;
|
||||
struct gpio_desc *gpio_sync_in;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
@ -210,6 +267,90 @@ err_unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7768_set_dig_fil(struct ad7768_state *st,
|
||||
enum ad7768_dec_rate dec_rate)
|
||||
{
|
||||
unsigned int mode;
|
||||
int ret;
|
||||
|
||||
if (dec_rate == AD7768_DEC_RATE_8 || dec_rate == AD7768_DEC_RATE_16)
|
||||
mode = AD7768_DIG_FIL_FIL(dec_rate);
|
||||
else
|
||||
mode = AD7768_DIG_FIL_DEC_RATE(dec_rate);
|
||||
|
||||
ret = ad7768_spi_reg_write(st, AD7768_REG_DIGITAL_FILTER, mode);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* A sync-in pulse is required every time the filter dec rate changes */
|
||||
gpiod_set_value(st->gpio_sync_in, 1);
|
||||
gpiod_set_value(st->gpio_sync_in, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7768_set_freq(struct ad7768_state *st,
|
||||
unsigned int freq)
|
||||
{
|
||||
unsigned int diff_new, diff_old, pwr_mode, i, idx;
|
||||
int res, ret;
|
||||
|
||||
diff_old = U32_MAX;
|
||||
idx = 0;
|
||||
|
||||
res = DIV_ROUND_CLOSEST(st->mclk_freq, freq);
|
||||
|
||||
/* Find the closest match for the desired sampling frequency */
|
||||
for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) {
|
||||
diff_new = abs(res - ad7768_clk_config[i].clk_div);
|
||||
if (diff_new < diff_old) {
|
||||
diff_old = diff_new;
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set both the mclk_div and pwrmode with a single write to the
|
||||
* POWER_CLOCK register
|
||||
*/
|
||||
pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div) |
|
||||
AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode);
|
||||
ret = ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK, pwr_mode);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ad7768_set_dig_fil(st, ad7768_clk_config[idx].dec_rate);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st->samp_freq = DIV_ROUND_CLOSEST(st->mclk_freq,
|
||||
ad7768_clk_config[idx].clk_div);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ad7768_sampling_freq_avail(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7768_state *st = iio_priv(indio_dev);
|
||||
unsigned int freq;
|
||||
int i, len = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) {
|
||||
freq = DIV_ROUND_CLOSEST(st->mclk_freq,
|
||||
ad7768_clk_config[i].clk_div);
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", freq);
|
||||
}
|
||||
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ad7768_sampling_freq_avail);
|
||||
|
||||
static int ad7768_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long info)
|
||||
@ -242,13 +383,43 @@ static int ad7768_read_raw(struct iio_dev *indio_dev,
|
||||
*val2 = chan->scan_type.realbits;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = st->samp_freq;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad7768_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long info)
|
||||
{
|
||||
struct ad7768_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return ad7768_set_freq(st, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static struct attribute *ad7768_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group ad7768_group = {
|
||||
.attrs = ad7768_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7768_info = {
|
||||
.attrs = &ad7768_group,
|
||||
.read_raw = &ad7768_read_raw,
|
||||
.write_raw = &ad7768_write_raw,
|
||||
.debugfs_reg_access = &ad7768_reg_access,
|
||||
};
|
||||
|
||||
@ -270,9 +441,13 @@ static int ad7768_setup(struct ad7768_state *st)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set power mode to fast */
|
||||
return ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK,
|
||||
AD7768_FAST_MODE);
|
||||
st->gpio_sync_in = devm_gpiod_get(&st->spi->dev, "adi,sync-in",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st->gpio_sync_in))
|
||||
return PTR_ERR(st->gpio_sync_in);
|
||||
|
||||
/* Set the default sampling frequency to 32000 kSPS */
|
||||
return ad7768_set_freq(st, 32000);
|
||||
}
|
||||
|
||||
static irqreturn_t ad7768_trigger_handler(int irq, void *p)
|
||||
@ -356,6 +531,13 @@ static void ad7768_regulator_disable(void *data)
|
||||
regulator_disable(st->vref);
|
||||
}
|
||||
|
||||
static void ad7768_clk_disable(void *data)
|
||||
{
|
||||
struct ad7768_state *st = data;
|
||||
|
||||
clk_disable_unprepare(st->mclk);
|
||||
}
|
||||
|
||||
static int ad7768_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7768_state *st;
|
||||
@ -383,6 +565,20 @@ static int ad7768_probe(struct spi_device *spi)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->mclk = devm_clk_get(&spi->dev, "mclk");
|
||||
if (IS_ERR(st->mclk))
|
||||
return PTR_ERR(st->mclk);
|
||||
|
||||
ret = clk_prepare_enable(st->mclk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(&spi->dev, ad7768_clk_disable, st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->mclk_freq = clk_get_rate(st->mclk);
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
mutex_init(&st->lock);
|
||||
|
||||
|
@ -115,6 +115,7 @@
|
||||
#define MAX_ADC_V2_CHANNELS 10
|
||||
#define MAX_ADC_V1_CHANNELS 8
|
||||
#define MAX_EXYNOS3250_ADC_CHANNELS 2
|
||||
#define MAX_EXYNOS4212_ADC_CHANNELS 4
|
||||
#define MAX_S5PV210_ADC_CHANNELS 10
|
||||
|
||||
/* Bit definitions common for ADC_V1 and ADC_V2 */
|
||||
@ -271,6 +272,19 @@ static void exynos_adc_v1_start_conv(struct exynos_adc *info,
|
||||
writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
|
||||
}
|
||||
|
||||
/* Exynos4212 and 4412 is like ADCv1 but with four channels only */
|
||||
static const struct exynos_adc_data exynos4212_adc_data = {
|
||||
.num_channels = MAX_EXYNOS4212_ADC_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
.needs_adc_phy = true,
|
||||
.phy_offset = EXYNOS_ADCV1_PHY_OFFSET,
|
||||
|
||||
.init_hw = exynos_adc_v1_init_hw,
|
||||
.exit_hw = exynos_adc_v1_exit_hw,
|
||||
.clear_irq = exynos_adc_v1_clear_irq,
|
||||
.start_conv = exynos_adc_v1_start_conv,
|
||||
};
|
||||
|
||||
static const struct exynos_adc_data exynos_adc_v1_data = {
|
||||
.num_channels = MAX_ADC_V1_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
@ -492,6 +506,9 @@ static const struct of_device_id exynos_adc_match[] = {
|
||||
}, {
|
||||
.compatible = "samsung,s5pv210-adc",
|
||||
.data = &exynos_adc_s5pv210_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos4212-adc",
|
||||
.data = &exynos4212_adc_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos-adc-v1",
|
||||
.data = &exynos_adc_v1_data,
|
||||
@ -929,7 +946,7 @@ static int exynos_adc_remove(struct platform_device *pdev)
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct exynos_adc *info = iio_priv(indio_dev);
|
||||
|
||||
if (IS_REACHABLE(CONFIG_INPUT)) {
|
||||
if (IS_REACHABLE(CONFIG_INPUT) && info->input) {
|
||||
free_irq(info->tsirq, info);
|
||||
input_unregister_device(info->input);
|
||||
}
|
||||
|
364
drivers/iio/adc/ingenic-adc.c
Normal file
364
drivers/iio/adc/ingenic-adc.c
Normal file
@ -0,0 +1,364 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ADC driver for the Ingenic JZ47xx SoCs
|
||||
* Copyright (c) 2019 Artur Rojek <contact@artur-rojek.eu>
|
||||
*
|
||||
* based on drivers/mfd/jz4740-adc.c
|
||||
*/
|
||||
|
||||
#include <dt-bindings/iio/adc/ingenic,adc.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define JZ_ADC_REG_ENABLE 0x00
|
||||
#define JZ_ADC_REG_CFG 0x04
|
||||
#define JZ_ADC_REG_CTRL 0x08
|
||||
#define JZ_ADC_REG_STATUS 0x0c
|
||||
#define JZ_ADC_REG_ADTCH 0x18
|
||||
#define JZ_ADC_REG_ADBDAT 0x1c
|
||||
#define JZ_ADC_REG_ADSDAT 0x20
|
||||
|
||||
#define JZ_ADC_REG_CFG_BAT_MD BIT(4)
|
||||
|
||||
#define JZ_ADC_AUX_VREF 3300
|
||||
#define JZ_ADC_AUX_VREF_BITS 12
|
||||
#define JZ_ADC_BATTERY_LOW_VREF 2500
|
||||
#define JZ_ADC_BATTERY_LOW_VREF_BITS 12
|
||||
#define JZ4725B_ADC_BATTERY_HIGH_VREF 7500
|
||||
#define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS 10
|
||||
#define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986)
|
||||
#define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12
|
||||
|
||||
struct ingenic_adc_soc_data {
|
||||
unsigned int battery_high_vref;
|
||||
unsigned int battery_high_vref_bits;
|
||||
const int *battery_raw_avail;
|
||||
size_t battery_raw_avail_size;
|
||||
const int *battery_scale_avail;
|
||||
size_t battery_scale_avail_size;
|
||||
};
|
||||
|
||||
struct ingenic_adc {
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
struct mutex lock;
|
||||
const struct ingenic_adc_soc_data *soc_data;
|
||||
bool low_vref_mode;
|
||||
};
|
||||
|
||||
static void ingenic_adc_set_config(struct ingenic_adc *adc,
|
||||
uint32_t mask,
|
||||
uint32_t val)
|
||||
{
|
||||
uint32_t cfg;
|
||||
|
||||
clk_enable(adc->clk);
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
cfg = readl(adc->base + JZ_ADC_REG_CFG) & ~mask;
|
||||
cfg |= val;
|
||||
writel(cfg, adc->base + JZ_ADC_REG_CFG);
|
||||
|
||||
mutex_unlock(&adc->lock);
|
||||
clk_disable(adc->clk);
|
||||
}
|
||||
|
||||
static void ingenic_adc_enable(struct ingenic_adc *adc,
|
||||
int engine,
|
||||
bool enabled)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
val = readb(adc->base + JZ_ADC_REG_ENABLE);
|
||||
|
||||
if (enabled)
|
||||
val |= BIT(engine);
|
||||
else
|
||||
val &= ~BIT(engine);
|
||||
|
||||
writeb(val, adc->base + JZ_ADC_REG_ENABLE);
|
||||
mutex_unlock(&adc->lock);
|
||||
}
|
||||
|
||||
static int ingenic_adc_capture(struct ingenic_adc *adc,
|
||||
int engine)
|
||||
{
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
ingenic_adc_enable(adc, engine, true);
|
||||
ret = readb_poll_timeout(adc->base + JZ_ADC_REG_ENABLE, val,
|
||||
!(val & BIT(engine)), 250, 1000);
|
||||
if (ret)
|
||||
ingenic_adc_enable(adc, engine, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ingenic_adc_write_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long m)
|
||||
{
|
||||
struct ingenic_adc *adc = iio_priv(iio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->channel) {
|
||||
case INGENIC_ADC_BATTERY:
|
||||
if (val > JZ_ADC_BATTERY_LOW_VREF) {
|
||||
ingenic_adc_set_config(adc,
|
||||
JZ_ADC_REG_CFG_BAT_MD,
|
||||
0);
|
||||
adc->low_vref_mode = false;
|
||||
} else {
|
||||
ingenic_adc_set_config(adc,
|
||||
JZ_ADC_REG_CFG_BAT_MD,
|
||||
JZ_ADC_REG_CFG_BAT_MD);
|
||||
adc->low_vref_mode = true;
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const int jz4725b_adc_battery_raw_avail[] = {
|
||||
0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1,
|
||||
};
|
||||
|
||||
static const int jz4725b_adc_battery_scale_avail[] = {
|
||||
JZ4725B_ADC_BATTERY_HIGH_VREF, JZ4725B_ADC_BATTERY_HIGH_VREF_BITS,
|
||||
JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS,
|
||||
};
|
||||
|
||||
static const int jz4740_adc_battery_raw_avail[] = {
|
||||
0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1,
|
||||
};
|
||||
|
||||
static const int jz4740_adc_battery_scale_avail[] = {
|
||||
JZ4740_ADC_BATTERY_HIGH_VREF, JZ4740_ADC_BATTERY_HIGH_VREF_BITS,
|
||||
JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS,
|
||||
};
|
||||
|
||||
static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = {
|
||||
.battery_high_vref = JZ4725B_ADC_BATTERY_HIGH_VREF,
|
||||
.battery_high_vref_bits = JZ4725B_ADC_BATTERY_HIGH_VREF_BITS,
|
||||
.battery_raw_avail = jz4725b_adc_battery_raw_avail,
|
||||
.battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail),
|
||||
.battery_scale_avail = jz4725b_adc_battery_scale_avail,
|
||||
.battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail),
|
||||
};
|
||||
|
||||
static const struct ingenic_adc_soc_data jz4740_adc_soc_data = {
|
||||
.battery_high_vref = JZ4740_ADC_BATTERY_HIGH_VREF,
|
||||
.battery_high_vref_bits = JZ4740_ADC_BATTERY_HIGH_VREF_BITS,
|
||||
.battery_raw_avail = jz4740_adc_battery_raw_avail,
|
||||
.battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail),
|
||||
.battery_scale_avail = jz4740_adc_battery_scale_avail,
|
||||
.battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail),
|
||||
};
|
||||
|
||||
static int ingenic_adc_read_avail(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals,
|
||||
int *type,
|
||||
int *length,
|
||||
long m)
|
||||
{
|
||||
struct ingenic_adc *adc = iio_priv(iio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*type = IIO_VAL_INT;
|
||||
*length = adc->soc_data->battery_raw_avail_size;
|
||||
*vals = adc->soc_data->battery_raw_avail;
|
||||
return IIO_AVAIL_RANGE;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*type = IIO_VAL_FRACTIONAL_LOG2;
|
||||
*length = adc->soc_data->battery_scale_avail_size;
|
||||
*vals = adc->soc_data->battery_scale_avail;
|
||||
return IIO_AVAIL_LIST;
|
||||
default:
|
||||
return -EINVAL;
|
||||
};
|
||||
}
|
||||
|
||||
static int ingenic_adc_read_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ingenic_adc *adc = iio_priv(iio_dev);
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
clk_enable(adc->clk);
|
||||
ret = ingenic_adc_capture(adc, chan->channel);
|
||||
if (ret) {
|
||||
clk_disable(adc->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (chan->channel) {
|
||||
case INGENIC_ADC_AUX:
|
||||
*val = readw(adc->base + JZ_ADC_REG_ADSDAT);
|
||||
break;
|
||||
case INGENIC_ADC_BATTERY:
|
||||
*val = readw(adc->base + JZ_ADC_REG_ADBDAT);
|
||||
break;
|
||||
}
|
||||
|
||||
clk_disable(adc->clk);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->channel) {
|
||||
case INGENIC_ADC_AUX:
|
||||
*val = JZ_ADC_AUX_VREF;
|
||||
*val2 = JZ_ADC_AUX_VREF_BITS;
|
||||
break;
|
||||
case INGENIC_ADC_BATTERY:
|
||||
if (adc->low_vref_mode) {
|
||||
*val = JZ_ADC_BATTERY_LOW_VREF;
|
||||
*val2 = JZ_ADC_BATTERY_LOW_VREF_BITS;
|
||||
} else {
|
||||
*val = adc->soc_data->battery_high_vref;
|
||||
*val2 = adc->soc_data->battery_high_vref_bits;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static void ingenic_adc_clk_cleanup(void *data)
|
||||
{
|
||||
clk_unprepare(data);
|
||||
}
|
||||
|
||||
static const struct iio_info ingenic_adc_info = {
|
||||
.write_raw = ingenic_adc_write_raw,
|
||||
.read_raw = ingenic_adc_read_raw,
|
||||
.read_avail = ingenic_adc_read_avail,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ingenic_channels[] = {
|
||||
{
|
||||
.extend_name = "aux",
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.indexed = 1,
|
||||
.channel = INGENIC_ADC_AUX,
|
||||
},
|
||||
{
|
||||
.extend_name = "battery",
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.indexed = 1,
|
||||
.channel = INGENIC_ADC_BATTERY,
|
||||
},
|
||||
};
|
||||
|
||||
static int ingenic_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct iio_dev *iio_dev;
|
||||
struct ingenic_adc *adc;
|
||||
struct resource *mem_base;
|
||||
const struct ingenic_adc_soc_data *soc_data;
|
||||
int ret;
|
||||
|
||||
soc_data = device_get_match_data(dev);
|
||||
if (!soc_data)
|
||||
return -EINVAL;
|
||||
|
||||
iio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
|
||||
if (!iio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(iio_dev);
|
||||
mutex_init(&adc->lock);
|
||||
adc->soc_data = soc_data;
|
||||
|
||||
mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
adc->base = devm_ioremap_resource(dev, mem_base);
|
||||
if (IS_ERR(adc->base)) {
|
||||
dev_err(dev, "Unable to ioremap mmio resource\n");
|
||||
return PTR_ERR(adc->base);
|
||||
}
|
||||
|
||||
adc->clk = devm_clk_get(dev, "adc");
|
||||
if (IS_ERR(adc->clk)) {
|
||||
dev_err(dev, "Unable to get clock\n");
|
||||
return PTR_ERR(adc->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(adc->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Put hardware in a known passive state. */
|
||||
writeb(0x00, adc->base + JZ_ADC_REG_ENABLE);
|
||||
writeb(0xff, adc->base + JZ_ADC_REG_CTRL);
|
||||
clk_disable(adc->clk);
|
||||
|
||||
ret = devm_add_action_or_reset(dev, ingenic_adc_clk_cleanup, adc->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Unable to add action\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
iio_dev->dev.parent = dev;
|
||||
iio_dev->name = "jz-adc";
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->channels = ingenic_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(ingenic_channels);
|
||||
iio_dev->info = &ingenic_adc_info;
|
||||
|
||||
ret = devm_iio_device_register(dev, iio_dev);
|
||||
if (ret)
|
||||
dev_err(dev, "Unable to register IIO device\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id ingenic_adc_of_match[] = {
|
||||
{ .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, },
|
||||
{ .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ingenic_adc_of_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver ingenic_adc_driver = {
|
||||
.driver = {
|
||||
.name = "ingenic-adc",
|
||||
.of_match_table = of_match_ptr(ingenic_adc_of_match),
|
||||
},
|
||||
.probe = ingenic_adc_probe,
|
||||
};
|
||||
module_platform_driver(ingenic_adc_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1,23 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* lpc32xx_adc.c - Support for ADC in LPC32XX
|
||||
*
|
||||
* 3-channel, 10-bit ADC
|
||||
*
|
||||
* Copyright (C) 2011, 2012 Roland Stigge <stigge@antcom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -232,7 +232,7 @@ static int ads124s_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV);
|
||||
if (ret) {
|
||||
dev_err(&priv->spi->dev, "Start converions failed\n");
|
||||
dev_err(&priv->spi->dev, "Start conversions failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -246,7 +246,7 @@ static int ads124s_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV);
|
||||
if (ret) {
|
||||
dev_err(&priv->spi->dev, "Stop converions failed\n");
|
||||
dev_err(&priv->spi->dev, "Stop conversions failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -283,12 +283,12 @@ static irqreturn_t ads124s_trigger_handler(int irq, void *p)
|
||||
|
||||
ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV);
|
||||
if (ret)
|
||||
dev_err(&priv->spi->dev, "Start ADC converions failed\n");
|
||||
dev_err(&priv->spi->dev, "Start ADC conversions 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");
|
||||
dev_err(&priv->spi->dev, "Stop ADC conversions failed\n");
|
||||
|
||||
j++;
|
||||
}
|
||||
|
@ -61,6 +61,16 @@ config IAQCORE
|
||||
iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds)
|
||||
sensors
|
||||
|
||||
config PMS7003
|
||||
tristate "Plantower PMS7003 particulate matter sensor"
|
||||
depends on SERIAL_DEV_BUS
|
||||
help
|
||||
Say Y here to build support for the Plantower PMS7003 particulate
|
||||
matter sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called pms7003.
|
||||
|
||||
config SPS30
|
||||
tristate "SPS30 particulate matter sensor"
|
||||
depends on I2C
|
||||
|
@ -9,6 +9,7 @@ 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_PMS7003) += pms7003.o
|
||||
obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o
|
||||
obj-$(CONFIG_SPS30) += sps30.o
|
||||
obj-$(CONFIG_VZ89X) += vz89x.o
|
||||
|
340
drivers/iio/chemical/pms7003.c
Normal file
340
drivers/iio/chemical/pms7003.c
Normal file
@ -0,0 +1,340 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Plantower PMS7003 particulate matter sensor driver
|
||||
*
|
||||
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/serdev.h>
|
||||
|
||||
#define PMS7003_DRIVER_NAME "pms7003"
|
||||
|
||||
#define PMS7003_MAGIC 0x424d
|
||||
/* last 2 data bytes hold frame checksum */
|
||||
#define PMS7003_MAX_DATA_LENGTH 28
|
||||
#define PMS7003_CHECKSUM_LENGTH 2
|
||||
#define PMS7003_PM10_OFFSET 10
|
||||
#define PMS7003_PM2P5_OFFSET 8
|
||||
#define PMS7003_PM1_OFFSET 6
|
||||
|
||||
#define PMS7003_TIMEOUT msecs_to_jiffies(6000)
|
||||
#define PMS7003_CMD_LENGTH 7
|
||||
#define PMS7003_PM_MAX 1000
|
||||
#define PMS7003_PM_MIN 0
|
||||
|
||||
enum {
|
||||
PM1,
|
||||
PM2P5,
|
||||
PM10,
|
||||
};
|
||||
|
||||
enum pms7003_cmd {
|
||||
CMD_WAKEUP,
|
||||
CMD_ENTER_PASSIVE_MODE,
|
||||
CMD_READ_PASSIVE,
|
||||
CMD_SLEEP,
|
||||
};
|
||||
|
||||
/*
|
||||
* commands have following format:
|
||||
*
|
||||
* +------+------+-----+------+-----+-----------+-----------+
|
||||
* | 0x42 | 0x4d | cmd | 0x00 | arg | cksum msb | cksum lsb |
|
||||
* +------+------+-----+------+-----+-----------+-----------+
|
||||
*/
|
||||
static const u8 pms7003_cmd_tbl[][PMS7003_CMD_LENGTH] = {
|
||||
[CMD_WAKEUP] = { 0x42, 0x4d, 0xe4, 0x00, 0x01, 0x01, 0x74 },
|
||||
[CMD_ENTER_PASSIVE_MODE] = { 0x42, 0x4d, 0xe1, 0x00, 0x00, 0x01, 0x70 },
|
||||
[CMD_READ_PASSIVE] = { 0x42, 0x4d, 0xe2, 0x00, 0x00, 0x01, 0x71 },
|
||||
[CMD_SLEEP] = { 0x42, 0x4d, 0xe4, 0x00, 0x00, 0x01, 0x73 },
|
||||
};
|
||||
|
||||
struct pms7003_frame {
|
||||
u8 data[PMS7003_MAX_DATA_LENGTH];
|
||||
u16 expected_length;
|
||||
u16 length;
|
||||
};
|
||||
|
||||
struct pms7003_state {
|
||||
struct serdev_device *serdev;
|
||||
struct pms7003_frame frame;
|
||||
struct completion frame_ready;
|
||||
struct mutex lock; /* must be held whenever state gets touched */
|
||||
};
|
||||
|
||||
static int pms7003_do_cmd(struct pms7003_state *state, enum pms7003_cmd cmd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = serdev_device_write(state->serdev, pms7003_cmd_tbl[cmd],
|
||||
PMS7003_CMD_LENGTH, PMS7003_TIMEOUT);
|
||||
if (ret < PMS7003_CMD_LENGTH)
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
ret = wait_for_completion_interruptible_timeout(&state->frame_ready,
|
||||
PMS7003_TIMEOUT);
|
||||
if (!ret)
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
static u16 pms7003_get_pm(const u8 *data)
|
||||
{
|
||||
return clamp_val(get_unaligned_be16(data),
|
||||
PMS7003_PM_MIN, PMS7003_PM_MAX);
|
||||
}
|
||||
|
||||
static irqreturn_t pms7003_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct pms7003_state *state = iio_priv(indio_dev);
|
||||
struct pms7003_frame *frame = &state->frame;
|
||||
u16 data[3 + 1 + 4]; /* PM1, PM2P5, PM10, padding, timestamp */
|
||||
int ret;
|
||||
|
||||
mutex_lock(&state->lock);
|
||||
ret = pms7003_do_cmd(state, CMD_READ_PASSIVE);
|
||||
if (ret) {
|
||||
mutex_unlock(&state->lock);
|
||||
goto err;
|
||||
}
|
||||
|
||||
data[PM1] = pms7003_get_pm(frame->data + PMS7003_PM1_OFFSET);
|
||||
data[PM2P5] = pms7003_get_pm(frame->data + PMS7003_PM2P5_OFFSET);
|
||||
data[PM10] = pms7003_get_pm(frame->data + PMS7003_PM10_OFFSET);
|
||||
mutex_unlock(&state->lock);
|
||||
|
||||
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 pms7003_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct pms7003_state *state = iio_priv(indio_dev);
|
||||
struct pms7003_frame *frame = &state->frame;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
switch (chan->type) {
|
||||
case IIO_MASSCONCENTRATION:
|
||||
mutex_lock(&state->lock);
|
||||
ret = pms7003_do_cmd(state, CMD_READ_PASSIVE);
|
||||
if (ret) {
|
||||
mutex_unlock(&state->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = pms7003_get_pm(frame->data + chan->address);
|
||||
mutex_unlock(&state->lock);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info pms7003_info = {
|
||||
.read_raw = pms7003_read_raw,
|
||||
};
|
||||
|
||||
#define PMS7003_CHAN(_index, _mod, _addr) { \
|
||||
.type = IIO_MASSCONCENTRATION, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## _mod, \
|
||||
.address = _addr, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
.scan_index = _index, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 10, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_CPU, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec pms7003_channels[] = {
|
||||
PMS7003_CHAN(0, PM1, PMS7003_PM1_OFFSET),
|
||||
PMS7003_CHAN(1, PM2P5, PMS7003_PM2P5_OFFSET),
|
||||
PMS7003_CHAN(2, PM10, PMS7003_PM10_OFFSET),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static u16 pms7003_calc_checksum(struct pms7003_frame *frame)
|
||||
{
|
||||
u16 checksum = (PMS7003_MAGIC >> 8) + (u8)(PMS7003_MAGIC & 0xff) +
|
||||
(frame->length >> 8) + (u8)frame->length;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < frame->length - PMS7003_CHECKSUM_LENGTH; i++)
|
||||
checksum += frame->data[i];
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
static bool pms7003_frame_is_okay(struct pms7003_frame *frame)
|
||||
{
|
||||
int offset = frame->length - PMS7003_CHECKSUM_LENGTH;
|
||||
u16 checksum = get_unaligned_be16(frame->data + offset);
|
||||
|
||||
return checksum == pms7003_calc_checksum(frame);
|
||||
}
|
||||
|
||||
static int pms7003_receive_buf(struct serdev_device *serdev,
|
||||
const unsigned char *buf, size_t size)
|
||||
{
|
||||
struct iio_dev *indio_dev = serdev_device_get_drvdata(serdev);
|
||||
struct pms7003_state *state = iio_priv(indio_dev);
|
||||
struct pms7003_frame *frame = &state->frame;
|
||||
int num;
|
||||
|
||||
if (!frame->expected_length) {
|
||||
u16 magic;
|
||||
|
||||
/* wait for SOF and data length */
|
||||
if (size < 4)
|
||||
return 0;
|
||||
|
||||
magic = get_unaligned_be16(buf);
|
||||
if (magic != PMS7003_MAGIC)
|
||||
return 2;
|
||||
|
||||
num = get_unaligned_be16(buf + 2);
|
||||
if (num <= PMS7003_MAX_DATA_LENGTH) {
|
||||
frame->expected_length = num;
|
||||
frame->length = 0;
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
num = min(size, (size_t)(frame->expected_length - frame->length));
|
||||
memcpy(frame->data + frame->length, buf, num);
|
||||
frame->length += num;
|
||||
|
||||
if (frame->length == frame->expected_length) {
|
||||
if (pms7003_frame_is_okay(frame))
|
||||
complete(&state->frame_ready);
|
||||
|
||||
frame->expected_length = 0;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
static const struct serdev_device_ops pms7003_serdev_ops = {
|
||||
.receive_buf = pms7003_receive_buf,
|
||||
.write_wakeup = serdev_device_write_wakeup,
|
||||
};
|
||||
|
||||
static void pms7003_stop(void *data)
|
||||
{
|
||||
struct pms7003_state *state = data;
|
||||
|
||||
pms7003_do_cmd(state, CMD_SLEEP);
|
||||
}
|
||||
|
||||
static const unsigned long pms7003_scan_masks[] = { 0x07, 0x00 };
|
||||
|
||||
static int pms7003_probe(struct serdev_device *serdev)
|
||||
{
|
||||
struct pms7003_state *state;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&serdev->dev, sizeof(*state));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
state = iio_priv(indio_dev);
|
||||
serdev_device_set_drvdata(serdev, indio_dev);
|
||||
state->serdev = serdev;
|
||||
indio_dev->dev.parent = &serdev->dev;
|
||||
indio_dev->info = &pms7003_info;
|
||||
indio_dev->name = PMS7003_DRIVER_NAME;
|
||||
indio_dev->channels = pms7003_channels,
|
||||
indio_dev->num_channels = ARRAY_SIZE(pms7003_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->available_scan_masks = pms7003_scan_masks;
|
||||
|
||||
mutex_init(&state->lock);
|
||||
init_completion(&state->frame_ready);
|
||||
|
||||
serdev_device_set_client_ops(serdev, &pms7003_serdev_ops);
|
||||
ret = devm_serdev_device_open(&serdev->dev, serdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
serdev_device_set_baudrate(serdev, 9600);
|
||||
serdev_device_set_flow_control(serdev, false);
|
||||
|
||||
ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pms7003_do_cmd(state, CMD_WAKEUP);
|
||||
if (ret) {
|
||||
dev_err(&serdev->dev, "failed to wakeup sensor\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = pms7003_do_cmd(state, CMD_ENTER_PASSIVE_MODE);
|
||||
if (ret) {
|
||||
dev_err(&serdev->dev, "failed to enter passive mode\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(&serdev->dev, pms7003_stop, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(&serdev->dev, indio_dev, NULL,
|
||||
pms7003_trigger_handler, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&serdev->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id pms7003_of_match[] = {
|
||||
{ .compatible = "plantower,pms7003" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pms7003_of_match);
|
||||
|
||||
static struct serdev_device_driver pms7003_driver = {
|
||||
.driver = {
|
||||
.name = PMS7003_DRIVER_NAME,
|
||||
.of_match_table = pms7003_of_match,
|
||||
},
|
||||
.probe = pms7003_probe,
|
||||
};
|
||||
module_serdev_device_driver(pms7003_driver);
|
||||
|
||||
MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
|
||||
MODULE_DESCRIPTION("Plantower PMS7003 particulate matter sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -118,6 +118,7 @@ static int sps30_do_cmd(struct sps30_state *state, u16 cmd, u8 *data, int size)
|
||||
case SPS30_READ_AUTO_CLEANING_PERIOD:
|
||||
buf[0] = SPS30_AUTO_CLEANING_PERIOD >> 8;
|
||||
buf[1] = (u8)SPS30_AUTO_CLEANING_PERIOD;
|
||||
/* fall through */
|
||||
case SPS30_READ_DATA_READY_FLAG:
|
||||
case SPS30_READ_DATA:
|
||||
case SPS30_READ_SERIAL:
|
||||
@ -210,7 +211,7 @@ static int sps30_do_meas(struct sps30_state *state, s32 *data, int size)
|
||||
msleep_interruptible(300);
|
||||
}
|
||||
|
||||
if (!tries)
|
||||
if (tries == -1)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
ret = sps30_do_cmd(state, SPS30_READ_DATA, tmp, sizeof(int) * size);
|
||||
@ -295,6 +296,8 @@ static int sps30_read_raw(struct iio_dev *indio_dev,
|
||||
*val2 = 10000;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -375,6 +375,16 @@ config TI_DAC7311
|
||||
|
||||
If compiled as a module, it will be called ti-dac7311.
|
||||
|
||||
config TI_DAC7612
|
||||
tristate "Texas Instruments 12-bit 2-channel DAC driver"
|
||||
depends on SPI_MASTER && GPIOLIB
|
||||
help
|
||||
Driver for the Texas Instruments DAC7612, DAC7612U, DAC7612UB
|
||||
The driver hand drive the load pin automatically, otherwise
|
||||
it needs to be toggled manually.
|
||||
|
||||
If compiled as a module, it will be called ti-dac7612.
|
||||
|
||||
config VF610_DAC
|
||||
tristate "Vybrid vf610 DAC driver"
|
||||
depends on OF
|
||||
|
@ -41,4 +41,5 @@ obj-$(CONFIG_STM32_DAC) += stm32-dac.o
|
||||
obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o
|
||||
obj-$(CONFIG_TI_DAC5571) += ti-dac5571.o
|
||||
obj-$(CONFIG_TI_DAC7311) += ti-dac7311.o
|
||||
obj-$(CONFIG_TI_DAC7612) += ti-dac7612.o
|
||||
obj-$(CONFIG_VF610_DAC) += vf610_dac.o
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* AD5672R, AD5674R, AD5676, AD5676R, AD5679R,
|
||||
* AD5681R, AD5682R, AD5683, AD5683R, AD5684,
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* AD5686R, AD5685R, AD5684R Digital to analog converters driver
|
||||
*
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* This file is part of AD5686 DAC driver
|
||||
*
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* AD5671R, AD5675R, AD5691R, AD5692R, AD5693, AD5693R,
|
||||
* AD5694, AD5694R, AD5695R, AD5696, AD5696R
|
||||
|
@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* AD5758 Digital to analog converters driver
|
||||
*
|
||||
|
184
drivers/iio/dac/ti-dac7612.c
Normal file
184
drivers/iio/dac/ti-dac7612.c
Normal file
@ -0,0 +1,184 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* DAC7612 Dual, 12-Bit Serial input Digital-to-Analog Converter
|
||||
*
|
||||
* Copyright 2019 Qtechnology A/S
|
||||
* 2019 Ricardo Ribalda <ricardo@ribalda.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define DAC7612_RESOLUTION 12
|
||||
#define DAC7612_ADDRESS 4
|
||||
#define DAC7612_START 5
|
||||
|
||||
struct dac7612 {
|
||||
struct spi_device *spi;
|
||||
struct gpio_desc *loaddacs;
|
||||
uint16_t cache[2];
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
uint8_t data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int dac7612_cmd_single(struct dac7612 *priv, int channel, u16 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
priv->data[0] = BIT(DAC7612_START) | (channel << DAC7612_ADDRESS);
|
||||
priv->data[0] |= val >> 8;
|
||||
priv->data[1] = val & 0xff;
|
||||
|
||||
priv->cache[channel] = val;
|
||||
|
||||
ret = spi_write(priv->spi, priv->data, sizeof(priv->data));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gpiod_set_value(priv->loaddacs, 1);
|
||||
gpiod_set_value(priv->loaddacs, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define dac7612_CHANNEL(chan, name) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.channel = (chan), \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.datasheet_name = name, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec dac7612_channels[] = {
|
||||
dac7612_CHANNEL(0, "OUTA"),
|
||||
dac7612_CHANNEL(1, "OUTB"),
|
||||
};
|
||||
|
||||
static int dac7612_read_raw(struct iio_dev *iio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct dac7612 *priv;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
priv = iio_priv(iio_dev);
|
||||
*val = priv->cache[chan->channel];
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 1;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int dac7612_write_raw(struct iio_dev *iio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct dac7612 *priv = iio_priv(iio_dev);
|
||||
int ret;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
if ((val >= BIT(DAC7612_RESOLUTION)) || val < 0 || val2)
|
||||
return -EINVAL;
|
||||
|
||||
if (val == priv->cache[chan->channel])
|
||||
return 0;
|
||||
|
||||
mutex_lock(&iio_dev->mlock);
|
||||
ret = dac7612_cmd_single(priv, chan->channel, val);
|
||||
mutex_unlock(&iio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info dac7612_info = {
|
||||
.read_raw = dac7612_read_raw,
|
||||
.write_raw = dac7612_write_raw,
|
||||
};
|
||||
|
||||
static int dac7612_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *iio_dev;
|
||||
struct dac7612 *priv;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
|
||||
if (!iio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
priv = iio_priv(iio_dev);
|
||||
/*
|
||||
* LOADDACS pin can be controlled by the driver or externally.
|
||||
* When controlled by the driver, the DAC value is updated after
|
||||
* every write.
|
||||
* When the driver does not control the PIN, the user or an external
|
||||
* event can change the value of all DACs by pulsing down the LOADDACs
|
||||
* pin.
|
||||
*/
|
||||
priv->loaddacs = devm_gpiod_get_optional(&spi->dev, "ti,loaddacs",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(priv->loaddacs))
|
||||
return PTR_ERR(priv->loaddacs);
|
||||
priv->spi = spi;
|
||||
spi_set_drvdata(spi, iio_dev);
|
||||
iio_dev->dev.parent = &spi->dev;
|
||||
iio_dev->info = &dac7612_info;
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->channels = dac7612_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(priv->cache);
|
||||
iio_dev->name = spi_get_device_id(spi)->name;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(priv->cache); i++) {
|
||||
ret = dac7612_cmd_single(priv, i, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(&spi->dev, iio_dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id dac7612_id[] = {
|
||||
{"ti-dac7612"},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, dac7612_id);
|
||||
|
||||
static const struct of_device_id dac7612_of_match[] = {
|
||||
{ .compatible = "ti,dac7612" },
|
||||
{ .compatible = "ti,dac7612u" },
|
||||
{ .compatible = "ti,dac7612ub" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dac7612_of_match);
|
||||
|
||||
static struct spi_driver dac7612_driver = {
|
||||
.driver = {
|
||||
.name = "ti-dac7612",
|
||||
.of_match_table = dac7612_of_match,
|
||||
},
|
||||
.probe = dac7612_probe,
|
||||
.id_table = dac7612_id,
|
||||
};
|
||||
module_spi_driver(dac7612_driver);
|
||||
|
||||
MODULE_AUTHOR("Ricardo Ribalda <ricardo@ribalda.com>");
|
||||
MODULE_DESCRIPTION("Texas Instruments DAC7612 DAC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -2,9 +2,20 @@
|
||||
#ifndef BMI160_H_
|
||||
#define BMI160_H_
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
struct bmi160_data {
|
||||
struct regmap *regmap;
|
||||
struct iio_trigger *trig;
|
||||
};
|
||||
|
||||
extern const struct regmap_config bmi160_regmap_config;
|
||||
|
||||
int bmi160_core_probe(struct device *dev, struct regmap *regmap,
|
||||
const char *name, bool use_spi);
|
||||
|
||||
int bmi160_enable_irq(struct regmap *regmap, bool enable);
|
||||
|
||||
int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type);
|
||||
|
||||
#endif /* BMI160_H_ */
|
||||
|
@ -1,26 +1,27 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* BMI160 - Bosch IMU (accel, gyro plus external magnetometer)
|
||||
*
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
* Copyright (c) 2019, Martin Kelly.
|
||||
*
|
||||
* IIO core driver for BMI160, with support for I2C/SPI busses
|
||||
*
|
||||
* TODO: magnetometer, interrupts, hardware FIFO
|
||||
* TODO: magnetometer, hardware FIFO
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
|
||||
#include "bmi160.h"
|
||||
|
||||
@ -64,8 +65,32 @@
|
||||
#define BMI160_CMD_GYRO_PM_FAST_STARTUP 0x17
|
||||
#define BMI160_CMD_SOFTRESET 0xB6
|
||||
|
||||
#define BMI160_REG_INT_EN 0x51
|
||||
#define BMI160_DRDY_INT_EN BIT(4)
|
||||
|
||||
#define BMI160_REG_INT_OUT_CTRL 0x53
|
||||
#define BMI160_INT_OUT_CTRL_MASK 0x0f
|
||||
#define BMI160_INT1_OUT_CTRL_SHIFT 0
|
||||
#define BMI160_INT2_OUT_CTRL_SHIFT 4
|
||||
#define BMI160_EDGE_TRIGGERED BIT(0)
|
||||
#define BMI160_ACTIVE_HIGH BIT(1)
|
||||
#define BMI160_OPEN_DRAIN BIT(2)
|
||||
#define BMI160_OUTPUT_EN BIT(3)
|
||||
|
||||
#define BMI160_REG_INT_LATCH 0x54
|
||||
#define BMI160_INT1_LATCH_MASK BIT(4)
|
||||
#define BMI160_INT2_LATCH_MASK BIT(5)
|
||||
|
||||
/* INT1 and INT2 are in the opposite order as in INT_OUT_CTRL! */
|
||||
#define BMI160_REG_INT_MAP 0x56
|
||||
#define BMI160_INT1_MAP_DRDY_EN 0x80
|
||||
#define BMI160_INT2_MAP_DRDY_EN 0x08
|
||||
|
||||
#define BMI160_REG_DUMMY 0x7F
|
||||
|
||||
#define BMI160_NORMAL_WRITE_USLEEP 2
|
||||
#define BMI160_SUSPENDED_WRITE_USLEEP 450
|
||||
|
||||
#define BMI160_ACCEL_PMU_MIN_USLEEP 3800
|
||||
#define BMI160_GYRO_PMU_MIN_USLEEP 80000
|
||||
#define BMI160_SOFTRESET_USLEEP 1000
|
||||
@ -108,8 +133,9 @@ enum bmi160_sensor_type {
|
||||
BMI160_NUM_SENSORS /* must be last */
|
||||
};
|
||||
|
||||
struct bmi160_data {
|
||||
struct regmap *regmap;
|
||||
enum bmi160_int_pin {
|
||||
BMI160_PIN_INT1,
|
||||
BMI160_PIN_INT2
|
||||
};
|
||||
|
||||
const struct regmap_config bmi160_regmap_config = {
|
||||
@ -273,7 +299,7 @@ int bmi160_set_mode(struct bmi160_data *data, enum bmi160_sensor_type t,
|
||||
cmd = bmi160_regs[t].pmu_cmd_suspend;
|
||||
|
||||
ret = regmap_write(data->regmap, BMI160_REG_CMD, cmd);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
usleep_range(bmi160_pmu_time[t], bmi160_pmu_time[t] + 1000);
|
||||
@ -305,7 +331,7 @@ int bmi160_get_scale(struct bmi160_data *data, enum bmi160_sensor_type t,
|
||||
int i, ret, val;
|
||||
|
||||
ret = regmap_read(data->regmap, bmi160_regs[t].range, &val);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < bmi160_scale_table[t].num; i++)
|
||||
@ -328,7 +354,7 @@ static int bmi160_get_data(struct bmi160_data *data, int chan_type,
|
||||
reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(sample);
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(sample));
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = sign_extend32(le16_to_cpu(sample), 15);
|
||||
@ -362,7 +388,7 @@ static int bmi160_get_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
|
||||
int i, val, ret;
|
||||
|
||||
ret = regmap_read(data->regmap, bmi160_regs[t].config, &val);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= bmi160_regs[t].config_odr_mask;
|
||||
@ -394,13 +420,12 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p)
|
||||
indio_dev->masklength) {
|
||||
ret = regmap_bulk_read(data->regmap, base + i * sizeof(sample),
|
||||
&sample, sizeof(sample));
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
goto done;
|
||||
buf[j++] = sample;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buf,
|
||||
iio_get_time_ns(indio_dev));
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buf, pf->timestamp);
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
return IRQ_HANDLED;
|
||||
@ -416,18 +441,18 @@ static int bmi160_read_raw(struct iio_dev *indio_dev,
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = bmi160_get_data(data, chan->type, chan->channel2, val);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
ret = bmi160_get_scale(data,
|
||||
bmi160_to_sensor(chan->type), val2);
|
||||
return ret < 0 ? ret : IIO_VAL_INT_PLUS_MICRO;
|
||||
return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = bmi160_get_odr(data, bmi160_to_sensor(chan->type),
|
||||
val, val2);
|
||||
return ret < 0 ? ret : IIO_VAL_INT_PLUS_MICRO;
|
||||
return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -498,6 +523,186 @@ static const char *bmi160_match_acpi_device(struct device *dev)
|
||||
return dev_name(dev);
|
||||
}
|
||||
|
||||
static int bmi160_write_conf_reg(struct regmap *regmap, unsigned int reg,
|
||||
unsigned int mask, unsigned int bits,
|
||||
unsigned int write_usleep)
|
||||
{
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(regmap, reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = (val & ~mask) | bits;
|
||||
|
||||
ret = regmap_write(regmap, reg, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* We need to wait after writing before we can write again. See the
|
||||
* datasheet, page 93.
|
||||
*/
|
||||
usleep_range(write_usleep, write_usleep + 1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bmi160_config_pin(struct regmap *regmap, enum bmi160_int_pin pin,
|
||||
bool open_drain, u8 irq_mask,
|
||||
unsigned long write_usleep)
|
||||
{
|
||||
int ret;
|
||||
struct device *dev = regmap_get_device(regmap);
|
||||
u8 int_out_ctrl_shift;
|
||||
u8 int_latch_mask;
|
||||
u8 int_map_mask;
|
||||
u8 int_out_ctrl_mask;
|
||||
u8 int_out_ctrl_bits;
|
||||
const char *pin_name;
|
||||
|
||||
switch (pin) {
|
||||
case BMI160_PIN_INT1:
|
||||
int_out_ctrl_shift = BMI160_INT1_OUT_CTRL_SHIFT;
|
||||
int_latch_mask = BMI160_INT1_LATCH_MASK;
|
||||
int_map_mask = BMI160_INT1_MAP_DRDY_EN;
|
||||
break;
|
||||
case BMI160_PIN_INT2:
|
||||
int_out_ctrl_shift = BMI160_INT2_OUT_CTRL_SHIFT;
|
||||
int_latch_mask = BMI160_INT2_LATCH_MASK;
|
||||
int_map_mask = BMI160_INT2_MAP_DRDY_EN;
|
||||
break;
|
||||
}
|
||||
int_out_ctrl_mask = BMI160_INT_OUT_CTRL_MASK << int_out_ctrl_shift;
|
||||
|
||||
/*
|
||||
* Enable the requested pin with the right settings:
|
||||
* - Push-pull/open-drain
|
||||
* - Active low/high
|
||||
* - Edge/level triggered
|
||||
*/
|
||||
int_out_ctrl_bits = BMI160_OUTPUT_EN;
|
||||
if (open_drain)
|
||||
/* Default is push-pull. */
|
||||
int_out_ctrl_bits |= BMI160_OPEN_DRAIN;
|
||||
int_out_ctrl_bits |= irq_mask;
|
||||
int_out_ctrl_bits <<= int_out_ctrl_shift;
|
||||
|
||||
ret = bmi160_write_conf_reg(regmap, BMI160_REG_INT_OUT_CTRL,
|
||||
int_out_ctrl_mask, int_out_ctrl_bits,
|
||||
write_usleep);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set the pin to input mode with no latching. */
|
||||
ret = bmi160_write_conf_reg(regmap, BMI160_REG_INT_LATCH,
|
||||
int_latch_mask, int_latch_mask,
|
||||
write_usleep);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Map interrupts to the requested pin. */
|
||||
ret = bmi160_write_conf_reg(regmap, BMI160_REG_INT_MAP,
|
||||
int_map_mask, int_map_mask,
|
||||
write_usleep);
|
||||
if (ret) {
|
||||
switch (pin) {
|
||||
case BMI160_PIN_INT1:
|
||||
pin_name = "INT1";
|
||||
break;
|
||||
case BMI160_PIN_INT2:
|
||||
pin_name = "INT2";
|
||||
break;
|
||||
}
|
||||
dev_err(dev, "Failed to configure %s IRQ pin", pin_name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bmi160_enable_irq(struct regmap *regmap, bool enable)
|
||||
{
|
||||
unsigned int enable_bit = 0;
|
||||
|
||||
if (enable)
|
||||
enable_bit = BMI160_DRDY_INT_EN;
|
||||
|
||||
return bmi160_write_conf_reg(regmap, BMI160_REG_INT_EN,
|
||||
BMI160_DRDY_INT_EN, enable_bit,
|
||||
BMI160_NORMAL_WRITE_USLEEP);
|
||||
}
|
||||
EXPORT_SYMBOL(bmi160_enable_irq);
|
||||
|
||||
static int bmi160_get_irq(struct device_node *of_node, enum bmi160_int_pin *pin)
|
||||
{
|
||||
int irq;
|
||||
|
||||
/* Use INT1 if possible, otherwise fall back to INT2. */
|
||||
irq = of_irq_get_byname(of_node, "INT1");
|
||||
if (irq > 0) {
|
||||
*pin = BMI160_PIN_INT1;
|
||||
return irq;
|
||||
}
|
||||
|
||||
irq = of_irq_get_byname(of_node, "INT2");
|
||||
if (irq > 0)
|
||||
*pin = BMI160_PIN_INT2;
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
static int bmi160_config_device_irq(struct iio_dev *indio_dev, int irq_type,
|
||||
enum bmi160_int_pin pin)
|
||||
{
|
||||
bool open_drain;
|
||||
u8 irq_mask;
|
||||
struct bmi160_data *data = iio_priv(indio_dev);
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
|
||||
/* Level-triggered, active-low is the default if we set all zeroes. */
|
||||
if (irq_type == IRQF_TRIGGER_RISING)
|
||||
irq_mask = BMI160_ACTIVE_HIGH | BMI160_EDGE_TRIGGERED;
|
||||
else if (irq_type == IRQF_TRIGGER_FALLING)
|
||||
irq_mask = BMI160_EDGE_TRIGGERED;
|
||||
else if (irq_type == IRQF_TRIGGER_HIGH)
|
||||
irq_mask = BMI160_ACTIVE_HIGH;
|
||||
else if (irq_type == IRQF_TRIGGER_LOW)
|
||||
irq_mask = 0;
|
||||
else {
|
||||
dev_err(&indio_dev->dev,
|
||||
"Invalid interrupt type 0x%x specified\n", irq_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
open_drain = of_property_read_bool(dev->of_node, "drive-open-drain");
|
||||
|
||||
return bmi160_config_pin(data->regmap, pin, open_drain, irq_mask,
|
||||
BMI160_NORMAL_WRITE_USLEEP);
|
||||
}
|
||||
|
||||
static int bmi160_setup_irq(struct iio_dev *indio_dev, int irq,
|
||||
enum bmi160_int_pin pin)
|
||||
{
|
||||
struct irq_data *desc;
|
||||
u32 irq_type;
|
||||
int ret;
|
||||
|
||||
desc = irq_get_irq_data(irq);
|
||||
if (!desc) {
|
||||
dev_err(&indio_dev->dev, "Could not find IRQ %d\n", irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq_type = irqd_get_trigger_type(desc);
|
||||
|
||||
ret = bmi160_config_device_irq(indio_dev, irq_type, pin);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return bmi160_probe_trigger(indio_dev, irq, irq_type);
|
||||
}
|
||||
|
||||
static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
|
||||
{
|
||||
int ret;
|
||||
@ -505,7 +710,7 @@ static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
|
||||
struct device *dev = regmap_get_device(data->regmap);
|
||||
|
||||
ret = regmap_write(data->regmap, BMI160_REG_CMD, BMI160_CMD_SOFTRESET);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
usleep_range(BMI160_SOFTRESET_USLEEP, BMI160_SOFTRESET_USLEEP + 1);
|
||||
@ -516,12 +721,12 @@ static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
|
||||
*/
|
||||
if (use_spi) {
|
||||
ret = regmap_read(data->regmap, BMI160_REG_DUMMY, &val);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, BMI160_REG_CHIP_ID, &val);
|
||||
if (ret < 0) {
|
||||
if (ret) {
|
||||
dev_err(dev, "Error reading chip id\n");
|
||||
return ret;
|
||||
}
|
||||
@ -532,16 +737,59 @@ static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
|
||||
}
|
||||
|
||||
ret = bmi160_set_mode(data, BMI160_ACCEL, true);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = bmi160_set_mode(data, BMI160_GYRO, true);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bmi160_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool enable)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
struct bmi160_data *data = iio_priv(indio_dev);
|
||||
|
||||
return bmi160_enable_irq(data->regmap, enable);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops bmi160_trigger_ops = {
|
||||
.set_trigger_state = &bmi160_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type)
|
||||
{
|
||||
struct bmi160_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
data->trig = devm_iio_trigger_alloc(&indio_dev->dev, "%s-dev%d",
|
||||
indio_dev->name, indio_dev->id);
|
||||
|
||||
if (data->trig == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = devm_request_irq(&indio_dev->dev, irq,
|
||||
&iio_trigger_generic_data_rdy_poll,
|
||||
irq_type, "bmi160", data->trig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->trig->dev.parent = regmap_get_device(data->regmap);
|
||||
data->trig->ops = &bmi160_trigger_ops;
|
||||
iio_trigger_set_drvdata(data->trig, indio_dev);
|
||||
|
||||
ret = devm_iio_trigger_register(&indio_dev->dev, data->trig);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->trig = iio_trigger_get(data->trig);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void bmi160_chip_uninit(void *data)
|
||||
{
|
||||
struct bmi160_data *bmi_data = data;
|
||||
@ -555,6 +803,8 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct bmi160_data *data;
|
||||
int irq;
|
||||
enum bmi160_int_pin int_pin;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
@ -566,11 +816,11 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
|
||||
data->regmap = regmap;
|
||||
|
||||
ret = bmi160_chip_init(data, use_spi);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, bmi160_chip_uninit, data);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!name && ACPI_HANDLE(dev))
|
||||
@ -583,16 +833,23 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &bmi160_info;
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
|
||||
iio_pollfunc_store_time,
|
||||
bmi160_trigger_handler, NULL);
|
||||
if (ret < 0)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_iio_device_register(dev, indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
irq = bmi160_get_irq(dev->of_node, &int_pin);
|
||||
if (irq > 0) {
|
||||
ret = bmi160_setup_irq(indio_dev, irq, int_pin);
|
||||
if (ret)
|
||||
dev_err(&indio_dev->dev, "Failed to setup IRQ %d\n",
|
||||
irq);
|
||||
} else {
|
||||
dev_info(&indio_dev->dev, "Not setting up IRQ trigger\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bmi160_core_probe);
|
||||
|
||||
|
@ -1,12 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* BMI160 - Bosch IMU, I2C bits
|
||||
*
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* 7-bit I2C slave address is:
|
||||
* - 0x68 if SDO is pulled to GND
|
||||
* - 0x69 if SDO is pulled to VDDIO
|
||||
|
@ -1,11 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* BMI160 - Bosch IMU, SPI bits
|
||||
*
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/module.h>
|
||||
|
10
include/dt-bindings/iio/adc/ingenic,adc.h
Normal file
10
include/dt-bindings/iio/adc/ingenic,adc.h
Normal file
@ -0,0 +1,10 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef _DT_BINDINGS_IIO_ADC_INGENIC_ADC_H
|
||||
#define _DT_BINDINGS_IIO_ADC_INGENIC_ADC_H
|
||||
|
||||
/* ADC channel idx. */
|
||||
#define INGENIC_ADC_AUX 0
|
||||
#define INGENIC_ADC_BATTERY 1
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user