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:
Greg Kroah-Hartman 2019-02-13 08:24:50 +01:00
commit 277c8e8b81
35 changed files with 1601 additions and 85 deletions

View File

@ -0,0 +1,48 @@
* Ingenic JZ47xx ADC controller IIO bindings
Required properties:
- compatible: Should be one of:
* ingenic,jz4725b-adc
* ingenic,jz4740-adc
- reg: ADC controller registers location and length.
- clocks: phandle to the SoC's ADC clock.
- clock-names: Must be set to "adc".
- #io-channel-cells: Must be set to <1> to indicate channels are selected
by index.
ADC clients must use the format described in iio-bindings.txt, giving
a phandle and IIO specifier pair ("io-channels") to the ADC controller.
Example:
#include <dt-bindings/iio/adc/ingenic,adc.h>
adc: adc@10070000 {
compatible = "ingenic,jz4740-adc";
#io-channel-cells = <1>;
reg = <0x10070000 0x30>;
clocks = <&cgu JZ4740_CLK_ADC>;
clock-names = "adc";
interrupt-parent = <&intc>;
interrupts = <18>;
};
adc-keys {
...
compatible = "adc-keys";
io-channels = <&adc INGENIC_ADC_AUX>;
io-channel-names = "buttons";
...
};
battery {
...
compatible = "ingenic,jz4740-battery";
io-channels = <&adc INGENIC_ADC_BATTERY>;
io-channel-names = "battery";
...
};

View File

@ -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>;
};

View File

@ -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

View File

@ -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 = <&reg_vcc5v0>;
};
};

View File

@ -0,0 +1,28 @@
* Texas Instruments Dual, 12-Bit Serial Input Digital-to-Analog Converter
The DAC7612 is a dual, 12-bit digital-to-analog converter (DAC) with guaranteed
12-bit monotonicity performance over the industrial temperature range.
Is is programmable through an SPI interface.
The internal DACs are loaded when the LOADDACS pin is pulled down.
http://www.ti.com/lit/ds/sbas106/sbas106.pdf
Required Properties:
- compatible: Should be one of:
"ti,dac7612"
"ti,dac7612u"
"ti,dac7612ub"
- reg: Definition as per Documentation/devicetree/bindings/spi/spi-bus.txt
Optional Properties:
- ti,loaddacs-gpios: GPIO descriptor for the LOADDACS pin.
- spi-*: Definition as per Documentation/devicetree/bindings/spi/spi-bus.txt
Example:
dac@1 {
compatible = "ti,dac7612";
reg = <0x1>;
ti,loaddacs-gpios = <&msmgpio 25 GPIO_ACTIVE_LOW>;
};

View File

@ -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";
}; };

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);
} }

View File

