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
|
@ -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
|
vref-supply is not added the ADC will use internal voltage
|
||||||
reference.
|
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:
|
Example:
|
||||||
|
|
||||||
adc: adc@f000c000 {
|
adc: adc@f000c000 {
|
||||||
|
@ -27,9 +22,3 @@ adc: adc@f000c000 {
|
||||||
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
|
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
clocks = <&clk NPCM7XX_CLK_ADC>;
|
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:
|
Required properties:
|
||||||
- compatible: Must be "samsung,exynos-adc-v1"
|
- compatible: Must be "samsung,exynos-adc-v1"
|
||||||
for exynos4412/5250 controllers.
|
for Exynos5250 controllers.
|
||||||
Must be "samsung,exynos-adc-v2" for
|
Must be "samsung,exynos-adc-v2" for
|
||||||
future controllers.
|
future controllers.
|
||||||
Must be "samsung,exynos3250-adc" for
|
Must be "samsung,exynos3250-adc" for
|
||||||
controllers compatible with ADC of Exynos3250.
|
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
|
Must be "samsung,exynos7-adc" for
|
||||||
the ADC in Exynos7 and compatibles
|
the ADC in Exynos7 and compatibles
|
||||||
Must be "samsung,s3c2410-adc" for
|
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>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -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)
|
- spi-max-frequency : set maximum clock frequency (only for SPI)
|
||||||
|
|
||||||
Optional properties:
|
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
|
- interrupt-names : set to "INT1" if INT1 pin should be used as interrupt
|
||||||
input, set to "INT2" if INT2 pin should be used instead
|
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:
|
Examples:
|
||||||
|
|
||||||
|
@ -20,7 +22,7 @@ bmi160@68 {
|
||||||
reg = <0x68>;
|
reg = <0x68>;
|
||||||
|
|
||||||
interrupt-parent = <&gpio4>;
|
interrupt-parent = <&gpio4>;
|
||||||
interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
|
interrupts = <12 IRQ_TYPE_EDGE_RISING>;
|
||||||
interrupt-names = "INT1";
|
interrupt-names = "INT1";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -304,6 +304,7 @@ phytec PHYTEC Messtechnik GmbH
|
||||||
picochip Picochip Ltd
|
picochip Picochip Ltd
|
||||||
pine64 Pine64
|
pine64 Pine64
|
||||||
pixcir PIXCIR MICROELECTRONICS Co., Ltd
|
pixcir PIXCIR MICROELECTRONICS Co., Ltd
|
||||||
|
plantower Plantower Co., Ltd
|
||||||
plathome Plat'Home Co., Ltd.
|
plathome Plat'Home Co., Ltd.
|
||||||
plda PLDA
|
plda PLDA
|
||||||
plx Broadcom Corporation (formerly PLX Technology)
|
plx Broadcom Corporation (formerly PLX Technology)
|
||||||
|
|
|
@ -15105,6 +15105,13 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: sound/soc/ti/
|
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
|
THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
|
||||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||||
L: linux-media@vger.kernel.org
|
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,
|
static int adxl345_write_raw(struct iio_dev *indio_dev,
|
||||||
struct iio_chan_spec const *chan,
|
struct iio_chan_spec const *chan,
|
||||||
int val, int val2, long mask)
|
int val, int val2, long mask)
|
||||||
{
|
{
|
||||||
struct adxl345_data *data = iio_priv(indio_dev);
|
struct adxl345_data *data = iio_priv(indio_dev);
|
||||||
s64 n;
|
s64 n;
|
||||||
|
|
|
@ -57,14 +57,17 @@ config AD7298
|
||||||
module will be called ad7298.
|
module will be called ad7298.
|
||||||
|
|
||||||
config AD7476
|
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
|
depends on SPI
|
||||||
select IIO_BUFFER
|
select IIO_BUFFER
|
||||||
select IIO_TRIGGERED_BUFFER
|
select IIO_TRIGGERED_BUFFER
|
||||||
help
|
help
|
||||||
Say yes here to build support for Analog Devices AD7273, AD7274, AD7276,
|
Say yes here to build support for the following SPI analog to
|
||||||
AD7277, AD7278, AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468,
|
digital converters (ADCs):
|
||||||
AD7495, AD7910, AD7920, AD7920 SPI analog to digital converters (ADC).
|
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
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called ad7476.
|
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.
|
Say yes here to build support for TI INA2xx family of Power Monitors.
|
||||||
This driver is mutually exclusive with the HWMON version.
|
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
|
config IMX7D_ADC
|
||||||
tristate "Freescale IMX7D ADC driver"
|
tristate "Freescale IMX7D ADC driver"
|
||||||
depends on ARCH_MXC || COMPILE_TEST
|
depends on ARCH_MXC || COMPILE_TEST
|
||||||
|
|
|
@ -40,6 +40,7 @@ obj-$(CONFIG_HI8435) += hi8435.o
|
||||||
obj-$(CONFIG_HX711) += hx711.o
|
obj-$(CONFIG_HX711) += hx711.o
|
||||||
obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
|
obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
|
||||||
obj-$(CONFIG_INA2XX_ADC) += ina2xx-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_LP8788_ADC) += lp8788_adc.o
|
||||||
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
|
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
|
||||||
obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
|
obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
|
||||||
|
|
|
@ -59,6 +59,9 @@ enum ad7476_supported_device_ids {
|
||||||
ID_ADC081S,
|
ID_ADC081S,
|
||||||
ID_ADC101S,
|
ID_ADC101S,
|
||||||
ID_ADC121S,
|
ID_ADC121S,
|
||||||
|
ID_ADS7866,
|
||||||
|
ID_ADS7867,
|
||||||
|
ID_ADS7868,
|
||||||
};
|
};
|
||||||
|
|
||||||
static irqreturn_t ad7476_trigger_handler(int irq, void *p)
|
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), \
|
#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
|
||||||
BIT(IIO_CHAN_INFO_RAW))
|
BIT(IIO_CHAN_INFO_RAW))
|
||||||
#define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0)
|
#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[] = {
|
static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
|
||||||
[ID_AD7091R] = {
|
[ID_AD7091R] = {
|
||||||
|
@ -209,6 +214,18 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
|
||||||
.channel[0] = ADC081S_CHAN(12),
|
.channel[0] = ADC081S_CHAN(12),
|
||||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
.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 = {
|
static const struct iio_info ad7476_info = {
|
||||||
|
@ -314,6 +331,9 @@ static const struct spi_device_id ad7476_id[] = {
|
||||||
{"adc081s", ID_ADC081S},
|
{"adc081s", ID_ADC081S},
|
||||||
{"adc101s", ID_ADC101S},
|
{"adc101s", ID_ADC101S},
|
||||||
{"adc121s", ID_ADC121S},
|
{"adc121s", ID_ADC121S},
|
||||||
|
{"ads7866", ID_ADS7866},
|
||||||
|
{"ads7867", ID_ADS7867},
|
||||||
|
{"ads7868", ID_ADS7868},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(spi, ad7476_id);
|
MODULE_DEVICE_TABLE(spi, ad7476_id);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/gpio/consumer.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
|
@ -59,6 +60,18 @@
|
||||||
#define AD7768_REG_DIG_DIAG_STATUS 0x30
|
#define AD7768_REG_DIG_DIAG_STATUS 0x30
|
||||||
#define AD7768_REG_MCLK_COUNTER 0x31
|
#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 */
|
/* AD7768_REG_CONVERSION */
|
||||||
#define AD7768_CONV_MODE_MSK GENMASK(2, 0)
|
#define AD7768_CONV_MODE_MSK GENMASK(2, 0)
|
||||||
#define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x)
|
#define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x)
|
||||||
|
@ -80,11 +93,51 @@ enum ad7768_pwrmode {
|
||||||
AD7768_FAST_MODE = 3
|
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[] = {
|
static const struct iio_chan_spec ad7768_channels[] = {
|
||||||
{
|
{
|
||||||
.type = IIO_VOLTAGE,
|
.type = IIO_VOLTAGE,
|
||||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||||
|
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||||
.indexed = 1,
|
.indexed = 1,
|
||||||
.channel = 0,
|
.channel = 0,
|
||||||
.scan_index = 0,
|
.scan_index = 0,
|
||||||
|
@ -102,8 +155,12 @@ struct ad7768_state {
|
||||||
struct spi_device *spi;
|
struct spi_device *spi;
|
||||||
struct regulator *vref;
|
struct regulator *vref;
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
|
struct clk *mclk;
|
||||||
|
unsigned int mclk_freq;
|
||||||
|
unsigned int samp_freq;
|
||||||
struct completion completion;
|
struct completion completion;
|
||||||
struct iio_trigger *trig;
|
struct iio_trigger *trig;
|
||||||
|
struct gpio_desc *gpio_sync_in;
|
||||||
/*
|
/*
|
||||||
* DMA (thus cache coherency maintenance) requires the
|
* DMA (thus cache coherency maintenance) requires the
|
||||||
* transfer buffers to live in their own cache lines.
|
* transfer buffers to live in their own cache lines.
|
||||||
|
@ -210,6 +267,90 @@ err_unlock:
|
||||||
return ret;
|
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,
|
static int ad7768_read_raw(struct iio_dev *indio_dev,
|
||||||
struct iio_chan_spec const *chan,
|
struct iio_chan_spec const *chan,
|
||||||
int *val, int *val2, long info)
|
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;
|
*val2 = chan->scan_type.realbits;
|
||||||
|
|
||||||
return IIO_VAL_FRACTIONAL_LOG2;
|
return IIO_VAL_FRACTIONAL_LOG2;
|
||||||
|
|
||||||
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||||
|
*val = st->samp_freq;
|
||||||
|
|
||||||
|
return IIO_VAL_INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -EINVAL;
|
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 = {
|
static const struct iio_info ad7768_info = {
|
||||||
|
.attrs = &ad7768_group,
|
||||||
.read_raw = &ad7768_read_raw,
|
.read_raw = &ad7768_read_raw,
|
||||||
|
.write_raw = &ad7768_write_raw,
|
||||||
.debugfs_reg_access = &ad7768_reg_access,
|
.debugfs_reg_access = &ad7768_reg_access,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -270,9 +441,13 @@ static int ad7768_setup(struct ad7768_state *st)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* Set power mode to fast */
|
st->gpio_sync_in = devm_gpiod_get(&st->spi->dev, "adi,sync-in",
|
||||||
return ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK,
|
GPIOD_OUT_LOW);
|
||||||
AD7768_FAST_MODE);
|
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)
|
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);
|
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)
|
static int ad7768_probe(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct ad7768_state *st;
|
struct ad7768_state *st;
|
||||||
|
@ -383,6 +565,20 @@ static int ad7768_probe(struct spi_device *spi)
|
||||||
if (ret)
|
if (ret)
|
||||||
return 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);
|
spi_set_drvdata(spi, indio_dev);
|
||||||
mutex_init(&st->lock);
|
mutex_init(&st->lock);
|
||||||
|
|
||||||
|
|
|
@ -115,6 +115,7 @@
|
||||||
#define MAX_ADC_V2_CHANNELS 10
|
#define MAX_ADC_V2_CHANNELS 10
|
||||||
#define MAX_ADC_V1_CHANNELS 8
|
#define MAX_ADC_V1_CHANNELS 8
|
||||||
#define MAX_EXYNOS3250_ADC_CHANNELS 2
|
#define MAX_EXYNOS3250_ADC_CHANNELS 2
|
||||||
|
#define MAX_EXYNOS4212_ADC_CHANNELS 4
|
||||||
#define MAX_S5PV210_ADC_CHANNELS 10
|
#define MAX_S5PV210_ADC_CHANNELS 10
|
||||||
|
|
||||||
/* Bit definitions common for ADC_V1 and ADC_V2 */
|
/* 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));
|
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 = {
|
static const struct exynos_adc_data exynos_adc_v1_data = {
|
||||||
.num_channels = MAX_ADC_V1_CHANNELS,
|
.num_channels = MAX_ADC_V1_CHANNELS,
|
||||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
.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",
|
.compatible = "samsung,s5pv210-adc",
|
||||||
.data = &exynos_adc_s5pv210_data,
|
.data = &exynos_adc_s5pv210_data,
|
||||||
|
}, {
|
||||||
|
.compatible = "samsung,exynos4212-adc",
|
||||||
|
.data = &exynos4212_adc_data,
|
||||||
}, {
|
}, {
|
||||||
.compatible = "samsung,exynos-adc-v1",
|
.compatible = "samsung,exynos-adc-v1",
|
||||||
.data = &exynos_adc_v1_data,
|
.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 iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||||
struct exynos_adc *info = iio_priv(indio_dev);
|
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);
|
free_irq(info->tsirq, info);
|
||||||
input_unregister_device(info->input);
|
input_unregister_device(info->input);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
* lpc32xx_adc.c - Support for ADC in LPC32XX
|
||||||
*
|
*
|
||||||
* 3-channel, 10-bit ADC
|
* 3-channel, 10-bit ADC
|
||||||
*
|
*
|
||||||
* Copyright (C) 2011, 2012 Roland Stigge <stigge@antcom.de>
|
* 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>
|
#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);
|
ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&priv->spi->dev, "Start converions failed\n");
|
dev_err(&priv->spi->dev, "Start conversions failed\n");
|
||||||
goto out;
|
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);
|
ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&priv->spi->dev, "Stop converions failed\n");
|
dev_err(&priv->spi->dev, "Stop conversions failed\n");
|
||||||
goto out;
|
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);
|
ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV);
|
||||||
if (ret)
|
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);
|
buffer[j] = ads124s_read(indio_dev, scan_index);
|
||||||
ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV);
|
ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV);
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_err(&priv->spi->dev, "Stop ADC converions failed\n");
|
dev_err(&priv->spi->dev, "Stop ADC conversions failed\n");
|
||||||
|
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,16 @@ config IAQCORE
|
||||||
iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds)
|
iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds)
|
||||||
sensors
|
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
|
config SPS30
|
||||||
tristate "SPS30 particulate matter sensor"
|
tristate "SPS30 particulate matter sensor"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
|
|
@ -9,6 +9,7 @@ obj-$(CONFIG_BME680_I2C) += bme680_i2c.o
|
||||||
obj-$(CONFIG_BME680_SPI) += bme680_spi.o
|
obj-$(CONFIG_BME680_SPI) += bme680_spi.o
|
||||||
obj-$(CONFIG_CCS811) += ccs811.o
|
obj-$(CONFIG_CCS811) += ccs811.o
|
||||||
obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
|
obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
|
||||||
|
obj-$(CONFIG_PMS7003) += pms7003.o
|
||||||
obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o
|
obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o
|
||||||
obj-$(CONFIG_SPS30) += sps30.o
|
obj-$(CONFIG_SPS30) += sps30.o
|
||||||
obj-$(CONFIG_VZ89X) += vz89x.o
|
obj-$(CONFIG_VZ89X) += vz89x.o
|
||||||
|
|
|
@ -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:
|
case SPS30_READ_AUTO_CLEANING_PERIOD:
|
||||||
buf[0] = SPS30_AUTO_CLEANING_PERIOD >> 8;
|
buf[0] = SPS30_AUTO_CLEANING_PERIOD >> 8;
|
||||||
buf[1] = (u8)SPS30_AUTO_CLEANING_PERIOD;
|
buf[1] = (u8)SPS30_AUTO_CLEANING_PERIOD;
|
||||||
|
/* fall through */
|
||||||
case SPS30_READ_DATA_READY_FLAG:
|
case SPS30_READ_DATA_READY_FLAG:
|
||||||
case SPS30_READ_DATA:
|
case SPS30_READ_DATA:
|
||||||
case SPS30_READ_SERIAL:
|
case SPS30_READ_SERIAL:
|
||||||
|
@ -210,7 +211,7 @@ static int sps30_do_meas(struct sps30_state *state, s32 *data, int size)
|
||||||
msleep_interruptible(300);
|
msleep_interruptible(300);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tries)
|
if (tries == -1)
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
|
|
||||||
ret = sps30_do_cmd(state, SPS30_READ_DATA, tmp, sizeof(int) * size);
|
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;
|
*val2 = 10000;
|
||||||
|
|
||||||
return IIO_VAL_INT_PLUS_MICRO;
|
return IIO_VAL_INT_PLUS_MICRO;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
|
@ -375,6 +375,16 @@ config TI_DAC7311
|
||||||
|
|
||||||
If compiled as a module, it will be called 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
|
config VF610_DAC
|
||||||
tristate "Vybrid vf610 DAC driver"
|
tristate "Vybrid vf610 DAC driver"
|
||||||
depends on OF
|
depends on OF
|
||||||
|
|
|
@ -41,4 +41,5 @@ obj-$(CONFIG_STM32_DAC) += stm32-dac.o
|
||||||
obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o
|
obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o
|
||||||
obj-$(CONFIG_TI_DAC5571) += ti-dac5571.o
|
obj-$(CONFIG_TI_DAC5571) += ti-dac5571.o
|
||||||
obj-$(CONFIG_TI_DAC7311) += ti-dac7311.o
|
obj-$(CONFIG_TI_DAC7311) += ti-dac7311.o
|
||||||
|
obj-$(CONFIG_TI_DAC7612) += ti-dac7612.o
|
||||||
obj-$(CONFIG_VF610_DAC) += vf610_dac.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,
|
* AD5672R, AD5674R, AD5676, AD5676R, AD5679R,
|
||||||
* AD5681R, AD5682R, AD5683, AD5683R, AD5684,
|
* 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
|
* 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
|
* 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,
|
* AD5671R, AD5675R, AD5691R, AD5692R, AD5693, AD5693R,
|
||||||
* AD5694, AD5694R, AD5695R, AD5696, AD5696R
|
* 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
|
* AD5758 Digital to analog converters driver
|
||||||
*
|
*
|
||||||
|
|
|
@ -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_
|
#ifndef BMI160_H_
|
||||||
#define 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;
|
extern const struct regmap_config bmi160_regmap_config;
|
||||||
|
|
||||||
int bmi160_core_probe(struct device *dev, struct regmap *regmap,
|
int bmi160_core_probe(struct device *dev, struct regmap *regmap,
|
||||||
const char *name, bool use_spi);
|
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_ */
|
#endif /* BMI160_H_ */
|
||||||
|
|
|
@ -1,26 +1,27 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* BMI160 - Bosch IMU (accel, gyro plus external magnetometer)
|
* BMI160 - Bosch IMU (accel, gyro plus external magnetometer)
|
||||||
*
|
*
|
||||||
* Copyright (c) 2016, Intel Corporation.
|
* Copyright (c) 2016, Intel Corporation.
|
||||||
*
|
* Copyright (c) 2019, Martin Kelly.
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* IIO core driver for BMI160, with support for I2C/SPI busses
|
* 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/module.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/of_irq.h>
|
||||||
|
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/iio/triggered_buffer.h>
|
#include <linux/iio/triggered_buffer.h>
|
||||||
#include <linux/iio/trigger_consumer.h>
|
#include <linux/iio/trigger_consumer.h>
|
||||||
#include <linux/iio/buffer.h>
|
#include <linux/iio/buffer.h>
|
||||||
#include <linux/iio/sysfs.h>
|
#include <linux/iio/sysfs.h>
|
||||||
|
#include <linux/iio/trigger.h>
|
||||||
|
|
||||||
#include "bmi160.h"
|
#include "bmi160.h"
|
||||||
|
|
||||||
|
@ -64,8 +65,32 @@
|
||||||
#define BMI160_CMD_GYRO_PM_FAST_STARTUP 0x17
|
#define BMI160_CMD_GYRO_PM_FAST_STARTUP 0x17
|
||||||
#define BMI160_CMD_SOFTRESET 0xB6
|
#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_REG_DUMMY 0x7F
|
||||||
|
|
||||||
|
#define BMI160_NORMAL_WRITE_USLEEP 2
|
||||||
|
#define BMI160_SUSPENDED_WRITE_USLEEP 450
|
||||||
|
|
||||||
#define BMI160_ACCEL_PMU_MIN_USLEEP 3800
|
#define BMI160_ACCEL_PMU_MIN_USLEEP 3800
|
||||||
#define BMI160_GYRO_PMU_MIN_USLEEP 80000
|
#define BMI160_GYRO_PMU_MIN_USLEEP 80000
|
||||||
#define BMI160_SOFTRESET_USLEEP 1000
|
#define BMI160_SOFTRESET_USLEEP 1000
|
||||||
|
@ -108,8 +133,9 @@ enum bmi160_sensor_type {
|
||||||
BMI160_NUM_SENSORS /* must be last */
|
BMI160_NUM_SENSORS /* must be last */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bmi160_data {
|
enum bmi160_int_pin {
|
||||||
struct regmap *regmap;
|
BMI160_PIN_INT1,
|
||||||
|
BMI160_PIN_INT2
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct regmap_config bmi160_regmap_config = {
|
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;
|
cmd = bmi160_regs[t].pmu_cmd_suspend;
|
||||||
|
|
||||||
ret = regmap_write(data->regmap, BMI160_REG_CMD, cmd);
|
ret = regmap_write(data->regmap, BMI160_REG_CMD, cmd);
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
usleep_range(bmi160_pmu_time[t], bmi160_pmu_time[t] + 1000);
|
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;
|
int i, ret, val;
|
||||||
|
|
||||||
ret = regmap_read(data->regmap, bmi160_regs[t].range, &val);
|
ret = regmap_read(data->regmap, bmi160_regs[t].range, &val);
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
for (i = 0; i < bmi160_scale_table[t].num; i++)
|
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);
|
reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(sample);
|
||||||
|
|
||||||
ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(sample));
|
ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(sample));
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
*val = sign_extend32(le16_to_cpu(sample), 15);
|
*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;
|
int i, val, ret;
|
||||||
|
|
||||||
ret = regmap_read(data->regmap, bmi160_regs[t].config, &val);
|
ret = regmap_read(data->regmap, bmi160_regs[t].config, &val);
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
val &= bmi160_regs[t].config_odr_mask;
|
val &= bmi160_regs[t].config_odr_mask;
|
||||||
|
@ -394,13 +420,12 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p)
|
||||||
indio_dev->masklength) {
|
indio_dev->masklength) {
|
||||||
ret = regmap_bulk_read(data->regmap, base + i * sizeof(sample),
|
ret = regmap_bulk_read(data->regmap, base + i * sizeof(sample),
|
||||||
&sample, sizeof(sample));
|
&sample, sizeof(sample));
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
goto done;
|
goto done;
|
||||||
buf[j++] = sample;
|
buf[j++] = sample;
|
||||||
}
|
}
|
||||||
|
|
||||||
iio_push_to_buffers_with_timestamp(indio_dev, buf,
|
iio_push_to_buffers_with_timestamp(indio_dev, buf, pf->timestamp);
|
||||||
iio_get_time_ns(indio_dev));
|
|
||||||
done:
|
done:
|
||||||
iio_trigger_notify_done(indio_dev->trig);
|
iio_trigger_notify_done(indio_dev->trig);
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
|
@ -416,18 +441,18 @@ static int bmi160_read_raw(struct iio_dev *indio_dev,
|
||||||
switch (mask) {
|
switch (mask) {
|
||||||
case IIO_CHAN_INFO_RAW:
|
case IIO_CHAN_INFO_RAW:
|
||||||
ret = bmi160_get_data(data, chan->type, chan->channel2, val);
|
ret = bmi160_get_data(data, chan->type, chan->channel2, val);
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
return IIO_VAL_INT;
|
return IIO_VAL_INT;
|
||||||
case IIO_CHAN_INFO_SCALE:
|
case IIO_CHAN_INFO_SCALE:
|
||||||
*val = 0;
|
*val = 0;
|
||||||
ret = bmi160_get_scale(data,
|
ret = bmi160_get_scale(data,
|
||||||
bmi160_to_sensor(chan->type), val2);
|
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:
|
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||||
ret = bmi160_get_odr(data, bmi160_to_sensor(chan->type),
|
ret = bmi160_get_odr(data, bmi160_to_sensor(chan->type),
|
||||||
val, val2);
|
val, val2);
|
||||||
return ret < 0 ? ret : IIO_VAL_INT_PLUS_MICRO;
|
return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -498,6 +523,186 @@ static const char *bmi160_match_acpi_device(struct device *dev)
|
||||||
return dev_name(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)
|
static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
|
||||||
{
|
{
|
||||||
int ret;
|
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);
|
struct device *dev = regmap_get_device(data->regmap);
|
||||||
|
|
||||||
ret = regmap_write(data->regmap, BMI160_REG_CMD, BMI160_CMD_SOFTRESET);
|
ret = regmap_write(data->regmap, BMI160_REG_CMD, BMI160_CMD_SOFTRESET);
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
usleep_range(BMI160_SOFTRESET_USLEEP, BMI160_SOFTRESET_USLEEP + 1);
|
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) {
|
if (use_spi) {
|
||||||
ret = regmap_read(data->regmap, BMI160_REG_DUMMY, &val);
|
ret = regmap_read(data->regmap, BMI160_REG_DUMMY, &val);
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = regmap_read(data->regmap, BMI160_REG_CHIP_ID, &val);
|
ret = regmap_read(data->regmap, BMI160_REG_CHIP_ID, &val);
|
||||||
if (ret < 0) {
|
if (ret) {
|
||||||
dev_err(dev, "Error reading chip id\n");
|
dev_err(dev, "Error reading chip id\n");
|
||||||
return ret;
|
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);
|
ret = bmi160_set_mode(data, BMI160_ACCEL, true);
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = bmi160_set_mode(data, BMI160_GYRO, true);
|
ret = bmi160_set_mode(data, BMI160_GYRO, true);
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return 0;
|
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)
|
static void bmi160_chip_uninit(void *data)
|
||||||
{
|
{
|
||||||
struct bmi160_data *bmi_data = 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 iio_dev *indio_dev;
|
||||||
struct bmi160_data *data;
|
struct bmi160_data *data;
|
||||||
|
int irq;
|
||||||
|
enum bmi160_int_pin int_pin;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
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;
|
data->regmap = regmap;
|
||||||
|
|
||||||
ret = bmi160_chip_init(data, use_spi);
|
ret = bmi160_chip_init(data, use_spi);
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = devm_add_action_or_reset(dev, bmi160_chip_uninit, data);
|
ret = devm_add_action_or_reset(dev, bmi160_chip_uninit, data);
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (!name && ACPI_HANDLE(dev))
|
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->modes = INDIO_DIRECT_MODE;
|
||||||
indio_dev->info = &bmi160_info;
|
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);
|
bmi160_trigger_handler, NULL);
|
||||||
if (ret < 0)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = devm_iio_device_register(dev, indio_dev);
|
irq = bmi160_get_irq(dev->of_node, &int_pin);
|
||||||
if (ret < 0)
|
if (irq > 0) {
|
||||||
return ret;
|
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);
|
EXPORT_SYMBOL_GPL(bmi160_core_probe);
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* BMI160 - Bosch IMU, I2C bits
|
* BMI160 - Bosch IMU, I2C bits
|
||||||
*
|
*
|
||||||
* Copyright (c) 2016, Intel Corporation.
|
* 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:
|
* 7-bit I2C slave address is:
|
||||||
* - 0x68 if SDO is pulled to GND
|
* - 0x68 if SDO is pulled to GND
|
||||||
* - 0x69 if SDO is pulled to VDDIO
|
* - 0x69 if SDO is pulled to VDDIO
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* BMI160 - Bosch IMU, SPI bits
|
* BMI160 - Bosch IMU, SPI bits
|
||||||
*
|
*
|
||||||
* Copyright (c) 2016, Intel Corporation.
|
* 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/acpi.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
|
|
@ -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