1
0
Fork 0

Staging/IIO patches for 5.1-rc1

Here is the big staging/iio driver pull request for 5.1-rc1.
 
 Lots of good IIO driver updates and cleanups in here as always.
 Combined with the removal of the xgifb driver, we have a net "loss" of
 over 9000 lines in the pull request, always a nice thing.
 
 As the outreachy application process is currently happening, there are
 loads of tiny checkpatch cleanup fixes all over the staging tree, which
 accounts for the majority of the fixups.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXH+gLQ8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ymBiQCeJpoBhG+W3r+kP8w65ZY8qU+/liIAn0Tkl4/k
 IX1dQzCsEpO1jA8AHj6n
 =7wCH
 -----END PGP SIGNATURE-----

Merge tag 'staging-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging

Pull staging/IIO updates from Greg KH:
 "Here is the big staging/iio driver pull request for 5.1-rc1.

  Lots of good IIO driver updates and cleanups in here as always.
  Combined with the removal of the xgifb driver, we have a net "loss" of
  over 9000 lines in the pull request, always a nice thing.

  As the outreachy application process is currently happening, there are
  loads of tiny checkpatch cleanup fixes all over the staging tree,
  which accounts for the majority of the fixups"

* tag 'staging-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (341 commits)
  staging: mt7621-dma: remove license boilerplate text
  staging: mt7621-dma: add SPDX GPL-2.0+ license identifier
  Staging: ks7010: Replace typecast to int
  Staging: vt6655: Align a static function declaration
  staging: speakup: fix line over 80 characters.
  staging: mt7621-eth: Remove license boilerplate text
  staging: mt7621-eth: Add SPDX license identifier
  staging: ks7010: removed custom Michael MIC implementation.
  staging: rtl8192e: Fix space and suspect issue
  Staging: vt6655: Modify comment style of SPDX License Identifier
  Staging: vt6655: Modify comment style for SPDX-License-Identifier
  Staging: vt6655: Align a function declaration
  Staging: vt6655: Alignment of function declaration
  staging: rtl8712: Fix indentation issue
  staging: wilc1000: fix incorrent type in initializer
  staging: rtl8188eu: remove unused P2P_PRIVATE_IOCTL_SET_LEN
  staging: rtl8188eu: remove unused enum P2P_PROTO_WK_ID
  staging: rtl8723bs: Remove duplicated include from drv_types.h
  Staging: vt6655: Alignment should match open parenthesis
  staging: erofs: fix mis-acted TAIL merging behavior
  ...
hifive-unleashed-5.1
Linus Torvalds 2019-03-06 16:29:27 -08:00
commit e266ca36da
335 changed files with 9652 additions and 18673 deletions

View File

@ -1554,6 +1554,10 @@ What: /sys/bus/iio/devices/iio:deviceX/in_concentration_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_co2_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_ethanol_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_ethanol_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_h2_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_h2_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentration_voc_raw
What: /sys/bus/iio/devices/iio:deviceX/in_concentrationX_voc_raw
KernelVersion: 4.3
@ -1684,4 +1688,19 @@ KernelVersion: 4.18
Contact: linux-iio@vger.kernel.org
Description:
Raw (unscaled) phase difference reading from channel Y
that can be processed to radians.
that can be processed to radians.
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm1_input
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm1_input
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm2p5_input
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm2p5_input
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm4_input
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm4_input
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentration_pm10_input
What: /sys/bus/iio/devices/iio:deviceX/in_massconcentrationY_pm10_input
KernelVersion: 4.22
Contact: linux-iio@vger.kernel.org
Description:
Mass concentration reading of particulate matter in ug / m3.
pmX consists of particles with aerodynamic diameter less or
equal to X micrometers.

View File

@ -0,0 +1,28 @@
What: /sys/bus/iio/devices/iio:deviceX/start_cleaning
Date: December 2018
KernelVersion: 4.22
Contact: linux-iio@vger.kernel.org
Description:
Writing 1 starts sensor self cleaning. Internal fan accelerates
to its maximum speed and keeps spinning for about 10 seconds in
order to blow out accumulated dust.
What: /sys/bus/iio/devices/iio:deviceX/cleaning_period
Date: January 2019
KernelVersion: 5.1
Contact: linux-iio@vger.kernel.org
Description:
Sensor is capable of triggering self cleaning periodically.
Period can be changed by writing a new value here. Upon reading
the current one is returned. Units are seconds.
Writing 0 disables periodical self cleaning entirely.
What: /sys/bus/iio/devices/iio:deviceX/cleaning_period_available
Date: January 2019
KernelVersion: 5.1
Contact: linux-iio@vger.kernel.org
Description:
The range of available values in seconds represented as the
minimum value, the step and the maximum value, all enclosed in
square brackets.

View File

@ -20,6 +20,10 @@ Optional properties:
- interrupt-names: should contain "INT1" and/or "INT2", the accelerometer's
interrupt line in use.
- vdd-supply: phandle to the regulator that provides vdd power to the accelerometer.
- vddio-supply: phandle to the regulator that provides vddio power to the accelerometer.
Example:
mma8453fc@1d {

View File

@ -0,0 +1,65 @@
Analog Devices AD7606 Simultaneous Sampling ADC
Required properties for the AD7606:
- compatible: Must be one of
* "adi,ad7605-4"
* "adi,ad7606-8"
* "adi,ad7606-6"
* "adi,ad7606-4"
- reg: SPI chip select number for the device
- spi-max-frequency: Max SPI frequency to use
see: Documentation/devicetree/bindings/spi/spi-bus.txt
- spi-cpha: See Documentation/devicetree/bindings/spi/spi-bus.txt
- avcc-supply: phandle to the Avcc power supply
- interrupts: IRQ line for the ADC
see: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
- adi,conversion-start-gpios: must be the device tree identifier of the CONVST pin.
This logic input is used to initiate conversions on the analog
input channels. As the line is active high, it should be marked
GPIO_ACTIVE_HIGH.
Optional properties:
- reset-gpios: must be the device tree identifier of the RESET pin. If specified,
it will be asserted during driver probe. As the line is active high,
it should be marked GPIO_ACTIVE_HIGH.
- standby-gpios: must be the device tree identifier of the STBY pin. This pin is used
to place the AD7606 into one of two power-down modes, Standby mode or
Shutdown mode. As the line is active low, it should be marked
GPIO_ACTIVE_LOW.
- adi,first-data-gpios: must be the device tree identifier of the FRSTDATA pin.
The FRSTDATA output indicates when the first channel, V1, is
being read back on either the parallel, byte or serial interface.
As the line is active high, it should be marked GPIO_ACTIVE_HIGH.
- adi,range-gpios: must be the device tree identifier of the RANGE pin. The polarity on
this pin determines the input range of the analog input channels. If
this pin is tied to a logic high, the analog input range is ±10V for
all channels. If this pin is tied to a logic low, the analog input range
is ±5V for all channels. As the line is active high, it should be marked
GPIO_ACTIVE_HIGH.
- adi,oversampling-ratio-gpios: must be the device tree identifier of the over-sampling
mode pins. As the line is active high, it should be marked
GPIO_ACTIVE_HIGH.
Example:
adc@0 {
compatible = "adi,ad7606-8";
reg = <0>;
spi-max-frequency = <1000000>;
spi-cpol;
avcc-supply = <&adc_vref>;
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
interrupt-parent = <&gpio>;
adi,conversion-start-gpios = <&gpio 17 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio 27 GPIO_ACTIVE_HIGH>;
adi,first-data-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>;
adi,oversampling-ratio-gpios = <&gpio 18 GPIO_ACTIVE_HIGH
&gpio 23 GPIO_ACTIVE_HIGH
&gpio 26 GPIO_ACTIVE_HIGH>;
standby-gpios = <&gpio 24 GPIO_ACTIVE_LOW>;
};

View File

@ -0,0 +1,41 @@
Analog Devices AD7768-1 ADC device driver
Required properties for the AD7768-1:
- compatible: Must be "adi,ad7768-1"
- reg: SPI chip select number for the device
- spi-max-frequency: Max SPI frequency to use
see: Documentation/devicetree/bindings/spi/spi-bus.txt
- clocks: phandle to the master clock (mclk)
see: Documentation/devicetree/bindings/clock/clock-bindings.txt
- clock-names: Must be "mclk".
- interrupts: IRQ line for the ADC
see: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
- vref-supply: vref supply can be used as reference for conversion
- adi,sync-in-gpios: must be the device tree identifier of the SYNC-IN pin. Enables
synchronization of multiple devices that require simultaneous sampling.
A pulse is always required if the configuration is changed in any way, for example
if the filter decimation rate changes. As the line is active low, it should
be marked GPIO_ACTIVE_LOW.
Optional properties:
- reset-gpios : GPIO spec for the RESET pin. If specified, it will be asserted during
driver probe. As the line is active low, it should be marked GPIO_ACTIVE_LOW.
Example:
adc@0 {
compatible = "adi,ad7768-1";
reg = <0>;
spi-max-frequency = <2000000>;
spi-cpol;
spi-cpha;
vref-supply = <&adc_vref>;
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
interrupt-parent = <&gpio>;
adi,sync-in-gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
reset-gpios = <&gpio 27 GPIO_ACTIVE_LOW>;
clocks = <&ad7768_mclk>;
clock-names = "mclk";
};

View File

@ -23,6 +23,10 @@ Required properties:
- #io-channel-cells: must be 1, see ../iio-bindings.txt
Optional properties:
- amlogic,hhi-sysctrl: phandle to the syscon which contains the 5th bit
of the TSC (temperature sensor coefficient) on
Meson8b and Meson8m2 (which used to calibrate the
temperature sensor)
- nvmem-cells: phandle to the temperature_calib eFuse cells
- nvmem-cell-names: if present (to enable the temperature sensor
calibration) this must contain "temperature_calib"

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

@ -0,0 +1,24 @@
Nuvoton NPCM Analog to Digital Converter (ADC)
The NPCM ADC is a 10-bit converter for eight channel inputs.
Required properties:
- compatible: "nuvoton,npcm750-adc" for the NPCM7XX BMC.
- reg: specifies physical base address and size of the registers.
- interrupts: Contain the ADC interrupt with flags for falling edge.
Optional properties:
- clocks: phandle of ADC reference clock, in case the clock is not
added the ADC will use the default ADC sample rate.
- vref-supply: The regulator supply ADC reference voltage, in case the
vref-supply is not added the ADC will use internal voltage
reference.
Example:
adc: adc@f000c000 {
compatible = "nuvoton,npcm750-adc";
reg = <0xf000c000 0x8>;
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk NPCM7XX_CLK_ADC>;
};

View File

@ -11,11 +11,13 @@ New driver handles the following
Required properties:
- compatible: Must be "samsung,exynos-adc-v1"
for exynos4412/5250 controllers.
for Exynos5250 controllers.
Must be "samsung,exynos-adc-v2" for
future controllers.
Must be "samsung,exynos3250-adc" for
controllers compatible with ADC of Exynos3250.
Must be "samsung,exynos4212-adc" for
controllers compatible with ADC of Exynos4212 and Exynos4412.
Must be "samsung,exynos7-adc" for
the ADC in Exynos7 and compatibles
Must be "samsung,s3c2410-adc" for

View File

@ -0,0 +1,25 @@
* Texas Instruments' ads124s08 and ads124s06 ADC chip
Required properties:
- compatible :
"ti,ads124s08"
"ti,ads124s06"
- reg : spi chip select number for the device
Recommended properties:
- spi-max-frequency : Definition as per
Documentation/devicetree/bindings/spi/spi-bus.txt
- spi-cpha : Definition as per
Documentation/devicetree/bindings/spi/spi-bus.txt
Optional properties:
- reset-gpios : GPIO pin used to reset the device.
Example:
adc@0 {
compatible = "ti,ads124s08";
reg = <0>;
spi-max-frequency = <1000000>;
spi-cpha;
reset-gpios = <&gpio1 16 GPIO_ACTIVE_LOW>;
};

View File

@ -0,0 +1,11 @@
Bosch Sensortec BME680 pressure/temperature/humidity/voc sensors
Required properties:
- compatible: must be "bosch,bme680"
Example:
bme680@76 {
compatible = "bosch,bme680";
reg = <0x76>;
};

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,15 @@
* Sensirion SGP30/SGPC3 multi-pixel Gas Sensor
Required properties:
- compatible: must be one of
"sensirion,sgp30"
"sensirion,sgpc3"
- reg: the I2C address of the sensor
Example:
gas@58 {
compatible = "sensirion,sgp30";
reg = <0x58>;
};

View File

@ -0,0 +1,12 @@
* Sensirion SPS30 particulate matter sensor
Required properties:
- compatible: must be "sensirion,sps30"
- reg: the I2C address of the sensor
Example:
sps30@69 {
compatible = "sensirion,sps30";
reg = <0x69>;
};

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

@ -0,0 +1,26 @@
Analog Devices AD5933/AD5934 Impedance Converter, Network Analyzer
https://www.analog.com/media/en/technical-documentation/data-sheets/AD5933.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/AD5934.pdf
Required properties:
- compatible : should be one of
"adi,ad5933"
"adi,ad5934"
- reg : the I2C address.
- vdd-supply : The regulator supply for DVDD, AVDD1 and AVDD2 when they
are connected together.
Optional properties:
- clocks : external clock reference.
- clock-names : must be "mclk" if clocks is set.
Example for a I2C device node:
impedance-analyzer@0d {
compatible = "adi,adxl345";
reg = <0x0d>;
vdd-supply = <&vdd_supply>;
clocks = <&ref_clk>;
clock-names = "mclk";
};

View File

@ -9,9 +9,11 @@ Required properties:
- spi-max-frequency : set maximum clock frequency (only for SPI)
Optional properties:
- interrupts : interrupt mapping for IRQ, must be IRQ_TYPE_LEVEL_LOW
- interrupts : interrupt mapping for IRQ
- interrupt-names : set to "INT1" if INT1 pin should be used as interrupt
input, set to "INT2" if INT2 pin should be used instead
- drive-open-drain : set if the specified interrupt pin should be configured as
open drain. If not set, defaults to push-pull.
Examples:
@ -20,7 +22,7 @@ bmi160@68 {
reg = <0x68>;
interrupt-parent = <&gpio4>;
interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
interrupts = <12 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "INT1";
};

View File

@ -11,6 +11,7 @@ Required properties:
"invensense,mpu9250"
"invensense,mpu9255"
"invensense,icm20608"
"invensense,icm20602"
- reg : the I2C address of the sensor
- interrupts: interrupt mapping for IRQ. It should be configured with flags
IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or

View File

@ -0,0 +1,24 @@
* MAX44009 Ambient Light Sensor
Required properties:
- compatible: should be "maxim,max44009"
- reg: the I2C address of the device (default is <0x4a>)
Optional properties:
- interrupts: interrupt mapping for GPIO IRQ. Should be configured with
IRQ_TYPE_EDGE_FALLING.
Refer to interrupt-controller/interrupts.txt for generic interrupt client
node bindings.
Example:
light-sensor@4a {
compatible = "maxim,max44009";
reg = <0x4a>;
interrupt-parent = <&gpio1>;
interrupts = <17 IRQ_TYPE_EDGE_FALLING>;
};

View File

@ -77,3 +77,4 @@ Pressure sensors:
- st,lps22hb-press
- st,lps33hw
- st,lps35hw
- st,lps22hh

View File

@ -310,6 +310,7 @@ phytec PHYTEC Messtechnik GmbH
picochip Picochip Ltd
pine64 Pine64
pixcir PIXCIR MICROELECTRONICS Co., Ltd
plantower Plantower Co., Ltd
plathome Plat'Home Co., Ltd.
plda PLDA
plx Broadcom Corporation (formerly PLX Technology)

View File

@ -854,6 +854,22 @@ S: Supported
F: drivers/iio/adc/ad7124.c
F: Documentation/devicetree/bindings/iio/adc/adi,ad7124.txt
ANALOG DEVICES INC AD7606 DRIVER
M: Stefan Popa <stefan.popa@analog.com>
L: linux-iio@vger.kernel.org
W: http://ez.analog.com/community/linux-device-drivers
S: Supported
F: drivers/iio/adc/ad7606.c
F: Documentation/devicetree/bindings/iio/adc/ad7606.txt
ANALOG DEVICES INC AD7768-1 DRIVER
M: Stefan Popa <stefan.popa@analog.com>
L: linux-iio@vger.kernel.org
W: http://ez.analog.com/community/linux-device-drivers
S: Supported
F: drivers/iio/adc/ad7768-1.c
F: Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.txt
ANALOG DEVICES INC AD9389B DRIVER
M: Hans Verkuil <hans.verkuil@cisco.com>
L: linux-media@vger.kernel.org
@ -14619,11 +14635,6 @@ L: linux-wireless@vger.kernel.org
S: Supported
F: drivers/staging/wilc1000/
STAGING - XGI Z7,Z9,Z11 PCI DISPLAY DRIVER
M: Arnaud Patard <arnaud.patard@rtp-net.org>
S: Odd Fixes
F: drivers/staging/xgifb/
STAGING SUBSYSTEM
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
@ -15218,6 +15229,13 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers)
S: Maintained
F: sound/soc/ti/
Texas Instruments' DAC7612 DAC Driver
M: Ricardo Ribalda <ricardo@ribalda.com>
L: linux-iio@vger.kernel.org
S: Supported
F: drivers/iio/dac/ti-dac7612.c
F: Documentation/devicetree/bindings/iio/dac/ti,dac7612.txt
THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org

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,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct adxl345_data *data = iio_priv(indio_dev);
s64 n;

View File

@ -31,6 +31,7 @@
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#define MMA8452_STATUS 0x00
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
@ -107,6 +108,8 @@ struct mma8452_data {
u8 data_cfg;
const struct mma_chip_info *chip_info;
int sleep_val;
struct regulator *vdd_reg;
struct regulator *vddio_reg;
};
/**
@ -1534,9 +1537,39 @@ static int mma8452_probe(struct i2c_client *client,
mutex_init(&data->lock);
data->chip_info = match->data;
data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
if (IS_ERR(data->vdd_reg)) {
if (PTR_ERR(data->vdd_reg) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_err(&client->dev, "failed to get VDD regulator!\n");
return PTR_ERR(data->vdd_reg);
}
data->vddio_reg = devm_regulator_get(&client->dev, "vddio");
if (IS_ERR(data->vddio_reg)) {
if (PTR_ERR(data->vddio_reg) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_err(&client->dev, "failed to get VDDIO regulator!\n");
return PTR_ERR(data->vddio_reg);
}
ret = regulator_enable(data->vdd_reg);
if (ret) {
dev_err(&client->dev, "failed to enable VDD regulator!\n");
return ret;
}
ret = regulator_enable(data->vddio_reg);
if (ret) {
dev_err(&client->dev, "failed to enable VDDIO regulator!\n");
goto disable_regulator_vdd;
}
ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
if (ret < 0)
return ret;
goto disable_regulators;
switch (ret) {
case MMA8451_DEVICE_ID:
@ -1549,7 +1582,8 @@ static int mma8452_probe(struct i2c_client *client,
break;
/* else: fall through */
default:
return -ENODEV;
ret = -ENODEV;
goto disable_regulators;
}
dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n",
@ -1566,13 +1600,13 @@ static int mma8452_probe(struct i2c_client *client,
ret = mma8452_reset(client);
if (ret < 0)
return ret;
goto disable_regulators;
data->data_cfg = MMA8452_DATA_CFG_FS_2G;
ret = i2c_smbus_write_byte_data(client, MMA8452_DATA_CFG,
data->data_cfg);
if (ret < 0)
return ret;
goto disable_regulators;
/*
* By default set transient threshold to max to avoid events if
@ -1581,7 +1615,7 @@ static int mma8452_probe(struct i2c_client *client,
ret = i2c_smbus_write_byte_data(client, MMA8452_TRANSIENT_THS,
MMA8452_TRANSIENT_THS_MASK);
if (ret < 0)
return ret;
goto disable_regulators;
if (client->irq) {
int irq2;
@ -1595,7 +1629,7 @@ static int mma8452_probe(struct i2c_client *client,
MMA8452_CTRL_REG5,
data->chip_info->all_events);
if (ret < 0)
return ret;
goto disable_regulators;
dev_dbg(&client->dev, "using interrupt line INT1\n");
}
@ -1604,11 +1638,11 @@ static int mma8452_probe(struct i2c_client *client,
MMA8452_CTRL_REG4,
data->chip_info->enabled_events);
if (ret < 0)
return ret;
goto disable_regulators;
ret = mma8452_trigger_setup(indio_dev);
if (ret < 0)
return ret;
goto disable_regulators;
}
data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
@ -1661,12 +1695,19 @@ buffer_cleanup:
trigger_cleanup:
mma8452_trigger_cleanup(indio_dev);
disable_regulators:
regulator_disable(data->vddio_reg);
disable_regulator_vdd:
regulator_disable(data->vdd_reg);
return ret;
}
static int mma8452_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct mma8452_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
@ -1678,6 +1719,9 @@ static int mma8452_remove(struct i2c_client *client)
mma8452_trigger_cleanup(indio_dev);
mma8452_standby(iio_priv(indio_dev));
regulator_disable(data->vddio_reg);
regulator_disable(data->vdd_reg);
return 0;
}
@ -1696,6 +1740,18 @@ static int mma8452_runtime_suspend(struct device *dev)
return -EAGAIN;
}
ret = regulator_disable(data->vddio_reg);
if (ret) {
dev_err(dev, "failed to disable VDDIO regulator\n");
return ret;
}
ret = regulator_disable(data->vdd_reg);
if (ret) {
dev_err(dev, "failed to disable VDD regulator\n");
return ret;
}
return 0;
}
@ -1705,9 +1761,22 @@ static int mma8452_runtime_resume(struct device *dev)
struct mma8452_data *data = iio_priv(indio_dev);
int ret, sleep_val;
ret = regulator_enable(data->vdd_reg);
if (ret) {
dev_err(dev, "failed to enable VDD regulator\n");
return ret;
}
ret = regulator_enable(data->vddio_reg);
if (ret) {
dev_err(dev, "failed to enable VDDIO regulator\n");
regulator_disable(data->vdd_reg);
return ret;
}
ret = mma8452_active(data);
if (ret < 0)
return ret;
goto runtime_resume_failed;
ret = mma8452_get_odr_index(data);
sleep_val = 1000 / mma8452_samp_freq[ret][0];
@ -1717,25 +1786,17 @@ static int mma8452_runtime_resume(struct device *dev)
msleep_interruptible(sleep_val);
return 0;
}
#endif
#ifdef CONFIG_PM_SLEEP
static int mma8452_suspend(struct device *dev)
{
return mma8452_standby(iio_priv(i2c_get_clientdata(
to_i2c_client(dev))));
}
runtime_resume_failed:
regulator_disable(data->vddio_reg);
regulator_disable(data->vdd_reg);
static int mma8452_resume(struct device *dev)
{
return mma8452_active(iio_priv(i2c_get_clientdata(
to_i2c_client(dev))));
return ret;
}
#endif
static const struct dev_pm_ops mma8452_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mma8452_suspend, mma8452_resume)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(mma8452_runtime_suspend,
mma8452_runtime_resume, NULL)
};

View File

@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/acpi.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/mutex.h>
@ -918,12 +919,167 @@ static const struct iio_trigger_ops st_accel_trigger_ops = {
#define ST_ACCEL_TRIGGER_OPS NULL
#endif
static const struct iio_mount_matrix *
get_mount_matrix(const struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct st_sensor_data *adata = iio_priv(indio_dev);
return adata->mount_matrix;
}
static const struct iio_chan_spec_ext_info mount_matrix_ext_info[] = {
IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, get_mount_matrix),
{ },
};
/* Read ST-specific _ONT orientation data from ACPI and generate an
* appropriate mount matrix.
*/
static int apply_acpi_orientation(struct iio_dev *indio_dev,
struct iio_chan_spec *channels)
{
#ifdef CONFIG_ACPI
struct st_sensor_data *adata = iio_priv(indio_dev);
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
struct acpi_device *adev;
union acpi_object *ont;
union acpi_object *elements;
acpi_status status;
int ret = -EINVAL;
unsigned int val;
int i, j;
int final_ont[3][3] = { { 0 }, };
/* For some reason, ST's _ONT translation does not apply directly
* to the data read from the sensor. Another translation must be
* performed first, as described by the matrix below. Perhaps
* ST required this specific translation for the first product
* where the device was mounted?
*/
const int default_ont[3][3] = {
{ 0, 1, 0 },
{ -1, 0, 0 },
{ 0, 0, -1 },
};
adev = ACPI_COMPANION(adata->dev);
if (!adev)
return 0;
/* Read _ONT data, which should be a package of 6 integers. */
status = acpi_evaluate_object(adev->handle, "_ONT", NULL, &buffer);
if (status == AE_NOT_FOUND) {
return 0;
} else if (ACPI_FAILURE(status)) {
dev_warn(&indio_dev->dev, "failed to execute _ONT: %d\n",
status);
return status;
}
ont = buffer.pointer;
if (ont->type != ACPI_TYPE_PACKAGE || ont->package.count != 6)
goto out;
/* The first 3 integers provide axis order information.
* e.g. 0 1 2 would indicate normal X,Y,Z ordering.
* e.g. 1 0 2 indicates that data arrives in order Y,X,Z.
*/
elements = ont->package.elements;
for (i = 0; i < 3; i++) {
if (elements[i].type != ACPI_TYPE_INTEGER)
goto out;
val = elements[i].integer.value;
if (val < 0 || val > 2)
goto out;
/* Avoiding full matrix multiplication, we simply reorder the
* columns in the default_ont matrix according to the
* ordering provided by _ONT.
*/
final_ont[0][i] = default_ont[0][val];
final_ont[1][i] = default_ont[1][val];
final_ont[2][i] = default_ont[2][val];
}
/* The final 3 integers provide sign flip information.
* 0 means no change, 1 means flip.
* e.g. 0 0 1 means that Z data should be sign-flipped.
* This is applied after the axis reordering from above.
*/
elements += 3;
for (i = 0; i < 3; i++) {
if (elements[i].type != ACPI_TYPE_INTEGER)
goto out;
val = elements[i].integer.value;
if (val != 0 && val != 1)
goto out;
if (!val)
continue;
/* Flip the values in the indicated column */
final_ont[0][i] *= -1;
final_ont[1][i] *= -1;
final_ont[2][i] *= -1;
}
/* Convert our integer matrix to a string-based iio_mount_matrix */
adata->mount_matrix = devm_kmalloc(&indio_dev->dev,
sizeof(*adata->mount_matrix),
GFP_KERNEL);
if (!adata->mount_matrix) {
ret = -ENOMEM;
goto out;
}
for (i = 0; i < 3; i++) {
for (j = 0; j < 3; j++) {
int matrix_val = final_ont[i][j];
char *str_value;
switch (matrix_val) {
case -1:
str_value = "-1";
break;
case 0:
str_value = "0";
break;
case 1:
str_value = "1";
break;
default:
goto out;
}
adata->mount_matrix->rotation[i * 3 + j] = str_value;
}
}
/* Expose the mount matrix via ext_info */
for (i = 0; i < indio_dev->num_channels; i++)
channels[i].ext_info = mount_matrix_ext_info;
ret = 0;
dev_info(&indio_dev->dev, "computed mount matrix from ACPI\n");
out:
kfree(buffer.pointer);
return ret;
#else /* !CONFIG_ACPI */
return 0;
#endif
}
int st_accel_common_probe(struct iio_dev *indio_dev)
{
struct st_sensor_data *adata = iio_priv(indio_dev);
struct st_sensors_platform_data *pdata =
(struct st_sensors_platform_data *)adata->dev->platform_data;
int irq = adata->get_irq_data_ready(indio_dev);
struct iio_chan_spec *channels;
size_t channels_size;
int err;
indio_dev->modes = INDIO_DIRECT_MODE;
@ -942,9 +1098,22 @@ int st_accel_common_probe(struct iio_dev *indio_dev)
adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS;
adata->multiread_bit = adata->sensor_settings->multi_read_bit;
indio_dev->channels = adata->sensor_settings->ch;
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
channels_size = indio_dev->num_channels * sizeof(struct iio_chan_spec);
channels = devm_kmemdup(&indio_dev->dev,
adata->sensor_settings->ch,
channels_size, GFP_KERNEL);
if (!channels) {
err = -ENOMEM;
goto st_accel_power_off;
}
if (apply_acpi_orientation(indio_dev, channels))
dev_warn(&indio_dev->dev,
"failed to apply ACPI orientation data: %d\n", err);
indio_dev->channels = channels;
adata->current_fullscale = (struct st_sensor_fullscale_avl *)
&adata->sensor_settings->fs.fs_avl[0];
adata->odr = adata->sensor_settings->odr.odr_avl[0].hz;

View File

@ -57,18 +57,48 @@ config AD7298
module will be called ad7298.
config AD7476
tristate "Analog Devices AD7476 and similar 1-channel ADCs driver"
tristate "Analog Devices AD7476 1-channel ADCs driver and other similar devices from AD an TI"
depends on SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Analog Devices AD7273, AD7274, AD7276,
AD7277, AD7278, AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468,
AD7495, AD7910, AD7920, AD7920 SPI analog to digital converters (ADC).
Say yes here to build support for the following SPI analog to
digital converters (ADCs):
Analog Devices: AD7273, AD7274, AD7276, AD7277, AD7278, AD7475,
AD7476, AD7477, AD7478, AD7466, AD7467, AD7468, AD7495, AD7910,
AD7920.
Texas Instruments: ADS7866, ADS7867, ADS7868.
To compile this driver as a module, choose M here: the
module will be called ad7476.
config AD7606
tristate
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
config AD7606_IFACE_PARALLEL
tristate "Analog Devices AD7606 ADC driver with parallel interface support"
depends on HAS_IOMEM
select AD7606
help
Say yes here to build parallel interface support for Analog Devices:
ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC).
To compile this driver as a module, choose M here: the
module will be called ad7606_parallel.
config AD7606_IFACE_SPI
tristate "Analog Devices AD7606 ADC driver with spi interface support"
depends on SPI
select AD7606
help
Say yes here to build spi interface support for Analog Devices:
ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC).
To compile this driver as a module, choose M here: the
module will be called ad7606_spi.
config AD7766
tristate "Analog Devices AD7766/AD7767 ADC driver"
depends on SPI_MASTER
@ -81,6 +111,19 @@ config AD7766
To compile this driver as a module, choose M here: the module will be
called ad7766.
config AD7768_1
tristate "Analog Devices AD7768-1 ADC driver"
depends on SPI
select IIO_BUFFER
select IIO_TRIGGER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Analog Devices AD7768-1 SPI
simultaneously sampling sigma-delta analog to digital converter (ADC).
To compile this driver as a module, choose M here: the module will be
called ad7768-1.
config AD7791
tristate "Analog Devices AD7791 ADC driver"
depends on SPI
@ -367,6 +410,15 @@ config INA2XX_ADC
Say yes here to build support for TI INA2xx family of Power Monitors.
This driver is mutually exclusive with the HWMON version.
config INGENIC_ADC
tristate "Ingenic JZ47xx SoCs ADC driver"
depends on MIPS || COMPILE_TEST
help
Say yes here to build support for the Ingenic JZ47xx SoCs ADC unit.
This driver can also be built as a module. If so, the module will be
called ingenic_adc.
config IMX7D_ADC
tristate "Freescale IMX7D ADC driver"
depends on ARCH_MXC || COMPILE_TEST
@ -576,6 +628,16 @@ config NAU7802
To compile this driver as a module, choose M here: the
module will be called nau7802.
config NPCM_ADC
tristate "Nuvoton NPCM ADC driver"
depends on ARCH_NPCM || COMPILE_TEST
depends on HAS_IOMEM
help
Say yes here to build support for Nuvoton NPCM ADC.
This driver can also be built as a module. If so, the module
will be called npcm_adc.
config PALMAS_GPADC
tristate "TI Palmas General Purpose ADC"
depends on MFD_PALMAS
@ -908,6 +970,16 @@ config TI_ADS8688
This driver can also be built as a module. If so, the module will be
called ti-ads8688.
config TI_ADS124S08
tristate "Texas Instruments ADS124S08"
depends on SPI && OF
help
If you say yes here you get support for Texas Instruments ADS124S08
and ADS124S06 ADC chips
This driver can also be built as a module. If so, the module will be
called ti-ads124s08.
config TI_AM335X_ADC
tristate "TI's AM335X ADC driver"
depends on MFD_TI_AM335X_TSCADC && HAS_DMA