@ -0,0 +1,364 @@
// SPDX-License-Identifier: GPL-2.0
/*
* ADC driver for the Ingenic JZ47xx SoCs
* Copyright (c) 2019 Artur Rojek <contact@artur-rojek.eu>
*
* based on drivers/mfd/jz4740-adc.c
*/
#include <dt-bindings/iio/adc/ingenic,adc.h>
#include <linux/clk.h>
#include <linux/iio/iio.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#define JZ_ADC_REG_ENABLE 0x00
#define JZ_ADC_REG_CFG 0x04
#define JZ_ADC_REG_CTRL 0x08
#define JZ_ADC_REG_STATUS 0x0c
#define JZ_ADC_REG_ADTCH 0x18
#define JZ_ADC_REG_ADBDAT 0x1c
#define JZ_ADC_REG_ADSDAT 0x20
#define JZ_ADC_REG_CFG_BAT_MD BIT(4)
#define JZ_ADC_AUX_VREF 3300
#define JZ_ADC_AUX_VREF_BITS 12
#define JZ_ADC_BATTERY_LOW_VREF 2500
#define JZ_ADC_BATTERY_LOW_VREF_BITS 12
#define JZ4725B_ADC_BATTERY_HIGH_VREF 7500
#define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS 10
#define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986)
#define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12
struct ingenic_adc_soc_data {
unsigned int battery_high_vref;
unsigned int battery_high_vref_bits;
const int *battery_raw_avail;
size_t battery_raw_avail_size;
const int *battery_scale_avail;
size_t battery_scale_avail_size;
};
struct ingenic_adc {
void __iomem *base;
struct clk *clk;
struct mutex lock;
const struct ingenic_adc_soc_data *soc_data;
bool low_vref_mode;
};
static void ingenic_adc_set_config(struct ingenic_adc *adc,
uint32_t mask,
uint32_t val)
{
uint32_t cfg;
clk_enable(adc->clk);
mutex_lock(&adc->lock);
cfg = readl(adc->base + JZ_ADC_REG_CFG) & ~mask;
cfg |= val;
writel(cfg, adc->base + JZ_ADC_REG_CFG);
mutex_unlock(&adc->lock);
clk_disable(adc->clk);
}
static void ingenic_adc_enable(struct ingenic_adc *adc,
int engine,
bool enabled)
{
u8 val;
mutex_lock(&adc->lock);
val = readb(adc->base + JZ_ADC_REG_ENABLE);
if (enabled)
val |= BIT(engine);
else
val &= ~BIT(engine);
writeb(val, adc->base + JZ_ADC_REG_ENABLE);
mutex_unlock(&adc->lock);
}
static int ingenic_adc_capture(struct ingenic_adc *adc,
int engine)
{
u8 val;
int ret;
ingenic_adc_enable(adc, engine, true);
ret = readb_poll_timeout(adc->base + JZ_ADC_REG_ENABLE, val,
!(val & BIT(engine)), 250, 1000);
if (ret)
ingenic_adc_enable(adc, engine, false);
return ret;
}
static int ingenic_adc_write_raw(struct iio_dev *iio_dev,
struct iio_chan_spec const *chan,
int val,
int val2,
long m)
{
struct ingenic_adc *adc = iio_priv(iio_dev);
switch (m) {
case IIO_CHAN_INFO_SCALE:
switch (chan->channel) {
case INGENIC_ADC_BATTERY:
if (val > JZ_ADC_BATTERY_LOW_VREF) {
ingenic_adc_set_config(adc,
JZ_ADC_REG_CFG_BAT_MD,
0);
adc->low_vref_mode = false;
} else {
ingenic_adc_set_config(adc,
JZ_ADC_REG_CFG_BAT_MD,
JZ_ADC_REG_CFG_BAT_MD);
adc->low_vref_mode = true;
}
return 0;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static const int jz4725b_adc_battery_raw_avail[] = {
0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1,
};
static const int jz4725b_adc_battery_scale_avail[] = {
JZ4725B_ADC_BATTERY_HIGH_VREF, JZ4725B_ADC_BATTERY_HIGH_VREF_BITS,
JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS,
};
static const int jz4740_adc_battery_raw_avail[] = {
0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1,
};
static const int jz4740_adc_battery_scale_avail[] = {
JZ4740_ADC_BATTERY_HIGH_VREF, JZ4740_ADC_BATTERY_HIGH_VREF_BITS,
JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS,
};
static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = {
.battery_high_vref = JZ4725B_ADC_BATTERY_HIGH_VREF,
.battery_high_vref_bits = JZ4725B_ADC_BATTERY_HIGH_VREF_BITS,
.battery_raw_avail = jz4725b_adc_battery_raw_avail,
.battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail),
.battery_scale_avail = jz4725b_adc_battery_scale_avail,
.battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail),
};
static const struct ingenic_adc_soc_data jz4740_adc_soc_data = {
.battery_high_vref = JZ4740_ADC_BATTERY_HIGH_VREF,
.battery_high_vref_bits = JZ4740_ADC_BATTERY_HIGH_VREF_BITS,
.battery_raw_avail = jz4740_adc_battery_raw_avail,
.battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail),
.battery_scale_avail = jz4740_adc_battery_scale_avail,
.battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail),
};
static int ingenic_adc_read_avail(struct iio_dev *iio_dev,
struct iio_chan_spec const *chan,
const int **vals,
int *type,
int *length,
long m)
{
struct ingenic_adc *adc = iio_priv(iio_dev);
switch (m) {
case IIO_CHAN_INFO_RAW:
*type = IIO_VAL_INT;
*length = adc->soc_data->battery_raw_avail_size;
*vals = adc->soc_data->battery_raw_avail;
return IIO_AVAIL_RANGE;
case IIO_CHAN_INFO_SCALE:
*type = IIO_VAL_FRACTIONAL_LOG2;
*length = adc->soc_data->battery_scale_avail_size;
*vals = adc->soc_data->battery_scale_avail;
return IIO_AVAIL_LIST;
default:
return -EINVAL;
};
}
static int ingenic_adc_read_raw(struct iio_dev *iio_dev,
struct iio_chan_spec const *chan,
int *val,
int *val2,
long m)
{
struct ingenic_adc *adc = iio_priv(iio_dev);
int ret;
switch (m) {
case IIO_CHAN_INFO_RAW:
clk_enable(adc->clk);
ret = ingenic_adc_capture(adc, chan->channel);
if (ret) {
clk_disable(adc->clk);
return ret;
}
switch (chan->channel) {
case INGENIC_ADC_AUX:
*val = readw(adc->base + JZ_ADC_REG_ADSDAT);
break;
case INGENIC_ADC_BATTERY:
*val = readw(adc->base + JZ_ADC_REG_ADBDAT);
break;
}
clk_disable(adc->clk);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
switch (chan->channel) {
case INGENIC_ADC_AUX:
*val = JZ_ADC_AUX_VREF;
*val2 = JZ_ADC_AUX_VREF_BITS;
break;
case INGENIC_ADC_BATTERY:
if (adc->low_vref_mode) {
*val = JZ_ADC_BATTERY_LOW_VREF;
*val2 = JZ_ADC_BATTERY_LOW_VREF_BITS;
} else {
*val = adc->soc_data->battery_high_vref;
*val2 = adc->soc_data->battery_high_vref_bits;
}
break;
}
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
}
static void ingenic_adc_clk_cleanup(void *data)
{
clk_unprepare(data);
}
static const struct iio_info ingenic_adc_info = {
.write_raw = ingenic_adc_write_raw,
.read_raw = ingenic_adc_read_raw,
.read_avail = ingenic_adc_read_avail,
};
static const struct iio_chan_spec ingenic_channels[] = {
{
.extend_name = "aux",
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.indexed = 1,
.channel = INGENIC_ADC_AUX,
},
{
.extend_name = "battery",
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.indexed = 1,
.channel = INGENIC_ADC_BATTERY,
},
};
static int ingenic_adc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct iio_dev *iio_dev;
struct ingenic_adc *adc;
struct resource *mem_base;
const struct ingenic_adc_soc_data *soc_data;
int ret;
soc_data = device_get_match_data(dev);
if (!soc_data)
return -EINVAL;
iio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
if (!iio_dev)
return -ENOMEM;
adc = iio_priv(iio_dev);
mutex_init(&adc->lock);
adc->soc_data = soc_data;
mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
adc->base = devm_ioremap_resource(dev, mem_base);
if (IS_ERR(adc->base)) {
dev_err(dev, "Unable to ioremap mmio resource\n");
return PTR_ERR(adc->base);
}
adc->clk = devm_clk_get(dev, "adc");
if (IS_ERR(adc->clk)) {
dev_err(dev, "Unable to get clock\n");
return PTR_ERR(adc->clk);
}
ret = clk_prepare_enable(adc->clk);
if (ret) {
dev_err(dev, "Failed to enable clock\n");
return ret;
}
/* Put hardware in a known passive state. */
writeb(0x00, adc->base + JZ_ADC_REG_ENABLE);
writeb(0xff, adc->base + JZ_ADC_REG_CTRL);
clk_disable(adc->clk);
ret = devm_add_action_or_reset(dev, ingenic_adc_clk_cleanup, adc->clk);
if (ret) {
dev_err(dev, "Unable to add action\n");
return ret;
}
iio_dev->dev.parent = dev;
iio_dev->name = "jz-adc";
iio_dev->modes = INDIO_DIRECT_MODE;
iio_dev->channels = ingenic_channels;
iio_dev->num_channels = ARRAY_SIZE(ingenic_channels);
iio_dev->info = &ingenic_adc_info;
ret = devm_iio_device_register(dev, iio_dev);
if (ret)
dev_err(dev, "Unable to register IIO device\n");
return ret;
}
#ifdef CONFIG_OF
static const struct of_device_id ingenic_adc_of_match[] = {
{ .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, },
{ .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, },
{ },
};
MODULE_DEVICE_TABLE(of, ingenic_adc_of_match);
#endif
static struct platform_driver ingenic_adc_driver = {
.driver = {
.name = "ingenic-adc",
.of_match_table = of_match_ptr(ingenic_adc_of_match),
},
.probe = ingenic_adc_probe,
};
module_platform_driver(ingenic_adc_driver);
MODULE_LICENSE("GPL v2");

