diff options
73 files changed, 4431 insertions, 1071 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 8127a08e366d..864f8efd12e5 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -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.
\ No newline at end of file + 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. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-sps30 b/Documentation/ABI/testing/sysfs-bus-iio-sps30 new file mode 100644 index 000000000000..143df8e89d08 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-sps30 @@ -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. diff --git a/Documentation/devicetree/bindings/iio/accel/mma8452.txt b/Documentation/devicetree/bindings/iio/accel/mma8452.txt index 2100e9af379c..e132394375a1 100644 --- a/Documentation/devicetree/bindings/iio/accel/mma8452.txt +++ b/Documentation/devicetree/bindings/iio/accel/mma8452.txt @@ -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 { diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7606.txt b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.txt new file mode 100644 index 000000000000..d7b6241ca881 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7606.txt @@ -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>; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.txt b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.txt new file mode 100644 index 000000000000..9f5b88cf680d --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.txt @@ -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"; + }; diff --git a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt index 325090e43ce6..75c775954102 100644 --- a/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt +++ b/Documentation/devicetree/bindings/iio/adc/amlogic,meson-saradc.txt @@ -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" diff --git a/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt b/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt new file mode 100644 index 000000000000..1b8132cd9060 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/nuvoton,npcm-adc.txt @@ -0,0 +1,35 @@ +Nuvoton NPCM Analog to Digital Converter (ADC) + +The NPCM ADC is a 10-bit converter for eight channel inputs. + +Required properties: +- compatible: "nuvoton,npcm750-adc" for the NPCM7XX BMC. +- reg: specifies physical base address and size of the registers. +- interrupts: Contain the ADC interrupt with flags for falling edge. + +Optional properties: +- clocks: phandle of ADC reference clock, in case the clock is not + added the ADC will use the default ADC sample rate. +- vref-supply: The regulator supply ADC reference voltage, in case the + vref-supply is not added the ADC will use internal voltage + reference. + +Required Node in the NPCM7xx BMC: +An additional register is present in the NPCM7xx SOC which is +assumed to be in the same device tree, with and marked as +compatible with "nuvoton,npcm750-rst". + +Example: + +adc: adc@f000c000 { + compatible = "nuvoton,npcm750-adc"; + reg = <0xf000c000 0x8>; + interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk NPCM7XX_CLK_ADC>; +}; + +rst: rst@f0801000 { + compatible = "nuvoton,npcm750-rst", "syscon", + "simple-mfd"; + reg = <0xf0801000 0x6C>; +}; diff --git a/Documentation/devicetree/bindings/iio/adc/ti-ads124s08.txt b/Documentation/devicetree/bindings/iio/adc/ti-ads124s08.txt new file mode 100644 index 000000000000..ecf807bb32f7 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ti-ads124s08.txt @@ -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>; +}; diff --git a/Documentation/devicetree/bindings/iio/chemical/bme680.txt b/Documentation/devicetree/bindings/iio/chemical/bme680.txt new file mode 100644 index 000000000000..7f3827cfb2ff --- /dev/null +++ b/Documentation/devicetree/bindings/iio/chemical/bme680.txt @@ -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>; +}; diff --git a/Documentation/devicetree/bindings/iio/chemical/sensirion,sgp30.txt b/Documentation/devicetree/bindings/iio/chemical/sensirion,sgp30.txt new file mode 100644 index 000000000000..5844ed58173c --- /dev/null +++ b/Documentation/devicetree/bindings/iio/chemical/sensirion,sgp30.txt @@ -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>; +}; diff --git a/Documentation/devicetree/bindings/iio/chemical/sensirion,sps30.txt b/Documentation/devicetree/bindings/iio/chemical/sensirion,sps30.txt new file mode 100644 index 000000000000..6eee2709b5b6 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/chemical/sensirion,sps30.txt @@ -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>; +}; diff --git a/Documentation/devicetree/bindings/iio/impedance-analyzer/ad5933.txt b/Documentation/devicetree/bindings/iio/impedance-analyzer/ad5933.txt new file mode 100644 index 000000000000..5ff38728ff91 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/impedance-analyzer/ad5933.txt @@ -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"; + }; diff --git a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt index 6ab9a9d196b0..268bf7568e19 100644 --- a/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt +++ b/Documentation/devicetree/bindings/iio/imu/inv_mpu6050.txt @@ -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 diff --git a/Documentation/devicetree/bindings/iio/light/max44009.txt b/Documentation/devicetree/bindings/iio/light/max44009.txt new file mode 100644 index 000000000000..4a98848e35c0 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/light/max44009.txt @@ -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>; +}; diff --git a/Documentation/devicetree/bindings/iio/st-sensors.txt b/Documentation/devicetree/bindings/iio/st-sensors.txt index ddcb95509599..52ee4baec6f0 100644 --- a/Documentation/devicetree/bindings/iio/st-sensors.txt +++ b/Documentation/devicetree/bindings/iio/st-sensors.txt @@ -77,3 +77,4 @@ Pressure sensors: - st,lps22hb-press - st,lps33hw - st,lps35hw +- st,lps22hh diff --git a/MAINTAINERS b/MAINTAINERS index 8b866f1f76b8..f35c77b029ff 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -854,6 +854,22 @@ S: Supported F: drivers/iio/adc/ad7124.c F: Documentation/devicetree/bindings/iio/adc/adi,ad7124.txt +ANALOG DEVICES INC AD7606 DRIVER +M: Stefan Popa <stefan.popa@analog.com> +L: linux-iio@vger.kernel.org +W: http://ez.analog.com/community/linux-device-drivers +S: Supported +F: drivers/iio/adc/ad7606.c +F: Documentation/devicetree/bindings/iio/adc/ad7606.txt + +ANALOG DEVICES INC AD7768-1 DRIVER +M: Stefan Popa <stefan.popa@analog.com> +L: linux-iio@vger.kernel.org +W: http://ez.analog.com/community/linux-device-drivers +S: Supported +F: drivers/iio/adc/ad7768-1.c +F: Documentation/devicetree/bindings/iio/adc/adi,ad7768-1.txt + ANALOG DEVICES INC AD9389B DRIVER M: Hans Verkuil <hans.verkuil@cisco.com> L: linux-media@vger.kernel.org diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 421a0a8a1379..302781126bc6 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -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) }; diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index f7b471121508..a3c0916479fa 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -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; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 7a3ca4ec0cb7..46ed6fa7e3f3 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -69,6 +69,33 @@ config AD7476 To compile this driver as a module, choose M here: the module will be called ad7476. +config AD7606 + tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + +config AD7606_IFACE_PARALLEL + tristate "Analog Devices AD7606 ADC driver with parallel interface support" + depends on HAS_IOMEM + select AD7606 + help + Say yes here to build parallel interface support for Analog Devices: + ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the + module will be called ad7606_parallel. + +config AD7606_IFACE_SPI + tristate "Analog Devices AD7606 ADC driver with spi interface support" + depends on SPI + select AD7606 + help + Say yes here to build spi interface support for Analog Devices: + ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC). + + To compile this driver as a module, choose M here: the + module will be called ad7606_spi. + config AD7766 tristate "Analog Devices AD7766/AD7767 ADC driver" depends on SPI_MASTER @@ -81,6 +108,19 @@ config AD7766 To compile this driver as a module, choose M here: the module will be called ad7766. +config AD7768_1 + tristate "Analog Devices AD7768-1 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices AD7768-1 SPI + simultaneously sampling sigma-delta analog to digital converter (ADC). + + To compile this driver as a module, choose M here: the module will be + called ad7768-1. + config AD7791 tristate "Analog Devices AD7791 ADC driver" depends on SPI @@ -576,6 +616,16 @@ config NAU7802 To compile this driver as a module, choose M here: the module will be called nau7802. +config NPCM_ADC + tristate "Nuvoton NPCM ADC driver" + depends on ARCH_NPCM || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for Nuvoton NPCM ADC. + + This driver can also be built as a module. If so, the module + will be called npcm_adc. + config PALMAS_GPADC tristate "TI Palmas General Purpose ADC" depends on MFD_PALMAS @@ -908,6 +958,16 @@ config TI_ADS8688 This driver can also be built as a module. If so, the module will be called ti-ads8688. +config TI_ADS124S08 + tristate "Texas Instruments ADS124S08" + depends on SPI && OF + help + If you say yes here you get support for Texas Instruments ADS124S08 + and ADS124S06 ADC chips + + This driver can also be built as a module. If so, the module will be + called ti-ads124s08. + config TI_AM335X_ADC tristate "TI's AM335X ADC driver" depends on MFD_TI_AM335X_TSCADC && HAS_DMA diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 07df37f621bd..79d6511f0af3 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -11,7 +11,11 @@ obj-$(CONFIG_AD7291) += ad7291.o obj-$(CONFIG_AD7298) += ad7298.o obj-$(CONFIG_AD7923) += ad7923.o obj-$(CONFIG_AD7476) += ad7476.o +obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o +obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o +obj-$(CONFIG_AD7606) += ad7606.o obj-$(CONFIG_AD7766) += ad7766.o +obj-$(CONFIG_AD7768_1) += ad7768-1.o obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7887) += ad7887.o @@ -55,6 +59,7 @@ obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_MESON_SARADC) += meson_saradc.o obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o obj-$(CONFIG_NAU7802) += nau7802.o +obj-$(CONFIG_NPCM_ADC) += npcm_adc.o obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o @@ -81,6 +86,7 @@ obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o +obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o diff --git a/drivers/staging/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index 7308fa8fbb4c..ebb8de03bbce 100644 --- a/drivers/staging/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -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); - if (ret) - goto error_disable_reg; - - ret = iio_triggered_buffer_setup(indio_dev, &ad7606_trigger_handler, - NULL, NULL); - if (ret) - goto error_free_irq; + st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", + indio_dev->name, indio_dev->id); + if (!st->trig) + return -ENOMEM; - ret = iio_device_register(indio_dev); + 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_unregister_ring; + return ret; - dev_set_drvdata(dev, indio_dev); + indio_dev->trig = iio_trigger_get(st->trig); - return 0; -error_unregister_ring: - iio_triggered_buffer_cleanup(indio_dev); + ret = devm_request_threaded_irq(dev, irq, + NULL, + &ad7606_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + name, indio_dev); + if (ret) + return ret; -error_free_irq: - free_irq(irq, indio_dev); + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &ad7606_trigger_handler, + &ad7606_buffer_ops); + if (ret) + return ret; -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) diff --git a/drivers/staging/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h index 86188054b60b..5d12410f68e1 100644 --- a/drivers/staging/iio/adc/ad7606.h +++ b/drivers/iio/adc/ad7606.h @@ -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, diff --git a/drivers/staging/iio/adc/ad7606_par.c b/drivers/iio/adc/ad7606_par.c index 8bd86e727b02..1b08028facde 100644 --- a/drivers/staging/iio/adc/ad7606_par.c +++ b/drivers/iio/adc/ad7606_par.c @@ -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>"); diff --git a/drivers/staging/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c index b76ca5a8c059..4fd0ec36a086 100644 --- a/drivers/staging/iio/adc/ad7606_spi.c +++ b/drivers/iio/adc/ad7606_spi.c @@ -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); diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c new file mode 100644 index 000000000000..78449e90b2f5 --- /dev/null +++ b/drivers/iio/adc/ad7768-1.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Analog Devices AD7768-1 SPI ADC driver + * + * Copyright 2017 Analog Devices Inc. + */ +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/sysfs.h> +#include <linux/spi/spi.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> + +/* AD7768 registers definition */ +#define AD7768_REG_CHIP_TYPE 0x3 +#define AD7768_REG_PROD_ID_L 0x4 +#define AD7768_REG_PROD_ID_H 0x5 +#define AD7768_REG_CHIP_GRADE 0x6 +#define AD7768_REG_SCRATCH_PAD 0x0A +#define AD7768_REG_VENDOR_L 0x0C +#define AD7768_REG_VENDOR_H 0x0D +#define AD7768_REG_INTERFACE_FORMAT 0x14 +#define AD7768_REG_POWER_CLOCK 0x15 +#define AD7768_REG_ANALOG 0x16 +#define AD7768_REG_ANALOG2 0x17 +#define AD7768_REG_CONVERSION 0x18 +#define AD7768_REG_DIGITAL_FILTER 0x19 +#define AD7768_REG_SINC3_DEC_RATE_MSB 0x1A +#define AD7768_REG_SINC3_DEC_RATE_LSB 0x1B +#define AD7768_REG_DUTY_CYCLE_RATIO 0x1C +#define AD7768_REG_SYNC_RESET 0x1D +#define AD7768_REG_GPIO_CONTROL 0x1E +#define AD7768_REG_GPIO_WRITE 0x1F +#define AD7768_REG_GPIO_READ 0x20 +#define AD7768_REG_OFFSET_HI 0x21 +#define AD7768_REG_OFFSET_MID 0x22 +#define AD7768_REG_OFFSET_LO 0x23 +#define AD7768_REG_GAIN_HI 0x24 +#define AD7768_REG_GAIN_MID 0x25 +#define AD7768_REG_GAIN_LO 0x26 +#define AD7768_REG_SPI_DIAG_ENABLE 0x28 +#define AD7768_REG_ADC_DIAG_ENABLE 0x29 +#define AD7768_REG_DIG_DIAG_ENABLE 0x2A +#define AD7768_REG_ADC_DATA 0x2C +#define AD7768_REG_MASTER_STATUS 0x2D +#define AD7768_REG_SPI_DIAG_STATUS 0x2E +#define AD7768_REG_ADC_DIAG_STATUS 0x2F +#define AD7768_REG_DIG_DIAG_STATUS 0x30 +#define AD7768_REG_MCLK_COUNTER 0x31 + +/* AD7768_REG_CONVERSION */ +#define AD7768_CONV_MODE_MSK GENMASK(2, 0) +#define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x) + +#define AD7768_RD_FLAG_MSK(x) (BIT(6) | ((x) & 0x3F)) +#define AD7768_WR_FLAG_MSK(x) ((x) & 0x3F) + +enum ad7768_conv_mode { + AD7768_CONTINUOUS, + AD7768_ONE_SHOT, + AD7768_SINGLE, + AD7768_PERIODIC, + AD7768_STANDBY +}; + +enum ad7768_pwrmode { + AD7768_ECO_MODE = 0, + AD7768_MED_MODE = 2, + AD7768_FAST_MODE = 3 +}; + +static const struct iio_chan_spec ad7768_channels[] = { + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .indexed = 1, + .channel = 0, + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .shift = 8, + .endianness = IIO_BE, + }, + }, +}; + +struct ad7768_state { + struct spi_device *spi; + struct regulator *vref; + struct mutex lock; + struct completion completion; + struct iio_trigger *trig; + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + union { + __be32 d32; + u8 d8[2]; + } data ____cacheline_aligned; +}; + +static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int addr, + unsigned int len) +{ + unsigned int shift; + int ret; + + shift = 32 - (8 * len); + st->data.d8[0] = AD7768_RD_FLAG_MSK(addr); + + ret = spi_write_then_read(st->spi, st->data.d8, 1, + &st->data.d32, len); + if (ret < 0) + return ret; + + return (be32_to_cpu(st->data.d32) >> shift); +} + +static int ad7768_spi_reg_write(struct ad7768_state *st, + unsigned int addr, + unsigned int val) +{ + st->data.d8[0] = AD7768_WR_FLAG_MSK(addr); + st->data.d8[1] = val & 0xFF; + + return spi_write(st->spi, st->data.d8, 2); +} + +static int ad7768_set_mode(struct ad7768_state *st, + enum ad7768_conv_mode mode) +{ + int regval; + + regval = ad7768_spi_reg_read(st, AD7768_REG_CONVERSION, 1); + if (regval < 0) + return regval; + + regval &= ~AD7768_CONV_MODE_MSK; + regval |= AD7768_CONV_MODE(mode); + + return ad7768_spi_reg_write(st, AD7768_REG_CONVERSION, regval); +} + +static int ad7768_scan_direct(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int readval, ret; + + reinit_completion(&st->completion); + + ret = ad7768_set_mode(st, AD7768_ONE_SHOT); + if (ret < 0) + return ret; + + ret = wait_for_completion_timeout(&st->completion, + msecs_to_jiffies(1000)); + if (!ret) + return -ETIMEDOUT; + + readval = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3); + if (readval < 0) + return readval; + /* + * Any SPI configuration of the AD7768-1 can only be + * performed in continuous conversion mode. + */ + ret = ad7768_set_mode(st, AD7768_CONTINUOUS); + if (ret < 0) + return ret; + + return readval; +} + +static int ad7768_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int writeval, + unsigned int *readval) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&st->lock); + if (readval) { + ret = ad7768_spi_reg_read(st, reg, 1); + if (ret < 0) + goto err_unlock; + *readval = ret; + ret = 0; + } else { + ret = ad7768_spi_reg_write(st, reg, writeval); + } +err_unlock: + mutex_unlock(&st->lock); + + return ret; +} + +static int ad7768_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int scale_uv, ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = ad7768_scan_direct(indio_dev); + if (ret >= 0) + *val = ret; + + iio_device_release_direct_mode(indio_dev); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + scale_uv = regulator_get_voltage(st->vref); + if (scale_uv < 0) + return scale_uv; + + *val = (scale_uv * 2) / 1000; + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + } + + return -EINVAL; +} + +static const struct iio_info ad7768_info = { + .read_raw = &ad7768_read_raw, + .debugfs_reg_access = &ad7768_reg_access, +}; + +static int ad7768_setup(struct ad7768_state *st) +{ + int ret; + + /* + * Two writes to the SPI_RESET[1:0] bits are required to initiate + * a software reset. The bits must first be set to 11, and then + * to 10. When the sequence is detected, the reset occurs. + * See the datasheet, page 70. + */ + ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x3); + if (ret) + return ret; + + ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x2); + if (ret) + return ret; + + /* Set power mode to fast */ + return ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK, + AD7768_FAST_MODE); +} + +static irqreturn_t ad7768_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + mutex_lock(&st->lock); + + ret = spi_read(st->spi, &st->data.d32, 3); + if (ret < 0) + goto err_unlock; + + iio_push_to_buffers_with_timestamp(indio_dev, &st->data.d32, + iio_get_time_ns(indio_dev)); + + iio_trigger_notify_done(indio_dev->trig); +err_unlock: + mutex_unlock(&st->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t ad7768_interrupt(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct ad7768_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll(st->trig); + else + complete(&st->completion); + + return IRQ_HANDLED; +}; + +static int ad7768_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + + iio_triggered_buffer_postenable(indio_dev); + /* + * Write a 1 to the LSB of the INTERFACE_FORMAT register to enter + * continuous read mode. Subsequent data reads do not require an + * initial 8-bit write to query the ADC_DATA register. + */ + return ad7768_spi_reg_write(st, AD7768_REG_INTERFACE_FORMAT, 0x01); +} + +static int ad7768_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad7768_state *st = iio_priv(indio_dev); + int ret; + + /* + * To exit continuous read mode, perform a single read of the ADC_DATA + * reg (0x2C), which allows further configuration of the device. + */ + ret = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3); + if (ret < 0) + return ret; + + return iio_triggered_buffer_predisable(indio_dev); +} + +static const struct iio_buffer_setup_ops ad7768_buffer_ops = { + .postenable = &ad7768_buffer_postenable, + .predisable = &ad7768_buffer_predisable, +}; + +static const struct iio_trigger_ops ad7768_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static void ad7768_regulator_disable(void *data) +{ + struct ad7768_state *st = data; + + regulator_disable(st->vref); +} + +static int ad7768_probe(struct spi_device *spi) +{ + struct ad7768_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + + st->vref = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(st->vref)) + return PTR_ERR(st->vref); + + ret = regulator_enable(st->vref); + if (ret) { + dev_err(&spi->dev, "Failed to enable specified vref supply\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, ad7768_regulator_disable, st); + if (ret) + return ret; + + spi_set_drvdata(spi, indio_dev); + mutex_init(&st->lock); + + indio_dev->channels = ad7768_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7768_channels); + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->info = &ad7768_info; + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED; + + ret = ad7768_setup(st); + if (ret < 0) { + dev_err(&spi->dev, "AD7768 setup failed\n"); + return ret; + } + + st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", + indio_dev->name, indio_dev->id); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad7768_trigger_ops; + st->trig->dev.parent = &spi->dev; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(&spi->dev, st->trig); + if (ret) + return ret; + + indio_dev->trig = iio_trigger_get(st->trig); + + init_completion(&st->completion); + + ret = devm_request_irq(&spi->dev, spi->irq, + &ad7768_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + &iio_pollfunc_store_time, + &ad7768_trigger_handler, + &ad7768_buffer_ops); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad7768_id_table[] = { + { "ad7768-1", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7768_id_table); + +static const struct of_device_id ad7768_of_match[] = { + { .compatible = "adi,ad7768-1" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ad7768_of_match); + +static struct spi_driver ad7768_driver = { + .driver = { + .name = "ad7768-1", + .of_match_table = ad7768_of_match, + }, + .probe = ad7768_probe, + .id_table = ad7768_id_table, +}; +module_spi_driver(ad7768_driver); + +MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7768-1 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c index 729becb2d3d9..f8600fbcdfe3 100644 --- a/drivers/iio/adc/meson_saradc.c +++ b/drivers/iio/adc/meson_saradc.c @@ -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 = { diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c new file mode 100644 index 000000000000..9e25bbec9c70 --- /dev/null +++ b/drivers/iio/adc/npcm_adc.c @@ -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"); diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c new file mode 100644 index 000000000000..c2cf58908fc8 --- /dev/null +++ b/drivers/iio/adc/ti-ads124s08.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0 +/* TI ADS124S0X chip family driver + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + */ + +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/slab.h> +#include <linux/sysfs.h> + +#include <linux/gpio/consumer.h> +#include <linux/spi/spi.h> + +#include <linux/iio/iio.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/sysfs.h> + +/* Commands */ +#define ADS124S08_CMD_NOP 0x00 +#define ADS124S08_CMD_WAKEUP 0x02 +#define ADS124S08_CMD_PWRDWN 0x04 +#define ADS124S08_CMD_RESET 0x06 +#define ADS124S08_CMD_START 0x08 +#define ADS124S08_CMD_STOP 0x0a +#define ADS124S08_CMD_SYOCAL 0x16 +#define ADS124S08_CMD_SYGCAL 0x17 +#define ADS124S08_CMD_SFOCAL 0x19 +#define ADS124S08_CMD_RDATA 0x12 +#define ADS124S08_CMD_RREG 0x20 +#define ADS124S08_CMD_WREG 0x40 + +/* Registers */ +#define ADS124S08_ID_REG 0x00 +#define ADS124S08_STATUS 0x01 +#define ADS124S08_INPUT_MUX 0x02 +#define ADS124S08_PGA 0x03 +#define ADS124S08_DATA_RATE 0x04 +#define ADS124S08_REF 0x05 +#define ADS124S08_IDACMAG 0x06 +#define ADS124S08_IDACMUX 0x07 +#define ADS124S08_VBIAS 0x08 +#define ADS124S08_SYS 0x09 +#define ADS124S08_OFCAL0 0x0a +#define ADS124S08_OFCAL1 0x0b +#define ADS124S08_OFCAL2 0x0c +#define ADS124S08_FSCAL0 0x0d +#define ADS124S08_FSCAL1 0x0e +#define ADS124S08_FSCAL2 0x0f +#define ADS124S08_GPIODAT 0x10 +#define ADS124S08_GPIOCON 0x11 + +/* ADS124S0x common channels */ +#define ADS124S08_AIN0 0x00 +#define ADS124S08_AIN1 0x01 +#define ADS124S08_AIN2 0x02 +#define ADS124S08_AIN3 0x03 +#define ADS124S08_AIN4 0x04 +#define ADS124S08_AIN5 0x05 +#define ADS124S08_AINCOM 0x0c +/* ADS124S08 only channels */ +#define ADS124S08_AIN6 0x06 +#define ADS124S08_AIN7 0x07 +#define ADS124S08_AIN8 0x08 +#define ADS124S08_AIN9 0x09 +#define ADS124S08_AIN10 0x0a +#define ADS124S08_AIN11 0x0b +#define ADS124S08_MAX_CHANNELS 12 + +#define ADS124S08_POS_MUX_SHIFT 0x04 +#define ADS124S08_INT_REF 0x09 + +#define ADS124S08_START_REG_MASK 0x1f +#define ADS124S08_NUM_BYTES_MASK 0x1f + +#define ADS124S08_START_CONV 0x01 +#define ADS124S08_STOP_CONV 0x00 + +enum ads124s_id { + ADS124S08_ID, + ADS124S06_ID, +}; + +struct ads124s_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct ads124s_private { + const struct ads124s_chip_info *chip_info; + struct gpio_desc *reset_gpio; + struct spi_device *spi; + struct mutex lock; + u8 data[5] ____cacheline_aligned; +}; + +#define ADS124S08_CHAN(index) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 32, \ + .storagebits = 32, \ + }, \ +} + +static const struct iio_chan_spec ads124s06_channels[] = { + ADS124S08_CHAN(0), + ADS124S08_CHAN(1), + ADS124S08_CHAN(2), + ADS124S08_CHAN(3), + ADS124S08_CHAN(4), + ADS124S08_CHAN(5), +}; + +static const struct iio_chan_spec ads124s08_channels[] = { + ADS124S08_CHAN(0), + ADS124S08_CHAN(1), + ADS124S08_CHAN(2), + ADS124S08_CHAN(3), + ADS124S08_CHAN(4), + ADS124S08_CHAN(5), + ADS124S08_CHAN(6), + ADS124S08_CHAN(7), + ADS124S08_CHAN(8), + ADS124S08_CHAN(9), + ADS124S08_CHAN(10), + ADS124S08_CHAN(11), +}; + +static const struct ads124s_chip_info ads124s_chip_info_tbl[] = { + [ADS124S08_ID] = { + .channels = ads124s08_channels, + .num_channels = ARRAY_SIZE(ads124s08_channels), + }, + [ADS124S06_ID] = { + .channels = ads124s06_channels, + .num_channels = ARRAY_SIZE(ads124s06_channels), + }, +}; + +static int ads124s_write_cmd(struct iio_dev *indio_dev, u8 command) +{ + struct ads124s_private *priv = iio_priv(indio_dev); + + priv->data[0] = command; + + return spi_write(priv->spi, &priv->data[0], 1); +} + +static int ads124s_write_reg(struct iio_dev *indio_dev, u8 reg, u8 data) +{ + struct ads124s_private *priv = iio_priv(indio_dev); + + priv->data[0] = ADS124S08_CMD_WREG | reg; + priv->data[1] = 0x0; + priv->data[2] = data; + + return spi_write(priv->spi, &priv->data[0], 3); +} + +static int ads124s_reset(struct iio_dev *indio_dev) +{ + struct ads124s_private *priv = iio_priv(indio_dev); + + if (priv->reset_gpio) { + gpiod_set_value(priv->reset_gpio, 0); + udelay(200); + gpiod_set_value(priv->reset_gpio, 1); + } else { + return ads124s_write_cmd(indio_dev, ADS124S08_CMD_RESET); + } + + return 0; +}; + +static int ads124s_read(struct iio_dev *indio_dev, unsigned int chan) +{ + struct ads124s_private *priv = iio_priv(indio_dev); + int ret; + u32 tmp; + struct spi_transfer t[] = { + { + .tx_buf = &priv->data[0], + .len = 4, + .cs_change = 1, + }, { + .tx_buf = &priv->data[1], + .rx_buf = &priv->data[1], + .len = 4, + }, + }; + + priv->data[0] = ADS124S08_CMD_RDATA; + memset(&priv->data[1], ADS124S08_CMD_NOP, sizeof(priv->data)); + + ret = spi_sync_transfer(priv->spi, t, ARRAY_SIZE(t)); + if (ret < 0) + return ret; + + tmp = priv->data[2] << 16 | priv->data[3] << 8 | priv->data[4]; + + return tmp; +} + +static int ads124s_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + struct ads124s_private *priv = iio_priv(indio_dev); + int ret; + + mutex_lock(&priv->lock); + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX, + chan->channel); + if (ret) { + dev_err(&priv->spi->dev, "Set ADC CH failed\n"); + goto out; + } + + ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV); + if (ret) { + dev_err(&priv->spi->dev, "Start converions failed\n"); + goto out; + } + + ret = ads124s_read(indio_dev, chan->channel); + if (ret < 0) { + dev_err(&priv->spi->dev, "Read ADC failed\n"); + goto out; + } + + *val = ret; + + ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV); + if (ret) { + dev_err(&priv->spi->dev, "Stop converions failed\n"); + goto out; + } + + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } +out: + mutex_unlock(&priv->lock); + return ret; +} + +static const struct iio_info ads124s_info = { + .read_raw = &ads124s_read_raw, +}; + +static irqreturn_t ads124s_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ads124s_private *priv = iio_priv(indio_dev); + u32 buffer[ADS124S08_MAX_CHANNELS + sizeof(s64)/sizeof(u16)]; + int scan_index, j = 0; + int ret; + + for_each_set_bit(scan_index, indio_dev->active_scan_mask, + indio_dev->masklength) { + ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX, + scan_index); + if (ret) + dev_err(&priv->spi->dev, "Set ADC CH failed\n"); + + ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV); + if (ret) + dev_err(&priv->spi->dev, "Start ADC converions failed\n"); + + buffer[j] = ads124s_read(indio_dev, scan_index); + ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV); + if (ret) + dev_err(&priv->spi->dev, "Stop ADC converions failed\n"); + + j++; + } + + iio_push_to_buffers_with_timestamp(indio_dev, buffer, + pf->timestamp); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ads124s_probe(struct spi_device *spi) +{ + struct ads124s_private *ads124s_priv; + struct iio_dev *indio_dev; + const struct spi_device_id *spi_id = spi_get_device_id(spi); + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ads124s_priv)); + if (indio_dev == NULL) + return -ENOMEM; + + ads124s_priv = iio_priv(indio_dev); + + ads124s_priv->reset_gpio = devm_gpiod_get_optional(&spi->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(ads124s_priv->reset_gpio)) + dev_info(&spi->dev, "Reset GPIO not defined\n"); + + ads124s_priv->chip_info = &ads124s_chip_info_tbl[spi_id->driver_data]; + + spi_set_drvdata(spi, indio_dev); + + ads124s_priv->spi = spi; + + indio_dev->name = spi_id->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ads124s_priv->chip_info->channels; + indio_dev->num_channels = ads124s_priv->chip_info->num_channels; + indio_dev->info = &ads124s_info; + + mutex_init(&ads124s_priv->lock); + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + ads124s_trigger_handler, NULL); + if (ret) { + dev_err(&spi->dev, "iio triggered buffer setup failed\n"); + return ret; + } + + ads124s_reset(indio_dev); + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ads124s_id[] = { + { "ads124s06", ADS124S06_ID }, + { "ads124s08", ADS124S08_ID }, + { } +}; +MODULE_DEVICE_TABLE(spi, ads124s_id); + +static const struct of_device_id ads124s_of_table[] = { + { .compatible = "ti,ads124s06" }, + { .compatible = "ti,ads124s08" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ads124s_of_table); + +static struct spi_driver ads124s_driver = { + .driver = { + .name = "ads124s08", + .of_match_table = ads124s_of_table, + }, + .probe = ads124s_probe, + .id_table = ads124s_id, +}; +module_spi_driver(ads124s_driver); + +MODULE_AUTHOR("Dan Murphy <dmuprhy@ti.com>"); +MODULE_DESCRIPTION("TI TI_ADS12S0X ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 3f6be5ac049a..b13c61539d46 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -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 */ diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index b8e005be4f87..57832b4360e9 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -61,6 +61,17 @@ config IAQCORE iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds) sensors +config SPS30 + tristate "SPS30 particulate matter sensor" + depends on I2C + select CRC8 + help + Say Y here to build support for the Sensirion SPS30 particulate + matter sensor. + + To compile this driver as a module, choose M here: the module will + be called sps30. + config VZ89X tristate "SGX Sensortech MiCS VZ89X VOC sensor" depends on I2C diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile index 2f4c4ba4d781..65bf0f89c0e4 100644 --- a/drivers/iio/chemical/Makefile +++ b/drivers/iio/chemical/Makefile @@ -9,4 +9,6 @@ obj-$(CONFIG_BME680_I2C) += bme680_i2c.o obj-$(CONFIG_BME680_SPI) += bme680_spi.o obj-$(CONFIG_CCS811) += ccs811.o obj-$(CONFIG_IAQCORE) += ams-iaq-core.o +obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o +obj-$(CONFIG_SPS30) += sps30.o obj-$(CONFIG_VZ89X) += vz89x.o diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c index 06d4be539d2e..b2f805b6b36a 100644 --- a/drivers/iio/chemical/bme680_i2c.c +++ b/drivers/iio/chemical/bme680_i2c.c @@ -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, diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c index c9fb05e8d0b9..d0b7bdd3f066 100644 --- a/drivers/iio/chemical/bme680_spi.c +++ b/drivers/iio/chemical/bme680_spi.c @@ -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, diff --git a/drivers/iio/chemical/sgp30.c b/drivers/iio/chemical/sgp30.c new file mode 100644 index 000000000000..8cc8fe5e356d --- /dev/null +++ b/drivers/iio/chemical/sgp30.c @@ -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"); diff --git a/drivers/iio/chemical/sps30.c b/drivers/iio/chemical/sps30.c new file mode 100644 index 000000000000..e03a28a67146 --- /dev/null +++ b/drivers/iio/chemical/sps30.c @@ -0,0 +1,545 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sensirion SPS30 particulate matter sensor driver + * + * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> + * + * I2C slave address: 0x69 + */ + +#include <asm/unaligned.h> +#include <linux/crc8.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/kernel.h> +#include <linux/module.h> + +#define SPS30_CRC8_POLYNOMIAL 0x31 +/* max number of bytes needed to store PM measurements or serial string */ +#define SPS30_MAX_READ_SIZE 48 +/* sensor measures reliably up to 3000 ug / m3 */ +#define SPS30_MAX_PM 3000 +/* minimum and maximum self cleaning periods in seconds */ +#define SPS30_AUTO_CLEANING_PERIOD_MIN 0 +#define SPS30_AUTO_CLEANING_PERIOD_MAX 604800 + +/* SPS30 commands */ +#define SPS30_START_MEAS 0x0010 +#define SPS30_STOP_MEAS 0x0104 +#define SPS30_RESET 0xd304 +#define SPS30_READ_DATA_READY_FLAG 0x0202 +#define SPS30_READ_DATA 0x0300 +#define SPS30_READ_SERIAL 0xd033 +#define SPS30_START_FAN_CLEANING 0x5607 +#define SPS30_AUTO_CLEANING_PERIOD 0x8004 +/* not a sensor command per se, used only to distinguish write from read */ +#define SPS30_READ_AUTO_CLEANING_PERIOD 0x8005 + +enum { + PM1, + PM2P5, + PM4, + PM10, +}; + +enum { + RESET, + MEASURING, +}; + +struct sps30_state { + struct i2c_client *client; + /* + * Guards against concurrent access to sensor registers. + * Must be held whenever sequence of commands is to be executed. + */ + struct mutex lock; + int state; +}; + +DECLARE_CRC8_TABLE(sps30_crc8_table); + +static int sps30_write_then_read(struct sps30_state *state, u8 *txbuf, + int txsize, u8 *rxbuf, int rxsize) +{ + int ret; + + /* + * Sensor does not support repeated start so instead of + * sending two i2c messages in a row we just send one by one. + */ + ret = i2c_master_send(state->client, txbuf, txsize); + if (ret != txsize) + return ret < 0 ? ret : -EIO; + + if (!rxbuf) + return 0; + + ret = i2c_master_recv(state->client, rxbuf, rxsize); + if (ret != rxsize) + return ret < 0 ? ret : -EIO; + + return 0; +} + +static int sps30_do_cmd(struct sps30_state *state, u16 cmd, u8 *data, int size) +{ + /* + * Internally sensor stores measurements in a following manner: + * + * PM1: upper two bytes, crc8, lower two bytes, crc8 + * PM2P5: upper two bytes, crc8, lower two bytes, crc8 + * PM4: upper two bytes, crc8, lower two bytes, crc8 + * PM10: upper two bytes, crc8, lower two bytes, crc8 + * + * What follows next are number concentration measurements and + * typical particle size measurement which we omit. + */ + u8 buf[SPS30_MAX_READ_SIZE] = { cmd >> 8, cmd }; + int i, ret = 0; + + switch (cmd) { + case SPS30_START_MEAS: + buf[2] = 0x03; + buf[3] = 0x00; + buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE); + ret = sps30_write_then_read(state, buf, 5, NULL, 0); + break; + case SPS30_STOP_MEAS: + case SPS30_RESET: + case SPS30_START_FAN_CLEANING: + ret = sps30_write_then_read(state, buf, 2, NULL, 0); + break; + case SPS30_READ_AUTO_CLEANING_PERIOD: + buf[0] = SPS30_AUTO_CLEANING_PERIOD >> 8; + buf[1] = (u8)SPS30_AUTO_CLEANING_PERIOD; + case SPS30_READ_DATA_READY_FLAG: + case SPS30_READ_DATA: + case SPS30_READ_SERIAL: + /* every two data bytes are checksummed */ + size += size / 2; + ret = sps30_write_then_read(state, buf, 2, buf, size); + break; + case SPS30_AUTO_CLEANING_PERIOD: + buf[2] = data[0]; + buf[3] = data[1]; + buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE); + buf[5] = data[2]; + buf[6] = data[3]; + buf[7] = crc8(sps30_crc8_table, &buf[5], 2, CRC8_INIT_VALUE); + ret = sps30_write_then_read(state, buf, 8, NULL, 0); + break; + } + + if (ret) + return ret; + + /* validate received data and strip off crc bytes */ + for (i = 0; i < size; i += 3) { + u8 crc = crc8(sps30_crc8_table, &buf[i], 2, CRC8_INIT_VALUE); + + if (crc != buf[i + 2]) { + dev_err(&state->client->dev, + "data integrity check failed\n"); + return -EIO; + } + + *data++ = buf[i]; + *data++ = buf[i + 1]; + } + + return 0; +} + +static s32 sps30_float_to_int_clamped(const u8 *fp) +{ + int val = get_unaligned_be32(fp); + int mantissa = val & GENMASK(22, 0); + /* this is fine since passed float is always non-negative */ + int exp = val >> 23; + int fraction, shift; + + /* special case 0 */ + if (!exp && !mantissa) + return 0; + + exp -= 127; + if (exp < 0) { + /* return values ranging from 1 to 99 */ + return ((((1 << 23) + mantissa) * 100) >> 23) >> (-exp); + } + + /* return values ranging from 100 to 300000 */ + shift = 23 - exp; + val = (1 << exp) + (mantissa >> shift); + if (val >= SPS30_MAX_PM) + return SPS30_MAX_PM * 100; + + fraction = mantissa & GENMASK(shift - 1, 0); + + return val * 100 + ((fraction * 100) >> shift); +} + +static int sps30_do_meas(struct sps30_state *state, s32 *data, int size) +{ + int i, ret, tries = 5; + u8 tmp[16]; + + if (state->state == RESET) { + ret = sps30_do_cmd(state, SPS30_START_MEAS, NULL, 0); + if (ret) + return ret; + + state->state = MEASURING; + } + + while (tries--) { + ret = sps30_do_cmd(state, SPS30_READ_DATA_READY_FLAG, tmp, 2); + if (ret) + return -EIO; + + /* new measurements ready to be read */ + if (tmp[1] == 1) + break; + + msleep_interruptible(300); + } + + if (!tries) + return -ETIMEDOUT; + + ret = sps30_do_cmd(state, SPS30_READ_DATA, tmp, sizeof(int) * size); + if (ret) + return ret; + + for (i = 0; i < size; i++) + data[i] = sps30_float_to_int_clamped(&tmp[4 * i]); + + return 0; +} + +static irqreturn_t sps30_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct sps30_state *state = iio_priv(indio_dev); + int ret; + s32 data[4 + 2]; /* PM1, PM2P5, PM4, PM10, timestamp */ + + mutex_lock(&state->lock); + ret = sps30_do_meas(state, data, 4); + mutex_unlock(&state->lock); + if (ret) + goto err; + + iio_push_to_buffers_with_timestamp(indio_dev, data, + iio_get_time_ns(indio_dev)); +err: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int sps30_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct sps30_state *state = iio_priv(indio_dev); + int data[4], ret = -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_MASSCONCENTRATION: + mutex_lock(&state->lock); + /* read up to the number of bytes actually needed */ + switch (chan->channel2) { + case IIO_MOD_PM1: + ret = sps30_do_meas(state, data, 1); + break; + case IIO_MOD_PM2P5: + ret = sps30_do_meas(state, data, 2); + break; + case IIO_MOD_PM4: + ret = sps30_do_meas(state, data, 3); + break; + case IIO_MOD_PM10: + ret = sps30_do_meas(state, data, 4); + break; + } + mutex_unlock(&state->lock); + if (ret) + return ret; + + *val = data[chan->address] / 100; + *val2 = (data[chan->address] % 100) * 10000; + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_MASSCONCENTRATION: + switch (chan->channel2) { + case IIO_MOD_PM1: + case IIO_MOD_PM2P5: + case IIO_MOD_PM4: + case IIO_MOD_PM10: + *val = 0; + *val2 = 10000; + + return IIO_VAL_INT_PLUS_MICRO; + } + default: + return -EINVAL; + } + } + + return -EINVAL; +} + +static int sps30_do_cmd_reset(struct sps30_state *state) +{ + int ret; + + ret = sps30_do_cmd(state, SPS30_RESET, NULL, 0); + msleep(300); + /* + * Power-on-reset causes sensor to produce some glitch on i2c bus and + * some controllers end up in error state. Recover simply by placing + * some data on the bus, for example STOP_MEAS command, which + * is NOP in this case. + */ + sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0); + state->state = RESET; + + return ret; +} + +static ssize_t start_cleaning_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sps30_state *state = iio_priv(indio_dev); + int val, ret; + + if (kstrtoint(buf, 0, &val) || val != 1) + return -EINVAL; + + mutex_lock(&state->lock); + ret = sps30_do_cmd(state, SPS30_START_FAN_CLEANING, NULL, 0); + mutex_unlock(&state->lock); + if (ret) + return ret; + + return len; +} + +static ssize_t cleaning_period_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sps30_state *state = iio_priv(indio_dev); + u8 tmp[4]; + int ret; + + mutex_lock(&state->lock); + ret = sps30_do_cmd(state, SPS30_READ_AUTO_CLEANING_PERIOD, tmp, 4); + mutex_unlock(&state->lock); + if (ret) + return ret; + + return sprintf(buf, "%d\n", get_unaligned_be32(tmp)); +} + +static ssize_t cleaning_period_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct sps30_state *state = iio_priv(indio_dev); + int val, ret; + u8 tmp[4]; + + if (kstrtoint(buf, 0, &val)) + return -EINVAL; + + if ((val < SPS30_AUTO_CLEANING_PERIOD_MIN) || + (val > SPS30_AUTO_CLEANING_PERIOD_MAX)) + return -EINVAL; + + put_unaligned_be32(val, tmp); + + mutex_lock(&state->lock); + ret = sps30_do_cmd(state, SPS30_AUTO_CLEANING_PERIOD, tmp, 0); + if (ret) { + mutex_unlock(&state->lock); + return ret; + } + + msleep(20); + + /* + * sensor requires reset in order to return up to date self cleaning + * period + */ + ret = sps30_do_cmd_reset(state); + if (ret) + dev_warn(dev, + "period changed but reads will return the old value\n"); + + mutex_unlock(&state->lock); + + return len; +} + +static ssize_t cleaning_period_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "[%d %d %d]\n", + SPS30_AUTO_CLEANING_PERIOD_MIN, 1, + SPS30_AUTO_CLEANING_PERIOD_MAX); +} + +static IIO_DEVICE_ATTR_WO(start_cleaning, 0); +static IIO_DEVICE_ATTR_RW(cleaning_period, 0); +static IIO_DEVICE_ATTR_RO(cleaning_period_available, 0); + +static struct attribute *sps30_attrs[] = { + &iio_dev_attr_start_cleaning.dev_attr.attr, + &iio_dev_attr_cleaning_period.dev_attr.attr, + &iio_dev_attr_cleaning_period_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group sps30_attr_group = { + .attrs = sps30_attrs, +}; + +static const struct iio_info sps30_info = { + .attrs = &sps30_attr_group, + .read_raw = sps30_read_raw, +}; + +#define SPS30_CHAN(_index, _mod) { \ + .type = IIO_MASSCONCENTRATION, \ + .modified = 1, \ + .channel2 = IIO_MOD_ ## _mod, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .address = _mod, \ + .scan_index = _index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 19, \ + .storagebits = 32, \ + .endianness = IIO_CPU, \ + }, \ +} + +static const struct iio_chan_spec sps30_channels[] = { + SPS30_CHAN(0, PM1), + SPS30_CHAN(1, PM2P5), + SPS30_CHAN(2, PM4), + SPS30_CHAN(3, PM10), + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static void sps30_stop_meas(void *data) +{ + struct sps30_state *state = data; + + sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0); +} + +static const unsigned long sps30_scan_masks[] = { 0x0f, 0x00 }; + +static int sps30_probe(struct i2c_client *client) +{ + struct iio_dev *indio_dev; + struct sps30_state *state; + u8 buf[32]; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EOPNOTSUPP; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*state)); + if (!indio_dev) + return -ENOMEM; + + state = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + state->client = client; + state->state = RESET; + indio_dev->dev.parent = &client->dev; + indio_dev->info = &sps30_info; + indio_dev->name = client->name; + indio_dev->channels = sps30_channels; + indio_dev->num_channels = ARRAY_SIZE(sps30_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->available_scan_masks = sps30_scan_masks; + + mutex_init(&state->lock); + crc8_populate_msb(sps30_crc8_table, SPS30_CRC8_POLYNOMIAL); + + ret = sps30_do_cmd_reset(state); + if (ret) { + dev_err(&client->dev, "failed to reset device\n"); + return ret; + } + + ret = sps30_do_cmd(state, SPS30_READ_SERIAL, buf, sizeof(buf)); + if (ret) { + dev_err(&client->dev, "failed to read serial number\n"); + return ret; + } + /* returned serial number is already NUL terminated */ + dev_info(&client->dev, "serial number: %s\n", buf); + + ret = devm_add_action_or_reset(&client->dev, sps30_stop_meas, state); + if (ret) + return ret; + + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, + sps30_trigger_handler, NULL); + if (ret) + return ret; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id sps30_id[] = { + { "sps30" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sps30_id); + +static const struct of_device_id sps30_of_match[] = { + { .compatible = "sensirion,sps30" }, + { } +}; +MODULE_DEVICE_TABLE(of, sps30_of_match); + +static struct i2c_driver sps30_driver = { + .driver = { + .name = "sps30", + .of_match_table = sps30_of_match, + }, + .id_table = sps30_id, + .probe_new = sps30_probe, +}; +module_i2c_driver(sps30_driver); + +MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); +MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 851b61eaf3da..f28daf67db6a 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -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. diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index 665fa6bd9ced..4d857c8da2d2 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * AD5672R, AD5676, AD5676R, AD5681R, AD5682R, AD5683, AD5683R, - * AD5684, AD5684R, AD5685R, AD5686, AD5686R + * AD5672R, AD5674R, AD5676, AD5676R, AD5679R, + * AD5681R, AD5682R, AD5683, AD5683R, AD5684, + * AD5684R, AD5685R, AD5686, AD5686R * Digital to analog converters driver * * Copyright 2018 Analog Devices Inc. @@ -102,8 +103,10 @@ static int ad5686_spi_remove(struct spi_device *spi) static const struct spi_device_id ad5686_spi_id[] = { {"ad5310r", ID_AD5310R}, {"ad5672r", ID_AD5672R}, + {"ad5674r", ID_AD5674R}, {"ad5676", ID_AD5676}, {"ad5676r", ID_AD5676R}, + {"ad5679r", ID_AD5679R}, {"ad5681r", ID_AD5681R}, {"ad5682r", ID_AD5682R}, {"ad5683", ID_AD5683}, diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index a332b93ca2c4..6dd2759b9092 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -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, diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index 19f6917d4738..4c3e171ce0cc 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -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, diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c index f3f94fbdd20a..3f9be69499ec 100644 --- a/drivers/iio/frequency/ad9523.c +++ b/drivers/iio/frequency/ad9523.c @@ -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) diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index 5483b2ea754d..d2fe9dbddda7 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -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. diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 1e428c196a82..650de0fefb7b 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -38,6 +38,29 @@ static const int gyro_scale_6050[] = {133090, 266181, 532362, 1064724}; */ static const int accel_scale[] = {598, 1196, 2392, 4785}; +static const struct inv_mpu6050_reg_map reg_set_icm20602 = { + .sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV, + .lpf = INV_MPU6050_REG_CONFIG, + .accel_lpf = INV_MPU6500_REG_ACCEL_CONFIG_2, + .user_ctrl = INV_MPU6050_REG_USER_CTRL, + .fifo_en = INV_MPU6050_REG_FIFO_EN, + .gyro_config = INV_MPU6050_REG_GYRO_CONFIG, + .accl_config = INV_MPU6050_REG_ACCEL_CONFIG, + .fifo_count_h = INV_MPU6050_REG_FIFO_COUNT_H, + .fifo_r_w = INV_MPU6050_REG_FIFO_R_W, + .raw_gyro = INV_MPU6050_REG_RAW_GYRO, + .raw_accl = INV_MPU6050_REG_RAW_ACCEL, + .temperature = INV_MPU6050_REG_TEMPERATURE, + .int_enable = INV_MPU6050_REG_INT_ENABLE, + .int_status = INV_MPU6050_REG_INT_STATUS, + .pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1, + .pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2, + .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG, + .accl_offset = INV_MPU6500_REG_ACCEL_OFFSET, + .gyro_offset = INV_MPU6050_REG_GYRO_OFFSET, + .i2c_if = INV_ICM20602_REG_I2C_IF, +}; + static const struct inv_mpu6050_reg_map reg_set_6500 = { .sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV, .lpf = INV_MPU6050_REG_CONFIG, @@ -58,6 +81,7 @@ static const struct inv_mpu6050_reg_map reg_set_6500 = { .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG, .accl_offset = INV_MPU6500_REG_ACCEL_OFFSET, .gyro_offset = INV_MPU6050_REG_GYRO_OFFSET, + .i2c_if = 0, }; static const struct inv_mpu6050_reg_map reg_set_6050 = { @@ -78,6 +102,7 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = { .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG, .accl_offset = INV_MPU6050_REG_ACCEL_OFFSET, .gyro_offset = INV_MPU6050_REG_GYRO_OFFSET, + .i2c_if = 0, }; static const struct inv_mpu6050_chip_config chip_config_6050 = { @@ -140,6 +165,12 @@ static const struct inv_mpu6050_hw hw_info[] = { .reg = ®_set_6500, .config = &chip_config_6050, }, + { + .whoami = INV_ICM20602_WHOAMI_VALUE, + .name = "ICM20602", + .reg = ®_set_icm20602, + .config = &chip_config_6050, + }, }; int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index dd758e3d403d..e46eb4ddea21 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -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); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 6bcc11fc1b88..325afd9f5f61 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -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 { diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index 227f50afff22..a112c3f45f74 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -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}, {} }; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c index 8e47dccdd40f..66fbcd94642d 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c @@ -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; diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 4f5cd9f60870..4700fd5d8c90 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -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 */ diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 36f458433480..5190eacfeb0a 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -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 diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 286bf3975372..e40794fbb435 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -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 diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c index b45400f8fef4..846df4dce48c 100644 --- a/drivers/iio/light/isl29018.c +++ b/drivers/iio/light/isl29018.c @@ -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; diff --git a/drivers/iio/light/max44009.c b/drivers/iio/light/max44009.c new file mode 100644 index 000000000000..00ba15499638 --- /dev/null +++ b/drivers/iio/light/max44009.c @@ -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"); diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index f063355480ba..dd990cdb04a8 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -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); diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index eaa7cfcb4c2a..efeb89f3df71 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -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: diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h index e67eb0d971bf..57946605f3ba 100644 --- a/drivers/iio/pressure/st_pressure.h +++ b/drivers/iio/pressure/st_pressure.h @@ -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 diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 4ddb6cf7d401..38dcdb7c000e 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -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, diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 2026a1012012..a60849dd4ea7 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -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); diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index 9a3441b128e7..79a12ed46e54 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -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); diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig index fc23059f1673..7a93d3a5c113 100644 --- a/drivers/staging/iio/adc/Kconfig +++ b/drivers/staging/iio/adc/Kconfig @@ -3,40 +3,6 @@ # menu "Analog to digital converters" -config AD7606 - tristate "Analog Devices AD7606 ADC driver" - depends on GPIOLIB || COMPILE_TEST - depends on HAS_IOMEM - select IIO_BUFFER - select IIO_TRIGGERED_BUFFER - help - Say yes here to build support for Analog Devices: - ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC). - - To compile this driver as a module, choose M here: the - module will be called ad7606. - -config AD7606_IFACE_PARALLEL - tristate "parallel interface support" - depends on AD7606 - help - Say yes here to include parallel interface support on the AD7606 - ADC driver. - - To compile this driver as a module, choose M here: the - module will be called ad7606_parallel. - -config AD7606_IFACE_SPI - tristate "spi interface support" - depends on AD7606 - depends on SPI - help - Say yes here to include parallel interface support on the AD7606 - ADC driver. - - To compile this driver as a module, choose M here: the - module will be called ad7606_spi. - config AD7780 tristate "Analog Devices AD7780 and similar ADCs driver" depends on SPI diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile index ebe83c1ad362..7a421088ff82 100644 --- a/drivers/staging/iio/adc/Makefile +++ b/drivers/staging/iio/adc/Makefile @@ -3,10 +3,6 @@ # Makefile for industrial I/O ADC drivers # -obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o -obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o -obj-$(CONFIG_AD7606) += ad7606.o - obj-$(CONFIG_AD7780) += ad7780.o obj-$(CONFIG_AD7816) += ad7816.o obj-$(CONFIG_AD7192) += ad7192.o diff --git a/drivers/staging/iio/adc/ad7280a.c b/drivers/staging/iio/adc/ad7280a.c index 14f6a3ced060..d9df12665176 100644 --- a/drivers/staging/iio/adc/ad7280a.c +++ b/drivers/staging/iio/adc/ad7280a.c @@ -97,6 +97,10 @@ #define AD7280A_NUM_CH (AD7280A_AUX_ADC_6 - \ AD7280A_CELL_VOLTAGE_1 + 1) +#define AD7280A_CALC_VOLTAGE_CHAN_NUM(d, c) ((d * AD7280A_CELLS_PER_DEV) + c) +#define AD7280A_CALC_TEMP_CHAN_NUM(d, c) ((d * AD7280A_CELLS_PER_DEV) + \ + c - AD7280A_CELLS_PER_DEV) + #define AD7280A_DEVADDR_MASTER 0 #define AD7280A_DEVADDR_ALL 0x1F /* 5-bit device address is sent LSB first */ @@ -496,72 +500,171 @@ static const struct attribute_group ad7280_attrs_group = { .attrs = ad7280_attributes, }; +static void ad7280_voltage_channel_init(struct iio_chan_spec *chan, int i) +{ + chan->type = IIO_VOLTAGE; + chan->differential = 1; + chan->channel = i; + chan->channel2 = chan->channel + 1; +} + +static void ad7280_temp_channel_init(struct iio_chan_spec *chan, int i) +{ + chan->type = IIO_TEMP; + chan->channel = i; +} + +static void ad7280_common_fields_init(struct iio_chan_spec *chan, int addr, + int cnt) +{ + chan->indexed = 1; + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + chan->address = addr; + chan->scan_index = cnt; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 12; + chan->scan_type.storagebits = 32; +} + +static void ad7280_total_voltage_channel_init(struct iio_chan_spec *chan, + int cnt, int dev) +{ + chan->type = IIO_VOLTAGE; + chan->differential = 1; + chan->channel = 0; + chan->channel2 = dev * AD7280A_CELLS_PER_DEV; + chan->address = AD7280A_ALL_CELLS; + chan->indexed = 1; + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); + chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); + chan->scan_index = cnt; + chan->scan_type.sign = 'u'; + chan->scan_type.realbits = 32; + chan->scan_type.storagebits = 32; +} + +static void ad7280_timestamp_channel_init(struct iio_chan_spec *chan, int cnt) +{ + chan->type = IIO_TIMESTAMP; + chan->channel = -1; + chan->scan_index = cnt; + chan->scan_type.sign = 's'; + chan->scan_type.realbits = 64; + chan->scan_type.storagebits = 64; +} + +static void ad7280_init_dev_channels(struct ad7280_state *st, int dev, int *cnt) +{ + int addr, ch, i; + struct iio_chan_spec *chan; + + for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_AUX_ADC_6; ch++) { + chan = &st->channels[*cnt]; + + if (ch < AD7280A_AUX_ADC_1) { + i = AD7280A_CALC_VOLTAGE_CHAN_NUM(dev, ch); + ad7280_voltage_channel_init(chan, i); + } else { + i = AD7280A_CALC_TEMP_CHAN_NUM(dev, ch); + ad7280_temp_channel_init(chan, i); + } + + addr = ad7280a_devaddr(dev) << 8 | ch; + ad7280_common_fields_init(chan, addr, *cnt); + + (*cnt)++; + } +} + static int ad7280_channel_init(struct ad7280_state *st) { - int dev, ch, cnt; + int dev, cnt = 0; st->channels = devm_kcalloc(&st->spi->dev, (st->slave_num + 1) * 12 + 2, sizeof(*st->channels), GFP_KERNEL); if (!st->channels) return -ENOMEM; - for (dev = 0, cnt = 0; dev <= st->slave_num; dev++) - for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_AUX_ADC_6; - ch++, cnt++) { - if (ch < AD7280A_AUX_ADC_1) { - st->channels[cnt].type = IIO_VOLTAGE; - st->channels[cnt].differential = 1; - st->channels[cnt].channel = (dev * 6) + ch; - st->channels[cnt].channel2 = - st->channels[cnt].channel + 1; - } else { - st->channels[cnt].type = IIO_TEMP; - st->channels[cnt].channel = (dev * 6) + ch - 6; - } - st->channels[cnt].indexed = 1; - st->channels[cnt].info_mask_separate = - BIT(IIO_CHAN_INFO_RAW); - st->channels[cnt].info_mask_shared_by_type = - BIT(IIO_CHAN_INFO_SCALE); - st->channels[cnt].address = - ad7280a_devaddr(dev) << 8 | ch; - st->channels[cnt].scan_index = cnt; - st->channels[cnt].scan_type.sign = 'u'; - st->channels[cnt].scan_type.realbits = 12; - st->channels[cnt].scan_type.storagebits = 32; - st->channels[cnt].scan_type.shift = 0; - } + for (dev = 0; dev <= st->slave_num; dev++) + ad7280_init_dev_channels(st, dev, &cnt); - st->channels[cnt].type = IIO_VOLTAGE; - st->channels[cnt].differential = 1; - st->channels[cnt].channel = 0; - st->channels[cnt].channel2 = dev * 6; - st->channels[cnt].address = AD7280A_ALL_CELLS; - st->channels[cnt].indexed = 1; - st->channels[cnt].info_mask_separate = BIT(IIO_CHAN_INFO_RAW); - st->channels[cnt].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); - st->channels[cnt].scan_index = cnt; - st->channels[cnt].scan_type.sign = 'u'; - st->channels[cnt].scan_type.realbits = 32; - st->channels[cnt].scan_type.storagebits = 32; - st->channels[cnt].scan_type.shift = 0; + ad7280_total_voltage_channel_init(&st->channels[cnt], cnt, dev); cnt++; - st->channels[cnt].type = IIO_TIMESTAMP; - st->channels[cnt].channel = -1; - st->channels[cnt].scan_index = cnt; - st->channels[cnt].scan_type.sign = 's'; - st->channels[cnt].scan_type.realbits = 64; - st->channels[cnt].scan_type.storagebits = 64; - st->channels[cnt].scan_type.shift = 0; + ad7280_timestamp_channel_init(&st->channels[cnt], cnt); return cnt + 1; } -static int ad7280_attr_init(struct ad7280_state *st) +static int ad7280_balance_switch_attr_init(struct iio_dev_attr *attr, + struct device *dev, int addr, int i) { - int dev, ch, cnt; - unsigned int index; + attr->address = addr; + attr->dev_attr.attr.mode = 0644; + attr->dev_attr.show = ad7280_show_balance_sw; + attr->dev_attr.store = ad7280_store_balance_sw; + attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, + "in%d-in%d_balance_switch_en", + i, i + 1); + if (!attr->dev_attr.attr.name) + return -ENOMEM; + + return 0; +} + +static int ad7280_balance_timer_attr_init(struct iio_dev_attr *attr, + struct device *dev, int addr, int i) +{ + attr->address = addr; + attr->dev_attr.attr.mode = 0644; + attr->dev_attr.show = ad7280_show_balance_timer; + attr->dev_attr.store = ad7280_store_balance_timer; + attr->dev_attr.attr.name = devm_kasprintf(dev, GFP_KERNEL, + "in%d-in%d_balance_timer", + i, i + 1); + if (!attr->dev_attr.attr.name) + return -ENOMEM; + + return 0; +} + +static int ad7280_init_dev_attrs(struct ad7280_state *st, int dev, int *cnt) +{ + int addr, ch, i, ret; struct iio_dev_attr *iio_attr; + struct device *sdev = &st->spi->dev; + + for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_CELL_VOLTAGE_6; ch++) { + iio_attr = &st->iio_attr[*cnt]; + addr = ad7280a_devaddr(dev) << 8 | ch; + i = dev * AD7280A_CELLS_PER_DEV + ch; + + ret = ad7280_balance_switch_attr_init(iio_attr, sdev, addr, i); + if (ret < 0) + return ret; + + ad7280_attributes[*cnt] = &iio_attr->dev_attr.attr; + + (*cnt)++; + iio_attr = &st->iio_attr[*cnt]; + addr = ad7280a_devaddr(dev) << 8 | (AD7280A_CB1_TIMER + ch); + + ret = ad7280_balance_timer_attr_init(iio_attr, sdev, addr, i); + if (ret < 0) + return ret; + + ad7280_attributes[*cnt] = &iio_attr->dev_attr.attr; + (*cnt)++; + } + + ad7280_attributes[*cnt] = NULL; + + return 0; +} + +static int ad7280_attr_init(struct ad7280_state *st) +{ + int dev, cnt = 0, ret; st->iio_attr = devm_kcalloc(&st->spi->dev, 2, sizeof(*st->iio_attr) * (st->slave_num + 1) * AD7280A_CELLS_PER_DEV, @@ -569,41 +672,11 @@ static int ad7280_attr_init(struct ad7280_state *st) if (!st->iio_attr) return -ENOMEM; - for (dev = 0, cnt = 0; dev <= st->slave_num; dev++) - for (ch = AD7280A_CELL_VOLTAGE_1; ch <= AD7280A_CELL_VOLTAGE_6; - ch++, cnt++) { - iio_attr = &st->iio_attr[cnt]; - index = dev * AD7280A_CELLS_PER_DEV + ch; - iio_attr->address = ad7280a_devaddr(dev) << 8 | ch; - iio_attr->dev_attr.attr.mode = 0644; - iio_attr->dev_attr.show = ad7280_show_balance_sw; - iio_attr->dev_attr.store = ad7280_store_balance_sw; - iio_attr->dev_attr.attr.name = - devm_kasprintf(&st->spi->dev, GFP_KERNEL, - "in%d-in%d_balance_switch_en", - index, index + 1); - if (!iio_attr->dev_attr.attr.name) - return -ENOMEM; - - ad7280_attributes[cnt] = &iio_attr->dev_attr.attr; - cnt++; - iio_attr = &st->iio_attr[cnt]; - iio_attr->address = ad7280a_devaddr(dev) << 8 | - (AD7280A_CB1_TIMER + ch); - iio_attr->dev_attr.attr.mode = 0644; - iio_attr->dev_attr.show = ad7280_show_balance_timer; - iio_attr->dev_attr.store = ad7280_store_balance_timer; - iio_attr->dev_attr.attr.name = - devm_kasprintf(&st->spi->dev, GFP_KERNEL, - "in%d-in%d_balance_timer", - index, index + 1); - if (!iio_attr->dev_attr.attr.name) - return -ENOMEM; - - ad7280_attributes[cnt] = &iio_attr->dev_attr.attr; - } - - ad7280_attributes[cnt] = NULL; + for (dev = 0; dev <= st->slave_num; dev++) { + ret = ad7280_init_dev_attrs(st, dev, &cnt); + if (ret < 0) + return ret; + } return 0; } diff --git a/drivers/staging/iio/adc/ad7816.c b/drivers/staging/iio/adc/ad7816.c index 5209651a1b25..ee50e7296795 100644 --- a/drivers/staging/iio/adc/ad7816.c +++ b/drivers/staging/iio/adc/ad7816.c @@ -65,7 +65,7 @@ enum ad7816_type { static int ad7816_spi_read(struct ad7816_chip_info *chip, u16 *data) { struct spi_device *spi_dev = chip->spi_dev; - int ret = 0; + int ret; __be16 buf; gpiod_set_value(chip->rdwr_pin, 1); @@ -106,7 +106,7 @@ static int ad7816_spi_read(struct ad7816_chip_info *chip, u16 *data) static int ad7816_spi_write(struct ad7816_chip_info *chip, u8 data) { struct spi_device *spi_dev = chip->spi_dev; - int ret = 0; + int ret; gpiod_set_value(chip->rdwr_pin, 1); gpiod_set_value(chip->rdwr_pin, 0); @@ -354,8 +354,7 @@ static int ad7816_probe(struct spi_device *spi_dev) { struct ad7816_chip_info *chip; struct iio_dev *indio_dev; - int ret = 0; - int i; + int i, ret; indio_dev = devm_iio_device_alloc(&spi_dev->dev, sizeof(*chip)); if (!indio_dev) diff --git a/drivers/staging/iio/addac/adt7316-i2c.c b/drivers/staging/iio/addac/adt7316-i2c.c index 2d51bd425662..0f26bc38edc6 100644 --- a/drivers/staging/iio/addac/adt7316-i2c.c +++ b/drivers/staging/iio/addac/adt7316-i2c.c @@ -43,7 +43,7 @@ static int adt7316_i2c_read(void *client, u8 reg, u8 *data) static int adt7316_i2c_write(void *client, u8 reg, u8 data) { struct i2c_client *cl = client; - int ret = 0; + int ret; ret = i2c_smbus_write_byte_data(cl, reg, data); if (ret < 0) @@ -55,7 +55,7 @@ static int adt7316_i2c_write(void *client, u8 reg, u8 data) static int adt7316_i2c_multi_read(void *client, u8 reg, u8 count, u8 *data) { struct i2c_client *cl = client; - int i, ret = 0; + int i, ret; if (count > ADT7316_REG_MAX_ADDR) count = ADT7316_REG_MAX_ADDR; @@ -74,7 +74,7 @@ static int adt7316_i2c_multi_read(void *client, u8 reg, u8 count, u8 *data) static int adt7316_i2c_multi_write(void *client, u8 reg, u8 count, u8 *data) { struct i2c_client *cl = client; - int i, ret = 0; + int i, ret; if (count > ADT7316_REG_MAX_ADDR) count = ADT7316_REG_MAX_ADDR; diff --git a/drivers/staging/iio/addac/adt7316-spi.c b/drivers/staging/iio/addac/adt7316-spi.c index e75827e326a6..8294b9c1e3c2 100644 --- a/drivers/staging/iio/addac/adt7316-spi.c +++ b/drivers/staging/iio/addac/adt7316-spi.c @@ -27,7 +27,7 @@ static int adt7316_spi_multi_read(void *client, u8 reg, u8 count, u8 *data) { struct spi_device *spi_dev = client; u8 cmd[2]; - int ret = 0; + int ret; if (count > ADT7316_REG_MAX_ADDR) count = ADT7316_REG_MAX_ADDR; @@ -56,7 +56,7 @@ static int adt7316_spi_multi_write(void *client, u8 reg, u8 count, u8 *data) { struct spi_device *spi_dev = client; u8 buf[ADT7316_REG_MAX_ADDR + 2]; - int i, ret = 0; + int i, ret; if (count > ADT7316_REG_MAX_ADDR) count = ADT7316_REG_MAX_ADDR; diff --git a/drivers/staging/iio/addac/adt7316.c b/drivers/staging/iio/addac/adt7316.c index dc93e85808e0..6f7891b567b9 100644 --- a/drivers/staging/iio/addac/adt7316.c +++ b/drivers/staging/iio/addac/adt7316.c @@ -47,6 +47,8 @@ #define ADT7516_MSB_AIN3 0xA #define ADT7516_MSB_AIN4 0xB #define ADT7316_DA_DATA_BASE 0x10 +#define ADT7316_DA_10_BIT_LSB_SHIFT 6 +#define ADT7316_DA_12_BIT_LSB_SHIFT 4 #define ADT7316_DA_MSB_DATA_REGS 4 #define ADT7316_LSB_DAC_A 0x10 #define ADT7316_MSB_DAC_A 0x11 @@ -59,8 +61,8 @@ #define ADT7316_CONFIG1 0x18 #define ADT7316_CONFIG2 0x19 #define ADT7316_CONFIG3 0x1A -#define ADT7316_LDAC_CONFIG 0x1B -#define ADT7316_DAC_CONFIG 0x1C +#define ADT7316_DAC_CONFIG 0x1B +#define ADT7316_LDAC_CONFIG 0x1C #define ADT7316_INT_MASK1 0x1D #define ADT7316_INT_MASK2 0x1E #define ADT7316_IN_TEMP_OFFSET 0x1F @@ -117,7 +119,7 @@ */ #define ADT7316_ADCLK_22_5 0x1 #define ADT7316_DA_HIGH_RESOLUTION 0x2 -#define ADT7316_DA_EN_VIA_DAC_LDCA 0x4 +#define ADT7316_DA_EN_VIA_DAC_LDAC 0x8 #define ADT7516_AIN_IN_VREF 0x10 #define ADT7316_EN_IN_TEMP_PROP_DACA 0x20 #define ADT7316_EN_EX_TEMP_PROP_DACB 0x40 @@ -127,6 +129,7 @@ */ #define ADT7316_DA_2VREF_CH_MASK 0xF #define ADT7316_DA_EN_MODE_MASK 0x30 +#define ADT7316_DA_EN_MODE_SHIFT 4 #define ADT7316_DA_EN_MODE_SINGLE 0x00 #define ADT7316_DA_EN_MODE_AB_CD 0x10 #define ADT7316_DA_EN_MODE_ABCD 0x20 @@ -632,9 +635,7 @@ static ssize_t adt7316_show_da_high_resolution(struct device *dev, struct adt7316_chip_info *chip = iio_priv(dev_info); if (chip->config3 & ADT7316_DA_HIGH_RESOLUTION) { - if (chip->id == ID_ADT7316 || chip->id == ID_ADT7516) - return sprintf(buf, "1 (12 bits)\n"); - if (chip->id == ID_ADT7317 || chip->id == ID_ADT7517) + if (chip->id != ID_ADT7318 && chip->id != ID_ADT7519) return sprintf(buf, "1 (10 bits)\n"); } @@ -651,17 +652,12 @@ static ssize_t adt7316_store_da_high_resolution(struct device *dev, u8 config3; int ret; - chip->dac_bits = 8; + if (chip->id == ID_ADT7318 || chip->id == ID_ADT7519) + return -EPERM; - if (buf[0] == '1') { - config3 = chip->config3 | ADT7316_DA_HIGH_RESOLUTION; - if (chip->id == ID_ADT7316 || chip->id == ID_ADT7516) - chip->dac_bits = 12; - else if (chip->id == ID_ADT7317 || chip->id == ID_ADT7517) - chip->dac_bits = 10; - } else { - config3 = chip->config3 & (~ADT7316_DA_HIGH_RESOLUTION); - } + config3 = chip->config3 & (~ADT7316_DA_HIGH_RESOLUTION); + if (buf[0] == '1') + config3 |= ADT7316_DA_HIGH_RESOLUTION; ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG3, config3); if (ret) @@ -851,7 +847,7 @@ static ssize_t adt7316_show_DAC_update_mode(struct device *dev, struct iio_dev *dev_info = dev_to_iio_dev(dev); struct adt7316_chip_info *chip = iio_priv(dev_info); - if (!(chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA)) + if (!(chip->config3 & ADT7316_DA_EN_VIA_DAC_LDAC)) return sprintf(buf, "manual\n"); switch (chip->dac_config & ADT7316_DA_EN_MODE_MASK) { @@ -880,15 +876,15 @@ static ssize_t adt7316_store_DAC_update_mode(struct device *dev, u8 data; int ret; - if (!(chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA)) + if (!(chip->config3 & ADT7316_DA_EN_VIA_DAC_LDAC)) return -EPERM; ret = kstrtou8(buf, 10, &data); - if (ret || data > ADT7316_DA_EN_MODE_MASK) + if (ret || data > (ADT7316_DA_EN_MODE_MASK >> ADT7316_DA_EN_MODE_SHIFT)) return -EINVAL; dac_config = chip->dac_config & (~ADT7316_DA_EN_MODE_MASK); - dac_config |= data; + dac_config |= data << ADT7316_DA_EN_MODE_SHIFT; ret = chip->bus.write(chip->bus.client, ADT7316_DAC_CONFIG, dac_config); if (ret) @@ -911,7 +907,7 @@ static ssize_t adt7316_show_all_DAC_update_modes(struct device *dev, struct iio_dev *dev_info = dev_to_iio_dev(dev); struct adt7316_chip_info *chip = iio_priv(dev_info); - if (chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA) + if (chip->config3 & ADT7316_DA_EN_VIA_DAC_LDAC) return sprintf(buf, "0 - auto at any MSB DAC writing\n" "1 - auto at MSB DAC AB and CD writing\n" "2 - auto at MSB DAC ABCD writing\n" @@ -933,7 +929,7 @@ static ssize_t adt7316_store_update_DAC(struct device *dev, u8 data; int ret; - if (chip->config3 & ADT7316_DA_EN_VIA_DAC_LDCA) { + if (chip->config3 & ADT7316_DA_EN_VIA_DAC_LDAC) { if ((chip->dac_config & ADT7316_DA_EN_MODE_MASK) != ADT7316_DA_EN_MODE_LDAC) return -EPERM; @@ -969,9 +965,6 @@ static ssize_t adt7316_show_DA_AB_Vref_bypass(struct device *dev, struct iio_dev *dev_info = dev_to_iio_dev(dev); struct adt7316_chip_info *chip = iio_priv(dev_info); - if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) - return -EPERM; - return sprintf(buf, "%d\n", !!(chip->dac_config & ADT7316_VREF_BYPASS_DAC_AB)); } @@ -986,9 +979,6 @@ static ssize_t adt7316_store_DA_AB_Vref_bypass(struct device *dev, u8 dac_config; int ret; - if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) - return -EPERM; - dac_config = chip->dac_config & (~ADT7316_VREF_BYPASS_DAC_AB); if (buf[0] == '1') dac_config |= ADT7316_VREF_BYPASS_DAC_AB; @@ -1014,9 +1004,6 @@ static ssize_t adt7316_show_DA_CD_Vref_bypass(struct device *dev, struct iio_dev *dev_info = dev_to_iio_dev(dev); struct adt7316_chip_info *chip = iio_priv(dev_info); - if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) - return -EPERM; - return sprintf(buf, "%d\n", !!(chip->dac_config & ADT7316_VREF_BYPASS_DAC_CD)); } @@ -1031,9 +1018,6 @@ static ssize_t adt7316_store_DA_CD_Vref_bypass(struct device *dev, u8 dac_config; int ret; - if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) - return -EPERM; - dac_config = chip->dac_config & (~ADT7316_VREF_BYPASS_DAC_CD); if (buf[0] == '1') dac_config |= ADT7316_VREF_BYPASS_DAC_CD; @@ -1061,10 +1045,10 @@ static ssize_t adt7316_show_DAC_internal_Vref(struct device *dev, if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) return sprintf(buf, "0x%x\n", - (chip->dac_config & ADT7516_DAC_IN_VREF_MASK) >> + (chip->ldac_config & ADT7516_DAC_IN_VREF_MASK) >> ADT7516_DAC_IN_VREF_OFFSET); return sprintf(buf, "%d\n", - !!(chip->dac_config & ADT7316_DAC_IN_VREF)); + !!(chip->ldac_config & ADT7316_DAC_IN_VREF)); } static ssize_t adt7316_store_DAC_internal_Vref(struct device *dev, @@ -1086,7 +1070,7 @@ static ssize_t adt7316_store_DAC_internal_Vref(struct device *dev, ldac_config = chip->ldac_config & (~ADT7516_DAC_IN_VREF_MASK); if (data & 0x1) ldac_config |= ADT7516_DAC_AB_IN_VREF; - else if (data & 0x2) + if (data & 0x2) ldac_config |= ADT7516_DAC_CD_IN_VREF; } else { ret = kstrtou8(buf, 16, &data); @@ -1410,7 +1394,7 @@ static IIO_DEVICE_ATTR(ex_analog_temp_offset, 0644, static ssize_t adt7316_show_DAC(struct adt7316_chip_info *chip, int channel, char *buf) { - u16 data; + u16 data = 0; u8 msb, lsb, offset; int ret; @@ -1435,7 +1419,11 @@ static ssize_t adt7316_show_DAC(struct adt7316_chip_info *chip, if (ret) return -EIO; - data = (msb << offset) + (lsb & ((1 << offset) - 1)); + if (chip->dac_bits == 12) + data = lsb >> ADT7316_DA_12_BIT_LSB_SHIFT; + else if (chip->dac_bits == 10) + data = lsb >> ADT7316_DA_10_BIT_LSB_SHIFT; + data |= msb << offset; return sprintf(buf, "%d\n", data); } @@ -1443,7 +1431,7 @@ static ssize_t adt7316_show_DAC(struct adt7316_chip_info *chip, static ssize_t adt7316_store_DAC(struct adt7316_chip_info *chip, int channel, const char *buf, size_t len) { - u8 msb, lsb, offset; + u8 msb, lsb, lsb_reg, offset; u16 data; int ret; @@ -1461,9 +1449,13 @@ static ssize_t adt7316_store_DAC(struct adt7316_chip_info *chip, return -EINVAL; if (chip->dac_bits > 8) { - lsb = data & (1 << offset); + lsb = data & ((1 << offset) - 1); + if (chip->dac_bits == 12) + lsb_reg = lsb << ADT7316_DA_12_BIT_LSB_SHIFT; + else + lsb_reg = lsb << ADT7316_DA_10_BIT_LSB_SHIFT; ret = chip->bus.write(chip->bus.client, - ADT7316_DA_DATA_BASE + channel * 2, lsb); + ADT7316_DA_DATA_BASE + channel * 2, lsb_reg); if (ret) return -EIO; } @@ -1710,8 +1702,6 @@ static struct attribute *adt7516_attributes[] = { &iio_dev_attr_DAC_update_mode.dev_attr.attr, &iio_dev_attr_all_DAC_update_modes.dev_attr.attr, &iio_dev_attr_update_DAC.dev_attr.attr, - &iio_dev_attr_DA_AB_Vref_bypass.dev_attr.attr, - &iio_dev_attr_DA_CD_Vref_bypass.dev_attr.attr, &iio_dev_attr_DAC_internal_Vref.dev_attr.attr, &iio_dev_attr_VDD.dev_attr.attr, &iio_dev_attr_in_temp.dev_attr.attr, @@ -1809,6 +1799,43 @@ static irqreturn_t adt7316_event_handler(int irq, void *private) return IRQ_HANDLED; } +static int adt7316_setup_irq(struct iio_dev *indio_dev) +{ + struct adt7316_chip_info *chip = iio_priv(indio_dev); + int irq_type, ret; + + irq_type = irqd_get_trigger_type(irq_get_irq_data(chip->bus.irq)); + + switch (irq_type) { + case IRQF_TRIGGER_HIGH: + case IRQF_TRIGGER_RISING: + break; + case IRQF_TRIGGER_LOW: + case IRQF_TRIGGER_FALLING: + break; + default: + dev_info(&indio_dev->dev, "mode %d unsupported, using IRQF_TRIGGER_LOW\n", + irq_type); + irq_type = IRQF_TRIGGER_LOW; + break; + } + + ret = devm_request_threaded_irq(&indio_dev->dev, chip->bus.irq, + NULL, adt7316_event_handler, + irq_type | IRQF_ONESHOT, + indio_dev->name, indio_dev); + if (ret) { + dev_err(&indio_dev->dev, "failed to request irq %d\n", + chip->bus.irq); + return ret; + } + + if (irq_type & IRQF_TRIGGER_HIGH) + chip->config1 |= ADT7316_INT_POLARITY; + + return 0; +} + /* * Show mask of enabled interrupts in Hex. */ @@ -2103,9 +2130,7 @@ int adt7316_probe(struct device *dev, struct adt7316_bus *bus, { struct adt7316_chip_info *chip; struct iio_dev *indio_dev; - unsigned short *adt7316_platform_data = dev->platform_data; - int irq_type = IRQF_TRIGGER_LOW; - int ret = 0; + int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*chip)); if (!indio_dev) @@ -2123,6 +2148,13 @@ int adt7316_probe(struct device *dev, struct adt7316_bus *bus, else return -ENODEV; + if (chip->id == ID_ADT7316 || chip->id == ID_ADT7516) + chip->dac_bits = 12; + else if (chip->id == ID_ADT7317 || chip->id == ID_ADT7517) + chip->dac_bits = 10; + else + chip->dac_bits = 8; + chip->ldac_pin = devm_gpiod_get_optional(dev, "adi,ldac", GPIOD_OUT_LOW); if (IS_ERR(chip->ldac_pin)) { ret = PTR_ERR(chip->ldac_pin); @@ -2130,8 +2162,8 @@ int adt7316_probe(struct device *dev, struct adt7316_bus *bus, return ret; } - if (chip->ldac_pin) { - chip->config3 |= ADT7316_DA_EN_VIA_DAC_LDCA; + if (!chip->ldac_pin) { + chip->config3 |= ADT7316_DA_EN_VIA_DAC_LDAC; if ((chip->id & ID_FAMILY_MASK) == ID_ADT75XX) chip->config1 |= ADT7516_SEL_AIN3; } @@ -2148,20 +2180,9 @@ int adt7316_probe(struct device *dev, struct adt7316_bus *bus, indio_dev->modes = INDIO_DIRECT_MODE; if (chip->bus.irq > 0) { - if (adt7316_platform_data[0]) - irq_type = adt7316_platform_data[0]; - - ret = devm_request_threaded_irq(dev, chip->bus.irq, - NULL, - adt7316_event_handler, - irq_type | IRQF_ONESHOT, - indio_dev->name, - indio_dev); + ret = adt7316_setup_irq(indio_dev); if (ret) return ret; - - if (irq_type & IRQF_TRIGGER_HIGH) - chip->config1 |= ADT7316_INT_POLARITY; } ret = chip->bus.write(chip->bus.client, ADT7316_CONFIG1, chip->config1); diff --git a/drivers/staging/iio/cdc/Kconfig b/drivers/staging/iio/cdc/Kconfig index 80211df8c577..b97478e7cbd0 100644 --- a/drivers/staging/iio/cdc/Kconfig +++ b/drivers/staging/iio/cdc/Kconfig @@ -13,16 +13,6 @@ config AD7150 To compile this driver as a module, choose M here: the module will be called ad7150. -config AD7152 - tristate "Analog Devices ad7152/3 capacitive sensor driver" - depends on I2C - help - Say yes here to build support for Analog Devices capacitive sensors. - (ad7152, ad7153) Provides direct access via sysfs. - - To compile this driver as a module, choose M here: the - module will be called ad7152. - config AD7746 tristate "Analog Devices AD7745, AD7746 AD7747 capacitive sensor driver" depends on I2C diff --git a/drivers/staging/iio/cdc/Makefile b/drivers/staging/iio/cdc/Makefile index a5fbabf5c8bf..1466bc31f244 100644 --- a/drivers/staging/iio/cdc/Makefile +++ b/drivers/staging/iio/cdc/Makefile @@ -3,5 +3,4 @@ # obj-$(CONFIG_AD7150) += ad7150.o -obj-$(CONFIG_AD7152) += ad7152.o obj-$(CONFIG_AD7746) += ad7746.o diff --git a/drivers/staging/iio/cdc/ad7152.c b/drivers/staging/iio/cdc/ad7152.c deleted file mode 100644 index 25f51db05d2d..000000000000 --- a/drivers/staging/iio/cdc/ad7152.c +++ /dev/null @@ -1,552 +0,0 @@ -/* - * AD7152 capacitive sensor driver supporting AD7152/3 - * - * Copyright 2010-2011a Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ - -#include <linux/interrupt.h> -#include <linux/device.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/sysfs.h> -#include <linux/i2c.h> -#include <linux/module.h> -#include <linux/delay.h> - -#include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> - -/* - * TODO: Check compliance of calibbias with abi (units) - */ -/* - * AD7152 registers definition - */ - -#define AD7152_REG_STATUS 0 -#define AD7152_REG_CH1_DATA_HIGH 1 -#define AD7152_REG_CH2_DATA_HIGH 3 -#define AD7152_REG_CH1_OFFS_HIGH 5 -#define AD7152_REG_CH2_OFFS_HIGH 7 -#define AD7152_REG_CH1_GAIN_HIGH 9 -#define AD7152_REG_CH1_SETUP 11 -#define AD7152_REG_CH2_GAIN_HIGH 12 -#define AD7152_REG_CH2_SETUP 14 -#define AD7152_REG_CFG 15 -#define AD7152_REG_RESEVERD 16 -#define AD7152_REG_CAPDAC_POS 17 -#define AD7152_REG_CAPDAC_NEG 18 -#define AD7152_REG_CFG2 26 - -/* Status Register Bit Designations (AD7152_REG_STATUS) */ -#define AD7152_STATUS_RDY1 BIT(0) -#define AD7152_STATUS_RDY2 BIT(1) -#define AD7152_STATUS_C1C2 BIT(2) -#define AD7152_STATUS_PWDN BIT(7) - -/* Setup Register Bit Designations (AD7152_REG_CHx_SETUP) */ -#define AD7152_SETUP_CAPDIFF BIT(5) -#define AD7152_SETUP_RANGE_2pF (0 << 6) -#define AD7152_SETUP_RANGE_0_5pF (1 << 6) -#define AD7152_SETUP_RANGE_1pF (2 << 6) -#define AD7152_SETUP_RANGE_4pF (3 << 6) -#define AD7152_SETUP_RANGE(x) ((x) << 6) - -/* Config Register Bit Designations (AD7152_REG_CFG) */ -#define AD7152_CONF_CH2EN BIT(3) -#define AD7152_CONF_CH1EN BIT(4) -#define AD7152_CONF_MODE_IDLE (0 << 0) -#define AD7152_CONF_MODE_CONT_CONV (1 << 0) -#define AD7152_CONF_MODE_SINGLE_CONV (2 << 0) -#define AD7152_CONF_MODE_OFFS_CAL (5 << 0) -#define AD7152_CONF_MODE_GAIN_CAL (6 << 0) - -/* Capdac Register Bit Designations (AD7152_REG_CAPDAC_XXX) */ -#define AD7152_CAPDAC_DACEN BIT(7) -#define AD7152_CAPDAC_DACP(x) ((x) & 0x1F) - -/* CFG2 Register Bit Designations (AD7152_REG_CFG2) */ -#define AD7152_CFG2_OSR(x) (((x) & 0x3) << 4) - -enum { - AD7152_DATA, - AD7152_OFFS, - AD7152_GAIN, - AD7152_SETUP -}; - -/* - * struct ad7152_chip_info - chip specific information - */ - -struct ad7152_chip_info { - struct i2c_client *client; - /* - * Capacitive channel digital filter setup; - * conversion time/update rate setup per channel - */ - u8 filter_rate_setup; - u8 setup[2]; - struct mutex state_lock; /* protect hardware state */ -}; - -static inline ssize_t ad7152_start_calib(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len, - u8 regval) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7152_chip_info *chip = iio_priv(indio_dev); - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - bool doit; - int ret, timeout = 10; - - ret = strtobool(buf, &doit); - if (ret < 0) - return ret; - - if (!doit) - return 0; - - if (this_attr->address == 0) - regval |= AD7152_CONF_CH1EN; - else - regval |= AD7152_CONF_CH2EN; - - mutex_lock(&chip->state_lock); - ret = i2c_smbus_write_byte_data(chip->client, AD7152_REG_CFG, regval); - if (ret < 0) - goto unlock; - - do { - mdelay(20); - ret = i2c_smbus_read_byte_data(chip->client, AD7152_REG_CFG); - if (ret < 0) - goto unlock; - - } while ((ret == regval) && timeout--); - - mutex_unlock(&chip->state_lock); - return len; - -unlock: - mutex_unlock(&chip->state_lock); - return ret; -} - -static ssize_t ad7152_start_offset_calib(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - return ad7152_start_calib(dev, attr, buf, len, - AD7152_CONF_MODE_OFFS_CAL); -} - -static ssize_t ad7152_start_gain_calib(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - return ad7152_start_calib(dev, attr, buf, len, - AD7152_CONF_MODE_GAIN_CAL); -} - -static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration, - 0200, NULL, ad7152_start_offset_calib, 0); -static IIO_DEVICE_ATTR(in_capacitance1_calibbias_calibration, - 0200, NULL, ad7152_start_offset_calib, 1); -static IIO_DEVICE_ATTR(in_capacitance0_calibscale_calibration, - 0200, NULL, ad7152_start_gain_calib, 0); -static IIO_DEVICE_ATTR(in_capacitance1_calibscale_calibration, - 0200, NULL, ad7152_start_gain_calib, 1); - -/* Values are Update Rate (Hz), Conversion Time (ms) + 1*/ -static const unsigned char ad7152_filter_rate_table[][2] = { - {200, 5 + 1}, {50, 20 + 1}, {20, 50 + 1}, {17, 60 + 1}, -}; - -static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("200 50 20 17"); - -static IIO_CONST_ATTR(in_capacitance_scale_available, - "0.000061050 0.000030525 0.000015263 0.000007631"); - -static struct attribute *ad7152_attributes[] = { - &iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr, - &iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr, - &iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr, - &iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr, - &iio_const_attr_in_capacitance_scale_available.dev_attr.attr, - &iio_const_attr_sampling_frequency_available.dev_attr.attr, - NULL, -}; - -static const struct attribute_group ad7152_attribute_group = { - .attrs = ad7152_attributes, -}; - -static const u8 ad7152_addresses[][4] = { - { AD7152_REG_CH1_DATA_HIGH, AD7152_REG_CH1_OFFS_HIGH, - AD7152_REG_CH1_GAIN_HIGH, AD7152_REG_CH1_SETUP }, - { AD7152_REG_CH2_DATA_HIGH, AD7152_REG_CH2_OFFS_HIGH, - AD7152_REG_CH2_GAIN_HIGH, AD7152_REG_CH2_SETUP }, -}; - -/* Values are nano relative to pf base. */ -static const int ad7152_scale_table[] = { - 30525, 7631, 15263, 61050 -}; - -/** - * read_raw handler for IIO_CHAN_INFO_SAMP_FREQ - * - * lock must be held - **/ -static int ad7152_read_raw_samp_freq(struct device *dev, int *val) -{ - struct ad7152_chip_info *chip = iio_priv(dev_to_iio_dev(dev)); - - *val = ad7152_filter_rate_table[chip->filter_rate_setup][0]; - - return 0; -} - -/** - * write_raw handler for IIO_CHAN_INFO_SAMP_FREQ - * - * lock must be held - **/ -static int ad7152_write_raw_samp_freq(struct device *dev, int val) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct ad7152_chip_info *chip = iio_priv(indio_dev); - int ret, i; - - for (i = 0; i < ARRAY_SIZE(ad7152_filter_rate_table); i++) - if (val >= ad7152_filter_rate_table[i][0]) - break; - - if (i >= ARRAY_SIZE(ad7152_filter_rate_table)) - i = ARRAY_SIZE(ad7152_filter_rate_table) - 1; - - ret = i2c_smbus_write_byte_data(chip->client, - AD7152_REG_CFG2, AD7152_CFG2_OSR(i)); - if (ret < 0) - return ret; - - chip->filter_rate_setup = i; - - return ret; -} - -static int ad7152_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct ad7152_chip_info *chip = iio_priv(indio_dev); - int ret, i; - - mutex_lock(&chip->state_lock); - - switch (mask) { - case IIO_CHAN_INFO_CALIBSCALE: - if (val != 1) { - ret = -EINVAL; - goto out; - } - - val = (val2 * 1024) / 15625; - - ret = i2c_smbus_write_word_data(chip->client, - ad7152_addresses[chan->channel][AD7152_GAIN], - swab16(val)); - if (ret < 0) - goto out; - - ret = 0; - break; - - case IIO_CHAN_INFO_CALIBBIAS: - if ((val < 0) | (val > 0xFFFF)) { - ret = -EINVAL; - goto out; - } - ret = i2c_smbus_write_word_data(chip->client, - ad7152_addresses[chan->channel][AD7152_OFFS], - swab16(val)); - if (ret < 0) - goto out; - - ret = 0; - break; - case IIO_CHAN_INFO_SCALE: - if (val) { - ret = -EINVAL; - goto out; - } - for (i = 0; i < ARRAY_SIZE(ad7152_scale_table); i++) - if (val2 == ad7152_scale_table[i]) - break; - - chip->setup[chan->channel] &= ~AD7152_SETUP_RANGE_4pF; - chip->setup[chan->channel] |= AD7152_SETUP_RANGE(i); - - ret = i2c_smbus_write_byte_data(chip->client, - ad7152_addresses[chan->channel][AD7152_SETUP], - chip->setup[chan->channel]); - if (ret < 0) - goto out; - - ret = 0; - break; - case IIO_CHAN_INFO_SAMP_FREQ: - if (val2) { - ret = -EINVAL; - goto out; - } - ret = ad7152_write_raw_samp_freq(&indio_dev->dev, val); - if (ret < 0) - goto out; - - ret = 0; - break; - default: - ret = -EINVAL; - } - -out: - mutex_unlock(&chip->state_lock); - return ret; -} - -static int ad7152_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, int *val2, - long mask) -{ - struct ad7152_chip_info *chip = iio_priv(indio_dev); - int ret; - u8 regval = 0; - - mutex_lock(&chip->state_lock); - - switch (mask) { - case IIO_CHAN_INFO_RAW: - /* First set whether in differential mode */ - - regval = chip->setup[chan->channel]; - - if (chan->differential) - chip->setup[chan->channel] |= AD7152_SETUP_CAPDIFF; - else - chip->setup[chan->channel] &= ~AD7152_SETUP_CAPDIFF; - - if (regval != chip->setup[chan->channel]) { - ret = i2c_smbus_write_byte_data(chip->client, - ad7152_addresses[chan->channel][AD7152_SETUP], - chip->setup[chan->channel]); - if (ret < 0) - goto out; - } - /* Make sure the channel is enabled */ - if (chan->channel == 0) - regval = AD7152_CONF_CH1EN; - else - regval = AD7152_CONF_CH2EN; - - /* Trigger a single read */ - regval |= AD7152_CONF_MODE_SINGLE_CONV; - ret = i2c_smbus_write_byte_data(chip->client, AD7152_REG_CFG, - regval); - if (ret < 0) - goto out; - - msleep(ad7152_filter_rate_table[chip->filter_rate_setup][1]); - /* Now read the actual register */ - ret = i2c_smbus_read_word_data(chip->client, - ad7152_addresses[chan->channel][AD7152_DATA]); - if (ret < 0) - goto out; - *val = swab16(ret); - - if (chan->differential) - *val -= 0x8000; - - ret = IIO_VAL_INT; - break; - case IIO_CHAN_INFO_CALIBSCALE: - - ret = i2c_smbus_read_word_data(chip->client, - ad7152_addresses[chan->channel][AD7152_GAIN]); - if (ret < 0) - goto out; - /* 1 + gain_val / 2^16 */ - *val = 1; - *val2 = (15625 * swab16(ret)) / 1024; - - ret = IIO_VAL_INT_PLUS_MICRO; - break; - case IIO_CHAN_INFO_CALIBBIAS: - ret = i2c_smbus_read_word_data(chip->client, - ad7152_addresses[chan->channel][AD7152_OFFS]); - if (ret < 0) - goto out; - *val = swab16(ret); - - ret = IIO_VAL_INT; - break; - case IIO_CHAN_INFO_SCALE: - ret = i2c_smbus_read_byte_data(chip->client, - ad7152_addresses[chan->channel][AD7152_SETUP]); - if (ret < 0) - goto out; - *val = 0; - *val2 = ad7152_scale_table[ret >> 6]; - - ret = IIO_VAL_INT_PLUS_NANO; - break; - case IIO_CHAN_INFO_SAMP_FREQ: - ret = ad7152_read_raw_samp_freq(&indio_dev->dev, val); - if (ret < 0) - goto out; - - ret = IIO_VAL_INT; - break; - default: - ret = -EINVAL; - } -out: - mutex_unlock(&chip->state_lock); - return ret; -} - -static int ad7152_write_raw_get_fmt(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - long mask) -{ - switch (mask) { - case IIO_CHAN_INFO_SCALE: - return IIO_VAL_INT_PLUS_NANO; - default: - return IIO_VAL_INT_PLUS_MICRO; - } -} - -static const struct iio_info ad7152_info = { - .attrs = &ad7152_attribute_group, - .read_raw = ad7152_read_raw, - .write_raw = ad7152_write_raw, - .write_raw_get_fmt = ad7152_write_raw_get_fmt, -}; - -static const struct iio_chan_spec ad7152_channels[] = { - { - .type = IIO_CAPACITANCE, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS) | - BIT(IIO_CHAN_INFO_SCALE), - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), - }, { - .type = IIO_CAPACITANCE, - .differential = 1, - .indexed = 1, - .channel = 0, - .channel2 = 2, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS) | - BIT(IIO_CHAN_INFO_SCALE), - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), - }, { - .type = IIO_CAPACITANCE, - .indexed = 1, - .channel = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS) | - BIT(IIO_CHAN_INFO_SCALE), - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), - }, { - .type = IIO_CAPACITANCE, - .differential = 1, - .indexed = 1, - .channel = 1, - .channel2 = 3, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS) | - BIT(IIO_CHAN_INFO_SCALE), - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), - } -}; - -/* - * device probe and remove - */ - -static int ad7152_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int ret = 0; - struct ad7152_chip_info *chip; - struct iio_dev *indio_dev; - - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); - if (!indio_dev) - return -ENOMEM; - chip = iio_priv(indio_dev); - /* this is only used for device removal purposes */ - i2c_set_clientdata(client, indio_dev); - - chip->client = client; - mutex_init(&chip->state_lock); - - /* Establish that the iio_dev is a child of the i2c device */ - indio_dev->name = id->name; - indio_dev->dev.parent = &client->dev; - indio_dev->info = &ad7152_info; - indio_dev->channels = ad7152_channels; - if (id->driver_data == 0) - indio_dev->num_channels = ARRAY_SIZE(ad7152_channels); - else - indio_dev->num_channels = 2; - indio_dev->num_channels = ARRAY_SIZE(ad7152_channels); - indio_dev->modes = INDIO_DIRECT_MODE; - - ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev); - if (ret) - return ret; - - dev_err(&client->dev, "%s capacitive sensor registered\n", id->name); - - return 0; -} - -static const struct i2c_device_id ad7152_id[] = { - { "ad7152", 0 }, - { "ad7153", 1 }, - {} -}; - -MODULE_DEVICE_TABLE(i2c, ad7152_id); - -static struct i2c_driver ad7152_driver = { - .driver = { - .name = KBUILD_MODNAME, - }, - .probe = ad7152_probe, - .id_table = ad7152_id, -}; -module_i2c_driver(ad7152_driver); - -MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); -MODULE_DESCRIPTION("Analog Devices AD7152/3 capacitive sensor driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c index 1e977014fe5f..0b0287503fb4 100644 --- a/drivers/staging/iio/frequency/ad9834.c +++ b/drivers/staging/iio/frequency/ad9834.c @@ -6,6 +6,7 @@ * Licensed under the GPL-2. */ +#include <linux/clk.h> #include <linux/interrupt.h> #include <linux/workqueue.h> #include <linux/device.h> @@ -71,7 +72,7 @@ struct ad9834_state { struct spi_device *spi; struct regulator *reg; - unsigned int mclk; + struct clk *mclk; unsigned short control; unsigned short devid; struct spi_transfer xfer; @@ -110,12 +111,15 @@ static unsigned int ad9834_calc_freqreg(unsigned long mclk, unsigned long fout) static int ad9834_write_frequency(struct ad9834_state *st, unsigned long addr, unsigned long fout) { + unsigned long clk_freq; unsigned long regval; - if (fout > (st->mclk / 2)) + clk_freq = clk_get_rate(st->mclk); + + if (fout > (clk_freq / 2)) return -EINVAL; - regval = ad9834_calc_freqreg(st->mclk, fout); + regval = ad9834_calc_freqreg(clk_freq, fout); st->freq_data[0] = cpu_to_be16(addr | (regval & RES_MASK(AD9834_FREQ_BITS / 2))); @@ -389,16 +393,11 @@ static const struct iio_info ad9833_info = { static int ad9834_probe(struct spi_device *spi) { - struct ad9834_platform_data *pdata = dev_get_platdata(&spi->dev); struct ad9834_state *st; struct iio_dev *indio_dev; struct regulator *reg; int ret; - if (!pdata) { - dev_dbg(&spi->dev, "no platform data?\n"); - return -ENODEV; - } reg = devm_regulator_get(&spi->dev, "avdd"); if (IS_ERR(reg)) @@ -418,7 +417,14 @@ static int ad9834_probe(struct spi_device *spi) spi_set_drvdata(spi, indio_dev); st = iio_priv(indio_dev); mutex_init(&st->lock); - st->mclk = pdata->mclk; + st->mclk = devm_clk_get(&spi->dev, NULL); + + ret = clk_prepare_enable(st->mclk); + if (ret) { + dev_err(&spi->dev, "Failed to enable master clock\n"); + goto error_disable_reg; + } + st->spi = spi; st->devid = spi_get_device_id(spi)->driver_data; st->reg = reg; @@ -454,42 +460,41 @@ static int ad9834_probe(struct spi_device *spi) spi_message_add_tail(&st->freq_xfer[1], &st->freq_msg); st->control = AD9834_B28 | AD9834_RESET; + st->control |= AD9834_DIV2; - if (!pdata->en_div2) - st->control |= AD9834_DIV2; - - if (!pdata->en_signbit_msb_out && (st->devid == ID_AD9834)) + if (st->devid == ID_AD9834) st->control |= AD9834_SIGN_PIB; st->data = cpu_to_be16(AD9834_REG_CMD | st->control); ret = spi_sync(st->spi, &st->msg); if (ret) { dev_err(&spi->dev, "device init failed\n"); - goto error_disable_reg; + goto error_clock_unprepare; } - ret = ad9834_write_frequency(st, AD9834_REG_FREQ0, pdata->freq0); + ret = ad9834_write_frequency(st, AD9834_REG_FREQ0, 1000000); if (ret) - goto error_disable_reg; + goto error_clock_unprepare; - ret = ad9834_write_frequency(st, AD9834_REG_FREQ1, pdata->freq1); + ret = ad9834_write_frequency(st, AD9834_REG_FREQ1, 5000000); if (ret) - goto error_disable_reg; + goto error_clock_unprepare; - ret = ad9834_write_phase(st, AD9834_REG_PHASE0, pdata->phase0); + ret = ad9834_write_phase(st, AD9834_REG_PHASE0, 512); if (ret) - goto error_disable_reg; + goto error_clock_unprepare; - ret = ad9834_write_phase(st, AD9834_REG_PHASE1, pdata->phase1); + ret = ad9834_write_phase(st, AD9834_REG_PHASE1, 1024); if (ret) - goto error_disable_reg; + goto error_clock_unprepare; ret = iio_device_register(indio_dev); if (ret) - goto error_disable_reg; + goto error_clock_unprepare; return 0; - +error_clock_unprepare: + clk_disable_unprepare(st->mclk); error_disable_reg: regulator_disable(reg); @@ -502,6 +507,7 @@ static int ad9834_remove(struct spi_device *spi) struct ad9834_state *st = iio_priv(indio_dev); iio_device_unregister(indio_dev); + clk_disable_unprepare(st->mclk); regulator_disable(st->reg); return 0; diff --git a/drivers/staging/iio/frequency/ad9834.h b/drivers/staging/iio/frequency/ad9834.h index ae620f38eb49..da7e83ceedad 100644 --- a/drivers/staging/iio/frequency/ad9834.h +++ b/drivers/staging/iio/frequency/ad9834.h @@ -8,32 +8,4 @@ #ifndef IIO_DDS_AD9834_H_ #define IIO_DDS_AD9834_H_ -/* - * TODO: struct ad7887_platform_data needs to go into include/linux/iio - */ - -/** - * struct ad9834_platform_data - platform specific information - * @mclk: master clock in Hz - * @freq0: power up freq0 tuning word in Hz - * @freq1: power up freq1 tuning word in Hz - * @phase0: power up phase0 value [0..4095] correlates with 0..2PI - * @phase1: power up phase1 value [0..4095] correlates with 0..2PI - * @en_div2: digital output/2 is passed to the SIGN BIT OUT pin - * @en_signbit_msb_out: the MSB (or MSB/2) of the DAC data is connected to the - * SIGN BIT OUT pin. en_div2 controls whether it is the MSB - * or MSB/2 that is output. if en_signbit_msb_out=false, - * the on-board comparator is connected to SIGN BIT OUT - */ - -struct ad9834_platform_data { - unsigned int mclk; - unsigned int freq0; - unsigned int freq1; - unsigned short phase0; - unsigned short phase1; - bool en_div2; - bool en_signbit_msb_out; -}; - #endif /* IIO_DDS_AD9834_H_ */ diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c index 9e52384f5370..3134295f014f 100644 --- a/drivers/staging/iio/impedance-analyzer/ad5933.c +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c @@ -16,6 +16,7 @@ #include <linux/err.h> #include <linux/delay.h> #include <linux/module.h> +#include <linux/clk.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -82,21 +83,10 @@ #define AD5933_POLL_TIME_ms 10 #define AD5933_INIT_EXCITATION_TIME_ms 100 -/** - * struct ad5933_platform_data - platform specific data - * @ext_clk_hz: the external clock frequency in Hz, if not set - * the driver uses the internal clock (16.776 MHz) - * @vref_mv: the external reference voltage in millivolt - */ - -struct ad5933_platform_data { - unsigned long ext_clk_hz; - unsigned short vref_mv; -}; - struct ad5933_state { struct i2c_client *client; struct regulator *reg; + struct clk *mclk; struct delayed_work work; struct mutex lock; /* Protect sensor state */ unsigned long mclk_hz; @@ -112,10 +102,6 @@ struct ad5933_state { unsigned int poll_time_jiffies; }; -static struct ad5933_platform_data ad5933_default_pdata = { - .vref_mv = 3300, -}; - #define AD5933_CHANNEL(_type, _extend_name, _info_mask_separate, _address, \ _scan_index, _realbits) { \ .type = (_type), \ @@ -691,10 +677,10 @@ static void ad5933_work(struct work_struct *work) static int ad5933_probe(struct i2c_client *client, const struct i2c_device_id *id) { - int ret, voltage_uv = 0; - struct ad5933_platform_data *pdata = dev_get_platdata(&client->dev); + int ret; struct ad5933_state *st; struct iio_dev *indio_dev; + unsigned long ext_clk_hz = 0; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); if (!indio_dev) @@ -706,9 +692,6 @@ static int ad5933_probe(struct i2c_client *client, mutex_init(&st->lock); - if (!pdata) - pdata = &ad5933_default_pdata; - st->reg = devm_regulator_get(&client->dev, "vdd"); if (IS_ERR(st->reg)) return PTR_ERR(st->reg); @@ -718,15 +701,28 @@ static int ad5933_probe(struct i2c_client *client, dev_err(&client->dev, "Failed to enable specified VDD supply\n"); return ret; } - voltage_uv = regulator_get_voltage(st->reg); + ret = regulator_get_voltage(st->reg); - if (voltage_uv) - st->vref_mv = voltage_uv / 1000; - else - st->vref_mv = pdata->vref_mv; + if (ret < 0) + goto error_disable_reg; + + st->vref_mv = ret / 1000; + + st->mclk = devm_clk_get(&client->dev, "mclk"); + if (IS_ERR(st->mclk) && PTR_ERR(st->mclk) != -ENOENT) { + ret = PTR_ERR(st->mclk); + goto error_disable_reg; + } - if (pdata->ext_clk_hz) { - st->mclk_hz = pdata->ext_clk_hz; + if (!IS_ERR(st->mclk)) { + ret = clk_prepare_enable(st->mclk); + if (ret < 0) + goto error_disable_reg; + ext_clk_hz = clk_get_rate(st->mclk); + } + + if (ext_clk_hz) { + st->mclk_hz = ext_clk_hz; st->ctrl_lb = AD5933_CTRL_EXT_SYSCLK; } else { st->mclk_hz = AD5933_INT_OSC_FREQ_Hz; @@ -746,7 +742,7 @@ static int ad5933_probe(struct i2c_client *client, ret = ad5933_register_ring_funcs_and_init(indio_dev); if (ret) - goto error_disable_reg; + goto error_disable_mclk; ret = ad5933_setup(st); if (ret) @@ -760,6 +756,8 @@ static int ad5933_probe(struct i2c_client *client, error_unreg_ring: iio_kfifo_free(indio_dev->buffer); +error_disable_mclk: + clk_disable_unprepare(st->mclk); error_disable_reg: regulator_disable(st->reg); @@ -774,6 +772,7 @@ static int ad5933_remove(struct i2c_client *client) iio_device_unregister(indio_dev); iio_kfifo_free(indio_dev->buffer); regulator_disable(st->reg); + clk_disable_unprepare(st->mclk); return 0; } diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index 8092b8e7f37e..45e9667f0a8c 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -260,6 +260,7 @@ struct st_sensor_settings { struct st_sensor_data { struct device *dev; struct iio_trigger *trig; + struct iio_mount_matrix *mount_matrix; struct st_sensor_settings *sensor_settings; struct st_sensor_fullscale_avl *current_fullscale; struct regulator *vdd; diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index 92baabc103ac..fdd81affca4b 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -46,6 +46,7 @@ enum iio_chan_type { IIO_GRAVITY, IIO_POSITIONRELATIVE, IIO_PHASE, + IIO_MASSCONCENTRATION, }; enum iio_modifier { @@ -87,6 +88,12 @@ enum iio_modifier { IIO_MOD_VOC, IIO_MOD_LIGHT_UV, IIO_MOD_LIGHT_DUV, + IIO_MOD_PM1, + IIO_MOD_PM2P5, + IIO_MOD_PM4, + IIO_MOD_PM10, + IIO_MOD_ETHANOL, + IIO_MOD_H2, }; enum iio_event_type { diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index ac2de6b7e89f..7bf9bde28bcc 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -60,6 +60,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_GRAVITY] = "gravity", [IIO_POSITIONRELATIVE] = "positionrelative", [IIO_PHASE] = "phase", + [IIO_MASSCONCENTRATION] = "massconcentration", }; static const char * const iio_ev_type_text[] = { @@ -114,7 +115,13 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_I] = "i", [IIO_MOD_Q] = "q", [IIO_MOD_CO2] = "co2", + [IIO_MOD_ETHANOL] = "ethanol", + [IIO_MOD_H2] = "h2", [IIO_MOD_VOC] = "voc", + [IIO_MOD_PM1] = "pm1", + [IIO_MOD_PM2P5] = "pm2p5", + [IIO_MOD_PM4] = "pm4", + [IIO_MOD_PM10] = "pm10", }; static bool event_is_known(struct iio_event_data *event) @@ -156,6 +163,7 @@ static bool event_is_known(struct iio_event_data *event) case IIO_GRAVITY: case IIO_POSITIONRELATIVE: case IIO_PHASE: + case IIO_MASSCONCENTRATION: break; default: return false; @@ -199,7 +207,13 @@ static bool event_is_known(struct iio_event_data *event) case IIO_MOD_I: case IIO_MOD_Q: case IIO_MOD_CO2: + case IIO_MOD_ETHANOL: + case IIO_MOD_H2: case IIO_MOD_VOC: + case IIO_MOD_PM1: + case IIO_MOD_PM2P5: + case IIO_MOD_PM4: + case IIO_MOD_PM10: break; default: return false; |