View File

@ -11,7 +11,11 @@ obj-$(CONFIG_AD7291) += ad7291.o
obj-$(CONFIG_AD7298) += ad7298.o
obj-$(CONFIG_AD7923) += ad7923.o
obj-$(CONFIG_AD7476) += ad7476.o
obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o
obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o
obj-$(CONFIG_AD7606) += ad7606.o
obj-$(CONFIG_AD7766) += ad7766.o
obj-$(CONFIG_AD7768_1) += ad7768-1.o
obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
@ -36,6 +40,7 @@ obj-$(CONFIG_HI8435) += hi8435.o
obj-$(CONFIG_HX711) += hx711.o
obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
@ -55,6 +60,7 @@ obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
@ -81,6 +87,7 @@ obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o

View File

@ -59,6 +59,9 @@ enum ad7476_supported_device_ids {
ID_ADC081S,
ID_ADC101S,
ID_ADC121S,
ID_ADS7866,
ID_ADS7867,
ID_ADS7868,
};
static irqreturn_t ad7476_trigger_handler(int irq, void *p)
@ -157,6 +160,8 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
BIT(IIO_CHAN_INFO_RAW))
#define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0)
#define ADS786X_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \
BIT(IIO_CHAN_INFO_RAW))
static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
[ID_AD7091R] = {
@ -209,6 +214,18 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
.channel[0] = ADC081S_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_ADS7866] = {
.channel[0] = ADS786X_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_ADS7867] = {
.channel[0] = ADS786X_CHAN(10),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_ADS7868] = {
.channel[0] = ADS786X_CHAN(8),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
};
static const struct iio_info ad7476_info = {
@ -314,6 +331,9 @@ static const struct spi_device_id ad7476_id[] = {
{"adc081s", ID_ADC081S},
{"adc101s", ID_ADC101S},
{"adc121s", ID_ADC121S},
{"ads7866", ID_ADS7866},
{"ads7867", ID_ADS7867},
{"ads7868", ID_ADS7868},
{}
};
MODULE_DEVICE_TABLE(spi, ad7476_id);

View File

@ -1,28 +1,29 @@
// SPDX-License-Identifier: GPL-2.0
/*
* AD7606 SPI ADC driver
*
* Copyright 2011 Analog Devices Inc.
*
* Licensed under the GPL-2.
*/
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/util_macros.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include "ad7606.h"
@ -30,8 +31,12 @@
* Scales are computed as 5000/32768 and 10000/32768 respectively,
* so that when applied to the raw values they provide mV values
*/
static const unsigned int scale_avail[2][2] = {
{0, 152588}, {0, 305176}
static const unsigned int scale_avail[2] = {
152588, 305176
};
static const unsigned int ad7606_oversampling_avail[7] = {
1, 2, 4, 8, 16, 32, 64,
};
static int ad7606_reset(struct ad7606_state *st)
@ -82,36 +87,24 @@ static int ad7606_read_samples(struct ad7606_state *st)
static irqreturn_t ad7606_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct ad7606_state *st = iio_priv(pf->indio_dev);
gpiod_set_value(st->gpio_convst, 1);
return IRQ_HANDLED;
}
/**
* ad7606_poll_bh_to_ring() bh of trigger launched polling to ring buffer
* @work_s: the work struct through which this was scheduled
*
* Currently there is no option in this driver to disable the saving of
* timestamps within the ring.
* I think the one copy of this at a time was to avoid problems if the
* trigger was set far too high and the reads then locked up the computer.
**/
static void ad7606_poll_bh_to_ring(struct work_struct *work_s)
{
struct ad7606_state *st = container_of(work_s, struct ad7606_state,
poll_work);
struct iio_dev *indio_dev = iio_priv_to_dev(st);
struct iio_dev *indio_dev = pf->indio_dev;
struct ad7606_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
ret = ad7606_read_samples(st);
if (ret == 0)
iio_push_to_buffers_with_timestamp(indio_dev, st->data,
iio_get_time_ns(indio_dev));
gpiod_set_value(st->gpio_convst, 0);
iio_trigger_notify_done(indio_dev->trig);
/* The rising edge of the CONVST signal starts a new conversion. */
gpiod_set_value(st->gpio_convst, 1);
mutex_unlock(&st->lock);
return IRQ_HANDLED;
}
static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
@ -119,12 +112,13 @@ static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
struct ad7606_state *st = iio_priv(indio_dev);
int ret;
st->done = false;
gpiod_set_value(st->gpio_convst, 1);
ret = wait_event_interruptible(st->wq_data_avail, st->done);
if (ret)
ret = wait_for_completion_timeout(&st->completion,
msecs_to_jiffies(1000));
if (!ret) {
ret = -ETIMEDOUT;
goto error_ret;
}
ret = ad7606_read_samples(st);
if (ret == 0)
@ -159,8 +153,8 @@ static int ad7606_read_raw(struct iio_dev *indio_dev,
*val = (short)ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = scale_avail[st->range][0];
*val2 = scale_avail[st->range][1];
*val = 0;
*val2 = scale_avail[st->range];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
*val = st->oversampling;
@ -176,8 +170,8 @@ static ssize_t in_voltage_scale_available_show(struct device *dev,
int i, len = 0;
for (i = 0; i < ARRAY_SIZE(scale_avail); i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "%d.%06u ",
scale_avail[i][0], scale_avail[i][1]);
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
scale_avail[i]);
buf[len - 1] = '\n';
@ -186,18 +180,6 @@ static ssize_t in_voltage_scale_available_show(struct device *dev,
static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0);
static int ad7606_oversampling_get_index(unsigned int val)
{
unsigned char supported[] = {1, 2, 4, 8, 16, 32, 64};
int i;
for (i = 0; i < ARRAY_SIZE(supported); i++)
if (val == supported[i])
return i;
return -EINVAL;
}
static int ad7606_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val,
@ -206,36 +188,29 @@ static int ad7606_write_raw(struct iio_dev *indio_dev,
{
struct ad7606_state *st = iio_priv(indio_dev);
DECLARE_BITMAP(values, 3);
int ret, i;
int i;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
ret = -EINVAL;
mutex_lock(&st->lock);
for (i = 0; i < ARRAY_SIZE(scale_avail); i++)
if (val2 == scale_avail[i][1]) {
gpiod_set_value(st->gpio_range, i);
st->range = i;
ret = 0;
break;
}
i = find_closest(val2, scale_avail, ARRAY_SIZE(scale_avail));
gpiod_set_value(st->gpio_range, i);
st->range = i;
mutex_unlock(&st->lock);
return ret;
return 0;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
if (val2)
return -EINVAL;
ret = ad7606_oversampling_get_index(val);
if (ret < 0)
return ret;
i = find_closest(val, ad7606_oversampling_avail,
ARRAY_SIZE(ad7606_oversampling_avail));
values[0] = ret;
values[0] = i;
mutex_lock(&st->lock);
gpiod_set_array_value(3, st->gpio_os->desc, st->gpio_os->info,
values);
st->oversampling = val;
gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
st->gpio_os->info, values);
st->oversampling = ad7606_oversampling_avail[i];
mutex_unlock(&st->lock);
return 0;
@ -274,8 +249,7 @@ static const struct attribute_group ad7606_attribute_group_range = {
.attrs = ad7606_attributes_range,
};
#define AD760X_CHANNEL(num, mask) \
{ \
#define AD760X_CHANNEL(num, mask) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = num, \
@ -290,7 +264,7 @@ static const struct attribute_group ad7606_attribute_group_range = {
.storagebits = 16, \
.endianness = IIO_CPU, \
}, \
}
}
#define AD7605_CHANNEL(num) \
AD760X_CHANNEL(num, 0)
@ -319,9 +293,7 @@ static const struct iio_chan_spec ad7606_channels[] = {
};
static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
/*
* More devices added in future
*/
/* More devices added in future */
[ID_AD7605_4] = {
.channels = ad7605_channels,
.num_channels = 5,
@ -347,7 +319,7 @@ static int ad7606_request_gpios(struct ad7606_state *st)
{
struct device *dev = st->dev;
st->gpio_convst = devm_gpiod_get(dev, "conversion-start",
st->gpio_convst = devm_gpiod_get(dev, "adi,conversion-start",
GPIOD_OUT_LOW);
if (IS_ERR(st->gpio_convst))
return PTR_ERR(st->gpio_convst);
@ -356,7 +328,8 @@ static int ad7606_request_gpios(struct ad7606_state *st)
if (IS_ERR(st->gpio_reset))
return PTR_ERR(st->gpio_reset);
st->gpio_range = devm_gpiod_get_optional(dev, "range", GPIOD_OUT_LOW);
st->gpio_range = devm_gpiod_get_optional(dev, "adi,range",
GPIOD_OUT_LOW);
if (IS_ERR(st->gpio_range))
return PTR_ERR(st->gpio_range);
@ -365,7 +338,7 @@ static int ad7606_request_gpios(struct ad7606_state *st)
if (IS_ERR(st->gpio_standby))
return PTR_ERR(st->gpio_standby);
st->gpio_frstdata = devm_gpiod_get_optional(dev, "first-data",
st->gpio_frstdata = devm_gpiod_get_optional(dev, "adi,first-data",
GPIOD_IN);
if (IS_ERR(st->gpio_frstdata))
return PTR_ERR(st->gpio_frstdata);
@ -373,13 +346,17 @@ static int ad7606_request_gpios(struct ad7606_state *st)
if (!st->chip_info->has_oversampling)
return 0;
st->gpio_os = devm_gpiod_get_array_optional(dev, "oversampling-ratio",
st->gpio_os = devm_gpiod_get_array_optional(dev,
"adi,oversampling-ratio",
GPIOD_OUT_LOW);
return PTR_ERR_OR_ZERO(st->gpio_os);
}
/**
* Interrupt handler
/*
* The BUSY signal indicates when conversions are in progress, so when a rising
* edge of CONVST is applied, BUSY goes logic high and transitions low at the
* end of the entire conversion process. The falling edge of the BUSY signal
* triggers this interrupt.
*/
static irqreturn_t ad7606_interrupt(int irq, void *dev_id)
{
@ -387,37 +364,87 @@ static irqreturn_t ad7606_interrupt(int irq, void *dev_id)
struct ad7606_state *st = iio_priv(indio_dev);
if (iio_buffer_enabled(indio_dev)) {
schedule_work(&st->poll_work);
gpiod_set_value(st->gpio_convst, 0);
iio_trigger_poll_chained(st->trig);
} else {
st->done = true;
wake_up_interruptible(&st->wq_data_avail);
complete(&st->completion);
}
return IRQ_HANDLED;
};
static int ad7606_validate_trigger(struct iio_dev *indio_dev,
struct iio_trigger *trig)
{
struct ad7606_state *st = iio_priv(indio_dev);
if (st->trig != trig)
return -EINVAL;
return 0;
}
static int ad7606_buffer_postenable(struct iio_dev *indio_dev)
{
struct ad7606_state *st = iio_priv(indio_dev);
iio_triggered_buffer_postenable(indio_dev);
gpiod_set_value(st->gpio_convst, 1);
return 0;
}
static int ad7606_buffer_predisable(struct iio_dev *indio_dev)
{
struct ad7606_state *st = iio_priv(indio_dev);
gpiod_set_value(st->gpio_convst, 0);
return iio_triggered_buffer_predisable(indio_dev);
}
static const struct iio_buffer_setup_ops ad7606_buffer_ops = {
.postenable = &ad7606_buffer_postenable,
.predisable = &ad7606_buffer_predisable,
};
static const struct iio_info ad7606_info_no_os_or_range = {
.read_raw = &ad7606_read_raw,
.validate_trigger = &ad7606_validate_trigger,
};
static const struct iio_info ad7606_info_os_and_range = {
.read_raw = &ad7606_read_raw,
.write_raw = &ad7606_write_raw,
.attrs = &ad7606_attribute_group_os_and_range,
.validate_trigger = &ad7606_validate_trigger,
};
static const struct iio_info ad7606_info_os = {
.read_raw = &ad7606_read_raw,
.write_raw = &ad7606_write_raw,
.attrs = &ad7606_attribute_group_os,
.validate_trigger = &ad7606_validate_trigger,
};
static const struct iio_info ad7606_info_range = {
.read_raw = &ad7606_read_raw,
.write_raw = &ad7606_write_raw,
.attrs = &ad7606_attribute_group_range,
.validate_trigger = &ad7606_validate_trigger,
};
static const struct iio_trigger_ops ad7606_trigger_ops = {
.validate_device = iio_trigger_validate_own_device,
};
static void ad7606_regulator_disable(void *data)
{
struct ad7606_state *st = data;
regulator_disable(st->reg);
}
int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
const char *name, unsigned int id,
const struct ad7606_bus_ops *bops)
@ -431,6 +458,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
return -ENOMEM;
st = iio_priv(indio_dev);
dev_set_drvdata(dev, indio_dev);
st->dev = dev;
mutex_init(&st->lock);
@ -439,7 +467,6 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
/* tied to logic low, analog input range is +/- 5V */
st->range = 0;
st->oversampling = 1;
INIT_WORK(&st->poll_work, &ad7606_poll_bh_to_ring);
st->reg = devm_regulator_get(dev, "avcc");
if (IS_ERR(st->reg))
@ -451,11 +478,15 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
return ret;
}
ret = devm_add_action_or_reset(dev, ad7606_regulator_disable, st);
if (ret)
return ret;
st->chip_info = &ad7606_chip_info_tbl[id];
ret = ad7606_request_gpios(st);
if (ret)
goto error_disable_reg;
return ret;
indio_dev->dev.parent = dev;
if (st->gpio_os) {
@ -474,56 +505,45 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
indio_dev->channels = st->chip_info->channels;
indio_dev->num_channels = st->chip_info->num_channels;
init_waitqueue_head(&st->wq_data_avail);
init_completion(&st->completion);
ret = ad7606_reset(st);
if (ret)
dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
ret = request_irq(irq, ad7606_interrupt, IRQF_TRIGGER_FALLING, name,
indio_dev);
st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
indio_dev->name, indio_dev->id);
if (!st->trig)
return -ENOMEM;
st->trig->ops = &ad7606_trigger_ops;
st->trig->dev.parent = dev;
iio_trigger_set_drvdata(st->trig, indio_dev);
ret = devm_iio_trigger_register(dev, st->trig);
if (ret)
goto error_disable_reg;
return ret;
ret = iio_triggered_buffer_setup(indio_dev, &ad7606_trigger_handler,
NULL, NULL);
indio_dev->trig = iio_trigger_get(st->trig);
ret = devm_request_threaded_irq(dev, irq,
NULL,
&ad7606_interrupt,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
name, indio_dev);
if (ret)
goto error_free_irq;
return ret;
ret = iio_device_register(indio_dev);
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
&iio_pollfunc_store_time,
&ad7606_trigger_handler,
&ad7606_buffer_ops);
if (ret)
goto error_unregister_ring;
return ret;
dev_set_drvdata(dev, indio_dev);
return 0;
error_unregister_ring:
iio_triggered_buffer_cleanup(indio_dev);
error_free_irq:
free_irq(irq, indio_dev);
error_disable_reg:
regulator_disable(st->reg);
return ret;
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_GPL(ad7606_probe);
int ad7606_remove(struct device *dev, int irq)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct ad7606_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
free_irq(irq, indio_dev);
regulator_disable(st->reg);
return 0;
}
EXPORT_SYMBOL_GPL(ad7606_remove);
#ifdef CONFIG_PM_SLEEP
static int ad7606_suspend(struct device *dev)

View File

@ -1,9 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* AD7606 ADC driver
*
* Copyright 2011 Analog Devices Inc.
*
* Licensed under the GPL-2.
*/
#ifndef IIO_ADC_AD7606_H_
@ -15,7 +14,6 @@
* @num_channels: number of channels
* @has_oversampling: whether the device has oversampling support
*/
struct ad7606_chip_info {
const struct iio_chan_spec *channels;
unsigned int num_channels;
@ -27,13 +25,9 @@ struct ad7606_chip_info {
* @dev pointer to kernel device
* @chip_info entry in the table of chips that describes this device
* @reg regulator info for the the power supply of the device
* @poll_work work struct for continuously reading data from the device
* into an IIO triggered buffer
* @wq_data_avail wait queue struct for buffer mode
* @bops bus operations (SPI or parallel)
* @range voltage range selection, selects which scale to apply
* @oversampling oversampling selection
* @done marks whether reading data is done
* @base_address address from where to read data in parallel operation
* @lock protect sensor state from concurrent accesses to GPIOs
* @gpio_convst GPIO descriptor for conversion start signal (CONVST)
@ -44,19 +38,17 @@ struct ad7606_chip_info {
* @gpio_frstdata GPIO descriptor for reading from device when data
* is being read on the first channel
* @gpio_os GPIO descriptors to control oversampling on the device
* @complete completion to indicate end of conversion
* @trig The IIO trigger associated with the device.
* @data buffer for reading data from the device
*/
struct ad7606_state {
struct device *dev;
const struct ad7606_chip_info *chip_info;
struct regulator *reg;
struct work_struct poll_work;
wait_queue_head_t wq_data_avail;
const struct ad7606_bus_ops *bops;
unsigned int range;
unsigned int oversampling;
bool done;
void __iomem *base_address;
struct mutex lock; /* protect sensor state */
@ -66,6 +58,8 @@ struct ad7606_state {
struct gpio_desc *gpio_standby;
struct gpio_desc *gpio_frstdata;
struct gpio_descs *gpio_os;
struct iio_trigger *trig;
struct completion completion;
/*
* DMA (thus cache coherency maintenance) requires the
@ -87,7 +81,6 @@ struct ad7606_bus_ops {
int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
const char *name, unsigned int id,
const struct ad7606_bus_ops *bops);
int ad7606_remove(struct device *dev, int irq);
enum ad7606_supported_device_ids {
ID_AD7605_4,

View File

@ -1,9 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* AD7606 Parallel Interface ADC driver
*
* Copyright 2011 Analog Devices Inc.
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
@ -27,7 +26,7 @@ static int ad7606_par16_read_block(struct device *dev,
}
static const struct ad7606_bus_ops ad7606_par16_bops = {
.read_block = ad7606_par16_read_block,
.read_block = ad7606_par16_read_block,
};
static int ad7606_par8_read_block(struct device *dev,
@ -42,7 +41,7 @@ static int ad7606_par8_read_block(struct device *dev,
}
static const struct ad7606_bus_ops ad7606_par8_bops = {
.read_block = ad7606_par8_read_block,
.read_block = ad7606_par8_read_block,
};
static int ad7606_par_probe(struct platform_device *pdev)
@ -72,40 +71,33 @@ static int ad7606_par_probe(struct platform_device *pdev)
&ad7606_par8_bops);
}
static int ad7606_par_remove(struct platform_device *pdev)
{
return ad7606_remove(&pdev->dev, platform_get_irq(pdev, 0));
}
static const struct platform_device_id ad7606_driver_ids[] = {
{
.name = "ad7605-4",
.driver_data = ID_AD7605_4,
}, {
.name = "ad7606-8",
.driver_data = ID_AD7606_8,
}, {
.name = "ad7606-6",
.driver_data = ID_AD7606_6,
}, {
.name = "ad7606-4",
.driver_data = ID_AD7606_4,
},
{ .name = "ad7605-4", .driver_data = ID_AD7605_4, },
{ .name = "ad7606-4", .driver_data = ID_AD7606_4, },
{ .name = "ad7606-6", .driver_data = ID_AD7606_6, },
{ .name = "ad7606-8", .driver_data = ID_AD7606_8, },
{ }
};
MODULE_DEVICE_TABLE(platform, ad7606_driver_ids);
static const struct of_device_id ad7606_of_match[] = {
{ .compatible = "adi,ad7605-4" },
{ .compatible = "adi,ad7606-4" },
{ .compatible = "adi,ad7606-6" },
{ .compatible = "adi,ad7606-8" },
{ },
};
MODULE_DEVICE_TABLE(of, ad7606_of_match);
static struct platform_driver ad7606_driver = {
.probe = ad7606_par_probe,
.remove = ad7606_par_remove,
.id_table = ad7606_driver_ids,
.driver = {
.name = "ad7606",
.pm = AD7606_PM_OPS,
.name = "ad7606",
.pm = AD7606_PM_OPS,
.of_match_table = ad7606_of_match,
},
};
module_platform_driver(ad7606_driver);
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");

View File

@ -1,9 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/*
* AD7606 SPI ADC driver
*
* Copyright 2011 Analog Devices Inc.
*
* Licensed under the GPL-2.
*/
#include <linux/module.h>
@ -37,7 +36,7 @@ static int ad7606_spi_read_block(struct device *dev,
}
static const struct ad7606_bus_ops ad7606_spi_bops = {
.read_block = ad7606_spi_read_block,
.read_block = ad7606_spi_read_block,
};
static int ad7606_spi_probe(struct spi_device *spi)
@ -49,28 +48,32 @@ static int ad7606_spi_probe(struct spi_device *spi)
&ad7606_spi_bops);
}
static int ad7606_spi_remove(struct spi_device *spi)
{
return ad7606_remove(&spi->dev, spi->irq);
}
static const struct spi_device_id ad7606_id[] = {
{"ad7605-4", ID_AD7605_4},
{"ad7606-8", ID_AD7606_8},
{"ad7606-6", ID_AD7606_6},
{"ad7606-4", ID_AD7606_4},
static const struct spi_device_id ad7606_id_table[] = {
{ "ad7605-4", ID_AD7605_4 },
{ "ad7606-4", ID_AD7606_4 },
{ "ad7606-6", ID_AD7606_6 },
{ "ad7606-8", ID_AD7606_8 },
{}
};
MODULE_DEVICE_TABLE(spi, ad7606_id);
MODULE_DEVICE_TABLE(spi, ad7606_id_table);
static const struct of_device_id ad7606_of_match[] = {
{ .compatible = "adi,ad7605-4" },
{ .compatible = "adi,ad7606-4" },
{ .compatible = "adi,ad7606-6" },
{ .compatible = "adi,ad7606-8" },
{ },
};
MODULE_DEVICE_TABLE(of, ad7606_of_match);
static struct spi_driver ad7606_driver = {
.driver = {
.name = "ad7606",
.of_match_table = ad7606_of_match,
.pm = AD7606_PM_OPS,
},
.probe = ad7606_spi_probe,
.remove = ad7606_spi_remove,
.id_table = ad7606_id,
.id_table = ad7606_id_table,
};
module_spi_driver(ad7606_driver);

View File

@ -0,0 +1,655 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Analog Devices AD7768-1 SPI ADC driver
*
* Copyright 2017 Analog Devices Inc.
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/regulator/consumer.h>
#include <linux/sysfs.h>
#include <linux/spi/spi.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
/* AD7768 registers definition */
#define AD7768_REG_CHIP_TYPE 0x3
#define AD7768_REG_PROD_ID_L 0x4
#define AD7768_REG_PROD_ID_H 0x5
#define AD7768_REG_CHIP_GRADE 0x6
#define AD7768_REG_SCRATCH_PAD 0x0A
#define AD7768_REG_VENDOR_L 0x0C
#define AD7768_REG_VENDOR_H 0x0D
#define AD7768_REG_INTERFACE_FORMAT 0x14
#define AD7768_REG_POWER_CLOCK 0x15
#define AD7768_REG_ANALOG 0x16
#define AD7768_REG_ANALOG2 0x17
#define AD7768_REG_CONVERSION 0x18
#define AD7768_REG_DIGITAL_FILTER 0x19
#define AD7768_REG_SINC3_DEC_RATE_MSB 0x1A
#define AD7768_REG_SINC3_DEC_RATE_LSB 0x1B
#define AD7768_REG_DUTY_CYCLE_RATIO 0x1C
#define AD7768_REG_SYNC_RESET 0x1D
#define AD7768_REG_GPIO_CONTROL 0x1E
#define AD7768_REG_GPIO_WRITE 0x1F
#define AD7768_REG_GPIO_READ 0x20
#define AD7768_REG_OFFSET_HI 0x21
#define AD7768_REG_OFFSET_MID 0x22
#define AD7768_REG_OFFSET_LO 0x23
#define AD7768_REG_GAIN_HI 0x24
#define AD7768_REG_GAIN_MID 0x25
#define AD7768_REG_GAIN_LO 0x26
#define AD7768_REG_SPI_DIAG_ENABLE 0x28
#define AD7768_REG_ADC_DIAG_ENABLE 0x29
#define AD7768_REG_DIG_DIAG_ENABLE 0x2A
#define AD7768_REG_ADC_DATA 0x2C
#define AD7768_REG_MASTER_STATUS 0x2D
#define AD7768_REG_SPI_DIAG_STATUS 0x2E
#define AD7768_REG_ADC_DIAG_STATUS 0x2F
#define AD7768_REG_DIG_DIAG_STATUS 0x30
#define AD7768_REG_MCLK_COUNTER 0x31
/* AD7768_REG_POWER_CLOCK */
#define AD7768_PWR_MCLK_DIV_MSK GENMASK(5, 4)
#define AD7768_PWR_MCLK_DIV(x) FIELD_PREP(AD7768_PWR_MCLK_DIV_MSK, x)
#define AD7768_PWR_PWRMODE_MSK GENMASK(1, 0)
#define AD7768_PWR_PWRMODE(x) FIELD_PREP(AD7768_PWR_PWRMODE_MSK, x)
/* AD7768_REG_DIGITAL_FILTER */
#define AD7768_DIG_FIL_FIL_MSK GENMASK(6, 4)
#define AD7768_DIG_FIL_FIL(x) FIELD_PREP(AD7768_DIG_FIL_FIL_MSK, x)
#define AD7768_DIG_FIL_DEC_MSK GENMASK(2, 0)
#define AD7768_DIG_FIL_DEC_RATE(x) FIELD_PREP(AD7768_DIG_FIL_DEC_MSK, x)
/* AD7768_REG_CONVERSION */
#define AD7768_CONV_MODE_MSK GENMASK(2, 0)
#define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x)
#define AD7768_RD_FLAG_MSK(x) (BIT(6) | ((x) & 0x3F))
#define AD7768_WR_FLAG_MSK(x) ((x) & 0x3F)
enum ad7768_conv_mode {
AD7768_CONTINUOUS,
AD7768_ONE_SHOT,
AD7768_SINGLE,
AD7768_PERIODIC,
AD7768_STANDBY
};
enum ad7768_pwrmode {
AD7768_ECO_MODE = 0,
AD7768_MED_MODE = 2,
AD7768_FAST_MODE = 3
};
enum ad7768_mclk_div {
AD7768_MCLK_DIV_16,
AD7768_MCLK_DIV_8,
AD7768_MCLK_DIV_4,
AD7768_MCLK_DIV_2
};
enum ad7768_dec_rate {
AD7768_DEC_RATE_32 = 0,
AD7768_DEC_RATE_64 = 1,
AD7768_DEC_RATE_128 = 2,
AD7768_DEC_RATE_256 = 3,
AD7768_DEC_RATE_512 = 4,
AD7768_DEC_RATE_1024 = 5,
AD7768_DEC_RATE_8 = 9,
AD7768_DEC_RATE_16 = 10
};
struct ad7768_clk_configuration {
enum ad7768_mclk_div mclk_div;
enum ad7768_dec_rate dec_rate;
unsigned int clk_div;
enum ad7768_pwrmode pwrmode;
};
static const struct ad7768_clk_configuration ad7768_clk_config[] = {
{ AD7768_MCLK_DIV_2, AD7768_DEC_RATE_8, 16, AD7768_FAST_MODE },
{ AD7768_MCLK_DIV_2, AD7768_DEC_RATE_16, 32, AD7768_FAST_MODE },
{ AD7768_MCLK_DIV_2, AD7768_DEC_RATE_32, 64, AD7768_FAST_MODE },
{ AD7768_MCLK_DIV_2, AD7768_DEC_RATE_64, 128, AD7768_FAST_MODE },
{ AD7768_MCLK_DIV_2, AD7768_DEC_RATE_128, 256, AD7768_FAST_MODE },
{ AD7768_MCLK_DIV_4, AD7768_DEC_RATE_128, 512, AD7768_MED_MODE },
{ AD7768_MCLK_DIV_4, AD7768_DEC_RATE_256, 1024, AD7768_MED_MODE },
{ AD7768_MCLK_DIV_4, AD7768_DEC_RATE_512, 2048, AD7768_MED_MODE },
{ AD7768_MCLK_DIV_4, AD7768_DEC_RATE_1024, 4096, AD7768_MED_MODE },
{ AD7768_MCLK_DIV_8, AD7768_DEC_RATE_1024, 8192, AD7768_MED_MODE },
{ AD7768_MCLK_DIV_16, AD7768_DEC_RATE_1024, 16384, AD7768_ECO_MODE },
};
static const struct iio_chan_spec ad7768_channels[] = {
{
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
.indexed = 1,
.channel = 0,
.scan_index = 0,
.scan_type = {
.sign = 'u',
.realbits = 24,
.storagebits = 32,
.shift = 8,
.endianness = IIO_BE,
},
},
};
struct ad7768_state {
struct spi_device *spi;
struct regulator *vref;
struct mutex lock;
struct clk *mclk;
unsigned int mclk_freq;
unsigned int samp_freq;
struct completion completion;
struct iio_trigger *trig;
struct gpio_desc *gpio_sync_in;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
*/
union {
__be32 d32;
u8 d8[2];
} data ____cacheline_aligned;
};
static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int addr,
unsigned int len)
{
unsigned int shift;
int ret;
shift = 32 - (8 * len);
st->data.d8[0] = AD7768_RD_FLAG_MSK(addr);
ret = spi_write_then_read(st->spi, st->data.d8, 1,
&st->data.d32, len);
if (ret < 0)
return ret;
return (be32_to_cpu(st->data.d32) >> shift);
}
static int ad7768_spi_reg_write(struct ad7768_state *st,
unsigned int addr,
unsigned int val)
{
st->data.d8[0] = AD7768_WR_FLAG_MSK(addr);
st->data.d8[1] = val & 0xFF;
return spi_write(st->spi, st->data.d8, 2);
}
static int ad7768_set_mode(struct ad7768_state *st,
enum ad7768_conv_mode mode)
{
int regval;
regval = ad7768_spi_reg_read(st, AD7768_REG_CONVERSION, 1);
if (regval < 0)
return regval;
regval &= ~AD7768_CONV_MODE_MSK;
regval |= AD7768_CONV_MODE(mode);
return ad7768_spi_reg_write(st, AD7768_REG_CONVERSION, regval);
}
static int ad7768_scan_direct(struct iio_dev *indio_dev)
{
struct ad7768_state *st = iio_priv(indio_dev);
int readval, ret;
reinit_completion(&st->completion);
ret = ad7768_set_mode(st, AD7768_ONE_SHOT);
if (ret < 0)
return ret;
ret = wait_for_completion_timeout(&st->completion,
msecs_to_jiffies(1000));
if (!ret)
return -ETIMEDOUT;
readval = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
if (readval < 0)
return readval;
/*
* Any SPI configuration of the AD7768-1 can only be
* performed in continuous conversion mode.
*/
ret = ad7768_set_mode(st, AD7768_CONTINUOUS);
if (ret < 0)
return ret;
return readval;
}
static int ad7768_reg_access(struct iio_dev *indio_dev,
unsigned int reg,
unsigned int writeval,
unsigned int *readval)
{
struct ad7768_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
if (readval) {
ret = ad7768_spi_reg_read(st, reg, 1);
if (ret < 0)
goto err_unlock;
*readval = ret;
ret = 0;
} else {
ret = ad7768_spi_reg_write(st, reg, writeval);
}
err_unlock:
mutex_unlock(&st->lock);
return ret;
}
static int ad7768_set_dig_fil(struct ad7768_state *st,
enum ad7768_dec_rate dec_rate)
{
unsigned int mode;
int ret;
if (dec_rate == AD7768_DEC_RATE_8 || dec_rate == AD7768_DEC_RATE_16)
mode = AD7768_DIG_FIL_FIL(dec_rate);
else
mode = AD7768_DIG_FIL_DEC_RATE(dec_rate);
ret = ad7768_spi_reg_write(st, AD7768_REG_DIGITAL_FILTER, mode);
if (ret < 0)
return ret;
/* A sync-in pulse is required every time the filter dec rate changes */
gpiod_set_value(st->gpio_sync_in, 1);
gpiod_set_value(st->gpio_sync_in, 0);
return 0;
}
static int ad7768_set_freq(struct ad7768_state *st,
unsigned int freq)
{
unsigned int diff_new, diff_old, pwr_mode, i, idx;
int res, ret;
diff_old = U32_MAX;
idx = 0;
res = DIV_ROUND_CLOSEST(st->mclk_freq, freq);
/* Find the closest match for the desired sampling frequency */
for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) {
diff_new = abs(res - ad7768_clk_config[i].clk_div);
if (diff_new < diff_old) {
diff_old = diff_new;
idx = i;
}
}
/*
* Set both the mclk_div and pwrmode with a single write to the
* POWER_CLOCK register
*/
pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div) |
AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode);
ret = ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK, pwr_mode);
if (ret < 0)
return ret;
ret = ad7768_set_dig_fil(st, ad7768_clk_config[idx].dec_rate);
if (ret < 0)
return ret;
st->samp_freq = DIV_ROUND_CLOSEST(st->mclk_freq,
ad7768_clk_config[idx].clk_div);
return 0;
}
static ssize_t ad7768_sampling_freq_avail(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct ad7768_state *st = iio_priv(indio_dev);
unsigned int freq;
int i, len = 0;
for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) {
freq = DIV_ROUND_CLOSEST(st->mclk_freq,
ad7768_clk_config[i].clk_div);
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", freq);
}
buf[len - 1] = '\n';
return len;
}
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ad7768_sampling_freq_avail);
static int ad7768_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long info)
{
struct ad7768_state *st = iio_priv(indio_dev);
int scale_uv, ret;
switch (info) {
case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev);
if (ret)
return ret;
ret = ad7768_scan_direct(indio_dev);
if (ret >= 0)
*val = ret;
iio_device_release_direct_mode(indio_dev);
if (ret < 0)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
scale_uv = regulator_get_voltage(st->vref);
if (scale_uv < 0)
return scale_uv;
*val = (scale_uv * 2) / 1000;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = st->samp_freq;
return IIO_VAL_INT;
}
return -EINVAL;
}
static int ad7768_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long info)
{
struct ad7768_state *st = iio_priv(indio_dev);
switch (info) {
case IIO_CHAN_INFO_SAMP_FREQ:
return ad7768_set_freq(st, val);
default:
return -EINVAL;
}
}
static struct attribute *ad7768_attributes[] = {
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
NULL
};
static const struct attribute_group ad7768_group = {
.attrs = ad7768_attributes,
};
static const struct iio_info ad7768_info = {
.attrs = &ad7768_group,
.read_raw = &ad7768_read_raw,
.write_raw = &ad7768_write_raw,
.debugfs_reg_access = &ad7768_reg_access,
};
static int ad7768_setup(struct ad7768_state *st)
{
int ret;
/*
* Two writes to the SPI_RESET[1:0] bits are required to initiate
* a software reset. The bits must first be set to 11, and then
* to 10. When the sequence is detected, the reset occurs.
* See the datasheet, page 70.
*/
ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x3);
if (ret)
return ret;
ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x2);
if (ret)
return ret;
st->gpio_sync_in = devm_gpiod_get(&st->spi->dev, "adi,sync-in",
GPIOD_OUT_LOW);
if (IS_ERR(st->gpio_sync_in))
return PTR_ERR(st->gpio_sync_in);
/* Set the default sampling frequency to 32000 kSPS */
return ad7768_set_freq(st, 32000);
}
static irqreturn_t ad7768_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ad7768_state *st = iio_priv(indio_dev);
int ret;
mutex_lock(&st->lock);
ret = spi_read(st->spi, &st->data.d32, 3);
if (ret < 0)
goto err_unlock;
iio_push_to_buffers_with_timestamp(indio_dev, &st->data.d32,
iio_get_time_ns(indio_dev));
iio_trigger_notify_done(indio_dev->trig);
err_unlock:
mutex_unlock(&st->lock);
return IRQ_HANDLED;
}
static irqreturn_t ad7768_interrupt(int irq, void *dev_id)
{
struct iio_dev *indio_dev = dev_id;
struct ad7768_state *st = iio_priv(indio_dev);
if (iio_buffer_enabled(indio_dev))
iio_trigger_poll(st->trig);
else
complete(&st->completion);
return IRQ_HANDLED;
};
static int ad7768_buffer_postenable(struct iio_dev *indio_dev)
{
struct ad7768_state *st = iio_priv(indio_dev);
iio_triggered_buffer_postenable(indio_dev);
/*
* Write a 1 to the LSB of the INTERFACE_FORMAT register to enter
* continuous read mode. Subsequent data reads do not require an
* initial 8-bit write to query the ADC_DATA register.
*/
return ad7768_spi_reg_write(st, AD7768_REG_INTERFACE_FORMAT, 0x01);
}
static int ad7768_buffer_predisable(struct iio_dev *indio_dev)
{
struct ad7768_state *st = iio_priv(indio_dev);
int ret;
/*
* To exit continuous read mode, perform a single read of the ADC_DATA
* reg (0x2C), which allows further configuration of the device.
*/
ret = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
if (ret < 0)
return ret;
return iio_triggered_buffer_predisable(indio_dev);
}
static const struct iio_buffer_setup_ops ad7768_buffer_ops = {
.postenable = &ad7768_buffer_postenable,
.predisable = &ad7768_buffer_predisable,
};
static const struct iio_trigger_ops ad7768_trigger_ops = {
.validate_device = iio_trigger_validate_own_device,
};
static void ad7768_regulator_disable(void *data)
{
struct ad7768_state *st = data;
regulator_disable(st->vref);
}
static void ad7768_clk_disable(void *data)
{
struct ad7768_state *st = data;
clk_disable_unprepare(st->mclk);
}
static int ad7768_probe(struct spi_device *spi)
{
struct ad7768_state *st;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
st->spi = spi;
st->vref = devm_regulator_get(&spi->dev, "vref");
if (IS_ERR(st->vref))
return PTR_ERR(st->vref);
ret = regulator_enable(st->vref);
if (ret) {
dev_err(&spi->dev, "Failed to enable specified vref supply\n");
return ret;
}
ret = devm_add_action_or_reset(&spi->dev, ad7768_regulator_disable, st);
if (ret)
return ret;
st->mclk = devm_clk_get(&spi->dev, "mclk");
if (IS_ERR(st->mclk))
return PTR_ERR(st->mclk);
ret = clk_prepare_enable(st->mclk);
if (ret < 0)
return ret;
ret = devm_add_action_or_reset(&spi->dev, ad7768_clk_disable, st);
if (ret)
return ret;
st->mclk_freq = clk_get_rate(st->mclk);
spi_set_drvdata(spi, indio_dev);
mutex_init(&st->lock);
indio_dev->channels = ad7768_channels;
indio_dev->num_channels = ARRAY_SIZE(ad7768_channels);
indio_dev->dev.parent = &spi->dev;
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->info = &ad7768_info;
indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
ret = ad7768_setup(st);
if (ret < 0) {
dev_err(&spi->dev, "AD7768 setup failed\n");
return ret;
}
st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
indio_dev->name, indio_dev->id);
if (!st->trig)
return -ENOMEM;
st->trig->ops = &ad7768_trigger_ops;
st->trig->dev.parent = &spi->dev;
iio_trigger_set_drvdata(st->trig, indio_dev);
ret = devm_iio_trigger_register(&spi->dev, st->trig);
if (ret)
return ret;
indio_dev->trig = iio_trigger_get(st->trig);
init_completion(&st->completion);
ret = devm_request_irq(&spi->dev, spi->irq,
&ad7768_interrupt,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
indio_dev->name, indio_dev);
if (ret)
return ret;
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
&iio_pollfunc_store_time,
&ad7768_trigger_handler,
&ad7768_buffer_ops);
if (ret)
return ret;
return devm_iio_device_register(&spi->dev, indio_dev);
}
static const struct spi_device_id ad7768_id_table[] = {
{ "ad7768-1", 0 },
{}
};
MODULE_DEVICE_TABLE(spi, ad7768_id_table);
static const struct of_device_id ad7768_of_match[] = {
{ .compatible = "adi,ad7768-1" },
{ },
};
MODULE_DEVICE_TABLE(of, ad7768_of_match);
static struct spi_driver ad7768_driver = {
.driver = {
.name = "ad7768-1",
.of_match_table = ad7768_of_match,
},
.probe = ad7768_probe,
.id_table = ad7768_id_table,
};
module_spi_driver(ad7768_driver);
MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7768-1 ADC driver");
MODULE_LICENSE("GPL v2");