View File

@ -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>

View File

@ -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++;
} }

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,340 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Plantower PMS7003 particulate matter sensor driver
*
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
*/
#include <asm/unaligned.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/serdev.h>
#define PMS7003_DRIVER_NAME "pms7003"
#define PMS7003_MAGIC 0x424d
/* last 2 data bytes hold frame checksum */
#define PMS7003_MAX_DATA_LENGTH 28
#define PMS7003_CHECKSUM_LENGTH 2
#define PMS7003_PM10_OFFSET 10
#define PMS7003_PM2P5_OFFSET 8
#define PMS7003_PM1_OFFSET 6
#define PMS7003_TIMEOUT msecs_to_jiffies(6000)
#define PMS7003_CMD_LENGTH 7
#define PMS7003_PM_MAX 1000
#define PMS7003_PM_MIN 0
enum {
PM1,
PM2P5,
PM10,
};
enum pms7003_cmd {
CMD_WAKEUP,
CMD_ENTER_PASSIVE_MODE,
CMD_READ_PASSIVE,
CMD_SLEEP,
};
/*
* commands have following format:
*
* +------+------+-----+------+-----+-----------+-----------+
* | 0x42 | 0x4d | cmd | 0x00 | arg | cksum msb | cksum lsb |
* +------+------+-----+------+-----+-----------+-----------+
*/
static const u8 pms7003_cmd_tbl[][PMS7003_CMD_LENGTH] = {
[CMD_WAKEUP] = { 0x42, 0x4d, 0xe4, 0x00, 0x01, 0x01, 0x74 },
[CMD_ENTER_PASSIVE_MODE] = { 0x42, 0x4d, 0xe1, 0x00, 0x00, 0x01, 0x70 },
[CMD_READ_PASSIVE] = { 0x42, 0x4d, 0xe2, 0x00, 0x00, 0x01, 0x71 },
[CMD_SLEEP] = { 0x42, 0x4d, 0xe4, 0x00, 0x00, 0x01, 0x73 },
};
struct pms7003_frame {
u8 data[PMS7003_MAX_DATA_LENGTH];
u16 expected_length;
u16 length;
};
struct pms7003_state {
struct serdev_device *serdev;
struct pms7003_frame frame;
struct completion frame_ready;
struct mutex lock; /* must be held whenever state gets touched */
};
static int pms7003_do_cmd(struct pms7003_state *state, enum pms7003_cmd cmd)
{
int ret;
ret = serdev_device_write(state->serdev, pms7003_cmd_tbl[cmd],
PMS7003_CMD_LENGTH, PMS7003_TIMEOUT);
if (ret < PMS7003_CMD_LENGTH)
return ret < 0 ? ret : -EIO;
ret = wait_for_completion_interruptible_timeout(&state->frame_ready,
PMS7003_TIMEOUT);
if (!ret)
ret = -ETIMEDOUT;
return ret < 0 ? ret : 0;
}
static u16 pms7003_get_pm(const u8 *data)
{
return clamp_val(get_unaligned_be16(data),
PMS7003_PM_MIN, PMS7003_PM_MAX);
}
static irqreturn_t pms7003_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct pms7003_state *state = iio_priv(indio_dev);
struct pms7003_frame *frame = &state->frame;
u16 data[3 + 1 + 4]; /* PM1, PM2P5, PM10, padding, timestamp */
int ret;
mutex_lock(&state->lock);
ret = pms7003_do_cmd(state, CMD_READ_PASSIVE);
if (ret) {
mutex_unlock(&state->lock);
goto err;
}
data[PM1] = pms7003_get_pm(frame->data + PMS7003_PM1_OFFSET);
data[PM2P5] = pms7003_get_pm(frame->data + PMS7003_PM2P5_OFFSET);
data[PM10] = pms7003_get_pm(frame->data + PMS7003_PM10_OFFSET);
mutex_unlock(&state->lock);
iio_push_to_buffers_with_timestamp(indio_dev, data,
iio_get_time_ns(indio_dev));
err:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int pms7003_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct pms7003_state *state = iio_priv(indio_dev);
struct pms7003_frame *frame = &state->frame;
int ret;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_MASSCONCENTRATION:
mutex_lock(&state->lock);
ret = pms7003_do_cmd(state, CMD_READ_PASSIVE);
if (ret) {
mutex_unlock(&state->lock);
return ret;
}
*val = pms7003_get_pm(frame->data + chan->address);
mutex_unlock(&state->lock);
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
return -EINVAL;
}
static const struct iio_info pms7003_info = {
.read_raw = pms7003_read_raw,
};
#define PMS7003_CHAN(_index, _mod, _addr) { \
.type = IIO_MASSCONCENTRATION, \
.modified = 1, \
.channel2 = IIO_MOD_ ## _mod, \
.address = _addr, \
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
.scan_index = _index, \
.scan_type = { \
.sign = 'u', \
.realbits = 10, \
.storagebits = 16, \
.endianness = IIO_CPU, \
}, \
}
static const struct iio_chan_spec pms7003_channels[] = {
PMS7003_CHAN(0, PM1, PMS7003_PM1_OFFSET),
PMS7003_CHAN(1, PM2P5, PMS7003_PM2P5_OFFSET),
PMS7003_CHAN(2, PM10, PMS7003_PM10_OFFSET),
IIO_CHAN_SOFT_TIMESTAMP(3),
};
static u16 pms7003_calc_checksum(struct pms7003_frame *frame)
{
u16 checksum = (PMS7003_MAGIC >> 8) + (u8)(PMS7003_MAGIC & 0xff) +
(frame->length >> 8) + (u8)frame->length;
int i;
for (i = 0; i < frame->length - PMS7003_CHECKSUM_LENGTH; i++)
checksum += frame->data[i];
return checksum;
}
static bool pms7003_frame_is_okay(struct pms7003_frame *frame)
{
int offset = frame->length - PMS7003_CHECKSUM_LENGTH;
u16 checksum = get_unaligned_be16(frame->data + offset);
return checksum == pms7003_calc_checksum(frame);
}
static int pms7003_receive_buf(struct serdev_device *serdev,
const unsigned char *buf, size_t size)
{
struct iio_dev *indio_dev = serdev_device_get_drvdata(serdev);
struct pms7003_state *state = iio_priv(indio_dev);
struct pms7003_frame *frame = &state->frame;
int num;
if (!frame->expected_length) {
u16 magic;
/* wait for SOF and data length */
if (size < 4)
return 0;
magic = get_unaligned_be16(buf);
if (magic != PMS7003_MAGIC)
return 2;
num = get_unaligned_be16(buf + 2);
if (num <= PMS7003_MAX_DATA_LENGTH) {
frame->expected_length = num;
frame->length = 0;
}
return 4;
}
num = min(size, (size_t)(frame->expected_length - frame->length));
memcpy(frame->data + frame->length, buf, num);
frame->length += num;
if (frame->length == frame->expected_length) {
if (pms7003_frame_is_okay(frame))
complete(&state->frame_ready);
frame->expected_length = 0;
}
return num;
}
static const struct serdev_device_ops pms7003_serdev_ops = {
.receive_buf = pms7003_receive_buf,
.write_wakeup = serdev_device_write_wakeup,
};
static void pms7003_stop(void *data)
{
struct pms7003_state *state = data;
pms7003_do_cmd(state, CMD_SLEEP);
}
static const unsigned long pms7003_scan_masks[] = { 0x07, 0x00 };
static int pms7003_probe(struct serdev_device *serdev)
{
struct pms7003_state *state;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&serdev->dev, sizeof(*state));
if (!indio_dev)
return -ENOMEM;
state = iio_priv(indio_dev);
serdev_device_set_drvdata(serdev, indio_dev);
state->serdev = serdev;
indio_dev->dev.parent = &serdev->dev;
indio_dev->info = &pms7003_info;
indio_dev->name = PMS7003_DRIVER_NAME;
indio_dev->channels = pms7003_channels,
indio_dev->num_channels = ARRAY_SIZE(pms7003_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->available_scan_masks = pms7003_scan_masks;
mutex_init(&state->lock);
init_completion(&state->frame_ready);
serdev_device_set_client_ops(serdev, &pms7003_serdev_ops);
ret = devm_serdev_device_open(&serdev->dev, serdev);
if (ret)
return ret;
serdev_device_set_baudrate(serdev, 9600);
serdev_device_set_flow_control(serdev, false);
ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
if (ret)
return ret;
ret = pms7003_do_cmd(state, CMD_WAKEUP);
if (ret) {
dev_err(&serdev->dev, "failed to wakeup sensor\n");
return ret;
}
ret = pms7003_do_cmd(state, CMD_ENTER_PASSIVE_MODE);
if (ret) {
dev_err(&serdev->dev, "failed to enter passive mode\n");
return ret;
}
ret = devm_add_action_or_reset(&serdev->dev, pms7003_stop, state);
if (ret)
return ret;
ret = devm_iio_triggered_buffer_setup(&serdev->dev, indio_dev, NULL,
pms7003_trigger_handler, NULL);
if (ret)
return ret;
return devm_iio_device_register(&serdev->dev, indio_dev);
}
static const struct of_device_id pms7003_of_match[] = {
{ .compatible = "plantower,pms7003" },
{ }
};
MODULE_DEVICE_TABLE(of, pms7003_of_match);
static struct serdev_device_driver pms7003_driver = {
.driver = {
.name = PMS7003_DRIVER_NAME,
.of_match_table = pms7003_of_match,
},
.probe = pms7003_probe,
};
module_serdev_device_driver(pms7003_driver);
MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
MODULE_DESCRIPTION("Plantower PMS7003 particulate matter sensor driver");
MODULE_LICENSE("GPL v2");

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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
* *