View File

@ -115,6 +115,7 @@
#define MAX_ADC_V2_CHANNELS 10
#define MAX_ADC_V1_CHANNELS 8
#define MAX_EXYNOS3250_ADC_CHANNELS 2
#define MAX_EXYNOS4212_ADC_CHANNELS 4
#define MAX_S5PV210_ADC_CHANNELS 10
/* Bit definitions common for ADC_V1 and ADC_V2 */
@ -271,6 +272,19 @@ static void exynos_adc_v1_start_conv(struct exynos_adc *info,
writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
}
/* Exynos4212 and 4412 is like ADCv1 but with four channels only */
static const struct exynos_adc_data exynos4212_adc_data = {
.num_channels = MAX_EXYNOS4212_ADC_CHANNELS,
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
.needs_adc_phy = true,
.phy_offset = EXYNOS_ADCV1_PHY_OFFSET,
.init_hw = exynos_adc_v1_init_hw,
.exit_hw = exynos_adc_v1_exit_hw,
.clear_irq = exynos_adc_v1_clear_irq,
.start_conv = exynos_adc_v1_start_conv,
};
static const struct exynos_adc_data exynos_adc_v1_data = {
.num_channels = MAX_ADC_V1_CHANNELS,
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
@ -492,6 +506,9 @@ static const struct of_device_id exynos_adc_match[] = {
}, {
.compatible = "samsung,s5pv210-adc",
.data = &exynos_adc_s5pv210_data,
}, {
.compatible = "samsung,exynos4212-adc",
.data = &exynos4212_adc_data,
}, {
.compatible = "samsung,exynos-adc-v1",
.data = &exynos_adc_v1_data,
@ -929,7 +946,7 @@ static int exynos_adc_remove(struct platform_device *pdev)
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct exynos_adc *info = iio_priv(indio_dev);
if (IS_REACHABLE(CONFIG_INPUT)) {
if (IS_REACHABLE(CONFIG_INPUT) && info->input) {
free_irq(info->tsirq, info);
input_unregister_device(info->input);
}

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
*
* 3-channel, 10-bit ADC
*
* Copyright (C) 2011, 2012 Roland Stigge <stigge@antcom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>

View File

@ -26,6 +26,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/mfd/syscon.h>
#define MESON_SAR_ADC_REG0 0x00
#define MESON_SAR_ADC_REG0_PANEL_DETECT BIT(31)
@ -174,6 +175,9 @@
#define MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL GENMASK(6, 0)
#define MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED BIT(7)
#define MESON_HHI_DPLL_TOP_0 0x318
#define MESON_HHI_DPLL_TOP_0_TSC_BIT4 BIT(9)
/* for use with IIO_VAL_INT_PLUS_MICRO */
#define MILLION 1000000
@ -280,6 +284,7 @@ struct meson_sar_adc_priv {
struct completion done;
int calibbias;
int calibscale;
struct regmap *tsc_regmap;
bool temperature_sensor_calibrated;
u8 temperature_sensor_coefficient;
u16 temperature_sensor_adc_val;
@ -727,6 +732,15 @@ static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev)
return ret;
}
priv->tsc_regmap =
syscon_regmap_lookup_by_phandle(indio_dev->dev.parent->of_node,
"amlogic,hhi-sysctrl");
if (IS_ERR(priv->tsc_regmap)) {
dev_err(indio_dev->dev.parent,
"failed to get amlogic,hhi-sysctrl regmap\n");
return PTR_ERR(priv->tsc_regmap);
}
read_len = MESON_SAR_ADC_EFUSE_BYTES;
buf = nvmem_cell_read(temperature_calib, &read_len);
if (IS_ERR(buf)) {
@ -861,6 +875,22 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
priv->temperature_sensor_coefficient);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
MESON_SAR_ADC_DELTA_10_TS_C_MASK, regval);
if (priv->param->temperature_trimming_bits == 5) {
if (priv->temperature_sensor_coefficient & BIT(4))
regval = MESON_HHI_DPLL_TOP_0_TSC_BIT4;
else
regval = 0;
/*
* bit [4] (the 5th bit when starting to count at 1)
* of the TSC is located in the HHI register area.
*/
regmap_update_bits(priv->tsc_regmap,
MESON_HHI_DPLL_TOP_0,
MESON_HHI_DPLL_TOP_0_TSC_BIT4,
regval);
}
} else {
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
MESON_SAR_ADC_DELTA_10_TS_REVE1, 0);
@ -1064,6 +1094,9 @@ static const struct meson_sar_adc_param meson_sar_adc_meson8b_param = {
.bandgap_reg = MESON_SAR_ADC_DELTA_10,
.regmap_config = &meson_sar_adc_regmap_config_meson8,
.resolution = 10,
.temperature_trimming_bits = 5,
.temperature_multiplier = 10,
.temperature_divider = 32,
};
static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = {

View File

@ -0,0 +1,335 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Nuvoton Technology corporation.
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/mfd/syscon.h>
#include <linux/io.h>
#include <linux/iio/iio.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
struct npcm_adc {
bool int_status;
u32 adc_sample_hz;
struct device *dev;
void __iomem *regs;
struct clk *adc_clk;
wait_queue_head_t wq;
struct regulator *vref;
struct regmap *rst_regmap;
};
/* NPCM7xx reset module */
#define NPCM7XX_IPSRST1_OFFSET 0x020
#define NPCM7XX_IPSRST1_ADC_RST BIT(27)
/* ADC registers */
#define NPCM_ADCCON 0x00
#define NPCM_ADCDATA 0x04
/* ADCCON Register Bits */
#define NPCM_ADCCON_ADC_INT_EN BIT(21)
#define NPCM_ADCCON_REFSEL BIT(19)
#define NPCM_ADCCON_ADC_INT_ST BIT(18)
#define NPCM_ADCCON_ADC_EN BIT(17)
#define NPCM_ADCCON_ADC_RST BIT(16)
#define NPCM_ADCCON_ADC_CONV BIT(13)
#define NPCM_ADCCON_CH_MASK GENMASK(27, 24)
#define NPCM_ADCCON_CH(x) ((x) << 24)
#define NPCM_ADCCON_DIV_SHIFT 1
#define NPCM_ADCCON_DIV_MASK GENMASK(8, 1)
#define NPCM_ADC_DATA_MASK(x) ((x) & GENMASK(9, 0))
#define NPCM_ADC_ENABLE (NPCM_ADCCON_ADC_EN | NPCM_ADCCON_ADC_INT_EN)
/* ADC General Definition */
#define NPCM_RESOLUTION_BITS 10
#define NPCM_INT_VREF_MV 2000
#define NPCM_ADC_CHAN(ch) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = ch, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
}
static const struct iio_chan_spec npcm_adc_iio_channels[] = {
NPCM_ADC_CHAN(0),
NPCM_ADC_CHAN(1),
NPCM_ADC_CHAN(2),
NPCM_ADC_CHAN(3),
NPCM_ADC_CHAN(4),
NPCM_ADC_CHAN(5),
NPCM_ADC_CHAN(6),
NPCM_ADC_CHAN(7),
};
static irqreturn_t npcm_adc_isr(int irq, void *data)
{
u32 regtemp;
struct iio_dev *indio_dev = data;
struct npcm_adc *info = iio_priv(indio_dev);
regtemp = ioread32(info->regs + NPCM_ADCCON);
if (regtemp & NPCM_ADCCON_ADC_INT_ST) {
iowrite32(regtemp, info->regs + NPCM_ADCCON);
wake_up_interruptible(&info->wq);
info->int_status = true;
}
return IRQ_HANDLED;
}
static int npcm_adc_read(struct npcm_adc *info, int *val, u8 channel)
{
int ret;
u32 regtemp;
/* Select ADC channel */
regtemp = ioread32(info->regs + NPCM_ADCCON);
regtemp &= ~NPCM_ADCCON_CH_MASK;
info->int_status = false;
iowrite32(regtemp | NPCM_ADCCON_CH(channel) |
NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
ret = wait_event_interruptible_timeout(info->wq, info->int_status,
msecs_to_jiffies(10));
if (ret == 0) {
regtemp = ioread32(info->regs + NPCM_ADCCON);
if ((regtemp & NPCM_ADCCON_ADC_CONV) && info->rst_regmap) {
/* if conversion failed - reset ADC module */
regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET,
NPCM7XX_IPSRST1_ADC_RST);
msleep(100);
regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET,
0x0);
msleep(100);
/* Enable ADC and start conversion module */
iowrite32(NPCM_ADC_ENABLE | NPCM_ADCCON_ADC_CONV,
info->regs + NPCM_ADCCON);
dev_err(info->dev, "RESET ADC Complete\n");
}
return -ETIMEDOUT;
}
if (ret < 0)
return ret;
*val = NPCM_ADC_DATA_MASK(ioread32(info->regs + NPCM_ADCDATA));
return 0;
}
static int npcm_adc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
int ret;
int vref_uv;
struct npcm_adc *info = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&indio_dev->mlock);
ret = npcm_adc_read(info, val, chan->channel);
mutex_unlock(&indio_dev->mlock);
if (ret) {
dev_err(info->dev, "NPCM ADC read failed\n");
return ret;
}
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
if (info->vref) {
vref_uv = regulator_get_voltage(info->vref);
*val = vref_uv / 1000;
} else {
*val = NPCM_INT_VREF_MV;
}
*val2 = NPCM_RESOLUTION_BITS;
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_SAMP_FREQ:
*val = info->adc_sample_hz;
return IIO_VAL_INT;
default:
return -EINVAL;
}
return 0;
}
static const struct iio_info npcm_adc_iio_info = {
.read_raw = &npcm_adc_read_raw,
};
static const struct of_device_id npcm_adc_match[] = {
{ .compatible = "nuvoton,npcm750-adc", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, npcm_adc_match);
static int npcm_adc_probe(struct platform_device *pdev)
{
int ret;
int irq;
u32 div;
u32 reg_con;
struct resource *res;
struct npcm_adc *info;
struct iio_dev *indio_dev;
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
if (!indio_dev)
return -ENOMEM;
info = iio_priv(indio_dev);
info->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
info->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(info->regs))
return PTR_ERR(info->regs);
info->adc_clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(info->adc_clk)) {
dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n");
return PTR_ERR(info->adc_clk);
}
/* calculate ADC clock sample rate */
reg_con = ioread32(info->regs + NPCM_ADCCON);
div = reg_con & NPCM_ADCCON_DIV_MASK;
div = div >> NPCM_ADCCON_DIV_SHIFT;
info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2);
if (of_device_is_compatible(np, "nuvoton,npcm750-adc")) {
info->rst_regmap = syscon_regmap_lookup_by_compatible
("nuvoton,npcm750-rst");
if (IS_ERR(info->rst_regmap)) {
dev_err(&pdev->dev, "Failed to find nuvoton,npcm750-rst\n");
ret = PTR_ERR(info->rst_regmap);
goto err_disable_clk;
}
}
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_err(dev, "failed getting interrupt resource\n");
ret = -EINVAL;
goto err_disable_clk;
}
ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0,
"NPCM_ADC", indio_dev);
if (ret < 0) {
dev_err(dev, "failed requesting interrupt\n");
goto err_disable_clk;
}
reg_con = ioread32(info->regs + NPCM_ADCCON);
info->vref = devm_regulator_get_optional(&pdev->dev, "vref");
if (!IS_ERR(info->vref)) {
ret = regulator_enable(info->vref);
if (ret) {
dev_err(&pdev->dev, "Can't enable ADC reference voltage\n");
goto err_disable_clk;
}
iowrite32(reg_con & ~NPCM_ADCCON_REFSEL,
info->regs + NPCM_ADCCON);
} else {
/*
* Any error which is not ENODEV indicates the regulator
* has been specified and so is a failure case.
*/
if (PTR_ERR(info->vref) != -ENODEV) {
ret = PTR_ERR(info->vref);
goto err_disable_clk;
}
/* Use internal reference */
iowrite32(reg_con | NPCM_ADCCON_REFSEL,
info->regs + NPCM_ADCCON);
}
init_waitqueue_head(&info->wq);
reg_con = ioread32(info->regs + NPCM_ADCCON);
reg_con |= NPCM_ADC_ENABLE;
/* Enable the ADC Module */
iowrite32(reg_con, info->regs + NPCM_ADCCON);
/* Start ADC conversion */
iowrite32(reg_con | NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
platform_set_drvdata(pdev, indio_dev);
indio_dev->name = dev_name(&pdev->dev);
indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &npcm_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = npcm_adc_iio_channels;
indio_dev->num_channels = ARRAY_SIZE(npcm_adc_iio_channels);
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(&pdev->dev, "Couldn't register the device.\n");
goto err_iio_register;
}
pr_info("NPCM ADC driver probed\n");
return 0;
err_iio_register:
iowrite32(reg_con & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
if (!IS_ERR(info->vref))
regulator_disable(info->vref);
err_disable_clk:
clk_disable_unprepare(info->adc_clk);
return ret;
}
static int npcm_adc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct npcm_adc *info = iio_priv(indio_dev);
u32 regtemp;
iio_device_unregister(indio_dev);
regtemp = ioread32(info->regs + NPCM_ADCCON);
iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
if (!IS_ERR(info->vref))
regulator_disable(info->vref);
clk_disable_unprepare(info->adc_clk);
return 0;
}
static struct platform_driver npcm_adc_driver = {
.probe = npcm_adc_probe,
.remove = npcm_adc_remove,
.driver = {
.name = "npcm_adc",
.of_match_table = npcm_adc_match,
},
};
module_platform_driver(npcm_adc_driver);
MODULE_DESCRIPTION("Nuvoton NPCM ADC Driver");
MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,376 @@
// SPDX-License-Identifier: GPL-2.0
/* TI ADS124S0X chip family driver
* Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
*/
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/gpio/consumer.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/sysfs.h>
/* Commands */
#define ADS124S08_CMD_NOP 0x00
#define ADS124S08_CMD_WAKEUP 0x02
#define ADS124S08_CMD_PWRDWN 0x04
#define ADS124S08_CMD_RESET 0x06
#define ADS124S08_CMD_START 0x08
#define ADS124S08_CMD_STOP 0x0a
#define ADS124S08_CMD_SYOCAL 0x16
#define ADS124S08_CMD_SYGCAL 0x17
#define ADS124S08_CMD_SFOCAL 0x19
#define ADS124S08_CMD_RDATA 0x12
#define ADS124S08_CMD_RREG 0x20
#define ADS124S08_CMD_WREG 0x40
/* Registers */
#define ADS124S08_ID_REG 0x00
#define ADS124S08_STATUS 0x01
#define ADS124S08_INPUT_MUX 0x02
#define ADS124S08_PGA 0x03
#define ADS124S08_DATA_RATE 0x04
#define ADS124S08_REF 0x05
#define ADS124S08_IDACMAG 0x06
#define ADS124S08_IDACMUX 0x07
#define ADS124S08_VBIAS 0x08
#define ADS124S08_SYS 0x09
#define ADS124S08_OFCAL0 0x0a
#define ADS124S08_OFCAL1 0x0b
#define ADS124S08_OFCAL2 0x0c
#define ADS124S08_FSCAL0 0x0d
#define ADS124S08_FSCAL1 0x0e
#define ADS124S08_FSCAL2 0x0f
#define ADS124S08_GPIODAT 0x10
#define ADS124S08_GPIOCON 0x11
/* ADS124S0x common channels */
#define ADS124S08_AIN0 0x00
#define ADS124S08_AIN1 0x01
#define ADS124S08_AIN2 0x02
#define ADS124S08_AIN3 0x03
#define ADS124S08_AIN4 0x04
#define ADS124S08_AIN5 0x05
#define ADS124S08_AINCOM 0x0c
/* ADS124S08 only channels */
#define ADS124S08_AIN6 0x06
#define ADS124S08_AIN7 0x07
#define ADS124S08_AIN8 0x08
#define ADS124S08_AIN9 0x09
#define ADS124S08_AIN10 0x0a
#define ADS124S08_AIN11 0x0b
#define ADS124S08_MAX_CHANNELS 12
#define ADS124S08_POS_MUX_SHIFT 0x04
#define ADS124S08_INT_REF 0x09
#define ADS124S08_START_REG_MASK 0x1f
#define ADS124S08_NUM_BYTES_MASK 0x1f
#define ADS124S08_START_CONV 0x01
#define ADS124S08_STOP_CONV 0x00
enum ads124s_id {
ADS124S08_ID,
ADS124S06_ID,
};
struct ads124s_chip_info {
const struct iio_chan_spec *channels;
unsigned int num_channels;
};
struct ads124s_private {
const struct ads124s_chip_info *chip_info;
struct gpio_desc *reset_gpio;
struct spi_device *spi;
struct mutex lock;
u8 data[5] ____cacheline_aligned;
};
#define ADS124S08_CHAN(index) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = index, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.scan_index = index, \
.scan_type = { \
.sign = 'u', \
.realbits = 32, \
.storagebits = 32, \
}, \
}
static const struct iio_chan_spec ads124s06_channels[] = {
ADS124S08_CHAN(0),
ADS124S08_CHAN(1),
ADS124S08_CHAN(2),
ADS124S08_CHAN(3),
ADS124S08_CHAN(4),
ADS124S08_CHAN(5),
};
static const struct iio_chan_spec ads124s08_channels[] = {
ADS124S08_CHAN(0),
ADS124S08_CHAN(1),
ADS124S08_CHAN(2),
ADS124S08_CHAN(3),
ADS124S08_CHAN(4),
ADS124S08_CHAN(5),
ADS124S08_CHAN(6),
ADS124S08_CHAN(7),
ADS124S08_CHAN(8),
ADS124S08_CHAN(9),
ADS124S08_CHAN(10),
ADS124S08_CHAN(11),
};
static const struct ads124s_chip_info ads124s_chip_info_tbl[] = {
[ADS124S08_ID] = {
.channels = ads124s08_channels,
.num_channels = ARRAY_SIZE(ads124s08_channels),
},
[ADS124S06_ID] = {
.channels = ads124s06_channels,
.num_channels = ARRAY_SIZE(ads124s06_channels),
},
};
static int ads124s_write_cmd(struct iio_dev *indio_dev, u8 command)
{
struct ads124s_private *priv = iio_priv(indio_dev);
priv->data[0] = command;
return spi_write(priv->spi, &priv->data[0], 1);
}
static int ads124s_write_reg(struct iio_dev *indio_dev, u8 reg, u8 data)
{
struct ads124s_private *priv = iio_priv(indio_dev);
priv->data[0] = ADS124S08_CMD_WREG | reg;
priv->data[1] = 0x0;
priv->data[2] = data;
return spi_write(priv->spi, &priv->data[0], 3);
}
static int ads124s_reset(struct iio_dev *indio_dev)
{
struct ads124s_private *priv = iio_priv(indio_dev);
if (priv->reset_gpio) {
gpiod_set_value(priv->reset_gpio, 0);
udelay(200);
gpiod_set_value(priv->reset_gpio, 1);
} else {
return ads124s_write_cmd(indio_dev, ADS124S08_CMD_RESET);
}
return 0;
};
static int ads124s_read(struct iio_dev *indio_dev, unsigned int chan)
{
struct ads124s_private *priv = iio_priv(indio_dev);
int ret;
u32 tmp;
struct spi_transfer t[] = {
{
.tx_buf = &priv->data[0],
.len = 4,
.cs_change = 1,
}, {
.tx_buf = &priv->data[1],
.rx_buf = &priv->data[1],
.len = 4,
},
};
priv->data[0] = ADS124S08_CMD_RDATA;
memset(&priv->data[1], ADS124S08_CMD_NOP, sizeof(priv->data));
ret = spi_sync_transfer(priv->spi, t, ARRAY_SIZE(t));
if (ret < 0)
return ret;
tmp = priv->data[2] << 16 | priv->data[3] << 8 | priv->data[4];
return tmp;
}
static int ads124s_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long m)
{
struct ads124s_private *priv = iio_priv(indio_dev);
int ret;
mutex_lock(&priv->lock);
switch (m) {
case IIO_CHAN_INFO_RAW:
ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX,
chan->channel);
if (ret) {
dev_err(&priv->spi->dev, "Set ADC CH failed\n");
goto out;
}
ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV);
if (ret) {
dev_err(&priv->spi->dev, "Start conversions failed\n");
goto out;
}
ret = ads124s_read(indio_dev, chan->channel);
if (ret < 0) {
dev_err(&priv->spi->dev, "Read ADC failed\n");
goto out;
}
*val = ret;
ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV);
if (ret) {
dev_err(&priv->spi->dev, "Stop conversions failed\n");
goto out;
}
ret = IIO_VAL_INT;
break;
default:
ret = -EINVAL;
break;
}
out:
mutex_unlock(&priv->lock);
return ret;
}
static const struct iio_info ads124s_info = {
.read_raw = &ads124s_read_raw,
};
static irqreturn_t ads124s_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ads124s_private *priv = iio_priv(indio_dev);
u32 buffer[ADS124S08_MAX_CHANNELS + sizeof(s64)/sizeof(u16)];
int scan_index, j = 0;
int ret;
for_each_set_bit(scan_index, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX,
scan_index);
if (ret)
dev_err(&priv->spi->dev, "Set ADC CH failed\n");
ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV);
if (ret)
dev_err(&priv->spi->dev, "Start ADC conversions failed\n");
buffer[j] = ads124s_read(indio_dev, scan_index);
ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV);
if (ret)
dev_err(&priv->spi->dev, "Stop ADC conversions failed\n");
j++;
}
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
pf->timestamp);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int ads124s_probe(struct spi_device *spi)
{
struct ads124s_private *ads124s_priv;
struct iio_dev *indio_dev;
const struct spi_device_id *spi_id = spi_get_device_id(spi);
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ads124s_priv));
if (indio_dev == NULL)
return -ENOMEM;
ads124s_priv = iio_priv(indio_dev);
ads124s_priv->reset_gpio = devm_gpiod_get_optional(&spi->dev,
"reset", GPIOD_OUT_LOW);
if (IS_ERR(ads124s_priv->reset_gpio))
dev_info(&spi->dev, "Reset GPIO not defined\n");
ads124s_priv->chip_info = &ads124s_chip_info_tbl[spi_id->driver_data];
spi_set_drvdata(spi, indio_dev);
ads124s_priv->spi = spi;
indio_dev->name = spi_id->name;
indio_dev->dev.parent = &spi->dev;
indio_dev->dev.of_node = spi->dev.of_node;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = ads124s_priv->chip_info->channels;
indio_dev->num_channels = ads124s_priv->chip_info->num_channels;
indio_dev->info = &ads124s_info;
mutex_init(&ads124s_priv->lock);
ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
ads124s_trigger_handler, NULL);
if (ret) {
dev_err(&spi->dev, "iio triggered buffer setup failed\n");
return ret;
}
ads124s_reset(indio_dev);
return devm_iio_device_register(&spi->dev, indio_dev);
}
static const struct spi_device_id ads124s_id[] = {
{ "ads124s06", ADS124S06_ID },
{ "ads124s08", ADS124S08_ID },
{ }
};
MODULE_DEVICE_TABLE(spi, ads124s_id);
static const struct of_device_id ads124s_of_table[] = {
{ .compatible = "ti,ads124s06" },
{ .compatible = "ti,ads124s08" },
{ },
};
MODULE_DEVICE_TABLE(of, ads124s_of_table);
static struct spi_driver ads124s_driver = {
.driver = {
.name = "ads124s08",
.of_match_table = ads124s_of_table,
},
.probe = ads124s_probe,
.id_table = ads124s_id,
};
module_spi_driver(ads124s_driver);
MODULE_AUTHOR("Dan Murphy <dmuprhy@ti.com>");
MODULE_DESCRIPTION("TI TI_ADS12S0X ADC");
MODULE_LICENSE("GPL v2");

View File

@ -1273,8 +1273,10 @@ static int xadc_probe(struct platform_device *pdev)
xadc->threshold[i] = 0xffff;
else
xadc->threshold[i] = 0;
xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i),
ret = xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i),
xadc->threshold[i]);
if (ret)
goto err_free_irq;
}
/* Go to non-buffered mode */

View File

@ -61,6 +61,27 @@ config IAQCORE
iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds)
sensors
config PMS7003
tristate "Plantower PMS7003 particulate matter sensor"
depends on SERIAL_DEV_BUS
help
Say Y here to build support for the Plantower PMS7003 particulate
matter sensor.
To compile this driver as a module, choose M here: the module will
be called pms7003.
config SPS30
tristate "SPS30 particulate matter sensor"
depends on I2C
select CRC8
help
Say Y here to build support for the Sensirion SPS30 particulate
matter sensor.
To compile this driver as a module, choose M here: the module will
be called sps30.
config VZ89X
tristate "SGX Sensortech MiCS VZ89X VOC sensor"
depends on I2C

View File

@ -9,4 +9,7 @@ obj-$(CONFIG_BME680_I2C) += bme680_i2c.o
obj-$(CONFIG_BME680_SPI) += bme680_spi.o
obj-$(CONFIG_CCS811) += ccs811.o
obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
obj-$(CONFIG_PMS7003) += pms7003.o
obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o
obj-$(CONFIG_SPS30) += sps30.o
obj-$(CONFIG_VZ89X) += vz89x.o

View File