View File

@ -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
* *

View File

@ -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

View File

@ -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
* *

View File

@ -0,0 +1,184 @@
// SPDX-License-Identifier: GPL-2.0
/*
* DAC7612 Dual, 12-Bit Serial input Digital-to-Analog Converter
*
* Copyright 2019 Qtechnology A/S
* 2019 Ricardo Ribalda <ricardo@ribalda.com>
*
* Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/gpio/consumer.h>
#include <linux/iio/iio.h>
#define DAC7612_RESOLUTION 12
#define DAC7612_ADDRESS 4
#define DAC7612_START 5
struct dac7612 {
struct spi_device *spi;
struct gpio_desc *loaddacs;
uint16_t cache[2];
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
*/
uint8_t data[2] ____cacheline_aligned;
};
static int dac7612_cmd_single(struct dac7612 *priv, int channel, u16 val)
{
int ret;
priv->data[0] = BIT(DAC7612_START) | (channel << DAC7612_ADDRESS);
priv->data[0] |= val >> 8;
priv->data[1] = val & 0xff;
priv->cache[channel] = val;
ret = spi_write(priv->spi, priv->data, sizeof(priv->data));
if (ret)
return ret;
gpiod_set_value(priv->loaddacs, 1);
gpiod_set_value(priv->loaddacs, 0);
return 0;
}
#define dac7612_CHANNEL(chan, name) { \
.type = IIO_VOLTAGE, \
.channel = (chan), \
.indexed = 1, \
.output = 1, \
.datasheet_name = name, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
}
static const struct iio_chan_spec dac7612_channels[] = {
dac7612_CHANNEL(0, "OUTA"),
dac7612_CHANNEL(1, "OUTB"),
};
static int dac7612_read_raw(struct iio_dev *iio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long mask)
{
struct dac7612 *priv;
switch (mask) {
case IIO_CHAN_INFO_RAW:
priv = iio_priv(iio_dev);
*val = priv->cache[chan->channel];
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 1;
return IIO_VAL_INT;
default:
return -EINVAL;
}
}
static int dac7612_write_raw(struct iio_dev *iio_dev,
const struct iio_chan_spec *chan,
int val, int val2, long mask)
{
struct dac7612 *priv = iio_priv(iio_dev);
int ret;
if (mask != IIO_CHAN_INFO_RAW)
return -EINVAL;
if ((val >= BIT(DAC7612_RESOLUTION)) || val < 0 || val2)
return -EINVAL;
if (val == priv->cache[chan->channel])
return 0;
mutex_lock(&iio_dev->mlock);
ret = dac7612_cmd_single(priv, chan->channel, val);
mutex_unlock(&iio_dev->mlock);
return ret;
}
static const struct iio_info dac7612_info = {
.read_raw = dac7612_read_raw,
.write_raw = dac7612_write_raw,
};
static int dac7612_probe(struct spi_device *spi)
{
struct iio_dev *iio_dev;
struct dac7612 *priv;
int i;
int ret;
iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
if (!iio_dev)
return -ENOMEM;
priv = iio_priv(iio_dev);
/*
* LOADDACS pin can be controlled by the driver or externally.
* When controlled by the driver, the DAC value is updated after
* every write.
* When the driver does not control the PIN, the user or an external
* event can change the value of all DACs by pulsing down the LOADDACs
* pin.
*/
priv->loaddacs = devm_gpiod_get_optional(&spi->dev, "ti,loaddacs",
GPIOD_OUT_LOW);
if (IS_ERR(priv->loaddacs))
return PTR_ERR(priv->loaddacs);
priv->spi = spi;
spi_set_drvdata(spi, iio_dev);
iio_dev->dev.parent = &spi->dev;
iio_dev->info = &dac7612_info;
iio_dev->modes = INDIO_DIRECT_MODE;
iio_dev->channels = dac7612_channels;
iio_dev->num_channels = ARRAY_SIZE(priv->cache);
iio_dev->name = spi_get_device_id(spi)->name;
for (i = 0; i < ARRAY_SIZE(priv->cache); i++) {
ret = dac7612_cmd_single(priv, i, 0);
if (ret)
return ret;
}
return devm_iio_device_register(&spi->dev, iio_dev);
}
static const struct spi_device_id dac7612_id[] = {
{"ti-dac7612"},
{}
};
MODULE_DEVICE_TABLE(spi, dac7612_id);
static const struct of_device_id dac7612_of_match[] = {
{ .compatible = "ti,dac7612" },
{ .compatible = "ti,dac7612u" },
{ .compatible = "ti,dac7612ub" },
{ },
};
MODULE_DEVICE_TABLE(of, dac7612_of_match);
static struct spi_driver dac7612_driver = {
.driver = {
.name = "ti-dac7612",
.of_match_table = dac7612_of_match,
},
.probe = dac7612_probe,
.id_table = dac7612_id,
};
module_spi_driver(dac7612_driver);
MODULE_AUTHOR("Ricardo Ribalda <ricardo@ribalda.com>");
MODULE_DESCRIPTION("Texas Instruments DAC7612 DAC driver");
MODULE_LICENSE("GPL v2");

View File

@ -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_ */

View File

@ -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);

View File

@ -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

View File

@ -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>

View File

@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _DT_BINDINGS_IIO_ADC_INGENIC_ADC_H
#define _DT_BINDINGS_IIO_ADC_INGENIC_ADC_H
/* ADC channel idx. */
#define INGENIC_ADC_AUX 0
#define INGENIC_ADC_BATTERY 1
#endif