@ -70,10 +70,17 @@ static const struct acpi_device_id bme680_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
static const struct of_device_id bme680_of_i2c_match[] = {
{ .compatible = "bosch,bme680", },
{},
};
MODULE_DEVICE_TABLE(of, bme680_of_i2c_match);
static struct i2c_driver bme680_i2c_driver = {
.driver = {
.name = "bme680_i2c",
.acpi_match_table = ACPI_PTR(bme680_acpi_match),
.of_match_table = bme680_of_i2c_match,
},
.probe = bme680_i2c_probe,
.id_table = bme680_i2c_id,

View File

@ -6,6 +6,7 @@
*/
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
@ -110,10 +111,17 @@ static const struct acpi_device_id bme680_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
static const struct of_device_id bme680_of_spi_match[] = {
{ .compatible = "bosch,bme680", },
{},
};
MODULE_DEVICE_TABLE(of, bme680_of_spi_match);
static struct spi_driver bme680_spi_driver = {
.driver = {
.name = "bme680_spi",
.acpi_match_table = ACPI_PTR(bme680_acpi_match),
.of_match_table = bme680_of_spi_match,
},
.probe = bme680_spi_probe,
.id_table = bme680_spi_id,

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

@ -0,0 +1,591 @@
// SPDX-License-Identifier: GPL-2.0
/*
* sgp30.c - Support for Sensirion SGP Gas Sensors
*
* Copyright (C) 2018 Andreas Brauchli <andreas.brauchli@sensirion.com>
*
* I2C slave address: 0x58
*
* Datasheets:
* https://www.sensirion.com/file/datasheet_sgp30
* https://www.sensirion.com/file/datasheet_sgpc3
*
* TODO:
* - baseline support
* - humidity compensation
* - power mode switching (SGPC3)
*/
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/i2c.h>
#include <linux/of_device.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define SGP_WORD_LEN 2
#define SGP_CRC8_POLYNOMIAL 0x31
#define SGP_CRC8_INIT 0xff
#define SGP_CRC8_LEN 1
#define SGP_CMD(cmd_word) cpu_to_be16(cmd_word)
#define SGP_CMD_DURATION_US 12000
#define SGP_MEASUREMENT_DURATION_US 50000
#define SGP_CMD_LEN SGP_WORD_LEN
#define SGP_CMD_MAX_BUF_SIZE (SGP_CMD_LEN + 2 * SGP_WORD_LEN)
#define SGP_MEASUREMENT_LEN 2
#define SGP30_MEASURE_INTERVAL_HZ 1
#define SGPC3_MEASURE_INTERVAL_HZ 2
#define SGP_VERS_PRODUCT(data) ((((data)->feature_set) & 0xf000) >> 12)
#define SGP_VERS_RESERVED(data) ((((data)->feature_set) & 0x0800) >> 11)
#define SGP_VERS_GEN(data) ((((data)->feature_set) & 0x0600) >> 9)
#define SGP_VERS_ENG_BIT(data) ((((data)->feature_set) & 0x0100) >> 8)
#define SGP_VERS_MAJOR(data) ((((data)->feature_set) & 0x00e0) >> 5)
#define SGP_VERS_MINOR(data) (((data)->feature_set) & 0x001f)
DECLARE_CRC8_TABLE(sgp_crc8_table);
enum sgp_product_id {
SGP30 = 0,
SGPC3,
};
enum sgp30_channel_idx {
SGP30_IAQ_TVOC_IDX = 0,
SGP30_IAQ_CO2EQ_IDX,
SGP30_SIG_ETOH_IDX,
SGP30_SIG_H2_IDX,
};
enum sgpc3_channel_idx {
SGPC3_IAQ_TVOC_IDX = 10,
SGPC3_SIG_ETOH_IDX,
};
enum sgp_cmd {
SGP_CMD_IAQ_INIT = SGP_CMD(0x2003),
SGP_CMD_IAQ_MEASURE = SGP_CMD(0x2008),
SGP_CMD_GET_FEATURE_SET = SGP_CMD(0x202f),
SGP_CMD_GET_SERIAL_ID = SGP_CMD(0x3682),
SGP30_CMD_MEASURE_SIGNAL = SGP_CMD(0x2050),
SGPC3_CMD_MEASURE_RAW = SGP_CMD(0x2046),
};
struct sgp_version {
u8 major;
u8 minor;
};
struct sgp_crc_word {
__be16 value;
u8 crc8;
} __attribute__((__packed__));
union sgp_reading {
u8 start;
struct sgp_crc_word raw_words[4];
};
enum _iaq_buffer_state {
IAQ_BUFFER_EMPTY = 0,
IAQ_BUFFER_DEFAULT_VALS,
IAQ_BUFFER_VALID,
};
struct sgp_data {
struct i2c_client *client;
struct task_struct *iaq_thread;
struct mutex data_lock;
unsigned long iaq_init_start_jiffies;
unsigned long iaq_defval_skip_jiffies;
u16 product_id;
u16 feature_set;
unsigned long measure_interval_jiffies;
enum sgp_cmd iaq_init_cmd;
enum sgp_cmd measure_iaq_cmd;
enum sgp_cmd measure_gas_signals_cmd;
union sgp_reading buffer;
union sgp_reading iaq_buffer;
enum _iaq_buffer_state iaq_buffer_state;
};
struct sgp_device {
const struct iio_chan_spec *channels;
int num_channels;
};
static const struct sgp_version supported_versions_sgp30[] = {
{
.major = 1,
.minor = 0,
},
};
static const struct sgp_version supported_versions_sgpc3[] = {
{
.major = 0,
.minor = 4,
},
};
static const struct iio_chan_spec sgp30_channels[] = {
{
.type = IIO_CONCENTRATION,
.channel2 = IIO_MOD_VOC,
.modified = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.address = SGP30_IAQ_TVOC_IDX,
},
{
.type = IIO_CONCENTRATION,
.channel2 = IIO_MOD_CO2,
.modified = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.address = SGP30_IAQ_CO2EQ_IDX,
},
{
.type = IIO_CONCENTRATION,
.channel2 = IIO_MOD_ETHANOL,
.modified = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.address = SGP30_SIG_ETOH_IDX,
},
{
.type = IIO_CONCENTRATION,
.channel2 = IIO_MOD_H2,
.modified = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.address = SGP30_SIG_H2_IDX,
},
};
static const struct iio_chan_spec sgpc3_channels[] = {
{
.type = IIO_CONCENTRATION,
.channel2 = IIO_MOD_VOC,
.modified = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.address = SGPC3_IAQ_TVOC_IDX,
},
{
.type = IIO_CONCENTRATION,
.channel2 = IIO_MOD_ETHANOL,
.modified = 1,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.address = SGPC3_SIG_ETOH_IDX,
},
};
static const struct sgp_device sgp_devices[] = {
[SGP30] = {
.channels = sgp30_channels,
.num_channels = ARRAY_SIZE(sgp30_channels),
},
[SGPC3] = {
.channels = sgpc3_channels,
.num_channels = ARRAY_SIZE(sgpc3_channels),
},
};
/**
* sgp_verify_buffer() - verify the checksums of the data buffer words
*
* @data: SGP data
* @buf: Raw data buffer
* @word_count: Num data words stored in the buffer, excluding CRC bytes
*
* Return: 0 on success, negative error otherwise.
*/
static int sgp_verify_buffer(const struct sgp_data *data,
union sgp_reading *buf, size_t word_count)
{
size_t size = word_count * (SGP_WORD_LEN + SGP_CRC8_LEN);
int i;
u8 crc;
u8 *data_buf = &buf->start;
for (i = 0; i < size; i += SGP_WORD_LEN + SGP_CRC8_LEN) {
crc = crc8(sgp_crc8_table, &data_buf[i], SGP_WORD_LEN,
SGP_CRC8_INIT);
if (crc != data_buf[i + SGP_WORD_LEN]) {
dev_err(&data->client->dev, "CRC error\n");
return -EIO;
}
}
return 0;
}
/**
* sgp_read_cmd() - reads data from sensor after issuing a command
* The caller must hold data->data_lock for the duration of the call.
* @data: SGP data
* @cmd: SGP Command to issue
* @buf: Raw data buffer to use
* @word_count: Num words to read, excluding CRC bytes
*
* Return: 0 on success, negative error otherwise.
*/
static int sgp_read_cmd(struct sgp_data *data, enum sgp_cmd cmd,
union sgp_reading *buf, size_t word_count,
unsigned long duration_us)
{
int ret;
struct i2c_client *client = data->client;
size_t size = word_count * (SGP_WORD_LEN + SGP_CRC8_LEN);
u8 *data_buf;
ret = i2c_master_send(client, (const char *)&cmd, SGP_CMD_LEN);
if (ret != SGP_CMD_LEN)
return -EIO;
usleep_range(duration_us, duration_us + 1000);
if (word_count == 0)
return 0;
data_buf = &buf->start;
ret = i2c_master_recv(client, data_buf, size);
if (ret < 0)
return ret;
if (ret != size)
return -EIO;
return sgp_verify_buffer(data, buf, word_count);
}
/**
* sgp_measure_iaq() - measure and retrieve IAQ values from sensor
* The caller must hold data->data_lock for the duration of the call.
* @data: SGP data
*
* Return: 0 on success, -EBUSY on default values, negative error
* otherwise.
*/
static int sgp_measure_iaq(struct sgp_data *data)
{
int ret;
/* data contains default values */
bool default_vals = !time_after(jiffies, data->iaq_init_start_jiffies +
data->iaq_defval_skip_jiffies);
ret = sgp_read_cmd(data, data->measure_iaq_cmd, &data->iaq_buffer,
SGP_MEASUREMENT_LEN, SGP_MEASUREMENT_DURATION_US);
if (ret < 0)
return ret;
data->iaq_buffer_state = IAQ_BUFFER_DEFAULT_VALS;
if (default_vals)
return -EBUSY;
data->iaq_buffer_state = IAQ_BUFFER_VALID;
return 0;
}
static void sgp_iaq_thread_sleep_until(const struct sgp_data *data,
unsigned long sleep_jiffies)
{
const long IAQ_POLL = 50000;
while (!time_after(jiffies, sleep_jiffies)) {
usleep_range(IAQ_POLL, IAQ_POLL + 10000);
if (kthread_should_stop() || data->iaq_init_start_jiffies == 0)
return;
}
}
static int sgp_iaq_threadfn(void *p)
{
struct sgp_data *data = (struct sgp_data *)p;
unsigned long next_update_jiffies;
int ret;
while (!kthread_should_stop()) {
mutex_lock(&data->data_lock);
if (data->iaq_init_start_jiffies == 0) {
ret = sgp_read_cmd(data, data->iaq_init_cmd, NULL, 0,
SGP_CMD_DURATION_US);
if (ret < 0)
goto unlock_sleep_continue;
data->iaq_init_start_jiffies = jiffies;
}
ret = sgp_measure_iaq(data);
if (ret && ret != -EBUSY) {
dev_warn(&data->client->dev,
"IAQ measurement error [%d]\n", ret);
}
unlock_sleep_continue:
next_update_jiffies = jiffies + data->measure_interval_jiffies;
mutex_unlock(&data->data_lock);
sgp_iaq_thread_sleep_until(data, next_update_jiffies);
}
return 0;
}
static int sgp_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct sgp_data *data = iio_priv(indio_dev);
struct sgp_crc_word *words;
int ret;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
mutex_lock(&data->data_lock);
if (data->iaq_buffer_state != IAQ_BUFFER_VALID) {
mutex_unlock(&data->data_lock);
return -EBUSY;
}
words = data->iaq_buffer.raw_words;
switch (chan->address) {
case SGP30_IAQ_TVOC_IDX:
case SGPC3_IAQ_TVOC_IDX:
*val = 0;
*val2 = be16_to_cpu(words[1].value);
ret = IIO_VAL_INT_PLUS_NANO;
break;
case SGP30_IAQ_CO2EQ_IDX:
*val = 0;
*val2 = be16_to_cpu(words[0].value);
ret = IIO_VAL_INT_PLUS_MICRO;
break;
default:
ret = -EINVAL;
break;
}
mutex_unlock(&data->data_lock);
break;
case IIO_CHAN_INFO_RAW:
mutex_lock(&data->data_lock);
if (chan->address == SGPC3_SIG_ETOH_IDX) {
if (data->iaq_buffer_state == IAQ_BUFFER_EMPTY)
ret = -EBUSY;
else
ret = 0;
words = data->iaq_buffer.raw_words;
} else {
ret = sgp_read_cmd(data, data->measure_gas_signals_cmd,
&data->buffer, SGP_MEASUREMENT_LEN,
SGP_MEASUREMENT_DURATION_US);
words = data->buffer.raw_words;
}
if (ret) {
mutex_unlock(&data->data_lock);
return ret;
}
switch (chan->address) {
case SGP30_SIG_ETOH_IDX:
*val = be16_to_cpu(words[1].value);
ret = IIO_VAL_INT;
break;
case SGPC3_SIG_ETOH_IDX:
case SGP30_SIG_H2_IDX:
*val = be16_to_cpu(words[0].value);
ret = IIO_VAL_INT;
break;
default:
ret = -EINVAL;
break;
}
mutex_unlock(&data->data_lock);
break;
default:
return -EINVAL;
}
return ret;
}
static int sgp_check_compat(struct sgp_data *data,
unsigned int product_id)
{
const struct sgp_version *supported_versions;
u16 ix, num_fs;
u16 product, generation, major, minor;
/* driver does not match product */
generation = SGP_VERS_GEN(data);
if (generation != 0) {
dev_err(&data->client->dev,
"incompatible product generation %d != 0", generation);
return -ENODEV;
}
product = SGP_VERS_PRODUCT(data);
if (product != product_id) {
dev_err(&data->client->dev,
"sensor reports a different product: 0x%04hx\n",
product);
return -ENODEV;
}
if (SGP_VERS_RESERVED(data))
dev_warn(&data->client->dev, "reserved bit is set\n");
/* engineering samples are not supported: no interface guarantees */
if (SGP_VERS_ENG_BIT(data))
return -ENODEV;
switch (product) {
case SGP30:
supported_versions = supported_versions_sgp30;
num_fs = ARRAY_SIZE(supported_versions_sgp30);
break;
case SGPC3:
supported_versions = supported_versions_sgpc3;
num_fs = ARRAY_SIZE(supported_versions_sgpc3);
break;
default:
return -ENODEV;
}
major = SGP_VERS_MAJOR(data);
minor = SGP_VERS_MINOR(data);
for (ix = 0; ix < num_fs; ix++) {
if (major == supported_versions[ix].major &&
minor >= supported_versions[ix].minor)
return 0;
}
dev_err(&data->client->dev, "unsupported sgp version: %d.%d\n",
major, minor);
return -ENODEV;
}
static void sgp_init(struct sgp_data *data)
{
data->iaq_init_cmd = SGP_CMD_IAQ_INIT;
data->iaq_init_start_jiffies = 0;
data->iaq_buffer_state = IAQ_BUFFER_EMPTY;
switch (SGP_VERS_PRODUCT(data)) {
case SGP30:
data->measure_interval_jiffies = SGP30_MEASURE_INTERVAL_HZ * HZ;
data->measure_iaq_cmd = SGP_CMD_IAQ_MEASURE;
data->measure_gas_signals_cmd = SGP30_CMD_MEASURE_SIGNAL;
data->product_id = SGP30;
data->iaq_defval_skip_jiffies = 15 * HZ;
break;
case SGPC3:
data->measure_interval_jiffies = SGPC3_MEASURE_INTERVAL_HZ * HZ;
data->measure_iaq_cmd = SGPC3_CMD_MEASURE_RAW;
data->measure_gas_signals_cmd = SGPC3_CMD_MEASURE_RAW;
data->product_id = SGPC3;
data->iaq_defval_skip_jiffies =
43 * data->measure_interval_jiffies;
break;
};
}
static const struct iio_info sgp_info = {
.read_raw = sgp_read_raw,
};
static const struct of_device_id sgp_dt_ids[] = {
{ .compatible = "sensirion,sgp30", .data = (void *)SGP30 },
{ .compatible = "sensirion,sgpc3", .data = (void *)SGPC3 },
{ }
};
static int sgp_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct iio_dev *indio_dev;
struct sgp_data *data;
const struct of_device_id *of_id;
unsigned long product_id;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
of_id = of_match_device(sgp_dt_ids, &client->dev);
if (of_id)
product_id = (unsigned long)of_id->data;
else
product_id = id->driver_data;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
crc8_populate_msb(sgp_crc8_table, SGP_CRC8_POLYNOMIAL);
mutex_init(&data->data_lock);
/* get feature set version and write it to client data */
ret = sgp_read_cmd(data, SGP_CMD_GET_FEATURE_SET, &data->buffer, 1,
SGP_CMD_DURATION_US);
if (ret < 0)
return ret;
data->feature_set = be16_to_cpu(data->buffer.raw_words[0].value);
ret = sgp_check_compat(data, product_id);
if (ret)
return ret;
indio_dev->dev.parent = &client->dev;
indio_dev->info = &sgp_info;
indio_dev->name = id->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = sgp_devices[product_id].channels;
indio_dev->num_channels = sgp_devices[product_id].num_channels;
sgp_init(data);
ret = devm_iio_device_register(&client->dev, indio_dev);
if (ret) {
dev_err(&client->dev, "failed to register iio device\n");
return ret;
}
data->iaq_thread = kthread_run(sgp_iaq_threadfn, data,
"%s-iaq", data->client->name);
return 0;
}
static int sgp_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct sgp_data *data = iio_priv(indio_dev);
if (data->iaq_thread)
kthread_stop(data->iaq_thread);
return 0;
}
static const struct i2c_device_id sgp_id[] = {
{ "sgp30", SGP30 },
{ "sgpc3", SGPC3 },
{ }
};
MODULE_DEVICE_TABLE(i2c, sgp_id);
MODULE_DEVICE_TABLE(of, sgp_dt_ids);
static struct i2c_driver sgp_driver = {
.driver = {
.name = "sgp30",
.of_match_table = of_match_ptr(sgp_dt_ids),
},
.probe = sgp_probe,
.remove = sgp_remove,
.id_table = sgp_id,
};
module_i2c_driver(sgp_driver);
MODULE_AUTHOR("Andreas Brauchli <andreas.brauchli@sensirion.com>");
MODULE_AUTHOR("Pascal Sachs <pascal.sachs@sensirion.com>");
MODULE_DESCRIPTION("Sensirion SGP gas sensors");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,548 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Sensirion SPS30 particulate matter sensor driver
*
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
*
* I2C slave address: 0x69
*/
#include <asm/unaligned.h>
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/kernel.h>
#include <linux/module.h>
#define SPS30_CRC8_POLYNOMIAL 0x31
/* max number of bytes needed to store PM measurements or serial string */
#define SPS30_MAX_READ_SIZE 48
/* sensor measures reliably up to 3000 ug / m3 */
#define SPS30_MAX_PM 3000
/* minimum and maximum self cleaning periods in seconds */
#define SPS30_AUTO_CLEANING_PERIOD_MIN 0
#define SPS30_AUTO_CLEANING_PERIOD_MAX 604800
/* SPS30 commands */
#define SPS30_START_MEAS 0x0010
#define SPS30_STOP_MEAS 0x0104
#define SPS30_RESET 0xd304
#define SPS30_READ_DATA_READY_FLAG 0x0202
#define SPS30_READ_DATA 0x0300
#define SPS30_READ_SERIAL 0xd033
#define SPS30_START_FAN_CLEANING 0x5607
#define SPS30_AUTO_CLEANING_PERIOD 0x8004
/* not a sensor command per se, used only to distinguish write from read */
#define SPS30_READ_AUTO_CLEANING_PERIOD 0x8005
enum {
PM1,
PM2P5,
PM4,
PM10,
};
enum {
RESET,
MEASURING,
};
struct sps30_state {
struct i2c_client *client;
/*
* Guards against concurrent access to sensor registers.
* Must be held whenever sequence of commands is to be executed.
*/
struct mutex lock;
int state;
};
DECLARE_CRC8_TABLE(sps30_crc8_table);
static int sps30_write_then_read(struct sps30_state *state, u8 *txbuf,
int txsize, u8 *rxbuf, int rxsize)
{
int ret;
/*
* Sensor does not support repeated start so instead of
* sending two i2c messages in a row we just send one by one.
*/
ret = i2c_master_send(state->client, txbuf, txsize);
if (ret != txsize)
return ret < 0 ? ret : -EIO;
if (!rxbuf)
return 0;
ret = i2c_master_recv(state->client, rxbuf, rxsize);
if (ret != rxsize)
return ret < 0 ? ret : -EIO;
return 0;
}
static int sps30_do_cmd(struct sps30_state *state, u16 cmd, u8 *data, int size)
{
/*
* Internally sensor stores measurements in a following manner:
*
* PM1: upper two bytes, crc8, lower two bytes, crc8
* PM2P5: upper two bytes, crc8, lower two bytes, crc8
* PM4: upper two bytes, crc8, lower two bytes, crc8
* PM10: upper two bytes, crc8, lower two bytes, crc8
*
* What follows next are number concentration measurements and
* typical particle size measurement which we omit.
*/
u8 buf[SPS30_MAX_READ_SIZE] = { cmd >> 8, cmd };
int i, ret = 0;
switch (cmd) {
case SPS30_START_MEAS:
buf[2] = 0x03;
buf[3] = 0x00;
buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE);
ret = sps30_write_then_read(state, buf, 5, NULL, 0);
break;
case SPS30_STOP_MEAS:
case SPS30_RESET:
case SPS30_START_FAN_CLEANING:
ret = sps30_write_then_read(state, buf, 2, NULL, 0);
break;
case SPS30_READ_AUTO_CLEANING_PERIOD:
buf[0] = SPS30_AUTO_CLEANING_PERIOD >> 8;
buf[1] = (u8)SPS30_AUTO_CLEANING_PERIOD;
/* fall through */
case SPS30_READ_DATA_READY_FLAG:
case SPS30_READ_DATA:
case SPS30_READ_SERIAL:
/* every two data bytes are checksummed */
size += size / 2;
ret = sps30_write_then_read(state, buf, 2, buf, size);
break;
case SPS30_AUTO_CLEANING_PERIOD:
buf[2] = data[0];
buf[3] = data[1];
buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE);
buf[5] = data[2];
buf[6] = data[3];
buf[7] = crc8(sps30_crc8_table, &buf[5], 2, CRC8_INIT_VALUE);
ret = sps30_write_then_read(state, buf, 8, NULL, 0);
break;
}
if (ret)
return ret;
/* validate received data and strip off crc bytes */
for (i = 0; i < size; i += 3) {
u8 crc = crc8(sps30_crc8_table, &buf[i], 2, CRC8_INIT_VALUE);
if (crc != buf[i + 2]) {
dev_err(&state->client->dev,
"data integrity check failed\n");
return -EIO;
}
*data++ = buf[i];
*data++ = buf[i + 1];
}
return 0;
}
static s32 sps30_float_to_int_clamped(const u8 *fp)
{
int val = get_unaligned_be32(fp);
int mantissa = val & GENMASK(22, 0);
/* this is fine since passed float is always non-negative */
int exp = val >> 23;
int fraction, shift;
/* special case 0 */
if (!exp && !mantissa)
return 0;
exp -= 127;
if (exp < 0) {
/* return values ranging from 1 to 99 */
return ((((1 << 23) + mantissa) * 100) >> 23) >> (-exp);
}
/* return values ranging from 100 to 300000 */
shift = 23 - exp;
val = (1 << exp) + (mantissa >> shift);
if (val >= SPS30_MAX_PM)
return SPS30_MAX_PM * 100;
fraction = mantissa & GENMASK(shift - 1, 0);
return val * 100 + ((fraction * 100) >> shift);
}
static int sps30_do_meas(struct sps30_state *state, s32 *data, int size)
{
int i, ret, tries = 5;
u8 tmp[16];
if (state->state == RESET) {
ret = sps30_do_cmd(state, SPS30_START_MEAS, NULL, 0);
if (ret)
return ret;
state->state = MEASURING;
}
while (tries--) {
ret = sps30_do_cmd(state, SPS30_READ_DATA_READY_FLAG, tmp, 2);
if (ret)
return -EIO;
/* new measurements ready to be read */
if (tmp[1] == 1)
break;
msleep_interruptible(300);
}
if (tries == -1)
return -ETIMEDOUT;
ret = sps30_do_cmd(state, SPS30_READ_DATA, tmp, sizeof(int) * size);
if (ret)
return ret;
for (i = 0; i < size; i++)
data[i] = sps30_float_to_int_clamped(&tmp[4 * i]);
return 0;
}
static irqreturn_t sps30_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct sps30_state *state = iio_priv(indio_dev);
int ret;
s32 data[4 + 2]; /* PM1, PM2P5, PM4, PM10, timestamp */
mutex_lock(&state->lock);
ret = sps30_do_meas(state, data, 4);
mutex_unlock(&state->lock);
if (ret)
goto err;
iio_push_to_buffers_with_timestamp(indio_dev, data,
iio_get_time_ns(indio_dev));
err:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int sps30_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct sps30_state *state = iio_priv(indio_dev);
int data[4], ret = -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_MASSCONCENTRATION:
mutex_lock(&state->lock);
/* read up to the number of bytes actually needed */
switch (chan->channel2) {
case IIO_MOD_PM1:
ret = sps30_do_meas(state, data, 1);
break;
case IIO_MOD_PM2P5:
ret = sps30_do_meas(state, data, 2);
break;
case IIO_MOD_PM4:
ret = sps30_do_meas(state, data, 3);
break;
case IIO_MOD_PM10:
ret = sps30_do_meas(state, data, 4);
break;
}
mutex_unlock(&state->lock);
if (ret)
return ret;
*val = data[chan->address] / 100;
*val2 = (data[chan->address] % 100) * 10000;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_MASSCONCENTRATION:
switch (chan->channel2) {
case IIO_MOD_PM1:
case IIO_MOD_PM2P5:
case IIO_MOD_PM4:
case IIO_MOD_PM10:
*val = 0;
*val2 = 10000;
return IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
return -EINVAL;
}
static int sps30_do_cmd_reset(struct sps30_state *state)
{
int ret;
ret = sps30_do_cmd(state, SPS30_RESET, NULL, 0);
msleep(300);
/*
* Power-on-reset causes sensor to produce some glitch on i2c bus and
* some controllers end up in error state. Recover simply by placing
* some data on the bus, for example STOP_MEAS command, which
* is NOP in this case.
*/
sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0);
state->state = RESET;
return ret;
}
static ssize_t start_cleaning_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct sps30_state *state = iio_priv(indio_dev);
int val, ret;
if (kstrtoint(buf, 0, &val) || val != 1)
return -EINVAL;
mutex_lock(&state->lock);
ret = sps30_do_cmd(state, SPS30_START_FAN_CLEANING, NULL, 0);
mutex_unlock(&state->lock);
if (ret)
return ret;
return len;
}
static ssize_t cleaning_period_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct sps30_state *state = iio_priv(indio_dev);
u8 tmp[4];
int ret;
mutex_lock(&state->lock);
ret = sps30_do_cmd(state, SPS30_READ_AUTO_CLEANING_PERIOD, tmp, 4);
mutex_unlock(&state->lock);
if (ret)
return ret;
return sprintf(buf, "%d\n", get_unaligned_be32(tmp));
}
static ssize_t cleaning_period_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct sps30_state *state = iio_priv(indio_dev);
int val, ret;
u8 tmp[4];
if (kstrtoint(buf, 0, &val))
return -EINVAL;
if ((val < SPS30_AUTO_CLEANING_PERIOD_MIN) ||
(val > SPS30_AUTO_CLEANING_PERIOD_MAX))
return -EINVAL;
put_unaligned_be32(val, tmp);
mutex_lock(&state->lock);
ret = sps30_do_cmd(state, SPS30_AUTO_CLEANING_PERIOD, tmp, 0);
if (ret) {
mutex_unlock(&state->lock);
return ret;
}
msleep(20);
/*
* sensor requires reset in order to return up to date self cleaning
* period
*/
ret = sps30_do_cmd_reset(state);
if (ret)
dev_warn(dev,
"period changed but reads will return the old value\n");
mutex_unlock(&state->lock);
return len;
}
static ssize_t cleaning_period_available_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
return snprintf(buf, PAGE_SIZE, "[%d %d %d]\n",
SPS30_AUTO_CLEANING_PERIOD_MIN, 1,
SPS30_AUTO_CLEANING_PERIOD_MAX);
}
static IIO_DEVICE_ATTR_WO(start_cleaning, 0);
static IIO_DEVICE_ATTR_RW(cleaning_period, 0);
static IIO_DEVICE_ATTR_RO(cleaning_period_available, 0);
static struct attribute *sps30_attrs[] = {
&iio_dev_attr_start_cleaning.dev_attr.attr,
&iio_dev_attr_cleaning_period.dev_attr.attr,
&iio_dev_attr_cleaning_period_available.dev_attr.attr,
NULL
};
static const struct attribute_group sps30_attr_group = {
.attrs = sps30_attrs,
};
static const struct iio_info sps30_info = {
.attrs = &sps30_attr_group,
.read_raw = sps30_read_raw,
};
#define SPS30_CHAN(_index, _mod) { \
.type = IIO_MASSCONCENTRATION, \
.modified = 1, \
.channel2 = IIO_MOD_ ## _mod, \
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.address = _mod, \
.scan_index = _index, \
.scan_type = { \
.sign = 'u', \
.realbits = 19, \
.storagebits = 32, \
.endianness = IIO_CPU, \
}, \
}
static const struct iio_chan_spec sps30_channels[] = {
SPS30_CHAN(0, PM1),
SPS30_CHAN(1, PM2P5),
SPS30_CHAN(2, PM4),
SPS30_CHAN(3, PM10),
IIO_CHAN_SOFT_TIMESTAMP(4),
};
static void sps30_stop_meas(void *data)
{
struct sps30_state *state = data;
sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0);
}
static const unsigned long sps30_scan_masks[] = { 0x0f, 0x00 };
static int sps30_probe(struct i2c_client *client)
{
struct iio_dev *indio_dev;
struct sps30_state *state;
u8 buf[32];
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -EOPNOTSUPP;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*state));
if (!indio_dev)
return -ENOMEM;
state = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
state->client = client;
state->state = RESET;
indio_dev->dev.parent = &client->dev;
indio_dev->info = &sps30_info;
indio_dev->name = client->name;
indio_dev->channels = sps30_channels;
indio_dev->num_channels = ARRAY_SIZE(sps30_channels);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->available_scan_masks = sps30_scan_masks;
mutex_init(&state->lock);
crc8_populate_msb(sps30_crc8_table, SPS30_CRC8_POLYNOMIAL);
ret = sps30_do_cmd_reset(state);
if (ret) {
dev_err(&client->dev, "failed to reset device\n");
return ret;
}
ret = sps30_do_cmd(state, SPS30_READ_SERIAL, buf, sizeof(buf));
if (ret) {
dev_err(&client->dev, "failed to read serial number\n");
return ret;
}
/* returned serial number is already NUL terminated */
dev_info(&client->dev, "serial number: %s\n", buf);
ret = devm_add_action_or_reset(&client->dev, sps30_stop_meas, state);
if (ret)
return ret;
ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL,
sps30_trigger_handler, NULL);
if (ret)
return ret;
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id sps30_id[] = {
{ "sps30" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sps30_id);
static const struct of_device_id sps30_of_match[] = {
{ .compatible = "sensirion,sps30" },
{ }
};
MODULE_DEVICE_TABLE(of, sps30_of_match);
static struct i2c_driver sps30_driver = {
.driver = {
.name = "sps30",
.of_match_table = sps30_of_match,
},
.id_table = sps30_id,
.probe_new = sps30_probe,
};
module_i2c_driver(sps30_driver);
MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor driver");
MODULE_LICENSE("GPL v2");

View File

@ -148,9 +148,9 @@ config AD5686_SPI
depends on SPI
select AD5686
help
Say yes here to build support for Analog Devices AD5672R, AD5676,
AD5676R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, AD5686R.
Voltage Output Digital to Analog Converter.
Say yes here to build support for Analog Devices AD5672R, AD5674R,
AD5676, AD5676R, AD5679R, AD5684, AD5684R, AD5684R, AD5685R, AD5686,
AD5686R Voltage Output Digital to Analog Converter.
To compile this driver as a module, choose M here: the
module will be called ad5686.
@ -375,6 +375,16 @@ config TI_DAC7311
If compiled as a module, it will be called ti-dac7311.
config TI_DAC7612
tristate "Texas Instruments 12-bit 2-channel DAC driver"
depends on SPI_MASTER && GPIOLIB
help
Driver for the Texas Instruments DAC7612, DAC7612U, DAC7612UB
The driver hand drive the load pin automatically, otherwise
it needs to be toggled manually.
If compiled as a module, it will be called ti-dac7612.
config VF610_DAC
tristate "Vybrid vf610 DAC driver"
depends on OF

View File

@ -41,4 +41,5 @@ obj-$(CONFIG_STM32_DAC) += stm32-dac.o
obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o
obj-$(CONFIG_TI_DAC5571) += ti-dac5571.o
obj-$(CONFIG_TI_DAC7311) += ti-dac7311.o
obj-$(CONFIG_TI_DAC7612) += ti-dac7612.o
obj-$(CONFIG_VF610_DAC) += vf610_dac.o

View File

@ -1,7 +1,8 @@
// SPDX-License-Identifier: GPL-2.0+
// SPDX-License-Identifier: GPL-2.0
/*
* AD5672R, AD5676, AD5676R, AD5681R, AD5682R, AD5683, AD5683R,
* AD5684, AD5684R, AD5685R, AD5686, AD5686R
* AD5672R, AD5674R, AD5676, AD5676R, AD5679R,
* AD5681R, AD5682R, AD5683, AD5683R, AD5684,
* AD5684R, AD5685R, AD5686, AD5686R
* Digital to analog converters driver
*
* Copyright 2018 Analog Devices Inc.
@ -102,8 +103,10 @@ static int ad5686_spi_remove(struct spi_device *spi)
static const struct spi_device_id ad5686_spi_id[] = {
{"ad5310r", ID_AD5310R},
{"ad5672r", ID_AD5672R},
{"ad5674r", ID_AD5674R},
{"ad5676", ID_AD5676},
{"ad5676r", ID_AD5676R},
{"ad5679r", ID_AD5679R},
{"ad5681r", ID_AD5681R},
{"ad5682r", ID_AD5682R},
{"ad5683", ID_AD5683},

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
*
@ -71,7 +71,7 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
int ret;
struct ad5686_state *st = iio_priv(indio_dev);
unsigned int val, ref_bit_msk;
u8 shift;
u8 shift, address = 0;
ret = strtobool(buf, &readin);
if (ret)
@ -94,6 +94,9 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
case AD5686_REGMAP:
shift = 0;
ref_bit_msk = 0;
/* AD5674R/AD5679R have 16 channels and 2 powerdown registers */
if (chan->channel > 0x7)
address = 0x8;
break;
case AD5693_REGMAP:
shift = 13;
@ -107,7 +110,8 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
if (!st->use_internal_vref)
val |= ref_bit_msk;
ret = st->write(st, AD5686_CMD_POWERDOWN_DAC, 0, val);
ret = st->write(st, AD5686_CMD_POWERDOWN_DAC,
address, val >> (address * 2));
return ret ? ret : len;
}
@ -226,10 +230,32 @@ static struct iio_chan_spec name[] = { \
AD5868_CHANNEL(7, 7, bits, _shift), \
}
#define DECLARE_AD5679_CHANNELS(name, bits, _shift) \
static struct iio_chan_spec name[] = { \
AD5868_CHANNEL(0, 0, bits, _shift), \
AD5868_CHANNEL(1, 1, bits, _shift), \
AD5868_CHANNEL(2, 2, bits, _shift), \
AD5868_CHANNEL(3, 3, bits, _shift), \
AD5868_CHANNEL(4, 4, bits, _shift), \
AD5868_CHANNEL(5, 5, bits, _shift), \
AD5868_CHANNEL(6, 6, bits, _shift), \
AD5868_CHANNEL(7, 7, bits, _shift), \
AD5868_CHANNEL(8, 8, bits, _shift), \
AD5868_CHANNEL(9, 9, bits, _shift), \
AD5868_CHANNEL(10, 10, bits, _shift), \
AD5868_CHANNEL(11, 11, bits, _shift), \
AD5868_CHANNEL(12, 12, bits, _shift), \
AD5868_CHANNEL(13, 13, bits, _shift), \
AD5868_CHANNEL(14, 14, bits, _shift), \
AD5868_CHANNEL(15, 15, bits, _shift), \
}
DECLARE_AD5693_CHANNELS(ad5310r_channels, 10, 2);
DECLARE_AD5693_CHANNELS(ad5311r_channels, 10, 6);
DECLARE_AD5676_CHANNELS(ad5672_channels, 12, 4);
DECLARE_AD5679_CHANNELS(ad5674r_channels, 12, 4);
DECLARE_AD5676_CHANNELS(ad5676_channels, 16, 0);
DECLARE_AD5679_CHANNELS(ad5679r_channels, 16, 0);
DECLARE_AD5686_CHANNELS(ad5684_channels, 12, 4);
DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2);
DECLARE_AD5686_CHANNELS(ad5686_channels, 16, 0);
@ -262,6 +288,12 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = {
.num_channels = 8,
.regmap_type = AD5686_REGMAP,
},
[ID_AD5674R] = {
.channels = ad5674r_channels,
.int_vref_mv = 2500,
.num_channels = 16,
.regmap_type = AD5686_REGMAP,
},
[ID_AD5675R] = {
.channels = ad5676_channels,
.int_vref_mv = 2500,
@ -279,6 +311,12 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = {
.num_channels = 8,
.regmap_type = AD5686_REGMAP,
},
[ID_AD5679R] = {
.channels = ad5679r_channels,
.int_vref_mv = 2500,
.num_channels = 16,
.regmap_type = AD5686_REGMAP,
},
[ID_AD5681R] = {
.channels = ad5691r_channels,
.int_vref_mv = 2500,

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
*
@ -54,9 +54,11 @@ enum ad5686_supported_device_ids {
ID_AD5311R,
ID_AD5671R,
ID_AD5672R,
ID_AD5674R,
ID_AD5675R,
ID_AD5676,
ID_AD5676R,
ID_AD5679R,
ID_AD5681R,
ID_AD5682R,
ID_AD5683,

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
// SPDX-License-Identifier: GPL-2.0
/*
* AD5671R, AD5675R, AD5691R, AD5692R, AD5693, AD5693R,
* AD5694, AD5694R, AD5695R, AD5696, AD5696R

View File

@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
// SPDX-License-Identifier: GPL-2.0
/*
* 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

@ -943,11 +943,14 @@ static int ad9523_setup(struct iio_dev *indio_dev)
}
}
for_each_clear_bit(i, &active_mask, AD9523_NUM_CHAN)
ad9523_write(indio_dev,
for_each_clear_bit(i, &active_mask, AD9523_NUM_CHAN) {
ret = ad9523_write(indio_dev,
AD9523_CHANNEL_CLOCK_DIST(i),
AD9523_CLK_DIST_DRIVER_MODE(TRISTATE) |
AD9523_CLK_DIST_PWR_DOWN_EN);
if (ret < 0)
return ret;
}
ret = ad9523_write(indio_dev, AD9523_POWER_DOWN_CTRL, 0);
if (ret < 0)

View File

@ -2,9 +2,20 @@
#ifndef BMI160_H_
#define BMI160_H_
#include <linux/iio/iio.h>
struct bmi160_data {
struct regmap *regmap;
struct iio_trigger *trig;
};
extern const struct regmap_config bmi160_regmap_config;
int bmi160_core_probe(struct device *dev, struct regmap *regmap,
const char *name, bool use_spi);
int bmi160_enable_irq(struct regmap *regmap, bool enable);
int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type);
#endif /* BMI160_H_ */

View File

@ -1,26 +1,27 @@
// SPDX-License-Identifier: GPL-2.0
/*
* BMI160 - Bosch IMU (accel, gyro plus external magnetometer)
*
* Copyright (c) 2016, Intel Corporation.
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details.
* Copyright (c) 2019, Martin Kelly.
*
* IIO core driver for BMI160, with support for I2C/SPI busses
*
* TODO: magnetometer, interrupts, hardware FIFO
* TODO: magnetometer, hardware FIFO
*/
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/iio/iio.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/buffer.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include "bmi160.h"
@ -64,8 +65,32 @@
#define BMI160_CMD_GYRO_PM_FAST_STARTUP 0x17
#define BMI160_CMD_SOFTRESET 0xB6
#define BMI160_REG_INT_EN 0x51
#define BMI160_DRDY_INT_EN BIT(4)
#define BMI160_REG_INT_OUT_CTRL 0x53
#define BMI160_INT_OUT_CTRL_MASK 0x0f
#define BMI160_INT1_OUT_CTRL_SHIFT 0
#define BMI160_INT2_OUT_CTRL_SHIFT 4
#define BMI160_EDGE_TRIGGERED BIT(0)
#define BMI160_ACTIVE_HIGH BIT(1)
#define BMI160_OPEN_DRAIN BIT(2)
#define BMI160_OUTPUT_EN BIT(3)
#define BMI160_REG_INT_LATCH 0x54
#define BMI160_INT1_LATCH_MASK BIT(4)
#define BMI160_INT2_LATCH_MASK BIT(5)
/* INT1 and INT2 are in the opposite order as in INT_OUT_CTRL! */
#define BMI160_REG_INT_MAP 0x56
#define BMI160_INT1_MAP_DRDY_EN 0x80
#define BMI160_INT2_MAP_DRDY_EN 0x08
#define BMI160_REG_DUMMY 0x7F
#define BMI160_NORMAL_WRITE_USLEEP 2
#define BMI160_SUSPENDED_WRITE_USLEEP 450
#define BMI160_ACCEL_PMU_MIN_USLEEP 3800
#define BMI160_GYRO_PMU_MIN_USLEEP 80000
#define BMI160_SOFTRESET_USLEEP 1000
@ -108,8 +133,9 @@ enum bmi160_sensor_type {
BMI160_NUM_SENSORS /* must be last */
};
struct bmi160_data {
struct regmap *regmap;
enum bmi160_int_pin {
BMI160_PIN_INT1,
BMI160_PIN_INT2
};
const struct regmap_config bmi160_regmap_config = {
@ -273,7 +299,7 @@ int bmi160_set_mode(struct bmi160_data *data, enum bmi160_sensor_type t,
cmd = bmi160_regs[t].pmu_cmd_suspend;
ret = regmap_write(data->regmap, BMI160_REG_CMD, cmd);
if (ret < 0)
if (ret)
return ret;
usleep_range(bmi160_pmu_time[t], bmi160_pmu_time[t] + 1000);
@ -305,7 +331,7 @@ int bmi160_get_scale(struct bmi160_data *data, enum bmi160_sensor_type t,
int i, ret, val;
ret = regmap_read(data->regmap, bmi160_regs[t].range, &val);
if (ret < 0)
if (ret)
return ret;
for (i = 0; i < bmi160_scale_table[t].num; i++)
@ -328,7 +354,7 @@ static int bmi160_get_data(struct bmi160_data *data, int chan_type,
reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(sample);
ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(sample));
if (ret < 0)
if (ret)
return ret;
*val = sign_extend32(le16_to_cpu(sample), 15);
@ -362,7 +388,7 @@ static int bmi160_get_odr(struct bmi160_data *data, enum bmi160_sensor_type t,
int i, val, ret;
ret = regmap_read(data->regmap, bmi160_regs[t].config, &val);
if (ret < 0)
if (ret)
return ret;
val &= bmi160_regs[t].config_odr_mask;
@ -394,13 +420,12 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p)
indio_dev->masklength) {
ret = regmap_bulk_read(data->regmap, base + i * sizeof(sample),
&sample, sizeof(sample));
if (ret < 0)
if (ret)
goto done;
buf[j++] = sample;
}
iio_push_to_buffers_with_timestamp(indio_dev, buf,
iio_get_time_ns(indio_dev));
iio_push_to_buffers_with_timestamp(indio_dev, buf, pf->timestamp);
done:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
@ -416,18 +441,18 @@ static int bmi160_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = bmi160_get_data(data, chan->type, chan->channel2, val);
if (ret < 0)
if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
ret = bmi160_get_scale(data,
bmi160_to_sensor(chan->type), val2);
return ret < 0 ? ret : IIO_VAL_INT_PLUS_MICRO;
return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = bmi160_get_odr(data, bmi160_to_sensor(chan->type),
val, val2);
return ret < 0 ? ret : IIO_VAL_INT_PLUS_MICRO;
return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
@ -498,6 +523,186 @@ static const char *bmi160_match_acpi_device(struct device *dev)
return dev_name(dev);
}
static int bmi160_write_conf_reg(struct regmap *regmap, unsigned int reg,
unsigned int mask, unsigned int bits,
unsigned int write_usleep)
{
int ret;
unsigned int val;
ret = regmap_read(regmap, reg, &val);
if (ret)
return ret;
val = (val & ~mask) | bits;
ret = regmap_write(regmap, reg, val);
if (ret)
return ret;
/*
* We need to wait after writing before we can write again. See the
* datasheet, page 93.
*/
usleep_range(write_usleep, write_usleep + 1000);
return 0;
}
static int bmi160_config_pin(struct regmap *regmap, enum bmi160_int_pin pin,
bool open_drain, u8 irq_mask,
unsigned long write_usleep)
{
int ret;
struct device *dev = regmap_get_device(regmap);
u8 int_out_ctrl_shift;
u8 int_latch_mask;
u8 int_map_mask;
u8 int_out_ctrl_mask;
u8 int_out_ctrl_bits;
const char *pin_name;
switch (pin) {
case BMI160_PIN_INT1:
int_out_ctrl_shift = BMI160_INT1_OUT_CTRL_SHIFT;
int_latch_mask = BMI160_INT1_LATCH_MASK;
int_map_mask = BMI160_INT1_MAP_DRDY_EN;
break;
case BMI160_PIN_INT2:
int_out_ctrl_shift = BMI160_INT2_OUT_CTRL_SHIFT;
int_latch_mask = BMI160_INT2_LATCH_MASK;
int_map_mask = BMI160_INT2_MAP_DRDY_EN;
break;
}
int_out_ctrl_mask = BMI160_INT_OUT_CTRL_MASK << int_out_ctrl_shift;
/*
* Enable the requested pin with the right settings:
* - Push-pull/open-drain
* - Active low/high
* - Edge/level triggered
*/
int_out_ctrl_bits = BMI160_OUTPUT_EN;
if (open_drain)
/* Default is push-pull. */
int_out_ctrl_bits |= BMI160_OPEN_DRAIN;
int_out_ctrl_bits |= irq_mask;
int_out_ctrl_bits <<= int_out_ctrl_shift;
ret = bmi160_write_conf_reg(regmap, BMI160_REG_INT_OUT_CTRL,
int_out_ctrl_mask, int_out_ctrl_bits,
write_usleep);
if (ret)
return ret;
/* Set the pin to input mode with no latching. */
ret = bmi160_write_conf_reg(regmap, BMI160_REG_INT_LATCH,
int_latch_mask, int_latch_mask,
write_usleep);
if (ret)
return ret;
/* Map interrupts to the requested pin. */
ret = bmi160_write_conf_reg(regmap, BMI160_REG_INT_MAP,
int_map_mask, int_map_mask,
write_usleep);
if (ret) {
switch (pin) {
case BMI160_PIN_INT1:
pin_name = "INT1";
break;
case BMI160_PIN_INT2:
pin_name = "INT2";
break;
}
dev_err(dev, "Failed to configure %s IRQ pin", pin_name);
}
return ret;
}
int bmi160_enable_irq(struct regmap *regmap, bool enable)
{
unsigned int enable_bit = 0;
if (enable)
enable_bit = BMI160_DRDY_INT_EN;
return bmi160_write_conf_reg(regmap, BMI160_REG_INT_EN,
BMI160_DRDY_INT_EN, enable_bit,
BMI160_NORMAL_WRITE_USLEEP);
}
EXPORT_SYMBOL(bmi160_enable_irq);
static int bmi160_get_irq(struct device_node *of_node, enum bmi160_int_pin *pin)
{
int irq;
/* Use INT1 if possible, otherwise fall back to INT2. */
irq = of_irq_get_byname(of_node, "INT1");
if (irq > 0) {
*pin = BMI160_PIN_INT1;
return irq;
}
irq = of_irq_get_byname(of_node, "INT2");
if (irq > 0)
*pin = BMI160_PIN_INT2;
return irq;
}
static int bmi160_config_device_irq(struct iio_dev *indio_dev, int irq_type,
enum bmi160_int_pin pin)
{
bool open_drain;
u8 irq_mask;
struct bmi160_data *data = iio_priv(indio_dev);
struct device *dev = regmap_get_device(data->regmap);
/* Level-triggered, active-low is the default if we set all zeroes. */
if (irq_type == IRQF_TRIGGER_RISING)
irq_mask = BMI160_ACTIVE_HIGH | BMI160_EDGE_TRIGGERED;
else if (irq_type == IRQF_TRIGGER_FALLING)
irq_mask = BMI160_EDGE_TRIGGERED;
else if (irq_type == IRQF_TRIGGER_HIGH)
irq_mask = BMI160_ACTIVE_HIGH;
else if (irq_type == IRQF_TRIGGER_LOW)
irq_mask = 0;
else {
dev_err(&indio_dev->dev,
"Invalid interrupt type 0x%x specified\n", irq_type);
return -EINVAL;
}
open_drain = of_property_read_bool(dev->of_node, "drive-open-drain");
return bmi160_config_pin(data->regmap, pin, open_drain, irq_mask,
BMI160_NORMAL_WRITE_USLEEP);
}
static int bmi160_setup_irq(struct iio_dev *indio_dev, int irq,
enum bmi160_int_pin pin)
{
struct irq_data *desc;
u32 irq_type;
int ret;
desc = irq_get_irq_data(irq);
if (!desc) {
dev_err(&indio_dev->dev, "Could not find IRQ %d\n", irq);
return -EINVAL;
}
irq_type = irqd_get_trigger_type(desc);
ret = bmi160_config_device_irq(indio_dev, irq_type, pin);
if (ret)
return ret;
return bmi160_probe_trigger(indio_dev, irq, irq_type);
}
static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
{
int ret;
@ -505,7 +710,7 @@ static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
struct device *dev = regmap_get_device(data->regmap);
ret = regmap_write(data->regmap, BMI160_REG_CMD, BMI160_CMD_SOFTRESET);
if (ret < 0)
if (ret)
return ret;
usleep_range(BMI160_SOFTRESET_USLEEP, BMI160_SOFTRESET_USLEEP + 1);
@ -516,12 +721,12 @@ static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
*/
if (use_spi) {
ret = regmap_read(data->regmap, BMI160_REG_DUMMY, &val);
if (ret < 0)
if (ret)
return ret;
}
ret = regmap_read(data->regmap, BMI160_REG_CHIP_ID, &val);
if (ret < 0) {
if (ret) {
dev_err(dev, "Error reading chip id\n");
return ret;
}
@ -532,16 +737,59 @@ static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
}
ret = bmi160_set_mode(data, BMI160_ACCEL, true);
if (ret < 0)
if (ret)
return ret;
ret = bmi160_set_mode(data, BMI160_GYRO, true);
if (ret < 0)
if (ret)
return ret;
return 0;
}
static int bmi160_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool enable)
{
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
struct bmi160_data *data = iio_priv(indio_dev);
return bmi160_enable_irq(data->regmap, enable);
}
static const struct iio_trigger_ops bmi160_trigger_ops = {
.set_trigger_state = &bmi160_data_rdy_trigger_set_state,
};
int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type)
{
struct bmi160_data *data = iio_priv(indio_dev);
int ret;
data->trig = devm_iio_trigger_alloc(&indio_dev->dev, "%s-dev%d",
indio_dev->name, indio_dev->id);
if (data->trig == NULL)
return -ENOMEM;
ret = devm_request_irq(&indio_dev->dev, irq,
&iio_trigger_generic_data_rdy_poll,
irq_type, "bmi160", data->trig);
if (ret)
return ret;
data->trig->dev.parent = regmap_get_device(data->regmap);
data->trig->ops = &bmi160_trigger_ops;
iio_trigger_set_drvdata(data->trig, indio_dev);
ret = devm_iio_trigger_register(&indio_dev->dev, data->trig);
if (ret)
return ret;
indio_dev->trig = iio_trigger_get(data->trig);
return 0;
}
static void bmi160_chip_uninit(void *data)
{
struct bmi160_data *bmi_data = data;
@ -555,6 +803,8 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
{
struct iio_dev *indio_dev;
struct bmi160_data *data;
int irq;
enum bmi160_int_pin int_pin;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
@ -566,11 +816,11 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
data->regmap = regmap;
ret = bmi160_chip_init(data, use_spi);
if (ret < 0)
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, bmi160_chip_uninit, data);
if (ret < 0)
if (ret)
return ret;
if (!name && ACPI_HANDLE(dev))
@ -583,16 +833,23 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &bmi160_info;
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
iio_pollfunc_store_time,
bmi160_trigger_handler, NULL);
if (ret < 0)
if (ret)
return ret;
ret = devm_iio_device_register(dev, indio_dev);
if (ret < 0)
return ret;
irq = bmi160_get_irq(dev->of_node, &int_pin);
if (irq > 0) {
ret = bmi160_setup_irq(indio_dev, irq, int_pin);
if (ret)
dev_err(&indio_dev->dev, "Failed to setup IRQ %d\n",
irq);
} else {
dev_info(&indio_dev->dev, "Not setting up IRQ trigger\n");
}
return 0;
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_GPL(bmi160_core_probe);

View File

@ -1,12 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
* BMI160 - Bosch IMU, I2C bits
*
* Copyright (c) 2016, Intel Corporation.
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details.
*
* 7-bit I2C slave address is:
* - 0x68 if SDO is pulled to GND
* - 0x69 if SDO is pulled to VDDIO

View File

@ -1,11 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
* BMI160 - Bosch IMU, SPI bits
*
* Copyright (c) 2016, Intel Corporation.
*
* This file is subject to the terms and conditions of version 2 of
* the GNU General Public License. See the file COPYING in the main
* directory of this archive for more details.
*/
#include <linux/acpi.h>
#include <linux/module.h>

View File

@ -13,8 +13,8 @@ config INV_MPU6050_I2C
select INV_MPU6050_IIO
select REGMAP_I2C
help
This driver supports the Invensense MPU6050/6500/9150 and ICM20608
motion tracking devices over I2C.
This driver supports the Invensense MPU6050/6500/9150 and
ICM20608/20602 motion tracking devices over I2C.
This driver can be built as a module. The module will be called
inv-mpu6050-i2c.
@ -24,7 +24,7 @@ config INV_MPU6050_SPI
select INV_MPU6050_IIO
select REGMAP_SPI
help
This driver supports the Invensense MPU6050/6500/9150 and ICM20608
motion tracking devices over SPI.
This driver supports the Invensense MPU6050/6500/9150 and
ICM20608/20602 motion tracking devices over SPI.
This driver can be built as a module. The module will be called
inv-mpu6050-spi.

View File

@ -38,6 +38,29 @@ static const int gyro_scale_6050[] = {133090, 266181, 532362, 1064724};
*/
static const int accel_scale[] = {598, 1196, 2392, 4785};
static const struct inv_mpu6050_reg_map reg_set_icm20602 = {
.sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV,
.lpf = INV_MPU6050_REG_CONFIG,
.accel_lpf = INV_MPU6500_REG_ACCEL_CONFIG_2,
.user_ctrl = INV_MPU6050_REG_USER_CTRL,
.fifo_en = INV_MPU6050_REG_FIFO_EN,
.gyro_config = INV_MPU6050_REG_GYRO_CONFIG,
.accl_config = INV_MPU6050_REG_ACCEL_CONFIG,
.fifo_count_h = INV_MPU6050_REG_FIFO_COUNT_H,
.fifo_r_w = INV_MPU6050_REG_FIFO_R_W,
.raw_gyro = INV_MPU6050_REG_RAW_GYRO,
.raw_accl = INV_MPU6050_REG_RAW_ACCEL,
.temperature = INV_MPU6050_REG_TEMPERATURE,
.int_enable = INV_MPU6050_REG_INT_ENABLE,
.int_status = INV_MPU6050_REG_INT_STATUS,
.pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1,
.pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2,
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
.accl_offset = INV_MPU6500_REG_ACCEL_OFFSET,
.gyro_offset = INV_MPU6050_REG_GYRO_OFFSET,
.i2c_if = INV_ICM20602_REG_I2C_IF,
};
static const struct inv_mpu6050_reg_map reg_set_6500 = {
.sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV,
.lpf = INV_MPU6050_REG_CONFIG,
@ -58,6 +81,7 @@ static const struct inv_mpu6050_reg_map reg_set_6500 = {
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
.accl_offset = INV_MPU6500_REG_ACCEL_OFFSET,
.gyro_offset = INV_MPU6050_REG_GYRO_OFFSET,
.i2c_if = 0,
};
static const struct inv_mpu6050_reg_map reg_set_6050 = {
@ -78,6 +102,7 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = {
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
.accl_offset = INV_MPU6050_REG_ACCEL_OFFSET,
.gyro_offset = INV_MPU6050_REG_GYRO_OFFSET,
.i2c_if = 0,
};
static const struct inv_mpu6050_chip_config chip_config_6050 = {
@ -140,6 +165,12 @@ static const struct inv_mpu6050_hw hw_info[] = {
.reg = &reg_set_6500,
.config = &chip_config_6050,
},
{
.whoami = INV_ICM20602_WHOAMI_VALUE,
.name = "ICM20602",
.reg = &reg_set_icm20602,
.config = &chip_config_6050,
},
};
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)

View File

@ -127,6 +127,7 @@ static int inv_mpu_probe(struct i2c_client *client,
st = iio_priv(dev_get_drvdata(&client->dev));
switch (st->chip_type) {
case INV_ICM20608:
case INV_ICM20602:
/* no i2c auxiliary bus on the chip */
break;
default:
@ -179,6 +180,7 @@ static const struct i2c_device_id inv_mpu_id[] = {
{"mpu9250", INV_MPU9250},
{"mpu9255", INV_MPU9255},
{"icm20608", INV_ICM20608},
{"icm20602", INV_ICM20602},
{}
};
@ -213,6 +215,10 @@ static const struct of_device_id inv_of_match[] = {
.compatible = "invensense,icm20608",
.data = (void *)INV_ICM20608
},
{
.compatible = "invensense,icm20602",
.data = (void *)INV_ICM20602
},
{ }
};
MODULE_DEVICE_TABLE(of, inv_of_match);

View File

@ -44,6 +44,7 @@
* @int_pin_cfg; Controls interrupt pin configuration.
* @accl_offset: Controls the accelerometer calibration offset.
* @gyro_offset: Controls the gyroscope calibration offset.
* @i2c_if: Controls the i2c interface
*/
struct inv_mpu6050_reg_map {
u8 sample_rate_div;
@ -65,6 +66,7 @@ struct inv_mpu6050_reg_map {
u8 int_pin_cfg;
u8 accl_offset;
u8 gyro_offset;
u8 i2c_if;
};
/*device enum */
@ -77,6 +79,7 @@ enum inv_devices {
INV_MPU9250,
INV_MPU9255,
INV_ICM20608,
INV_ICM20602,
INV_NUM_PARTS
};
@ -195,6 +198,10 @@ struct inv_mpu6050_state {
#define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38
#define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07
/* ICM20602 register */
#define INV_ICM20602_REG_I2C_IF 0x70
#define INV_ICM20602_BIT_I2C_IF_DIS 0x40
#define INV_MPU6050_REG_FIFO_COUNT_H 0x72
#define INV_MPU6050_REG_FIFO_R_W 0x74
@ -261,6 +268,7 @@ struct inv_mpu6050_state {
#define INV_MPU9255_WHOAMI_VALUE 0x73
#define INV_MPU6515_WHOAMI_VALUE 0x74
#define INV_ICM20608_WHOAMI_VALUE 0xAF
#define INV_ICM20602_WHOAMI_VALUE 0x12
/* scan element definition */
enum inv_mpu6050_scan {

View File

@ -31,9 +31,14 @@ static int inv_mpu_i2c_disable(struct iio_dev *indio_dev)
if (ret)
return ret;
st->chip_config.user_ctrl |= INV_MPU6050_BIT_I2C_IF_DIS;
ret = regmap_write(st->map, st->reg->user_ctrl,
st->chip_config.user_ctrl);
if (st->reg->i2c_if) {
ret = regmap_write(st->map, st->reg->i2c_if,
INV_ICM20602_BIT_I2C_IF_DIS);
} else {
st->chip_config.user_ctrl |= INV_MPU6050_BIT_I2C_IF_DIS;
ret = regmap_write(st->map, st->reg->user_ctrl,
st->chip_config.user_ctrl);
}
if (ret) {
inv_mpu6050_set_power_itg(st, false);
return ret;
@ -81,6 +86,7 @@ static const struct spi_device_id inv_mpu_id[] = {
{"mpu9250", INV_MPU9250},
{"mpu9255", INV_MPU9255},
{"icm20608", INV_ICM20608},
{"icm20602", INV_ICM20602},
{}
};

View File

@ -105,12 +105,10 @@ static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw)
static int st_lsm6dsx_shub_read_reg(struct st_lsm6dsx_hw *hw, u8 addr,
u8 *data, int len)
{
const struct st_lsm6dsx_shub_settings *hub_settings;
int err;
mutex_lock(&hw->page_lock);
hub_settings = &hw->settings->shub_settings;
err = st_lsm6dsx_set_page(hw, true);
if (err < 0)
goto out;

View File

@ -87,6 +87,7 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_GRAVITY] = "gravity",
[IIO_POSITIONRELATIVE] = "positionrelative",
[IIO_PHASE] = "phase",
[IIO_MASSCONCENTRATION] = "massconcentration",
};
static const char * const iio_modifier_names[] = {
@ -127,6 +128,10 @@ static const char * const iio_modifier_names[] = {
[IIO_MOD_Q] = "q",
[IIO_MOD_CO2] = "co2",
[IIO_MOD_VOC] = "voc",
[IIO_MOD_PM1] = "pm1",
[IIO_MOD_PM2P5] = "pm2p5",
[IIO_MOD_PM4] = "pm4",
[IIO_MOD_PM10] = "pm10",
};
/* relies on pairs of these shared then separate */

View File

@ -299,6 +299,16 @@ config MAX44000
To compile this driver as a module, choose M here:
the module will be called max44000.
config MAX44009
tristate "MAX44009 Ambient Light Sensor"
depends on I2C
help
Say Y here if you want to build support for Maxim Integrated's
MAX44009 ambient light sensor device.
To compile this driver as a module, choose M here:
the module will be called max44009.
config OPT3001
tristate "Texas Instruments OPT3001 Light Sensor"
depends on I2C

View File

@ -28,6 +28,7 @@ obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
obj-$(CONFIG_LTR501) += ltr501.o
obj-$(CONFIG_LV0104CS) += lv0104cs.o
obj-$(CONFIG_MAX44000) += max44000.o
obj-$(CONFIG_MAX44009) += max44009.o
obj-$(CONFIG_OPT3001) += opt3001.o
obj-$(CONFIG_PA12203001) += pa12203001.o
obj-$(CONFIG_RPR0521) += rpr0521.o

View File

@ -23,6 +23,7 @@
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@ -95,6 +96,7 @@ struct isl29018_chip {
struct isl29018_scale scale;
int prox_scheme;
bool suspended;
struct regulator *vcc_reg;
};
static int isl29018_set_integration_time(struct isl29018_chip *chip,
@ -708,6 +710,16 @@ static const char *isl29018_match_acpi_device(struct device *dev, int *data)
return dev_name(dev);
}
static void isl29018_disable_regulator_action(void *_data)
{
struct isl29018_chip *chip = _data;
int err;
err = regulator_disable(chip->vcc_reg);
if (err)
pr_err("failed to disable isl29018's VCC regulator!\n");
}
static int isl29018_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@ -742,6 +754,27 @@ static int isl29018_probe(struct i2c_client *client,
chip->scale = isl29018_scales[chip->int_time][0];
chip->suspended = false;
chip->vcc_reg = devm_regulator_get(&client->dev, "vcc");
if (IS_ERR(chip->vcc_reg)) {
err = PTR_ERR(chip->vcc_reg);
if (err != -EPROBE_DEFER)
dev_err(&client->dev, "failed to get VCC regulator!\n");
return err;
}
err = regulator_enable(chip->vcc_reg);
if (err) {
dev_err(&client->dev, "failed to enable VCC regulator!\n");
return err;
}
err = devm_add_action_or_reset(&client->dev, isl29018_disable_regulator_action,
chip);
if (err) {
dev_err(&client->dev, "failed to setup regulator cleanup action!\n");
return err;
}
chip->regmap = devm_regmap_init_i2c(client,
isl29018_chip_info_tbl[dev_id].regmap_cfg);
if (IS_ERR(chip->regmap)) {
@ -768,6 +801,7 @@ static int isl29018_probe(struct i2c_client *client,
static int isl29018_suspend(struct device *dev)
{
struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev));
int ret;
mutex_lock(&chip->lock);
@ -777,10 +811,13 @@ static int isl29018_suspend(struct device *dev)
* So we do not have much to do here.
*/
chip->suspended = true;
ret = regulator_disable(chip->vcc_reg);
if (ret)
dev_err(dev, "failed to disable VCC regulator\n");
mutex_unlock(&chip->lock);
return 0;
return ret;
}
static int isl29018_resume(struct device *dev)
@ -790,6 +827,13 @@ static int isl29018_resume(struct device *dev)
mutex_lock(&chip->lock);
err = regulator_enable(chip->vcc_reg);
if (err) {
dev_err(dev, "failed to enable VCC regulator\n");
mutex_unlock(&chip->lock);
return err;
}
err = isl29018_chip_init(chip);
if (!err)
chip->suspended = false;

View File

@ -0,0 +1,555 @@
// SPDX-License-Identifier: GPL-2.0
/*
* max44009.c - Support for MAX44009 Ambient Light Sensor
*
* Copyright (c) 2019 Robert Eshleman <bobbyeshleman@gmail.com>
*
* Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX44009.pdf
*
* TODO: Support continuous mode and configuring from manual mode to
* automatic mode.
*
* Default I2C address: 0x4a
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/bits.h>
#include <linux/i2c.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/util_macros.h>
#define MAX44009_DRV_NAME "max44009"
/* Registers in datasheet order */
#define MAX44009_REG_INT_STATUS 0x0
#define MAX44009_REG_INT_EN 0x1
#define MAX44009_REG_CFG 0x2
#define MAX44009_REG_LUX_HI 0x3
#define MAX44009_REG_LUX_LO 0x4
#define MAX44009_REG_UPPER_THR 0x5
#define MAX44009_REG_LOWER_THR 0x6
#define MAX44009_REG_THR_TIMER 0x7
#define MAX44009_CFG_TIM_MASK GENMASK(2, 0)
#define MAX44009_CFG_MAN_MODE_MASK BIT(6)
/* The maximum rising threshold for the max44009 */
#define MAX44009_MAXIMUM_THRESHOLD 7520256
#define MAX44009_THRESH_EXP_MASK (0xf << 4)
#define MAX44009_THRESH_EXP_RSHIFT 4
#define MAX44009_THRESH_MANT_LSHIFT 4
#define MAX44009_THRESH_MANT_MASK 0xf
#define MAX44009_UPPER_THR_MINIMUM 15
/* The max44009 always scales raw readings by 0.045 and is non-configurable */
#define MAX44009_SCALE_NUMERATOR 45
#define MAX44009_SCALE_DENOMINATOR 1000
/* The fixed-point fractional multiplier for de-scaling threshold values */
#define MAX44009_FRACT_MULT 1000000
static const u32 max44009_int_time_ns_array[] = {
800000000,
400000000,
200000000,
100000000,
50000000, /* Manual mode only */
25000000, /* Manual mode only */
12500000, /* Manual mode only */
6250000, /* Manual mode only */
};
static const char max44009_int_time_str[] =
"0.8 "
"0.4 "
"0.2 "
"0.1 "
"0.05 "
"0.025 "
"0.0125 "
"0.00625";
struct max44009_data {
struct i2c_client *client;
struct mutex lock;
};
static const struct iio_event_spec max44009_event_spec[] = {
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_RISING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE),
},
{
.type = IIO_EV_TYPE_THRESH,
.dir = IIO_EV_DIR_FALLING,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE),
},
};
static const struct iio_chan_spec max44009_channels[] = {
{
.type = IIO_LIGHT,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
BIT(IIO_CHAN_INFO_INT_TIME),
.event_spec = max44009_event_spec,
.num_event_specs = ARRAY_SIZE(max44009_event_spec),
},
};
static int max44009_read_int_time(struct max44009_data *data)
{
int ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_CFG);
if (ret < 0)
return ret;
return max44009_int_time_ns_array[ret & MAX44009_CFG_TIM_MASK];
}
static int max44009_write_int_time(struct max44009_data *data,
int val, int val2)
{
struct i2c_client *client = data->client;
int ret, int_time, config;
s64 ns;
ns = val * NSEC_PER_SEC + val2;
int_time = find_closest_descending(
ns,
max44009_int_time_ns_array,
ARRAY_SIZE(max44009_int_time_ns_array));
ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG);
if (ret < 0)
return ret;
config = ret;
config &= int_time;
/*
* To set the integration time, the device must also be in manual
* mode.
*/
config |= MAX44009_CFG_MAN_MODE_MASK;
return i2c_smbus_write_byte_data(client, MAX44009_REG_CFG, config);
}
static int max44009_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val,
int val2, long mask)
{
struct max44009_data *data = iio_priv(indio_dev);
int ret;
if (mask == IIO_CHAN_INFO_INT_TIME && chan->type == IIO_LIGHT) {
mutex_lock(&data->lock);
ret = max44009_write_int_time(data, val, val2);
mutex_unlock(&data->lock);
return ret;
}
return -EINVAL;
}
static int max44009_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long mask)
{
return IIO_VAL_INT_PLUS_NANO;
}
static int max44009_lux_raw(u8 hi, u8 lo)
{
int mantissa;
int exponent;
/*
* The mantissa consists of the low nibble of the Lux High Byte
* and the low nibble of the Lux Low Byte.
*/
mantissa = ((hi & 0xf) << 4) | (lo & 0xf);
/* The exponent byte is just the upper nibble of the Lux High Byte */
exponent = (hi >> 4) & 0xf;
/*
* The exponent value is base 2 to the power of the raw exponent byte.
*/
exponent = 1 << exponent;
return exponent * mantissa;
}
#define MAX44009_READ_LUX_XFER_LEN (4)
static int max44009_read_lux_raw(struct max44009_data *data)
{
int ret;
u8 hireg = MAX44009_REG_LUX_HI;
u8 loreg = MAX44009_REG_LUX_LO;
u8 lo = 0;
u8 hi = 0;
struct i2c_msg msgs[] = {
{
.addr = data->client->addr,
.flags = 0,
.len = sizeof(hireg),
.buf = &hireg,
},
{
.addr = data->client->addr,
.flags = I2C_M_RD,
.len = sizeof(hi),
.buf = &hi,
},
{
.addr = data->client->addr,
.flags = 0,
.len = sizeof(loreg),
.buf = &loreg,
},
{
.addr = data->client->addr,
.flags = I2C_M_RD,
.len = sizeof(lo),
.buf = &lo,
}
};
/*
* Use i2c_transfer instead of smbus read because i2c_transfer
* does NOT use a stop bit between address write and data read.
* Using a stop bit causes disjoint upper/lower byte reads and
* reduces accuracy.
*/
ret = i2c_transfer(data->client->adapter,
msgs, MAX44009_READ_LUX_XFER_LEN);
if (ret != MAX44009_READ_LUX_XFER_LEN)
return -EIO;
return max44009_lux_raw(hi, lo);
}
static int max44009_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val,
int *val2, long mask)
{
struct max44009_data *data = iio_priv(indio_dev);
int lux_raw;
int ret;
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_LIGHT:
ret = max44009_read_lux_raw(data);
if (ret < 0)
return ret;
lux_raw = ret;
*val = lux_raw * MAX44009_SCALE_NUMERATOR;
*val2 = MAX44009_SCALE_DENOMINATOR;
return IIO_VAL_FRACTIONAL;
default:
return -EINVAL;
}
case IIO_CHAN_INFO_INT_TIME:
switch (chan->type) {
case IIO_LIGHT:
ret = max44009_read_int_time(data);
if (ret < 0)
return ret;
*val2 = ret;
*val = 0;
return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
default:
return -EINVAL;
}
}
static IIO_CONST_ATTR(illuminance_integration_time_available,
max44009_int_time_str);
static struct attribute *max44009_attributes[] = {
&iio_const_attr_illuminance_integration_time_available.dev_attr.attr,
NULL,
};
static const struct attribute_group max44009_attribute_group = {
.attrs = max44009_attributes,
};
static int max44009_threshold_byte_from_fraction(int integral, int fractional)
{
int mantissa, exp;
if ((integral <= 0 && fractional <= 0) ||
integral > MAX44009_MAXIMUM_THRESHOLD ||
(integral == MAX44009_MAXIMUM_THRESHOLD && fractional != 0))
return -EINVAL;
/* Reverse scaling of fixed-point integral */
mantissa = integral * MAX44009_SCALE_DENOMINATOR;
mantissa /= MAX44009_SCALE_NUMERATOR;
/* Reverse scaling of fixed-point fractional */
mantissa += fractional / MAX44009_FRACT_MULT *
(MAX44009_SCALE_DENOMINATOR / MAX44009_SCALE_NUMERATOR);
for (exp = 0; mantissa > 0xff; exp++)
mantissa >>= 1;
mantissa >>= 4;
mantissa &= 0xf;
exp <<= 4;
return exp | mantissa;
}
static int max44009_get_thr_reg(enum iio_event_direction dir)
{
switch (dir) {
case IIO_EV_DIR_RISING:
return MAX44009_REG_UPPER_THR;
case IIO_EV_DIR_FALLING:
return MAX44009_REG_LOWER_THR;
default:
return -EINVAL;
}
}
static int max44009_write_event_value(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int val, int val2)
{
struct max44009_data *data = iio_priv(indio_dev);
int reg, threshold;
if (info != IIO_EV_INFO_VALUE || chan->type != IIO_LIGHT)
return -EINVAL;
threshold = max44009_threshold_byte_from_fraction(val, val2);
if (threshold < 0)
return threshold;
reg = max44009_get_thr_reg(dir);
if (reg < 0)
return reg;
return i2c_smbus_write_byte_data(data->client, reg, threshold);
}
static int max44009_read_threshold(struct iio_dev *indio_dev,
enum iio_event_direction dir)
{
struct max44009_data *data = iio_priv(indio_dev);
int byte, reg;
int mantissa, exponent;
reg = max44009_get_thr_reg(dir);
if (reg < 0)
return reg;
byte = i2c_smbus_read_byte_data(data->client, reg);
if (byte < 0)
return byte;
mantissa = byte & MAX44009_THRESH_MANT_MASK;
mantissa <<= MAX44009_THRESH_MANT_LSHIFT;
/*
* To get the upper threshold, always adds the minimum upper threshold
* value to the shifted byte value (see datasheet).
*/
if (dir == IIO_EV_DIR_RISING)
mantissa += MAX44009_UPPER_THR_MINIMUM;
/*
* Exponent is base 2 to the power of the threshold exponent byte
* value
*/
exponent = byte & MAX44009_THRESH_EXP_MASK;
exponent >>= MAX44009_THRESH_EXP_RSHIFT;
return (1 << exponent) * mantissa;
}
static int max44009_read_event_value(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
enum iio_event_info info,
int *val, int *val2)
{
int ret;
int threshold;
if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)
return -EINVAL;
ret = max44009_read_threshold(indio_dev, dir);
if (ret < 0)
return ret;
threshold = ret;
*val = threshold * MAX44009_SCALE_NUMERATOR;
*val2 = MAX44009_SCALE_DENOMINATOR;
return IIO_VAL_FRACTIONAL;
}
static int max44009_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir,
int state)
{
struct max44009_data *data = iio_priv(indio_dev);
int ret;
if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)
return -EINVAL;
ret = i2c_smbus_write_byte_data(data->client,
MAX44009_REG_INT_EN, state);
if (ret < 0)
return ret;
/*
* Set device to trigger interrupt immediately upon exceeding
* the threshold limit.
*/
return i2c_smbus_write_byte_data(data->client,
MAX44009_REG_THR_TIMER, 0);
}
static int max44009_read_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
enum iio_event_direction dir)
{
struct max44009_data *data = iio_priv(indio_dev);
if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)
return -EINVAL;
return i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_EN);
}
static const struct iio_info max44009_info = {
.read_raw = max44009_read_raw,
.write_raw = max44009_write_raw,
.write_raw_get_fmt = max44009_write_raw_get_fmt,
.read_event_value = max44009_read_event_value,
.read_event_config = max44009_read_event_config,
.write_event_value = max44009_write_event_value,
.write_event_config = max44009_write_event_config,
.attrs = &max44009_attribute_group,
};
static irqreturn_t max44009_threaded_irq_handler(int irq, void *p)
{
struct iio_dev *indio_dev = p;
struct max44009_data *data = iio_priv(indio_dev);
int ret;
ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_STATUS);
if (ret) {
iio_push_event(indio_dev,
IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0,
IIO_EV_TYPE_THRESH,
IIO_EV_DIR_EITHER),
iio_get_time_ns(indio_dev));
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static int max44009_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct max44009_data *data;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
indio_dev->dev.parent = &client->dev;
indio_dev->info = &max44009_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = MAX44009_DRV_NAME;
indio_dev->channels = max44009_channels;
indio_dev->num_channels = ARRAY_SIZE(max44009_channels);
mutex_init(&data->lock);
/* Clear any stale interrupt bit */
ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG);
if (ret < 0)
return ret;
if (client->irq > 0) {
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL,
max44009_threaded_irq_handler,
IRQF_TRIGGER_FALLING |
IRQF_ONESHOT | IRQF_SHARED,
"max44009_event",
indio_dev);
if (ret < 0)
return ret;
}
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id max44009_id[] = {
{ "max44009", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max44009_id);
static struct i2c_driver max44009_driver = {
.driver = {
.name = MAX44009_DRV_NAME,
},
.probe = max44009_probe,
.id_table = max44009_id,
};
module_i2c_driver(max44009_driver);
static const struct of_device_id max44009_of_match[] = {
{ .compatible = "maxim,max44009" },
{ }
};
MODULE_DEVICE_TABLE(of, max44009_of_match);
MODULE_AUTHOR("Robert Eshleman <bobbyeshleman@gmail.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("MAX44009 ambient light sensor driver");

View File

@ -20,6 +20,7 @@
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
#define MAG3110_STATUS 0x00
#define MAG3110_OUT_X 0x01 /* MSB first */
@ -56,6 +57,8 @@ struct mag3110_data {
struct mutex lock;
u8 ctrl_reg1;
int sleep_val;
struct regulator *vdd_reg;
struct regulator *vddio_reg;
};
static int mag3110_request(struct mag3110_data *data)
@ -469,17 +472,50 @@ static int mag3110_probe(struct i2c_client *client,
struct iio_dev *indio_dev;
int ret;
ret = i2c_smbus_read_byte_data(client, MAG3110_WHO_AM_I);
if (ret < 0)
return ret;
if (ret != MAG3110_DEVICE_ID)
return -ENODEV;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
if (IS_ERR(data->vdd_reg)) {
if (PTR_ERR(data->vdd_reg) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_err(&client->dev, "failed to get VDD regulator!\n");
return PTR_ERR(data->vdd_reg);
}
data->vddio_reg = devm_regulator_get(&client->dev, "vddio");
if (IS_ERR(data->vddio_reg)) {
if (PTR_ERR(data->vddio_reg) == -EPROBE_DEFER)
return -EPROBE_DEFER;
dev_err(&client->dev, "failed to get VDDIO regulator!\n");
return PTR_ERR(data->vddio_reg);
}
ret = regulator_enable(data->vdd_reg);
if (ret) {
dev_err(&client->dev, "failed to enable VDD regulator!\n");
return ret;
}
ret = regulator_enable(data->vddio_reg);
if (ret) {
dev_err(&client->dev, "failed to enable VDDIO regulator!\n");
goto disable_regulator_vdd;
}
ret = i2c_smbus_read_byte_data(client, MAG3110_WHO_AM_I);
if (ret < 0)
goto disable_regulators;
if (ret != MAG3110_DEVICE_ID) {
ret = -ENODEV;
goto disable_regulators;
}
data->client = client;
mutex_init(&data->lock);
@ -499,7 +535,7 @@ static int mag3110_probe(struct i2c_client *client,
ret = mag3110_change_config(data, MAG3110_CTRL_REG1, data->ctrl_reg1);
if (ret < 0)
return ret;
goto disable_regulators;
ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG2,
MAG3110_CTRL_AUTO_MRST_EN);
@ -520,16 +556,24 @@ buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
standby_on_error:
mag3110_standby(iio_priv(indio_dev));
disable_regulators:
regulator_disable(data->vddio_reg);
disable_regulator_vdd:
regulator_disable(data->vdd_reg);
return ret;
}
static int mag3110_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct mag3110_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
mag3110_standby(iio_priv(indio_dev));
regulator_disable(data->vddio_reg);
regulator_disable(data->vdd_reg);
return 0;
}
@ -537,14 +581,48 @@ static int mag3110_remove(struct i2c_client *client)
#ifdef CONFIG_PM_SLEEP
static int mag3110_suspend(struct device *dev)
{
return mag3110_standby(iio_priv(i2c_get_clientdata(
struct mag3110_data *data = iio_priv(i2c_get_clientdata(
to_i2c_client(dev)));
int ret;
ret = mag3110_standby(iio_priv(i2c_get_clientdata(
to_i2c_client(dev))));
if (ret)
return ret;
ret = regulator_disable(data->vddio_reg);
if (ret) {
dev_err(dev, "failed to disable VDDIO regulator\n");
return ret;
}
ret = regulator_disable(data->vdd_reg);
if (ret) {
dev_err(dev, "failed to disable VDD regulator\n");
return ret;
}
return 0;
}
static int mag3110_resume(struct device *dev)
{
struct mag3110_data *data = iio_priv(i2c_get_clientdata(
to_i2c_client(dev)));
int ret;
ret = regulator_enable(data->vdd_reg);
if (ret) {
dev_err(dev, "failed to enable VDD regulator\n");
return ret;
}
ret = regulator_enable(data->vddio_reg);
if (ret) {
dev_err(dev, "failed to enable VDDIO regulator\n");
regulator_disable(data->vdd_reg);
return ret;
}
return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
data->ctrl_reg1);

View File

@ -165,7 +165,7 @@ config IIO_ST_PRESS
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
help
Say yes here to build support for STMicroelectronics pressure
sensors: LPS001WP, LPS25H, LPS331AP, LPS22HB.
sensors: LPS001WP, LPS25H, LPS331AP, LPS22HB, LPS22HH.
This driver can also be built as a module. If so, these modules
will be created:

View File

@ -21,6 +21,7 @@ enum st_press_type {
LPS22HB,
LPS33HW,
LPS35HW,
LPS22HH,
ST_PRESS_MAX,
};
@ -30,6 +31,7 @@ enum st_press_type {
#define LPS22HB_PRESS_DEV_NAME "lps22hb"
#define LPS33HW_PRESS_DEV_NAME "lps33hw"
#define LPS35HW_PRESS_DEV_NAME "lps35hw"
#define LPS22HH_PRESS_DEV_NAME "lps22hh"
/**
* struct st_sensors_platform_data - default press platform data

View File

@ -492,6 +492,75 @@ static const struct st_sensor_settings st_press_sensors_settings[] = {
.multi_read_bit = false,
.bootime = 2,
},
{
/*
* CUSTOM VALUES FOR LPS22HH SENSOR
* See LPS22HH datasheet:
* http://www2.st.com/resource/en/datasheet/lps22hh.pdf
*/
.wai = 0xb3,
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LPS22HH_PRESS_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_press_lps22hb_channels,
.num_ch = ARRAY_SIZE(st_press_lps22hb_channels),
.odr = {
.addr = 0x10,
.mask = 0x70,
.odr_avl = {
{ .hz = 1, .value = 0x01 },
{ .hz = 10, .value = 0x02 },
{ .hz = 25, .value = 0x03 },
{ .hz = 50, .value = 0x04 },
{ .hz = 75, .value = 0x05 },
{ .hz = 100, .value = 0x06 },
{ .hz = 200, .value = 0x07 },
},
},
.pw = {
.addr = 0x10,
.mask = 0x70,
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
},
.fs = {
.fs_avl = {
/*
* Pressure and temperature sensitivity values
* as defined in table 3 of LPS22HH datasheet.
*/
[0] = {
.num = ST_PRESS_FS_AVL_1260MB,
.gain = ST_PRESS_KPASCAL_NANO_SCALE,
.gain2 = ST_PRESS_LPS22HB_LSB_PER_CELSIUS,
},
},
},
.bdu = {
.addr = 0x10,
.mask = BIT(1),
},
.drdy_irq = {
.int1 = {
.addr = 0x12,
.mask = BIT(2),
.addr_od = 0x11,
.mask_od = BIT(5),
},
.addr_ihl = 0x11,
.mask_ihl = BIT(6),
.stat_drdy = {
.addr = ST_SENSORS_DEFAULT_STAT_ADDR,
.mask = 0x03,
},
},
.sim = {
.addr = 0x10,
.value = BIT(0),
},
.multi_read_bit = false,
.bootime = 2,
},
};
static int st_press_write_raw(struct iio_dev *indio_dev,

View File

@ -45,6 +45,10 @@ static const struct of_device_id st_press_of_match[] = {
.compatible = "st,lps35hw",
.data = LPS35HW_PRESS_DEV_NAME,
},
{
.compatible = "st,lps22hh",
.data = LPS22HH_PRESS_DEV_NAME,
},
{},
};
MODULE_DEVICE_TABLE(of, st_press_of_match);
@ -69,6 +73,7 @@ static const struct i2c_device_id st_press_id_table[] = {
{ LPS22HB_PRESS_DEV_NAME, LPS22HB },
{ LPS33HW_PRESS_DEV_NAME, LPS33HW },
{ LPS35HW_PRESS_DEV_NAME, LPS35HW },
{ LPS22HH_PRESS_DEV_NAME, LPS22HH },
{},
};
MODULE_DEVICE_TABLE(i2c, st_press_id_table);

View File

@ -49,6 +49,10 @@ static const struct of_device_id st_press_of_match[] = {
.compatible = "st,lps35hw",
.data = LPS35HW_PRESS_DEV_NAME,
},
{
.compatible = "st,lps22hh",
.data = LPS22HH_PRESS_DEV_NAME,
},
{},
};
MODULE_DEVICE_TABLE(of, st_press_of_match);
@ -93,6 +97,7 @@ static const struct spi_device_id st_press_id_table[] = {
{ LPS22HB_PRESS_DEV_NAME },
{ LPS33HW_PRESS_DEV_NAME },
{ LPS35HW_PRESS_DEV_NAME },
{ LPS22HH_PRESS_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(spi, st_press_id_table);

View File

@ -56,8 +56,6 @@ source "drivers/staging/iio/Kconfig"
source "drivers/staging/sm750fb/Kconfig"
source "drivers/staging/xgifb/Kconfig"
source "drivers/staging/emxx_udc/Kconfig"
source "drivers/staging/speakup/Kconfig"
@ -104,12 +102,16 @@ source "drivers/staging/pi433/Kconfig"
source "drivers/staging/mt7621-pci/Kconfig"
source "drivers/staging/mt7621-pci-phy/Kconfig"
source "drivers/staging/mt7621-pinctrl/Kconfig"
source "drivers/staging/mt7621-spi/Kconfig"
source "drivers/staging/mt7621-dma/Kconfig"
source "drivers/staging/ralink-gdma/Kconfig"
source "drivers/staging/mt7621-mmc/Kconfig"
source "drivers/staging/mt7621-eth/Kconfig"

View File

@ -20,7 +20,6 @@ obj-$(CONFIG_VT6656) += vt6656/
obj-$(CONFIG_VME_BUS) += vme/
obj-$(CONFIG_IIO) += iio/
obj-$(CONFIG_FB_SM750) += sm750fb/
obj-$(CONFIG_FB_XGI) += xgifb/
obj-$(CONFIG_USB_EMXX) += emxx_udc/
obj-$(CONFIG_SPEAKUP) += speakup/
obj-$(CONFIG_MFD_NVEC) += nvec/
@ -41,12 +40,14 @@ obj-$(CONFIG_GREYBUS) += greybus/
obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/
obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/
obj-$(CONFIG_PI433) += pi433/
obj-$(CONFIG_SOC_MT7621) += mt7621-pci/
obj-$(CONFIG_SOC_MT7621) += mt7621-pinctrl/
obj-$(CONFIG_SOC_MT7621) += mt7621-spi/
obj-$(CONFIG_PCI_MT7621) += mt7621-pci/
obj-$(CONFIG_PCI_MT7621_PHY) += mt7621-pci-phy/
obj-$(CONFIG_PINCTRL_RT2880) += mt7621-pinctrl/
obj-$(CONFIG_SPI_MT7621) += mt7621-spi/
obj-$(CONFIG_SOC_MT7621) += mt7621-dma/
obj-$(CONFIG_SOC_MT7621) += mt7621-mmc/
obj-$(CONFIG_SOC_MT7621) += mt7621-eth/
obj-$(CONFIG_DMA_RALINK) += ralink-gdma/
obj-$(CONFIG_MTK_MMC) += mt7621-mmc/
obj-$(CONFIG_NET_MEDIATEK_SOC_STAGING) += mt7621-eth/
obj-$(CONFIG_SOC_MT7621) += mt7621-dts/
obj-$(CONFIG_STAGING_GASKET_FRAMEWORK) += gasket/
obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/

View File

@ -75,6 +75,9 @@ struct ashmem_range {
/* LRU list of unpinned pages, protected by ashmem_mutex */
static LIST_HEAD(ashmem_lru_list);
static atomic_t ashmem_shrink_inflight = ATOMIC_INIT(0);
static DECLARE_WAIT_QUEUE_HEAD(ashmem_shrink_wait);
/*
* long lru_count - The count of pages on our LRU list.
*
@ -126,7 +129,8 @@ static inline bool page_range_in_range(struct ashmem_range *range,
page_range_subsumes_range(range, start, end);
}
static inline bool range_before_page(struct ashmem_range *range, size_t page)
static inline bool range_before_page(struct ashmem_range *range,
size_t page)
{
return range->pgend < page;
}
@ -168,19 +172,15 @@ static inline void lru_del(struct ashmem_range *range)
* @end: The ending page (inclusive)
*
* This function is protected by ashmem_mutex.
*
* Return: 0 if successful, or -ENOMEM if there is an error
*/
static int range_alloc(struct ashmem_area *asma,
struct ashmem_range *prev_range, unsigned int purged,
size_t start, size_t end)
static void range_alloc(struct ashmem_area *asma,
struct ashmem_range *prev_range, unsigned int purged,
size_t start, size_t end,
struct ashmem_range **new_range)
{
struct ashmem_range *range;
range = kmem_cache_zalloc(ashmem_range_cachep, GFP_KERNEL);
if (!range)
return -ENOMEM;
struct ashmem_range *range = *new_range;
*new_range = NULL;
range->asma = asma;
range->pgstart = start;
range->pgend = end;
@ -190,8 +190,6 @@ static int range_alloc(struct ashmem_area *asma,
if (range_on_lru(range))
lru_add(range);
return 0;
}
/**
@ -438,7 +436,6 @@ out:
static unsigned long
ashmem_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
{
struct ashmem_range *range, *next;
unsigned long freed = 0;
/* We might recurse into filesystem code, so bail out if necessary */
@ -448,21 +445,33 @@ ashmem_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
if (!mutex_trylock(&ashmem_mutex))
return -1;
list_for_each_entry_safe(range, next, &ashmem_lru_list, lru) {
while (!list_empty(&ashmem_lru_list)) {
struct ashmem_range *range =
list_first_entry(&ashmem_lru_list, typeof(*range), lru);
loff_t start = range->pgstart * PAGE_SIZE;
loff_t end = (range->pgend + 1) * PAGE_SIZE;
struct file *f = range->asma->file;
range->asma->file->f_op->fallocate(range->asma->file,
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
start, end - start);
get_file(f);
atomic_inc(&ashmem_shrink_inflight);
range->purged = ASHMEM_WAS_PURGED;
lru_del(range);
freed += range_size(range);
mutex_unlock(&ashmem_mutex);
f->f_op->fallocate(f,
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
start, end - start);
fput(f);
if (atomic_dec_and_test(&ashmem_shrink_inflight))
wake_up_all(&ashmem_shrink_wait);
if (!mutex_trylock(&ashmem_mutex))
goto out;
if (--sc->nr_to_scan <= 0)
break;
}
mutex_unlock(&ashmem_mutex);
out:
return freed;
}
@ -582,7 +591,8 @@ static int get_name(struct ashmem_area *asma, void __user *name)
*
* Caller must hold ashmem_mutex.
*/
static int ashmem_pin(struct ashmem_area *asma, size_t pgstart, size_t pgend)
static int ashmem_pin(struct ashmem_area *asma, size_t pgstart, size_t pgend,
struct ashmem_range **new_range)
{
struct ashmem_range *range, *next;
int ret = ASHMEM_NOT_PURGED;
@ -635,7 +645,7 @@ static int ashmem_pin(struct ashmem_area *asma, size_t pgstart, size_t pgend)
* second half and adjust the first chunk's endpoint.
*/
range_alloc(asma, range, range->purged,
pgend + 1, range->pgend);
pgend + 1, range->pgend, new_range);
range_shrink(range, range->pgstart, pgstart - 1);
break;
}
@ -649,7 +659,8 @@ static int ashmem_pin(struct ashmem_area *asma, size_t pgstart, size_t pgend)
*
* Caller must hold ashmem_mutex.
*/
static int ashmem_unpin(struct ashmem_area *asma, size_t pgstart, size_t pgend)
static int ashmem_unpin(struct ashmem_area *asma, size_t pgstart, size_t pgend,
struct ashmem_range **new_range)
{
struct ashmem_range *range, *next;
unsigned int purged = ASHMEM_NOT_PURGED;
@ -675,7 +686,8 @@ restart:
}
}
return range_alloc(asma, range, purged, pgstart, pgend);
range_alloc(asma, range, purged, pgstart, pgend, new_range);
return 0;
}
/*
@ -708,11 +720,19 @@ static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd,
struct ashmem_pin pin;
size_t pgstart, pgend;
int ret = -EINVAL;
struct ashmem_range *range = NULL;
if (copy_from_user(&pin, p, sizeof(pin)))
return -EFAULT;
if (cmd == ASHMEM_PIN || cmd == ASHMEM_UNPIN) {
range = kmem_cache_zalloc(ashmem_range_cachep, GFP_KERNEL);
if (!range)
return -ENOMEM;
}
mutex_lock(&ashmem_mutex);
wait_event(ashmem_shrink_wait, !atomic_read(&ashmem_shrink_inflight));
if (!asma->file)
goto out_unlock;
@ -735,10 +755,10 @@ static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd,
switch (cmd) {
case ASHMEM_PIN:
ret = ashmem_pin(asma, pgstart, pgend);
ret = ashmem_pin(asma, pgstart, pgend, &range);
break;
case ASHMEM_UNPIN:
ret = ashmem_unpin(asma, pgstart, pgend);
ret = ashmem_unpin(asma, pgstart, pgend, &range);
break;
case ASHMEM_GET_PIN_STATUS:
ret = ashmem_get_pin_status(asma, pgstart, pgend);
@ -747,6 +767,8 @@ static int ashmem_pin_unpin(struct ashmem_area *asma, unsigned long cmd,
out_unlock:
mutex_unlock(&ashmem_mutex);
if (range)
kmem_cache_free(ashmem_range_cachep, range);
return ret;
}

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_ION) += ion.o ion-ioctl.o ion_heap.o
obj-$(CONFIG_ION) += ion.o ion_heap.o
obj-$(CONFIG_ION_SYSTEM_HEAP) += ion_system_heap.o ion_page_pool.o
obj-$(CONFIG_ION_CARVEOUT_HEAP) += ion_carveout_heap.o
obj-$(CONFIG_ION_CHUNK_HEAP) += ion_chunk_heap.o

View File

@ -1,98 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2011 Google, Inc.
*/
#include <linux/kernel.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include "ion.h"
union ion_ioctl_arg {
struct ion_allocation_data allocation;
struct ion_heap_query query;
};
static int validate_ioctl_arg(unsigned int cmd, union ion_ioctl_arg *arg)
{
switch (cmd) {
case ION_IOC_HEAP_QUERY:
if (arg->query.reserved0 ||
arg->query.reserved1 ||
arg->query.reserved2)
return -EINVAL;
break;
default:
break;
}
return 0;
}
/* fix up the cases where the ioctl direction bits are incorrect */
static unsigned int ion_ioctl_dir(unsigned int cmd)
{
switch (cmd) {
default:
return _IOC_DIR(cmd);
}
}
long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
unsigned int dir;
union ion_ioctl_arg data;
dir = ion_ioctl_dir(cmd);
if (_IOC_SIZE(cmd) > sizeof(data))
return -EINVAL;
/*
* The copy_from_user is unconditional here for both read and write
* to do the validate. If there is no write for the ioctl, the
* buffer is cleared
*/
if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
return -EFAULT;
ret = validate_ioctl_arg(cmd, &data);
if (ret) {
pr_warn_once("%s: ioctl validate failed\n", __func__);
return ret;
}
if (!(dir & _IOC_WRITE))
memset(&data, 0, sizeof(data));
switch (cmd) {
case ION_IOC_ALLOC:
{
int fd;
fd = ion_alloc(data.allocation.len,
data.allocation.heap_id_mask,
data.allocation.flags);
if (fd < 0)
return fd;
data.allocation.fd = fd;
break;
}
case ION_IOC_HEAP_QUERY:
ret = ion_query_heaps(&data.query);
break;
default:
return -ENOTTY;
}
if (dir & _IOC_READ) {
if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd)))
return -EFAULT;
}
return ret;
}

View File

@ -1,11 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/*
* drivers/staging/android/ion/ion.c
* ION Memory Allocator
*
* Copyright (C) 2011 Google, Inc.
*/
#include <linux/anon_inodes.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/dma-buf.h>
@ -14,10 +13,8 @@
#include <linux/file.h>
#include <linux/freezer.h>
#include <linux/fs.h>
#include <linux/idr.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/memblock.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/mm_types.h>
@ -390,7 +387,7 @@ static const struct dma_buf_ops dma_buf_ops = {
.unmap = ion_dma_buf_kunmap,
};
int ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags)
static int ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags)
{
struct ion_device *dev = internal_dev;
struct ion_buffer *buffer = NULL;
@ -447,7 +444,7 @@ int ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags)
return fd;
}
int ion_query_heaps(struct ion_heap_query *query)
static int ion_query_heaps(struct ion_heap_query *query)
{
struct ion_device *dev = internal_dev;
struct ion_heap_data __user *buffer = u64_to_user_ptr(query->heaps);
@ -492,6 +489,81 @@ out:
return ret;
}
union ion_ioctl_arg {
struct ion_allocation_data allocation;
struct ion_heap_query query;
};
static int validate_ioctl_arg(unsigned int cmd, union ion_ioctl_arg *arg)
{
switch (cmd) {
case ION_IOC_HEAP_QUERY:
if (arg->query.reserved0 ||
arg->query.reserved1 ||
arg->query.reserved2)
return -EINVAL;
break;
default:
break;
}
return 0;
}
static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
union ion_ioctl_arg data;
if (_IOC_SIZE(cmd) > sizeof(data))
return -EINVAL;
/*
* The copy_from_user is unconditional here for both read and write
* to do the validate. If there is no write for the ioctl, the
* buffer is cleared
*/
if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
return -EFAULT;
ret = validate_ioctl_arg(cmd, &data);
if (ret) {
pr_warn_once("%s: ioctl validate failed\n", __func__);
return ret;
}
if (!(_IOC_DIR(cmd) & _IOC_WRITE))
memset(&data, 0, sizeof(data));
switch (cmd) {
case ION_IOC_ALLOC:
{
int fd;
fd = ion_alloc(data.allocation.len,
data.allocation.heap_id_mask,
data.allocation.flags);
if (fd < 0)
return fd;
data.allocation.fd = fd;
break;
}
case ION_IOC_HEAP_QUERY:
ret = ion_query_heaps(&data.query);
break;
default:
return -ENOTTY;
}
if (_IOC_DIR(cmd) & _IOC_READ) {
if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd)))
return -EFAULT;
}
return ret;
}
static const struct file_operations ion_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = ion_ioctl,

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* drivers/staging/android/ion/ion.h
* ION Memory Allocator kernel interface header
*
* Copyright (C) 2011 Google, Inc.
*/
@ -21,33 +21,10 @@
#include "../uapi/ion.h"
/**
* struct ion_platform_heap - defines a heap in the given platform
* @type: type of the heap from ion_heap_type enum
* @id: unique identifier for heap. When allocating higher numb ers
* will be allocated from first. At allocation these are passed
* as a bit mask and therefore can not exceed ION_NUM_HEAP_IDS.
* @name: used for debug purposes
* @base: base address of heap in physical memory if applicable
* @size: size of the heap in bytes if applicable
* @priv: private info passed from the board file
*
* Provided by the board file.
*/
struct ion_platform_heap {
enum ion_heap_type type;
unsigned int id;
const char *name;
phys_addr_t base;
size_t size;
phys_addr_t align;
void *priv;
};
/**
* struct ion_buffer - metadata for a particular buffer
* @ref: reference count
* @node: node in the ion_device buffers tree
* @list: element in list of deferred freeable buffers
* @dev: back pointer to the ion_device
* @heap: back pointer to the heap the buffer came from
* @flags: buffer specific flags
@ -58,7 +35,8 @@ struct ion_platform_heap {
* @lock: protects the buffers cnt fields
* @kmap_cnt: number of times the buffer is mapped to the kernel
* @vaddr: the kernel mapping if kmap_cnt is not zero
* @sg_table: the sg table for the buffer if dmap_cnt is not zero
* @sg_table: the sg table for the buffer
* @attachments: list of devices attached to this buffer
*/
struct ion_buffer {
union {
@ -174,12 +152,16 @@ struct ion_heap {
unsigned long flags;
unsigned int id;
const char *name;
/* deferred free support */
struct shrinker shrinker;
struct list_head free_list;
size_t free_list_size;
spinlock_t free_lock;
wait_queue_head_t waitqueue;
struct task_struct *task;
/* heap statistics */
u64 num_of_buffers;
u64 num_of_alloc_bytes;
u64 alloc_bytes_wm;
@ -205,10 +187,6 @@ int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
int ion_heap_buffer_zero(struct ion_buffer *buffer);
int ion_heap_pages_zero(struct page *page, size_t size, pgprot_t pgprot);
int ion_alloc(size_t len,
unsigned int heap_id_mask,
unsigned int flags);
/**
* ion_heap_init_shrinker
* @heap: the heap
@ -330,8 +308,4 @@ void ion_page_pool_free(struct ion_page_pool *pool, struct page *page);
int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
int nr_to_scan);
long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
int ion_query_heaps(struct ion_heap_query *query);
#endif /* _ION_H */

View File

@ -1,10 +1,10 @@
// SPDX-License-Identifier: GPL-2.0
/*
* drivers/staging/android/ion/ion_carveout_heap.c
* ION Memory Allocator carveout heap helper
*
* Copyright (C) 2011 Google, Inc.
*/
#include <linux/spinlock.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/genalloc.h>
@ -12,7 +12,7 @@
#include <linux/mm.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include "ion.h"
#define ION_CARVEOUT_ALLOCATE_FAIL -1
@ -20,7 +20,6 @@
struct ion_carveout_heap {
struct ion_heap heap;
struct gen_pool *pool;
phys_addr_t base;
};
static phys_addr_t ion_carveout_allocate(struct ion_heap *heap,
@ -44,6 +43,7 @@ static void ion_carveout_free(struct ion_heap *heap, phys_addr_t addr,
if (addr == ION_CARVEOUT_ALLOCATE_FAIL)
return;
gen_pool_free(carveout_heap->pool, addr, size);
}
@ -103,17 +103,14 @@ static struct ion_heap_ops carveout_heap_ops = {
.unmap_kernel = ion_heap_unmap_kernel,
};
struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
struct ion_heap *ion_carveout_heap_create(phys_addr_t base, size_t size)
{
struct ion_carveout_heap *carveout_heap;
int ret;
struct page *page;
size_t size;
page = pfn_to_page(PFN_DOWN(heap_data->base));
size = heap_data->size;
page = pfn_to_page(PFN_DOWN(base));
ret = ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL));
if (ret)
return ERR_PTR(ret);
@ -127,9 +124,7 @@ struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
kfree(carveout_heap);
return ERR_PTR(-ENOMEM);
}
carveout_heap->base = heap_data->base;
gen_pool_add(carveout_heap->pool, carveout_heap->base, heap_data->size,
-1);
gen_pool_add(carveout_heap->pool, base, size, -1);
carveout_heap->heap.ops = &carveout_heap_ops;
carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT;
carveout_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;

View File

@ -1,23 +1,22 @@
// SPDX-License-Identifier: GPL-2.0
/*
* drivers/staging/android/ion/ion_chunk_heap.c
* ION memory allocator chunk heap helper
*
* Copyright (C) 2012 Google, Inc.
*/
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include "ion.h"
struct ion_chunk_heap {
struct ion_heap heap;
struct gen_pool *pool;
phys_addr_t base;
unsigned long chunk_size;
unsigned long size;
unsigned long allocated;
@ -108,16 +107,13 @@ static struct ion_heap_ops chunk_heap_ops = {
.unmap_kernel = ion_heap_unmap_kernel,
};
struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data)
struct ion_heap *ion_chunk_heap_create(phys_addr_t base, size_t size, size_t chunk_size)
{
struct ion_chunk_heap *chunk_heap;
int ret;
struct page *page;
size_t size;
page = pfn_to_page(PFN_DOWN(heap_data->base));
size = heap_data->size;
page = pfn_to_page(PFN_DOWN(base));
ret = ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL));
if (ret)
return ERR_PTR(ret);
@ -126,23 +122,21 @@ struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data)
if (!chunk_heap)
return ERR_PTR(-ENOMEM);
chunk_heap->chunk_size = (unsigned long)heap_data->priv;
chunk_heap->chunk_size = chunk_size;
chunk_heap->pool = gen_pool_create(get_order(chunk_heap->chunk_size) +
PAGE_SHIFT, -1);
if (!chunk_heap->pool) {
ret = -ENOMEM;
goto error_gen_pool_create;
}
chunk_heap->base = heap_data->base;
chunk_heap->size = heap_data->size;
chunk_heap->size = size;
chunk_heap->allocated = 0;
gen_pool_add(chunk_heap->pool, chunk_heap->base, heap_data->size, -1);
gen_pool_add(chunk_heap->pool, base, size, -1);
chunk_heap->heap.ops = &chunk_heap_ops;
chunk_heap->heap.type = ION_HEAP_TYPE_CHUNK;
chunk_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
pr_debug("%s: base %pa size %zu\n", __func__,
&chunk_heap->base, heap_data->size);
pr_debug("%s: base %pa size %zu\n", __func__, &base, size);
return &chunk_heap->heap;
@ -150,4 +144,3 @@ error_gen_pool_create:
kfree(chunk_heap);
return ERR_PTR(ret);
}

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* drivers/staging/android/ion/ion_cma_heap.c
* ION Memory Allocator CMA heap exporter
*
* Copyright (C) Linaro 2012
* Author: <benjamin.gaignard@linaro.org> for ST-Ericsson.
@ -111,10 +111,6 @@ static struct ion_heap *__ion_cma_heap_create(struct cma *cma)
return ERR_PTR(-ENOMEM);
cma_heap->heap.ops = &ion_cma_ops;
/*
* get device from private heaps data, later it will be
* used to make the link with reserved CMA memory
*/
cma_heap->cma = cma;
cma_heap->heap.type = ION_HEAP_TYPE_DMA;
return &cma_heap->heap;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* drivers/staging/android/ion/ion_heap.c
* ION Memory Allocator generic heap helpers
*
* Copyright (C) 2011 Google, Inc.
*/
@ -14,6 +14,7 @@
#include <uapi/linux/sched/types.h>
#include <linux/scatterlist.h>
#include <linux/vmalloc.h>
#include "ion.h"
void *ion_heap_map_kernel(struct ion_heap *heap,
@ -92,6 +93,7 @@ int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
if (addr >= vma->vm_end)
return 0;
}
return 0;
}
@ -254,6 +256,7 @@ int ion_heap_init_deferred_free(struct ion_heap *heap)
return PTR_ERR_OR_ZERO(heap->task);
}
sched_setscheduler(heap->task, SCHED_IDLE, &param);
return 0;
}
@ -265,8 +268,10 @@ static unsigned long ion_heap_shrink_count(struct shrinker *shrinker,
int total = 0;
total = ion_heap_freelist_size(heap) / PAGE_SIZE;
if (heap->ops->shrink)
total += heap->ops->shrink(heap, sc->gfp_mask, 0);
return total;
}
@ -295,6 +300,7 @@ static unsigned long ion_heap_shrink_scan(struct shrinker *shrinker,
if (heap->ops->shrink)
freed += heap->ops->shrink(heap, sc->gfp_mask, to_scan);
return freed;
}

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* drivers/staging/android/ion/ion_mem_pool.c
* ION Memory Allocator page pool helpers
*
* Copyright (C) 2011 Google, Inc.
*/

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* drivers/staging/android/ion/ion_system_heap.c
* ION Memory Allocator system heap exporter
*
* Copyright (C) 2011 Google, Inc.
*/
@ -13,6 +13,7 @@
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include "ion.h"
#define NUM_ORDERS ARRAY_SIZE(orders)
@ -223,10 +224,10 @@ static void ion_system_heap_destroy_pools(struct ion_page_pool **pools)
static int ion_system_heap_create_pools(struct ion_page_pool **pools)
{
int i;
gfp_t gfp_flags = low_order_gfp_flags;
for (i = 0; i < NUM_ORDERS; i++) {
struct ion_page_pool *pool;
gfp_t gfp_flags = low_order_gfp_flags;
if (orders[i] > 4)
gfp_flags = high_order_gfp_flags;
@ -236,6 +237,7 @@ static int ion_system_heap_create_pools(struct ion_page_pool **pools)
goto err_create_pool;
pools[i] = pool;
}
return 0;
err_create_pool:
@ -274,6 +276,7 @@ static int ion_system_heap_create(void)
heap->name = "ion_system_heap";
ion_device_add_heap(heap);
return 0;
}
device_initcall(ion_system_heap_create);
@ -355,6 +358,7 @@ static struct ion_heap *__ion_system_contig_heap_create(void)
heap->ops = &kmalloc_ops;
heap->type = ION_HEAP_TYPE_SYSTEM_CONTIG;
heap->name = "ion_system_contig_heap";
return heap;
}
@ -367,7 +371,7 @@ static int ion_system_contig_heap_create(void)
return PTR_ERR(heap);
ion_device_add_heap(heap);
return 0;
}
device_initcall(ion_system_contig_heap_create);

View File

@ -1,4 +1,4 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
* drivers/staging/android/uapi/ion.h
*

View File

@ -29,7 +29,6 @@
#include <linux/syscalls.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/file.h>
#include "uapi/vsoc_shm.h"

View File

@ -1605,9 +1605,8 @@ static int do_insn_ioctl(struct comedi_device *dev,
unsigned int n_data = MIN_SAMPLES;
int ret = 0;
if (copy_from_user(&insn, arg, sizeof(insn))) {
if (copy_from_user(&insn, arg, sizeof(insn)))
return -EFAULT;
}
n_data = max(n_data, insn.n);

View File

@ -252,9 +252,9 @@ static int cb_pcimdas_di_insn_bits(struct comedi_device *dev,
}
static int cb_pcimdas_do_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
struct cb_pcimdas_private *devpriv = dev->private;

View File

@ -656,6 +656,7 @@ static int ni_660x_set_pfi_routing(struct comedi_device *dev,
case NI_660X_PFI_OUTPUT_DIO:
if (chan > 31)
return -EINVAL;
break;
default:
return -EINVAL;
}

View File

@ -49,116 +49,117 @@
/* defines for the PCI-DIO-32HS */
#define Window_Address 4 /* W */
#define Interrupt_And_Window_Status 4 /* R */
#define IntStatus1 BIT(0)
#define IntStatus2 BIT(1)
#define WindowAddressStatus_mask 0x7c
#define WINDOW_ADDRESS 4 /* W */
#define INTERRUPT_AND_WINDOW_STATUS 4 /* R */
#define INT_STATUS_1 BIT(0)
#define INT_STATUS_2 BIT(1)
#define WINDOW_ADDRESS_STATUS_MASK 0x7c
#define Master_DMA_And_Interrupt_Control 5 /* W */
#define InterruptLine(x) ((x) & 3)
#define OpenInt BIT(2)
#define Group_Status 5 /* R */
#define DataLeft BIT(0)
#define Req BIT(2)
#define StopTrig BIT(3)
#define MASTER_DMA_AND_INTERRUPT_CONTROL 5 /* W */
#define INTERRUPT_LINE(x) ((x) & 3)
#define OPEN_INT BIT(2)
#define GROUP_STATUS 5 /* R */
#define DATA_LEFT BIT(0)
#define REQ BIT(2)
#define STOP_TRIG BIT(3)
#define Group_1_Flags 6 /* R */
#define Group_2_Flags 7 /* R */
#define TransferReady BIT(0)
#define CountExpired BIT(1)
#define Waited BIT(5)
#define PrimaryTC BIT(6)
#define SecondaryTC BIT(7)
#define GROUP_1_FLAGS 6 /* R */
#define GROUP_2_FLAGS 7 /* R */
#define TRANSFER_READY BIT(0)
#define COUNT_EXPIRED BIT(1)
#define WAITED BIT(5)
#define PRIMARY_TC BIT(6)
#define SECONDARY_TC BIT(7)
/* #define SerialRose */
/* #define ReqRose */
/* #define Paused */
#define Group_1_First_Clear 6 /* W */
#define Group_2_First_Clear 7 /* W */
#define ClearWaited BIT(3)
#define ClearPrimaryTC BIT(4)
#define ClearSecondaryTC BIT(5)
#define DMAReset BIT(6)
#define FIFOReset BIT(7)
#define ClearAll 0xf8
#define GROUP_1_FIRST_CLEAR 6 /* W */
#define GROUP_2_FIRST_CLEAR 7 /* W */
#define CLEAR_WAITED BIT(3)
#define CLEAR_PRIMARY_TC BIT(4)
#define CLEAR_SECONDARY_TC BIT(5)
#define DMA_RESET BIT(6)
#define FIFO_RESET BIT(7)
#define CLEAR_ALL 0xf8
#define Group_1_FIFO 8 /* W */
#define Group_2_FIFO 12 /* W */
#define GROUP_1_FIFO 8 /* W */
#define GROUP_2_FIFO 12 /* W */
#define Transfer_Count 20
#define Chip_ID_D 24
#define Chip_ID_I 25
#define Chip_ID_O 26
#define Chip_Version 27
#define Port_IO(x) (28 + (x))
#define Port_Pin_Directions(x) (32 + (x))
#define Port_Pin_Mask(x) (36 + (x))
#define Port_Pin_Polarities(x) (40 + (x))
#define TRANSFER_COUNT 20
#define CHIP_ID_D 24
#define CHIP_ID_I 25
#define CHIP_ID_O 26
#define CHIP_VERSION 27
#define PORT_IO(x) (28 + (x))
#define PORT_PIN_DIRECTIONS(x) (32 + (x))
#define PORT_PIN_MASK(x) (36 + (x))
#define PORT_PIN_POLARITIES(x) (40 + (x))
#define Master_Clock_Routing 45
#define RTSIClocking(x) (((x) & 3) << 4)
#define MASTER_CLOCK_ROUTING 45
#define RTSI_CLOCKING(x) (((x) & 3) << 4)
#define Group_1_Second_Clear 46 /* W */
#define Group_2_Second_Clear 47 /* W */
#define ClearExpired BIT(0)
#define GROUP_1_SECOND_CLEAR 46 /* W */
#define GROUP_2_SECOND_CLEAR 47 /* W */
#define CLEAR_EXPIRED BIT(0)
#define Port_Pattern(x) (48 + (x))
#define PORT_PATTERN(x) (48 + (x))
#define Data_Path 64
#define FIFOEnableA BIT(0)
#define FIFOEnableB BIT(1)
#define FIFOEnableC BIT(2)
#define FIFOEnableD BIT(3)
#define Funneling(x) (((x) & 3) << 4)
#define GroupDirection BIT(7)
#define DATA_PATH 64
#define FIFO_ENABLE_A BIT(0)
#define FIFO_ENABLE_B BIT(1)
#define FIFO_ENABLE_C BIT(2)
#define FIFO_ENABLE_D BIT(3)
#define FUNNELING(x) (((x) & 3) << 4)
#define GROUP_DIRECTION BIT(7)
#define Protocol_Register_1 65
#define OpMode Protocol_Register_1
#define RunMode(x) ((x) & 7)
#define Numbered BIT(3)
#define PROTOCOL_REGISTER_1 65
#define OP_MODE PROTOCOL_REGISTER_1
#define RUN_MODE(x) ((x) & 7)
#define NUMBERED BIT(3)
#define Protocol_Register_2 66
#define ClockReg Protocol_Register_2
#define ClockLine(x) (((x) & 3) << 5)
#define InvertStopTrig BIT(7)
#define DataLatching(x) (((x) & 3) << 5)
#define PROTOCOL_REGISTER_2 66
#define CLOCK_REG PROTOCOL_REGISTER_2
#define CLOCK_LINE(x) (((x) & 3) << 5)
#define INVERT_STOP_TRIG BIT(7)
#define DATA_LATCHING(x) (((x) & 3) << 5)
#define Protocol_Register_3 67
#define Sequence Protocol_Register_3
#define PROTOCOL_REGISTER_3 67
#define SEQUENCE PROTOCOL_REGISTER_3
#define Protocol_Register_14 68 /* 16 bit */
#define ClockSpeed Protocol_Register_14
#define PROTOCOL_REGISTER_14 68 /* 16 bit */
#define CLOCK_SPEED PROTOCOL_REGISTER_14
#define Protocol_Register_4 70
#define ReqReg Protocol_Register_4
#define ReqConditioning(x) (((x) & 7) << 3)
#define PROTOCOL_REGISTER_4 70
#define REQ_REG PROTOCOL_REGISTER_4
#define REQ_CONDITIONING(x) (((x) & 7) << 3)
#define Protocol_Register_5 71
#define BlockMode Protocol_Register_5
#define PROTOCOL_REGISTER_5 71
#define BLOCK_MODE PROTOCOL_REGISTER_5
#define FIFO_Control 72
#define ReadyLevel(x) ((x) & 7)
#define READY_LEVEL(x) ((x) & 7)
#define Protocol_Register_6 73
#define LinePolarities Protocol_Register_6
#define InvertAck BIT(0)
#define InvertReq BIT(1)
#define InvertClock BIT(2)
#define InvertSerial BIT(3)
#define OpenAck BIT(4)
#define OpenClock BIT(5)
#define PROTOCOL_REGISTER_6 73
#define LINE_POLARITIES PROTOCOL_REGISTER_6
#define INVERT_ACK BIT(0)
#define INVERT_REQ BIT(1)
#define INVERT_CLOCK BIT(2)
#define INVERT_SERIAL BIT(3)
#define OPEN_ACK BIT(4)
#define OPEN_CLOCK BIT(5)
#define Protocol_Register_7 74
#define AckSer Protocol_Register_7
#define AckLine(x) (((x) & 3) << 2)
#define ExchangePins BIT(7)
#define PROTOCOL_REGISTER_7 74
#define ACK_SER PROTOCOL_REGISTER_7
#define ACK_LINE(x) (((x) & 3) << 2)
#define EXCHANGE_PINS BIT(7)
#define Interrupt_Control 75
/* bits same as flags */
#define INTERRUPT_CONTROL 75
/* bits same as flags */
#define DMA_LINE_CONTROL_GROUP1 76
#define DMA_LINE_CONTROL_GROUP2 108
#define DMA_Line_Control_Group1 76
#define DMA_Line_Control_Group2 108
/* channel zero is none */
static inline unsigned int primary_DMAChannel_bits(unsigned int channel)
{
@ -170,41 +171,41 @@ static inline unsigned int secondary_DMAChannel_bits(unsigned int channel)
return (channel << 2) & 0xc;
}
#define Transfer_Size_Control 77
#define TransferWidth(x) ((x) & 3)
#define TransferLength(x) (((x) & 3) << 3)
#define RequireRLevel BIT(5)
#define TRANSFER_SIZE_CONTROL 77
#define TRANSFER_WIDTH(x) ((x) & 3)
#define TRANSFER_LENGTH(x) (((x) & 3) << 3)
#define REQUIRE_R_LEVEL BIT(5)
#define Protocol_Register_15 79
#define DAQOptions Protocol_Register_15
#define StartSource(x) ((x) & 0x3)
#define InvertStart BIT(2)
#define StopSource(x) (((x) & 0x3) << 3)
#define ReqStart BIT(6)
#define PreStart BIT(7)
#define PROTOCOL_REGISTER_15 79
#define DAQ_OPTIONS PROTOCOL_REGISTER_15
#define START_SOURCE(x) ((x) & 0x3)
#define INVERT_START BIT(2)
#define STOP_SOURCE(x) (((x) & 0x3) << 3)
#define REQ_START BIT(6)
#define PRE_START BIT(7)
#define Pattern_Detection 81
#define DetectionMethod BIT(0)
#define InvertMatch BIT(1)
#define IE_Pattern_Detection BIT(2)
#define PATTERN_DETECTION 81
#define DETECTION_METHOD BIT(0)
#define INVERT_MATCH BIT(1)
#define IE_PATTERN_DETECTION BIT(2)
#define Protocol_Register_9 82
#define ReqDelay Protocol_Register_9
#define PROTOCOL_REGISTER_9 82
#define REQ_DELAY PROTOCOL_REGISTER_9
#define Protocol_Register_10 83
#define ReqNotDelay Protocol_Register_10
#define PROTOCOL_REGISTER_10 83
#define REQ_NOT_DELAY PROTOCOL_REGISTER_10
#define Protocol_Register_11 84
#define AckDelay Protocol_Register_11
#define PROTOCOL_REGISTER_11 84
#define ACK_DELAY PROTOCOL_REGISTER_11
#define Protocol_Register_12 85
#define AckNotDelay Protocol_Register_12
#define PROTOCOL_REGISTER_12 85
#define ACK_NOT_DELAY PROTOCOL_REGISTER_12
#define Protocol_Register_13 86
#define Data1Delay Protocol_Register_13
#define PROTOCOL_REGISTER_13 86
#define DATA_1_DELAY PROTOCOL_REGISTER_13
#define Protocol_Register_8 88 /* 32 bit */
#define StartDelay Protocol_Register_8
#define PROTOCOL_REGISTER_8 88 /* 32 bit */
#define START_DELAY PROTOCOL_REGISTER_8
/* Firmware files for PCI-6524 */
#define FW_PCI_6534_MAIN "ni6534a.bin"
@ -246,9 +247,10 @@ enum FPGA_Control_Bits {
#define TIMER_BASE 50 /* nanoseconds */
#ifdef USE_DMA
#define IntEn (CountExpired | Waited | PrimaryTC | SecondaryTC)
#define INT_EN (COUNT_EXPIRED | WAITED | PRIMARY_TC | SECONDARY_TC)
#else
#define IntEn (TransferReady | CountExpired | Waited | PrimaryTC | SecondaryTC)
#define INT_EN (TRANSFER_READY | COUNT_EXPIRED | WAITED \
| PRIMARY_TC | SECONDARY_TC)
#endif
enum nidio_boardid {
@ -283,7 +285,7 @@ struct nidio96_private {
struct mite *mite;
int boardtype;
int dio;
unsigned short OpModeBits;
unsigned short OP_MODEBits;
struct mite_channel *di_mite_chan;
struct mite_ring *di_mite_ring;
spinlock_t mite_channel_lock;
@ -307,7 +309,7 @@ static int ni_pcidio_request_di_mite_channel(struct comedi_device *dev)
devpriv->di_mite_chan->dir = COMEDI_INPUT;
writeb(primary_DMAChannel_bits(devpriv->di_mite_chan->channel) |
secondary_DMAChannel_bits(devpriv->di_mite_chan->channel),
dev->mmio + DMA_Line_Control_Group1);
dev->mmio + DMA_LINE_CONTROL_GROUP1);
mmiowb();
spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
return 0;
@ -324,7 +326,7 @@ static void ni_pcidio_release_di_mite_channel(struct comedi_device *dev)
devpriv->di_mite_chan = NULL;
writeb(primary_DMAChannel_bits(0) |
secondary_DMAChannel_bits(0),
dev->mmio + DMA_Line_Control_Group1);
dev->mmio + DMA_LINE_CONTROL_GROUP1);
mmiowb();
}
spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
@ -391,8 +393,8 @@ static irqreturn_t nidio_interrupt(int irq, void *d)
/* Lock to avoid race with comedi_poll */
spin_lock(&dev->spinlock);
status = readb(dev->mmio + Interrupt_And_Window_Status);
flags = readb(dev->mmio + Group_1_Flags);
status = readb(dev->mmio + INTERRUPT_AND_WINDOW_STATUS);
flags = readb(dev->mmio + GROUP_1_FLAGS);
spin_lock(&devpriv->mite_channel_lock);
if (devpriv->di_mite_chan) {
@ -401,63 +403,63 @@ static irqreturn_t nidio_interrupt(int irq, void *d)
}
spin_unlock(&devpriv->mite_channel_lock);
while (status & DataLeft) {
while (status & DATA_LEFT) {
work++;
if (work > 20) {
dev_dbg(dev->class_dev, "too much work in interrupt\n");
writeb(0x00,
dev->mmio + Master_DMA_And_Interrupt_Control);
dev->mmio + MASTER_DMA_AND_INTERRUPT_CONTROL);
break;
}
flags &= IntEn;
flags &= INT_EN;
if (flags & TransferReady) {
while (flags & TransferReady) {
if (flags & TRANSFER_READY) {
while (flags & TRANSFER_READY) {
work++;
if (work > 100) {
dev_dbg(dev->class_dev,
"too much work in interrupt\n");
writeb(0x00, dev->mmio +
Master_DMA_And_Interrupt_Control
MASTER_DMA_AND_INTERRUPT_CONTROL
);
goto out;
}
auxdata = readl(dev->mmio + Group_1_FIFO);
auxdata = readl(dev->mmio + GROUP_1_FIFO);
comedi_buf_write_samples(s, &auxdata, 1);
flags = readb(dev->mmio + Group_1_Flags);
flags = readb(dev->mmio + GROUP_1_FLAGS);
}
}
if (flags & CountExpired) {
writeb(ClearExpired, dev->mmio + Group_1_Second_Clear);
if (flags & COUNT_EXPIRED) {
writeb(CLEAR_EXPIRED, dev->mmio + GROUP_1_SECOND_CLEAR);
async->events |= COMEDI_CB_EOA;
writeb(0x00, dev->mmio + OpMode);
writeb(0x00, dev->mmio + OP_MODE);
break;
} else if (flags & Waited) {
writeb(ClearWaited, dev->mmio + Group_1_First_Clear);
} else if (flags & WAITED) {
writeb(CLEAR_WAITED, dev->mmio + GROUP_1_FIRST_CLEAR);
async->events |= COMEDI_CB_ERROR;
break;
} else if (flags & PrimaryTC) {
writeb(ClearPrimaryTC,
dev->mmio + Group_1_First_Clear);
} else if (flags & PRIMARY_TC) {
writeb(CLEAR_PRIMARY_TC,
dev->mmio + GROUP_1_FIRST_CLEAR);
async->events |= COMEDI_CB_EOA;
} else if (flags & SecondaryTC) {
writeb(ClearSecondaryTC,
dev->mmio + Group_1_First_Clear);
} else if (flags & SECONDARY_TC) {
writeb(CLEAR_SECONDARY_TC,
dev->mmio + GROUP_1_FIRST_CLEAR);
async->events |= COMEDI_CB_EOA;
}
flags = readb(dev->mmio + Group_1_Flags);
status = readb(dev->mmio + Interrupt_And_Window_Status);
flags = readb(dev->mmio + GROUP_1_FLAGS);
status = readb(dev->mmio + INTERRUPT_AND_WINDOW_STATUS);
}
out:
comedi_handle_events(dev, s);
#if 0
if (!tag)
writeb(0x03, dev->mmio + Master_DMA_And_Interrupt_Control);
writeb(0x03, dev->mmio + MASTER_DMA_AND_INTERRUPT_CONTROL);
#endif
spin_unlock(&dev->spinlock);
@ -484,7 +486,7 @@ static int ni_pcidio_insn_config(struct comedi_device *dev,
if (ret)
return ret;
writel(s->io_bits, dev->mmio + Port_Pin_Directions(0));
writel(s->io_bits, dev->mmio + PORT_PIN_DIRECTIONS(0));
return insn->n;
}
@ -495,9 +497,9 @@ static int ni_pcidio_insn_bits(struct comedi_device *dev,
unsigned int *data)
{
if (comedi_dio_update_state(s, data))
writel(s->state, dev->mmio + Port_IO(0));
writel(s->state, dev->mmio + PORT_IO(0));
data[1] = readl(dev->mmio + Port_IO(0));
data[1] = readl(dev->mmio + PORT_IO(0));
return insn->n;
}
@ -609,7 +611,7 @@ static int ni_pcidio_inttrig(struct comedi_device *dev,
if (trig_num != cmd->start_arg)
return -EINVAL;
writeb(devpriv->OpModeBits, dev->mmio + OpMode);
writeb(devpriv->OP_MODEBits, dev->mmio + OP_MODE);
s->async->inttrig = NULL;
return 1;
@ -621,78 +623,78 @@ static int ni_pcidio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
struct comedi_cmd *cmd = &s->async->cmd;
/* XXX configure ports for input */
writel(0x0000, dev->mmio + Port_Pin_Directions(0));
writel(0x0000, dev->mmio + PORT_PIN_DIRECTIONS(0));
if (1) {
/* enable fifos A B C D */
writeb(0x0f, dev->mmio + Data_Path);
writeb(0x0f, dev->mmio + DATA_PATH);
/* set transfer width a 32 bits */
writeb(TransferWidth(0) | TransferLength(0),
dev->mmio + Transfer_Size_Control);
writeb(TRANSFER_WIDTH(0) | TRANSFER_LENGTH(0),
dev->mmio + TRANSFER_SIZE_CONTROL);
} else {
writeb(0x03, dev->mmio + Data_Path);
writeb(TransferWidth(3) | TransferLength(0),
dev->mmio + Transfer_Size_Control);
writeb(0x03, dev->mmio + DATA_PATH);
writeb(TRANSFER_WIDTH(3) | TRANSFER_LENGTH(0),
dev->mmio + TRANSFER_SIZE_CONTROL);
}
/* protocol configuration */
if (cmd->scan_begin_src == TRIG_TIMER) {
/* page 4-5, "input with internal REQs" */
writeb(0, dev->mmio + OpMode);
writeb(0x00, dev->mmio + ClockReg);
writeb(1, dev->mmio + Sequence);
writeb(0x04, dev->mmio + ReqReg);
writeb(4, dev->mmio + BlockMode);
writeb(3, dev->mmio + LinePolarities);
writeb(0xc0, dev->mmio + AckSer);
writeb(0, dev->mmio + OP_MODE);
writeb(0x00, dev->mmio + CLOCK_REG);
writeb(1, dev->mmio + SEQUENCE);
writeb(0x04, dev->mmio + REQ_REG);
writeb(4, dev->mmio + BLOCK_MODE);
writeb(3, dev->mmio + LINE_POLARITIES);
writeb(0xc0, dev->mmio + ACK_SER);
writel(ni_pcidio_ns_to_timer(&cmd->scan_begin_arg,
CMDF_ROUND_NEAREST),
dev->mmio + StartDelay);
writeb(1, dev->mmio + ReqDelay);
writeb(1, dev->mmio + ReqNotDelay);
writeb(1, dev->mmio + AckDelay);
writeb(0x0b, dev->mmio + AckNotDelay);
writeb(0x01, dev->mmio + Data1Delay);
dev->mmio + START_DELAY);
writeb(1, dev->mmio + REQ_DELAY);
writeb(1, dev->mmio + REQ_NOT_DELAY);
writeb(1, dev->mmio + ACK_DELAY);
writeb(0x0b, dev->mmio + ACK_NOT_DELAY);
writeb(0x01, dev->mmio + DATA_1_DELAY);
/*
* manual, page 4-5:
* ClockSpeed comment is incorrectly listed on DAQOptions
* CLOCK_SPEED comment is incorrectly listed on DAQ_OPTIONS
*/
writew(0, dev->mmio + ClockSpeed);
writeb(0, dev->mmio + DAQOptions);
writew(0, dev->mmio + CLOCK_SPEED);
writeb(0, dev->mmio + DAQ_OPTIONS);
} else {
/* TRIG_EXT */
/* page 4-5, "input with external REQs" */
writeb(0, dev->mmio + OpMode);
writeb(0x00, dev->mmio + ClockReg);
writeb(0, dev->mmio + Sequence);
writeb(0x00, dev->mmio + ReqReg);
writeb(4, dev->mmio + BlockMode);
writeb(0, dev->mmio + OP_MODE);
writeb(0x00, dev->mmio + CLOCK_REG);
writeb(0, dev->mmio + SEQUENCE);
writeb(0x00, dev->mmio + REQ_REG);
writeb(4, dev->mmio + BLOCK_MODE);
if (!(cmd->scan_begin_arg & CR_INVERT)) /* Leading Edge */
writeb(0, dev->mmio + LinePolarities);
writeb(0, dev->mmio + LINE_POLARITIES);
else /* Trailing Edge */
writeb(2, dev->mmio + LinePolarities);
writeb(0x00, dev->mmio + AckSer);
writel(1, dev->mmio + StartDelay);
writeb(1, dev->mmio + ReqDelay);
writeb(1, dev->mmio + ReqNotDelay);
writeb(1, dev->mmio + AckDelay);
writeb(0x0C, dev->mmio + AckNotDelay);
writeb(0x10, dev->mmio + Data1Delay);
writew(0, dev->mmio + ClockSpeed);
writeb(0x60, dev->mmio + DAQOptions);
writeb(2, dev->mmio + LINE_POLARITIES);
writeb(0x00, dev->mmio + ACK_SER);
writel(1, dev->mmio + START_DELAY);
writeb(1, dev->mmio + REQ_DELAY);
writeb(1, dev->mmio + REQ_NOT_DELAY);
writeb(1, dev->mmio + ACK_DELAY);
writeb(0x0C, dev->mmio + ACK_NOT_DELAY);
writeb(0x10, dev->mmio + DATA_1_DELAY);
writew(0, dev->mmio + CLOCK_SPEED);
writeb(0x60, dev->mmio + DAQ_OPTIONS);
}
if (cmd->stop_src == TRIG_COUNT) {
writel(cmd->stop_arg,
dev->mmio + Transfer_Count);
dev->mmio + TRANSFER_COUNT);
} else {
/* XXX */
}
#ifdef USE_DMA
writeb(ClearPrimaryTC | ClearSecondaryTC,
dev->mmio + Group_1_First_Clear);
writeb(CLEAR_PRIMARY_TC | CLEAR_SECONDARY_TC,
dev->mmio + GROUP_1_FIRST_CLEAR);
{
int retval = setup_mite_dma(dev, s);
@ -701,25 +703,25 @@ static int ni_pcidio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
return retval;
}
#else
writeb(0x00, dev->mmio + DMA_Line_Control_Group1);
writeb(0x00, dev->mmio + DMA_LINE_CONTROL_GROUP1);
#endif
writeb(0x00, dev->mmio + DMA_Line_Control_Group2);
writeb(0x00, dev->mmio + DMA_LINE_CONTROL_GROUP2);
/* clear and enable interrupts */
writeb(0xff, dev->mmio + Group_1_First_Clear);
/* writeb(ClearExpired, dev->mmio+Group_1_Second_Clear); */
writeb(0xff, dev->mmio + GROUP_1_FIRST_CLEAR);
/* writeb(CLEAR_EXPIRED, dev->mmio+GROUP_1_SECOND_CLEAR); */
writeb(IntEn, dev->mmio + Interrupt_Control);
writeb(0x03, dev->mmio + Master_DMA_And_Interrupt_Control);
writeb(INT_EN, dev->mmio + INTERRUPT_CONTROL);
writeb(0x03, dev->mmio + MASTER_DMA_AND_INTERRUPT_CONTROL);
if (cmd->stop_src == TRIG_NONE) {
devpriv->OpModeBits = DataLatching(0) | RunMode(7);
devpriv->OP_MODEBits = DATA_LATCHING(0) | RUN_MODE(7);
} else { /* TRIG_TIMER */
devpriv->OpModeBits = Numbered | RunMode(7);
devpriv->OP_MODEBits = NUMBERED | RUN_MODE(7);
}
if (cmd->start_src == TRIG_NOW) {
/* start */
writeb(devpriv->OpModeBits, dev->mmio + OpMode);
writeb(devpriv->OP_MODEBits, dev->mmio + OP_MODE);
s->async->inttrig = NULL;
} else {
/* TRIG_INT */
@ -732,7 +734,7 @@ static int ni_pcidio_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
static int ni_pcidio_cancel(struct comedi_device *dev,
struct comedi_subdevice *s)
{
writeb(0x00, dev->mmio + Master_DMA_And_Interrupt_Control);
writeb(0x00, dev->mmio + MASTER_DMA_AND_INTERRUPT_CONTROL);
ni_pcidio_release_di_mite_channel(dev);
return 0;
@ -869,12 +871,12 @@ static int pci_6534_upload_firmware(struct comedi_device *dev)
static void nidio_reset_board(struct comedi_device *dev)
{
writel(0, dev->mmio + Port_IO(0));
writel(0, dev->mmio + Port_Pin_Directions(0));
writel(0, dev->mmio + Port_Pin_Mask(0));
writel(0, dev->mmio + PORT_IO(0));
writel(0, dev->mmio + PORT_PIN_DIRECTIONS(0));
writel(0, dev->mmio + PORT_PIN_MASK(0));
/* disable interrupts on board */
writeb(0, dev->mmio + Master_DMA_And_Interrupt_Control);
writeb(0, dev->mmio + MASTER_DMA_AND_INTERRUPT_CONTROL);
}
static int nidio_auto_attach(struct comedi_device *dev,
@ -925,7 +927,7 @@ static int nidio_auto_attach(struct comedi_device *dev,
return ret;
dev_info(dev->class_dev, "%s rev=%d\n", dev->board_name,
readb(dev->mmio + Chip_Version));
readb(dev->mmio + CHIP_VERSION));
s = &dev->subdevices[0];

View File

@ -224,13 +224,16 @@ static void ni_tio_set_bits_transient(struct ni_gpct *counter,
unsigned int transient)
{
struct ni_gpct_device *counter_dev = counter->counter_dev;
unsigned int chip = counter->chip_index;
unsigned long flags;
if (reg < NITIO_NUM_REGS) {
if (reg < NITIO_NUM_REGS && chip < counter_dev->num_chips) {
unsigned int *regs = counter_dev->regs[chip];
spin_lock_irqsave(&counter_dev->regs_lock, flags);
counter_dev->regs[reg] &= ~mask;
counter_dev->regs[reg] |= (value & mask);
ni_tio_write(counter, counter_dev->regs[reg] | transient, reg);
regs[reg] &= ~mask;
regs[reg] |= (value & mask);
ni_tio_write(counter, regs[reg] | transient, reg);
mmiowb();
spin_unlock_irqrestore(&counter_dev->regs_lock, flags);
}
@ -267,12 +270,13 @@ unsigned int ni_tio_get_soft_copy(const struct ni_gpct *counter,
enum ni_gpct_register reg)
{
struct ni_gpct_device *counter_dev = counter->counter_dev;
unsigned int chip = counter->chip_index;
unsigned int value = 0;
unsigned long flags;
if (reg < NITIO_NUM_REGS) {
if (reg < NITIO_NUM_REGS && chip < counter_dev->num_chips) {
spin_lock_irqsave(&counter_dev->regs_lock, flags);
value = counter_dev->regs[reg];
value = counter_dev->regs[chip][reg];
spin_unlock_irqrestore(&counter_dev->regs_lock, flags);
}
return value;
@ -302,6 +306,7 @@ static int ni_m_series_clock_src_select(const struct ni_gpct *counter,
{
struct ni_gpct_device *counter_dev = counter->counter_dev;
unsigned int cidx = counter->counter_index;
unsigned int chip = counter->chip_index;
unsigned int second_gate_reg = NITIO_GATE2_REG(cidx);
unsigned int clock_source = 0;
unsigned int src;
@ -318,7 +323,7 @@ static int ni_m_series_clock_src_select(const struct ni_gpct *counter,
clock_source = NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS;
break;
case NI_M_TIMEBASE_3_CLK:
if (counter_dev->regs[second_gate_reg] & GI_SRC_SUBSEL)
if (counter_dev->regs[chip][second_gate_reg] & GI_SRC_SUBSEL)
clock_source =
NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS;
else
@ -328,7 +333,7 @@ static int ni_m_series_clock_src_select(const struct ni_gpct *counter,
clock_source = NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS;
break;
case NI_M_NEXT_GATE_CLK:
if (counter_dev->regs[second_gate_reg] & GI_SRC_SUBSEL)
if (counter_dev->regs[chip][second_gate_reg] & GI_SRC_SUBSEL)
clock_source = NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS;
else
clock_source = NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS;
@ -721,6 +726,7 @@ static void ni_tio_set_source_subselect(struct ni_gpct *counter,
{
struct ni_gpct_device *counter_dev = counter->counter_dev;
unsigned int cidx = counter->counter_index;
unsigned int chip = counter->chip_index;
unsigned int second_gate_reg = NITIO_GATE2_REG(cidx);
if (counter_dev->variant != ni_gpct_variant_m_series)
@ -729,18 +735,18 @@ static void ni_tio_set_source_subselect(struct ni_gpct *counter,
/* Gi_Source_Subselect is zero */
case NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS:
case NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS:
counter_dev->regs[second_gate_reg] &= ~GI_SRC_SUBSEL;
counter_dev->regs[chip][second_gate_reg] &= ~GI_SRC_SUBSEL;
break;
/* Gi_Source_Subselect is one */
case NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS:
case NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS:
counter_dev->regs[second_gate_reg] |= GI_SRC_SUBSEL;
counter_dev->regs[chip][second_gate_reg] |= GI_SRC_SUBSEL;
break;
/* Gi_Source_Subselect doesn't matter */
default:
return;
}
ni_tio_write(counter, counter_dev->regs[second_gate_reg],
ni_tio_write(counter, counter_dev->regs[chip][second_gate_reg],
second_gate_reg);
}
@ -1116,6 +1122,7 @@ static int ni_tio_set_other_src(struct ni_gpct *counter, unsigned int index,
{
struct ni_gpct_device *counter_dev = counter->counter_dev;
unsigned int cidx = counter->counter_index;
unsigned int chip = counter->chip_index;
unsigned int abz_reg, shift, mask;
if (counter_dev->variant != ni_gpct_variant_m_series)
@ -1141,9 +1148,9 @@ static int ni_tio_set_other_src(struct ni_gpct *counter, unsigned int index,
if (source > 0x1f)
source = 0x1f; /* Disable gate */
counter_dev->regs[abz_reg] &= ~mask;
counter_dev->regs[abz_reg] |= (source << shift) & mask;
ni_tio_write(counter, counter_dev->regs[abz_reg], abz_reg);
counter_dev->regs[chip][abz_reg] &= ~mask;
counter_dev->regs[chip][abz_reg] |= (source << shift) & mask;
ni_tio_write(counter, counter_dev->regs[chip][abz_reg], abz_reg);
return 0;
}
@ -1632,6 +1639,7 @@ int ni_tio_insn_read(struct comedi_device *dev,
struct ni_gpct_device *counter_dev = counter->counter_dev;
unsigned int channel = CR_CHAN(insn->chanspec);
unsigned int cidx = counter->counter_index;
unsigned int chip = counter->chip_index;
int i;
for (i = 0; i < insn->n; i++) {
@ -1640,10 +1648,12 @@ int ni_tio_insn_read(struct comedi_device *dev,
data[i] = ni_tio_read_sw_save_reg(dev, s);
break;
case 1:
data[i] = counter_dev->regs[NITIO_LOADA_REG(cidx)];
data[i] =
counter_dev->regs[chip][NITIO_LOADA_REG(cidx)];
break;
case 2:
data[i] = counter_dev->regs[NITIO_LOADB_REG(cidx)];
data[i] =
counter_dev->regs[chip][NITIO_LOADB_REG(cidx)];
break;
}
}
@ -1670,6 +1680,7 @@ int ni_tio_insn_write(struct comedi_device *dev,
struct ni_gpct_device *counter_dev = counter->counter_dev;
unsigned int channel = CR_CHAN(insn->chanspec);
unsigned int cidx = counter->counter_index;
unsigned int chip = counter->chip_index;
unsigned int load_reg;
if (insn->n < 1)
@ -1690,14 +1701,15 @@ int ni_tio_insn_write(struct comedi_device *dev,
ni_tio_set_bits_transient(counter, NITIO_CMD_REG(cidx),
0, 0, GI_LOAD);
/* restore load reg */
ni_tio_write(counter, counter_dev->regs[load_reg], load_reg);
ni_tio_write(counter, counter_dev->regs[chip][load_reg],
load_reg);
break;
case 1:
counter_dev->regs[NITIO_LOADA_REG(cidx)] = data[0];
counter_dev->regs[chip][NITIO_LOADA_REG(cidx)] = data[0];
ni_tio_write(counter, data[0], NITIO_LOADA_REG(cidx));
break;
case 2:
counter_dev->regs[NITIO_LOADB_REG(cidx)] = data[0];
counter_dev->regs[chip][NITIO_LOADB_REG(cidx)] = data[0];
ni_tio_write(counter, data[0], NITIO_LOADB_REG(cidx));
break;
default:
@ -1711,11 +1723,12 @@ void ni_tio_init_counter(struct ni_gpct *counter)
{
struct ni_gpct_device *counter_dev = counter->counter_dev;
unsigned int cidx = counter->counter_index;
unsigned int chip = counter->chip_index;
ni_tio_reset_count_and_disarm(counter);
/* initialize counter registers */
counter_dev->regs[NITIO_AUTO_INC_REG(cidx)] = 0x0;
counter_dev->regs[chip][NITIO_AUTO_INC_REG(cidx)] = 0x0;
ni_tio_write(counter, 0x0, NITIO_AUTO_INC_REG(cidx));
ni_tio_set_bits(counter, NITIO_CMD_REG(cidx),
@ -1723,10 +1736,10 @@ void ni_tio_init_counter(struct ni_gpct *counter)
ni_tio_set_bits(counter, NITIO_MODE_REG(cidx), ~0, 0);
counter_dev->regs[NITIO_LOADA_REG(cidx)] = 0x0;
counter_dev->regs[chip][NITIO_LOADA_REG(cidx)] = 0x0;
ni_tio_write(counter, 0x0, NITIO_LOADA_REG(cidx));
counter_dev->regs[NITIO_LOADB_REG(cidx)] = 0x0;
counter_dev->regs[chip][NITIO_LOADB_REG(cidx)] = 0x0;
ni_tio_write(counter, 0x0, NITIO_LOADB_REG(cidx));
ni_tio_set_bits(counter, NITIO_INPUT_SEL_REG(cidx), ~0, 0);
@ -1735,7 +1748,7 @@ void ni_tio_init_counter(struct ni_gpct *counter)
ni_tio_set_bits(counter, NITIO_CNT_MODE_REG(cidx), ~0, 0);
if (ni_tio_has_gate2_registers(counter_dev)) {
counter_dev->regs[NITIO_GATE2_REG(cidx)] = 0x0;
counter_dev->regs[chip][NITIO_GATE2_REG(cidx)] = 0x0;
ni_tio_write(counter, 0x0, NITIO_GATE2_REG(cidx));
}
@ -1776,9 +1789,16 @@ ni_gpct_device_construct(struct comedi_device *dev,
spin_lock_init(&counter_dev->regs_lock);
counter_dev->num_counters = num_counters;
counter_dev->num_chips = DIV_ROUND_UP(num_counters, counters_per_chip);
counter_dev->counters = kcalloc(num_counters, sizeof(*counter),
GFP_KERNEL);
if (!counter_dev->counters) {
counter_dev->regs = kcalloc(counter_dev->num_chips,
sizeof(*counter_dev->regs), GFP_KERNEL);
if (!counter_dev->regs || !counter_dev->counters) {
kfree(counter_dev->regs);
kfree(counter_dev->counters);
kfree(counter_dev);
return NULL;
}
@ -1790,8 +1810,6 @@ ni_gpct_device_construct(struct comedi_device *dev,
counter->counter_index = i % counters_per_chip;
spin_lock_init(&counter->lock);
}
counter_dev->num_counters = num_counters;
counter_dev->counters_per_chip = counters_per_chip;
return counter_dev;
}
@ -1801,6 +1819,7 @@ void ni_gpct_device_destroy(struct ni_gpct_device *counter_dev)
{
if (!counter_dev)
return;
kfree(counter_dev->regs);
kfree(counter_dev->counters);
kfree(counter_dev);
}

View File

@ -107,8 +107,8 @@ struct ni_gpct_device {
enum ni_gpct_variant variant;
struct ni_gpct *counters;
unsigned int num_counters;
unsigned int counters_per_chip;
unsigned int regs[NITIO_NUM_REGS];
unsigned int num_chips;
unsigned int (*regs)[NITIO_NUM_REGS]; /* [num_chips][NITIO_NUM_REGS] */
spinlock_t regs_lock; /* protects 'regs' */
const struct ni_route_tables *routing_tables; /* link to routes */
};

View File

@ -61,7 +61,7 @@
#define USBDUXFASTSUB_CPUCS 0xE600
/*
* max lenghth of the transfer-buffer for software upload
* max length of the transfer-buffer for software upload
*/
#define TB_LEN 0x2000

Some files were not shown because too many files have changed in this diff Show More