diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2021-10-19 11:44:28 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2021-10-19 11:44:28 +0200 |
commit | 6bce28cb49320f403ab075ecdc2feb6cee24c416 (patch) | |
tree | bf4e29c34eac9e865d891086ef739e184188cd8c | |
parent | f5245a5fdf757f50a6c905fc16cceb1a6146ccf5 (diff) | |
parent | 0336d605daeea142c3ec9b143d0131a137f45108 (diff) | |
download | linux-stable-6bce28cb49320f403ab075ecdc2feb6cee24c416.tar.gz linux-stable-6bce28cb49320f403ab075ecdc2feb6cee24c416.tar.bz2 linux-stable-6bce28cb49320f403ab075ecdc2feb6cee24c416.zip |
Merge tag 'iio-for-5.16a-split-take4' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into char-misc-next
Jonathan writes:
First set of IIO new device and feature support for the 5.16 cycle
Counter subsystem changes now sent separately.
This has been a busy cycle, so lots here and a few more stragglers to
come next week.
Big new feature in this cycle is probably output buffer support.
This has been in the works for a very long time so it's great to see
Mihail pick up the challenge and build upon his predecessors work to finally
bring this feature to mainline.
New device support
------------------
* adi,adxl313
- New driver and dt bindings for this low power accelerometer.
* adi,adxl355
- New driver and dt bindings for this accelerometer.
- Later series adds buffer support.
* asahi-kasei,ak8975
- Minor additions to driver to support ak09916
* aspeed,aspeed-adc
- Substantial rework plus feature additions to add support for the
ast2600 including a new dt bindings doc.
* atmel,at91_sama5d2
- Rework and support introduced for the sama7g5 parts.
* maxim,max31865
- New driver and bindings for this RTD temperature sensor chip.
* nxp,imx8qxp
- New driver and bindings for the ADC found on the i.MX 8QuadXPlus Soc.
* senseair,sunrise
- New driver and bindings for this family of carbon dioxide gas sensors.
* sensiron,scd4x
- New driver and bindings for this carbon dioxide gas sensor.
New features
------------
* Output buffer support. Works in a similar fashion to input buffers, but
in this case userspace pushes data into the kfifo which is then drained
to the device when a trigger occurs. Support added to the ad5766 DAC
driver.
* Core, devm_iio_map_array_register() to avoid need for
devm_add_action_or_reset() based cleanup in fully managed allocation
drivers.
* Core iio_push_to_buffers_with_ts_unaligned() function to safely handle a
few drivers where it really hard to ensure the correct data alignment in
an iio_push_to_buffers_with_timestamp() call. Note this uses a bounce
buffer so should be avoided whenever possible. Used in the ti,adc108s102,
invense,mpu3050 and adi,adis16400. This closes the last known set
of drivers with alignment issues at this interface.
* maxim,max1027
- Substantial rework to this driver main target of which was supporting
use of other triggers than it's own EOC interrupt.
- Transfer optimization.
* nxp,fxls8962af
- Threshold even support including using it as a wakeup source.
Cleanups, minor fixes etc
-------------------------
Chances of a common type to multiple drivers:
* devm_ conversion and drop of .remove() callbacks in:
- adi,ad5064
- adi,ad7291
- adi,ad7303
- adi,ad7746
- adi,ad9832
- adi,adis16080
- dialog,da9150-gpadc
- intel,mrfld_adc
- marvell,berlin2
- maxim,max1363
- maxim,max44000
- nuvoton,nau7802
- st_sensors (includes a lot of rework!)
- ti,ads8344
- ti,lp8788
* devm_platform_ioremap_resource() used to reduce boilerplate
- cirrus,ep93xx
- rockchip,saradc
- stm,stm32-dac
* Use dev_err_probe() in more places to both not print on deferred probe and
ensure a reason for the deferral is available for debug purposes.
- adi,ad8801
- capella,cm36651
- linear,ltc1660
- maxim,ds4424
- maxim,max5821
- microchip,mcp4922
- nxp,lpc18xx
- onnn,noa1305
- st,lsm9ds0
- st,st_sensors
- st,stm32-dac
- ti,afe4403
- ti,afe4404
- ti,dac7311
* Drop error returns in SPI and I2C remove() functions as they are ignored and
long term plan is to change these all over to returning void. In some cases
these patches just make it 'obvious' they always return 0 where it was the
case before but not easy to tell.
- adi,ad5380
- adi,ad5446
- adi,ad5686
- adi,ad5592r
- bosch,bma400
- bosch,bmc150
- fsl,mma7455
- honeywell,hmc5843
- kionix,kxsd9
- maxim,max5487
- meas,ms5611
- ti,afe4403
Driver specific changes
* adi,ad5770r
- Bring driver inline with documented bindings.
* adi,ad7746
- Trivial style fix
* adi,ad7949
- Express some magic values as the underlying parts via new #defines.
- Make it work with SPI controllers that don't support 14 or 16 bit messages
- Support selection of voltage reference from dt including expanding the
dt-bindings to cover this new functionality.
* adi,ad799x
- Implement selection of external reference voltage on AD7991, AD7995 and
AD7999.
- Add missing dt-bindings doc for devices supported by this driver.
* adi,adislib
- Move interrupt startup to better location in startup flow.
- Handle devices that cannot mask/unmask the drdy pin and must instead mask
at the interrupt controller. Applies to the adis16460 and adis16475 from
which we then drop equivalent code.
* adi,ltc2983
- Add support for optional reset pin.
- Fail to probe if no channels specified in dt binding.
* asahi-kasei,ak8975
- dt-binding additions of missing vid-supply regulator.
* aspeed,aspeed-adc
- Typo fix.
* fsl,mma7660
- Mark acpi_device_id table __maybe_unused to avoid build warning.
* fsl,imx25-gcq
- Avoid initializing regulators that aren't used.
* invensense,mpu3050
- Drop a dead protection against a clash with the old input driver.
* invensense,mpu6050
- Rework code to not use strcpy() and hence avoid possibility of wrong sized
buffers. Note this wasn't a bug, but the new code is a lot more readable.
- Mark acpi_device_id table __maybe_unused to avoid build warning.
* kionix,kxcjk1013
- dt-binding addition to note it supports interrupts.
* marvell,berlin2-adc
- Enable COMPILE_TEST building.
* maxim,max1027
- Avoid returning success in an error path.
* nxp,imx8qxp
- Fix warning when runtime pm not enabled via __maybe_unused.
* ricoh,rn5t618
- Use the new devm_iio_map_array_register() instead of open coding the same.
* samsung,exynos_adc
- Improve kconfig help text.
* st,lsm6dsx
- Move max_fifo_size into the fifo_ops structure where the other configuration
parameters are found.
* st,st_sensors:
- Reorder to ensure we turn the power off after removing userspace interfaces.
* senseair,sunrise
- Add missing I2C dependency.
* ti,twl6030
- Small code tidy up.
* tag 'iio-for-5.16a-split-take4' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (148 commits)
iio: imx8qxp-adc: mark PM functions as __maybe_unused
iio: pressure: ms5611: Make ms5611_remove() return void
iio: potentiometer: max5487: Don't return an error in .remove()
iio: magn: hmc5843: Make hmc5843_common_remove() return void
iio: health: afe4403: Don't return an error in .remove()
iio: dac: ad5686: Make ad5686_remove() return void
iio: dac: ad5592r: Make ad5592r_remove() return void
iio: dac: ad5446: Make ad5446_remove() return void
iio: dac: ad5380: Make ad5380_remove() return void
iio: accel: mma7455: Make mma7455_core_remove() return void
iio: accel: kxsd9: Make kxsd9_common_remove() return void
iio: accel: bmi088: Make bmi088_accel_core_remove() return void
iio: accel: bmc150: Make bmc150_accel_core_remove() return void
iio: accel: bma400: Make bma400_remove() return void
drivers:iio:dac:ad5766.c: Add trigger buffer
iio: triggered-buffer: extend support to configure output buffers
iio: kfifo-buffer: Add output buffer support
iio: Add output buffer support
iio: documentation: Document scd4x calibration use
drivers: iio: chemical: Add support for Sensirion SCD4x CO2 sensor
...
168 files changed, 6998 insertions, 1592 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 6ad47a67521c..c551301b33f1 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -429,6 +429,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_angl_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_x_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_y_scale What: /sys/bus/iio/devices/iio:deviceX/in_intensity_z_scale +What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_scale KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -1957,3 +1958,44 @@ Description: Specify the percent for light sensor relative to the channel absolute value that a data field should change before an event is generated. Units are a percentage of the prior reading. + +What: /sys/bus/iio/devices/iio:deviceX/calibration_auto_enable +Date: June 2020 +KernelVersion: 5.8 +Contact: linux-iio@vger.kernel.org +Description: + Some sensors have the ability to apply auto calibration at + runtime. For example, it may be necessary to compensate for + contaminant build-up in a measurement chamber or optical + element deterioration that would otherwise lead to sensor drift. + + Writing 1 or 0 to this attribute will respectively activate or + deactivate this auto calibration function. + + Upon reading, the current status is returned. + +What: /sys/bus/iio/devices/iio:deviceX/calibration_forced_value +Date: June 2020 +KernelVersion: 5.8 +Contact: linux-iio@vger.kernel.org +Description: + Some sensors have the ability to apply a manual calibration using + a known measurement value, perhaps obtained from an external + reference device. + + Writing a value to this function will force such a calibration + change. For the scd30 the value should be from the range + [400 1 2000]. + + Note for the scd30 that a valid value may only be obtained once + it is has been written. Until then any read back of this value + should be ignored. As for the scd4x an error will be returned + immediately if the manual calibration has failed. + +What: /sys/bus/iio/devices/iio:deviceX/calibration_forced_value_available +KernelVersion: 5.15 +Contact: linux-iio@vger.kernel.org +Description: + Available range for the forced calibration value, expressed as: + + - a range specified as "[min step max]" diff --git a/Documentation/ABI/testing/sysfs-bus-iio-chemical-sunrise-co2 b/Documentation/ABI/testing/sysfs-bus-iio-chemical-sunrise-co2 new file mode 100644 index 000000000000..ee7aeb11709b --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-chemical-sunrise-co2 @@ -0,0 +1,38 @@ +What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_calibration_factory +Date: August 2021 +KernelVersion: 5.16 +Contact: Jacopo Mondi <jacopo@jmondi.org> +Description: + Writing '1' triggers a 'Factory' calibration cycle. + +What: /sys/bus/iio/devices/iio:deviceX/in_concentration_co2_calibration_background +Date: August 2021 +KernelVersion: 5.16 +Contact: Jacopo Mondi <jacopo@jmondi.org> +Description: + Writing '1' triggers a 'Background' calibration cycle. + +What: /sys/bus/iio/devices/iio:deviceX/error_status_available +Date: August 2021 +KernelVersion: 5.16 +Contact: Jacopo Mondi <jacopo@jmondi.org> +Description: + Reading returns the list of possible chip error status. + Available options are: + - 'error_fatal': Analog front-end initialization error + - 'error_i2c': Read/write to non-existing register + - 'error_algorithm': Corrupted parameters + - 'error_calibration': Calibration has failed + - 'error_self_diagnostic': Internal interface failure + - 'error_out_of_range': Measured concentration out of scale + - 'error_memory': Error during memory operations + - 'error_no_measurement': Cleared at first measurement + - 'error_low_voltage': Sensor regulated voltage too low + - 'error_measurement_timeout': Unable to complete measurement + +What: /sys/bus/iio/devices/iio:deviceX/error_status +Date: August 2021 +KernelVersion: 5.16 +Contact: Jacopo Mondi <jacopo@jmondi.org> +Description: + Reading returns the current chip error status. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-scd30 b/Documentation/ABI/testing/sysfs-bus-iio-scd30 deleted file mode 100644 index b9712f390bec..000000000000 --- a/Documentation/ABI/testing/sysfs-bus-iio-scd30 +++ /dev/null @@ -1,34 +0,0 @@ -What: /sys/bus/iio/devices/iio:deviceX/calibration_auto_enable -Date: June 2020 -KernelVersion: 5.8 -Contact: linux-iio@vger.kernel.org -Description: - Contaminants build-up in the measurement chamber or optical - elements deterioration leads to sensor drift. - - One can compensate for sensor drift by using automatic self - calibration procedure (asc). - - Writing 1 or 0 to this attribute will respectively activate or - deactivate asc. - - Upon reading current asc status is returned. - -What: /sys/bus/iio/devices/iio:deviceX/calibration_forced_value -Date: June 2020 -KernelVersion: 5.8 -Contact: linux-iio@vger.kernel.org -Description: - Contaminants build-up in the measurement chamber or optical - elements deterioration leads to sensor drift. - - One can compensate for sensor drift by using forced - recalibration (frc). This is useful in case there's known - co2 reference available nearby the sensor. - - Picking value from the range [400 1 2000] and writing it to the - sensor will set frc. - - Upon reading current frc value is returned. Note that after - power cycling default value (i.e 400) is returned even though - internally sensor had recalibrated itself. diff --git a/Documentation/ABI/testing/sysfs-bus-iio-temperature-max31865 b/Documentation/ABI/testing/sysfs-bus-iio-temperature-max31865 new file mode 100644 index 000000000000..4b072da92218 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-temperature-max31865 @@ -0,0 +1,20 @@ +What: /sys/bus/iio/devices/iio:deviceX/fault_ovuv +KernelVersion: 5.11 +Contact: linux-iio@vger.kernel.org +Description: + Overvoltage or Undervoltage Input fault. The internal circuitry + is protected from excessive voltages applied to the thermocouple + cables at FORCE+, FORCE2, RTDIN+ & RTDIN-. This circuitry turn + off when the input voltage is negative or greater than VDD. + + Reading returns '1' if input voltage is negative or greater + than VDD, otherwise '0'. + +What: /sys/bus/iio/devices/iio:deviceX/in_filter_notch_center_frequency +KernelVersion: 5.11 +Contact: linux-iio@vger.kernel.org +Description: + Notch frequency in Hz for a noise rejection filter. Used i.e for + line noise rejection. + + Valid notch filter values are 50 Hz and 60 Hz. diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml new file mode 100644 index 000000000000..d6afc1b8c272 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/accel/adi,adxl313.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADXL313 3-Axis Digital Accelerometer + +maintainers: + - Lucas Stankus <lucas.p.stankus@gmail.com> + +description: | + Analog Devices ADXL313 3-Axis Digital Accelerometer that supports + both I2C & SPI interfaces. + https://www.analog.com/en/products/adxl313.html + +properties: + compatible: + enum: + - adi,adxl313 + + reg: + maxItems: 1 + + spi-3wire: true + + spi-max-frequency: true + + vs-supply: + description: Regulator that supplies power to the accelerometer + + vdd-supply: + description: Regulator that supplies the digital interface supply voltage + + interrupts: + minItems: 1 + maxItems: 2 + + interrupt-names: + minItems: 1 + maxItems: 2 + items: + enum: + - INT1 + - INT2 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + /* Example for a I2C device node */ + accelerometer@53 { + compatible = "adi,adxl313"; + reg = <0x53>; + interrupt-parent = <&gpio0>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "INT1"; + }; + }; + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + + /* Example for a SPI device node */ + accelerometer@0 { + compatible = "adi,adxl313"; + reg = <0>; + spi-max-frequency = <5000000>; + interrupt-parent = <&gpio0>; + interrupts = <0 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "INT1"; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml b/Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml new file mode 100644 index 000000000000..ba54d6998f2e --- /dev/null +++ b/Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml @@ -0,0 +1,88 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/accel/adi,adxl355.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADXL355 3-Axis, Low noise MEMS Accelerometer + +maintainers: + - Puranjay Mohan <puranjay12@gmail.com> + +description: | + Analog Devices ADXL355 3-Axis, Low noise MEMS Accelerometer that supports + both I2C & SPI interfaces + https://www.analog.com/en/products/adxl355.html + +properties: + compatible: + enum: + - adi,adxl355 + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 3 + description: | + Type for DRDY should be IRQ_TYPE_EDGE_RISING. + Three configurable interrupt lines exist. + + interrupt-names: + description: Specify which interrupt line is in use. + items: + enum: + - INT1 + - INT2 + - DRDY + minItems: 1 + maxItems: 3 + + vdd-supply: + description: Regulator that provides power to the sensor + + vddio-supply: + description: Regulator that provides power to the bus + + spi-max-frequency: true + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + + /* Example for a I2C device node */ + accelerometer@1d { + compatible = "adi,adxl355"; + reg = <0x1d>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "DRDY"; + }; + }; + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + spi { + #address-cells = <1>; + #size-cells = <0>; + + accelerometer@0 { + compatible = "adi,adxl355"; + reg = <0>; + spi-max-frequency = <1000000>; + interrupt-parent = <&gpio>; + interrupts = <25 IRQ_TYPE_EDGE_RISING>; + interrupt-names = "DRDY"; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml b/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml index 52fa0f7c2d0e..714e48e613de 100644 --- a/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml +++ b/Documentation/devicetree/bindings/iio/accel/kionix,kxcjk1013.yaml @@ -21,6 +21,9 @@ properties: reg: maxItems: 1 + interrupts: + maxItems: 1 + vdd-supply: true vddio-supply: true diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml index 9b56bd4d5510..0b10ed5f74ae 100644 --- a/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad7949.yaml @@ -26,19 +26,43 @@ properties: reg: maxItems: 1 + vrefin-supply: + description: + Buffered ADC reference voltage supply. + vref-supply: description: - ADC reference voltage supply + Unbuffered ADC reference voltage supply. + + adi,internal-ref-microvolt: + description: | + Internal reference voltage selection in microvolts. + + If no internal reference is specified, the channel will default to the + external reference defined by vrefin-supply (or vref-supply). + vrefin-supply will take precedence over vref-supply if both are defined. + + If no supplies are defined, the reference selection will default to + 4096mV internal reference. + + enum: [2500000, 4096000] + default: 4096000 + spi-max-frequency: true - "#io-channel-cells": + '#io-channel-cells': const: 1 + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + required: - compatible - reg - - vref-supply additionalProperties: false @@ -49,9 +73,30 @@ examples: #size-cells = <0>; adc@0 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "adi,ad7949"; reg = <0>; vref-supply = <&vdd_supply>; }; + + adc@1 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "adi,ad7949"; + reg = <1>; + vrefin-supply = <&vdd_supply>; + }; + + adc@2 { + #address-cells = <1>; + #size-cells = <0>; + + compatible = "adi,ad7949"; + reg = <2>; + adi,internal-ref-microvolt = <4096000>; + }; }; ... diff --git a/Documentation/devicetree/bindings/iio/adc/adi,ad799x.yaml b/Documentation/devicetree/bindings/iio/adc/adi,ad799x.yaml new file mode 100644 index 000000000000..29641ce7175b --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/adi,ad799x.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/adi,ad799x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD799x analog to digital converters + +maintainers: + - Michael Hennerich <Michael.Hennerich@analog.com> + +description: | + Support for Analog Devices AD7991, AD7992, AD7993, AD7994, AD7995, AD7997, AD7998, + AD7999 and similar analog to digital converters. + Specifications on the converters can be found at: + AD7991, AD7995, AD7999: + https://www.analog.com/media/en/technical-documentation/data-sheets/AD7991_7995_7999.pdf + AD7992: + https://www.analog.com/media/en/technical-documentation/data-sheets/AD7992.pdf + AD7993, AD7994: + https://www.analog.com/media/en/technical-documentation/data-sheets/AD7993_7994.pdf + AD7997, AD7998: + https://www.analog.com/media/en/technical-documentation/data-sheets/AD7997_7998.pdf + +properties: + compatible: + enum: + - adi,ad7991 + - adi,ad7992 + - adi,ad7993 + - adi,ad7994 + - adi,ad7995 + - adi,ad7997 + - adi,ad7998 + - adi,ad7999 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vcc-supply: + description: + ADC power supply + + vref-supply: + description: + ADC reference voltage supply, optional for AD7991, AD7995 and AD7999 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + adc1: adc@28 { + reg = <0x28>; + compatible = "adi,ad7991"; + interrupts = <13 2>; + interrupt-parent = <&gpio6>; + + vcc-supply = <&vcc_3v3>; + vref-supply = <&adc_vref>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml new file mode 100644 index 000000000000..b283c8ca2bbf --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/aspeed,ast2600-adc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ADC that forms part of an ASPEED server management processor. + +maintainers: + - Billy Tsai <billy_tsai@aspeedtech.com> + +description: | + • 10-bits resolution for 16 voltage channels. + • The device split into two individual engine and each contains 8 voltage + channels. + • Channel scanning can be non-continuous. + • Programmable ADC clock frequency. + • Programmable upper and lower threshold for each channels. + • Interrupt when larger or less than threshold for each channels. + • Support hysteresis for each channels. + • Built-in a compensating method. + • Built-in a register to trim internal reference voltage. + • Internal or External reference voltage. + • Support 2 Internal reference voltage 1.2v or 2.5v. + • Integrate dividing circuit for battery sensing. + +properties: + compatible: + enum: + - aspeed,ast2600-adc0 + - aspeed,ast2600-adc1 + description: + Their trimming data, which is used to calibrate internal reference volage, + locates in different address of OTP. + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + description: + Input clock used to derive the sample clock. Expected to be the + SoC's APB clock. + + resets: + maxItems: 1 + + "#io-channel-cells": + const: 1 + + vref-supply: + description: + The external regulator supply ADC reference voltage. + + aspeed,int-vref-microvolt: + enum: [1200000, 2500000] + description: + ADC internal reference voltage in microvolts. + + aspeed,battery-sensing: + type: boolean + description: + Inform the driver that last channel will be used to sensor battery. + + aspeed,trim-data-valid: + type: boolean + description: | + The ADC reference voltage can be calibrated to obtain the trimming + data which will be stored in otp. This property informs the driver that + the data store in the otp is valid. + +required: + - compatible + - reg + - clocks + - resets + - "#io-channel-cells" + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/ast2600-clock.h> + adc0: adc@1e6e9000 { + compatible = "aspeed,ast2600-adc0"; + reg = <0x1e6e9000 0x100>; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_ADC>; + #io-channel-cells = <1>; + aspeed,int-vref-microvolt = <2500000>; + }; + adc1: adc@1e6e9100 { + compatible = "aspeed,ast2600-adc1"; + reg = <0x1e6e9100 0x100>; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_ADC>; + #io-channel-cells = <1>; + aspeed,int-vref-microvolt = <2500000>; + }; +... diff --git a/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml b/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml index 79c13b408eda..efed361215b4 100644 --- a/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/atmel,sama5d2-adc.yaml @@ -15,6 +15,7 @@ properties: enum: - atmel,sama5d2-adc - microchip,sam9x60-adc + - microchip,sama7g5-adc reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/iio/adc/nxp,imx8qxp-adc.yaml b/Documentation/devicetree/bindings/iio/adc/nxp,imx8qxp-adc.yaml new file mode 100644 index 000000000000..9c59a20a6032 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/nxp,imx8qxp-adc.yaml @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/nxp,imx8qxp-adc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP IMX8QXP ADC bindings + +maintainers: + - Cai Huoqing <caihuoqing@baidu.com> + +description: + Supports the ADC found on the IMX8QXP SoC. + +properties: + compatible: + const: nxp,imx8qxp-adc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: per + - const: ipg + + assigned-clocks: + maxItems: 1 + + assigned-clock-rates: + maxItems: 1 + + power-domains: + maxItems: 1 + + "#io-channel-cells": + const: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - assigned-clocks + - assigned-clock-rates + - power-domains + - "#io-channel-cells" + +additionalProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/firmware/imx/rsrc.h> + soc { + #address-cells = <2>; + #size-cells = <2>; + adc@5a880000 { + compatible = "nxp,imx8qxp-adc"; + reg = <0x0 0x5a880000 0x0 0x10000>; + interrupts = <GIC_SPI 240 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk IMX_SC_R_ADC_0>, + <&clk IMX_SC_R_ADC_0>; + clock-names = "per", "ipg"; + assigned-clocks = <&clk IMX_SC_R_ADC_0>; + assigned-clock-rates = <24000000>; + power-domains = <&pd IMX_SC_R_ADC_0>; + #io-channel-cells = <1>; + }; + }; +... diff --git a/Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml b/Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml new file mode 100644 index 000000000000..337fe09e4bb8 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml @@ -0,0 +1,55 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/chemical/senseair,sunrise.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Senseair Sunrise 006-0-0007 CO2 Sensor + +maintainers: + - Jacopo Mondi <jacopo@jmondi.org> + +description: | + Senseair Sunrise 006-0-0007 is a NDIR CO2 sensor. It supports I2C or UART buses + for communications and control. + + Datasheets: + https://rmtplusstoragesenseair.blob.core.windows.net/docs/Dev/publicerat/PSP11704.pdf + https://rmtplusstoragesenseair.blob.core.windows.net/docs/Dev/publicerat/PSH11649.pdf + https://rmtplusstoragesenseair.blob.core.windows.net/docs/Dev/publicerat/TDE5531.pdf + https://rmtplusstoragesenseair.blob.core.windows.net/docs/Market/publicerat/TDE7318.pdf + +properties: + compatible: + const: senseair,sunrise-006-0-0007 + + reg: + maxItems: 1 + + ndry-gpios: + maxItems: 1 + description: + Phandle to the GPIO line connected to the nDRY pin. Typically active low. + + en-gpios: + maxItems: 1 + description: + Phandle to the GPIO line connected to the EN pin. Typically active high. + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + co2-sensor@68 { + compatible = "senseair,sunrise-006-0-0007"; + reg = <0x68>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/chemical/sensirion,scd4x.yaml b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd4x.yaml new file mode 100644 index 000000000000..798f48d05279 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/chemical/sensirion,scd4x.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/chemical/sensirion,scd4x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sensirion SCD4X carbon dioxide sensor + +maintainers: + - Roan van Dijk <roan@protonic.nl> + +description: | + Air quality sensor capable of measuring co2 concentration, temperature + and relative humidity. + +properties: + compatible: + enum: + - sensirion,scd40 + - sensirion,scd41 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + vdd-supply: true + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + co2-sensor@62 { + compatible = "sensirion,scd41"; + reg = <0x62>; + }; + }; diff --git a/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml b/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml index a0a1ffe017df..9790f75fc669 100644 --- a/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml +++ b/Documentation/devicetree/bindings/iio/magnetometer/asahi-kasei,ak8975.yaml @@ -17,11 +17,13 @@ properties: - asahi-kasei,ak8963 - asahi-kasei,ak09911 - asahi-kasei,ak09912 + - asahi-kasei,ak09916 - enum: - ak8975 - ak8963 - ak09911 - ak09912 + - ak09916 deprecated: true reg: @@ -43,6 +45,11 @@ properties: an optional regulator that needs to be on to provide VDD power to the sensor. + vid-supply: + description: | + an optional regulator that needs to be on to provide VID power to + the sensor. + mount-matrix: description: an optional 3x3 mounting rotation matrix. diff --git a/Documentation/devicetree/bindings/iio/temperature/maxim,max31865.yaml b/Documentation/devicetree/bindings/iio/temperature/maxim,max31865.yaml new file mode 100644 index 000000000000..aafb33b16549 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/temperature/maxim,max31865.yaml @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/temperature/maxim,max31865.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Maxim MAX31865 Resistance Temperature Detector. + +maintainers: + - Navin Sankar Velliangiri <navin@linumiz.com> + +description: | + https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf + +properties: + compatible: + const: maxim,max31865 + + reg: + maxItems: 1 + + maxim,3-wire: + description: + Identifies the number of wires used by the RTD. Setting this property + enables 3-wire RTD connection. Else 2-wire or 4-wire RTD connection. + type: boolean + + spi-max-frequency: true + spi-cpha: true + +required: + - compatible + - reg + - spi-cpha + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + + temp_sensor@0 { + compatible = "maxim,max31865"; + reg = <0>; + spi-max-frequency = <400000>; + spi-cpha; + maxim,3-wire; + }; + }; +... diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index a867f7102c35..42b529a8e5db 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1018,6 +1018,8 @@ patternProperties: description: Shenzhen SEI Robotics Co., Ltd "^semtech,.*": description: Semtech Corporation + "^senseair,.*": + description: Senseair AB "^sensirion,.*": description: Sensirion AG "^sensortek,.*": diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 650096523f4f..148e19381b79 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -287,6 +287,7 @@ IIO devm_iio_device_register() devm_iio_dmaengine_buffer_setup() devm_iio_kfifo_buffer_setup() + devm_iio_map_array_register() devm_iio_triggered_buffer_setup() devm_iio_trigger_alloc() devm_iio_trigger_register() diff --git a/MAINTAINERS b/MAINTAINERS index efa61ac936d3..8f414775978a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -591,6 +591,12 @@ L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/adv_swbutton.c +ADXL313 THREE-AXIS DIGITAL ACCELEROMETER DRIVER +M: Lucas Stankus <lucas.p.stankus@gmail.com> +S: Supported +F: Documentation/devicetree/bindings/iio/accel/adi,adxl313.yaml +F: drivers/iio/accel/adxl313* + ADXL34X THREE-AXIS DIGITAL ACCELEROMETER DRIVER (ADXL345/ADXL346) M: Michael Hennerich <michael.hennerich@analog.com> S: Supported @@ -599,6 +605,16 @@ W: http://ez.analog.com/community/linux-device-drivers F: Documentation/devicetree/bindings/iio/accel/adi,adxl345.yaml F: drivers/input/misc/adxl34x.c +ADXL355 THREE-AXIS DIGITAL ACCELEROMETER DRIVER +M: Puranjay Mohan <puranjay12@gmail.com> +L: linux-iio@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/iio/accel/adi,adxl355.yaml +F: drivers/iio/accel/adxl355.h +F: drivers/iio/accel/adxl355_core.c +F: drivers/iio/accel/adxl355_i2c.c +F: drivers/iio/accel/adxl355_spi.c + ADXL372 THREE-AXIS DIGITAL ACCELEROMETER DRIVER M: Michael Hennerich <michael.hennerich@analog.com> S: Supported @@ -13472,6 +13488,13 @@ S: Maintained F: Documentation/devicetree/bindings/display/imx/nxp,imx8mq-dcss.yaml F: drivers/gpu/drm/imx/dcss/ +NXP i.MX 8QXP ADC DRIVER +M: Cai Huoqing <caihuoqing@baidu.com> +L: linux-iio@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/iio/adc/nxp,imx8qxp-adc.yaml +F: drivers/iio/adc/imx8qxp-adc.c + NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER M: Jagan Teki <jagan@amarulasolutions.com> S: Maintained @@ -16872,6 +16895,13 @@ S: Maintained F: drivers/misc/phantom.c F: include/uapi/linux/phantom.h +SENSEAIR SUNRISE 006-0-0007 +M: Jacopo Mondi <jacopo@jmondi.org> +S: Maintained +F: Documentation/ABI/testing/sysfs-bus-iio-chemical-sunrise-co2 +F: Documentation/devicetree/bindings/iio/chemical/senseair,sunrise.yaml +F: drivers/iio/chemical/sunrise_co2.c + SENSIRION SCD30 CARBON DIOXIDE SENSOR DRIVER M: Tomasz Duszynski <tomasz.duszynski@octakon.com> S: Maintained @@ -16881,6 +16911,12 @@ F: drivers/iio/chemical/scd30_core.c F: drivers/iio/chemical/scd30_i2c.c F: drivers/iio/chemical/scd30_serial.c +SENSIRION SCD4X CARBON DIOXIDE SENSOR DRIVER +M: Roan van Dijk <roan@protonic.nl> +S: Maintained +F: Documentation/devicetree/bindings/iio/chemical/sensirion,scd4x.yaml +F: drivers/iio/chemical/scd4x.c + SENSIRION SGP40 GAS SENSOR DRIVER M: Andreas Klinger <ak@it-klinger.de> S: Maintained diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index a0e9061f6d6b..49587c992a6d 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -30,6 +30,35 @@ config ADIS16209 To compile this driver as a module, say M here: the module will be called adis16209. +config ADXL313 + tristate + +config ADXL313_I2C + tristate "Analog Devices ADXL313 3-Axis Digital Accelerometer I2C Driver" + depends on I2C + select ADXL313 + select REGMAP_I2C + help + Say Y here if you want to build i2c support for the Analog Devices + ADXL313 3-axis digital accelerometer. + + To compile this driver as a module, choose M here: the module + will be called adxl313_i2c and you will also get adxl313_core + for the core module. + +config ADXL313_SPI + tristate "Analog Devices ADXL313 3-Axis Digital Accelerometer SPI Driver" + depends on SPI + select ADXL313 + select REGMAP_SPI + help + Say Y here if you want to build spi support for the Analog Devices + ADXL313 3-axis digital accelerometer. + + To compile this driver as a module, choose M here: the module + will be called adxl313_spi and you will also get adxl313_core + for the core module. + config ADXL345 tristate @@ -61,6 +90,39 @@ config ADXL345_SPI will be called adxl345_spi and you will also get adxl345_core for the core module. +config ADXL355 + tristate + +config ADXL355_I2C + tristate "Analog Devices ADXL355 3-Axis Digital Accelerometer I2C Driver" + depends on I2C + select ADXL355 + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say Y here if you want to build i2c support for the Analog Devices + ADXL355 3-axis digital accelerometer. + + To compile this driver as a module, choose M here: the module + will be called adxl355_i2c and you will also get adxl355_core + for the core module. + +config ADXL355_SPI + tristate "Analog Devices ADXL355 3-Axis Digital Accelerometer SPI Driver" + depends on SPI + select ADXL355 + select REGMAP_SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say Y here if you want to build spi support for the Analog Devices + ADXL355 3-axis digital accelerometer. + + To compile this driver as a module, choose M here: the module + will be called adxl355_spi and you will also get adxl355_core + for the core module. + config ADXL372 tristate select IIO_BUFFER diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 89280e823bcd..d03e2f6bba08 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -6,9 +6,15 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_ADIS16201) += adis16201.o obj-$(CONFIG_ADIS16209) += adis16209.o +obj-$(CONFIG_ADXL313) += adxl313_core.o +obj-$(CONFIG_ADXL313_I2C) += adxl313_i2c.o +obj-$(CONFIG_ADXL313_SPI) += adxl313_spi.o obj-$(CONFIG_ADXL345) += adxl345_core.o obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o +obj-$(CONFIG_ADXL355) += adxl355_core.o +obj-$(CONFIG_ADXL355_I2C) += adxl355_i2c.o +obj-$(CONFIG_ADXL355_SPI) += adxl355_spi.o obj-$(CONFIG_ADXL372) += adxl372.o obj-$(CONFIG_ADXL372_I2C) += adxl372_i2c.o obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h new file mode 100644 index 000000000000..4415f2fc07e1 --- /dev/null +++ b/drivers/iio/accel/adxl313.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ADXL313 3-Axis Digital Accelerometer + * + * Copyright (c) 2021 Lucas Stankus <lucas.p.stankus@gmail.com> + */ + +#ifndef _ADXL313_H_ +#define _ADXL313_H_ + +/* ADXL313 register definitions */ +#define ADXL313_REG_DEVID0 0x00 +#define ADXL313_REG_DEVID1 0x01 +#define ADXL313_REG_PARTID 0x02 +#define ADXL313_REG_XID 0x04 +#define ADXL313_REG_SOFT_RESET 0x18 +#define ADXL313_REG_OFS_AXIS(index) (0x1E + (index)) +#define ADXL313_REG_THRESH_ACT 0x24 +#define ADXL313_REG_ACT_INACT_CTL 0x27 +#define ADXL313_REG_BW_RATE 0x2C +#define ADXL313_REG_POWER_CTL 0x2D +#define ADXL313_REG_INT_MAP 0x2F +#define ADXL313_REG_DATA_FORMAT 0x31 +#define ADXL313_REG_DATA_AXIS(index) (0x32 + ((index) * 2)) +#define ADXL313_REG_FIFO_CTL 0x38 +#define ADXL313_REG_FIFO_STATUS 0x39 + +#define ADXL313_DEVID0 0xAD +#define ADXL313_DEVID1 0x1D +#define ADXL313_PARTID 0xCB +#define ADXL313_SOFT_RESET 0x52 + +#define ADXL313_RATE_MSK GENMASK(3, 0) +#define ADXL313_RATE_BASE 6 + +#define ADXL313_POWER_CTL_MSK GENMASK(3, 2) +#define ADXL313_MEASUREMENT_MODE BIT(3) + +#define ADXL313_RANGE_MSK GENMASK(1, 0) +#define ADXL313_RANGE_4G 3 + +#define ADXL313_FULL_RES BIT(3) +#define ADXL313_SPI_3WIRE BIT(6) +#define ADXL313_I2C_DISABLE BIT(6) + +extern const struct regmap_access_table adxl313_readable_regs_table; + +extern const struct regmap_access_table adxl313_writable_regs_table; + +int adxl313_core_probe(struct device *dev, + struct regmap *regmap, + const char *name, + int (*setup)(struct device *, struct regmap *)); +#endif /* _ADXL313_H_ */ diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c new file mode 100644 index 000000000000..0d243341f1a7 --- /dev/null +++ b/drivers/iio/accel/adxl313_core.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL313 3-Axis Digital Accelerometer + * + * Copyright (c) 2021 Lucas Stankus <lucas.p.stankus@gmail.com> + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL313.pdf + */ + +#include <linux/bitfield.h> +#include <linux/iio/iio.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "adxl313.h" + +static const struct regmap_range adxl313_readable_reg_range[] = { + regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_XID), + regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET), + regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)), + regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL), + regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS), +}; + +const struct regmap_access_table adxl313_readable_regs_table = { + .yes_ranges = adxl313_readable_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl313_readable_reg_range), +}; +EXPORT_SYMBOL_GPL(adxl313_readable_regs_table); + +static const struct regmap_range adxl313_writable_reg_range[] = { + regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET), + regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)), + regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL), + regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_INT_MAP), + regmap_reg_range(ADXL313_REG_DATA_FORMAT, ADXL313_REG_DATA_FORMAT), + regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL), +}; + +const struct regmap_access_table adxl313_writable_regs_table = { + .yes_ranges = adxl313_writable_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl313_writable_reg_range), +}; +EXPORT_SYMBOL_GPL(adxl313_writable_regs_table); + +struct adxl313_data { + struct regmap *regmap; + struct mutex lock; /* lock to protect transf_buf */ + __le16 transf_buf ____cacheline_aligned; +}; + +static const int adxl313_odr_freqs[][2] = { + [0] = { 6, 250000 }, + [1] = { 12, 500000 }, + [2] = { 25, 0 }, + [3] = { 50, 0 }, + [4] = { 100, 0 }, + [5] = { 200, 0 }, + [6] = { 400, 0 }, + [7] = { 800, 0 }, + [8] = { 1600, 0 }, + [9] = { 3200, 0 }, +}; + +#define ADXL313_ACCEL_CHANNEL(index, axis) { \ + .type = IIO_ACCEL, \ + .address = index, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_type = { \ + .realbits = 13, \ + }, \ +} + +static const struct iio_chan_spec adxl313_channels[] = { + ADXL313_ACCEL_CHANNEL(0, X), + ADXL313_ACCEL_CHANNEL(1, Y), + ADXL313_ACCEL_CHANNEL(2, Z), +}; + +static int adxl313_set_odr(struct adxl313_data *data, + unsigned int freq1, unsigned int freq2) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(adxl313_odr_freqs); i++) { + if (adxl313_odr_freqs[i][0] == freq1 && + adxl313_odr_freqs[i][1] == freq2) + break; + } + + if (i == ARRAY_SIZE(adxl313_odr_freqs)) + return -EINVAL; + + return regmap_update_bits(data->regmap, ADXL313_REG_BW_RATE, + ADXL313_RATE_MSK, + FIELD_PREP(ADXL313_RATE_MSK, ADXL313_RATE_BASE + i)); +} + +static int adxl313_read_axis(struct adxl313_data *data, + struct iio_chan_spec const *chan) +{ + int ret; + + mutex_lock(&data->lock); + + ret = regmap_bulk_read(data->regmap, + ADXL313_REG_DATA_AXIS(chan->address), + &data->transf_buf, sizeof(data->transf_buf)); + if (ret) + goto unlock_ret; + + ret = le16_to_cpu(data->transf_buf); + +unlock_ret: + mutex_unlock(&data->lock); + return ret; +} + +static int adxl313_read_freq_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (const int *)adxl313_odr_freqs; + *length = ARRAY_SIZE(adxl313_odr_freqs) * 2; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int adxl313_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct adxl313_data *data = iio_priv(indio_dev); + unsigned int regval; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = adxl313_read_axis(data, chan); + if (ret < 0) + return ret; + + *val = sign_extend32(ret, chan->scan_type.realbits - 1); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* + * Scale for any g range is given in datasheet as + * 1024 LSB/g = 0.0009765625 * 9.80665 = 0.009576806640625 m/s^2 + */ + *val = 0; + *val2 = 9576806; + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_CALIBBIAS: + ret = regmap_read(data->regmap, + ADXL313_REG_OFS_AXIS(chan->address), ®val); + if (ret) + return ret; + + /* + * 8-bit resolution at +/- 0.5g, that is 4x accel data scale + * factor at full resolution + */ + *val = sign_extend32(regval, 7) * 4; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = regmap_read(data->regmap, ADXL313_REG_BW_RATE, ®val); + if (ret) + return ret; + + ret = FIELD_GET(ADXL313_RATE_MSK, regval) - ADXL313_RATE_BASE; + *val = adxl313_odr_freqs[ret][0]; + *val2 = adxl313_odr_freqs[ret][1]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int adxl313_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct adxl313_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + /* + * 8-bit resolution at +/- 0.5g, that is 4x accel data scale + * factor at full resolution + */ + if (clamp_val(val, -128 * 4, 127 * 4) != val) + return -EINVAL; + + return regmap_write(data->regmap, + ADXL313_REG_OFS_AXIS(chan->address), + val / 4); + case IIO_CHAN_INFO_SAMP_FREQ: + return adxl313_set_odr(data, val, val2); + default: + return -EINVAL; + } +} + +static const struct iio_info adxl313_info = { + .read_raw = adxl313_read_raw, + .write_raw = adxl313_write_raw, + .read_avail = adxl313_read_freq_avail, +}; + +static int adxl313_setup(struct device *dev, struct adxl313_data *data, + int (*setup)(struct device *, struct regmap *)) +{ + unsigned int regval; + int ret; + + /* Ensures the device is in a consistent state after start up */ + ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET, + ADXL313_SOFT_RESET); + if (ret) + return ret; + + if (setup) { + ret = setup(dev, data->regmap); + if (ret) + return ret; + } + + ret = regmap_read(data->regmap, ADXL313_REG_DEVID0, ®val); + if (ret) + return ret; + + if (regval != ADXL313_DEVID0) { + dev_err(dev, "Invalid manufacturer ID: 0x%02x\n", regval); + return -ENODEV; + } + + ret = regmap_read(data->regmap, ADXL313_REG_DEVID1, ®val); + if (ret) + return ret; + + if (regval != ADXL313_DEVID1) { + dev_err(dev, "Invalid mems ID: 0x%02x\n", regval); + return -ENODEV; + } + + ret = regmap_read(data->regmap, ADXL313_REG_PARTID, ®val); + if (ret) + return ret; + + if (regval != ADXL313_PARTID) { + dev_err(dev, "Invalid device ID: 0x%02x\n", regval); + return -ENODEV; + } + + /* Sets the range to +/- 4g */ + ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT, + ADXL313_RANGE_MSK, + FIELD_PREP(ADXL313_RANGE_MSK, ADXL313_RANGE_4G)); + if (ret) + return ret; + + /* Enables full resolution */ + ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT, + ADXL313_FULL_RES, ADXL313_FULL_RES); + if (ret) + return ret; + + /* Enables measurement mode */ + return regmap_update_bits(data->regmap, ADXL313_REG_POWER_CTL, + ADXL313_POWER_CTL_MSK, + ADXL313_MEASUREMENT_MODE); +} + +/** + * adxl313_core_probe() - probe and setup for adxl313 accelerometer + * @dev: Driver model representation of the device + * @regmap: Register map of the device + * @name: Device name buffer reference + * @setup: Setup routine to be executed right before the standard device + * setup, can also be set to NULL if not required + * + * Return: 0 on success, negative errno on error cases + */ +int adxl313_core_probe(struct device *dev, + struct regmap *regmap, + const char *name, + int (*setup)(struct device *, struct regmap *)) +{ + struct adxl313_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->regmap = regmap; + mutex_init(&data->lock); + + indio_dev->name = name; + indio_dev->info = &adxl313_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = adxl313_channels; + indio_dev->num_channels = ARRAY_SIZE(adxl313_channels); + + ret = adxl313_setup(dev, data, setup); + if (ret) { + dev_err(dev, "ADXL313 setup failed\n"); + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_GPL(adxl313_core_probe); + +MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>"); +MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/adxl313_i2c.c b/drivers/iio/accel/adxl313_i2c.c new file mode 100644 index 000000000000..82e9fb2db1e6 --- /dev/null +++ b/drivers/iio/accel/adxl313_i2c.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL313 3-Axis Digital Accelerometer + * + * Copyright (c) 2021 Lucas Stankus <lucas.p.stankus@gmail.com> + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL313.pdf + */ + +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "adxl313.h" + +static const struct regmap_config adxl313_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &adxl313_readable_regs_table, + .wr_table = &adxl313_writable_regs_table, + .max_register = 0x39, +}; + +static int adxl313_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &adxl313_i2c_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Error initializing i2c regmap: %ld\n", + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return adxl313_core_probe(&client->dev, regmap, client->name, NULL); +} + +static const struct i2c_device_id adxl313_i2c_id[] = { + { "adxl313" }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, adxl313_i2c_id); + +static const struct of_device_id adxl313_of_match[] = { + { .compatible = "adi,adxl313" }, + { } +}; + +MODULE_DEVICE_TABLE(of, adxl313_of_match); + +static struct i2c_driver adxl313_i2c_driver = { + .driver = { + .name = "adxl313_i2c", + .of_match_table = adxl313_of_match, + }, + .probe_new = adxl313_i2c_probe, + .id_table = adxl313_i2c_id, +}; + +module_i2c_driver(adxl313_i2c_driver); + +MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>"); +MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c new file mode 100644 index 000000000000..a6162f36ef52 --- /dev/null +++ b/drivers/iio/accel/adxl313_spi.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL313 3-Axis Digital Accelerometer + * + * Copyright (c) 2021 Lucas Stankus <lucas.p.stankus@gmail.com> + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL313.pdf + */ + +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "adxl313.h" + +static const struct regmap_config adxl313_spi_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .rd_table = &adxl313_readable_regs_table, + .wr_table = &adxl313_writable_regs_table, + .max_register = 0x39, + /* Setting bits 7 and 6 enables multiple-byte read */ + .read_flag_mask = BIT(7) | BIT(6), +}; + +static int adxl313_spi_setup(struct device *dev, struct regmap *regmap) +{ + struct spi_device *spi = container_of(dev, struct spi_device, dev); + int ret; + + if (spi->mode & SPI_3WIRE) { + ret = regmap_write(regmap, ADXL313_REG_DATA_FORMAT, + ADXL313_SPI_3WIRE); + if (ret) + return ret; + } + + return regmap_update_bits(regmap, ADXL313_REG_POWER_CTL, + ADXL313_I2C_DISABLE, ADXL313_I2C_DISABLE); +} + +static int adxl313_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap *regmap; + int ret; + + spi->mode |= SPI_MODE_3; + ret = spi_setup(spi); + if (ret) + return ret; + + regmap = devm_regmap_init_spi(spi, &adxl313_spi_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return adxl313_core_probe(&spi->dev, regmap, id->name, + &adxl313_spi_setup); +} + +static const struct spi_device_id adxl313_spi_id[] = { + { "adxl313" }, + { } +}; + +MODULE_DEVICE_TABLE(spi, adxl313_spi_id); + +static const struct of_device_id adxl313_of_match[] = { + { .compatible = "adi,adxl313" }, + { } +}; + +MODULE_DEVICE_TABLE(of, adxl313_of_match); + +static struct spi_driver adxl313_spi_driver = { + .driver = { + .name = "adxl313_spi", + .of_match_table = adxl313_of_match, + }, + .probe = adxl313_spi_probe, + .id_table = adxl313_spi_id, +}; + +module_spi_driver(adxl313_spi_driver); + +MODULE_AUTHOR("Lucas Stankus <lucas.p.stankus@gmail.com>"); +MODULE_DESCRIPTION("ADXL313 3-Axis Digital Accelerometer SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/adxl355.h b/drivers/iio/accel/adxl355.h new file mode 100644 index 000000000000..6dd49b13e4fd --- /dev/null +++ b/drivers/iio/accel/adxl355.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ADXL355 3-Axis Digital Accelerometer + * + * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com> + */ + +#ifndef _ADXL355_H_ +#define _ADXL355_H_ + +#include <linux/regmap.h> + +struct device; + +extern const struct regmap_access_table adxl355_readable_regs_tbl; +extern const struct regmap_access_table adxl355_writeable_regs_tbl; + +int adxl355_core_probe(struct device *dev, struct regmap *regmap, + const char *name); + +#endif /* _ADXL355_H_ */ diff --git a/drivers/iio/accel/adxl355_core.c b/drivers/iio/accel/adxl355_core.c new file mode 100644 index 000000000000..4f485909f459 --- /dev/null +++ b/drivers/iio/accel/adxl355_core.c @@ -0,0 +1,765 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL355 3-Axis Digital Accelerometer IIO core driver + * + * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com> + * + * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/adxl354_adxl355.pdf + */ + +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/limits.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/of_irq.h> +#include <linux/regmap.h> +#include <asm/unaligned.h> + +#include "adxl355.h" + +/* ADXL355 Register Definitions */ +#define ADXL355_DEVID_AD_REG 0x00 +#define ADXL355_DEVID_MST_REG 0x01 +#define ADXL355_PARTID_REG 0x02 +#define ADXL355_STATUS_REG 0x04 +#define ADXL355_FIFO_ENTRIES_REG 0x05 +#define ADXL355_TEMP2_REG 0x06 +#define ADXL355_XDATA3_REG 0x08 +#define ADXL355_YDATA3_REG 0x0B +#define ADXL355_ZDATA3_REG 0x0E +#define ADXL355_FIFO_DATA_REG 0x11 +#define ADXL355_OFFSET_X_H_REG 0x1E +#define ADXL355_OFFSET_Y_H_REG 0x20 +#define ADXL355_OFFSET_Z_H_REG 0x22 +#define ADXL355_ACT_EN_REG 0x24 +#define ADXL355_ACT_THRESH_H_REG 0x25 +#define ADXL355_ACT_THRESH_L_REG 0x26 +#define ADXL355_ACT_COUNT_REG 0x27 +#define ADXL355_FILTER_REG 0x28 +#define ADXL355_FILTER_ODR_MSK GENMASK(3, 0) +#define ADXL355_FILTER_HPF_MSK GENMASK(6, 4) +#define ADXL355_FIFO_SAMPLES_REG 0x29 +#define ADXL355_INT_MAP_REG 0x2A +#define ADXL355_SYNC_REG 0x2B +#define ADXL355_RANGE_REG 0x2C +#define ADXL355_POWER_CTL_REG 0x2D +#define ADXL355_POWER_CTL_MODE_MSK GENMASK(1, 0) +#define ADXL355_POWER_CTL_DRDY_MSK BIT(2) +#define ADXL355_SELF_TEST_REG 0x2E +#define ADXL355_RESET_REG 0x2F + +#define ADXL355_DEVID_AD_VAL 0xAD +#define ADXL355_DEVID_MST_VAL 0x1D +#define ADXL355_PARTID_VAL 0xED +#define ADXL355_RESET_CODE 0x52 + +#define MEGA 1000000UL +#define TERA 1000000000000ULL + +static const struct regmap_range adxl355_read_reg_range[] = { + regmap_reg_range(ADXL355_DEVID_AD_REG, ADXL355_FIFO_DATA_REG), + regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_SELF_TEST_REG), +}; + +const struct regmap_access_table adxl355_readable_regs_tbl = { + .yes_ranges = adxl355_read_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl355_read_reg_range), +}; +EXPORT_SYMBOL_GPL(adxl355_readable_regs_tbl); + +static const struct regmap_range adxl355_write_reg_range[] = { + regmap_reg_range(ADXL355_OFFSET_X_H_REG, ADXL355_RESET_REG), +}; + +const struct regmap_access_table adxl355_writeable_regs_tbl = { + .yes_ranges = adxl355_write_reg_range, + .n_yes_ranges = ARRAY_SIZE(adxl355_write_reg_range), +}; +EXPORT_SYMBOL_GPL(adxl355_writeable_regs_tbl); + +enum adxl355_op_mode { + ADXL355_MEASUREMENT, + ADXL355_STANDBY, + ADXL355_TEMP_OFF, +}; + +enum adxl355_odr { + ADXL355_ODR_4000HZ, + ADXL355_ODR_2000HZ, + ADXL355_ODR_1000HZ, + ADXL355_ODR_500HZ, + ADXL355_ODR_250HZ, + ADXL355_ODR_125HZ, + ADXL355_ODR_62_5HZ, + ADXL355_ODR_31_25HZ, + ADXL355_ODR_15_625HZ, + ADXL355_ODR_7_813HZ, + ADXL355_ODR_3_906HZ, +}; + +enum adxl355_hpf_3db { + ADXL355_HPF_OFF, + ADXL355_HPF_24_7, + ADXL355_HPF_6_2084, + ADXL355_HPF_1_5545, + ADXL355_HPF_0_3862, + ADXL355_HPF_0_0954, + ADXL355_HPF_0_0238, +}; + +static const int adxl355_odr_table[][2] = { + [0] = {4000, 0}, + [1] = {2000, 0}, + [2] = {1000, 0}, + [3] = {500, 0}, + [4] = {250, 0}, + [5] = {125, 0}, + [6] = {62, 500000}, + [7] = {31, 250000}, + [8] = {15, 625000}, + [9] = {7, 813000}, + [10] = {3, 906000}, +}; + +static const int adxl355_hpf_3db_multipliers[] = { + 0, + 247000, + 62084, + 15545, + 3862, + 954, + 238, +}; + +enum adxl355_chans { + chan_x, chan_y, chan_z, +}; + +struct adxl355_chan_info { + u8 data_reg; + u8 offset_reg; +}; + +static const struct adxl355_chan_info adxl355_chans[] = { + [chan_x] = { + .data_reg = ADXL355_XDATA3_REG, + .offset_reg = ADXL355_OFFSET_X_H_REG + }, + [chan_y] = { + .data_reg = ADXL355_YDATA3_REG, + .offset_reg = ADXL355_OFFSET_Y_H_REG + }, + [chan_z] = { + .data_reg = ADXL355_ZDATA3_REG, + .offset_reg = ADXL355_OFFSET_Z_H_REG + }, +}; + +struct adxl355_data { + struct regmap *regmap; + struct device *dev; + struct mutex lock; /* lock to protect op_mode */ + enum adxl355_op_mode op_mode; + enum adxl355_odr odr; + enum adxl355_hpf_3db hpf_3db; + int calibbias[3]; + int adxl355_hpf_3db_table[7][2]; + struct iio_trigger *dready_trig; + union { + u8 transf_buf[3]; + struct { + u8 buf[14]; + s64 ts; + } buffer; + } ____cacheline_aligned; +}; + +static int adxl355_set_op_mode(struct adxl355_data *data, + enum adxl355_op_mode op_mode) +{ + int ret; + + if (data->op_mode == op_mode) + return 0; + + ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG, + ADXL355_POWER_CTL_MODE_MSK, op_mode); + if (ret) + return ret; + + data->op_mode = op_mode; + + return ret; +} + +static int adxl355_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct adxl355_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG, + ADXL355_POWER_CTL_DRDY_MSK, + FIELD_PREP(ADXL355_POWER_CTL_DRDY_MSK, + state ? 0 : 1)); + mutex_unlock(&data->lock); + + return ret; +} + +static void adxl355_fill_3db_frequency_table(struct adxl355_data *data) +{ + u32 multiplier; + u64 div, rem; + u64 odr; + int i; + + odr = mul_u64_u32_shr(adxl355_odr_table[data->odr][0], MEGA, 0) + + adxl355_odr_table[data->odr][1]; + + for (i = 0; i < ARRAY_SIZE(adxl355_hpf_3db_multipliers); i++) { + multiplier = adxl355_hpf_3db_multipliers[i]; + div = div64_u64_rem(mul_u64_u32_shr(odr, multiplier, 0), + TERA * 100, &rem); + + data->adxl355_hpf_3db_table[i][0] = div; + data->adxl355_hpf_3db_table[i][1] = div_u64(rem, MEGA * 100); + } +} + +static int adxl355_setup(struct adxl355_data *data) +{ + unsigned int regval; + int ret; + + ret = regmap_read(data->regmap, ADXL355_DEVID_AD_REG, ®val); + if (ret) + return ret; + + if (regval != ADXL355_DEVID_AD_VAL) { + dev_err(data->dev, "Invalid ADI ID 0x%02x\n", regval); + return -ENODEV; + } + + ret = regmap_read(data->regmap, ADXL355_DEVID_MST_REG, ®val); + if (ret) + return ret; + + if (regval != ADXL355_DEVID_MST_VAL) { + dev_err(data->dev, "Invalid MEMS ID 0x%02x\n", regval); + return -ENODEV; + } + + ret = regmap_read(data->regmap, ADXL355_PARTID_REG, ®val); + if (ret) + return ret; + + if (regval != ADXL355_PARTID_VAL) { + dev_err(data->dev, "Invalid DEV ID 0x%02x\n", regval); + return -ENODEV; + } + + /* + * Perform a software reset to make sure the device is in a consistent + * state after start-up. + */ + ret = regmap_write(data->regmap, ADXL355_RESET_REG, ADXL355_RESET_CODE); + if (ret) + return ret; + + ret = regmap_update_bits(data->regmap, ADXL355_POWER_CTL_REG, + ADXL355_POWER_CTL_DRDY_MSK, + FIELD_PREP(ADXL355_POWER_CTL_DRDY_MSK, 1)); + if (ret) + return ret; + + adxl355_fill_3db_frequency_table(data); + + return adxl355_set_op_mode(data, ADXL355_MEASUREMENT); +} + +static int adxl355_get_temp_data(struct adxl355_data *data, u8 addr) +{ + return regmap_bulk_read(data->regmap, addr, data->transf_buf, 2); +} + +static int adxl355_read_axis(struct adxl355_data *data, u8 addr) +{ + int ret; + + ret = regmap_bulk_read(data->regmap, addr, data->transf_buf, + ARRAY_SIZE(data->transf_buf)); + if (ret) + return ret; + + return get_unaligned_be24(data->transf_buf); +} + +static int adxl355_find_match(const int (*freq_tbl)[2], const int n, + const int val, const int val2) +{ + int i; + + for (i = 0; i < n; i++) { + if (freq_tbl[i][0] == val && freq_tbl[i][1] == val2) + return i; + } + + return -EINVAL; +} + +static int adxl355_set_odr(struct adxl355_data *data, + enum adxl355_odr odr) +{ + int ret; + + mutex_lock(&data->lock); + + if (data->odr == odr) { + mutex_unlock(&data->lock); + return 0; + } + + ret = adxl355_set_op_mode(data, ADXL355_STANDBY); + if (ret) + goto err_unlock; + + ret = regmap_update_bits(data->regmap, ADXL355_FILTER_REG, + ADXL355_FILTER_ODR_MSK, + FIELD_PREP(ADXL355_FILTER_ODR_MSK, odr)); + if (ret) + goto err_set_opmode; + + data->odr = odr; + adxl355_fill_3db_frequency_table(data); + + ret = adxl355_set_op_mode(data, ADXL355_MEASUREMENT); + if (ret) + goto err_set_opmode; + + mutex_unlock(&data->lock); + return 0; + +err_set_opmode: + adxl355_set_op_mode(data, ADXL355_MEASUREMENT); +err_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int adxl355_set_hpf_3db(struct adxl355_data *data, + enum adxl355_hpf_3db hpf) +{ + int ret; + + mutex_lock(&data->lock); + + if (data->hpf_3db == hpf) { + mutex_unlock(&data->lock); + return 0; + } + + ret = adxl355_set_op_mode(data, ADXL355_STANDBY); + if (ret) + goto err_unlock; + + ret = regmap_update_bits(data->regmap, ADXL355_FILTER_REG, + ADXL355_FILTER_HPF_MSK, + FIELD_PREP(ADXL355_FILTER_HPF_MSK, hpf)); + if (ret) + goto err_set_opmode; + + data->hpf_3db = hpf; + + ret = adxl355_set_op_mode(data, ADXL355_MEASUREMENT); + if (ret) + goto err_set_opmode; + + mutex_unlock(&data->lock); + return 0; + +err_set_opmode: + adxl355_set_op_mode(data, ADXL355_MEASUREMENT); +err_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int adxl355_set_calibbias(struct adxl355_data *data, + enum adxl355_chans chan, int calibbias) +{ + int ret; + + mutex_lock(&data->lock); + + ret = adxl355_set_op_mode(data, ADXL355_STANDBY); + if (ret) + goto err_unlock; + + put_unaligned_be16(calibbias, data->transf_buf); + ret = regmap_bulk_write(data->regmap, + adxl355_chans[chan].offset_reg, + data->transf_buf, 2); + if (ret) + goto err_set_opmode; + + data->calibbias[chan] = calibbias; + + ret = adxl355_set_op_mode(data, ADXL355_MEASUREMENT); + if (ret) + goto err_set_opmode; + + mutex_unlock(&data->lock); + return 0; + +err_set_opmode: + adxl355_set_op_mode(data, ADXL355_MEASUREMENT); +err_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int adxl355_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct adxl355_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_TEMP: + ret = adxl355_get_temp_data(data, chan->address); + if (ret < 0) + return ret; + *val = get_unaligned_be16(data->transf_buf); + + return IIO_VAL_INT; + case IIO_ACCEL: + ret = adxl355_read_axis(data, adxl355_chans[ + chan->address].data_reg); + if (ret < 0) + return ret; + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); + return IIO_VAL_INT; + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + /* + * The datasheet defines an intercept of 1885 LSB at 25 degC + * and a slope of -9.05 LSB/C. The following formula can be used + * to find the temperature: + * Temp = ((RAW - 1885)/(-9.05)) + 25 but this doesn't follow + * the format of the IIO which is Temp = (RAW + OFFSET) * SCALE. + * Hence using some rearranging we get the scale as -110.497238 + * and offset as -2111.25. + */ + case IIO_TEMP: + *val = -110; + *val2 = 497238; + return IIO_VAL_INT_PLUS_MICRO; + /* + * At +/- 2g with 20-bit resolution, scale is given in datasheet + * as 3.9ug/LSB = 0.0000039 * 9.80665 = 0.00003824593 m/s^2. + */ + case IIO_ACCEL: + *val = 0; + *val2 = 38245; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + *val = -2111; + *val2 = 250000; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + *val = sign_extend32(data->calibbias[chan->address], 15); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = adxl355_odr_table[data->odr][0]; + *val2 = adxl355_odr_table[data->odr][1]; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + *val = data->adxl355_hpf_3db_table[data->hpf_3db][0]; + *val2 = data->adxl355_hpf_3db_table[data->hpf_3db][1]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int adxl355_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct adxl355_data *data = iio_priv(indio_dev); + int odr_idx, hpf_idx, calibbias; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + odr_idx = adxl355_find_match(adxl355_odr_table, + ARRAY_SIZE(adxl355_odr_table), + val, val2); + if (odr_idx < 0) + return odr_idx; + + return adxl355_set_odr(data, odr_idx); + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + hpf_idx = adxl355_find_match(data->adxl355_hpf_3db_table, + ARRAY_SIZE(data->adxl355_hpf_3db_table), + val, val2); + if (hpf_idx < 0) + return hpf_idx; + + return adxl355_set_hpf_3db(data, hpf_idx); + case IIO_CHAN_INFO_CALIBBIAS: + calibbias = clamp_t(int, val, S16_MIN, S16_MAX); + + return adxl355_set_calibbias(data, chan->address, calibbias); + default: + return -EINVAL; + } +} + +static int adxl355_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct adxl355_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (const int *)adxl355_odr_table; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(adxl355_odr_table) * 2; + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + *vals = (const int *)data->adxl355_hpf_3db_table; + *type = IIO_VAL_INT_PLUS_MICRO; + /* Values are stored in a 2D matrix */ + *length = ARRAY_SIZE(data->adxl355_hpf_3db_table) * 2; + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const unsigned long adxl355_avail_scan_masks[] = { + GENMASK(3, 0), + 0 +}; + +static const struct iio_info adxl355_info = { + .read_raw = adxl355_read_raw, + .write_raw = adxl355_write_raw, + .read_avail = &adxl355_read_avail, +}; + +static const struct iio_trigger_ops adxl355_trigger_ops = { + .set_trigger_state = &adxl355_data_rdy_trigger_set_state, + .validate_device = &iio_trigger_validate_own_device, +}; + +static irqreturn_t adxl355_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adxl355_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + + /* + * data->buffer is used both for triggered buffer support + * and read/write_raw(), hence, it has to be zeroed here before usage. + */ + data->buffer.buf[0] = 0; + + /* + * The acceleration data is 24 bits and big endian. It has to be saved + * in 32 bits, hence, it is saved in the 2nd byte of the 4 byte buffer. + * The buf array is 14 bytes as it includes 3x4=12 bytes for + * accelaration data of x, y, and z axis. It also includes 2 bytes for + * temperature data. + */ + ret = regmap_bulk_read(data->regmap, ADXL355_XDATA3_REG, + &data->buffer.buf[1], 3); + if (ret) + goto out_unlock_notify; + + ret = regmap_bulk_read(data->regmap, ADXL355_YDATA3_REG, + &data->buffer.buf[5], 3); + if (ret) + goto out_unlock_notify; + + ret = regmap_bulk_read(data->regmap, ADXL355_ZDATA3_REG, + &data->buffer.buf[9], 3); + if (ret) + goto out_unlock_notify; + + ret = regmap_bulk_read(data->regmap, ADXL355_TEMP2_REG, + &data->buffer.buf[12], 2); + if (ret) + goto out_unlock_notify; + + iio_push_to_buffers_with_timestamp(indio_dev, &data->buffer, + pf->timestamp); + +out_unlock_notify: + mutex_unlock(&data->lock); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +#define ADXL355_ACCEL_CHANNEL(index, reg, axis) { \ + .type = IIO_ACCEL, \ + .address = reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 20, \ + .storagebits = 32, \ + .shift = 4, \ + .endianness = IIO_BE, \ + } \ +} + +static const struct iio_chan_spec adxl355_channels[] = { + ADXL355_ACCEL_CHANNEL(0, chan_x, X), + ADXL355_ACCEL_CHANNEL(1, chan_y, Y), + ADXL355_ACCEL_CHANNEL(2, chan_z, Z), + { + .type = IIO_TEMP, + .address = ADXL355_TEMP2_REG, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = 3, + .scan_type = { + .sign = 's', + .realbits = 12, + .storagebits = 16, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static int adxl355_probe_trigger(struct iio_dev *indio_dev, int irq) +{ + struct adxl355_data *data = iio_priv(indio_dev); + int ret; + + data->dready_trig = devm_iio_trigger_alloc(data->dev, "%s-dev%d", + indio_dev->name, + iio_device_id(indio_dev)); + if (!data->dready_trig) + return -ENOMEM; + + data->dready_trig->ops = &adxl355_trigger_ops; + iio_trigger_set_drvdata(data->dready_trig, indio_dev); + + ret = devm_request_irq(data->dev, irq, + &iio_trigger_generic_data_rdy_poll, + IRQF_ONESHOT, "adxl355_irq", data->dready_trig); + if (ret) + return dev_err_probe(data->dev, ret, "request irq %d failed\n", + irq); + + ret = devm_iio_trigger_register(data->dev, data->dready_trig); + if (ret) { + dev_err(data->dev, "iio trigger register failed\n"); + return ret; + } + + indio_dev->trig = iio_trigger_get(data->dready_trig); + + return 0; +} + +int adxl355_core_probe(struct device *dev, struct regmap *regmap, + const char *name) +{ + struct adxl355_data *data; + struct iio_dev *indio_dev; + int ret; + int irq; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->regmap = regmap; + data->dev = dev; + data->op_mode = ADXL355_STANDBY; + mutex_init(&data->lock); + + indio_dev->name = name; + indio_dev->info = &adxl355_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = adxl355_channels; + indio_dev->num_channels = ARRAY_SIZE(adxl355_channels); + indio_dev->available_scan_masks = adxl355_avail_scan_masks; + + ret = adxl355_setup(data); + if (ret) { + dev_err(dev, "ADXL355 setup failed\n"); + return ret; + } + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &adxl355_trigger_handler, NULL); + if (ret) { + dev_err(dev, "iio triggered buffer setup failed\n"); + return ret; + } + + /* + * TODO: Would be good to move it to the generic version. + */ + irq = of_irq_get_byname(dev->of_node, "DRDY"); + if (irq > 0) { + ret = adxl355_probe_trigger(indio_dev, irq); + if (ret) + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_GPL(adxl355_core_probe); + +MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>"); +MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/adxl355_i2c.c b/drivers/iio/accel/adxl355_i2c.c new file mode 100644 index 000000000000..5a987bda9060 --- /dev/null +++ b/drivers/iio/accel/adxl355_i2c.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL355 3-Axis Digital Accelerometer I2C driver + * + * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com> + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/regmap.h> + +#include "adxl355.h" + +static const struct regmap_config adxl355_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x2F, + .rd_table = &adxl355_readable_regs_tbl, + .wr_table = &adxl355_writeable_regs_tbl, +}; + +static int adxl355_i2c_probe(struct i2c_client *client) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &adxl355_i2c_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Error initializing i2c regmap: %ld\n", + PTR_ERR(regmap)); + + return PTR_ERR(regmap); + } + + return adxl355_core_probe(&client->dev, regmap, client->name); +} + +static const struct i2c_device_id adxl355_i2c_id[] = { + { "adxl355", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adxl355_i2c_id); + +static const struct of_device_id adxl355_of_match[] = { + { .compatible = "adi,adxl355" }, + { } +}; +MODULE_DEVICE_TABLE(of, adxl355_of_match); + +static struct i2c_driver adxl355_i2c_driver = { + .driver = { + .name = "adxl355_i2c", + .of_match_table = adxl355_of_match, + }, + .probe_new = adxl355_i2c_probe, + .id_table = adxl355_i2c_id, +}; +module_i2c_driver(adxl355_i2c_driver); + +MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>"); +MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/adxl355_spi.c b/drivers/iio/accel/adxl355_spi.c new file mode 100644 index 000000000000..fb225aeb56e3 --- /dev/null +++ b/drivers/iio/accel/adxl355_spi.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADXL355 3-Axis Digital Accelerometer SPI driver + * + * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com> + */ + +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "adxl355.h" + +static const struct regmap_config adxl355_spi_regmap_config = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 8, + .read_flag_mask = BIT(0), + .max_register = 0x2F, + .rd_table = &adxl355_readable_regs_tbl, + .wr_table = &adxl355_writeable_regs_tbl, +}; + +static int adxl355_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &adxl355_spi_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Error initializing spi regmap: %ld\n", + PTR_ERR(regmap)); + + return PTR_ERR(regmap); + } + + return adxl355_core_probe(&spi->dev, regmap, id->name); +} + +static const struct spi_device_id adxl355_spi_id[] = { + { "adxl355", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, adxl355_spi_id); + +static const struct of_device_id adxl355_of_match[] = { + { .compatible = "adi,adxl355" }, + { } +}; +MODULE_DEVICE_TABLE(of, adxl355_of_match); + +static struct spi_driver adxl355_spi_driver = { + .driver = { + .name = "adxl355_spi", + .of_match_table = adxl355_of_match, + }, + .probe = adxl355_spi_probe, + .id_table = adxl355_spi_id, +}; +module_spi_driver(adxl355_spi_driver); + +MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>"); +MODULE_DESCRIPTION("ADXL355 3-Axis Digital Accelerometer SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index fc9592407717..758952584f8c 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -1214,6 +1214,7 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, ret = devm_iio_triggered_buffer_setup_ext(dev, indio_dev, NULL, adxl372_trigger_handler, + IIO_BUFFER_DIRECTION_IN, &adxl372_buffer_ops, adxl372_fifo_attributes); if (ret < 0) diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h index 5ad10db9819f..c4c8d74155c2 100644 --- a/drivers/iio/accel/bma400.h +++ b/drivers/iio/accel/bma400.h @@ -94,6 +94,6 @@ extern const struct regmap_config bma400_regmap_config; int bma400_probe(struct device *dev, struct regmap *regmap, const char *name); -int bma400_remove(struct device *dev); +void bma400_remove(struct device *dev); #endif diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index 21520e022a21..fd2647b728d3 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -828,7 +828,7 @@ int bma400_probe(struct device *dev, struct regmap *regmap, const char *name) } EXPORT_SYMBOL(bma400_probe); -int bma400_remove(struct device *dev) +void bma400_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bma400_data *data = iio_priv(indio_dev); @@ -838,12 +838,13 @@ int bma400_remove(struct device *dev) ret = bma400_set_power_mode(data, POWER_MODE_SLEEP); mutex_unlock(&data->mutex); + if (ret) + dev_warn(dev, "Failed to put device into sleep mode (%pe)\n", ERR_PTR(ret)); + regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); iio_device_unregister(indio_dev); - - return ret; } EXPORT_SYMBOL(bma400_remove); diff --git a/drivers/iio/accel/bma400_i2c.c b/drivers/iio/accel/bma400_i2c.c index 9dcb7cc9996e..f50df5310beb 100644 --- a/drivers/iio/accel/bma400_i2c.c +++ b/drivers/iio/accel/bma400_i2c.c @@ -29,7 +29,9 @@ static int bma400_i2c_probe(struct i2c_client *client, static int bma400_i2c_remove(struct i2c_client *client) { - return bma400_remove(&client->dev); + bma400_remove(&client->dev); + + return 0; } static const struct i2c_device_id bma400_i2c_ids[] = { diff --git a/drivers/iio/accel/bma400_spi.c b/drivers/iio/accel/bma400_spi.c index 7c2825904e08..9f622e37477b 100644 --- a/drivers/iio/accel/bma400_spi.c +++ b/drivers/iio/accel/bma400_spi.c @@ -89,7 +89,9 @@ static int bma400_spi_probe(struct spi_device *spi) static int bma400_spi_remove(struct spi_device *spi) { - return bma400_remove(&spi->dev); + bma400_remove(&spi->dev); + + return 0; } static const struct spi_device_id bma400_spi_ids[] = { diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index e8693a42ad46..b0678c351e82 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -1734,6 +1734,7 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, ret = iio_triggered_buffer_setup_ext(indio_dev, &iio_pollfunc_store_time, bmc150_accel_trigger_handler, + IIO_BUFFER_DIRECTION_IN, &bmc150_accel_buffer_ops, fifo_attrs); if (ret < 0) { @@ -1799,7 +1800,7 @@ err_disable_regulators: } EXPORT_SYMBOL_GPL(bmc150_accel_core_probe); -int bmc150_accel_core_remove(struct device *dev) +void bmc150_accel_core_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmc150_accel_data *data = iio_priv(indio_dev); @@ -1819,8 +1820,6 @@ int bmc150_accel_core_remove(struct device *dev) regulator_bulk_disable(ARRAY_SIZE(data->regulators), data->regulators); - - return 0; } EXPORT_SYMBOL_GPL(bmc150_accel_core_remove); diff --git a/drivers/iio/accel/bmc150-accel-i2c.c b/drivers/iio/accel/bmc150-accel-i2c.c index 88bd8a25f142..9e52df9a8f07 100644 --- a/drivers/iio/accel/bmc150-accel-i2c.c +++ b/drivers/iio/accel/bmc150-accel-i2c.c @@ -213,7 +213,9 @@ static int bmc150_accel_remove(struct i2c_client *client) { bmc150_acpi_dual_accel_remove(client); - return bmc150_accel_core_remove(&client->dev); + bmc150_accel_core_remove(&client->dev); + + return 0; } static const struct acpi_device_id bmc150_accel_acpi_match[] = { diff --git a/drivers/iio/accel/bmc150-accel-spi.c b/drivers/iio/accel/bmc150-accel-spi.c index 191e312dc91a..11559567cb39 100644 --- a/drivers/iio/accel/bmc150-accel-spi.c +++ b/drivers/iio/accel/bmc150-accel-spi.c @@ -37,7 +37,9 @@ static int bmc150_accel_probe(struct spi_device *spi) static int bmc150_accel_remove(struct spi_device *spi) { - return bmc150_accel_core_remove(&spi->dev); + bmc150_accel_core_remove(&spi->dev); + + return 0; } static const struct acpi_device_id bmc150_accel_acpi_match[] = { diff --git a/drivers/iio/accel/bmc150-accel.h b/drivers/iio/accel/bmc150-accel.h index 1bb5023e8ed9..7775c5edaeef 100644 --- a/drivers/iio/accel/bmc150-accel.h +++ b/drivers/iio/accel/bmc150-accel.h @@ -88,7 +88,7 @@ struct bmc150_accel_data { int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, enum bmc150_type type, const char *name, bool block_supported); -int bmc150_accel_core_remove(struct device *dev); +void bmc150_accel_core_remove(struct device *dev); extern const struct dev_pm_ops bmc150_accel_pm_ops; extern const struct regmap_config bmc150_regmap_conf; diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c index a06dae5c971d..d74465214feb 100644 --- a/drivers/iio/accel/bmi088-accel-core.c +++ b/drivers/iio/accel/bmi088-accel-core.c @@ -536,7 +536,7 @@ int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap, EXPORT_SYMBOL_GPL(bmi088_accel_core_probe); -int bmi088_accel_core_remove(struct device *dev) +void bmi088_accel_core_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmi088_accel_data *data = iio_priv(indio_dev); @@ -546,8 +546,6 @@ int bmi088_accel_core_remove(struct device *dev) pm_runtime_disable(dev); pm_runtime_set_suspended(dev); bmi088_accel_power_down(data); - - return 0; } EXPORT_SYMBOL_GPL(bmi088_accel_core_remove); diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c index dd1e3f6cf211..758ad2f12896 100644 --- a/drivers/iio/accel/bmi088-accel-spi.c +++ b/drivers/iio/accel/bmi088-accel-spi.c @@ -58,7 +58,9 @@ static int bmi088_accel_probe(struct spi_device *spi) static int bmi088_accel_remove(struct spi_device *spi) { - return bmi088_accel_core_remove(&spi->dev); + bmi088_accel_core_remove(&spi->dev); + + return 0; } static const struct spi_device_id bmi088_accel_id[] = { diff --git a/drivers/iio/accel/bmi088-accel.h b/drivers/iio/accel/bmi088-accel.h index 5c25f16b672c..5d40c7cf1cbc 100644 --- a/drivers/iio/accel/bmi088-accel.h +++ b/drivers/iio/accel/bmi088-accel.h @@ -13,6 +13,6 @@ extern const struct dev_pm_ops bmi088_accel_pm_ops; int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, const char *name, bool block_supported); -int bmi088_accel_core_remove(struct device *dev); +void bmi088_accel_core_remove(struct device *dev); #endif /* BMI088_ACCEL_H */ diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c index f41db9e0249a..32989d91b982 100644 --- a/drivers/iio/accel/fxls8962af-core.c +++ b/drivers/iio/accel/fxls8962af-core.c @@ -22,6 +22,7 @@ #include <linux/regmap.h> #include <linux/iio/buffer.h> +#include <linux/iio/events.h> #include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h> #include <linux/iio/sysfs.h> @@ -30,6 +31,7 @@ #define FXLS8962AF_INT_STATUS 0x00 #define FXLS8962AF_INT_STATUS_SRC_BOOT BIT(0) +#define FXLS8962AF_INT_STATUS_SRC_SDCD_OT BIT(4) #define FXLS8962AF_INT_STATUS_SRC_BUF BIT(5) #define FXLS8962AF_INT_STATUS_SRC_DRDY BIT(7) #define FXLS8962AF_TEMP_OUT 0x01 @@ -73,6 +75,7 @@ #define FXLS8962AF_ASLP_COUNT_LSB 0x1e #define FXLS8962AF_INT_EN 0x20 +#define FXLS8962AF_INT_EN_SDCD_OT_EN BIT(5) #define FXLS8962AF_INT_EN_BUF_EN BIT(6) #define FXLS8962AF_INT_PIN_SEL 0x21 #define FXLS8962AF_INT_PIN_SEL_MASK GENMASK(7, 0) @@ -96,9 +99,21 @@ #define FXLS8962AF_ORIENT_THS_REG 0x2c #define FXLS8962AF_SDCD_INT_SRC1 0x2d +#define FXLS8962AF_SDCD_INT_SRC1_X_OT BIT(5) +#define FXLS8962AF_SDCD_INT_SRC1_X_POL BIT(4) +#define FXLS8962AF_SDCD_INT_SRC1_Y_OT BIT(3) +#define FXLS8962AF_SDCD_INT_SRC1_Y_POL BIT(2) +#define FXLS8962AF_SDCD_INT_SRC1_Z_OT BIT(1) +#define FXLS8962AF_SDCD_INT_SRC1_Z_POL BIT(0) #define FXLS8962AF_SDCD_INT_SRC2 0x2e #define FXLS8962AF_SDCD_CONFIG1 0x2f +#define FXLS8962AF_SDCD_CONFIG1_Z_OT_EN BIT(3) +#define FXLS8962AF_SDCD_CONFIG1_Y_OT_EN BIT(4) +#define FXLS8962AF_SDCD_CONFIG1_X_OT_EN BIT(5) +#define FXLS8962AF_SDCD_CONFIG1_OT_ELE BIT(7) #define FXLS8962AF_SDCD_CONFIG2 0x30 +#define FXLS8962AF_SDCD_CONFIG2_SDCD_EN BIT(7) +#define FXLS8962AF_SC2_REF_UPDM_AC GENMASK(6, 5) #define FXLS8962AF_SDCD_OT_DBCNT 0x31 #define FXLS8962AF_SDCD_WT_DBCNT 0x32 #define FXLS8962AF_SDCD_LTHS_LSB 0x33 @@ -151,7 +166,11 @@ struct fxls8962af_data { } scan; int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */ struct iio_mount_matrix orientation; + int irq; u8 watermark; + u8 enable_event; + u16 lower_thres; + u16 upper_thres; }; const struct regmap_config fxls8962af_regmap_conf = { @@ -238,7 +257,7 @@ static int fxls8962af_get_out(struct fxls8962af_data *data, } ret = regmap_bulk_read(data->regmap, chan->address, - &raw_val, (chan->scan_type.storagebits / 8)); + &raw_val, sizeof(data->lower_thres)); if (!is_active) fxls8962af_power_off(data); @@ -451,6 +470,15 @@ static int fxls8962af_write_raw(struct iio_dev *indio_dev, } } +static int fxls8962af_event_setup(struct fxls8962af_data *data, int state) +{ + /* Enable wakeup interrupt */ + int mask = FXLS8962AF_INT_EN_SDCD_OT_EN; + int value = state ? mask : 0; + + return regmap_update_bits(data->regmap, FXLS8962AF_INT_EN, mask, value); +} + static int fxls8962af_set_watermark(struct iio_dev *indio_dev, unsigned val) { struct fxls8962af_data *data = iio_priv(indio_dev); @@ -463,6 +491,217 @@ static int fxls8962af_set_watermark(struct iio_dev *indio_dev, unsigned val) return 0; } +static int __fxls8962af_set_thresholds(struct fxls8962af_data *data, + const struct iio_chan_spec *chan, + enum iio_event_direction dir, + int val) +{ + switch (dir) { + case IIO_EV_DIR_FALLING: + data->lower_thres = val; + return regmap_bulk_write(data->regmap, FXLS8962AF_SDCD_LTHS_LSB, + &data->lower_thres, sizeof(data->lower_thres)); + case IIO_EV_DIR_RISING: + data->upper_thres = val; + return regmap_bulk_write(data->regmap, FXLS8962AF_SDCD_UTHS_LSB, + &data->upper_thres, sizeof(data->upper_thres)); + default: + return -EINVAL; + } +} + +static int fxls8962af_read_event(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 fxls8962af_data *data = iio_priv(indio_dev); + int ret; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + switch (dir) { + case IIO_EV_DIR_FALLING: + ret = regmap_bulk_read(data->regmap, FXLS8962AF_SDCD_LTHS_LSB, + &data->lower_thres, sizeof(data->lower_thres)); + if (ret) + return ret; + + *val = sign_extend32(data->lower_thres, chan->scan_type.realbits - 1); + return IIO_VAL_INT; + case IIO_EV_DIR_RISING: + ret = regmap_bulk_read(data->regmap, FXLS8962AF_SDCD_UTHS_LSB, + &data->upper_thres, sizeof(data->upper_thres)); + if (ret) + return ret; + + *val = sign_extend32(data->upper_thres, chan->scan_type.realbits - 1); + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int fxls8962af_write_event(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 fxls8962af_data *data = iio_priv(indio_dev); + int ret, val_masked; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + if (val < -2048 || val > 2047) + return -EINVAL; + + if (data->enable_event) + return -EBUSY; + + val_masked = val & GENMASK(11, 0); + if (fxls8962af_is_active(data)) { + ret = fxls8962af_standby(data); + if (ret) + return ret; + + ret = __fxls8962af_set_thresholds(data, chan, dir, val_masked); + if (ret) + return ret; + + return fxls8962af_active(data); + } else { + return __fxls8962af_set_thresholds(data, chan, dir, val_masked); + } +} + +static int +fxls8962af_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 fxls8962af_data *data = iio_priv(indio_dev); + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + return !!(FXLS8962AF_SDCD_CONFIG1_X_OT_EN & data->enable_event); + case IIO_MOD_Y: + return !!(FXLS8962AF_SDCD_CONFIG1_Y_OT_EN & data->enable_event); + case IIO_MOD_Z: + return !!(FXLS8962AF_SDCD_CONFIG1_Z_OT_EN & data->enable_event); + default: + return -EINVAL; + } +} + +static int +fxls8962af_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 fxls8962af_data *data = iio_priv(indio_dev); + u8 enable_event, enable_bits; + int ret, value; + + if (type != IIO_EV_TYPE_THRESH) + return -EINVAL; + + switch (chan->channel2) { + case IIO_MOD_X: + enable_bits = FXLS8962AF_SDCD_CONFIG1_X_OT_EN; + break; + case IIO_MOD_Y: + enable_bits = FXLS8962AF_SDCD_CONFIG1_Y_OT_EN; + break; + case IIO_MOD_Z: + enable_bits = FXLS8962AF_SDCD_CONFIG1_Z_OT_EN; + break; + default: + return -EINVAL; + } + + if (state) + enable_event = data->enable_event | enable_bits; + else + enable_event = data->enable_event & ~enable_bits; + + if (data->enable_event == enable_event) + return 0; + + ret = fxls8962af_standby(data); + if (ret) + return ret; + + /* Enable events */ + value = enable_event | FXLS8962AF_SDCD_CONFIG1_OT_ELE; + ret = regmap_write(data->regmap, FXLS8962AF_SDCD_CONFIG1, value); + if (ret) + return ret; + + /* + * Enable update of SDCD_REF_X/Y/Z values with the current decimated and + * trimmed X/Y/Z acceleration input data. This allows for acceleration + * slope detection with Data(n) to Data(n–1) always used as the input + * to the window comparator. + */ + value = enable_event ? + FXLS8962AF_SDCD_CONFIG2_SDCD_EN | FXLS8962AF_SC2_REF_UPDM_AC : + 0x00; + ret = regmap_write(data->regmap, FXLS8962AF_SDCD_CONFIG2, value); + if (ret) + return ret; + + ret = fxls8962af_event_setup(data, state); + if (ret) + return ret; + + data->enable_event = enable_event; + + if (data->enable_event) { + fxls8962af_active(data); + ret = fxls8962af_power_on(data); + } else { + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + /* Not in buffered mode so disable power */ + ret = fxls8962af_power_off(data); + + iio_device_release_direct_mode(indio_dev); + } + + return ret; +} + +static const struct iio_event_spec fxls8962af_event[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, +}; + #define FXLS8962AF_CHANNEL(axis, reg, idx) { \ .type = IIO_ACCEL, \ .address = reg, \ @@ -481,6 +720,8 @@ static int fxls8962af_set_watermark(struct iio_dev *indio_dev, unsigned val) .shift = 4, \ .endianness = IIO_BE, \ }, \ + .event_spec = fxls8962af_event, \ + .num_event_specs = ARRAY_SIZE(fxls8962af_event), \ } #define FXLS8962AF_TEMP_CHANNEL { \ @@ -522,6 +763,10 @@ static const struct iio_info fxls8962af_info = { .read_raw = &fxls8962af_read_raw, .write_raw = &fxls8962af_write_raw, .write_raw_get_fmt = fxls8962af_write_raw_get_fmt, + .read_event_value = fxls8962af_read_event, + .write_event_value = fxls8962af_write_event, + .read_event_config = fxls8962af_read_event_config, + .write_event_config = fxls8962af_write_event_config, .read_avail = fxls8962af_read_avail, .hwfifo_set_watermark = fxls8962af_set_watermark, }; @@ -605,7 +850,8 @@ static int fxls8962af_buffer_predisable(struct iio_dev *indio_dev) ret = __fxls8962af_fifo_set_mode(data, false); - fxls8962af_active(data); + if (data->enable_event) + fxls8962af_active(data); return ret; } @@ -614,7 +860,10 @@ static int fxls8962af_buffer_postdisable(struct iio_dev *indio_dev) { struct fxls8962af_data *data = iio_priv(indio_dev); - return fxls8962af_power_off(data); + if (!data->enable_event) + fxls8962af_power_off(data); + + return 0; } static const struct iio_buffer_setup_ops fxls8962af_buffer_ops = { @@ -725,6 +974,45 @@ static int fxls8962af_fifo_flush(struct iio_dev *indio_dev) return count; } +static int fxls8962af_event_interrupt(struct iio_dev *indio_dev) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + s64 ts = iio_get_time_ns(indio_dev); + unsigned int reg; + u64 ev_code; + int ret; + + ret = regmap_read(data->regmap, FXLS8962AF_SDCD_INT_SRC1, ®); + if (ret) + return ret; + + if (reg & FXLS8962AF_SDCD_INT_SRC1_X_OT) { + ev_code = reg & FXLS8962AF_SDCD_INT_SRC1_X_POL ? + IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, + IIO_EV_TYPE_THRESH, ev_code), ts); + } + + if (reg & FXLS8962AF_SDCD_INT_SRC1_Y_OT) { + ev_code = reg & FXLS8962AF_SDCD_INT_SRC1_Y_POL ? + IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, + IIO_EV_TYPE_THRESH, ev_code), ts); + } + + if (reg & FXLS8962AF_SDCD_INT_SRC1_Z_OT) { + ev_code = reg & FXLS8962AF_SDCD_INT_SRC1_Z_POL ? + IIO_EV_DIR_RISING : IIO_EV_DIR_FALLING; + iio_push_event(indio_dev, + IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X, + IIO_EV_TYPE_THRESH, ev_code), ts); + } + + return 0; +} + static irqreturn_t fxls8962af_interrupt(int irq, void *p) { struct iio_dev *indio_dev = p; @@ -744,6 +1032,14 @@ static irqreturn_t fxls8962af_interrupt(int irq, void *p) return IRQ_HANDLED; } + if (reg & FXLS8962AF_INT_STATUS_SRC_SDCD_OT) { + ret = fxls8962af_event_interrupt(indio_dev); + if (ret < 0) + return IRQ_NONE; + + return IRQ_HANDLED; + } + return IRQ_NONE; } @@ -861,6 +1157,7 @@ int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq) data = iio_priv(indio_dev); dev_set_drvdata(dev, indio_dev); data->regmap = regmap; + data->irq = irq; ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) @@ -930,6 +1227,9 @@ int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq) if (ret) return ret; + if (device_property_read_bool(dev, "wakeup-source")) + device_init_wakeup(dev, true); + return devm_iio_device_register(dev, indio_dev); } EXPORT_SYMBOL_GPL(fxls8962af_core_probe); @@ -955,9 +1255,46 @@ static int __maybe_unused fxls8962af_runtime_resume(struct device *dev) return fxls8962af_active(data); } +static int __maybe_unused fxls8962af_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct fxls8962af_data *data = iio_priv(indio_dev); + + if (device_may_wakeup(dev) && data->enable_event) { + enable_irq_wake(data->irq); + + /* + * Disable buffer, as the buffer is so small the device will wake + * almost immediately. + */ + if (iio_buffer_enabled(indio_dev)) + fxls8962af_buffer_predisable(indio_dev); + } else { + fxls8962af_runtime_suspend(dev); + } + + return 0; +} + +static int __maybe_unused fxls8962af_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct fxls8962af_data *data = iio_priv(indio_dev); + + if (device_may_wakeup(dev) && data->enable_event) { + disable_irq_wake(data->irq); + + if (iio_buffer_enabled(indio_dev)) + fxls8962af_buffer_postenable(indio_dev); + } else { + fxls8962af_runtime_resume(dev); + } + + return 0; +} + const struct dev_pm_ops fxls8962af_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + SET_SYSTEM_SLEEP_PM_OPS(fxls8962af_suspend, fxls8962af_resume) SET_RUNTIME_PM_OPS(fxls8962af_runtime_suspend, fxls8962af_runtime_resume, NULL) }; diff --git a/drivers/iio/accel/kxsd9-i2c.c b/drivers/iio/accel/kxsd9-i2c.c index b580d605f848..274b41a6e603 100644 --- a/drivers/iio/accel/kxsd9-i2c.c +++ b/drivers/iio/accel/kxsd9-i2c.c @@ -34,7 +34,9 @@ static int kxsd9_i2c_probe(struct i2c_client *i2c, static int kxsd9_i2c_remove(struct i2c_client *client) { - return kxsd9_common_remove(&client->dev); + kxsd9_common_remove(&client->dev); + + return 0; } static const struct of_device_id kxsd9_of_match[] = { diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c index 7971ec1eeb7e..441e6b764281 100644 --- a/drivers/iio/accel/kxsd9-spi.c +++ b/drivers/iio/accel/kxsd9-spi.c @@ -34,7 +34,9 @@ static int kxsd9_spi_probe(struct spi_device *spi) static int kxsd9_spi_remove(struct spi_device *spi) { - return kxsd9_common_remove(&spi->dev); + kxsd9_common_remove(&spi->dev); + + return 0; } static const struct spi_device_id kxsd9_spi_id[] = { diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index bf7ed9e7d00f..2faf85ca996e 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -478,7 +478,7 @@ err_power_down: } EXPORT_SYMBOL(kxsd9_common_probe); -int kxsd9_common_remove(struct device *dev) +void kxsd9_common_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct kxsd9_state *st = iio_priv(indio_dev); @@ -489,8 +489,6 @@ int kxsd9_common_remove(struct device *dev) pm_runtime_put_noidle(dev); pm_runtime_disable(dev); kxsd9_power_down(st); - - return 0; } EXPORT_SYMBOL(kxsd9_common_remove); diff --git a/drivers/iio/accel/kxsd9.h b/drivers/iio/accel/kxsd9.h index 5e3ca212f5be..c04dbfa4e0d0 100644 --- a/drivers/iio/accel/kxsd9.h +++ b/drivers/iio/accel/kxsd9.h @@ -8,6 +8,6 @@ int kxsd9_common_probe(struct device *dev, struct regmap *map, const char *name); -int kxsd9_common_remove(struct device *dev); +void kxsd9_common_remove(struct device *dev); extern const struct dev_pm_ops kxsd9_dev_pm_ops; diff --git a/drivers/iio/accel/mma7455.h b/drivers/iio/accel/mma7455.h index 4e3fa988f690..1fcc4b64b3af 100644 --- a/drivers/iio/accel/mma7455.h +++ b/drivers/iio/accel/mma7455.h @@ -11,6 +11,6 @@ extern const struct regmap_config mma7455_core_regmap; int mma7455_core_probe(struct device *dev, struct regmap *regmap, const char *name); -int mma7455_core_remove(struct device *dev); +void mma7455_core_remove(struct device *dev); #endif diff --git a/drivers/iio/accel/mma7455_core.c b/drivers/iio/accel/mma7455_core.c index 922bd38ff6ea..777c6c384b09 100644 --- a/drivers/iio/accel/mma7455_core.c +++ b/drivers/iio/accel/mma7455_core.c @@ -294,7 +294,7 @@ int mma7455_core_probe(struct device *dev, struct regmap *regmap, } EXPORT_SYMBOL_GPL(mma7455_core_probe); -int mma7455_core_remove(struct device *dev) +void mma7455_core_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct mma7455_data *mma7455 = iio_priv(indio_dev); @@ -304,8 +304,6 @@ int mma7455_core_remove(struct device *dev) regmap_write(mma7455->regmap, MMA7455_REG_MCTL, MMA7455_MCTL_MODE_STANDBY); - - return 0; } EXPORT_SYMBOL_GPL(mma7455_core_remove); diff --git a/drivers/iio/accel/mma7455_i2c.c b/drivers/iio/accel/mma7455_i2c.c index cddeaa9e230a..8a5256516f9f 100644 --- a/drivers/iio/accel/mma7455_i2c.c +++ b/drivers/iio/accel/mma7455_i2c.c @@ -28,7 +28,9 @@ static int mma7455_i2c_probe(struct i2c_client *i2c, static int mma7455_i2c_remove(struct i2c_client *i2c) { - return mma7455_core_remove(&i2c->dev); + mma7455_core_remove(&i2c->dev); + + return 0; } static const struct i2c_device_id mma7455_i2c_ids[] = { diff --git a/drivers/iio/accel/mma7455_spi.c b/drivers/iio/accel/mma7455_spi.c index eb82cdfa8abc..ecf690692dcc 100644 --- a/drivers/iio/accel/mma7455_spi.c +++ b/drivers/iio/accel/mma7455_spi.c @@ -24,7 +24,9 @@ static int mma7455_spi_probe(struct spi_device *spi) static int mma7455_spi_remove(struct spi_device *spi) { - return mma7455_core_remove(&spi->dev); + mma7455_core_remove(&spi->dev); + + return 0; } static const struct spi_device_id mma7455_spi_ids[] = { diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c index 47f5cd66e996..cd6cdf2c51b0 100644 --- a/drivers/iio/accel/mma7660.c +++ b/drivers/iio/accel/mma7660.c @@ -254,7 +254,7 @@ static const struct of_device_id mma7660_of_match[] = { }; MODULE_DEVICE_TABLE(of, mma7660_of_match); -static const struct acpi_device_id mma7660_acpi_id[] = { +static const struct acpi_device_id __maybe_unused mma7660_acpi_id[] = { {"MMA7660", 0}, {} }; diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index f1e6ec380667..31ea19d0ba71 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -1210,7 +1210,7 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev) }; - adev = ACPI_COMPANION(adata->dev); + adev = ACPI_COMPANION(indio_dev->dev.parent); if (!adev) return 0; @@ -1334,7 +1334,8 @@ EXPORT_SYMBOL(st_accel_get_settings); 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 = dev_get_platdata(adata->dev); + struct device *parent = indio_dev->dev.parent; + struct st_sensors_platform_data *pdata = dev_get_platdata(parent); int err; indio_dev->modes = INDIO_DIRECT_MODE; @@ -1354,7 +1355,7 @@ int st_accel_common_probe(struct iio_dev *indio_dev) */ err = apply_acpi_orientation(indio_dev); if (err) { - err = iio_read_mount_matrix(adata->dev, &adata->mount_matrix); + err = iio_read_mount_matrix(parent, &adata->mount_matrix); if (err) return err; } @@ -1380,32 +1381,10 @@ int st_accel_common_probe(struct iio_dev *indio_dev) return err; } - err = iio_device_register(indio_dev); - if (err) - goto st_accel_device_register_error; - - dev_info(&indio_dev->dev, "registered accelerometer %s\n", - indio_dev->name); - - return 0; - -st_accel_device_register_error: - if (adata->irq > 0) - st_sensors_deallocate_trigger(indio_dev); - return err; + return devm_iio_device_register(parent, indio_dev); } EXPORT_SYMBOL(st_accel_common_probe); -void st_accel_common_remove(struct iio_dev *indio_dev) -{ - struct st_sensor_data *adata = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (adata->irq > 0) - st_sensors_deallocate_trigger(indio_dev); -} -EXPORT_SYMBOL(st_accel_common_remove); - MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics accelerometers driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index f711756e41e3..c0ce78eebad9 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -177,27 +177,7 @@ static int st_accel_i2c_probe(struct i2c_client *client) if (ret) return ret; - ret = st_accel_common_probe(indio_dev); - if (ret < 0) - goto st_accel_power_off; - - return 0; - -st_accel_power_off: - st_sensors_power_disable(indio_dev); - - return ret; -} - -static int st_accel_i2c_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - st_sensors_power_disable(indio_dev); - - st_accel_common_remove(indio_dev); - - return 0; + return st_accel_common_probe(indio_dev); } static struct i2c_driver st_accel_driver = { @@ -207,7 +187,6 @@ static struct i2c_driver st_accel_driver = { .acpi_match_table = ACPI_PTR(st_accel_acpi_match), }, .probe_new = st_accel_i2c_probe, - .remove = st_accel_i2c_remove, .id_table = st_accel_id_table, }; module_i2c_driver(st_accel_driver); diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index bb45d9ff95b8..b74a1c6d03de 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -127,27 +127,7 @@ static int st_accel_spi_probe(struct spi_device *spi) if (err) return err; - err = st_accel_common_probe(indio_dev); - if (err < 0) - goto st_accel_power_off; - - return 0; - -st_accel_power_off: - st_sensors_power_disable(indio_dev); - - return err; -} - -static int st_accel_spi_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - - st_sensors_power_disable(indio_dev); - - st_accel_common_remove(indio_dev); - - return 0; + return st_accel_common_probe(indio_dev); } static const struct spi_device_id st_accel_id_table[] = { @@ -177,7 +157,6 @@ static struct spi_driver st_accel_driver = { .of_match_table = st_accel_of_match, }, .probe = st_accel_spi_probe, - .remove = st_accel_spi_remove, .id_table = st_accel_id_table, }; module_spi_driver(st_accel_driver); diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index af168e1c9fdb..8bf5b62a73f4 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -354,7 +354,7 @@ config BCM_IPROC_ADC config BERLIN2_ADC tristate "Marvell Berlin2 ADC driver" - depends on ARCH_BERLIN + depends on ARCH_BERLIN || COMPILE_TEST help Marvell Berlin2 ADC driver. This ADC has 8 channels, with one used for temperature measurement. @@ -430,9 +430,9 @@ config EXYNOS_ADC depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || ARCH_S5PV210 || (OF && COMPILE_TEST) depends on HAS_IOMEM help - Core support for the ADC block found in the Samsung EXYNOS series - of SoCs for drivers such as the touchscreen and hwmon to use to share - this resource. + Driver for the ADC block found in the Samsung S3C (S3C2410, S3C2416, + S3C2440, S3C2443, S3C6410), S5Pv210 and Exynos SoCs. + Choose Y here only if you build for such Samsung SoC. To compile this driver as a module, choose M here: the module will be called exynos_adc. @@ -530,6 +530,16 @@ config IMX7D_ADC This driver can also be built as a module. If so, the module will be called imx7d_adc. +config IMX8QXP_ADC + tristate "NXP IMX8QXP ADC driver" + depends on ARCH_MXC_ARM64 || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to build support for IMX8QXP ADC. + + This driver can also be built as a module. If so, the module will be + called imx8qxp-adc. + config LP8788_ADC tristate "LP8788 ADC driver" depends on MFD_LP8788 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index d68550f493e3..d3f53549720c 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_FSL_MX25_ADC) += fsl-imx25-gcq.o obj-$(CONFIG_HI8435) += hi8435.o obj-$(CONFIG_HX711) += hx711.o obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o +obj-$(CONFIG_IMX8QXP_ADC) += imx8qxp-adc.o obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o obj-$(CONFIG_INTEL_MRFLD_ADC) += intel_mrfld_adc.o diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c index 2301a0e27f23..e9129dac762f 100644 --- a/drivers/iio/adc/ad7291.c +++ b/drivers/iio/adc/ad7291.c @@ -460,6 +460,11 @@ static const struct iio_info ad7291_info = { .write_event_value = &ad7291_write_event_value, }; +static void ad7291_reg_disable(void *reg) +{ + regulator_disable(reg); +} + static int ad7291_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -473,8 +478,6 @@ static int ad7291_probe(struct i2c_client *client, chip = iio_priv(indio_dev); mutex_init(&chip->state_lock); - /* this is only used for device removal purposes */ - i2c_set_clientdata(client, indio_dev); chip->client = client; @@ -495,6 +498,11 @@ static int ad7291_probe(struct i2c_client *client, if (ret) return ret; + ret = devm_add_action_or_reset(&client->dev, ad7291_reg_disable, + chip->reg); + if (ret) + return ret; + chip->command |= AD7291_EXT_REF; } @@ -506,58 +514,25 @@ static int ad7291_probe(struct i2c_client *client, indio_dev->modes = INDIO_DIRECT_MODE; ret = ad7291_i2c_write(chip, AD7291_COMMAND, AD7291_RESET); - if (ret) { - ret = -EIO; - goto error_disable_reg; - } + if (ret) + return -EIO; ret = ad7291_i2c_write(chip, AD7291_COMMAND, chip->command); - if (ret) { - ret = -EIO; - goto error_disable_reg; - } + if (ret) + return -EIO; if (client->irq > 0) { - ret = request_threaded_irq(client->irq, - NULL, - &ad7291_event_handler, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - id->name, - indio_dev); + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, + &ad7291_event_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + id->name, + indio_dev); if (ret) - goto error_disable_reg; + return ret; } - ret = iio_device_register(indio_dev); - if (ret) - goto error_unreg_irq; - - return 0; - -error_unreg_irq: - if (client->irq) - free_irq(client->irq, indio_dev); -error_disable_reg: - if (chip->reg) - regulator_disable(chip->reg); - - return ret; -} - -static int ad7291_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct ad7291_chip_info *chip = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - if (client->irq) - free_irq(client->irq, indio_dev); - - if (chip->reg) - regulator_disable(chip->reg); - - return 0; + return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id ad7291_id[] = { @@ -579,7 +554,6 @@ static struct i2c_driver ad7291_driver = { .of_match_table = ad7291_of_match, }, .probe = ad7291_probe, - .remove = ad7291_remove, .id_table = ad7291_id, }; module_i2c_driver(ad7291_driver); diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c index 1b4b3203e428..44bb5fde83de 100644 --- a/drivers/iio/adc/ad7949.c +++ b/drivers/iio/adc/ad7949.c @@ -11,12 +11,41 @@ #include <linux/module.h> #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> +#include <linux/bitfield.h> -#define AD7949_MASK_CHANNEL_SEL GENMASK(9, 7) -#define AD7949_MASK_TOTAL GENMASK(13, 0) -#define AD7949_OFFSET_CHANNEL_SEL 7 -#define AD7949_CFG_READ_BACK 0x1 -#define AD7949_CFG_REG_SIZE_BITS 14 +#define AD7949_CFG_MASK_TOTAL GENMASK(13, 0) + +/* CFG: Configuration Update */ +#define AD7949_CFG_MASK_OVERWRITE BIT(13) + +/* INCC: Input Channel Configuration */ +#define AD7949_CFG_MASK_INCC GENMASK(12, 10) +#define AD7949_CFG_VAL_INCC_UNIPOLAR_GND 7 +#define AD7949_CFG_VAL_INCC_UNIPOLAR_COMM 6 +#define AD7949_CFG_VAL_INCC_UNIPOLAR_DIFF 4 +#define AD7949_CFG_VAL_INCC_TEMP 3 +#define AD7949_CFG_VAL_INCC_BIPOLAR 2 +#define AD7949_CFG_VAL_INCC_BIPOLAR_DIFF 0 + +/* INX: Input channel Selection in a binary fashion */ +#define AD7949_CFG_MASK_INX GENMASK(9, 7) + +/* BW: select bandwidth for low-pass filter. Full or Quarter */ +#define AD7949_CFG_MASK_BW_FULL BIT(6) + +/* REF: reference/buffer selection */ +#define AD7949_CFG_MASK_REF GENMASK(5, 3) +#define AD7949_CFG_VAL_REF_EXT_TEMP_BUF 3 +#define AD7949_CFG_VAL_REF_EXT_TEMP 2 +#define AD7949_CFG_VAL_REF_INT_4096 1 +#define AD7949_CFG_VAL_REF_INT_2500 0 +#define AD7949_CFG_VAL_REF_EXTERNAL BIT(1) + +/* SEQ: channel sequencer. Allows for scanning channels */ +#define AD7949_CFG_MASK_SEQ GENMASK(2, 1) + +/* RB: Read back the CFG register */ +#define AD7949_CFG_MASK_RBN BIT(0) enum { ID_AD7949 = 0, @@ -41,41 +70,51 @@ static const struct ad7949_adc_spec ad7949_adc_spec[] = { * @vref: regulator generating Vref * @indio_dev: reference to iio structure * @spi: reference to spi structure + * @refsel: reference selection * @resolution: resolution of the chip * @cfg: copy of the configuration register * @current_channel: current channel in use * @buffer: buffer to send / receive data to / from device + * @buf8b: be16 buffer to exchange data with the device in 8-bit transfers */ struct ad7949_adc_chip { struct mutex lock; struct regulator *vref; struct iio_dev *indio_dev; struct spi_device *spi; + u32 refsel; u8 resolution; u16 cfg; unsigned int current_channel; u16 buffer ____cacheline_aligned; + __be16 buf8b; }; static int ad7949_spi_write_cfg(struct ad7949_adc_chip *ad7949_adc, u16 val, u16 mask) { int ret; - int bits_per_word = ad7949_adc->resolution; - int shift = bits_per_word - AD7949_CFG_REG_SIZE_BITS; - struct spi_message msg; - struct spi_transfer tx[] = { - { - .tx_buf = &ad7949_adc->buffer, - .len = 2, - .bits_per_word = bits_per_word, - }, - }; ad7949_adc->cfg = (val & mask) | (ad7949_adc->cfg & ~mask); - ad7949_adc->buffer = ad7949_adc->cfg << shift; - spi_message_init_with_transfers(&msg, tx, 1); - ret = spi_sync(ad7949_adc->spi, &msg); + + switch (ad7949_adc->spi->bits_per_word) { + case 16: + ad7949_adc->buffer = ad7949_adc->cfg << 2; + ret = spi_write(ad7949_adc->spi, &ad7949_adc->buffer, 2); + break; + case 14: + ad7949_adc->buffer = ad7949_adc->cfg; + ret = spi_write(ad7949_adc->spi, &ad7949_adc->buffer, 2); + break; + case 8: + /* Here, type is big endian as it must be sent in two transfers */ + ad7949_adc->buf8b = cpu_to_be16(ad7949_adc->cfg << 2); + ret = spi_write(ad7949_adc->spi, &ad7949_adc->buf8b, 2); + break; + default: + dev_err(&ad7949_adc->indio_dev->dev, "unsupported BPW\n"); + return -EINVAL; + } /* * This delay is to avoid a new request before the required time to @@ -90,16 +129,6 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, { int ret; int i; - int bits_per_word = ad7949_adc->resolution; - int mask = GENMASK(ad7949_adc->resolution - 1, 0); - struct spi_message msg; - struct spi_transfer tx[] = { - { - .rx_buf = &ad7949_adc->buffer, - .len = 2, - .bits_per_word = bits_per_word, - }, - }; /* * 1: write CFG for sample N and read old data (sample N-2) @@ -109,8 +138,8 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, */ for (i = 0; i < 2; i++) { ret = ad7949_spi_write_cfg(ad7949_adc, - channel << AD7949_OFFSET_CHANNEL_SEL, - AD7949_MASK_CHANNEL_SEL); + FIELD_PREP(AD7949_CFG_MASK_INX, channel), + AD7949_CFG_MASK_INX); if (ret) return ret; if (channel == ad7949_adc->current_channel) @@ -118,9 +147,11 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, } /* 3: write something and read actual data */ - ad7949_adc->buffer = 0; - spi_message_init_with_transfers(&msg, tx, 1); - ret = spi_sync(ad7949_adc->spi, &msg); + if (ad7949_adc->spi->bits_per_word == 8) + ret = spi_read(ad7949_adc->spi, &ad7949_adc->buf8b, 2); + else + ret = spi_read(ad7949_adc->spi, &ad7949_adc->buffer, 2); + if (ret) return ret; @@ -132,7 +163,25 @@ static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val, ad7949_adc->current_channel = channel; - *val = ad7949_adc->buffer & mask; + switch (ad7949_adc->spi->bits_per_word) { + case 16: + *val = ad7949_adc->buffer; + /* Shift-out padding bits */ + *val >>= 16 - ad7949_adc->resolution; + break; + case 14: + *val = ad7949_adc->buffer & GENMASK(13, 0); + break; + case 8: + /* Here, type is big endian as data was sent in two transfers */ + *val = be16_to_cpu(ad7949_adc->buf8b); + /* Shift-out padding bits */ + *val >>= 16 - ad7949_adc->resolution; + break; + default: + dev_err(&ad7949_adc->indio_dev->dev, "unsupported BPW\n"); + return -EINVAL; + } return 0; } @@ -178,12 +227,26 @@ static int ad7949_spi_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - ret = regulator_get_voltage(ad7949_adc->vref); - if (ret < 0) - return ret; + switch (ad7949_adc->refsel) { + case AD7949_CFG_VAL_REF_INT_2500: + *val = 2500; + break; + case AD7949_CFG_VAL_REF_INT_4096: + *val = 4096; + break; + case AD7949_CFG_VAL_REF_EXT_TEMP: + case AD7949_CFG_VAL_REF_EXT_TEMP_BUF: + ret = regulator_get_voltage(ad7949_adc->vref); + if (ret < 0) + return ret; + + /* convert value back to mV */ + *val = ret / 1000; + break; + } - *val = ret / 5000; - return IIO_VAL_INT; + *val2 = (1 << ad7949_adc->resolution) - 1; + return IIO_VAL_FRACTIONAL; } return -EINVAL; @@ -199,8 +262,8 @@ static int ad7949_spi_reg_access(struct iio_dev *indio_dev, if (readval) *readval = ad7949_adc->cfg; else - ret = ad7949_spi_write_cfg(ad7949_adc, - writeval & AD7949_MASK_TOTAL, AD7949_MASK_TOTAL); + ret = ad7949_spi_write_cfg(ad7949_adc, writeval, + AD7949_CFG_MASK_TOTAL); return ret; } @@ -214,10 +277,19 @@ static int ad7949_spi_init(struct ad7949_adc_chip *ad7949_adc) { int ret; int val; + u16 cfg; - /* Sequencer disabled, CFG readback disabled, IN0 as default channel */ ad7949_adc->current_channel = 0; - ret = ad7949_spi_write_cfg(ad7949_adc, 0x3C79, AD7949_MASK_TOTAL); + + cfg = FIELD_PREP(AD7949_CFG_MASK_OVERWRITE, 1) | + FIELD_PREP(AD7949_CFG_MASK_INCC, AD7949_CFG_VAL_INCC_UNIPOLAR_GND) | + FIELD_PREP(AD7949_CFG_MASK_INX, ad7949_adc->current_channel) | + FIELD_PREP(AD7949_CFG_MASK_BW_FULL, 1) | + FIELD_PREP(AD7949_CFG_MASK_REF, ad7949_adc->refsel) | + FIELD_PREP(AD7949_CFG_MASK_SEQ, 0x0) | + FIELD_PREP(AD7949_CFG_MASK_RBN, 1); + + ret = ad7949_spi_write_cfg(ad7949_adc, cfg, AD7949_CFG_MASK_TOTAL); /* * Do two dummy conversions to apply the first configuration setting. @@ -229,12 +301,19 @@ static int ad7949_spi_init(struct ad7949_adc_chip *ad7949_adc) return ret; } +static void ad7949_disable_reg(void *reg) +{ + regulator_disable(reg); +} + static int ad7949_spi_probe(struct spi_device *spi) { + u32 spi_ctrl_mask = spi->controller->bits_per_word_mask; struct device *dev = &spi->dev; const struct ad7949_adc_spec *spec; struct ad7949_adc_chip *ad7949_adc; struct iio_dev *indio_dev; + u32 tmp; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*ad7949_adc)); @@ -257,16 +336,64 @@ static int ad7949_spi_probe(struct spi_device *spi) indio_dev->num_channels = spec->num_channels; ad7949_adc->resolution = spec->resolution; - ad7949_adc->vref = devm_regulator_get(dev, "vref"); + /* Set SPI bits per word */ + if (spi_ctrl_mask & SPI_BPW_MASK(ad7949_adc->resolution)) { + spi->bits_per_word = ad7949_adc->resolution; + } else if (spi_ctrl_mask == SPI_BPW_MASK(16)) { + spi->bits_per_word = 16; + } else if (spi_ctrl_mask == SPI_BPW_MASK(8)) { + spi->bits_per_word = 8; + } else { + dev_err(dev, "unable to find common BPW with spi controller\n"); + return -EINVAL; + } + + /* Setup internal voltage reference */ + tmp = 4096000; + device_property_read_u32(dev, "adi,internal-ref-microvolt", &tmp); + + switch (tmp) { + case 2500000: + ad7949_adc->refsel = AD7949_CFG_VAL_REF_INT_2500; + break; + case 4096000: + ad7949_adc->refsel = AD7949_CFG_VAL_REF_INT_4096; + break; + default: + dev_err(dev, "unsupported internal voltage reference\n"); + return -EINVAL; + } + + /* Setup external voltage reference, buffered? */ + ad7949_adc->vref = devm_regulator_get_optional(dev, "vrefin"); if (IS_ERR(ad7949_adc->vref)) { - dev_err(dev, "fail to request regulator\n"); - return PTR_ERR(ad7949_adc->vref); + ret = PTR_ERR(ad7949_adc->vref); + if (ret != -ENODEV) + return ret; + /* unbuffered? */ + ad7949_adc->vref = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(ad7949_adc->vref)) { + ret = PTR_ERR(ad7949_adc->vref); + if (ret != -ENODEV) + return ret; + } else { + ad7949_adc->refsel = AD7949_CFG_VAL_REF_EXT_TEMP; + } + } else { + ad7949_adc->refsel = AD7949_CFG_VAL_REF_EXT_TEMP_BUF; } - ret = regulator_enable(ad7949_adc->vref); - if (ret < 0) { - dev_err(dev, "fail to enable regulator\n"); - return ret; + if (ad7949_adc->refsel & AD7949_CFG_VAL_REF_EXTERNAL) { + ret = regulator_enable(ad7949_adc->vref); + if (ret < 0) { + dev_err(dev, "fail to enable regulator\n"); + return ret; + } + + ret = devm_add_action_or_reset(dev, ad7949_disable_reg, + ad7949_adc->vref); + if (ret) + return ret; } mutex_init(&ad7949_adc->lock); @@ -274,36 +401,16 @@ static int ad7949_spi_probe(struct spi_device *spi) ret = ad7949_spi_init(ad7949_adc); if (ret) { dev_err(dev, "enable to init this device: %d\n", ret); - goto err; + return ret; } - ret = iio_device_register(indio_dev); - if (ret) { + ret = devm_iio_device_register(dev, indio_dev); + if (ret) dev_err(dev, "fail to register iio device: %d\n", ret); - goto err; - } - - return 0; - -err: - mutex_destroy(&ad7949_adc->lock); - regulator_disable(ad7949_adc->vref); return ret; } -static int ad7949_spi_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - mutex_destroy(&ad7949_adc->lock); - regulator_disable(ad7949_adc->vref); - - return 0; -} - static const struct of_device_id ad7949_spi_of_id[] = { { .compatible = "adi,ad7949" }, { .compatible = "adi,ad7682" }, @@ -326,7 +433,6 @@ static struct spi_driver ad7949_spi_driver = { .of_match_table = ad7949_spi_of_id, }, .probe = ad7949_spi_probe, - .remove = ad7949_spi_remove, .id_table = ad7949_spi_id, }; module_spi_driver(ad7949_spi_driver); diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index 18bf8386d50a..220228c375d3 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -299,7 +299,11 @@ static int ad799x_read_raw(struct iio_dev *indio_dev, GENMASK(chan->scan_type.realbits - 1, 0); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - ret = regulator_get_voltage(st->vref); + if (st->vref) + ret = regulator_get_voltage(st->vref); + else + ret = regulator_get_voltage(st->reg); + if (ret < 0) return ret; *val = ret / 1000; @@ -770,6 +774,7 @@ static int ad799x_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret; + int extra_config = 0; struct ad799x_state *st; struct iio_dev *indio_dev; const struct ad799x_chip_info *chip_info = @@ -797,14 +802,36 @@ static int ad799x_probe(struct i2c_client *client, ret = regulator_enable(st->reg); if (ret) return ret; - st->vref = devm_regulator_get(&client->dev, "vref"); + + /* check if an external reference is supplied */ + st->vref = devm_regulator_get_optional(&client->dev, "vref"); + if (IS_ERR(st->vref)) { - ret = PTR_ERR(st->vref); - goto error_disable_reg; + if (PTR_ERR(st->vref) == -ENODEV) { + st->vref = NULL; + dev_info(&client->dev, "Using VCC reference voltage\n"); + } else { + ret = PTR_ERR(st->vref); + goto error_disable_reg; + } + } + + if (st->vref) { + /* + * Use external reference voltage if supported by hardware. + * This is optional if voltage / regulator present, use VCC otherwise. + */ + if ((st->id == ad7991) || (st->id == ad7995) || (st->id == ad7999)) { + dev_info(&client->dev, "Using external reference voltage\n"); + extra_config |= AD7991_REF_SEL; + ret = regulator_enable(st->vref); + if (ret) + goto error_disable_reg; + } else { + st->vref = NULL; + dev_warn(&client->dev, "Supplied reference not supported\n"); + } } - ret = regulator_enable(st->vref); - if (ret) - goto error_disable_reg; st->client = client; @@ -815,7 +842,7 @@ static int ad799x_probe(struct i2c_client *client, indio_dev->channels = st->chip_config->channel; indio_dev->num_channels = chip_info->num_channels; - ret = ad799x_update_config(st, st->chip_config->default_config); + ret = ad799x_update_config(st, st->chip_config->default_config | extra_config); if (ret) goto error_disable_vref; @@ -845,7 +872,8 @@ static int ad799x_probe(struct i2c_client *client, error_cleanup_ring: iio_triggered_buffer_cleanup(indio_dev); error_disable_vref: - regulator_disable(st->vref); + if (st->vref) + regulator_disable(st->vref); error_disable_reg: regulator_disable(st->reg); @@ -860,7 +888,8 @@ static int ad799x_remove(struct i2c_client *client) iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); - regulator_disable(st->vref); + if (st->vref) + regulator_disable(st->vref); regulator_disable(st->reg); kfree(st->rx_buf); @@ -872,7 +901,8 @@ static int __maybe_unused ad799x_suspend(struct device *dev) struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); struct ad799x_state *st = iio_priv(indio_dev); - regulator_disable(st->vref); + if (st->vref) + regulator_disable(st->vref); regulator_disable(st->reg); return 0; @@ -889,17 +919,21 @@ static int __maybe_unused ad799x_resume(struct device *dev) dev_err(dev, "Unable to enable vcc regulator\n"); return ret; } - ret = regulator_enable(st->vref); - if (ret) { - regulator_disable(st->reg); - dev_err(dev, "Unable to enable vref regulator\n"); - return ret; + + if (st->vref) { + ret = regulator_enable(st->vref); + if (ret) { + regulator_disable(st->reg); + dev_err(dev, "Unable to enable vref regulator\n"); + return ret; + } } /* resync config */ ret = ad799x_update_config(st, st->config); if (ret) { - regulator_disable(st->vref); + if (st->vref) + regulator_disable(st->vref); regulator_disable(st->reg); return ret; } diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 34ec0c28b2df..e939b84cbb56 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -1,8 +1,15 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Aspeed AST2400/2500 ADC + * Aspeed AST2400/2500/2600 ADC * * Copyright (C) 2017 Google, Inc. + * Copyright (C) 2021 Aspeed Technology Inc. + * + * ADC clock formula: + * Ast2400/Ast2500: + * clock period = period of PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1) + * Ast2600: + * clock period = period of PCLK * 2 * (ADC0C[15:0] + 1) */ #include <linux/clk.h> @@ -13,9 +20,13 @@ #include <linux/module.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/spinlock.h> #include <linux/types.h> +#include <linux/bitfield.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> #include <linux/iio/iio.h> #include <linux/iio/driver.h> @@ -28,34 +39,87 @@ #define ASPEED_REG_INTERRUPT_CONTROL 0x04 #define ASPEED_REG_VGA_DETECT_CONTROL 0x08 #define ASPEED_REG_CLOCK_CONTROL 0x0C -#define ASPEED_REG_MAX 0xC0 - -#define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1) -#define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1) -#define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1) - -#define ASPEED_ENGINE_ENABLE BIT(0) - -#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) +#define ASPEED_REG_COMPENSATION_TRIM 0xC4 +/* + * The register offset between 0xC8~0xCC can be read and won't affect the + * hardware logic in each version of ADC. + */ +#define ASPEED_REG_MAX 0xD0 + +#define ASPEED_ADC_ENGINE_ENABLE BIT(0) +#define ASPEED_ADC_OP_MODE GENMASK(3, 1) +#define ASPEED_ADC_OP_MODE_PWR_DOWN 0 +#define ASPEED_ADC_OP_MODE_STANDBY 1 +#define ASPEED_ADC_OP_MODE_NORMAL 7 +#define ASPEED_ADC_CTRL_COMPENSATION BIT(4) +#define ASPEED_ADC_AUTO_COMPENSATION BIT(5) +/* + * Bit 6 determines not only the reference voltage range but also the dividing + * circuit for battery sensing. + */ +#define ASPEED_ADC_REF_VOLTAGE GENMASK(7, 6) +#define ASPEED_ADC_REF_VOLTAGE_2500mV 0 +#define ASPEED_ADC_REF_VOLTAGE_1200mV 1 +#define ASPEED_ADC_REF_VOLTAGE_EXT_HIGH 2 +#define ASPEED_ADC_REF_VOLTAGE_EXT_LOW 3 +#define ASPEED_ADC_BAT_SENSING_DIV BIT(6) +#define ASPEED_ADC_BAT_SENSING_DIV_2_3 0 +#define ASPEED_ADC_BAT_SENSING_DIV_1_3 1 +#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) +#define ASPEED_ADC_CH7_MODE BIT(12) +#define ASPEED_ADC_CH7_NORMAL 0 +#define ASPEED_ADC_CH7_BAT 1 +#define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13) +#define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16) +#define ASPEED_ADC_CTRL_CHANNEL_ENABLE(ch) FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, BIT(ch)) #define ASPEED_ADC_INIT_POLLING_TIME 500 #define ASPEED_ADC_INIT_TIMEOUT 500000 +/* + * When the sampling rate is too high, the ADC may not have enough charging + * time, resulting in a low voltage value. Thus, the default uses a slow + * sampling rate for most use cases. + */ +#define ASPEED_ADC_DEF_SAMPLING_RATE 65000 + +struct aspeed_adc_trim_locate { + const unsigned int offset; + const unsigned int field; +}; struct aspeed_adc_model_data { const char *model_name; unsigned int min_sampling_rate; // Hz unsigned int max_sampling_rate; // Hz - unsigned int vref_voltage; // mV + unsigned int vref_fixed_mv; bool wait_init_sequence; + bool need_prescaler; + bool bat_sense_sup; + u8 scaler_bit_width; + unsigned int num_channels; + const struct aspeed_adc_trim_locate *trim_locate; +}; + +struct adc_gain { + u8 mult; + u8 div; }; struct aspeed_adc_data { struct device *dev; + const struct aspeed_adc_model_data *model_data; + struct regulator *regulator; void __iomem *base; spinlock_t clk_lock; + struct clk_hw *fixed_div_clk; struct clk_hw *clk_prescaler; struct clk_hw *clk_scaler; struct reset_control *rst; + int vref_mv; + u32 sample_period_ns; + int cv; + bool battery_sensing; + struct adc_gain battery_mode_gain; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -65,7 +129,8 @@ struct aspeed_adc_data { .address = (_data_reg_addr), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ } static const struct iio_chan_spec aspeed_adc_iio_channels[] = { @@ -87,21 +152,170 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = { ASPEED_CHAN(15, 0x2E), }; +#define ASPEED_BAT_CHAN(_idx, _data_reg_addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .address = (_data_reg_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} +static const struct iio_chan_spec aspeed_adc_iio_bat_channels[] = { + ASPEED_CHAN(0, 0x10), + ASPEED_CHAN(1, 0x12), + ASPEED_CHAN(2, 0x14), + ASPEED_CHAN(3, 0x16), + ASPEED_CHAN(4, 0x18), + ASPEED_CHAN(5, 0x1A), + ASPEED_CHAN(6, 0x1C), + ASPEED_BAT_CHAN(7, 0x1E), +}; + +static int aspeed_adc_set_trim_data(struct iio_dev *indio_dev) +{ + struct device_node *syscon; + struct regmap *scu; + u32 scu_otp, trimming_val; + struct aspeed_adc_data *data = iio_priv(indio_dev); + + syscon = of_find_node_by_name(NULL, "syscon"); + if (syscon == NULL) { + dev_warn(data->dev, "Couldn't find syscon node\n"); + return -EOPNOTSUPP; + } + scu = syscon_node_to_regmap(syscon); + if (IS_ERR(scu)) { + dev_warn(data->dev, "Failed to get syscon regmap\n"); + return -EOPNOTSUPP; + } + if (data->model_data->trim_locate) { + if (regmap_read(scu, data->model_data->trim_locate->offset, + &scu_otp)) { + dev_warn(data->dev, + "Failed to get adc trimming data\n"); + trimming_val = 0x8; + } else { + trimming_val = + ((scu_otp) & + (data->model_data->trim_locate->field)) >> + __ffs(data->model_data->trim_locate->field); + } + dev_dbg(data->dev, + "trimming val = %d, offset = %08x, fields = %08x\n", + trimming_val, data->model_data->trim_locate->offset, + data->model_data->trim_locate->field); + writel(trimming_val, data->base + ASPEED_REG_COMPENSATION_TRIM); + } + return 0; +} + +static int aspeed_adc_compensation(struct iio_dev *indio_dev) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + u32 index, adc_raw = 0; + u32 adc_engine_control_reg_val; + + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val &= ~ASPEED_ADC_OP_MODE; + adc_engine_control_reg_val |= + (FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE); + /* + * Enable compensating sensing: + * After that, the input voltage of ADC will force to half of the reference + * voltage. So the expected reading raw data will become half of the max + * value. We can get compensating value = 0x200 - ADC read raw value. + * It is recommended to average at least 10 samples to get a final CV. + */ + writel(adc_engine_control_reg_val | ASPEED_ADC_CTRL_COMPENSATION | + ASPEED_ADC_CTRL_CHANNEL_ENABLE(0), + data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * After enable compensating sensing mode need to wait some time for ADC stable + * Experiment result is 1ms. + */ + mdelay(1); + + for (index = 0; index < 16; index++) { + /* + * Waiting for the sampling period ensures that the value acquired + * is fresh each time. + */ + ndelay(data->sample_period_ns); + adc_raw += readw(data->base + aspeed_adc_iio_channels[0].address); + } + adc_raw >>= 4; + data->cv = BIT(ASPEED_RESOLUTION_BITS - 1) - adc_raw; + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); + dev_dbg(data->dev, "Compensating value = %d\n", data->cv); + + return 0; +} + +static int aspeed_adc_set_sampling_rate(struct iio_dev *indio_dev, u32 rate) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + + if (rate < data->model_data->min_sampling_rate || + rate > data->model_data->max_sampling_rate) + return -EINVAL; + /* Each sampling needs 12 clocks to convert.*/ + clk_set_rate(data->clk_scaler->clk, rate * ASPEED_CLOCKS_PER_SAMPLE); + rate = clk_get_rate(data->clk_scaler->clk); + data->sample_period_ns = DIV_ROUND_UP_ULL( + (u64)NSEC_PER_SEC * ASPEED_CLOCKS_PER_SAMPLE, rate); + dev_dbg(data->dev, "Adc clock = %d sample period = %d ns", rate, + data->sample_period_ns); + + return 0; +} + static int aspeed_adc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct aspeed_adc_data *data = iio_priv(indio_dev); - const struct aspeed_adc_model_data *model_data = - of_device_get_match_data(data->dev); + u32 adc_engine_control_reg_val; switch (mask) { case IIO_CHAN_INFO_RAW: - *val = readw(data->base + chan->address); + if (data->battery_sensing && chan->channel == 7) { + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + writel(adc_engine_control_reg_val | + FIELD_PREP(ASPEED_ADC_CH7_MODE, + ASPEED_ADC_CH7_BAT) | + ASPEED_ADC_BAT_SENSING_ENABLE, + data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * After enable battery sensing mode need to wait some time for adc stable + * Experiment result is 1ms. + */ + mdelay(1); + *val = readw(data->base + chan->address); + *val = (*val * data->battery_mode_gain.mult) / + data->battery_mode_gain.div; + /* Restore control register value */ + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); + } else + *val = readw(data->base + chan->address); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_OFFSET: + if (data->battery_sensing && chan->channel == 7) + *val = (data->cv * data->battery_mode_gain.mult) / + data->battery_mode_gain.div; + else + *val = data->cv; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = model_data->vref_voltage; + *val = data->vref_mv; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -119,19 +333,9 @@ static int aspeed_adc_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { - struct aspeed_adc_data *data = iio_priv(indio_dev); - const struct aspeed_adc_model_data *model_data = - of_device_get_match_data(data->dev); - switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - if (val < model_data->min_sampling_rate || - val > model_data->max_sampling_rate) - return -EINVAL; - - clk_set_rate(data->clk_scaler->clk, - val * ASPEED_CLOCKS_PER_SAMPLE); - return 0; + return aspeed_adc_set_sampling_rate(indio_dev, val); case IIO_CHAN_INFO_SCALE: case IIO_CHAN_INFO_RAW: @@ -168,14 +372,119 @@ static const struct iio_info aspeed_adc_iio_info = { .debugfs_reg_access = aspeed_adc_reg_access, }; +static void aspeed_adc_unregister_fixed_divider(void *data) +{ + struct clk_hw *clk = data; + + clk_hw_unregister_fixed_factor(clk); +} + +static void aspeed_adc_reset_assert(void *data) +{ + struct reset_control *rst = data; + + reset_control_assert(rst); +} + +static void aspeed_adc_clk_disable_unprepare(void *data) +{ + struct clk *clk = data; + + clk_disable_unprepare(clk); +} + +static void aspeed_adc_power_down(void *data) +{ + struct aspeed_adc_data *priv_data = data; + + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), + priv_data->base + ASPEED_REG_ENGINE_CONTROL); +} + +static void aspeed_adc_reg_disable(void *data) +{ + struct regulator *reg = data; + + regulator_disable(reg); +} + +static int aspeed_adc_vref_config(struct iio_dev *indio_dev) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + int ret; + u32 adc_engine_control_reg_val; + + if (data->model_data->vref_fixed_mv) { + data->vref_mv = data->model_data->vref_fixed_mv; + return 0; + } + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + data->regulator = devm_regulator_get_optional(data->dev, "vref"); + if (!IS_ERR(data->regulator)) { + ret = regulator_enable(data->regulator); + if (ret) + return ret; + ret = devm_add_action_or_reset( + data->dev, aspeed_adc_reg_disable, data->regulator); + if (ret) + return ret; + data->vref_mv = regulator_get_voltage(data->regulator); + /* Conversion from uV to mV */ + data->vref_mv /= 1000; + if ((data->vref_mv >= 1550) && (data->vref_mv <= 2700)) + writel(adc_engine_control_reg_val | + FIELD_PREP( + ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_EXT_HIGH), + data->base + ASPEED_REG_ENGINE_CONTROL); + else if ((data->vref_mv >= 900) && (data->vref_mv <= 1650)) + writel(adc_engine_control_reg_val | + FIELD_PREP( + ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_EXT_LOW), + data->base + ASPEED_REG_ENGINE_CONTROL); + else { + dev_err(data->dev, "Regulator voltage %d not support", + data->vref_mv); + return -EOPNOTSUPP; + } + } else { + if (PTR_ERR(data->regulator) != -ENODEV) + return PTR_ERR(data->regulator); + data->vref_mv = 2500000; + of_property_read_u32(data->dev->of_node, + "aspeed,int-vref-microvolt", + &data->vref_mv); + /* Conversion from uV to mV */ + data->vref_mv /= 1000; + if (data->vref_mv == 2500) + writel(adc_engine_control_reg_val | + FIELD_PREP(ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_2500mV), + data->base + ASPEED_REG_ENGINE_CONTROL); + else if (data->vref_mv == 1200) + writel(adc_engine_control_reg_val | + FIELD_PREP(ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_1200mV), + data->base + ASPEED_REG_ENGINE_CONTROL); + else { + dev_err(data->dev, "Voltage %d not support", data->vref_mv); + return -EOPNOTSUPP; + } + } + + return 0; +} + static int aspeed_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; struct aspeed_adc_data *data; - const struct aspeed_adc_model_data *model_data; - const char *clk_parent_name; int ret; u32 adc_engine_control_reg_val; + unsigned long scaler_flags = 0; + char clk_name[32], clk_parent_name[32]; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data)); if (!indio_dev) @@ -183,6 +492,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) data = iio_priv(indio_dev); data->dev = &pdev->dev; + data->model_data = of_device_get_match_data(&pdev->dev); platform_set_drvdata(pdev, indio_dev); data->base = devm_platform_ioremap_resource(pdev, 0); @@ -191,45 +501,117 @@ static int aspeed_adc_probe(struct platform_device *pdev) /* Register ADC clock prescaler with source specified by device tree. */ spin_lock_init(&data->clk_lock); - clk_parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); - - data->clk_prescaler = clk_hw_register_divider( - &pdev->dev, "prescaler", clk_parent_name, 0, - data->base + ASPEED_REG_CLOCK_CONTROL, - 17, 15, 0, &data->clk_lock); - if (IS_ERR(data->clk_prescaler)) - return PTR_ERR(data->clk_prescaler); - + snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), "%s", + of_clk_get_parent_name(pdev->dev.of_node, 0)); + snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-fixed-div", + data->model_data->model_name); + data->fixed_div_clk = clk_hw_register_fixed_factor( + &pdev->dev, clk_name, clk_parent_name, 0, 1, 2); + if (IS_ERR(data->fixed_div_clk)) + return PTR_ERR(data->fixed_div_clk); + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_unregister_fixed_divider, + data->fixed_div_clk); + if (ret) + return ret; + snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), clk_name); + + if (data->model_data->need_prescaler) { + snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler", + data->model_data->model_name); + data->clk_prescaler = devm_clk_hw_register_divider( + &pdev->dev, clk_name, clk_parent_name, 0, + data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0, + &data->clk_lock); + if (IS_ERR(data->clk_prescaler)) + return PTR_ERR(data->clk_prescaler); + snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), + clk_name); + scaler_flags = CLK_SET_RATE_PARENT; + } /* * Register ADC clock scaler downstream from the prescaler. Allow rate * setting to adjust the prescaler as well. */ - data->clk_scaler = clk_hw_register_divider( - &pdev->dev, "scaler", "prescaler", - CLK_SET_RATE_PARENT, - data->base + ASPEED_REG_CLOCK_CONTROL, - 0, 10, 0, &data->clk_lock); - if (IS_ERR(data->clk_scaler)) { - ret = PTR_ERR(data->clk_scaler); - goto scaler_error; - } - - data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); + snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-scaler", + data->model_data->model_name); + data->clk_scaler = devm_clk_hw_register_divider( + &pdev->dev, clk_name, clk_parent_name, scaler_flags, + data->base + ASPEED_REG_CLOCK_CONTROL, 0, + data->model_data->scaler_bit_width, 0, &data->clk_lock); + if (IS_ERR(data->clk_scaler)) + return PTR_ERR(data->clk_scaler); + + data->rst = devm_reset_control_get_shared(&pdev->dev, NULL); if (IS_ERR(data->rst)) { dev_err(&pdev->dev, "invalid or missing reset controller device tree entry"); - ret = PTR_ERR(data->rst); - goto reset_error; + return PTR_ERR(data->rst); } reset_control_deassert(data->rst); - model_data = of_device_get_match_data(&pdev->dev); + ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert, + data->rst); + if (ret) + return ret; - if (model_data->wait_init_sequence) { - /* Enable engine in normal mode. */ - writel(ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE, - data->base + ASPEED_REG_ENGINE_CONTROL); + ret = aspeed_adc_vref_config(indio_dev); + if (ret) + return ret; + if (of_find_property(data->dev->of_node, "aspeed,trim-data-valid", + NULL)) { + ret = aspeed_adc_set_trim_data(indio_dev); + if (ret) + return ret; + } + + if (of_find_property(data->dev->of_node, "aspeed,battery-sensing", + NULL)) { + if (data->model_data->bat_sense_sup) { + data->battery_sensing = 1; + if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) & + ASPEED_ADC_BAT_SENSING_DIV) { + data->battery_mode_gain.mult = 3; + data->battery_mode_gain.div = 1; + } else { + data->battery_mode_gain.mult = 3; + data->battery_mode_gain.div = 2; + } + } else + dev_warn(&pdev->dev, + "Failed to enable battery-sensing mode\n"); + } + + ret = clk_prepare_enable(data->clk_scaler->clk); + if (ret) + return ret; + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_clk_disable_unprepare, + data->clk_scaler->clk); + if (ret) + return ret; + ret = aspeed_adc_set_sampling_rate(indio_dev, + ASPEED_ADC_DEF_SAMPLING_RATE); + if (ret) + return ret; + + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val |= + FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE; + /* Enable engine in normal mode. */ + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); + + ret = devm_add_action_or_reset(data->dev, aspeed_adc_power_down, + data); + if (ret) + return ret; + + if (data->model_data->wait_init_sequence) { /* Wait for initial sequence complete. */ ret = readl_poll_timeout(data->base + ASPEED_REG_ENGINE_CONTROL, adc_engine_control_reg_val, @@ -238,87 +620,99 @@ static int aspeed_adc_probe(struct platform_device *pdev) ASPEED_ADC_INIT_POLLING_TIME, ASPEED_ADC_INIT_TIMEOUT); if (ret) - goto poll_timeout_error; + return ret; } + aspeed_adc_compensation(indio_dev); /* Start all channels in normal mode. */ - ret = clk_prepare_enable(data->clk_scaler->clk); - if (ret) - goto clk_enable_error; - - adc_engine_control_reg_val = GENMASK(31, 16) | - ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE; + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val |= ASPEED_ADC_CTRL_CHANNEL; writel(adc_engine_control_reg_val, - data->base + ASPEED_REG_ENGINE_CONTROL); + data->base + ASPEED_REG_ENGINE_CONTROL); - model_data = of_device_get_match_data(&pdev->dev); - indio_dev->name = model_data->model_name; + indio_dev->name = data->model_data->model_name; indio_dev->info = &aspeed_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = aspeed_adc_iio_channels; - indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels); - - ret = iio_device_register(indio_dev); - if (ret) - goto iio_register_error; - - return 0; + indio_dev->channels = data->battery_sensing ? + aspeed_adc_iio_bat_channels : + aspeed_adc_iio_channels; + indio_dev->num_channels = data->model_data->num_channels; -iio_register_error: - writel(ASPEED_OPERATION_MODE_POWER_DOWN, - data->base + ASPEED_REG_ENGINE_CONTROL); - clk_disable_unprepare(data->clk_scaler->clk); -clk_enable_error: -poll_timeout_error: - reset_control_assert(data->rst); -reset_error: - clk_hw_unregister_divider(data->clk_scaler); -scaler_error: - clk_hw_unregister_divider(data->clk_prescaler); + ret = devm_iio_device_register(data->dev, indio_dev); return ret; } -static int aspeed_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct aspeed_adc_data *data = iio_priv(indio_dev); +static const struct aspeed_adc_trim_locate ast2500_adc_trim = { + .offset = 0x154, + .field = GENMASK(31, 28), +}; - iio_device_unregister(indio_dev); - writel(ASPEED_OPERATION_MODE_POWER_DOWN, - data->base + ASPEED_REG_ENGINE_CONTROL); - clk_disable_unprepare(data->clk_scaler->clk); - reset_control_assert(data->rst); - clk_hw_unregister_divider(data->clk_scaler); - clk_hw_unregister_divider(data->clk_prescaler); +static const struct aspeed_adc_trim_locate ast2600_adc0_trim = { + .offset = 0x5d0, + .field = GENMASK(3, 0), +}; - return 0; -} +static const struct aspeed_adc_trim_locate ast2600_adc1_trim = { + .offset = 0x5d0, + .field = GENMASK(7, 4), +}; static const struct aspeed_adc_model_data ast2400_model_data = { .model_name = "ast2400-adc", - .vref_voltage = 2500, // mV + .vref_fixed_mv = 2500, .min_sampling_rate = 10000, .max_sampling_rate = 500000, + .need_prescaler = true, + .scaler_bit_width = 10, + .num_channels = 16, }; static const struct aspeed_adc_model_data ast2500_model_data = { .model_name = "ast2500-adc", - .vref_voltage = 1800, // mV + .vref_fixed_mv = 1800, .min_sampling_rate = 1, .max_sampling_rate = 1000000, .wait_init_sequence = true, + .need_prescaler = true, + .scaler_bit_width = 10, + .num_channels = 16, + .trim_locate = &ast2500_adc_trim, +}; + +static const struct aspeed_adc_model_data ast2600_adc0_model_data = { + .model_name = "ast2600-adc0", + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, + .wait_init_sequence = true, + .bat_sense_sup = true, + .scaler_bit_width = 16, + .num_channels = 8, + .trim_locate = &ast2600_adc0_trim, +}; + +static const struct aspeed_adc_model_data ast2600_adc1_model_data = { + .model_name = "ast2600-adc1", + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, + .wait_init_sequence = true, + .bat_sense_sup = true, + .scaler_bit_width = 16, + .num_channels = 8, + .trim_locate = &ast2600_adc1_trim, }; static const struct of_device_id aspeed_adc_matches[] = { { .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data }, { .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data }, + { .compatible = "aspeed,ast2600-adc0", .data = &ast2600_adc0_model_data }, + { .compatible = "aspeed,ast2600-adc1", .data = &ast2600_adc1_model_data }, {}, }; MODULE_DEVICE_TABLE(of, aspeed_adc_matches); static struct platform_driver aspeed_adc_driver = { .probe = aspeed_adc_probe, - .remove = aspeed_adc_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = aspeed_adc_matches, @@ -328,5 +722,5 @@ static struct platform_driver aspeed_adc_driver = { module_platform_driver(aspeed_adc_driver); MODULE_AUTHOR("Rick Altherr <raltherr@google.com>"); -MODULE_DESCRIPTION("Aspeed AST2400/2500 ADC Driver"); +MODULE_DESCRIPTION("Aspeed AST2400/2500/2600 ADC Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index ea5ca163d879..4c922ef634f8 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -4,6 +4,8 @@ * * Copyright (C) 2015 Atmel, * 2015 Ludovic Desroches <ludovic.desroches@atmel.com> + * 2021 Microchip Technology, Inc. and its subsidiaries + * 2021 Eugen Hristev <eugen.hristev@microchip.com> */ #include <linux/bitops.h> @@ -27,8 +29,9 @@ #include <linux/pinctrl/consumer.h> #include <linux/regulator/consumer.h> +struct at91_adc_reg_layout { /* Control Register */ -#define AT91_SAMA5D2_CR 0x00 + u16 CR; /* Software Reset */ #define AT91_SAMA5D2_CR_SWRST BIT(0) /* Start Conversion */ @@ -39,7 +42,7 @@ #define AT91_SAMA5D2_CR_CMPRST BIT(4) /* Mode Register */ -#define AT91_SAMA5D2_MR 0x04 + u16 MR; /* Trigger Selection */ #define AT91_SAMA5D2_MR_TRGSEL(v) ((v) << 1) /* ADTRG */ @@ -82,19 +85,19 @@ #define AT91_SAMA5D2_MR_USEQ BIT(31) /* Channel Sequence Register 1 */ -#define AT91_SAMA5D2_SEQR1 0x08 + u16 SEQR1; /* Channel Sequence Register 2 */ -#define AT91_SAMA5D2_SEQR2 0x0c + u16 SEQR2; /* Channel Enable Register */ -#define AT91_SAMA5D2_CHER 0x10 + u16 CHER; /* Channel Disable Register */ -#define AT91_SAMA5D2_CHDR 0x14 + u16 CHDR; /* Channel Status Register */ -#define AT91_SAMA5D2_CHSR 0x18 + u16 CHSR; /* Last Converted Data Register */ -#define AT91_SAMA5D2_LCDR 0x20 + u16 LCDR; /* Interrupt Enable Register */ -#define AT91_SAMA5D2_IER 0x24 + u16 IER; /* Interrupt Enable Register - TS X measurement ready */ #define AT91_SAMA5D2_IER_XRDY BIT(20) /* Interrupt Enable Register - TS Y measurement ready */ @@ -109,22 +112,31 @@ #define AT91_SAMA5D2_IER_PEN BIT(29) /* Interrupt Enable Register - No pen detect */ #define AT91_SAMA5D2_IER_NOPEN BIT(30) + /* Interrupt Disable Register */ -#define AT91_SAMA5D2_IDR 0x28 + u16 IDR; /* Interrupt Mask Register */ -#define AT91_SAMA5D2_IMR 0x2c + u16 IMR; /* Interrupt Status Register */ -#define AT91_SAMA5D2_ISR 0x30 + u16 ISR; +/* End of Conversion Interrupt Enable Register */ + u16 EOC_IER; +/* End of Conversion Interrupt Disable Register */ + u16 EOC_IDR; +/* End of Conversion Interrupt Mask Register */ + u16 EOC_IMR; +/* End of Conversion Interrupt Status Register */ + u16 EOC_ISR; /* Interrupt Status Register - Pen touching sense status */ #define AT91_SAMA5D2_ISR_PENS BIT(31) /* Last Channel Trigger Mode Register */ -#define AT91_SAMA5D2_LCTMR 0x34 + u16 LCTMR; /* Last Channel Compare Window Register */ -#define AT91_SAMA5D2_LCCWR 0x38 + u16 LCCWR; /* Overrun Status Register */ -#define AT91_SAMA5D2_OVER 0x3c + u16 OVER; /* Extended Mode Register */ -#define AT91_SAMA5D2_EMR 0x40 + u16 EMR; /* Extended Mode Register - Oversampling rate */ #define AT91_SAMA5D2_EMR_OSR(V) ((V) << 16) #define AT91_SAMA5D2_EMR_OSR_MASK GENMASK(17, 16) @@ -134,24 +146,22 @@ /* Extended Mode Register - Averaging on single trigger event */ #define AT91_SAMA5D2_EMR_ASTE(V) ((V) << 20) + /* Compare Window Register */ -#define AT91_SAMA5D2_CWR 0x44 + u16 CWR; /* Channel Gain Register */ -#define AT91_SAMA5D2_CGR 0x48 - + u16 CGR; /* Channel Offset Register */ -#define AT91_SAMA5D2_COR 0x4c -#define AT91_SAMA5D2_COR_DIFF_OFFSET 16 - -/* Channel Data Register 0 */ -#define AT91_SAMA5D2_CDR0 0x50 + u16 COR; +/* Channel Offset Register differential offset - constant, not a register */ + u16 COR_diff_offset; /* Analog Control Register */ -#define AT91_SAMA5D2_ACR 0x94 + u16 ACR; /* Analog Control Register - Pen detect sensitivity mask */ #define AT91_SAMA5D2_ACR_PENDETSENS_MASK GENMASK(1, 0) /* Touchscreen Mode Register */ -#define AT91_SAMA5D2_TSMR 0xb0 + u16 TSMR; /* Touchscreen Mode Register - No touch mode */ #define AT91_SAMA5D2_TSMR_TSMODE_NONE 0 /* Touchscreen Mode Register - 4 wire screen, no pressure measurement */ @@ -180,13 +190,13 @@ #define AT91_SAMA5D2_TSMR_PENDET_ENA BIT(24) /* Touchscreen X Position Register */ -#define AT91_SAMA5D2_XPOSR 0xb4 + u16 XPOSR; /* Touchscreen Y Position Register */ -#define AT91_SAMA5D2_YPOSR 0xb8 + u16 YPOSR; /* Touchscreen Pressure Register */ -#define AT91_SAMA5D2_PRESSR 0xbc + u16 PRESSR; /* Trigger Register */ -#define AT91_SAMA5D2_TRGR 0xc0 + u16 TRGR; /* Mask for TRGMOD field of TRGR register */ #define AT91_SAMA5D2_TRGR_TRGMOD_MASK GENMASK(2, 0) /* No trigger, only software trigger can start conversions */ @@ -205,30 +215,85 @@ #define AT91_SAMA5D2_TRGR_TRGPER(x) ((x) << 16) /* Correction Select Register */ -#define AT91_SAMA5D2_COSR 0xd0 + u16 COSR; /* Correction Value Register */ -#define AT91_SAMA5D2_CVR 0xd4 + u16 CVR; /* Channel Error Correction Register */ -#define AT91_SAMA5D2_CECR 0xd8 + u16 CECR; /* Write Protection Mode Register */ -#define AT91_SAMA5D2_WPMR 0xe4 + u16 WPMR; /* Write Protection Status Register */ -#define AT91_SAMA5D2_WPSR 0xe8 + u16 WPSR; /* Version Register */ -#define AT91_SAMA5D2_VERSION 0xfc - -#define AT91_SAMA5D2_HW_TRIG_CNT 3 -#define AT91_SAMA5D2_SINGLE_CHAN_CNT 12 -#define AT91_SAMA5D2_DIFF_CHAN_CNT 6 + u16 VERSION; +}; -#define AT91_SAMA5D2_TIMESTAMP_CHAN_IDX (AT91_SAMA5D2_SINGLE_CHAN_CNT + \ - AT91_SAMA5D2_DIFF_CHAN_CNT + 1) +static const struct at91_adc_reg_layout sama5d2_layout = { + .CR = 0x00, + .MR = 0x04, + .SEQR1 = 0x08, + .SEQR2 = 0x0c, + .CHER = 0x10, + .CHDR = 0x14, + .CHSR = 0x18, + .LCDR = 0x20, + .IER = 0x24, + .IDR = 0x28, + .IMR = 0x2c, + .ISR = 0x30, + .LCTMR = 0x34, + .LCCWR = 0x38, + .OVER = 0x3c, + .EMR = 0x40, + .CWR = 0x44, + .CGR = 0x48, + .COR = 0x4c, + .COR_diff_offset = 16, + .ACR = 0x94, + .TSMR = 0xb0, + .XPOSR = 0xb4, + .YPOSR = 0xb8, + .PRESSR = 0xbc, + .TRGR = 0xc0, + .COSR = 0xd0, + .CVR = 0xd4, + .CECR = 0xd8, + .WPMR = 0xe4, + .WPSR = 0xe8, + .VERSION = 0xfc, +}; -#define AT91_SAMA5D2_TOUCH_X_CHAN_IDX (AT91_SAMA5D2_SINGLE_CHAN_CNT + \ - AT91_SAMA5D2_DIFF_CHAN_CNT * 2) -#define AT91_SAMA5D2_TOUCH_Y_CHAN_IDX (AT91_SAMA5D2_TOUCH_X_CHAN_IDX + 1) -#define AT91_SAMA5D2_TOUCH_P_CHAN_IDX (AT91_SAMA5D2_TOUCH_Y_CHAN_IDX + 1) -#define AT91_SAMA5D2_MAX_CHAN_IDX AT91_SAMA5D2_TOUCH_P_CHAN_IDX +static const struct at91_adc_reg_layout sama7g5_layout = { + .CR = 0x00, + .MR = 0x04, + .SEQR1 = 0x08, + .SEQR2 = 0x0c, + .CHER = 0x10, + .CHDR = 0x14, + .CHSR = 0x18, + .LCDR = 0x20, + .IER = 0x24, + .IDR = 0x28, + .IMR = 0x2c, + .ISR = 0x30, + .EOC_IER = 0x34, + .EOC_IDR = 0x38, + .EOC_IMR = 0x3c, + .EOC_ISR = 0x40, + .OVER = 0x4c, + .EMR = 0x50, + .CWR = 0x54, + .COR = 0x5c, + .COR_diff_offset = 0, + .ACR = 0xe0, + .TRGR = 0x100, + .COSR = 0x104, + .CVR = 0x108, + .CECR = 0x10c, + .WPMR = 0x118, + .WPSR = 0x11c, + .VERSION = 0x130, +}; #define AT91_SAMA5D2_TOUCH_SAMPLE_PERIOD_US 2000 /* 2ms */ #define AT91_SAMA5D2_TOUCH_PEN_DETECT_DEBOUNCE_US 200 @@ -237,18 +302,6 @@ #define AT91_SAMA5D2_MAX_POS_BITS 12 -/* - * Maximum number of bytes to hold conversion from all channels - * without the timestamp. - */ -#define AT91_BUFFER_MAX_CONVERSION_BYTES ((AT91_SAMA5D2_SINGLE_CHAN_CNT + \ - AT91_SAMA5D2_DIFF_CHAN_CNT) * 2) - -/* This total must also include the timestamp */ -#define AT91_BUFFER_MAX_BYTES (AT91_BUFFER_MAX_CONVERSION_BYTES + 8) - -#define AT91_BUFFER_MAX_HWORDS (AT91_BUFFER_MAX_BYTES / 2) - #define AT91_HWFIFO_MAX_SIZE_STR "128" #define AT91_HWFIFO_MAX_SIZE 128 @@ -257,12 +310,12 @@ #define AT91_OSR_4SAMPLES 4 #define AT91_OSR_16SAMPLES 16 -#define AT91_SAMA5D2_CHAN_SINGLE(num, addr) \ +#define AT91_SAMA5D2_CHAN_SINGLE(index, num, addr) \ { \ .type = IIO_VOLTAGE, \ .channel = num, \ .address = addr, \ - .scan_index = num, \ + .scan_index = index, \ .scan_type = { \ .sign = 'u', \ .realbits = 14, \ @@ -276,14 +329,14 @@ .indexed = 1, \ } -#define AT91_SAMA5D2_CHAN_DIFF(num, num2, addr) \ +#define AT91_SAMA5D2_CHAN_DIFF(index, num, num2, addr) \ { \ .type = IIO_VOLTAGE, \ .differential = 1, \ .channel = num, \ .channel2 = num2, \ .address = addr, \ - .scan_index = num + AT91_SAMA5D2_SINGLE_CHAN_CNT, \ + .scan_index = index, \ .scan_type = { \ .sign = 's', \ .realbits = 14, \ @@ -330,13 +383,51 @@ .datasheet_name = name, \ } -#define at91_adc_readl(st, reg) readl_relaxed(st->base + reg) -#define at91_adc_writel(st, reg, val) writel_relaxed(val, st->base + reg) +#define at91_adc_readl(st, reg) \ + readl_relaxed((st)->base + (st)->soc_info.platform->layout->reg) +#define at91_adc_read_chan(st, reg) \ + readl_relaxed((st)->base + reg) +#define at91_adc_writel(st, reg, val) \ + writel_relaxed(val, (st)->base + (st)->soc_info.platform->layout->reg) + +/** + * struct at91_adc_platform - at91-sama5d2 platform information struct + * @layout: pointer to the reg layout struct + * @adc_channels: pointer to an array of channels for registering in + * the iio subsystem + * @nr_channels: number of physical channels available + * @touch_chan_x: index of the touchscreen X channel + * @touch_chan_y: index of the touchscreen Y channel + * @touch_chan_p: index of the touchscreen P channel + * @max_channels: number of total channels + * @max_index: highest channel index (highest index may be higher + * than the total channel number) + * @hw_trig_cnt: number of possible hardware triggers + */ +struct at91_adc_platform { + const struct at91_adc_reg_layout *layout; + const struct iio_chan_spec (*adc_channels)[]; + unsigned int nr_channels; + unsigned int touch_chan_x; + unsigned int touch_chan_y; + unsigned int touch_chan_p; + unsigned int max_channels; + unsigned int max_index; + unsigned int hw_trig_cnt; +}; +/** + * struct at91_adc_soc_info - at91-sama5d2 soc information struct + * @startup_time: device startup time + * @min_sample_rate: minimum sample rate in Hz + * @max_sample_rate: maximum sample rate in Hz + * @platform: pointer to the platform structure + */ struct at91_adc_soc_info { unsigned startup_time; unsigned min_sample_rate; unsigned max_sample_rate; + const struct at91_adc_platform *platform; }; struct at91_adc_trigger { @@ -384,6 +475,15 @@ struct at91_adc_touch { struct work_struct workq; }; +/* + * Buffer size requirements: + * No channels * bytes_per_channel(2) + timestamp bytes (8) + * Divided by 2 because we need half words. + * We assume 32 channels for now, has to be increased if needed. + * Nobody minds a buffer being too big. + */ +#define AT91_BUFFER_MAX_HWORDS ((32 * 2 + 8) / 2) + struct at91_adc_state { void __iomem *base; int irq; @@ -439,29 +539,94 @@ static const struct at91_adc_trigger at91_adc_trigger_list[] = { }, }; -static const struct iio_chan_spec at91_adc_channels[] = { - AT91_SAMA5D2_CHAN_SINGLE(0, 0x50), - AT91_SAMA5D2_CHAN_SINGLE(1, 0x54), - AT91_SAMA5D2_CHAN_SINGLE(2, 0x58), - AT91_SAMA5D2_CHAN_SINGLE(3, 0x5c), - AT91_SAMA5D2_CHAN_SINGLE(4, 0x60), - AT91_SAMA5D2_CHAN_SINGLE(5, 0x64), - AT91_SAMA5D2_CHAN_SINGLE(6, 0x68), - AT91_SAMA5D2_CHAN_SINGLE(7, 0x6c), - AT91_SAMA5D2_CHAN_SINGLE(8, 0x70), - AT91_SAMA5D2_CHAN_SINGLE(9, 0x74), - AT91_SAMA5D2_CHAN_SINGLE(10, 0x78), - AT91_SAMA5D2_CHAN_SINGLE(11, 0x7c), - AT91_SAMA5D2_CHAN_DIFF(0, 1, 0x50), - AT91_SAMA5D2_CHAN_DIFF(2, 3, 0x58), - AT91_SAMA5D2_CHAN_DIFF(4, 5, 0x60), - AT91_SAMA5D2_CHAN_DIFF(6, 7, 0x68), - AT91_SAMA5D2_CHAN_DIFF(8, 9, 0x70), - AT91_SAMA5D2_CHAN_DIFF(10, 11, 0x78), - IIO_CHAN_SOFT_TIMESTAMP(AT91_SAMA5D2_TIMESTAMP_CHAN_IDX), - AT91_SAMA5D2_CHAN_TOUCH(AT91_SAMA5D2_TOUCH_X_CHAN_IDX, "x", IIO_MOD_X), - AT91_SAMA5D2_CHAN_TOUCH(AT91_SAMA5D2_TOUCH_Y_CHAN_IDX, "y", IIO_MOD_Y), - AT91_SAMA5D2_CHAN_PRESSURE(AT91_SAMA5D2_TOUCH_P_CHAN_IDX, "pressure"), +static const struct iio_chan_spec at91_sama5d2_adc_channels[] = { + AT91_SAMA5D2_CHAN_SINGLE(0, 0, 0x50), + AT91_SAMA5D2_CHAN_SINGLE(1, 1, 0x54), + AT91_SAMA5D2_CHAN_SINGLE(2, 2, 0x58), + AT91_SAMA5D2_CHAN_SINGLE(3, 3, 0x5c), + AT91_SAMA5D2_CHAN_SINGLE(4, 4, 0x60), + AT91_SAMA5D2_CHAN_SINGLE(5, 5, 0x64), + AT91_SAMA5D2_CHAN_SINGLE(6, 6, 0x68), + AT91_SAMA5D2_CHAN_SINGLE(7, 7, 0x6c), + AT91_SAMA5D2_CHAN_SINGLE(8, 8, 0x70), + AT91_SAMA5D2_CHAN_SINGLE(9, 9, 0x74), + AT91_SAMA5D2_CHAN_SINGLE(10, 10, 0x78), + AT91_SAMA5D2_CHAN_SINGLE(11, 11, 0x7c), + /* original ABI has the differential channels with a gap in between */ + AT91_SAMA5D2_CHAN_DIFF(12, 0, 1, 0x50), + AT91_SAMA5D2_CHAN_DIFF(14, 2, 3, 0x58), + AT91_SAMA5D2_CHAN_DIFF(16, 4, 5, 0x60), + AT91_SAMA5D2_CHAN_DIFF(18, 6, 7, 0x68), + AT91_SAMA5D2_CHAN_DIFF(20, 8, 9, 0x70), + AT91_SAMA5D2_CHAN_DIFF(22, 10, 11, 0x78), + IIO_CHAN_SOFT_TIMESTAMP(23), + AT91_SAMA5D2_CHAN_TOUCH(24, "x", IIO_MOD_X), + AT91_SAMA5D2_CHAN_TOUCH(25, "y", IIO_MOD_Y), + AT91_SAMA5D2_CHAN_PRESSURE(26, "pressure"), +}; + +static const struct iio_chan_spec at91_sama7g5_adc_channels[] = { + AT91_SAMA5D2_CHAN_SINGLE(0, 0, 0x60), + AT91_SAMA5D2_CHAN_SINGLE(1, 1, 0x64), + AT91_SAMA5D2_CHAN_SINGLE(2, 2, 0x68), + AT91_SAMA5D2_CHAN_SINGLE(3, 3, 0x6c), + AT91_SAMA5D2_CHAN_SINGLE(4, 4, 0x70), + AT91_SAMA5D2_CHAN_SINGLE(5, 5, 0x74), + AT91_SAMA5D2_CHAN_SINGLE(6, 6, 0x78), + AT91_SAMA5D2_CHAN_SINGLE(7, 7, 0x7c), + AT91_SAMA5D2_CHAN_SINGLE(8, 8, 0x80), + AT91_SAMA5D2_CHAN_SINGLE(9, 9, 0x84), + AT91_SAMA5D2_CHAN_SINGLE(10, 10, 0x88), + AT91_SAMA5D2_CHAN_SINGLE(11, 11, 0x8c), + AT91_SAMA5D2_CHAN_SINGLE(12, 12, 0x90), + AT91_SAMA5D2_CHAN_SINGLE(13, 13, 0x94), + AT91_SAMA5D2_CHAN_SINGLE(14, 14, 0x98), + AT91_SAMA5D2_CHAN_SINGLE(15, 15, 0x9c), + AT91_SAMA5D2_CHAN_DIFF(16, 0, 1, 0x60), + AT91_SAMA5D2_CHAN_DIFF(17, 2, 3, 0x68), + AT91_SAMA5D2_CHAN_DIFF(18, 4, 5, 0x70), + AT91_SAMA5D2_CHAN_DIFF(19, 6, 7, 0x78), + AT91_SAMA5D2_CHAN_DIFF(20, 8, 9, 0x80), + AT91_SAMA5D2_CHAN_DIFF(21, 10, 11, 0x88), + AT91_SAMA5D2_CHAN_DIFF(22, 12, 13, 0x90), + AT91_SAMA5D2_CHAN_DIFF(23, 14, 15, 0x98), + IIO_CHAN_SOFT_TIMESTAMP(24), +}; + +static const struct at91_adc_platform sama5d2_platform = { + .layout = &sama5d2_layout, + .adc_channels = &at91_sama5d2_adc_channels, +#define AT91_SAMA5D2_SINGLE_CHAN_CNT 12 +#define AT91_SAMA5D2_DIFF_CHAN_CNT 6 + .nr_channels = AT91_SAMA5D2_SINGLE_CHAN_CNT + + AT91_SAMA5D2_DIFF_CHAN_CNT, +#define AT91_SAMA5D2_TOUCH_X_CHAN_IDX (AT91_SAMA5D2_SINGLE_CHAN_CNT + \ + AT91_SAMA5D2_DIFF_CHAN_CNT * 2) + .touch_chan_x = AT91_SAMA5D2_TOUCH_X_CHAN_IDX, +#define AT91_SAMA5D2_TOUCH_Y_CHAN_IDX (AT91_SAMA5D2_TOUCH_X_CHAN_IDX + 1) + .touch_chan_y = AT91_SAMA5D2_TOUCH_Y_CHAN_IDX, +#define AT91_SAMA5D2_TOUCH_P_CHAN_IDX (AT91_SAMA5D2_TOUCH_Y_CHAN_IDX + 1) + .touch_chan_p = AT91_SAMA5D2_TOUCH_P_CHAN_IDX, +#define AT91_SAMA5D2_MAX_CHAN_IDX AT91_SAMA5D2_TOUCH_P_CHAN_IDX + .max_channels = ARRAY_SIZE(at91_sama5d2_adc_channels), + .max_index = AT91_SAMA5D2_MAX_CHAN_IDX, +#define AT91_SAMA5D2_HW_TRIG_CNT 3 + .hw_trig_cnt = AT91_SAMA5D2_HW_TRIG_CNT, +}; + +static const struct at91_adc_platform sama7g5_platform = { + .layout = &sama7g5_layout, + .adc_channels = &at91_sama7g5_adc_channels, +#define AT91_SAMA7G5_SINGLE_CHAN_CNT 16 +#define AT91_SAMA7G5_DIFF_CHAN_CNT 8 + .nr_channels = AT91_SAMA7G5_SINGLE_CHAN_CNT + + AT91_SAMA7G5_DIFF_CHAN_CNT, +#define AT91_SAMA7G5_MAX_CHAN_IDX (AT91_SAMA7G5_SINGLE_CHAN_CNT + \ + AT91_SAMA7G5_DIFF_CHAN_CNT) + .max_channels = ARRAY_SIZE(at91_sama7g5_adc_channels), + .max_index = AT91_SAMA7G5_MAX_CHAN_IDX, +#define AT91_SAMA7G5_HW_TRIG_CNT 3 + .hw_trig_cnt = AT91_SAMA7G5_HW_TRIG_CNT, }; static int at91_adc_chan_xlate(struct iio_dev *indio_dev, int chan) @@ -495,6 +660,7 @@ static unsigned int at91_adc_active_scan_mask_to_reg(struct iio_dev *indio_dev) { u32 mask = 0; u8 bit; + struct at91_adc_state *st = iio_priv(indio_dev); for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->num_channels) { @@ -503,13 +669,66 @@ static unsigned int at91_adc_active_scan_mask_to_reg(struct iio_dev *indio_dev) mask |= BIT(chan->channel); } - return mask & GENMASK(11, 0); + return mask & GENMASK(st->soc_info.platform->nr_channels, 0); +} + +static void at91_adc_cor(struct at91_adc_state *st, + struct iio_chan_spec const *chan) +{ + u32 cor, cur_cor; + + cor = BIT(chan->channel) | BIT(chan->channel2); + + cur_cor = at91_adc_readl(st, COR); + cor <<= st->soc_info.platform->layout->COR_diff_offset; + if (chan->differential) + at91_adc_writel(st, COR, cur_cor | cor); + else + at91_adc_writel(st, COR, cur_cor & ~cor); +} + +static void at91_adc_irq_status(struct at91_adc_state *st, u32 *status, + u32 *eoc) +{ + *status = at91_adc_readl(st, ISR); + if (st->soc_info.platform->layout->EOC_ISR) + *eoc = at91_adc_readl(st, EOC_ISR); + else + *eoc = *status; +} + +static void at91_adc_irq_mask(struct at91_adc_state *st, u32 *status, u32 *eoc) +{ + *status = at91_adc_readl(st, IMR); + if (st->soc_info.platform->layout->EOC_IMR) + *eoc = at91_adc_readl(st, EOC_IMR); + else + *eoc = *status; +} + +static void at91_adc_eoc_dis(struct at91_adc_state *st, unsigned int channel) +{ + /* + * On some products having the EOC bits in a separate register, + * errata recommends not writing this register (EOC_IDR). + * On products having the EOC bits in the IDR register, it's fine to write it. + */ + if (!st->soc_info.platform->layout->EOC_IDR) + at91_adc_writel(st, IDR, BIT(channel)); +} + +static void at91_adc_eoc_ena(struct at91_adc_state *st, unsigned int channel) +{ + if (!st->soc_info.platform->layout->EOC_IDR) + at91_adc_writel(st, IER, BIT(channel)); + else + at91_adc_writel(st, EOC_IER, BIT(channel)); } static void at91_adc_config_emr(struct at91_adc_state *st) { /* configure the extended mode register */ - unsigned int emr = at91_adc_readl(st, AT91_SAMA5D2_EMR); + unsigned int emr = at91_adc_readl(st, EMR); /* select oversampling per single trigger event */ emr |= AT91_SAMA5D2_EMR_ASTE(1); @@ -533,7 +752,7 @@ static void at91_adc_config_emr(struct at91_adc_state *st) break; } - at91_adc_writel(st, AT91_SAMA5D2_EMR, emr); + at91_adc_writel(st, EMR, emr); } static int at91_adc_adjust_val_osr(struct at91_adc_state *st, int *val) @@ -586,9 +805,9 @@ static int at91_adc_configure_touch(struct at91_adc_state *st, bool state) if (!state) { /* disabling touch IRQs and setting mode to no touch enabled */ - at91_adc_writel(st, AT91_SAMA5D2_IDR, + at91_adc_writel(st, IDR, AT91_SAMA5D2_IER_PEN | AT91_SAMA5D2_IER_NOPEN); - at91_adc_writel(st, AT91_SAMA5D2_TSMR, 0); + at91_adc_writel(st, TSMR, 0); return 0; } /* @@ -614,26 +833,26 @@ static int at91_adc_configure_touch(struct at91_adc_state *st, bool state) tsmr |= AT91_SAMA5D2_TSMR_PENDET_ENA; tsmr |= AT91_SAMA5D2_TSMR_TSFREQ(2) & AT91_SAMA5D2_TSMR_TSFREQ_MASK; - at91_adc_writel(st, AT91_SAMA5D2_TSMR, tsmr); + at91_adc_writel(st, TSMR, tsmr); - acr = at91_adc_readl(st, AT91_SAMA5D2_ACR); + acr = at91_adc_readl(st, ACR); acr &= ~AT91_SAMA5D2_ACR_PENDETSENS_MASK; acr |= 0x02 & AT91_SAMA5D2_ACR_PENDETSENS_MASK; - at91_adc_writel(st, AT91_SAMA5D2_ACR, acr); + at91_adc_writel(st, ACR, acr); /* Sample Period Time = (TRGPER + 1) / ADCClock */ st->touch_st.sample_period_val = round_up((AT91_SAMA5D2_TOUCH_SAMPLE_PERIOD_US * clk_khz / 1000) - 1, 1); /* enable pen detect IRQ */ - at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_PEN); + at91_adc_writel(st, IER, AT91_SAMA5D2_IER_PEN); return 0; } static u16 at91_adc_touch_pos(struct at91_adc_state *st, int reg) { - u32 val; + u32 val = 0; u32 scale, result, pos; /* @@ -642,7 +861,11 @@ static u16 at91_adc_touch_pos(struct at91_adc_state *st, int reg) * max = 2^AT91_SAMA5D2_MAX_POS_BITS - 1 */ /* first half of register is the x or y, second half is the scale */ - val = at91_adc_readl(st, reg); + if (reg == st->soc_info.platform->layout->XPOSR) + val = at91_adc_readl(st, XPOSR); + else if (reg == st->soc_info.platform->layout->YPOSR) + val = at91_adc_readl(st, YPOSR); + if (!val) dev_dbg(&st->indio_dev->dev, "pos is 0\n"); @@ -660,13 +883,13 @@ static u16 at91_adc_touch_pos(struct at91_adc_state *st, int reg) static u16 at91_adc_touch_x_pos(struct at91_adc_state *st) { - st->touch_st.x_pos = at91_adc_touch_pos(st, AT91_SAMA5D2_XPOSR); + st->touch_st.x_pos = at91_adc_touch_pos(st, st->soc_info.platform->layout->XPOSR); return st->touch_st.x_pos; } static u16 at91_adc_touch_y_pos(struct at91_adc_state *st) { - return at91_adc_touch_pos(st, AT91_SAMA5D2_YPOSR); + return at91_adc_touch_pos(st, st->soc_info.platform->layout->YPOSR); } static u16 at91_adc_touch_pressure(struct at91_adc_state *st) @@ -678,7 +901,7 @@ static u16 at91_adc_touch_pressure(struct at91_adc_state *st) u32 factor = 1000; /* calculate the pressure */ - val = at91_adc_readl(st, AT91_SAMA5D2_PRESSR); + val = at91_adc_readl(st, PRESSR); z1 = val & AT91_SAMA5D2_XYZ_MASK; z2 = (val >> 16) & AT91_SAMA5D2_XYZ_MASK; @@ -702,9 +925,9 @@ static int at91_adc_read_position(struct at91_adc_state *st, int chan, u16 *val) *val = 0; if (!st->touch_st.touching) return -ENODATA; - if (chan == AT91_SAMA5D2_TOUCH_X_CHAN_IDX) + if (chan == st->soc_info.platform->touch_chan_x) *val = at91_adc_touch_x_pos(st); - else if (chan == AT91_SAMA5D2_TOUCH_Y_CHAN_IDX) + else if (chan == st->soc_info.platform->touch_chan_y) *val = at91_adc_touch_y_pos(st); else return -ENODATA; @@ -717,7 +940,7 @@ static int at91_adc_read_pressure(struct at91_adc_state *st, int chan, u16 *val) *val = 0; if (!st->touch_st.touching) return -ENODATA; - if (chan == AT91_SAMA5D2_TOUCH_P_CHAN_IDX) + if (chan == st->soc_info.platform->touch_chan_p) *val = at91_adc_touch_pressure(st); else return -ENODATA; @@ -729,7 +952,7 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) { struct iio_dev *indio = iio_trigger_get_drvdata(trig); struct at91_adc_state *st = iio_priv(indio); - u32 status = at91_adc_readl(st, AT91_SAMA5D2_TRGR); + u32 status = at91_adc_readl(st, TRGR); /* clear TRGMOD */ status &= ~AT91_SAMA5D2_TRGR_TRGMOD_MASK; @@ -738,7 +961,7 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) status |= st->selected_trig->trgmod_value; /* set/unset hw trigger */ - at91_adc_writel(st, AT91_SAMA5D2_TRGR, status); + at91_adc_writel(st, TRGR, status); return 0; } @@ -755,7 +978,7 @@ static void at91_adc_reenable_trigger(struct iio_trigger *trig) enable_irq(st->irq); /* Needed to ACK the DRDY interruption */ - at91_adc_readl(st, AT91_SAMA5D2_LCDR); + at91_adc_readl(st, LCDR); } static const struct iio_trigger_ops at91_adc_trigger_ops = { @@ -850,7 +1073,7 @@ static int at91_adc_dma_start(struct iio_dev *indio_dev) } /* enable general overrun error signaling */ - at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_GOVRE); + at91_adc_writel(st, IER, AT91_SAMA5D2_IER_GOVRE); /* Issue pending DMA requests */ dma_async_issue_pending(st->dma_st.dma_chan); @@ -880,7 +1103,7 @@ static bool at91_adc_current_chan_is_touch(struct iio_dev *indio_dev) return !!bitmap_subset(indio_dev->active_scan_mask, &st->touch_st.channels_bitmask, - AT91_SAMA5D2_MAX_CHAN_IDX + 1); + st->soc_info.platform->max_index + 1); } static int at91_adc_buffer_prepare(struct iio_dev *indio_dev) @@ -908,8 +1131,6 @@ static int at91_adc_buffer_prepare(struct iio_dev *indio_dev) indio_dev->num_channels) { struct iio_chan_spec const *chan = at91_adc_chan_get(indio_dev, bit); - u32 cor; - if (!chan) continue; /* these channel types cannot be handled by this trigger */ @@ -917,22 +1138,13 @@ static int at91_adc_buffer_prepare(struct iio_dev *indio_dev) chan->type == IIO_PRESSURE) continue; - cor = at91_adc_readl(st, AT91_SAMA5D2_COR); - - if (chan->differential) - cor |= (BIT(chan->channel) | BIT(chan->channel2)) << - AT91_SAMA5D2_COR_DIFF_OFFSET; - else - cor &= ~(BIT(chan->channel) << - AT91_SAMA5D2_COR_DIFF_OFFSET); - - at91_adc_writel(st, AT91_SAMA5D2_COR, cor); + at91_adc_cor(st, chan); - at91_adc_writel(st, AT91_SAMA5D2_CHER, BIT(chan->channel)); + at91_adc_writel(st, CHER, BIT(chan->channel)); } if (at91_adc_buffer_check_use_irq(indio_dev, st)) - at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_DRDY); + at91_adc_writel(st, IER, AT91_SAMA5D2_IER_DRDY); return 0; } @@ -968,17 +1180,17 @@ static int at91_adc_buffer_postdisable(struct iio_dev *indio_dev) chan->type == IIO_PRESSURE) continue; - at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel)); + at91_adc_writel(st, CHDR, BIT(chan->channel)); if (st->dma_st.dma_chan) - at91_adc_readl(st, chan->address); + at91_adc_read_chan(st, chan->address); } if (at91_adc_buffer_check_use_irq(indio_dev, st)) - at91_adc_writel(st, AT91_SAMA5D2_IDR, AT91_SAMA5D2_IER_DRDY); + at91_adc_writel(st, IDR, AT91_SAMA5D2_IER_DRDY); /* read overflow register to clear possible overflow status */ - at91_adc_readl(st, AT91_SAMA5D2_OVER); + at91_adc_readl(st, OVER); /* if we are using DMA we must clear registers and end DMA */ if (st->dma_st.dma_chan) @@ -1021,13 +1233,15 @@ static void at91_adc_trigger_handler_nodma(struct iio_dev *indio_dev, u8 bit; u32 mask = at91_adc_active_scan_mask_to_reg(indio_dev); unsigned int timeout = 50; + u32 status, imr, eoc = 0, eoc_imr; /* * Check if the conversion is ready. If not, wait a little bit, and * in case of timeout exit with an error. */ - while ((at91_adc_readl(st, AT91_SAMA5D2_ISR) & mask) != mask && - timeout) { + while (((eoc & mask) != mask) && timeout) { + at91_adc_irq_status(st, &status, &eoc); + at91_adc_irq_mask(st, &imr, &eoc_imr); usleep_range(50, 100); timeout--; } @@ -1054,7 +1268,7 @@ static void at91_adc_trigger_handler_nodma(struct iio_dev *indio_dev, * Thus, emit a warning. */ if (chan->type == IIO_VOLTAGE) { - val = at91_adc_readl(st, chan->address); + val = at91_adc_read_chan(st, chan->address); at91_adc_adjust_val_osr(st, &val); st->buffer[i] = val; } else { @@ -1075,7 +1289,7 @@ static void at91_adc_trigger_handler_dma(struct iio_dev *indio_dev) s64 interval; int sample_index = 0, sample_count, sample_size; - u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR); + u32 status = at91_adc_readl(st, ISR); /* if we reached this point, we cannot sample faster */ if (status & AT91_SAMA5D2_IER_GOVRE) pr_info_ratelimited("%s: conversion overrun detected\n", @@ -1127,7 +1341,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p) * actually polling the trigger now. */ if (iio_trigger_validate_own_device(indio_dev->trig, indio_dev)) - at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_START); + at91_adc_writel(st, CR, AT91_SAMA5D2_CR_START); if (st->dma_st.dma_chan) at91_adc_trigger_handler_dma(indio_dev); @@ -1174,11 +1388,11 @@ static void at91_adc_setup_samp_freq(struct iio_dev *indio_dev, unsigned freq) startup = at91_adc_startup_time(st->soc_info.startup_time, freq / 1000); - mr = at91_adc_readl(st, AT91_SAMA5D2_MR); + mr = at91_adc_readl(st, MR); mr &= ~(AT91_SAMA5D2_MR_STARTUP_MASK | AT91_SAMA5D2_MR_PRESCAL_MASK); mr |= AT91_SAMA5D2_MR_STARTUP(startup); mr |= AT91_SAMA5D2_MR_PRESCAL(prescal); - at91_adc_writel(st, AT91_SAMA5D2_MR, mr); + at91_adc_writel(st, MR, mr); dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n", freq, startup, prescal); @@ -1198,7 +1412,7 @@ static void at91_adc_touch_data_handler(struct iio_dev *indio_dev) int i = 0; for_each_set_bit(bit, indio_dev->active_scan_mask, - AT91_SAMA5D2_MAX_CHAN_IDX + 1) { + st->soc_info.platform->max_index + 1) { struct iio_chan_spec const *chan = at91_adc_chan_get(indio_dev, bit); @@ -1224,12 +1438,11 @@ static void at91_adc_touch_data_handler(struct iio_dev *indio_dev) static void at91_adc_pen_detect_interrupt(struct at91_adc_state *st) { - at91_adc_writel(st, AT91_SAMA5D2_IDR, AT91_SAMA5D2_IER_PEN); - at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_NOPEN | + at91_adc_writel(st, IDR, AT91_SAMA5D2_IER_PEN); + at91_adc_writel(st, IER, AT91_SAMA5D2_IER_NOPEN | AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY | AT91_SAMA5D2_IER_PRDY); - at91_adc_writel(st, AT91_SAMA5D2_TRGR, - AT91_SAMA5D2_TRGR_TRGMOD_PERIODIC | + at91_adc_writel(st, TRGR, AT91_SAMA5D2_TRGR_TRGMOD_PERIODIC | AT91_SAMA5D2_TRGR_TRGPER(st->touch_st.sample_period_val)); st->touch_st.touching = true; } @@ -1238,16 +1451,15 @@ static void at91_adc_no_pen_detect_interrupt(struct iio_dev *indio_dev) { struct at91_adc_state *st = iio_priv(indio_dev); - at91_adc_writel(st, AT91_SAMA5D2_TRGR, - AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER); - at91_adc_writel(st, AT91_SAMA5D2_IDR, AT91_SAMA5D2_IER_NOPEN | + at91_adc_writel(st, TRGR, AT91_SAMA5D2_TRGR_TRGMOD_NO_TRIGGER); + at91_adc_writel(st, IDR, AT91_SAMA5D2_IER_NOPEN | AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY | AT91_SAMA5D2_IER_PRDY); st->touch_st.touching = false; at91_adc_touch_data_handler(indio_dev); - at91_adc_writel(st, AT91_SAMA5D2_IER, AT91_SAMA5D2_IER_PEN); + at91_adc_writel(st, IER, AT91_SAMA5D2_IER_PEN); } static void at91_adc_workq_handler(struct work_struct *workq) @@ -1265,12 +1477,14 @@ static irqreturn_t at91_adc_interrupt(int irq, void *private) { struct iio_dev *indio = private; struct at91_adc_state *st = iio_priv(indio); - u32 status = at91_adc_readl(st, AT91_SAMA5D2_ISR); - u32 imr = at91_adc_readl(st, AT91_SAMA5D2_IMR); + u32 status, eoc, imr, eoc_imr; u32 rdy_mask = AT91_SAMA5D2_IER_XRDY | AT91_SAMA5D2_IER_YRDY | AT91_SAMA5D2_IER_PRDY; - if (!(status & imr)) + at91_adc_irq_status(st, &status, &eoc); + at91_adc_irq_mask(st, &imr, &eoc_imr); + + if (!(status & imr) && !(eoc & eoc_imr)) return IRQ_NONE; if (status & AT91_SAMA5D2_IER_PEN) { /* pen detected IRQ */ @@ -1287,9 +1501,9 @@ static irqreturn_t at91_adc_interrupt(int irq, void *private) * touching, but the measurements are not ready yet. * read and ignore. */ - status = at91_adc_readl(st, AT91_SAMA5D2_XPOSR); - status = at91_adc_readl(st, AT91_SAMA5D2_YPOSR); - status = at91_adc_readl(st, AT91_SAMA5D2_PRESSR); + status = at91_adc_readl(st, XPOSR); + status = at91_adc_readl(st, YPOSR); + status = at91_adc_readl(st, PRESSR); } else if (iio_buffer_enabled(indio) && (status & AT91_SAMA5D2_IER_DRDY)) { /* triggered buffer without DMA */ @@ -1301,7 +1515,7 @@ static irqreturn_t at91_adc_interrupt(int irq, void *private) WARN(true, "Unexpected irq occurred\n"); } else if (!iio_buffer_enabled(indio)) { /* software requested conversion */ - st->conversion_value = at91_adc_readl(st, st->chan->address); + st->conversion_value = at91_adc_read_chan(st, st->chan->address); st->conversion_done = true; wake_up_interruptible(&st->wq_data_available); } @@ -1312,7 +1526,6 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { struct at91_adc_state *st = iio_priv(indio_dev); - u32 cor = 0; u16 tmp_val; int ret; @@ -1358,14 +1571,10 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, st->chan = chan; - if (chan->differential) - cor = (BIT(chan->channel) | BIT(chan->channel2)) << - AT91_SAMA5D2_COR_DIFF_OFFSET; - - at91_adc_writel(st, AT91_SAMA5D2_COR, cor); - at91_adc_writel(st, AT91_SAMA5D2_CHER, BIT(chan->channel)); - at91_adc_writel(st, AT91_SAMA5D2_IER, BIT(chan->channel)); - at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_START); + at91_adc_cor(st, chan); + at91_adc_writel(st, CHER, BIT(chan->channel)); + at91_adc_eoc_ena(st, chan->channel); + at91_adc_writel(st, CR, AT91_SAMA5D2_CR_START); ret = wait_event_interruptible_timeout(st->wq_data_available, st->conversion_done, @@ -1381,11 +1590,11 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev, st->conversion_done = false; } - at91_adc_writel(st, AT91_SAMA5D2_IDR, BIT(chan->channel)); - at91_adc_writel(st, AT91_SAMA5D2_CHDR, BIT(chan->channel)); + at91_adc_eoc_dis(st, st->chan->channel); + at91_adc_writel(st, CHDR, BIT(chan->channel)); /* Needed to ACK the DRDY interruption */ - at91_adc_readl(st, AT91_SAMA5D2_LCDR); + at91_adc_readl(st, LCDR); mutex_unlock(&st->lock); @@ -1457,14 +1666,15 @@ static void at91_adc_dma_init(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct at91_adc_state *st = iio_priv(indio_dev); struct dma_slave_config config = {0}; + /* we have 2 bytes for each channel */ + unsigned int sample_size = st->soc_info.platform->nr_channels * 2; /* * We make the buffer double the size of the fifo, * such that DMA uses one half of the buffer (full fifo size) * and the software uses the other half to read/write. */ unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE * - AT91_BUFFER_MAX_CONVERSION_BYTES * 2, - PAGE_SIZE); + sample_size * 2, PAGE_SIZE); if (st->dma_st.dma_chan) return; @@ -1488,7 +1698,7 @@ static void at91_adc_dma_init(struct platform_device *pdev) /* Configure DMA channel to read data register */ config.direction = DMA_DEV_TO_MEM; config.src_addr = (phys_addr_t)(st->dma_st.phys_addr - + AT91_SAMA5D2_LCDR); + + st->soc_info.platform->layout->LCDR); config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; config.src_maxburst = 1; config.dst_maxburst = 1; @@ -1517,9 +1727,10 @@ static void at91_adc_dma_disable(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct at91_adc_state *st = iio_priv(indio_dev); + /* we have 2 bytes for each channel */ + unsigned int sample_size = st->soc_info.platform->nr_channels * 2; unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE * - AT91_BUFFER_MAX_CONVERSION_BYTES * 2, - PAGE_SIZE); + sample_size * 2, PAGE_SIZE); /* if we are not using DMA, just return */ if (!st->dma_st.dma_chan) @@ -1580,14 +1791,14 @@ static int at91_adc_update_scan_mode(struct iio_dev *indio_dev, struct at91_adc_state *st = iio_priv(indio_dev); if (bitmap_subset(scan_mask, &st->touch_st.channels_bitmask, - AT91_SAMA5D2_MAX_CHAN_IDX + 1)) + st->soc_info.platform->max_index + 1)) return 0; /* * if the new bitmap is a combination of touchscreen and regular * channels, then we are not fine */ if (bitmap_intersects(&st->touch_st.channels_bitmask, scan_mask, - AT91_SAMA5D2_MAX_CHAN_IDX + 1)) + st->soc_info.platform->max_index + 1)) return -EINVAL; return 0; } @@ -1596,13 +1807,15 @@ static void at91_adc_hw_init(struct iio_dev *indio_dev) { struct at91_adc_state *st = iio_priv(indio_dev); - at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST); - at91_adc_writel(st, AT91_SAMA5D2_IDR, 0xffffffff); + at91_adc_writel(st, CR, AT91_SAMA5D2_CR_SWRST); + if (st->soc_info.platform->layout->EOC_IDR) + at91_adc_writel(st, EOC_IDR, 0xffffffff); + at91_adc_writel(st, IDR, 0xffffffff); /* * Transfer field must be set to 2 according to the datasheet and * allows different analog settings for each channel. */ - at91_adc_writel(st, AT91_SAMA5D2_MR, + at91_adc_writel(st, MR, AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH); at91_adc_setup_samp_freq(indio_dev, st->soc_info.min_sample_rate); @@ -1681,8 +1894,8 @@ static int at91_adc_buffer_and_trigger_init(struct device *dev, fifo_attrs = NULL; ret = devm_iio_triggered_buffer_setup_ext(&indio->dev, indio, - &iio_pollfunc_store_time, - &at91_adc_trigger_handler, &at91_buffer_setup_ops, fifo_attrs); + &iio_pollfunc_store_time, &at91_adc_trigger_handler, + IIO_BUFFER_DIRECTION_IN, &at91_buffer_setup_ops, fifo_attrs); if (ret < 0) { dev_err(dev, "couldn't initialize the buffer.\n"); return ret; @@ -1718,21 +1931,23 @@ static int at91_adc_probe(struct platform_device *pdev) if (!indio_dev) return -ENOMEM; + st = iio_priv(indio_dev); + st->indio_dev = indio_dev; + + st->soc_info.platform = of_device_get_match_data(&pdev->dev); + indio_dev->name = dev_name(&pdev->dev); indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; indio_dev->info = &at91_adc_info; - indio_dev->channels = at91_adc_channels; - indio_dev->num_channels = ARRAY_SIZE(at91_adc_channels); - - st = iio_priv(indio_dev); - st->indio_dev = indio_dev; + indio_dev->channels = *st->soc_info.platform->adc_channels; + indio_dev->num_channels = st->soc_info.platform->max_channels; bitmap_set(&st->touch_st.channels_bitmask, - AT91_SAMA5D2_TOUCH_X_CHAN_IDX, 1); + st->soc_info.platform->touch_chan_x, 1); bitmap_set(&st->touch_st.channels_bitmask, - AT91_SAMA5D2_TOUCH_Y_CHAN_IDX, 1); + st->soc_info.platform->touch_chan_y, 1); bitmap_set(&st->touch_st.channels_bitmask, - AT91_SAMA5D2_TOUCH_P_CHAN_IDX, 1); + st->soc_info.platform->touch_chan_p, 1); st->oversampling_ratio = AT91_OSR_1SAMPLES; @@ -1772,7 +1987,7 @@ static int at91_adc_probe(struct platform_device *pdev) st->selected_trig = NULL; /* find the right trigger, or no trigger at all */ - for (i = 0; i < AT91_SAMA5D2_HW_TRIG_CNT + 1; i++) + for (i = 0; i < st->soc_info.platform->hw_trig_cnt + 1; i++) if (at91_adc_trigger_list[i].edge_type == edge_type) { st->selected_trig = &at91_adc_trigger_list[i]; break; @@ -1833,12 +2048,12 @@ static int at91_adc_probe(struct platform_device *pdev) goto vref_disable; } - at91_adc_hw_init(indio_dev); - ret = clk_prepare_enable(st->per_clk); if (ret) goto vref_disable; + at91_adc_hw_init(indio_dev); + platform_set_drvdata(pdev, indio_dev); ret = at91_adc_buffer_and_trigger_init(&pdev->dev, indio_dev); @@ -1857,7 +2072,7 @@ static int at91_adc_probe(struct platform_device *pdev) st->selected_trig->name); dev_info(&pdev->dev, "version: %x\n", - readl_relaxed(st->base + AT91_SAMA5D2_VERSION)); + readl_relaxed(st->base + st->soc_info.platform->layout->VERSION)); return 0; @@ -1900,7 +2115,7 @@ static __maybe_unused int at91_adc_suspend(struct device *dev) * and can be used by for other devices. * Otherwise, ADC will hog them and we can't go to suspend mode. */ - at91_adc_writel(st, AT91_SAMA5D2_CR, AT91_SAMA5D2_CR_SWRST); + at91_adc_writel(st, CR, AT91_SAMA5D2_CR_SWRST); clk_disable_unprepare(st->per_clk); regulator_disable(st->vref); @@ -1960,6 +2175,10 @@ static SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, at91_adc_resume); static const struct of_device_id at91_adc_dt_match[] = { { .compatible = "atmel,sama5d2-adc", + .data = (const void *)&sama5d2_platform, + }, { + .compatible = "microchip,sama7g5-adc", + .data = (const void *)&sama7g5_platform, }, { /* sentinel */ } @@ -1977,6 +2196,7 @@ static struct platform_driver at91_adc_driver = { }; module_platform_driver(at91_adc_driver) -MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>"); +MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@microchip.com>"); +MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com"); MODULE_DESCRIPTION("Atmel AT91 SAMA5D2 ADC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c index 5f5e8b39e4d2..a4b8be5b8f88 100644 --- a/drivers/iio/adc/axp288_adc.c +++ b/drivers/iio/adc/axp288_adc.c @@ -259,7 +259,7 @@ static int axp288_adc_probe(struct platform_device *pdev) info->irq = platform_get_irq(pdev, 0); if (info->irq < 0) return info->irq; - platform_set_drvdata(pdev, indio_dev); + info->regmap = axp20x->regmap; /* * Set ADC to enabled state at all time, including system suspend. @@ -276,31 +276,12 @@ static int axp288_adc_probe(struct platform_device *pdev) indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels); indio_dev->info = &axp288_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; - ret = iio_map_array_register(indio_dev, axp288_adc_default_maps); + + ret = devm_iio_map_array_register(&pdev->dev, indio_dev, axp288_adc_default_maps); if (ret < 0) return ret; - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&pdev->dev, "unable to register iio device\n"); - goto err_array_unregister; - } - return 0; - -err_array_unregister: - iio_map_array_unregister(indio_dev); - - return ret; -} - -static int axp288_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - - iio_device_unregister(indio_dev); - iio_map_array_unregister(indio_dev); - - return 0; + return devm_iio_device_register(&pdev->dev, indio_dev); } static const struct platform_device_id axp288_adc_id_table[] = { @@ -310,7 +291,6 @@ static const struct platform_device_id axp288_adc_id_table[] = { static struct platform_driver axp288_adc_driver = { .probe = axp288_adc_probe, - .remove = axp288_adc_remove, .id_table = axp288_adc_id_table, .driver = { .name = "axp288_adc", diff --git a/drivers/iio/adc/berlin2-adc.c b/drivers/iio/adc/berlin2-adc.c index 8b04b95b7b7a..03987d7e6b3d 100644 --- a/drivers/iio/adc/berlin2-adc.c +++ b/drivers/iio/adc/berlin2-adc.c @@ -280,6 +280,13 @@ static const struct iio_info berlin2_adc_info = { .read_raw = berlin2_adc_read_raw, }; +static void berlin2_adc_powerdown(void *regmap) +{ + regmap_update_bits(regmap, BERLIN2_SM_CTRL, + BERLIN2_SM_CTRL_ADC_POWER, 0); + +} + static int berlin2_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; @@ -293,7 +300,6 @@ static int berlin2_adc_probe(struct platform_device *pdev) return -ENOMEM; priv = iio_priv(indio_dev); - platform_set_drvdata(pdev, indio_dev); priv->regmap = syscon_node_to_regmap(parent_np); of_node_put(parent_np); @@ -333,29 +339,12 @@ static int berlin2_adc_probe(struct platform_device *pdev) BERLIN2_SM_CTRL_ADC_POWER, BERLIN2_SM_CTRL_ADC_POWER); - ret = iio_device_register(indio_dev); - if (ret) { - /* Power down the ADC */ - regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, - BERLIN2_SM_CTRL_ADC_POWER, 0); + ret = devm_add_action_or_reset(&pdev->dev, berlin2_adc_powerdown, + priv->regmap); + if (ret) return ret; - } - - return 0; -} - -static int berlin2_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct berlin2_adc_priv *priv = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - /* Power down the ADC */ - regmap_update_bits(priv->regmap, BERLIN2_SM_CTRL, - BERLIN2_SM_CTRL_ADC_POWER, 0); - return 0; + return devm_iio_device_register(&pdev->dev, indio_dev); } static const struct of_device_id berlin2_adc_match[] = { @@ -370,7 +359,6 @@ static struct platform_driver berlin2_adc_driver = { .of_match_table = berlin2_adc_match, }, .probe = berlin2_adc_probe, - .remove = berlin2_adc_remove, }; module_platform_driver(berlin2_adc_driver); diff --git a/drivers/iio/adc/da9150-gpadc.c b/drivers/iio/adc/da9150-gpadc.c index 7a7a54a7ed76..8f0d3fb63b67 100644 --- a/drivers/iio/adc/da9150-gpadc.c +++ b/drivers/iio/adc/da9150-gpadc.c @@ -330,7 +330,6 @@ static int da9150_gpadc_probe(struct platform_device *pdev) } gpadc = iio_priv(indio_dev); - platform_set_drvdata(pdev, indio_dev); gpadc->da9150 = da9150; gpadc->dev = dev; mutex_init(&gpadc->lock); @@ -347,7 +346,7 @@ static int da9150_gpadc_probe(struct platform_device *pdev) return ret; } - ret = iio_map_array_register(indio_dev, da9150_gpadc_default_maps); + ret = devm_iio_map_array_register(&pdev->dev, indio_dev, da9150_gpadc_default_maps); if (ret) { dev_err(dev, "Failed to register IIO maps: %d\n", ret); return ret; @@ -359,28 +358,7 @@ static int da9150_gpadc_probe(struct platform_device *pdev) indio_dev->channels = da9150_gpadc_channels; indio_dev->num_channels = ARRAY_SIZE(da9150_gpadc_channels); - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(dev, "Failed to register IIO device: %d\n", ret); - goto iio_map_unreg; - } - - return 0; - -iio_map_unreg: - iio_map_array_unregister(indio_dev); - - return ret; -} - -static int da9150_gpadc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - - iio_device_unregister(indio_dev); - iio_map_array_unregister(indio_dev); - - return 0; + return devm_iio_device_register(&pdev->dev, indio_dev); } static struct platform_driver da9150_gpadc_driver = { @@ -388,7 +366,6 @@ static struct platform_driver da9150_gpadc_driver = { .name = "da9150-gpadc", }, .probe = da9150_gpadc_probe, - .remove = da9150_gpadc_remove, }; module_platform_driver(da9150_gpadc_driver); diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c index 8edd6407b7c3..fd5a9404c8dc 100644 --- a/drivers/iio/adc/ep93xx_adc.c +++ b/drivers/iio/adc/ep93xx_adc.c @@ -156,15 +156,13 @@ static int ep93xx_adc_probe(struct platform_device *pdev) struct iio_dev *iiodev; struct ep93xx_adc_priv *priv; struct clk *pclk; - struct resource *res; iiodev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); if (!iiodev) return -ENOMEM; priv = iio_priv(iiodev); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(&pdev->dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c index 329c555b55cc..551e83ae573c 100644 --- a/drivers/iio/adc/fsl-imx25-gcq.c +++ b/drivers/iio/adc/fsl-imx25-gcq.c @@ -172,13 +172,35 @@ static const struct regmap_config mx25_gcq_regconfig = { .reg_stride = 4, }; +static int mx25_gcq_ext_regulator_setup(struct device *dev, + struct mx25_gcq_priv *priv, u32 refp) +{ + char reg_name[12]; + int ret; + + if (priv->vref[refp]) + return 0; + + ret = snprintf(reg_name, sizeof(reg_name), "vref-%s", + mx25_gcq_refp_names[refp]); + if (ret < 0) + return ret; + + priv->vref[refp] = devm_regulator_get_optional(dev, reg_name); + if (IS_ERR(priv->vref[refp])) + return dev_err_probe(dev, PTR_ERR(priv->vref[refp]), + "Error, trying to use external voltage reference without a %s regulator.", + reg_name); + + return 0; +} + static int mx25_gcq_setup_cfgs(struct platform_device *pdev, struct mx25_gcq_priv *priv) { struct device_node *np = pdev->dev.of_node; struct device_node *child; struct device *dev = &pdev->dev; - unsigned int refp_used[4] = {}; int ret, i; /* @@ -194,19 +216,6 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, MX25_ADCQ_CFG_IN(i) | MX25_ADCQ_CFG_REFN_NGND2); - /* - * First get all regulators to store them in channel_vref_mv if - * necessary. Later we use that information for proper IIO scale - * information. - */ - priv->vref[MX25_ADC_REFP_INT] = NULL; - priv->vref[MX25_ADC_REFP_EXT] = - devm_regulator_get_optional(dev, "vref-ext"); - priv->vref[MX25_ADC_REFP_XP] = - devm_regulator_get_optional(dev, "vref-xp"); - priv->vref[MX25_ADC_REFP_YP] = - devm_regulator_get_optional(dev, "vref-yp"); - for_each_child_of_node(np, child) { u32 reg; u32 refp = MX25_ADCQ_CFG_REFP_INT; @@ -233,11 +242,10 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, case MX25_ADC_REFP_EXT: case MX25_ADC_REFP_XP: case MX25_ADC_REFP_YP: - if (IS_ERR(priv->vref[refp])) { - dev_err(dev, "Error, trying to use external voltage reference without a vref-%s regulator.", - mx25_gcq_refp_names[refp]); + ret = mx25_gcq_ext_regulator_setup(&pdev->dev, priv, refp); + if (ret) { of_node_put(child); - return PTR_ERR(priv->vref[refp]); + return ret; } priv->channel_vref_mv[reg] = regulator_get_voltage(priv->vref[refp]); @@ -253,8 +261,6 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, return -EINVAL; } - ++refp_used[refp]; - /* * Shift the read values to the correct positions within the * register. @@ -285,15 +291,6 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, regmap_write(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK | MX25_ADCQ_CR_QSM_FQS); - /* Remove unused regulators */ - for (i = 0; i != 4; ++i) { - if (!refp_used[i]) { - if (!IS_ERR_OR_NULL(priv->vref[i])) - devm_regulator_put(priv->vref[i]); - priv->vref[i] = NULL; - } - } - return 0; } diff --git a/drivers/iio/adc/imx8qxp-adc.c b/drivers/iio/adc/imx8qxp-adc.c new file mode 100644 index 000000000000..901dd8e1b32f --- /dev/null +++ b/drivers/iio/adc/imx8qxp-adc.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * NXP i.MX8QXP ADC driver + * + * Based on the work of Haibo Chen <haibo.chen@nxp.com> + * The initial developer of the original code is Haibo Chen. + * Portions created by Haibo Chen are Copyright (C) 2018 NXP. + * All Rights Reserved. + * + * Copyright (C) 2018 NXP + * Copyright (C) 2021 Cai Huoqing + */ +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> + +#include <linux/iio/iio.h> + +#define ADC_DRIVER_NAME "imx8qxp-adc" + +/* Register map definition */ +#define IMX8QXP_ADR_ADC_CTRL 0x10 +#define IMX8QXP_ADR_ADC_STAT 0x14 +#define IMX8QXP_ADR_ADC_IE 0x18 +#define IMX8QXP_ADR_ADC_DE 0x1c +#define IMX8QXP_ADR_ADC_CFG 0x20 +#define IMX8QXP_ADR_ADC_FCTRL 0x30 +#define IMX8QXP_ADR_ADC_SWTRIG 0x34 +#define IMX8QXP_ADR_ADC_TCTRL(tid) (0xc0 + (tid) * 4) +#define IMX8QXP_ADR_ADC_CMDH(cid) (0x100 + (cid) * 8) +#define IMX8QXP_ADR_ADC_CMDL(cid) (0x104 + (cid) * 8) +#define IMX8QXP_ADR_ADC_RESFIFO 0x300 +#define IMX8QXP_ADR_ADC_TST 0xffc + +/* ADC bit shift */ +#define IMX8QXP_ADC_IE_FWMIE_MASK GENMASK(1, 0) +#define IMX8QXP_ADC_CTRL_FIFO_RESET_MASK BIT(8) +#define IMX8QXP_ADC_CTRL_SOFTWARE_RESET_MASK BIT(1) +#define IMX8QXP_ADC_CTRL_ADC_EN_MASK BIT(0) +#define IMX8QXP_ADC_TCTRL_TCMD_MASK GENMASK(31, 24) +#define IMX8QXP_ADC_TCTRL_TDLY_MASK GENMASK(23, 16) +#define IMX8QXP_ADC_TCTRL_TPRI_MASK GENMASK(15, 8) +#define IMX8QXP_ADC_TCTRL_HTEN_MASK GENMASK(7, 0) +#define IMX8QXP_ADC_CMDL_CSCALE_MASK GENMASK(13, 8) +#define IMX8QXP_ADC_CMDL_MODE_MASK BIT(7) +#define IMX8QXP_ADC_CMDL_DIFF_MASK BIT(6) +#define IMX8QXP_ADC_CMDL_ABSEL_MASK BIT(5) +#define IMX8QXP_ADC_CMDL_ADCH_MASK GENMASK(2, 0) +#define IMX8QXP_ADC_CMDH_NEXT_MASK GENMASK(31, 24) +#define IMX8QXP_ADC_CMDH_LOOP_MASK GENMASK(23, 16) +#define IMX8QXP_ADC_CMDH_AVGS_MASK GENMASK(15, 12) +#define IMX8QXP_ADC_CMDH_STS_MASK BIT(8) +#define IMX8QXP_ADC_CMDH_LWI_MASK GENMASK(7, 7) +#define IMX8QXP_ADC_CMDH_CMPEN_MASK GENMASK(0, 0) +#define IMX8QXP_ADC_CFG_PWREN_MASK BIT(28) +#define IMX8QXP_ADC_CFG_PUDLY_MASK GENMASK(23, 16) +#define IMX8QXP_ADC_CFG_REFSEL_MASK GENMASK(7, 6) +#define IMX8QXP_ADC_CFG_PWRSEL_MASK GENMASK(5, 4) +#define IMX8QXP_ADC_CFG_TPRICTRL_MASK GENMASK(3, 0) +#define IMX8QXP_ADC_FCTRL_FWMARK_MASK GENMASK(20, 16) +#define IMX8QXP_ADC_FCTRL_FCOUNT_MASK GENMASK(4, 0) +#define IMX8QXP_ADC_RESFIFO_VAL_MASK GENMASK(18, 3) + +/* ADC PARAMETER*/ +#define IMX8QXP_ADC_CMDL_CHANNEL_SCALE_FULL GENMASK(5, 0) +#define IMX8QXP_ADC_CMDL_SEL_A_A_B_CHANNEL 0 +#define IMX8QXP_ADC_CMDL_STANDARD_RESOLUTION 0 +#define IMX8QXP_ADC_CMDL_MODE_SINGLE 0 +#define IMX8QXP_ADC_CMDH_LWI_INCREMENT_DIS 0 +#define IMX8QXP_ADC_CMDH_CMPEN_DIS 0 +#define IMX8QXP_ADC_PAUSE_EN BIT(31) +#define IMX8QXP_ADC_TCTRL_TPRI_PRIORITY_HIGH 0 + +#define IMX8QXP_ADC_TCTRL_HTEN_HW_TIRG_DIS 0 + +#define IMX8QXP_ADC_TIMEOUT msecs_to_jiffies(100) + +struct imx8qxp_adc { + struct device *dev; + void __iomem *regs; + struct clk *clk; + struct clk *ipg_clk; + struct regulator *vref; + /* Serialise ADC channel reads */ + struct mutex lock; + struct completion completion; +}; + +#define IMX8QXP_ADC_CHAN(_idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .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 imx8qxp_adc_iio_channels[] = { + IMX8QXP_ADC_CHAN(0), + IMX8QXP_ADC_CHAN(1), + IMX8QXP_ADC_CHAN(2), + IMX8QXP_ADC_CHAN(3), + IMX8QXP_ADC_CHAN(4), + IMX8QXP_ADC_CHAN(5), + IMX8QXP_ADC_CHAN(6), + IMX8QXP_ADC_CHAN(7), +}; + +static void imx8qxp_adc_reset(struct imx8qxp_adc *adc) +{ + u32 ctrl; + + /*software reset, need to clear the set bit*/ + ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_CTRL); + ctrl |= FIELD_PREP(IMX8QXP_ADC_CTRL_SOFTWARE_RESET_MASK, 1); + writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); + udelay(10); + ctrl &= ~FIELD_PREP(IMX8QXP_ADC_CTRL_SOFTWARE_RESET_MASK, 1); + writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); + + /* reset the fifo */ + ctrl |= FIELD_PREP(IMX8QXP_ADC_CTRL_FIFO_RESET_MASK, 1); + writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); +} + +static void imx8qxp_adc_reg_config(struct imx8qxp_adc *adc, int channel) +{ + u32 adc_cfg, adc_tctrl, adc_cmdl, adc_cmdh; + + /* ADC configuration */ + adc_cfg = FIELD_PREP(IMX8QXP_ADC_CFG_PWREN_MASK, 1) | + FIELD_PREP(IMX8QXP_ADC_CFG_PUDLY_MASK, 0x80)| + FIELD_PREP(IMX8QXP_ADC_CFG_REFSEL_MASK, 0) | + FIELD_PREP(IMX8QXP_ADC_CFG_PWRSEL_MASK, 3) | + FIELD_PREP(IMX8QXP_ADC_CFG_TPRICTRL_MASK, 0); + writel(adc_cfg, adc->regs + IMX8QXP_ADR_ADC_CFG); + + /* config the trigger control */ + adc_tctrl = FIELD_PREP(IMX8QXP_ADC_TCTRL_TCMD_MASK, 1) | + FIELD_PREP(IMX8QXP_ADC_TCTRL_TDLY_MASK, 0) | + FIELD_PREP(IMX8QXP_ADC_TCTRL_TPRI_MASK, IMX8QXP_ADC_TCTRL_TPRI_PRIORITY_HIGH) | + FIELD_PREP(IMX8QXP_ADC_TCTRL_HTEN_MASK, IMX8QXP_ADC_TCTRL_HTEN_HW_TIRG_DIS); + writel(adc_tctrl, adc->regs + IMX8QXP_ADR_ADC_TCTRL(0)); + + /* config the cmd */ + adc_cmdl = FIELD_PREP(IMX8QXP_ADC_CMDL_CSCALE_MASK, IMX8QXP_ADC_CMDL_CHANNEL_SCALE_FULL) | + FIELD_PREP(IMX8QXP_ADC_CMDL_MODE_MASK, IMX8QXP_ADC_CMDL_STANDARD_RESOLUTION) | + FIELD_PREP(IMX8QXP_ADC_CMDL_DIFF_MASK, IMX8QXP_ADC_CMDL_MODE_SINGLE) | + FIELD_PREP(IMX8QXP_ADC_CMDL_ABSEL_MASK, IMX8QXP_ADC_CMDL_SEL_A_A_B_CHANNEL) | + FIELD_PREP(IMX8QXP_ADC_CMDL_ADCH_MASK, channel); + writel(adc_cmdl, adc->regs + IMX8QXP_ADR_ADC_CMDL(0)); + + adc_cmdh = FIELD_PREP(IMX8QXP_ADC_CMDH_NEXT_MASK, 0) | + FIELD_PREP(IMX8QXP_ADC_CMDH_LOOP_MASK, 0) | + FIELD_PREP(IMX8QXP_ADC_CMDH_AVGS_MASK, 7) | + FIELD_PREP(IMX8QXP_ADC_CMDH_STS_MASK, 0) | + FIELD_PREP(IMX8QXP_ADC_CMDH_LWI_MASK, IMX8QXP_ADC_CMDH_LWI_INCREMENT_DIS) | + FIELD_PREP(IMX8QXP_ADC_CMDH_CMPEN_MASK, IMX8QXP_ADC_CMDH_CMPEN_DIS); + writel(adc_cmdh, adc->regs + IMX8QXP_ADR_ADC_CMDH(0)); +} + +static void imx8qxp_adc_fifo_config(struct imx8qxp_adc *adc) +{ + u32 fifo_ctrl, interrupt_en; + + fifo_ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_FCTRL); + fifo_ctrl &= ~IMX8QXP_ADC_FCTRL_FWMARK_MASK; + /* set the watermark level to 1 */ + fifo_ctrl |= FIELD_PREP(IMX8QXP_ADC_FCTRL_FWMARK_MASK, 0); + writel(fifo_ctrl, adc->regs + IMX8QXP_ADR_ADC_FCTRL); + + /* FIFO Watermark Interrupt Enable */ + interrupt_en = readl(adc->regs + IMX8QXP_ADR_ADC_IE); + interrupt_en |= FIELD_PREP(IMX8QXP_ADC_IE_FWMIE_MASK, 1); + writel(interrupt_en, adc->regs + IMX8QXP_ADR_ADC_IE); +} + +static void imx8qxp_adc_disable(struct imx8qxp_adc *adc) +{ + u32 ctrl; + + ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_CTRL); + ctrl &= ~FIELD_PREP(IMX8QXP_ADC_CTRL_ADC_EN_MASK, 1); + writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); +} + +static int imx8qxp_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct imx8qxp_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + + u32 ctrl, vref_uv; + long ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + pm_runtime_get_sync(dev); + + mutex_lock(&adc->lock); + reinit_completion(&adc->completion); + + imx8qxp_adc_reg_config(adc, chan->channel); + + imx8qxp_adc_fifo_config(adc); + + /* adc enable */ + ctrl = readl(adc->regs + IMX8QXP_ADR_ADC_CTRL); + ctrl |= FIELD_PREP(IMX8QXP_ADC_CTRL_ADC_EN_MASK, 1); + writel(ctrl, adc->regs + IMX8QXP_ADR_ADC_CTRL); + /* adc start */ + writel(1, adc->regs + IMX8QXP_ADR_ADC_SWTRIG); + + ret = wait_for_completion_interruptible_timeout(&adc->completion, + IMX8QXP_ADC_TIMEOUT); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_sync_autosuspend(dev); + + if (ret == 0) { + mutex_unlock(&adc->lock); + return -ETIMEDOUT; + } + if (ret < 0) { + mutex_unlock(&adc->lock); + return ret; + } + + *val = FIELD_GET(IMX8QXP_ADC_RESFIFO_VAL_MASK, + readl(adc->regs + IMX8QXP_ADR_ADC_RESFIFO)); + + mutex_unlock(&adc->lock); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + vref_uv = regulator_get_voltage(adc->vref); + *val = vref_uv / 1000; + *val2 = 12; + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *val = clk_get_rate(adc->clk) / 3; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static irqreturn_t imx8qxp_adc_isr(int irq, void *dev_id) +{ + struct imx8qxp_adc *adc = dev_id; + u32 fifo_count; + + fifo_count = FIELD_GET(IMX8QXP_ADC_FCTRL_FCOUNT_MASK, + readl(adc->regs + IMX8QXP_ADR_ADC_FCTRL)); + + if (fifo_count) + complete(&adc->completion); + + return IRQ_HANDLED; +} + +static int imx8qxp_adc_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct imx8qxp_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + + if (!readval || reg % 4 || reg > IMX8QXP_ADR_ADC_TST) + return -EINVAL; + + pm_runtime_get_sync(dev); + + *readval = readl(adc->regs + reg); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_sync_autosuspend(dev); + + return 0; +} + +static const struct iio_info imx8qxp_adc_iio_info = { + .read_raw = &imx8qxp_adc_read_raw, + .debugfs_reg_access = &imx8qxp_adc_reg_access, +}; + +static int imx8qxp_adc_probe(struct platform_device *pdev) +{ + struct imx8qxp_adc *adc; + struct iio_dev *indio_dev; + struct device *dev = &pdev->dev; + int irq; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) { + dev_err(dev, "Failed allocating iio device\n"); + return -ENOMEM; + } + + adc = iio_priv(indio_dev); + adc->dev = dev; + + mutex_init(&adc->lock); + adc->regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(adc->regs)) + return PTR_ERR(adc->regs); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + adc->clk = devm_clk_get(dev, "per"); + if (IS_ERR(adc->clk)) + return dev_err_probe(dev, PTR_ERR(adc->clk), "Failed getting clock\n"); + + adc->ipg_clk = devm_clk_get(dev, "ipg"); + if (IS_ERR(adc->ipg_clk)) + return dev_err_probe(dev, PTR_ERR(adc->ipg_clk), "Failed getting clock\n"); + + adc->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(adc->vref)) + return dev_err_probe(dev, PTR_ERR(adc->vref), "Failed getting reference voltage\n"); + + ret = regulator_enable(adc->vref); + if (ret) { + dev_err(dev, "Can't enable adc reference top voltage\n"); + return ret; + } + + platform_set_drvdata(pdev, indio_dev); + + init_completion(&adc->completion); + + indio_dev->name = ADC_DRIVER_NAME; + indio_dev->info = &imx8qxp_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = imx8qxp_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(imx8qxp_adc_iio_channels); + + ret = clk_prepare_enable(adc->clk); + if (ret) { + dev_err(&pdev->dev, "Could not prepare or enable the clock.\n"); + goto error_regulator_disable; + } + + ret = clk_prepare_enable(adc->ipg_clk); + if (ret) { + dev_err(&pdev->dev, "Could not prepare or enable the clock.\n"); + goto error_adc_clk_disable; + } + + ret = devm_request_irq(dev, irq, imx8qxp_adc_isr, 0, ADC_DRIVER_NAME, adc); + if (ret < 0) { + dev_err(dev, "Failed requesting irq, irq = %d\n", irq); + goto error_ipg_clk_disable; + } + + imx8qxp_adc_reset(adc); + + ret = iio_device_register(indio_dev); + if (ret) { + imx8qxp_adc_disable(adc); + dev_err(dev, "Couldn't register the device.\n"); + goto error_ipg_clk_disable; + } + + pm_runtime_set_active(dev); + pm_runtime_set_autosuspend_delay(dev, 50); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + + return 0; + +error_ipg_clk_disable: + clk_disable_unprepare(adc->ipg_clk); +error_adc_clk_disable: + clk_disable_unprepare(adc->clk); +error_regulator_disable: + regulator_disable(adc->vref); + + return ret; +} + +static int imx8qxp_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct imx8qxp_adc *adc = iio_priv(indio_dev); + struct device *dev = adc->dev; + + pm_runtime_get_sync(dev); + + iio_device_unregister(indio_dev); + + imx8qxp_adc_disable(adc); + + clk_disable_unprepare(adc->clk); + clk_disable_unprepare(adc->ipg_clk); + regulator_disable(adc->vref); + + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + + return 0; +} + +static __maybe_unused int imx8qxp_adc_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct imx8qxp_adc *adc = iio_priv(indio_dev); + + imx8qxp_adc_disable(adc); + + clk_disable_unprepare(adc->clk); + clk_disable_unprepare(adc->ipg_clk); + regulator_disable(adc->vref); + + return 0; +} + +static __maybe_unused int imx8qxp_adc_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct imx8qxp_adc *adc = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(adc->vref); + if (ret) { + dev_err(dev, "Can't enable adc reference top voltage, err = %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(adc->clk); + if (ret) { + dev_err(dev, "Could not prepare or enable clock.\n"); + goto err_disable_reg; + } + + ret = clk_prepare_enable(adc->ipg_clk); + if (ret) { + dev_err(dev, "Could not prepare or enable clock.\n"); + goto err_unprepare_clk; + } + + imx8qxp_adc_reset(adc); + + return 0; + +err_unprepare_clk: + clk_disable_unprepare(adc->clk); + +err_disable_reg: + regulator_disable(adc->vref); + + return ret; +} + +static const struct dev_pm_ops imx8qxp_adc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(imx8qxp_adc_runtime_suspend, imx8qxp_adc_runtime_resume, NULL) +}; + +static const struct of_device_id imx8qxp_adc_match[] = { + { .compatible = "nxp,imx8qxp-adc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx8qxp_adc_match); + +static struct platform_driver imx8qxp_adc_driver = { + .probe = imx8qxp_adc_probe, + .remove = imx8qxp_adc_remove, + .driver = { + .name = ADC_DRIVER_NAME, + .of_match_table = imx8qxp_adc_match, + .pm = &imx8qxp_adc_pm_ops, + }, +}; + +module_platform_driver(imx8qxp_adc_driver); + +MODULE_DESCRIPTION("i.MX8QuadXPlus ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/intel_mrfld_adc.c b/drivers/iio/adc/intel_mrfld_adc.c index 75394350eb4c..616de0c3a049 100644 --- a/drivers/iio/adc/intel_mrfld_adc.c +++ b/drivers/iio/adc/intel_mrfld_adc.c @@ -205,8 +205,6 @@ static int mrfld_adc_probe(struct platform_device *pdev) if (ret) return ret; - platform_set_drvdata(pdev, indio_dev); - indio_dev->name = pdev->name; indio_dev->channels = mrfld_adc_channels; @@ -214,28 +212,11 @@ static int mrfld_adc_probe(struct platform_device *pdev) indio_dev->info = &mrfld_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; - ret = iio_map_array_register(indio_dev, iio_maps); + ret = devm_iio_map_array_register(dev, indio_dev, iio_maps); if (ret) return ret; - ret = devm_iio_device_register(dev, indio_dev); - if (ret < 0) - goto err_array_unregister; - - return 0; - -err_array_unregister: - iio_map_array_unregister(indio_dev); - return ret; -} - -static int mrfld_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - - iio_map_array_unregister(indio_dev); - - return 0; + return devm_iio_device_register(dev, indio_dev); } static const struct platform_device_id mrfld_adc_id_table[] = { @@ -249,7 +230,6 @@ static struct platform_driver mrfld_adc_driver = { .name = "mrfld_bcove_adc", }, .probe = mrfld_adc_probe, - .remove = mrfld_adc_remove, .id_table = mrfld_adc_id_table, }; module_platform_driver(mrfld_adc_driver); diff --git a/drivers/iio/adc/lp8788_adc.c b/drivers/iio/adc/lp8788_adc.c index 8fb57e375529..6d9b354bc705 100644 --- a/drivers/iio/adc/lp8788_adc.c +++ b/drivers/iio/adc/lp8788_adc.c @@ -163,7 +163,8 @@ static struct iio_map lp8788_default_iio_maps[] = { { } }; -static int lp8788_iio_map_register(struct iio_dev *indio_dev, +static int lp8788_iio_map_register(struct device *dev, + struct iio_dev *indio_dev, struct lp8788_platform_data *pdata, struct lp8788_adc *adc) { @@ -173,7 +174,7 @@ static int lp8788_iio_map_register(struct iio_dev *indio_dev, map = (!pdata || !pdata->adc_pdata) ? lp8788_default_iio_maps : pdata->adc_pdata; - ret = iio_map_array_register(indio_dev, map); + ret = devm_iio_map_array_register(dev, indio_dev, map); if (ret) { dev_err(&indio_dev->dev, "iio map err: %d\n", ret); return ret; @@ -196,9 +197,8 @@ static int lp8788_adc_probe(struct platform_device *pdev) adc = iio_priv(indio_dev); adc->lp = lp; - platform_set_drvdata(pdev, indio_dev); - ret = lp8788_iio_map_register(indio_dev, lp->pdata, adc); + ret = lp8788_iio_map_register(&pdev->dev, indio_dev, lp->pdata, adc); if (ret) return ret; @@ -210,32 +210,11 @@ static int lp8788_adc_probe(struct platform_device *pdev) indio_dev->channels = lp8788_adc_channels; indio_dev->num_channels = ARRAY_SIZE(lp8788_adc_channels); - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(&pdev->dev, "iio dev register err: %d\n", ret); - goto err_iio_device; - } - - return 0; - -err_iio_device: - iio_map_array_unregister(indio_dev); - return ret; -} - -static int lp8788_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - - iio_device_unregister(indio_dev); - iio_map_array_unregister(indio_dev); - - return 0; + return devm_iio_device_register(&pdev->dev, indio_dev); } static struct platform_driver lp8788_adc_driver = { .probe = lp8788_adc_probe, - .remove = lp8788_adc_remove, .driver = { .name = LP8788_DEV_ADC, }, diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index b753658bb41e..4daf1d576c4e 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -60,6 +60,9 @@ #define MAX1027_NAVG_32 (0x03 << 2) #define MAX1027_AVG_EN BIT(4) +/* Device can achieve 300ksps so we assume a 3.33us conversion delay */ +#define MAX1027_CONVERSION_UDELAY 4 + enum max1027_id { max1027, max1029, @@ -172,18 +175,53 @@ static const struct iio_chan_spec max1231_channels[] = { MAX1X31_CHANNELS(12), }; +/* + * These devices are able to scan from 0 to N, N being the highest voltage + * channel requested by the user. The temperature can be included or not, + * but cannot be retrieved alone. Based on the below + * ->available_scan_masks, the core will select the most appropriate + * ->active_scan_mask and the "minimum" number of channels will be + * scanned and pushed to the buffers. + * + * For example, if the user wants channels 1, 4 and 5, all channels from + * 0 to 5 will be scanned and pushed to the IIO buffers. The core will then + * filter out the unneeded samples based on the ->active_scan_mask that has + * been selected and only channels 1, 4 and 5 will be available to the user + * in the shared buffer. + */ +#define MAX1X27_SCAN_MASK_TEMP BIT(0) + +#define MAX1X27_SCAN_MASKS(temp) \ + GENMASK(1, 1 - (temp)), GENMASK(2, 1 - (temp)), \ + GENMASK(3, 1 - (temp)), GENMASK(4, 1 - (temp)), \ + GENMASK(5, 1 - (temp)), GENMASK(6, 1 - (temp)), \ + GENMASK(7, 1 - (temp)), GENMASK(8, 1 - (temp)) + +#define MAX1X29_SCAN_MASKS(temp) \ + MAX1X27_SCAN_MASKS(temp), \ + GENMASK(9, 1 - (temp)), GENMASK(10, 1 - (temp)), \ + GENMASK(11, 1 - (temp)), GENMASK(12, 1 - (temp)) + +#define MAX1X31_SCAN_MASKS(temp) \ + MAX1X29_SCAN_MASKS(temp), \ + GENMASK(13, 1 - (temp)), GENMASK(14, 1 - (temp)), \ + GENMASK(15, 1 - (temp)), GENMASK(16, 1 - (temp)) + static const unsigned long max1027_available_scan_masks[] = { - 0x000001ff, + MAX1X27_SCAN_MASKS(0), + MAX1X27_SCAN_MASKS(1), 0x00000000, }; static const unsigned long max1029_available_scan_masks[] = { - 0x00001fff, + MAX1X29_SCAN_MASKS(0), + MAX1X29_SCAN_MASKS(1), 0x00000000, }; static const unsigned long max1031_available_scan_masks[] = { - 0x0001ffff, + MAX1X31_SCAN_MASKS(0), + MAX1X31_SCAN_MASKS(1), 0x00000000, }; @@ -232,10 +270,65 @@ struct max1027_state { struct iio_trigger *trig; __be16 *buffer; struct mutex lock; + struct completion complete; u8 reg ____cacheline_aligned; }; +static int max1027_wait_eoc(struct iio_dev *indio_dev) +{ + struct max1027_state *st = iio_priv(indio_dev); + unsigned int conversion_time = MAX1027_CONVERSION_UDELAY; + int ret; + + if (st->spi->irq) { + ret = wait_for_completion_timeout(&st->complete, + msecs_to_jiffies(1000)); + reinit_completion(&st->complete); + if (!ret) + return -ETIMEDOUT; + } else { + if (indio_dev->active_scan_mask) + conversion_time *= hweight32(*indio_dev->active_scan_mask); + + usleep_range(conversion_time, conversion_time * 2); + } + + return 0; +} + +/* Scan from chan 0 to the highest requested channel. Include temperature on demand. */ +static int max1027_configure_chans_and_start(struct iio_dev *indio_dev) +{ + struct max1027_state *st = iio_priv(indio_dev); + + st->reg = MAX1027_CONV_REG | MAX1027_SCAN_0_N; + st->reg |= MAX1027_CHAN(fls(*indio_dev->active_scan_mask) - 2); + if (*indio_dev->active_scan_mask & MAX1X27_SCAN_MASK_TEMP) + st->reg |= MAX1027_TEMP; + + return spi_write(st->spi, &st->reg, 1); +} + +static int max1027_enable_trigger(struct iio_dev *indio_dev, bool enable) +{ + struct max1027_state *st = iio_priv(indio_dev); + + st->reg = MAX1027_SETUP_REG | MAX1027_REF_MODE2; + + /* + * Start acquisition on: + * MODE0: external hardware trigger wired to the cnvst input pin + * MODE2: conversion register write + */ + if (enable) + st->reg |= MAX1027_CKS_MODE0; + else + st->reg |= MAX1027_CKS_MODE2; + + return spi_write(st->spi, &st->reg, 1); +} + static int max1027_read_single_value(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) @@ -243,19 +336,9 @@ static int max1027_read_single_value(struct iio_dev *indio_dev, int ret; struct max1027_state *st = iio_priv(indio_dev); - if (iio_buffer_enabled(indio_dev)) { - dev_warn(&indio_dev->dev, "trigger mode already enabled"); - return -EBUSY; - } - - /* Start acquisition on conversion register write */ - st->reg = MAX1027_SETUP_REG | MAX1027_REF_MODE2 | MAX1027_CKS_MODE2; - ret = spi_write(st->spi, &st->reg, 1); - if (ret < 0) { - dev_err(&indio_dev->dev, - "Failed to configure setup register\n"); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) return ret; - } /* Configure conversion register with the requested chan */ st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) | @@ -266,18 +349,24 @@ static int max1027_read_single_value(struct iio_dev *indio_dev, if (ret < 0) { dev_err(&indio_dev->dev, "Failed to configure conversion register\n"); + iio_device_release_direct_mode(indio_dev); return ret; } /* * For an unknown reason, when we use the mode "10" (write * conversion register), the interrupt doesn't occur every time. - * So we just wait 1 ms. + * So we just wait the maximum conversion time and deliver the value. */ - mdelay(1); + ret = max1027_wait_eoc(indio_dev); + if (ret) + return ret; /* Read result */ ret = spi_read(st->spi, st->buffer, (chan->type == IIO_TEMP) ? 4 : 2); + + iio_device_release_direct_mode(indio_dev); + if (ret < 0) return ret; @@ -327,8 +416,8 @@ static int max1027_read_raw(struct iio_dev *indio_dev, } static int max1027_debugfs_reg_access(struct iio_dev *indio_dev, - unsigned reg, unsigned writeval, - unsigned *readval) + unsigned int reg, unsigned int writeval, + unsigned int *readval) { struct max1027_state *st = iio_priv(indio_dev); u8 *val = (u8 *)st->buffer; @@ -343,61 +432,96 @@ static int max1027_debugfs_reg_access(struct iio_dev *indio_dev, return spi_write(st->spi, val, 1); } -static int max1027_validate_trigger(struct iio_dev *indio_dev, - struct iio_trigger *trig) +static int max1027_set_cnvst_trigger_state(struct iio_trigger *trig, bool state) { - struct max1027_state *st = iio_priv(indio_dev); + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + int ret; + + /* + * In order to disable the convst trigger, start acquisition on + * conversion register write, which basically disables triggering + * conversions upon cnvst changes and thus has the effect of disabling + * the external hardware trigger. + */ + ret = max1027_enable_trigger(indio_dev, state); + if (ret) + return ret; - if (st->trig != trig) - return -EINVAL; + if (state) { + ret = max1027_configure_chans_and_start(indio_dev); + if (ret) + return ret; + } return 0; } -static int max1027_set_trigger_state(struct iio_trigger *trig, bool state) +static int max1027_read_scan(struct iio_dev *indio_dev) { - struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); struct max1027_state *st = iio_priv(indio_dev); + unsigned int scanned_chans; int ret; - if (state) { - /* Start acquisition on cnvst */ - st->reg = MAX1027_SETUP_REG | MAX1027_CKS_MODE0 | - MAX1027_REF_MODE2; - ret = spi_write(st->spi, &st->reg, 1); - if (ret < 0) - return ret; + scanned_chans = fls(*indio_dev->active_scan_mask) - 1; + if (*indio_dev->active_scan_mask & MAX1X27_SCAN_MASK_TEMP) + scanned_chans++; - /* Scan from 0 to max */ - st->reg = MAX1027_CONV_REG | MAX1027_CHAN(0) | - MAX1027_SCAN_N_M | MAX1027_TEMP; - ret = spi_write(st->spi, &st->reg, 1); - if (ret < 0) - return ret; - } else { - /* Start acquisition on conversion register write */ - st->reg = MAX1027_SETUP_REG | MAX1027_CKS_MODE2 | - MAX1027_REF_MODE2; - ret = spi_write(st->spi, &st->reg, 1); - if (ret < 0) - return ret; - } + /* fill buffer with all channel */ + ret = spi_read(st->spi, st->buffer, scanned_chans * 2); + if (ret < 0) + return ret; + + iio_push_to_buffers(indio_dev, st->buffer); return 0; } +static irqreturn_t max1027_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct max1027_state *st = iio_priv(indio_dev); + + /* + * If buffers are disabled (raw read) or when using external triggers, + * we just need to unlock the waiters which will then handle the data. + * + * When using the internal trigger, we must hand-off the choice of the + * handler to the core which will then lookup through the interrupt tree + * for the right handler registered with iio_triggered_buffer_setup() + * to execute, as this trigger might very well be used in conjunction + * with another device. The core will then call the relevant handler to + * perform the data processing step. + */ + if (!iio_buffer_enabled(indio_dev)) + complete(&st->complete); + else + iio_trigger_poll(indio_dev->trig); + + return IRQ_HANDLED; +} + static irqreturn_t max1027_trigger_handler(int irq, void *private) { struct iio_poll_func *pf = private; struct iio_dev *indio_dev = pf->indio_dev; - struct max1027_state *st = iio_priv(indio_dev); + int ret; - pr_debug("%s(irq=%d, private=0x%p)\n", __func__, irq, private); + if (!iio_trigger_using_own(indio_dev)) { + ret = max1027_configure_chans_and_start(indio_dev); + if (ret) + goto out; - /* fill buffer with all channel */ - spi_read(st->spi, st->buffer, indio_dev->masklength * 2); + /* This is a threaded handler, it is fine to wait for an IRQ */ + ret = max1027_wait_eoc(indio_dev); + if (ret) + goto out; + } - iio_push_to_buffers(indio_dev, st->buffer); + ret = max1027_read_scan(indio_dev); +out: + if (ret) + dev_err(&indio_dev->dev, + "Cannot read scanned values (%d)\n", ret); iio_trigger_notify_done(indio_dev->trig); @@ -406,12 +530,11 @@ static irqreturn_t max1027_trigger_handler(int irq, void *private) static const struct iio_trigger_ops max1027_trigger_ops = { .validate_device = &iio_trigger_validate_own_device, - .set_trigger_state = &max1027_set_trigger_state, + .set_trigger_state = &max1027_set_cnvst_trigger_state, }; static const struct iio_info max1027_info = { .read_raw = &max1027_read_raw, - .validate_trigger = &max1027_validate_trigger, .debugfs_reg_access = &max1027_debugfs_reg_access, }; @@ -421,10 +544,8 @@ static int max1027_probe(struct spi_device *spi) struct iio_dev *indio_dev; struct max1027_state *st; - pr_debug("%s: probe(spi = 0x%p)\n", __func__, spi); - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); - if (indio_dev == NULL) { + if (!indio_dev) { pr_err("Can't allocate iio device\n"); return -ENOMEM; } @@ -434,6 +555,7 @@ static int max1027_probe(struct spi_device *spi) st->info = &max1027_chip_info_tbl[spi_get_device_id(spi)->driver_data]; mutex_init(&st->lock); + init_completion(&st->complete); indio_dev->name = spi_get_device_id(spi)->name; indio_dev->info = &max1027_info; @@ -443,26 +565,26 @@ static int max1027_probe(struct spi_device *spi) indio_dev->available_scan_masks = st->info->available_scan_masks; st->buffer = devm_kmalloc_array(&indio_dev->dev, - indio_dev->num_channels, 2, - GFP_KERNEL); - if (st->buffer == NULL) { - dev_err(&indio_dev->dev, "Can't allocate buffer\n"); + indio_dev->num_channels, 2, + GFP_KERNEL); + if (!st->buffer) return -ENOMEM; + + /* Enable triggered buffers */ + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + &iio_pollfunc_store_time, + &max1027_trigger_handler, + NULL); + if (ret < 0) { + dev_err(&indio_dev->dev, "Failed to setup buffer\n"); + return ret; } + /* If there is an EOC interrupt, register the cnvst hardware trigger */ if (spi->irq) { - ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, - &iio_pollfunc_store_time, - &max1027_trigger_handler, - NULL); - if (ret < 0) { - dev_err(&indio_dev->dev, "Failed to setup buffer\n"); - return ret; - } - st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-trigger", indio_dev->name); - if (st->trig == NULL) { + if (!st->trig) { ret = -ENOMEM; dev_err(&indio_dev->dev, "Failed to allocate iio trigger\n"); @@ -479,12 +601,9 @@ static int max1027_probe(struct spi_device *spi) return ret; } - ret = devm_request_threaded_irq(&spi->dev, spi->irq, - iio_trigger_generic_data_rdy_poll, - NULL, - IRQF_TRIGGER_FALLING, - spi->dev.driver->name, - st->trig); + ret = devm_request_irq(&spi->dev, spi->irq, max1027_handler, + IRQF_TRIGGER_FALLING, + spi->dev.driver->name, indio_dev); if (ret < 0) { dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n"); return ret; @@ -507,6 +626,11 @@ static int max1027_probe(struct spi_device *spi) return ret; } + /* Assume conversion on register write for now */ + ret = max1027_enable_trigger(indio_dev, false); + if (ret) + return ret; + return devm_iio_device_register(&spi->dev, indio_dev); } diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index f2b576c69949..eef55ed4814a 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -1577,6 +1577,11 @@ static const struct of_device_id max1363_of_match[] = { }; MODULE_DEVICE_TABLE(of, max1363_of_match); +static void max1363_reg_disable(void *reg) +{ + regulator_disable(reg); +} + static int max1363_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1590,7 +1595,8 @@ static int max1363_probe(struct i2c_client *client, if (!indio_dev) return -ENOMEM; - ret = iio_map_array_register(indio_dev, client->dev.platform_data); + ret = devm_iio_map_array_register(&client->dev, indio_dev, + client->dev.platform_data); if (ret < 0) return ret; @@ -1598,17 +1604,16 @@ static int max1363_probe(struct i2c_client *client, mutex_init(&st->lock); st->reg = devm_regulator_get(&client->dev, "vcc"); - if (IS_ERR(st->reg)) { - ret = PTR_ERR(st->reg); - goto error_unregister_map; - } + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); ret = regulator_enable(st->reg); if (ret) - goto error_unregister_map; + return ret; - /* this is only used for device removal purposes */ - i2c_set_clientdata(client, indio_dev); + ret = devm_add_action_or_reset(&client->dev, max1363_reg_disable, st->reg); + if (ret) + return ret; st->chip_info = device_get_match_data(&client->dev); if (!st->chip_info) @@ -1622,13 +1627,17 @@ static int max1363_probe(struct i2c_client *client, ret = regulator_enable(vref); if (ret) - goto error_disable_reg; + return ret; + + ret = devm_add_action_or_reset(&client->dev, max1363_reg_disable, vref); + if (ret) + return ret; + st->vref = vref; vref_uv = regulator_get_voltage(vref); - if (vref_uv <= 0) { - ret = -EINVAL; - goto error_disable_reg; - } + if (vref_uv <= 0) + return -EINVAL; + st->vref_uv = vref_uv; } @@ -1640,13 +1649,12 @@ static int max1363_probe(struct i2c_client *client, st->send = max1363_smbus_send; st->recv = max1363_smbus_recv; } else { - ret = -EOPNOTSUPP; - goto error_disable_reg; + return -EOPNOTSUPP; } ret = max1363_alloc_scan_masks(indio_dev); if (ret) - goto error_disable_reg; + return ret; indio_dev->name = id->name; indio_dev->channels = st->chip_info->channels; @@ -1655,12 +1663,12 @@ static int max1363_probe(struct i2c_client *client, indio_dev->modes = INDIO_DIRECT_MODE; ret = max1363_initial_setup(st); if (ret < 0) - goto error_disable_reg; + return ret; - ret = iio_triggered_buffer_setup(indio_dev, NULL, - &max1363_trigger_handler, NULL); + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, + &max1363_trigger_handler, NULL); if (ret) - goto error_disable_reg; + return ret; if (client->irq) { ret = devm_request_threaded_irq(&client->dev, st->client->irq, @@ -1671,39 +1679,10 @@ static int max1363_probe(struct i2c_client *client, indio_dev); if (ret) - goto error_uninit_buffer; + return ret; } - ret = iio_device_register(indio_dev); - if (ret < 0) - goto error_uninit_buffer; - - return 0; - -error_uninit_buffer: - iio_triggered_buffer_cleanup(indio_dev); -error_disable_reg: - if (st->vref) - regulator_disable(st->vref); - regulator_disable(st->reg); -error_unregister_map: - iio_map_array_unregister(indio_dev); - return ret; -} - -static int max1363_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct max1363_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - if (st->vref) - regulator_disable(st->vref); - regulator_disable(st->reg); - iio_map_array_unregister(indio_dev); - - return 0; + return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id max1363_id[] = { @@ -1756,7 +1735,6 @@ static struct i2c_driver max1363_driver = { .of_match_table = max1363_of_match, }, .probe = max1363_probe, - .remove = max1363_remove, .id_table = max1363_id, }; module_i2c_driver(max1363_driver); diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c index bb70b51d25b1..976c235f3079 100644 --- a/drivers/iio/adc/nau7802.c +++ b/drivers/iio/adc/nau7802.c @@ -428,8 +428,6 @@ static int nau7802_probe(struct i2c_client *client, st = iio_priv(indio_dev); - i2c_set_clientdata(client, indio_dev); - indio_dev->name = dev_name(&client->dev); indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &nau7802_info; @@ -495,13 +493,13 @@ static int nau7802_probe(struct i2c_client *client, * will enable them back when we will need them.. */ if (client->irq) { - ret = request_threaded_irq(client->irq, - NULL, - nau7802_eoc_trigger, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT | - IRQF_NO_AUTOEN, - client->dev.driver->name, - indio_dev); + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, + nau7802_eoc_trigger, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT | + IRQF_NO_AUTOEN, + client->dev.driver->name, + indio_dev); if (ret) { /* * What may happen here is that our IRQ controller is @@ -526,7 +524,7 @@ static int nau7802_probe(struct i2c_client *client, ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL2, NAU7802_CTRL2_CRS(st->sample_rate)); if (ret) - goto error_free_irq; + return ret; } /* Setup the ADC channels available on the board */ @@ -536,36 +534,7 @@ static int nau7802_probe(struct i2c_client *client, mutex_init(&st->lock); mutex_init(&st->data_lock); - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "Couldn't register the device.\n"); - goto error_device_register; - } - - return 0; - -error_device_register: - mutex_destroy(&st->lock); - mutex_destroy(&st->data_lock); -error_free_irq: - if (client->irq) - free_irq(client->irq, indio_dev); - - return ret; -} - -static int nau7802_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - struct nau7802_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - mutex_destroy(&st->lock); - mutex_destroy(&st->data_lock); - if (client->irq) - free_irq(client->irq, indio_dev); - - return 0; + return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id nau7802_i2c_id[] = { @@ -582,7 +551,6 @@ MODULE_DEVICE_TABLE(of, nau7802_dt_ids); static struct i2c_driver nau7802_driver = { .probe = nau7802_probe, - .remove = nau7802_remove, .id_table = nau7802_i2c_id, .driver = { .name = "nau7802", diff --git a/drivers/iio/adc/rn5t618-adc.c b/drivers/iio/adc/rn5t618-adc.c index c56fccb2c8e1..7d891b4ea461 100644 --- a/drivers/iio/adc/rn5t618-adc.c +++ b/drivers/iio/adc/rn5t618-adc.c @@ -197,13 +197,6 @@ static struct iio_map rn5t618_maps[] = { { /* sentinel */ } }; -static void unregister_map(void *data) -{ - struct iio_dev *iio_dev = (struct iio_dev *) data; - - iio_map_array_unregister(iio_dev); -} - static int rn5t618_adc_probe(struct platform_device *pdev) { int ret; @@ -254,11 +247,7 @@ static int rn5t618_adc_probe(struct platform_device *pdev) return ret; } - ret = iio_map_array_register(iio_dev, rn5t618_maps); - if (ret < 0) - return ret; - - ret = devm_add_action_or_reset(adc->dev, unregister_map, iio_dev); + ret = devm_iio_map_array_register(adc->dev, iio_dev, rn5t618_maps); if (ret < 0) return ret; diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c index a237fe469a30..a56a0d7337ca 100644 --- a/drivers/iio/adc/rockchip_saradc.c +++ b/drivers/iio/adc/rockchip_saradc.c @@ -319,7 +319,6 @@ static int rockchip_saradc_probe(struct platform_device *pdev) struct rockchip_saradc *info = NULL; struct device_node *np = pdev->dev.of_node; struct iio_dev *indio_dev = NULL; - struct resource *mem; const struct of_device_id *match; int ret; int irq; @@ -348,8 +347,7 @@ static int rockchip_saradc_probe(struct platform_device *pdev) return -EINVAL; } - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - info->regs = devm_ioremap_resource(&pdev->dev, mem); + info->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(info->regs)) return PTR_ERR(info->regs); diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c index db902aef2abe..c8e48881c37f 100644 --- a/drivers/iio/adc/ti-adc108s102.c +++ b/drivers/iio/adc/ti-adc108s102.c @@ -75,9 +75,9 @@ struct adc108s102_state { * rx_buf: |XX|R0|R1|R2|R3|R4|R5|R6|R7|tt|tt|tt|tt| * * tx_buf: 8 channel read commands, plus 1 dummy command - * rx_buf: 1 dummy response, 8 channel responses, plus 64-bit timestamp + * rx_buf: 1 dummy response, 8 channel responses */ - __be16 rx_buf[13] ____cacheline_aligned; + __be16 rx_buf[9] ____cacheline_aligned; __be16 tx_buf[9] ____cacheline_aligned; }; @@ -149,9 +149,10 @@ static irqreturn_t adc108s102_trigger_handler(int irq, void *p) goto out_notify; /* Skip the dummy response in the first slot */ - iio_push_to_buffers_with_timestamp(indio_dev, - (u8 *)&st->rx_buf[1], - iio_get_time_ns(indio_dev)); + iio_push_to_buffers_with_ts_unaligned(indio_dev, + &st->rx_buf[1], + st->ring_xfer.len - sizeof(st->rx_buf[1]), + iio_get_time_ns(indio_dev)); out_notify: iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ti-ads8344.c b/drivers/iio/adc/ti-ads8344.c index a345a30d74fa..c96d2a9ba924 100644 --- a/drivers/iio/adc/ti-ads8344.c +++ b/drivers/iio/adc/ti-ads8344.c @@ -133,6 +133,11 @@ static const struct iio_info ads8344_info = { .read_raw = ads8344_read_raw, }; +static void ads8344_reg_disable(void *data) +{ + regulator_disable(data); +} + static int ads8344_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -161,26 +166,11 @@ static int ads8344_probe(struct spi_device *spi) if (ret) return ret; - spi_set_drvdata(spi, indio_dev); - - ret = iio_device_register(indio_dev); - if (ret) { - regulator_disable(adc->reg); + ret = devm_add_action_or_reset(&spi->dev, ads8344_reg_disable, adc->reg); + if (ret) return ret; - } - - return 0; -} - -static int ads8344_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ads8344 *adc = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - regulator_disable(adc->reg); - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct of_device_id ads8344_of_match[] = { @@ -195,7 +185,6 @@ static struct spi_driver ads8344_driver = { .of_match_table = ads8344_of_match, }, .probe = ads8344_probe, - .remove = ads8344_remove, }; module_spi_driver(ads8344_driver); diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c index c6416ad795ca..afdb59e0b526 100644 --- a/drivers/iio/adc/twl6030-gpadc.c +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -900,7 +900,7 @@ static int twl6030_gpadc_probe(struct platform_device *pdev) ret = pdata->calibrate(gpadc); if (ret < 0) { - dev_err(&pdev->dev, "failed to read calibration registers\n"); + dev_err(dev, "failed to read calibration registers\n"); return ret; } @@ -914,14 +914,14 @@ static int twl6030_gpadc_probe(struct platform_device *pdev) ret = twl6030_gpadc_enable_irq(TWL6030_GPADC_RT_SW1_EOC_MASK); if (ret < 0) { - dev_err(&pdev->dev, "failed to enable GPADC interrupt\n"); + dev_err(dev, "failed to enable GPADC interrupt\n"); return ret; } ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, TWL6030_GPADCS, TWL6030_REG_TOGGLE1); if (ret < 0) { - dev_err(&pdev->dev, "failed to enable GPADC module\n"); + dev_err(dev, "failed to enable GPADC module\n"); return ret; } diff --git a/drivers/iio/buffer/industrialio-triggered-buffer.c b/drivers/iio/buffer/industrialio-triggered-buffer.c index f77c4538141e..8d4fc97d1005 100644 --- a/drivers/iio/buffer/industrialio-triggered-buffer.c +++ b/drivers/iio/buffer/industrialio-triggered-buffer.c @@ -19,6 +19,7 @@ * @indio_dev: IIO device structure * @h: Function which will be used as pollfunc top half * @thread: Function which will be used as pollfunc bottom half + * @direction: Direction of the data stream (in/out). * @setup_ops: Buffer setup functions to use for this device. * If NULL the default setup functions for triggered * buffers will be used. @@ -38,6 +39,7 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev, irqreturn_t (*h)(int irq, void *p), irqreturn_t (*thread)(int irq, void *p), + enum iio_buffer_direction direction, const struct iio_buffer_setup_ops *setup_ops, const struct attribute **buffer_attrs) { @@ -68,6 +70,7 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev, /* Flag that polled ring buffering is possible */ indio_dev->modes |= INDIO_BUFFER_TRIGGERED; + buffer->direction = direction; buffer->attrs = buffer_attrs; ret = iio_device_attach_buffer(indio_dev, buffer); @@ -105,13 +108,14 @@ int devm_iio_triggered_buffer_setup_ext(struct device *dev, struct iio_dev *indio_dev, irqreturn_t (*h)(int irq, void *p), irqreturn_t (*thread)(int irq, void *p), + enum iio_buffer_direction direction, const struct iio_buffer_setup_ops *ops, const struct attribute **buffer_attrs) { int ret; - ret = iio_triggered_buffer_setup_ext(indio_dev, h, thread, ops, - buffer_attrs); + ret = iio_triggered_buffer_setup_ext(indio_dev, h, thread, direction, + ops, buffer_attrs); if (ret) return ret; diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index 516eb3465de1..416d35a61ae2 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -138,10 +138,60 @@ static void iio_kfifo_buffer_release(struct iio_buffer *buffer) kfree(kf); } +static size_t iio_kfifo_buf_space_available(struct iio_buffer *r) +{ + struct iio_kfifo *kf = iio_to_kfifo(r); + size_t avail; + + mutex_lock(&kf->user_lock); + avail = kfifo_avail(&kf->kf); + mutex_unlock(&kf->user_lock); + + return avail; +} + +static int iio_kfifo_remove_from(struct iio_buffer *r, void *data) +{ + int ret; + struct iio_kfifo *kf = iio_to_kfifo(r); + + if (kfifo_size(&kf->kf) < 1) + return -EBUSY; + + ret = kfifo_out(&kf->kf, data, 1); + if (ret != 1) + return -EBUSY; + + wake_up_interruptible_poll(&r->pollq, EPOLLOUT | EPOLLWRNORM); + + return 0; +} + +static int iio_kfifo_write(struct iio_buffer *r, size_t n, + const char __user *buf) +{ + struct iio_kfifo *kf = iio_to_kfifo(r); + int ret, copied; + + mutex_lock(&kf->user_lock); + if (!kfifo_initialized(&kf->kf) || n < kfifo_esize(&kf->kf)) + ret = -EINVAL; + else + ret = kfifo_from_user(&kf->kf, buf, n, &copied); + mutex_unlock(&kf->user_lock); + if (ret) + return ret; + + return copied; +} + static const struct iio_buffer_access_funcs kfifo_access_funcs = { .store_to = &iio_store_to_kfifo, .read = &iio_read_kfifo, .data_available = iio_kfifo_buf_data_available, + .remove_from = &iio_kfifo_remove_from, + .write = &iio_kfifo_write, + .space_available = &iio_kfifo_buf_space_available, .request_update = &iio_request_update_kfifo, .set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo, .set_length = &iio_set_length_kfifo, diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index c03667e62732..c30657e10ee1 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -118,6 +118,19 @@ config SCD30_SERIAL To compile this driver as a module, choose M here: the module will be called scd30_serial. +config SCD4X + tristate "SCD4X carbon dioxide sensor driver" + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + depends on I2C + select CRC8 + help + Say Y here to build support for the Sensirion SCD4X sensor with carbon + dioxide, relative humidity and temperature sensing capabilities. + + To compile this driver as a module, choose M here: the module will + be called scd4x. + config SENSIRION_SGP30 tristate "Sensirion SGPxx gas sensors" depends on I2C @@ -170,6 +183,17 @@ config SPS30_SERIAL To compile this driver as a module, choose M here: the module will be called sps30_serial. +config SENSEAIR_SUNRISE_CO2 + tristate "Senseair Sunrise 006-0-0007 CO2 sensor" + depends on I2C + select REGMAP_I2C + help + Say yes here to build support for Senseair Sunrise 006-0-0007 CO2 + sensor. + + To compile this driver as a module, choose M here: the + module will be called sunrise_co2. + 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 d07af581f234..a11e777a7a00 100644 --- a/drivers/iio/chemical/Makefile +++ b/drivers/iio/chemical/Makefile @@ -15,6 +15,8 @@ obj-$(CONFIG_PMS7003) += pms7003.o obj-$(CONFIG_SCD30_CORE) += scd30_core.o obj-$(CONFIG_SCD30_I2C) += scd30_i2c.o obj-$(CONFIG_SCD30_SERIAL) += scd30_serial.o +obj-$(CONFIG_SCD4X) += scd4x.o +obj-$(CONFIG_SENSEAIR_SUNRISE_CO2) += sunrise_co2.o obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o obj-$(CONFIG_SENSIRION_SGP40) += sgp40.o obj-$(CONFIG_SPS30) += sps30.o diff --git a/drivers/iio/chemical/scd4x.c b/drivers/iio/chemical/scd4x.c new file mode 100644 index 000000000000..ebebcb117ba2 --- /dev/null +++ b/drivers/iio/chemical/scd4x.c @@ -0,0 +1,691 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sensirion SCD4X carbon dioxide sensor i2c driver + * + * Copyright (C) 2021 Protonic Holland + * Author: Roan van Dijk <roan@protonic.nl> + * + * I2C slave address: 0x62 + * + * Datasheets: + * https://www.sensirion.com/file/datasheet_scd4x + */ + +#include <asm/unaligned.h> +#include <linux/crc8.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/types.h> +#include <linux/kernel.h> +#include <linux/mutex.h> +#include <linux/string.h> +#include <linux/sysfs.h> +#include <linux/types.h> + +#define SCD4X_CRC8_POLYNOMIAL 0x31 +#define SCD4X_TIMEOUT_ERR 1000 +#define SCD4X_READ_BUF_SIZE 9 +#define SCD4X_COMMAND_BUF_SIZE 2 +#define SCD4X_WRITE_BUF_SIZE 5 +#define SCD4X_FRC_MIN_PPM 0 +#define SCD4X_FRC_MAX_PPM 2000 +#define SCD4X_READY_MASK 0x01 + +/*Commands SCD4X*/ +enum scd4x_cmd { + CMD_START_MEAS = 0x21b1, + CMD_READ_MEAS = 0xec05, + CMD_STOP_MEAS = 0x3f86, + CMD_SET_TEMP_OFFSET = 0x241d, + CMD_GET_TEMP_OFFSET = 0x2318, + CMD_FRC = 0x362f, + CMD_SET_ASC = 0x2416, + CMD_GET_ASC = 0x2313, + CMD_GET_DATA_READY = 0xe4b8, +}; + +enum scd4x_channel_idx { + SCD4X_CO2, + SCD4X_TEMP, + SCD4X_HR, +}; + +struct scd4x_state { + struct i2c_client *client; + /* maintain access to device, to prevent concurrent reads/writes */ + struct mutex lock; + struct regulator *vdd; +}; + +DECLARE_CRC8_TABLE(scd4x_crc8_table); + +static int scd4x_i2c_xfer(struct scd4x_state *state, char *txbuf, int txsize, + char *rxbuf, int rxsize) +{ + struct i2c_client *client = state->client; + int ret; + + ret = i2c_master_send(client, txbuf, txsize); + + if (ret < 0) + return ret; + if (ret != txsize) + return -EIO; + + if (rxsize == 0) + return 0; + + ret = i2c_master_recv(client, rxbuf, rxsize); + if (ret < 0) + return ret; + if (ret != rxsize) + return -EIO; + + return 0; +} + +static int scd4x_send_command(struct scd4x_state *state, enum scd4x_cmd cmd) +{ + char buf[SCD4X_COMMAND_BUF_SIZE]; + int ret; + + /* + * Measurement needs to be stopped before sending commands. + * Except stop and start command. + */ + if ((cmd != CMD_STOP_MEAS) && (cmd != CMD_START_MEAS)) { + + ret = scd4x_send_command(state, CMD_STOP_MEAS); + if (ret) + return ret; + + /* execution time for stopping measurement */ + msleep_interruptible(500); + } + + put_unaligned_be16(cmd, buf); + ret = scd4x_i2c_xfer(state, buf, 2, buf, 0); + if (ret) + return ret; + + if ((cmd != CMD_STOP_MEAS) && (cmd != CMD_START_MEAS)) { + ret = scd4x_send_command(state, CMD_START_MEAS); + if (ret) + return ret; + } + + return 0; +} + +static int scd4x_read(struct scd4x_state *state, enum scd4x_cmd cmd, + void *response, int response_sz) +{ + struct i2c_client *client = state->client; + char buf[SCD4X_READ_BUF_SIZE]; + char *rsp = response; + int i, ret; + char crc; + + /* + * Measurement needs to be stopped before sending commands. + * Except for reading measurement and data ready command. + */ + if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS)) { + ret = scd4x_send_command(state, CMD_STOP_MEAS); + if (ret) + return ret; + + /* execution time for stopping measurement */ + msleep_interruptible(500); + } + + /* CRC byte for every 2 bytes of data */ + response_sz += response_sz / 2; + + put_unaligned_be16(cmd, buf); + ret = scd4x_i2c_xfer(state, buf, 2, buf, response_sz); + if (ret) + return ret; + + for (i = 0; i < response_sz; i += 3) { + crc = crc8(scd4x_crc8_table, buf + i, 2, CRC8_INIT_VALUE); + if (crc != buf[i + 2]) { + dev_err(&client->dev, "CRC error\n"); + return -EIO; + } + + *rsp++ = buf[i]; + *rsp++ = buf[i + 1]; + } + + /* start measurement */ + if ((cmd != CMD_GET_DATA_READY) && (cmd != CMD_READ_MEAS)) { + ret = scd4x_send_command(state, CMD_START_MEAS); + if (ret) + return ret; + } + + return 0; +} + +static int scd4x_write(struct scd4x_state *state, enum scd4x_cmd cmd, uint16_t arg) +{ + char buf[SCD4X_WRITE_BUF_SIZE]; + int ret; + char crc; + + put_unaligned_be16(cmd, buf); + put_unaligned_be16(arg, buf + 2); + + crc = crc8(scd4x_crc8_table, buf + 2, 2, CRC8_INIT_VALUE); + buf[4] = crc; + + /* measurement needs to be stopped before sending commands */ + ret = scd4x_send_command(state, CMD_STOP_MEAS); + if (ret) + return ret; + + /* execution time */ + msleep_interruptible(500); + + ret = scd4x_i2c_xfer(state, buf, SCD4X_WRITE_BUF_SIZE, buf, 0); + if (ret) + return ret; + + /* start measurement, except for forced calibration command */ + if (cmd != CMD_FRC) { + ret = scd4x_send_command(state, CMD_START_MEAS); + if (ret) + return ret; + } + + return 0; +} + +static int scd4x_write_and_fetch(struct scd4x_state *state, enum scd4x_cmd cmd, + uint16_t arg, void *response, int response_sz) +{ + struct i2c_client *client = state->client; + char buf[SCD4X_READ_BUF_SIZE]; + char *rsp = response; + int i, ret; + char crc; + + ret = scd4x_write(state, CMD_FRC, arg); + if (ret) + goto err; + + /* execution time */ + msleep_interruptible(400); + + /* CRC byte for every 2 bytes of data */ + response_sz += response_sz / 2; + + ret = i2c_master_recv(client, buf, response_sz); + if (ret < 0) + goto err; + if (ret != response_sz) { + ret = -EIO; + goto err; + } + + for (i = 0; i < response_sz; i += 3) { + crc = crc8(scd4x_crc8_table, buf + i, 2, CRC8_INIT_VALUE); + if (crc != buf[i + 2]) { + dev_err(&client->dev, "CRC error\n"); + ret = -EIO; + goto err; + } + + *rsp++ = buf[i]; + *rsp++ = buf[i + 1]; + } + + return scd4x_send_command(state, CMD_START_MEAS); + +err: + /* + * on error try to start the measurement, + * puts sensor back into continuous measurement + */ + scd4x_send_command(state, CMD_START_MEAS); + + return ret; +} + +static int scd4x_read_meas(struct scd4x_state *state, uint16_t *meas) +{ + int i, ret; + __be16 buf[3]; + + ret = scd4x_read(state, CMD_READ_MEAS, buf, sizeof(buf)); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(buf); i++) + meas[i] = be16_to_cpu(buf[i]); + + return 0; +} + +static int scd4x_wait_meas_poll(struct scd4x_state *state) +{ + struct i2c_client *client = state->client; + int tries = 6; + int ret; + + do { + __be16 bval; + uint16_t val; + + ret = scd4x_read(state, CMD_GET_DATA_READY, &bval, sizeof(bval)); + if (ret) + return -EIO; + val = be16_to_cpu(bval); + + /* new measurement available */ + if (val & 0x7FF) + return 0; + + msleep_interruptible(1000); + } while (--tries); + + /* try to start sensor on timeout */ + ret = scd4x_send_command(state, CMD_START_MEAS); + if (ret) + dev_err(&client->dev, "failed to start measurement: %d\n", ret); + + return -ETIMEDOUT; +} + +static int scd4x_read_poll(struct scd4x_state *state, uint16_t *buf) +{ + int ret; + + ret = scd4x_wait_meas_poll(state); + if (ret) + return ret; + + return scd4x_read_meas(state, buf); +} + +static int scd4x_read_channel(struct scd4x_state *state, int chan) +{ + int ret; + uint16_t buf[3]; + + ret = scd4x_read_poll(state, buf); + if (ret) + return ret; + + return buf[chan]; +} + +static int scd4x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct scd4x_state *state = iio_priv(indio_dev); + int ret; + __be16 tmp; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + mutex_lock(&state->lock); + ret = scd4x_read_channel(state, chan->address); + mutex_unlock(&state->lock); + + iio_device_release_direct_mode(indio_dev); + if (ret < 0) + return ret; + + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_TEMP) { + *val = 175000; + *val2 = 65536; + return IIO_VAL_FRACTIONAL; + } else if (chan->type == IIO_HUMIDITYRELATIVE) { + *val = 100000; + *val2 = 65536; + return IIO_VAL_FRACTIONAL; + } + return -EINVAL; + case IIO_CHAN_INFO_OFFSET: + *val = -16852; + *val2 = 114286; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + mutex_lock(&state->lock); + ret = scd4x_read(state, CMD_GET_TEMP_OFFSET, &tmp, sizeof(tmp)); + mutex_unlock(&state->lock); + if (ret) + return ret; + + *val = be16_to_cpu(tmp); + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int scd4x_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct scd4x_state *state = iio_priv(indio_dev); + int ret = 0; + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + mutex_lock(&state->lock); + ret = scd4x_write(state, CMD_SET_TEMP_OFFSET, val); + mutex_unlock(&state->lock); + + return ret; + default: + return -EINVAL; + } +} + +static ssize_t calibration_auto_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct scd4x_state *state = iio_priv(indio_dev); + int ret; + __be16 bval; + u16 val; + + mutex_lock(&state->lock); + ret = scd4x_read(state, CMD_GET_ASC, &bval, sizeof(bval)); + mutex_unlock(&state->lock); + if (ret) { + dev_err(dev, "failed to read automatic calibration"); + return ret; + } + + val = (be16_to_cpu(bval) & SCD4X_READY_MASK) ? 1 : 0; + + return sprintf(buf, "%d\n", val); +} + +static ssize_t calibration_auto_enable_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 scd4x_state *state = iio_priv(indio_dev); + bool val; + int ret; + uint16_t value; + + ret = kstrtobool(buf, &val); + if (ret) + return ret; + + value = val; + + mutex_lock(&state->lock); + ret = scd4x_write(state, CMD_SET_ASC, value); + mutex_unlock(&state->lock); + if (ret) + dev_err(dev, "failed to set automatic calibration"); + + return ret ?: len; +} + +static ssize_t calibration_forced_value_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 scd4x_state *state = iio_priv(indio_dev); + uint16_t val, arg; + int ret; + + ret = kstrtou16(buf, 0, &arg); + if (ret) + return ret; + + if (arg < SCD4X_FRC_MIN_PPM || arg > SCD4X_FRC_MAX_PPM) + return -EINVAL; + + mutex_lock(&state->lock); + ret = scd4x_write_and_fetch(state, CMD_FRC, arg, &val, sizeof(val)); + mutex_unlock(&state->lock); + + if (val == 0xff) { + dev_err(dev, "forced calibration has failed"); + return -EINVAL; + } + + return ret ?: len; +} + +static IIO_DEVICE_ATTR_RW(calibration_auto_enable, 0); +static IIO_DEVICE_ATTR_WO(calibration_forced_value, 0); + +static IIO_CONST_ATTR(calibration_forced_value_available, + __stringify([SCD4X_FRC_MIN_PPM 1 SCD4X_FRC_MAX_PPM])); + +static struct attribute *scd4x_attrs[] = { + &iio_dev_attr_calibration_auto_enable.dev_attr.attr, + &iio_dev_attr_calibration_forced_value.dev_attr.attr, + &iio_const_attr_calibration_forced_value_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group scd4x_attr_group = { + .attrs = scd4x_attrs, +}; + +static const struct iio_info scd4x_info = { + .attrs = &scd4x_attr_group, + .read_raw = scd4x_read_raw, + .write_raw = scd4x_write_raw, +}; + +static const struct iio_chan_spec scd4x_channels[] = { + { + .type = IIO_CONCENTRATION, + .channel2 = IIO_MOD_CO2, + .modified = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .address = SCD4X_CO2, + .scan_index = SCD4X_CO2, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .address = SCD4X_TEMP, + .scan_index = SCD4X_TEMP, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + }, + { + .type = IIO_HUMIDITYRELATIVE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .address = SCD4X_HR, + .scan_index = SCD4X_HR, + .scan_type = { + .sign = 'u', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_BE, + }, + }, +}; + +static int __maybe_unused scd4x_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct scd4x_state *state = iio_priv(indio_dev); + int ret; + + ret = scd4x_send_command(state, CMD_STOP_MEAS); + if (ret) + return ret; + + return regulator_disable(state->vdd); +} + +static int __maybe_unused scd4x_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct scd4x_state *state = iio_priv(indio_dev); + int ret; + + ret = regulator_enable(state->vdd); + if (ret) + return ret; + + return scd4x_send_command(state, CMD_START_MEAS); +} + +static __maybe_unused SIMPLE_DEV_PM_OPS(scd4x_pm_ops, scd4x_suspend, scd4x_resume); + +static void scd4x_stop_meas(void *state) +{ + scd4x_send_command(state, CMD_STOP_MEAS); +} + +static void scd4x_disable_regulator(void *data) +{ + struct scd4x_state *state = data; + + regulator_disable(state->vdd); +} + +static irqreturn_t scd4x_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct scd4x_state *state = iio_priv(indio_dev); + struct { + uint16_t data[3]; + int64_t ts __aligned(8); + } scan; + int ret; + + memset(&scan, 0, sizeof(scan)); + mutex_lock(&state->lock); + ret = scd4x_read_poll(state, scan.data); + mutex_unlock(&state->lock); + if (ret) + goto out; + + iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev)); +out: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int scd4x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + static const unsigned long scd4x_scan_masks[] = { 0x07, 0x00 }; + struct device *dev = &client->dev; + struct iio_dev *indio_dev; + struct scd4x_state *state; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*state)); + if (!indio_dev) + return -ENOMEM; + + state = iio_priv(indio_dev); + mutex_init(&state->lock); + state->client = client; + crc8_populate_msb(scd4x_crc8_table, SCD4X_CRC8_POLYNOMIAL); + + indio_dev->info = &scd4x_info; + indio_dev->name = client->name; + indio_dev->channels = scd4x_channels; + indio_dev->num_channels = ARRAY_SIZE(scd4x_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->available_scan_masks = scd4x_scan_masks; + + state->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(state->vdd)) + return dev_err_probe(dev, PTR_ERR(state->vdd), "failed to get regulator\n"); + + ret = regulator_enable(state->vdd); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, scd4x_disable_regulator, state); + if (ret) + return ret; + + ret = scd4x_send_command(state, CMD_STOP_MEAS); + if (ret) { + dev_err(dev, "failed to stop measurement: %d\n", ret); + return ret; + } + + /* execution time */ + msleep_interruptible(500); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, scd4x_trigger_handler, NULL); + if (ret) + return ret; + + ret = scd4x_send_command(state, CMD_START_MEAS); + if (ret) { + dev_err(dev, "failed to start measurement: %d\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(dev, scd4x_stop_meas, state); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id scd4x_dt_ids[] = { + { .compatible = "sensirion,scd40" }, + { .compatible = "sensirion,scd41" }, + { } +}; +MODULE_DEVICE_TABLE(of, scd4x_dt_ids); + +static struct i2c_driver scd4x_i2c_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = scd4x_dt_ids, + .pm = &scd4x_pm_ops + }, + .probe = scd4x_probe, +}; +module_i2c_driver(scd4x_i2c_driver); + +MODULE_AUTHOR("Roan van Dijk <roan@protonic.nl>"); +MODULE_DESCRIPTION("Sensirion SCD4X carbon dioxide sensor core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/sunrise_co2.c b/drivers/iio/chemical/sunrise_co2.c new file mode 100644 index 000000000000..233bd0f379c9 --- /dev/null +++ b/drivers/iio/chemical/sunrise_co2.c @@ -0,0 +1,537 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Senseair Sunrise 006-0-0007 CO2 sensor driver. + * + * Copyright (C) 2021 Jacopo Mondi + * + * List of features not yet supported by the driver: + * - controllable EN pin + * - single-shot operations using the nDRY pin. + * - ABC/target calibration + */ + +#include <linux/bitops.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/time64.h> + +#include <linux/iio/iio.h> + +#define DRIVER_NAME "sunrise_co2" + +#define SUNRISE_ERROR_STATUS_REG 0x00 +#define SUNRISE_CO2_FILTERED_COMP_REG 0x06 +#define SUNRISE_CHIP_TEMPERATURE_REG 0x08 +#define SUNRISE_CALIBRATION_STATUS_REG 0x81 +#define SUNRISE_CALIBRATION_COMMAND_REG 0x82 +#define SUNRISE_CALIBRATION_FACTORY_CMD 0x7c02 +#define SUNRISE_CALIBRATION_BACKGROUND_CMD 0x7c06 +/* + * The calibration timeout is not characterized in the datasheet. + * Use 30 seconds as a reasonable upper limit. + */ +#define SUNRISE_CALIBRATION_TIMEOUT_US (30 * USEC_PER_SEC) + +struct sunrise_dev { + struct i2c_client *client; + struct regmap *regmap; + /* Protects access to IIO attributes. */ + struct mutex lock; + bool ignore_nak; +}; + +/* Custom regmap read/write operations: perform unlocked access to the i2c bus. */ + +static int sunrise_regmap_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, size_t val_size) +{ + struct i2c_client *client = context; + struct sunrise_dev *sunrise = i2c_get_clientdata(client); + union i2c_smbus_data data; + int ret; + + if (reg_size != 1 || !val_size) + return -EINVAL; + + memset(&data, 0, sizeof(data)); + data.block[0] = val_size; + + /* + * Wake up sensor by sending sensor address: START, sensor address, + * STOP. Sensor will not ACK this byte. + * + * The chip enters a low power state after 15ms without + * communications or after a complete read/write sequence. + */ + __i2c_smbus_xfer(client->adapter, client->addr, + sunrise->ignore_nak ? I2C_M_IGNORE_NAK : 0, + I2C_SMBUS_WRITE, 0, I2C_SMBUS_BYTE_DATA, &data); + + usleep_range(500, 1500); + + ret = __i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_READ, ((u8 *)reg_buf)[0], + I2C_SMBUS_I2C_BLOCK_DATA, &data); + if (ret < 0) + return ret; + + memcpy(val_buf, &data.block[1], data.block[0]); + + return 0; +} + +static int sunrise_regmap_write(void *context, const void *val_buf, size_t count) +{ + struct i2c_client *client = context; + struct sunrise_dev *sunrise = i2c_get_clientdata(client); + union i2c_smbus_data data; + + /* Discard reg address from values count. */ + if (!count) + return -EINVAL; + count--; + + memset(&data, 0, sizeof(data)); + data.block[0] = count; + memcpy(&data.block[1], (u8 *)val_buf + 1, count); + + __i2c_smbus_xfer(client->adapter, client->addr, + sunrise->ignore_nak ? I2C_M_IGNORE_NAK : 0, + I2C_SMBUS_WRITE, 0, I2C_SMBUS_BYTE_DATA, &data); + + usleep_range(500, 1500); + + return __i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, ((u8 *)val_buf)[0], + I2C_SMBUS_I2C_BLOCK_DATA, &data); +} + +/* + * Sunrise i2c read/write operations: lock the i2c segment to avoid losing the + * wake up session. Use custom regmap operations that perform unlocked access to + * the i2c bus. + */ +static int sunrise_read_byte(struct sunrise_dev *sunrise, u8 reg) +{ + const struct i2c_client *client = sunrise->client; + const struct device *dev = &client->dev; + unsigned int val; + int ret; + + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); + ret = regmap_read(sunrise->regmap, reg, &val); + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + if (ret) { + dev_err(dev, "Read byte failed: reg 0x%02x (%d)\n", reg, ret); + return ret; + } + + return val; +} + +static int sunrise_read_word(struct sunrise_dev *sunrise, u8 reg, u16 *val) +{ + const struct i2c_client *client = sunrise->client; + const struct device *dev = &client->dev; + __be16 be_val; + int ret; + + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); + ret = regmap_bulk_read(sunrise->regmap, reg, &be_val, sizeof(be_val)); + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + if (ret) { + dev_err(dev, "Read word failed: reg 0x%02x (%d)\n", reg, ret); + return ret; + } + + *val = be16_to_cpu(be_val); + + return 0; +} + +static int sunrise_write_byte(struct sunrise_dev *sunrise, u8 reg, u8 val) +{ + const struct i2c_client *client = sunrise->client; + const struct device *dev = &client->dev; + int ret; + + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); + ret = regmap_write(sunrise->regmap, reg, val); + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + if (ret) + dev_err(dev, "Write byte failed: reg 0x%02x (%d)\n", reg, ret); + + return ret; +} + +static int sunrise_write_word(struct sunrise_dev *sunrise, u8 reg, u16 data) +{ + const struct i2c_client *client = sunrise->client; + const struct device *dev = &client->dev; + __be16 be_data = cpu_to_be16(data); + int ret; + + i2c_lock_bus(client->adapter, I2C_LOCK_SEGMENT); + ret = regmap_bulk_write(sunrise->regmap, reg, &be_data, sizeof(be_data)); + i2c_unlock_bus(client->adapter, I2C_LOCK_SEGMENT); + if (ret) + dev_err(dev, "Write word failed: reg 0x%02x (%d)\n", reg, ret); + + return ret; +} + +/* Trigger a calibration cycle. */ + +enum { + SUNRISE_CALIBRATION_FACTORY, + SUNRISE_CALIBRATION_BACKGROUND, +}; + +static const struct sunrise_calib_data { + u16 cmd; + u8 bit; + const char * const name; +} calib_data[] = { + [SUNRISE_CALIBRATION_FACTORY] = { + SUNRISE_CALIBRATION_FACTORY_CMD, + BIT(2), + "factory_calibration", + }, + [SUNRISE_CALIBRATION_BACKGROUND] = { + SUNRISE_CALIBRATION_BACKGROUND_CMD, + BIT(5), + "background_calibration", + }, +}; + +static int sunrise_calibrate(struct sunrise_dev *sunrise, + const struct sunrise_calib_data *data) +{ + unsigned int status; + int ret; + + /* Reset the calibration status reg. */ + ret = sunrise_write_byte(sunrise, SUNRISE_CALIBRATION_STATUS_REG, 0x00); + if (ret) + return ret; + + /* Write a calibration command and poll the calibration status bit. */ + ret = sunrise_write_word(sunrise, SUNRISE_CALIBRATION_COMMAND_REG, data->cmd); + if (ret) + return ret; + + dev_dbg(&sunrise->client->dev, "%s in progress\n", data->name); + + /* + * Calibration takes several seconds, so the sleep time between reads + * can be pretty relaxed. + */ + return read_poll_timeout(sunrise_read_byte, status, status & data->bit, + 200000, SUNRISE_CALIBRATION_TIMEOUT_US, false, + sunrise, SUNRISE_CALIBRATION_STATUS_REG); +} + +static ssize_t sunrise_cal_factory_write(struct iio_dev *iiodev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct sunrise_dev *sunrise = iio_priv(iiodev); + bool enable; + int ret; + + ret = kstrtobool(buf, &enable); + if (ret) + return ret; + + if (!enable) + return len; + + mutex_lock(&sunrise->lock); + ret = sunrise_calibrate(sunrise, &calib_data[SUNRISE_CALIBRATION_FACTORY]); + mutex_unlock(&sunrise->lock); + if (ret) + return ret; + + return len; +} + +static ssize_t sunrise_cal_background_write(struct iio_dev *iiodev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct sunrise_dev *sunrise = iio_priv(iiodev); + bool enable; + int ret; + + ret = kstrtobool(buf, &enable); + if (ret) + return ret; + + if (!enable) + return len; + + mutex_lock(&sunrise->lock); + ret = sunrise_calibrate(sunrise, &calib_data[SUNRISE_CALIBRATION_BACKGROUND]); + mutex_unlock(&sunrise->lock); + if (ret) + return ret; + + return len; +} + + /* Enumerate and retrieve the chip error status. */ +enum { + SUNRISE_ERROR_FATAL, + SUNRISE_ERROR_I2C, + SUNRISE_ERROR_ALGORITHM, + SUNRISE_ERROR_CALIBRATION, + SUNRISE_ERROR_SELF_DIAGNOSTIC, + SUNRISE_ERROR_OUT_OF_RANGE, + SUNRISE_ERROR_MEMORY, + SUNRISE_ERROR_NO_MEASUREMENT, + SUNRISE_ERROR_LOW_VOLTAGE, + SUNRISE_ERROR_MEASUREMENT_TIMEOUT, +}; + +static const char * const sunrise_error_statuses[] = { + [SUNRISE_ERROR_FATAL] = "error_fatal", + [SUNRISE_ERROR_I2C] = "error_i2c", + [SUNRISE_ERROR_ALGORITHM] = "error_algorithm", + [SUNRISE_ERROR_CALIBRATION] = "error_calibration", + [SUNRISE_ERROR_SELF_DIAGNOSTIC] = "error_self_diagnostic", + [SUNRISE_ERROR_OUT_OF_RANGE] = "error_out_of_range", + [SUNRISE_ERROR_MEMORY] = "error_memory", + [SUNRISE_ERROR_NO_MEASUREMENT] = "error_no_measurement", + [SUNRISE_ERROR_LOW_VOLTAGE] = "error_low_voltage", + [SUNRISE_ERROR_MEASUREMENT_TIMEOUT] = "error_measurement_timeout", +}; + +static const struct iio_enum sunrise_error_statuses_enum = { + .items = sunrise_error_statuses, + .num_items = ARRAY_SIZE(sunrise_error_statuses), +}; + +static ssize_t sunrise_error_status_read(struct iio_dev *iiodev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct sunrise_dev *sunrise = iio_priv(iiodev); + unsigned long errors; + ssize_t len = 0; + u16 value; + int ret; + u8 i; + + mutex_lock(&sunrise->lock); + ret = sunrise_read_word(sunrise, SUNRISE_ERROR_STATUS_REG, &value); + if (ret) { + mutex_unlock(&sunrise->lock); + return ret; + } + + errors = value; + for_each_set_bit(i, &errors, ARRAY_SIZE(sunrise_error_statuses)) + len += sysfs_emit_at(buf, len, "%s ", sunrise_error_statuses[i]); + + if (len) + buf[len - 1] = '\n'; + + mutex_unlock(&sunrise->lock); + + return len; +} + +static const struct iio_chan_spec_ext_info sunrise_concentration_ext_info[] = { + /* Calibration triggers. */ + { + .name = "calibration_factory", + .write = sunrise_cal_factory_write, + .shared = IIO_SEPARATE, + }, + { + .name = "calibration_background", + .write = sunrise_cal_background_write, + .shared = IIO_SEPARATE, + }, + + /* Error statuses. */ + { + .name = "error_status", + .read = sunrise_error_status_read, + .shared = IIO_SHARED_BY_ALL, + }, + { + .name = "error_status_available", + .shared = IIO_SHARED_BY_ALL, + .read = iio_enum_available_read, + .private = (uintptr_t)&sunrise_error_statuses_enum, + }, + {} +}; + +static const struct iio_chan_spec sunrise_channels[] = { + { + .type = IIO_CONCENTRATION, + .modified = 1, + .channel2 = IIO_MOD_CO2, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .ext_info = sunrise_concentration_ext_info, + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static int sunrise_read_raw(struct iio_dev *iio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct sunrise_dev *sunrise = iio_priv(iio_dev); + u16 value; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_CONCENTRATION: + mutex_lock(&sunrise->lock); + ret = sunrise_read_word(sunrise, SUNRISE_CO2_FILTERED_COMP_REG, + &value); + *val = value; + mutex_unlock(&sunrise->lock); + + if (ret) + return ret; + + return IIO_VAL_INT; + + case IIO_TEMP: + mutex_lock(&sunrise->lock); + ret = sunrise_read_word(sunrise, SUNRISE_CHIP_TEMPERATURE_REG, + &value); + *val = value; + mutex_unlock(&sunrise->lock); + + if (ret) + return ret; + + return IIO_VAL_INT; + + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_CONCENTRATION: + /* + * 1 / 10^4 to comply with IIO scale for CO2 + * (percentage). The chip CO2 reading range is [400 - + * 5000] ppm which corresponds to [0,004 - 0,5] %. + */ + *val = 1; + *val2 = 10000; + return IIO_VAL_FRACTIONAL; + + case IIO_TEMP: + /* x10 to comply with IIO scale (millidegrees celsius). */ + *val = 10; + return IIO_VAL_INT; + + default: + return -EINVAL; + } + + default: + return -EINVAL; + } +} + +static const struct iio_info sunrise_info = { + .read_raw = sunrise_read_raw, +}; + +static const struct regmap_bus sunrise_regmap_bus = { + .read = sunrise_regmap_read, + .write = sunrise_regmap_write, +}; + +static const struct regmap_config sunrise_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int sunrise_probe(struct i2c_client *client) +{ + struct sunrise_dev *sunrise; + struct iio_dev *iio_dev; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA)) { + dev_err(&client->dev, + "Adapter does not support required functionalities\n"); + return -EOPNOTSUPP; + } + + iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*sunrise)); + if (!iio_dev) + return -ENOMEM; + + sunrise = iio_priv(iio_dev); + sunrise->client = client; + mutex_init(&sunrise->lock); + + i2c_set_clientdata(client, sunrise); + + sunrise->regmap = devm_regmap_init(&client->dev, &sunrise_regmap_bus, + client, &sunrise_regmap_config); + if (IS_ERR(sunrise->regmap)) { + dev_err(&client->dev, "Failed to initialize regmap\n"); + return PTR_ERR(sunrise->regmap); + } + + /* + * The chip nacks the wake up message. If the adapter does not support + * protocol mangling do not set the I2C_M_IGNORE_NAK flag at the expense + * of possible cruft in the logs. + */ + if (i2c_check_functionality(client->adapter, I2C_FUNC_PROTOCOL_MANGLING)) + sunrise->ignore_nak = true; + + iio_dev->info = &sunrise_info; + iio_dev->name = DRIVER_NAME; + iio_dev->channels = sunrise_channels; + iio_dev->num_channels = ARRAY_SIZE(sunrise_channels); + iio_dev->modes = INDIO_DIRECT_MODE; + + return devm_iio_device_register(&client->dev, iio_dev); +} + +static const struct of_device_id sunrise_of_match[] = { + { .compatible = "senseair,sunrise-006-0-0007" }, + {} +}; +MODULE_DEVICE_TABLE(of, sunrise_of_match); + +static struct i2c_driver sunrise_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = sunrise_of_match, + }, + .probe_new = sunrise_probe, +}; +module_i2c_driver(sunrise_driver); + +MODULE_AUTHOR("Jacopo Mondi <jacopo@jmondi.org>"); +MODULE_DESCRIPTION("Senseair Sunrise 006-0-0007 CO2 sensor IIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index a4ec11a3b68a..1151434038d4 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -241,8 +241,9 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, fifo_attrs = NULL; ret = iio_triggered_buffer_setup_ext(indio_dev, - &iio_pollfunc_store_time, - NULL, NULL, fifo_attrs); + &iio_pollfunc_store_time, NULL, + IIO_BUFFER_DIRECTION_IN, + NULL, fifo_attrs); if (ret) { dev_err(&indio_dev->dev, "Triggered Buffer Setup Failed\n"); return ret; diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 0bbb090b108c..1de395bda03e 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -215,17 +215,23 @@ int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable) } EXPORT_SYMBOL(st_sensors_set_axis_enable); +static void st_reg_disable(void *reg) +{ + regulator_disable(reg); +} + int st_sensors_power_enable(struct iio_dev *indio_dev) { struct st_sensor_data *pdata = iio_priv(indio_dev); + struct device *parent = indio_dev->dev.parent; int err; /* Regulators not mandatory, but if requested we should enable them. */ - pdata->vdd = devm_regulator_get(indio_dev->dev.parent, "vdd"); - if (IS_ERR(pdata->vdd)) { - dev_err(&indio_dev->dev, "unable to get Vdd supply\n"); - return PTR_ERR(pdata->vdd); - } + pdata->vdd = devm_regulator_get(parent, "vdd"); + if (IS_ERR(pdata->vdd)) + return dev_err_probe(&indio_dev->dev, PTR_ERR(pdata->vdd), + "unable to get Vdd supply\n"); + err = regulator_enable(pdata->vdd); if (err != 0) { dev_warn(&indio_dev->dev, @@ -233,36 +239,26 @@ int st_sensors_power_enable(struct iio_dev *indio_dev) return err; } - pdata->vdd_io = devm_regulator_get(indio_dev->dev.parent, "vddio"); - if (IS_ERR(pdata->vdd_io)) { - dev_err(&indio_dev->dev, "unable to get Vdd_IO supply\n"); - err = PTR_ERR(pdata->vdd_io); - goto st_sensors_disable_vdd; - } + err = devm_add_action_or_reset(parent, st_reg_disable, pdata->vdd); + if (err) + return err; + + pdata->vdd_io = devm_regulator_get(parent, "vddio"); + if (IS_ERR(pdata->vdd_io)) + return dev_err_probe(&indio_dev->dev, PTR_ERR(pdata->vdd_io), + "unable to get Vdd_IO supply\n"); + err = regulator_enable(pdata->vdd_io); if (err != 0) { dev_warn(&indio_dev->dev, "Failed to enable specified Vdd_IO supply\n"); - goto st_sensors_disable_vdd; + return err; } - return 0; - -st_sensors_disable_vdd: - regulator_disable(pdata->vdd); - return err; + return devm_add_action_or_reset(parent, st_reg_disable, pdata->vdd_io); } EXPORT_SYMBOL(st_sensors_power_enable); -void st_sensors_power_disable(struct iio_dev *indio_dev) -{ - struct st_sensor_data *pdata = iio_priv(indio_dev); - - regulator_disable(pdata->vdd); - regulator_disable(pdata->vdd_io); -} -EXPORT_SYMBOL(st_sensors_power_disable); - static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev, struct st_sensors_platform_data *pdata) { diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c index b3ff88700866..18bd3c3d99bc 100644 --- a/drivers/iio/common/st_sensors/st_sensors_i2c.c +++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c @@ -57,7 +57,6 @@ int st_sensors_i2c_configure(struct iio_dev *indio_dev, indio_dev->name = client->name; - sdata->dev = &client->dev; sdata->irq = client->irq; return 0; diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c index 0d1d66c77cd8..7c60050e90dc 100644 --- a/drivers/iio/common/st_sensors/st_sensors_spi.c +++ b/drivers/iio/common/st_sensors/st_sensors_spi.c @@ -109,7 +109,6 @@ int st_sensors_spi_configure(struct iio_dev *indio_dev, indio_dev->name = spi->modalias; - sdata->dev = &spi->dev; sdata->irq = spi->irq; return 0; diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 64e0a748a855..392d74449886 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -42,7 +42,8 @@ static bool st_sensors_new_samples_available(struct iio_dev *indio_dev, sdata->sensor_settings->drdy_irq.stat_drdy.addr, &status); if (ret < 0) { - dev_err(sdata->dev, "error checking samples available\n"); + dev_err(indio_dev->dev.parent, + "error checking samples available\n"); return false; } @@ -87,7 +88,7 @@ static irqreturn_t st_sensors_irq_thread(int irq, void *p) st_sensors_new_samples_available(indio_dev, sdata)) { iio_trigger_poll_chained(p); } else { - dev_dbg(sdata->dev, "spurious IRQ\n"); + dev_dbg(indio_dev->dev.parent, "spurious IRQ\n"); return IRQ_NONE; } @@ -107,7 +108,8 @@ static irqreturn_t st_sensors_irq_thread(int irq, void *p) */ while (sdata->hw_irq_trigger && st_sensors_new_samples_available(indio_dev, sdata)) { - dev_dbg(sdata->dev, "more samples came in during polling\n"); + dev_dbg(indio_dev->dev.parent, + "more samples came in during polling\n"); sdata->hw_timestamp = iio_get_time_ns(indio_dev); iio_trigger_poll_chained(p); } @@ -119,11 +121,12 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, const struct iio_trigger_ops *trigger_ops) { struct st_sensor_data *sdata = iio_priv(indio_dev); + struct device *parent = indio_dev->dev.parent; unsigned long irq_trig; int err; - sdata->trig = iio_trigger_alloc(sdata->dev, "%s-trigger", - indio_dev->name); + sdata->trig = devm_iio_trigger_alloc(parent, "%s-trigger", + indio_dev->name); if (sdata->trig == NULL) { dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n"); return -ENOMEM; @@ -153,7 +156,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, sdata->sensor_settings->drdy_irq.addr_ihl, sdata->sensor_settings->drdy_irq.mask_ihl, 1); if (err < 0) - goto iio_trigger_free; + return err; dev_info(&indio_dev->dev, "interrupts on the falling edge or active low level\n"); } @@ -179,8 +182,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, if (!sdata->sensor_settings->drdy_irq.stat_drdy.addr) { dev_err(&indio_dev->dev, "edge IRQ not supported w/o stat register.\n"); - err = -EOPNOTSUPP; - goto iio_trigger_free; + return -EOPNOTSUPP; } sdata->edge_irq = true; } else { @@ -205,44 +207,29 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, sdata->sensor_settings->drdy_irq.stat_drdy.addr) irq_trig |= IRQF_SHARED; - err = request_threaded_irq(sdata->irq, - st_sensors_irq_handler, - st_sensors_irq_thread, - irq_trig, - sdata->trig->name, - sdata->trig); + err = devm_request_threaded_irq(parent, + sdata->irq, + st_sensors_irq_handler, + st_sensors_irq_thread, + irq_trig, + sdata->trig->name, + sdata->trig); if (err) { dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n"); - goto iio_trigger_free; + return err; } - err = iio_trigger_register(sdata->trig); + err = devm_iio_trigger_register(parent, sdata->trig); if (err < 0) { dev_err(&indio_dev->dev, "failed to register iio trigger.\n"); - goto iio_trigger_register_error; + return err; } indio_dev->trig = iio_trigger_get(sdata->trig); return 0; - -iio_trigger_register_error: - free_irq(sdata->irq, sdata->trig); -iio_trigger_free: - iio_trigger_free(sdata->trig); - return err; } EXPORT_SYMBOL(st_sensors_allocate_trigger); -void st_sensors_deallocate_trigger(struct iio_dev *indio_dev) -{ - struct st_sensor_data *sdata = iio_priv(indio_dev); - - iio_trigger_unregister(sdata->trig); - free_irq(sdata->irq, sdata->trig); - iio_trigger_free(sdata->trig); -} -EXPORT_SYMBOL(st_sensors_deallocate_trigger); - int st_sensors_validate_device(struct iio_trigger *trig, struct iio_dev *indio_dev) { diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c index dff623b65e4f..fd9cac4f6321 100644 --- a/drivers/iio/dac/ad5064.c +++ b/drivers/iio/dac/ad5064.c @@ -843,6 +843,13 @@ static int ad5064_request_vref(struct ad5064_state *st, struct device *dev) return ret; } +static void ad5064_bulk_reg_disable(void *data) +{ + struct ad5064_state *st = data; + + regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg); +} + static int ad5064_probe(struct device *dev, enum ad5064_type type, const char *name, ad5064_write_func write) { @@ -858,7 +865,6 @@ static int ad5064_probe(struct device *dev, enum ad5064_type type, st = iio_priv(indio_dev); mutex_init(&st->lock); - dev_set_drvdata(dev, indio_dev); st->chip_info = &ad5064_chip_info_tbl[type]; st->dev = dev; @@ -872,6 +878,10 @@ static int ad5064_probe(struct device *dev, enum ad5064_type type, ret = regulator_bulk_enable(ad5064_num_vref(st), st->vref_reg); if (ret) return ret; + + ret = devm_add_action_or_reset(dev, ad5064_bulk_reg_disable, st); + if (ret) + return ret; } indio_dev->name = name; @@ -887,30 +897,7 @@ static int ad5064_probe(struct device *dev, enum ad5064_type type, st->dac_cache[i] = midscale; } - ret = iio_device_register(indio_dev); - if (ret) - goto error_disable_reg; - - return 0; - -error_disable_reg: - if (!st->use_internal_vref) - regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg); - - return ret; -} - -static int ad5064_remove(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - struct ad5064_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - if (!st->use_internal_vref) - regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg); - - return 0; + return devm_iio_device_register(dev, indio_dev); } #if IS_ENABLED(CONFIG_SPI_MASTER) @@ -932,11 +919,6 @@ static int ad5064_spi_probe(struct spi_device *spi) ad5064_spi_write); } -static int ad5064_spi_remove(struct spi_device *spi) -{ - return ad5064_remove(&spi->dev); -} - static const struct spi_device_id ad5064_spi_ids[] = { {"ad5024", ID_AD5024}, {"ad5025", ID_AD5025}, @@ -963,7 +945,6 @@ static struct spi_driver ad5064_spi_driver = { .name = "ad5064", }, .probe = ad5064_spi_probe, - .remove = ad5064_spi_remove, .id_table = ad5064_spi_ids, }; @@ -1019,11 +1000,6 @@ static int ad5064_i2c_probe(struct i2c_client *i2c, ad5064_i2c_write); } -static int ad5064_i2c_remove(struct i2c_client *i2c) -{ - return ad5064_remove(&i2c->dev); -} - static const struct i2c_device_id ad5064_i2c_ids[] = { {"ad5625", ID_AD5625 }, {"ad5625r-1v25", ID_AD5625R_1V25 }, @@ -1081,7 +1057,6 @@ static struct i2c_driver ad5064_i2c_driver = { .name = "ad5064", }, .probe = ad5064_i2c_probe, - .remove = ad5064_i2c_remove, .id_table = ad5064_i2c_ids, }; diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 53db5b4e4c53..8ca26bb4b62f 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -444,7 +444,7 @@ error_free_reg: return ret; } -static int ad5380_remove(struct device *dev) +static void ad5380_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ad5380_state *st = iio_priv(indio_dev); @@ -453,11 +453,8 @@ static int ad5380_remove(struct device *dev) kfree(indio_dev->channels); - if (!IS_ERR(st->vref_reg)) { + if (!IS_ERR(st->vref_reg)) regulator_disable(st->vref_reg); - } - - return 0; } static bool ad5380_reg_false(struct device *dev, unsigned int reg) @@ -493,7 +490,9 @@ static int ad5380_spi_probe(struct spi_device *spi) static int ad5380_spi_remove(struct spi_device *spi) { - return ad5380_remove(&spi->dev); + ad5380_remove(&spi->dev); + + return 0; } static const struct spi_device_id ad5380_spi_ids[] = { @@ -566,7 +565,9 @@ static int ad5380_i2c_probe(struct i2c_client *i2c, static int ad5380_i2c_remove(struct i2c_client *i2c) { - return ad5380_remove(&i2c->dev); + ad5380_remove(&i2c->dev); + + return 0; } static const struct i2c_device_id ad5380_i2c_ids[] = { diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index 488ec69967d6..0ef761521e87 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -283,7 +283,7 @@ error_disable_reg: return ret; } -static int ad5446_remove(struct device *dev) +static void ad5446_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ad5446_state *st = iio_priv(indio_dev); @@ -291,8 +291,6 @@ static int ad5446_remove(struct device *dev) iio_device_unregister(indio_dev); if (!IS_ERR(st->reg)) regulator_disable(st->reg); - - return 0; } #if IS_ENABLED(CONFIG_SPI_MASTER) @@ -495,7 +493,9 @@ static int ad5446_spi_probe(struct spi_device *spi) static int ad5446_spi_remove(struct spi_device *spi) { - return ad5446_remove(&spi->dev); + ad5446_remove(&spi->dev); + + return 0; } static struct spi_driver ad5446_spi_driver = { @@ -572,7 +572,9 @@ static int ad5446_i2c_probe(struct i2c_client *i2c, static int ad5446_i2c_remove(struct i2c_client *i2c) { - return ad5446_remove(&i2c->dev); + ad5446_remove(&i2c->dev); + + return 0; } static const struct i2c_device_id ad5446_i2c_ids[] = { diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c index 0405e92b9e8c..2fcc59728fd6 100644 --- a/drivers/iio/dac/ad5592r-base.c +++ b/drivers/iio/dac/ad5592r-base.c @@ -663,7 +663,7 @@ error_disable_reg: } EXPORT_SYMBOL_GPL(ad5592r_probe); -int ad5592r_remove(struct device *dev) +void ad5592r_remove(struct device *dev) { struct iio_dev *iio_dev = dev_get_drvdata(dev); struct ad5592r_state *st = iio_priv(iio_dev); @@ -674,8 +674,6 @@ int ad5592r_remove(struct device *dev) if (st->reg) regulator_disable(st->reg); - - return 0; } EXPORT_SYMBOL_GPL(ad5592r_remove); diff --git a/drivers/iio/dac/ad5592r-base.h b/drivers/iio/dac/ad5592r-base.h index 23dac2f1ff8a..2a22ef691996 100644 --- a/drivers/iio/dac/ad5592r-base.h +++ b/drivers/iio/dac/ad5592r-base.h @@ -71,6 +71,6 @@ struct ad5592r_state { int ad5592r_probe(struct device *dev, const char *name, const struct ad5592r_rw_ops *ops); -int ad5592r_remove(struct device *dev); +void ad5592r_remove(struct device *dev); #endif /* __DRIVERS_IIO_DAC_AD5592R_BASE_H__ */ diff --git a/drivers/iio/dac/ad5592r.c b/drivers/iio/dac/ad5592r.c index 41f651500668..6bfd7951e18c 100644 --- a/drivers/iio/dac/ad5592r.c +++ b/drivers/iio/dac/ad5592r.c @@ -132,7 +132,9 @@ static int ad5592r_spi_probe(struct spi_device *spi) static int ad5592r_spi_remove(struct spi_device *spi) { - return ad5592r_remove(&spi->dev); + ad5592r_remove(&spi->dev); + + return 0; } static const struct spi_device_id ad5592r_spi_ids[] = { diff --git a/drivers/iio/dac/ad5593r.c b/drivers/iio/dac/ad5593r.c index 5b4df36fdc2a..64dd7a0bddf7 100644 --- a/drivers/iio/dac/ad5593r.c +++ b/drivers/iio/dac/ad5593r.c @@ -99,7 +99,9 @@ static int ad5593r_i2c_probe(struct i2c_client *i2c, static int ad5593r_i2c_remove(struct i2c_client *i2c) { - return ad5592r_remove(&i2c->dev); + ad5592r_remove(&i2c->dev); + + return 0; } static const struct i2c_device_id ad5593r_i2c_ids[] = { diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c index 0188ded5137c..2628810fdbb1 100644 --- a/drivers/iio/dac/ad5686-spi.c +++ b/drivers/iio/dac/ad5686-spi.c @@ -97,7 +97,9 @@ static int ad5686_spi_probe(struct spi_device *spi) static int ad5686_spi_remove(struct spi_device *spi) { - return ad5686_remove(&spi->dev); + ad5686_remove(&spi->dev); + + return 0; } static const struct spi_device_id ad5686_spi_id[] = { diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index fcb64f20ff64..8f001db775f4 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -538,7 +538,7 @@ error_disable_reg: } EXPORT_SYMBOL_GPL(ad5686_probe); -int ad5686_remove(struct device *dev) +void ad5686_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct ad5686_state *st = iio_priv(indio_dev); @@ -546,8 +546,6 @@ int ad5686_remove(struct device *dev) iio_device_unregister(indio_dev); if (!IS_ERR(st->reg)) regulator_disable(st->reg); - - return 0; } EXPORT_SYMBOL_GPL(ad5686_remove); diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index f89a6f92b427..cd5fff9e9d53 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -154,7 +154,7 @@ int ad5686_probe(struct device *dev, const char *name, ad5686_write_func write, ad5686_read_func read); -int ad5686_remove(struct device *dev); +void ad5686_remove(struct device *dev); #endif /* __DRIVERS_IIO_DAC_AD5686_H__ */ diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index 24a6a4a5a2e0..93f0e0e66c22 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -67,7 +67,9 @@ static int ad5686_i2c_probe(struct i2c_client *i2c, static int ad5686_i2c_remove(struct i2c_client *i2c) { - return ad5686_remove(&i2c->dev); + ad5686_remove(&i2c->dev); + + return 0; } static const struct i2c_device_id ad5686_i2c_id[] = { diff --git a/drivers/iio/dac/ad5766.c b/drivers/iio/dac/ad5766.c index 3104ec32dfac..2b1edcb25444 100644 --- a/drivers/iio/dac/ad5766.c +++ b/drivers/iio/dac/ad5766.c @@ -5,10 +5,13 @@ * Copyright 2019-2020 Analog Devices Inc. */ #include <linux/bitfield.h> +#include <linux/bitops.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/gpio/consumer.h> #include <linux/iio/iio.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> #include <linux/module.h> #include <linux/spi/spi.h> #include <asm/unaligned.h> @@ -455,6 +458,7 @@ static const struct iio_chan_spec_ext_info ad5766_ext_info[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | \ BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = (_chan), \ .scan_type = { \ .sign = 'u', \ .realbits = (_bits), \ @@ -576,6 +580,35 @@ static int ad5766_default_setup(struct ad5766_state *st) return __ad5766_spi_write(st, AD5766_CMD_SPAN_REG, st->crt_range); } +static irqreturn_t ad5766_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct iio_buffer *buffer = indio_dev->buffer; + struct ad5766_state *st = iio_priv(indio_dev); + int ret, ch, i; + u16 data[ARRAY_SIZE(ad5766_channels)]; + + ret = iio_pop_from_buffer(buffer, data); + if (ret) + goto done; + + i = 0; + mutex_lock(&st->lock); + for_each_set_bit(ch, indio_dev->active_scan_mask, + st->chip_info->num_channels - 1) + __ad5766_spi_write(st, AD5766_CMD_WR_IN_REG(ch), data[i++]); + + __ad5766_spi_write(st, AD5766_CMD_SW_LDAC, + *indio_dev->active_scan_mask); + mutex_unlock(&st->lock); + +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static int ad5766_probe(struct spi_device *spi) { enum ad5766_type type; @@ -609,6 +642,15 @@ static int ad5766_probe(struct spi_device *spi) if (ret) return ret; + /* Configure trigger buffer */ + ret = devm_iio_triggered_buffer_setup_ext(&spi->dev, indio_dev, NULL, + ad5766_trigger_handler, + IIO_BUFFER_DIRECTION_OUT, + NULL, + NULL); + if (ret) + return ret; + return devm_iio_device_register(&spi->dev, indio_dev); } diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c index 8107f7bbbe3c..7e2fd32e993a 100644 --- a/drivers/iio/dac/ad5770r.c +++ b/drivers/iio/dac/ad5770r.c @@ -522,7 +522,7 @@ static int ad5770r_channel_config(struct ad5770r_state *st) return -EINVAL; device_for_each_child_node(&st->spi->dev, child) { - ret = fwnode_property_read_u32(child, "num", &num); + ret = fwnode_property_read_u32(child, "reg", &num); if (ret) goto err_child_out; if (num >= AD5770R_MAX_CHANNELS) { diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index e1b6a92df12f..91eaaf793b3e 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -198,6 +198,11 @@ static const struct iio_chan_spec ad7303_channels[] = { AD7303_CHANNEL(1), }; +static void ad7303_reg_disable(void *reg) +{ + regulator_disable(reg); +} + static int ad7303_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); @@ -210,7 +215,6 @@ static int ad7303_probe(struct spi_device *spi) return -ENOMEM; st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); st->spi = spi; @@ -224,18 +228,27 @@ static int ad7303_probe(struct spi_device *spi) if (ret) return ret; + ret = devm_add_action_or_reset(&spi->dev, ad7303_reg_disable, st->vdd_reg); + if (ret) + return ret; + st->vref_reg = devm_regulator_get_optional(&spi->dev, "REF"); if (IS_ERR(st->vref_reg)) { ret = PTR_ERR(st->vref_reg); if (ret != -ENODEV) - goto err_disable_vdd_reg; + return ret; st->vref_reg = NULL; } if (st->vref_reg) { ret = regulator_enable(st->vref_reg); if (ret) - goto err_disable_vdd_reg; + return ret; + + ret = devm_add_action_or_reset(&spi->dev, ad7303_reg_disable, + st->vref_reg); + if (ret) + return ret; st->config |= AD7303_CFG_EXTERNAL_VREF; } @@ -246,32 +259,7 @@ static int ad7303_probe(struct spi_device *spi) indio_dev->channels = ad7303_channels; indio_dev->num_channels = ARRAY_SIZE(ad7303_channels); - ret = iio_device_register(indio_dev); - if (ret) - goto err_disable_vref_reg; - - return 0; - -err_disable_vref_reg: - if (st->vref_reg) - regulator_disable(st->vref_reg); -err_disable_vdd_reg: - regulator_disable(st->vdd_reg); - return ret; -} - -static int ad7303_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad7303_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - - if (st->vref_reg) - regulator_disable(st->vref_reg); - regulator_disable(st->vdd_reg); - - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct of_device_id ad7303_spi_of_match[] = { @@ -292,7 +280,6 @@ static struct spi_driver ad7303_driver = { .of_match_table = ad7303_spi_of_match, }, .probe = ad7303_probe, - .remove = ad7303_remove, .id_table = ad7303_spi_ids, }; module_spi_driver(ad7303_driver); diff --git a/drivers/iio/dac/ad8801.c b/drivers/iio/dac/ad8801.c index 6354b7c8f052..5ecfdad54dec 100644 --- a/drivers/iio/dac/ad8801.c +++ b/drivers/iio/dac/ad8801.c @@ -123,10 +123,9 @@ static int ad8801_probe(struct spi_device *spi) id = spi_get_device_id(spi); state->vrefh_reg = devm_regulator_get(&spi->dev, "vrefh"); - if (IS_ERR(state->vrefh_reg)) { - dev_err(&spi->dev, "Vrefh regulator not specified\n"); - return PTR_ERR(state->vrefh_reg); - } + if (IS_ERR(state->vrefh_reg)) + return dev_err_probe(&spi->dev, PTR_ERR(state->vrefh_reg), + "Vrefh regulator not specified\n"); ret = regulator_enable(state->vrefh_reg); if (ret) { @@ -146,8 +145,8 @@ static int ad8801_probe(struct spi_device *spi) if (id->driver_data == ID_AD8803) { state->vrefl_reg = devm_regulator_get(&spi->dev, "vrefl"); if (IS_ERR(state->vrefl_reg)) { - dev_err(&spi->dev, "Vrefl regulator not specified\n"); - ret = PTR_ERR(state->vrefl_reg); + ret = dev_err_probe(&spi->dev, PTR_ERR(state->vrefl_reg), + "Vrefl regulator not specified\n"); goto error_disable_vrefh_reg; } diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c index 79527fbc250a..5a5e967b0be4 100644 --- a/drivers/iio/dac/ds4424.c +++ b/drivers/iio/dac/ds4424.c @@ -232,12 +232,9 @@ static int ds4424_probe(struct i2c_client *client, indio_dev->name = id->name; data->vcc_reg = devm_regulator_get(&client->dev, "vcc"); - if (IS_ERR(data->vcc_reg)) { - dev_err(&client->dev, - "Failed to get vcc-supply regulator. err: %ld\n", - PTR_ERR(data->vcc_reg)); - return PTR_ERR(data->vcc_reg); - } + if (IS_ERR(data->vcc_reg)) + return dev_err_probe(&client->dev, PTR_ERR(data->vcc_reg), + "Failed to get vcc-supply regulator.\n"); mutex_init(&data->lock); ret = regulator_enable(data->vcc_reg); diff --git a/drivers/iio/dac/lpc18xx_dac.c b/drivers/iio/dac/lpc18xx_dac.c index 9e38607a189e..5502e4f62f0d 100644 --- a/drivers/iio/dac/lpc18xx_dac.c +++ b/drivers/iio/dac/lpc18xx_dac.c @@ -121,16 +121,14 @@ static int lpc18xx_dac_probe(struct platform_device *pdev) return PTR_ERR(dac->base); dac->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(dac->clk)) { - dev_err(&pdev->dev, "error getting clock\n"); - return PTR_ERR(dac->clk); - } + if (IS_ERR(dac->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(dac->clk), + "error getting clock\n"); dac->vref = devm_regulator_get(&pdev->dev, "vref"); - if (IS_ERR(dac->vref)) { - dev_err(&pdev->dev, "error getting regulator\n"); - return PTR_ERR(dac->vref); - } + if (IS_ERR(dac->vref)) + return dev_err_probe(&pdev->dev, PTR_ERR(dac->vref), + "error getting regulator\n"); indio_dev->name = dev_name(&pdev->dev); indio_dev->info = &lpc18xx_dac_info; diff --git a/drivers/iio/dac/ltc1660.c b/drivers/iio/dac/ltc1660.c index dc10188540ca..f6ec9bf5815e 100644 --- a/drivers/iio/dac/ltc1660.c +++ b/drivers/iio/dac/ltc1660.c @@ -172,10 +172,9 @@ static int ltc1660_probe(struct spi_device *spi) } priv->vref_reg = devm_regulator_get(&spi->dev, "vref"); - if (IS_ERR(priv->vref_reg)) { - dev_err(&spi->dev, "vref regulator not specified\n"); - return PTR_ERR(priv->vref_reg); - } + if (IS_ERR(priv->vref_reg)) + return dev_err_probe(&spi->dev, PTR_ERR(priv->vref_reg), + "vref regulator not specified\n"); ret = regulator_enable(priv->vref_reg); if (ret) { diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c index bd0b7f361154..7da4710a6408 100644 --- a/drivers/iio/dac/max5821.c +++ b/drivers/iio/dac/max5821.c @@ -321,12 +321,9 @@ static int max5821_probe(struct i2c_client *client, } data->vref_reg = devm_regulator_get(&client->dev, "vref"); - if (IS_ERR(data->vref_reg)) { - ret = PTR_ERR(data->vref_reg); - dev_err(&client->dev, - "Failed to get vref regulator: %d\n", ret); - return ret; - } + if (IS_ERR(data->vref_reg)) + return dev_err_probe(&client->dev, PTR_ERR(data->vref_reg), + "Failed to get vref regulator\n"); ret = regulator_enable(data->vref_reg); if (ret) { diff --git a/drivers/iio/dac/mcp4922.c b/drivers/iio/dac/mcp4922.c index c4e430b4050e..0ae414ee1716 100644 --- a/drivers/iio/dac/mcp4922.c +++ b/drivers/iio/dac/mcp4922.c @@ -130,10 +130,9 @@ static int mcp4922_probe(struct spi_device *spi) state = iio_priv(indio_dev); state->spi = spi; state->vref_reg = devm_regulator_get(&spi->dev, "vref"); - if (IS_ERR(state->vref_reg)) { - dev_err(&spi->dev, "Vref regulator not specified\n"); - return PTR_ERR(state->vref_reg); - } + if (IS_ERR(state->vref_reg)) + return dev_err_probe(&spi->dev, PTR_ERR(state->vref_reg), + "Vref regulator not specified\n"); ret = regulator_enable(state->vref_reg); if (ret) { diff --git a/drivers/iio/dac/stm32-dac-core.c b/drivers/iio/dac/stm32-dac-core.c index 906436780347..bd7a3b20e645 100644 --- a/drivers/iio/dac/stm32-dac-core.c +++ b/drivers/iio/dac/stm32-dac-core.c @@ -90,7 +90,6 @@ static int stm32_dac_probe(struct platform_device *pdev) const struct stm32_dac_cfg *cfg; struct stm32_dac_priv *priv; struct regmap *regmap; - struct resource *res; void __iomem *mmio; struct reset_control *rst; int ret; @@ -106,8 +105,7 @@ static int stm32_dac_probe(struct platform_device *pdev) cfg = (const struct stm32_dac_cfg *) of_match_device(dev->driver->of_match_table, dev)->data; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - mmio = devm_ioremap_resource(dev, res); + mmio = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mmio)) return PTR_ERR(mmio); @@ -118,18 +116,12 @@ static int stm32_dac_probe(struct platform_device *pdev) priv->common.regmap = regmap; priv->pclk = devm_clk_get(dev, "pclk"); - if (IS_ERR(priv->pclk)) { - ret = PTR_ERR(priv->pclk); - dev_err(dev, "pclk get failed\n"); - return ret; - } + if (IS_ERR(priv->pclk)) + return dev_err_probe(dev, PTR_ERR(priv->pclk), "pclk get failed\n"); priv->vref = devm_regulator_get(dev, "vref"); - if (IS_ERR(priv->vref)) { - ret = PTR_ERR(priv->vref); - dev_err(dev, "vref get failed, %d\n", ret); - return ret; - } + if (IS_ERR(priv->vref)) + return dev_err_probe(dev, PTR_ERR(priv->vref), "vref get failed\n"); pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); diff --git a/drivers/iio/dac/ti-dac7311.c b/drivers/iio/dac/ti-dac7311.c index 9d0b253be841..09218c3029f0 100644 --- a/drivers/iio/dac/ti-dac7311.c +++ b/drivers/iio/dac/ti-dac7311.c @@ -266,10 +266,9 @@ static int ti_dac_probe(struct spi_device *spi) ti_dac->resolution = spec->resolution; ti_dac->vref = devm_regulator_get(dev, "vref"); - if (IS_ERR(ti_dac->vref)) { - dev_err(dev, "error to get regulator\n"); - return PTR_ERR(ti_dac->vref); - } + if (IS_ERR(ti_dac->vref)) + return dev_err_probe(dev, PTR_ERR(ti_dac->vref), + "error to get regulator\n"); ret = regulator_enable(ti_dac->vref); if (ret < 0) { diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index 20b5ac7ab66a..a672f7d12bbb 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -126,7 +126,6 @@ config MPU3050 config MPU3050_I2C tristate "Invensense MPU3050 devices on I2C" - depends on !(INPUT_MPU3050=y || INPUT_MPU3050=m) depends on I2C select MPU3050 select REGMAP_I2C diff --git a/drivers/iio/gyro/adis16080.c b/drivers/iio/gyro/adis16080.c index e2f4d943e220..acef59d822b1 100644 --- a/drivers/iio/gyro/adis16080.c +++ b/drivers/iio/gyro/adis16080.c @@ -195,8 +195,6 @@ static int adis16080_probe(struct spi_device *spi) if (!indio_dev) return -ENOMEM; st = iio_priv(indio_dev); - /* this is only used for removal purposes */ - spi_set_drvdata(spi, indio_dev); mutex_init(&st->lock); @@ -210,13 +208,7 @@ static int adis16080_probe(struct spi_device *spi) indio_dev->info = &adis16080_info; indio_dev->modes = INDIO_DIRECT_MODE; - return iio_device_register(indio_dev); -} - -static int adis16080_remove(struct spi_device *spi) -{ - iio_device_unregister(spi_get_drvdata(spi)); - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id adis16080_ids[] = { @@ -231,7 +223,6 @@ static struct spi_driver adis16080_driver = { .name = "adis16080", }, .probe = adis16080_probe, - .remove = adis16080_remove, .id_table = adis16080_ids, }; module_spi_driver(adis16080_driver); diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index 3225de1f023b..ea387efab62d 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -471,13 +471,10 @@ static irqreturn_t mpu3050_trigger_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct mpu3050 *mpu3050 = iio_priv(indio_dev); int ret; - /* - * Temperature 1*16 bits - * Three axes 3*16 bits - * Timestamp 64 bits (4*16 bits) - * Sum total 8*16 bits - */ - __be16 hw_values[8]; + struct { + __be16 chans[4]; + s64 timestamp __aligned(8); + } scan; s64 timestamp; unsigned int datums_from_fifo = 0; @@ -572,9 +569,10 @@ static irqreturn_t mpu3050_trigger_handler(int irq, void *p) fifo_values[4]); /* Index past the footer (fifo_values[0]) and push */ - iio_push_to_buffers_with_timestamp(indio_dev, - &fifo_values[1], - timestamp); + iio_push_to_buffers_with_ts_unaligned(indio_dev, + &fifo_values[1], + sizeof(__be16) * 4, + timestamp); fifocnt -= toread; datums_from_fifo++; @@ -632,15 +630,15 @@ static irqreturn_t mpu3050_trigger_handler(int irq, void *p) goto out_trigger_unlock; } - ret = regmap_bulk_read(mpu3050->map, MPU3050_TEMP_H, &hw_values, - sizeof(hw_values)); + ret = regmap_bulk_read(mpu3050->map, MPU3050_TEMP_H, scan.chans, + sizeof(scan.chans)); if (ret) { dev_err(mpu3050->dev, "error reading axis data\n"); goto out_trigger_unlock; } - iio_push_to_buffers_with_timestamp(indio_dev, hw_values, timestamp); + iio_push_to_buffers_with_timestamp(indio_dev, &scan, timestamp); out_trigger_unlock: mutex_unlock(&mpu3050->lock); diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index e8fc8af65143..201050b76fe5 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -478,6 +478,7 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *gdata = iio_priv(indio_dev); struct st_sensors_platform_data *pdata; + struct device *parent = indio_dev->dev.parent; int err; indio_dev->modes = INDIO_DIRECT_MODE; @@ -491,7 +492,7 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) indio_dev->channels = gdata->sensor_settings->ch; indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; - err = iio_read_mount_matrix(gdata->dev, &gdata->mount_matrix); + err = iio_read_mount_matrix(parent, &gdata->mount_matrix); if (err) return err; @@ -515,32 +516,10 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) return err; } - err = iio_device_register(indio_dev); - if (err) - goto st_gyro_device_register_error; - - dev_info(&indio_dev->dev, "registered gyroscope %s\n", - indio_dev->name); - - return 0; - -st_gyro_device_register_error: - if (gdata->irq > 0) - st_sensors_deallocate_trigger(indio_dev); - return err; + return devm_iio_device_register(parent, indio_dev); } EXPORT_SYMBOL(st_gyro_common_probe); -void st_gyro_common_remove(struct iio_dev *indio_dev) -{ - struct st_sensor_data *gdata = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (gdata->irq > 0) - st_sensors_deallocate_trigger(indio_dev); -} -EXPORT_SYMBOL(st_gyro_common_remove); - MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics gyroscopes driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c index 3ef86e16ee65..163c7ba300c1 100644 --- a/drivers/iio/gyro/st_gyro_i2c.c +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -90,27 +90,7 @@ static int st_gyro_i2c_probe(struct i2c_client *client, if (err) return err; - err = st_gyro_common_probe(indio_dev); - if (err < 0) - goto st_gyro_power_off; - - return 0; - -st_gyro_power_off: - st_sensors_power_disable(indio_dev); - - return err; -} - -static int st_gyro_i2c_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - st_sensors_power_disable(indio_dev); - - st_gyro_common_remove(indio_dev); - - return 0; + return st_gyro_common_probe(indio_dev); } static const struct i2c_device_id st_gyro_id_table[] = { @@ -133,7 +113,6 @@ static struct i2c_driver st_gyro_driver = { .of_match_table = st_gyro_of_match, }, .probe = st_gyro_i2c_probe, - .remove = st_gyro_i2c_remove, .id_table = st_gyro_id_table, }; module_i2c_driver(st_gyro_driver); diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c index 41d835493347..b0023f9b9771 100644 --- a/drivers/iio/gyro/st_gyro_spi.c +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -94,27 +94,7 @@ static int st_gyro_spi_probe(struct spi_device *spi) if (err) return err; - err = st_gyro_common_probe(indio_dev); - if (err < 0) - goto st_gyro_power_off; - - return 0; - -st_gyro_power_off: - st_sensors_power_disable(indio_dev); - - return err; -} - -static int st_gyro_spi_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - - st_sensors_power_disable(indio_dev); - - st_gyro_common_remove(indio_dev); - - return 0; + return st_gyro_common_probe(indio_dev); } static const struct spi_device_id st_gyro_id_table[] = { @@ -137,7 +117,6 @@ static struct spi_driver st_gyro_driver = { .of_match_table = st_gyro_of_match, }, .probe = st_gyro_spi_probe, - .remove = st_gyro_spi_remove, .id_table = st_gyro_id_table, }; module_spi_driver(st_gyro_driver); diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index d4921385aaf7..97b82f9a8e45 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -487,10 +487,10 @@ static int afe4403_probe(struct spi_device *spi) } afe->regulator = devm_regulator_get(afe->dev, "tx_sup"); - if (IS_ERR(afe->regulator)) { - dev_err(afe->dev, "Unable to get regulator\n"); - return PTR_ERR(afe->regulator); - } + if (IS_ERR(afe->regulator)) + return dev_err_probe(afe->dev, PTR_ERR(afe->regulator), + "Unable to get regulator\n"); + ret = regulator_enable(afe->regulator); if (ret) { dev_err(afe->dev, "Unable to enable regulator\n"); @@ -589,10 +589,8 @@ static int afe4403_remove(struct spi_device *spi) iio_trigger_unregister(afe->trig); ret = regulator_disable(afe->regulator); - if (ret) { - dev_err(afe->dev, "Unable to disable regulator\n"); - return ret; - } + if (ret) + dev_warn(afe->dev, "Unable to disable regulator\n"); return 0; } diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index d8a27dfe074a..7ef3f5e34de5 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -494,10 +494,10 @@ static int afe4404_probe(struct i2c_client *client, } afe->regulator = devm_regulator_get(afe->dev, "tx_sup"); - if (IS_ERR(afe->regulator)) { - dev_err(afe->dev, "Unable to get regulator\n"); - return PTR_ERR(afe->regulator); - } + if (IS_ERR(afe->regulator)) + return dev_err_probe(afe->dev, PTR_ERR(afe->regulator), + "Unable to get regulator\n"); + ret = regulator_enable(afe->regulator); if (ret) { dev_err(afe->dev, "Unable to enable regulator\n"); diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index 8f4a9b264962..61e318431de9 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -68,12 +68,15 @@ __poll_t iio_buffer_poll_wrapper(struct file *filp, struct poll_table_struct *wait); ssize_t iio_buffer_read_wrapper(struct file *filp, char __user *buf, size_t n, loff_t *f_ps); +ssize_t iio_buffer_write_wrapper(struct file *filp, const char __user *buf, + size_t n, loff_t *f_ps); int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev); void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev); #define iio_buffer_poll_addr (&iio_buffer_poll_wrapper) #define iio_buffer_read_outer_addr (&iio_buffer_read_wrapper) +#define iio_buffer_write_outer_addr (&iio_buffer_write_wrapper) void iio_disable_all_buffers(struct iio_dev *indio_dev); void iio_buffer_wakeup_poll(struct iio_dev *indio_dev); @@ -83,6 +86,7 @@ void iio_device_detach_buffers(struct iio_dev *indio_dev); #define iio_buffer_poll_addr NULL #define iio_buffer_read_outer_addr NULL +#define iio_buffer_write_outer_addr NULL static inline int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) { diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index b9a06ca29bee..cb0d66bf6561 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -286,6 +286,13 @@ int adis_enable_irq(struct adis *adis, bool enable) if (adis->data->enable_irq) { ret = adis->data->enable_irq(adis, enable); goto out_unlock; + } else if (adis->data->unmasked_drdy) { + if (enable) + enable_irq(adis->spi->irq); + else + disable_irq(adis->spi->irq); + + goto out_unlock; } ret = __adis_read_reg_16(adis, adis->data->msc_ctrl_reg, &msc); @@ -430,6 +437,14 @@ int __adis_initial_startup(struct adis *adis) if (ret) return ret; + /* + * don't bother calling this if we can't unmask the IRQ as in this case + * the IRQ is most likely not yet requested and we will request it + * with 'IRQF_NO_AUTOEN' anyways. + */ + if (!adis->data->unmasked_drdy) + adis_enable_irq(adis, false); + if (!adis->data->prod_id_reg) return 0; @@ -526,7 +541,7 @@ int adis_init(struct adis *adis, struct iio_dev *indio_dev, adis->current_page = 0; } - return adis_enable_irq(adis, false); + return 0; } EXPORT_SYMBOL_GPL(adis_init); diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index b12917a7cb60..9fd30e62d6e8 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -641,13 +641,23 @@ static irqreturn_t adis16400_trigger_handler(int irq, void *p) if (ret) dev_err(&adis->spi->dev, "Failed to read data: %d\n", ret); - if (st->variant->flags & ADIS16400_BURST_DIAG_STAT) + if (st->variant->flags & ADIS16400_BURST_DIAG_STAT) { buffer = adis->buffer + sizeof(u16); - else - buffer = adis->buffer; + /* + * The size here is always larger than, or equal to the true + * size of the channel data. This may result in a larger copy + * than necessary, but as the target buffer will be + * buffer->scan_bytes this will be safe. + */ + iio_push_to_buffers_with_ts_unaligned(indio_dev, buffer, + indio_dev->scan_bytes - sizeof(pf->timestamp), + pf->timestamp); + } else { + iio_push_to_buffers_with_timestamp(indio_dev, + adis->buffer, + pf->timestamp); + } - iio_push_to_buffers_with_timestamp(indio_dev, buffer, - pf->timestamp); iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c index a6f9fba3e03f..b01988170118 100644 --- a/drivers/iio/imu/adis16460.c +++ b/drivers/iio/imu/adis16460.c @@ -319,20 +319,6 @@ static const struct iio_info adis16460_info = { .debugfs_reg_access = adis_debugfs_reg_access, }; -static int adis16460_enable_irq(struct adis *adis, bool enable) -{ - /* - * There is no way to gate the data-ready signal internally inside the - * ADIS16460 :( - */ - if (enable) - enable_irq(adis->spi->irq); - else - disable_irq(adis->spi->irq); - - return 0; -} - #define ADIS16460_DIAG_STAT_IN_CLK_OOS 7 #define ADIS16460_DIAG_STAT_FLASH_MEM 6 #define ADIS16460_DIAG_STAT_SELF_TEST 5 @@ -373,7 +359,7 @@ static const struct adis_data adis16460_data = { BIT(ADIS16460_DIAG_STAT_OVERRANGE) | BIT(ADIS16460_DIAG_STAT_SPI_COMM) | BIT(ADIS16460_DIAG_STAT_FLASH_UPT), - .enable_irq = adis16460_enable_irq, + .unmasked_drdy = true, .timeouts = &adis16460_timeouts, }; @@ -400,8 +386,6 @@ static int adis16460_probe(struct spi_device *spi) if (ret) return ret; - /* We cannot mask the interrupt, so ensure it isn't auto enabled */ - st->adis.irq_flag |= IRQF_NO_AUTOEN; ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL); if (ret) return ret; diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index 287fff39a927..ea91d127077d 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -607,20 +607,6 @@ static const char * const adis16475_status_error_msgs[] = { [ADIS16475_DIAG_STAT_CLK] = "Clock error", }; -static int adis16475_enable_irq(struct adis *adis, bool enable) -{ - /* - * There is no way to gate the data-ready signal internally inside the - * ADIS16475. We can only control it's polarity... - */ - if (enable) - enable_irq(adis->spi->irq); - else - disable_irq(adis->spi->irq); - - return 0; -} - #define ADIS16475_DATA(_prod_id, _timeouts) \ { \ .msc_ctrl_reg = ADIS16475_REG_MSG_CTRL, \ @@ -641,7 +627,7 @@ static int adis16475_enable_irq(struct adis *adis, bool enable) BIT(ADIS16475_DIAG_STAT_SENSOR) | \ BIT(ADIS16475_DIAG_STAT_MEMORY) | \ BIT(ADIS16475_DIAG_STAT_CLK), \ - .enable_irq = adis16475_enable_irq, \ + .unmasked_drdy = true, \ .timeouts = (_timeouts), \ .burst_reg_cmd = ADIS16475_REG_GLOB_CMD, \ .burst_len = ADIS16475_BURST_MAX_DATA, \ @@ -1255,9 +1241,6 @@ static int adis16475_config_irq_pin(struct adis16475 *st) return -EINVAL; } - /* We cannot mask the interrupt so ensure it's not enabled at request */ - st->adis.irq_flag |= IRQF_NO_AUTOEN; - val = ADIS16475_MSG_CTRL_DR_POL(polarity); ret = __adis_update_bits(&st->adis, ADIS16475_REG_MSG_CTRL, ADIS16475_MSG_CTRL_DR_POL_MASK, val); diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c index 48eedc29b28a..c461bd1e8e69 100644 --- a/drivers/iio/imu/adis_trigger.c +++ b/drivers/iio/imu/adis_trigger.c @@ -30,6 +30,10 @@ static const struct iio_trigger_ops adis_trigger_ops = { static int adis_validate_irq_flag(struct adis *adis) { unsigned long direction = adis->irq_flag & IRQF_TRIGGER_MASK; + + /* We cannot mask the interrupt so ensure it's not enabled at request */ + if (adis->data->unmasked_drdy) + adis->irq_flag |= IRQF_NO_AUTOEN; /* * Typically this devices have data ready either on the rising edge or * on the falling edge of the data ready pin. This checks enforces that diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 95f16951c8f4..3ef17e3f50e2 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -249,7 +249,7 @@ static const struct of_device_id inv_of_match[] = { }; MODULE_DEVICE_TABLE(of, inv_of_match); -static const struct acpi_device_id inv_acpi_match[] = { +static const struct acpi_device_id __maybe_unused inv_acpi_match[] = { {"INVN6500", INV_MPU6500}, { }, }; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c index f282e9cc34c5..6aee6c989485 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_magn.c @@ -261,6 +261,7 @@ int inv_mpu_magn_set_rate(const struct inv_mpu6050_state *st, int fifo_rate) */ int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st) { + struct device *dev = regmap_get_device(st->map); const char *orient; char *str; int i; @@ -279,22 +280,27 @@ int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st) st->magn_orient.rotation[4] = st->orientation.rotation[1]; st->magn_orient.rotation[5] = st->orientation.rotation[2]; /* z <- -z */ - for (i = 0; i < 3; ++i) { - orient = st->orientation.rotation[6 + i]; - /* use length + 2 for adding minus sign if needed */ - str = devm_kzalloc(regmap_get_device(st->map), - strlen(orient) + 2, GFP_KERNEL); - if (str == NULL) + for (i = 6; i < 9; ++i) { + orient = st->orientation.rotation[i]; + + /* + * The value is negated according to one of the following + * rules: + * + * 1) Drop leading minus. + * 2) Leave 0 as is. + * 3) Add leading minus. + */ + if (orient[0] == '-') + str = devm_kstrdup(dev, orient + 1, GFP_KERNEL); + else if (!strcmp(orient, "0")) + str = devm_kstrdup(dev, orient, GFP_KERNEL); + else + str = devm_kasprintf(dev, GFP_KERNEL, "-%s", orient); + if (!str) return -ENOMEM; - if (strcmp(orient, "0") == 0) { - strcpy(str, orient); - } else if (orient[0] == '-') { - strcpy(str, &orient[1]); - } else { - str[0] = '-'; - strcpy(&str[1], orient); - } - st->magn_orient.rotation[6 + i] = str; + + st->magn_orient.rotation[i] = str; } break; default: diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 5ef55763a6cc..6ac4eac36458 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -143,6 +143,7 @@ struct st_lsm6dsx_fs_table_entry { * @read_fifo: Read FIFO callback. * @fifo_th: FIFO threshold register info (addr + mask). * @fifo_diff: FIFO diff status register info (addr + mask). + * @max_size: Sensor max fifo length in FIFO words. * @th_wl: FIFO threshold word length. */ struct st_lsm6dsx_fifo_ops { @@ -156,6 +157,7 @@ struct st_lsm6dsx_fifo_ops { u8 addr; u16 mask; } fifo_diff; + u16 max_size; u8 th_wl; }; @@ -271,7 +273,6 @@ struct st_lsm6dsx_ext_dev_settings { * @reset: register address for reset. * @boot: register address for boot. * @bdu: register address for Block Data Update. - * @max_fifo_size: Sensor max fifo length in FIFO words. * @id: List of hw id/device name supported by the driver configuration. * @channels: IIO channels supported by the device. * @irq_config: interrupts related registers. @@ -288,7 +289,6 @@ struct st_lsm6dsx_settings { struct st_lsm6dsx_reg reset; struct st_lsm6dsx_reg boot; struct st_lsm6dsx_reg bdu; - u16 max_fifo_size; struct { enum st_lsm6dsx_hw_id hw_id; const char *name; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index db45f1fc0b81..0f54df85134a 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -102,7 +102,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x22, .mask = BIT(6), }, - .max_fifo_size = 32, .id = { { .hw_id = ST_LSM9DS1_ID, @@ -194,6 +193,9 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .mask = BIT(4), }, }, + .fifo_ops = { + .max_size = 32, + }, }, { .reset = { @@ -208,7 +210,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x12, .mask = BIT(6), }, - .max_fifo_size = 1365, .id = { { .hw_id = ST_LSM6DS3_ID, @@ -329,6 +330,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x3a, .mask = GENMASK(11, 0), }, + .max_size = 1365, .th_wl = 3, /* 1LSB = 2B */ }, .ts_settings = { @@ -374,7 +376,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x12, .mask = BIT(6), }, - .max_fifo_size = 682, .id = { { .hw_id = ST_LSM6DS3H_ID, @@ -495,6 +496,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x3a, .mask = GENMASK(11, 0), }, + .max_size = 682, .th_wl = 3, /* 1LSB = 2B */ }, .ts_settings = { @@ -540,7 +542,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x12, .mask = BIT(6), }, - .max_fifo_size = 682, .id = { { .hw_id = ST_LSM6DSL_ID, @@ -677,6 +678,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x3a, .mask = GENMASK(10, 0), }, + .max_size = 682, .th_wl = 3, /* 1LSB = 2B */ }, .ts_settings = { @@ -759,7 +761,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x12, .mask = BIT(6), }, - .max_fifo_size = 512, .id = { { .hw_id = ST_LSM6DSR_ID, @@ -910,6 +911,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x3a, .mask = GENMASK(9, 0), }, + .max_size = 512, .th_wl = 1, }, .ts_settings = { @@ -984,7 +986,6 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x12, .mask = BIT(6), }, - .max_fifo_size = 512, .id = { { .hw_id = ST_ASM330LHH_ID, @@ -1119,6 +1120,7 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { .addr = 0x3a, .mask = GENMASK(9, 0), }, + .max_size = 512, .th_wl = 1, }, .ts_settings = { @@ -1603,7 +1605,7 @@ int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val) struct st_lsm6dsx_hw *hw = sensor->hw; int err; - if (val < 1 || val > hw->settings->max_fifo_size) + if (val < 1 || val > hw->settings->fifo_ops.max_size) return -EINVAL; mutex_lock(&hw->conf_lock); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h index 146393afd9a7..76678cdefb07 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h @@ -18,6 +18,5 @@ struct st_lsm9ds0 { }; int st_lsm9ds0_probe(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap); -int st_lsm9ds0_remove(struct st_lsm9ds0 *lsm9ds0); #endif /* ST_LSM9DS0_H */ diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c index 5e6625140db7..9fb06b7cde3c 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c @@ -24,10 +24,10 @@ static int st_lsm9ds0_power_enable(struct device *dev, struct st_lsm9ds0 *lsm9ds /* Regulators not mandatory, but if requested we should enable them. */ lsm9ds0->vdd = devm_regulator_get(dev, "vdd"); - if (IS_ERR(lsm9ds0->vdd)) { - dev_err(dev, "unable to get Vdd supply\n"); - return PTR_ERR(lsm9ds0->vdd); - } + if (IS_ERR(lsm9ds0->vdd)) + return dev_err_probe(dev, PTR_ERR(lsm9ds0->vdd), + "unable to get Vdd supply\n"); + ret = regulator_enable(lsm9ds0->vdd); if (ret) { dev_warn(dev, "Failed to enable specified Vdd supply\n"); @@ -36,9 +36,9 @@ static int st_lsm9ds0_power_enable(struct device *dev, struct st_lsm9ds0 *lsm9ds lsm9ds0->vdd_io = devm_regulator_get(dev, "vddio"); if (IS_ERR(lsm9ds0->vdd_io)) { - dev_err(dev, "unable to get Vdd_IO supply\n"); regulator_disable(lsm9ds0->vdd); - return PTR_ERR(lsm9ds0->vdd_io); + return dev_err_probe(dev, PTR_ERR(lsm9ds0->vdd_io), + "unable to get Vdd_IO supply\n"); } ret = regulator_enable(lsm9ds0->vdd_io); if (ret) { @@ -90,7 +90,6 @@ static int st_lsm9ds0_probe_accel(struct st_lsm9ds0 *lsm9ds0, struct regmap *reg data = iio_priv(lsm9ds0->accel); data->sensor_settings = (struct st_sensor_settings *)settings; - data->dev = dev; data->irq = lsm9ds0->irq; data->regmap = regmap; data->vdd = lsm9ds0->vdd; @@ -119,7 +118,6 @@ static int st_lsm9ds0_probe_magn(struct st_lsm9ds0 *lsm9ds0, struct regmap *regm data = iio_priv(lsm9ds0->magn); data->sensor_settings = (struct st_sensor_settings *)settings; - data->dev = dev; data->irq = lsm9ds0->irq; data->regmap = regmap; data->vdd = lsm9ds0->vdd; @@ -142,23 +140,10 @@ int st_lsm9ds0_probe(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap) return ret; /* Setup magnetometer device */ - ret = st_lsm9ds0_probe_magn(lsm9ds0, regmap); - if (ret) - st_accel_common_remove(lsm9ds0->accel); - - return ret; + return st_lsm9ds0_probe_magn(lsm9ds0, regmap); } EXPORT_SYMBOL_GPL(st_lsm9ds0_probe); -int st_lsm9ds0_remove(struct st_lsm9ds0 *lsm9ds0) -{ - st_magn_common_remove(lsm9ds0->magn); - st_accel_common_remove(lsm9ds0->accel); - - return 0; -} -EXPORT_SYMBOL_GPL(st_lsm9ds0_remove); - MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); MODULE_DESCRIPTION("STMicroelectronics LSM9DS0 IMU core driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c index 78bede358747..8f205c477e6f 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c @@ -64,18 +64,12 @@ static int st_lsm9ds0_i2c_probe(struct i2c_client *client) return st_lsm9ds0_probe(lsm9ds0, regmap); } -static int st_lsm9ds0_i2c_remove(struct i2c_client *client) -{ - return st_lsm9ds0_remove(i2c_get_clientdata(client)); -} - static struct i2c_driver st_lsm9ds0_driver = { .driver = { .name = "st-lsm9ds0-i2c", .of_match_table = st_lsm9ds0_of_match, }, .probe_new = st_lsm9ds0_i2c_probe, - .remove = st_lsm9ds0_i2c_remove, .id_table = st_lsm9ds0_id_table, }; module_i2c_driver(st_lsm9ds0_driver); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c index 180b54e66438..0ddfa53166af 100644 --- a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c @@ -63,18 +63,12 @@ static int st_lsm9ds0_spi_probe(struct spi_device *spi) return st_lsm9ds0_probe(lsm9ds0, regmap); } -static int st_lsm9ds0_spi_remove(struct spi_device *spi) -{ - return st_lsm9ds0_remove(spi_get_drvdata(spi)); -} - static struct spi_driver st_lsm9ds0_driver = { .driver = { .name = "st-lsm9ds0-spi", .of_match_table = st_lsm9ds0_of_match, }, .probe = st_lsm9ds0_spi_probe, - .remove = st_lsm9ds0_spi_remove, .id_table = st_lsm9ds0_id_table, }; module_spi_driver(st_lsm9ds0_driver); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index a95cc2da56be..b884d78657cb 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -120,6 +120,9 @@ static ssize_t iio_buffer_read(struct file *filp, char __user *buf, if (!rb || !rb->access->read) return -EINVAL; + if (rb->direction != IIO_BUFFER_DIRECTION_IN) + return -EPERM; + datum_size = rb->bytes_per_datum; /* @@ -161,6 +164,65 @@ static ssize_t iio_buffer_read(struct file *filp, char __user *buf, return ret; } +static size_t iio_buffer_space_available(struct iio_buffer *buf) +{ + if (buf->access->space_available) + return buf->access->space_available(buf); + + return SIZE_MAX; +} + +static ssize_t iio_buffer_write(struct file *filp, const char __user *buf, + size_t n, loff_t *f_ps) +{ + struct iio_dev_buffer_pair *ib = filp->private_data; + struct iio_buffer *rb = ib->buffer; + struct iio_dev *indio_dev = ib->indio_dev; + DEFINE_WAIT_FUNC(wait, woken_wake_function); + int ret; + size_t written; + + if (!indio_dev->info) + return -ENODEV; + + if (!rb || !rb->access->write) + return -EINVAL; + + if (rb->direction != IIO_BUFFER_DIRECTION_OUT) + return -EPERM; + + written = 0; + add_wait_queue(&rb->pollq, &wait); + do { + if (indio_dev->info == NULL) + return -ENODEV; + + if (!iio_buffer_space_available(rb)) { + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + wait_woken(&wait, TASK_INTERRUPTIBLE, + MAX_SCHEDULE_TIMEOUT); + continue; + } + + ret = rb->access->write(rb, n - written, buf + written); + if (ret == 0 && (filp->f_flags & O_NONBLOCK)) + ret = -EAGAIN; + + if (ret > 0) { + written += ret; + if (written != n && !(filp->f_flags & O_NONBLOCK)) + continue; + } + } while (ret == 0); + remove_wait_queue(&rb->pollq, &wait); + + return ret < 0 ? ret : n; +} + /** * iio_buffer_poll() - poll the buffer to find out if it has data * @filp: File structure pointer for device access @@ -181,8 +243,18 @@ static __poll_t iio_buffer_poll(struct file *filp, return 0; poll_wait(filp, &rb->pollq, wait); - if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0)) - return EPOLLIN | EPOLLRDNORM; + + switch (rb->direction) { + case IIO_BUFFER_DIRECTION_IN: + if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0)) + return EPOLLIN | EPOLLRDNORM; + break; + case IIO_BUFFER_DIRECTION_OUT: + if (iio_buffer_space_available(rb)) + return EPOLLOUT | EPOLLWRNORM; + break; + } + return 0; } @@ -199,6 +271,19 @@ ssize_t iio_buffer_read_wrapper(struct file *filp, char __user *buf, return iio_buffer_read(filp, buf, n, f_ps); } +ssize_t iio_buffer_write_wrapper(struct file *filp, const char __user *buf, + size_t n, loff_t *f_ps) +{ + struct iio_dev_buffer_pair *ib = filp->private_data; + struct iio_buffer *rb = ib->buffer; + + /* check if buffer was opened through new API */ + if (test_bit(IIO_BUSY_BIT_POS, &rb->flags)) + return -EBUSY; + + return iio_buffer_write(filp, buf, n, f_ps); +} + __poll_t iio_buffer_poll_wrapper(struct file *filp, struct poll_table_struct *wait) { @@ -231,6 +316,15 @@ void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) } } +int iio_pop_from_buffer(struct iio_buffer *buffer, void *data) +{ + if (!buffer || !buffer->access || !buffer->access->remove_from) + return -EINVAL; + + return buffer->access->remove_from(buffer, data); +} +EXPORT_SYMBOL_GPL(iio_pop_from_buffer); + void iio_buffer_init(struct iio_buffer *buffer) { INIT_LIST_HEAD(&buffer->demux_list); @@ -1156,6 +1250,10 @@ int iio_update_buffers(struct iio_dev *indio_dev, if (insert_buffer == remove_buffer) return 0; + if (insert_buffer && + (insert_buffer->direction == IIO_BUFFER_DIRECTION_OUT)) + return -EINVAL; + mutex_lock(&iio_dev_opaque->info_exist_lock); mutex_lock(&indio_dev->mlock); @@ -1277,6 +1375,22 @@ static ssize_t iio_dma_show_data_available(struct device *dev, return sysfs_emit(buf, "%zu\n", iio_buffer_data_available(buffer)); } +static ssize_t direction_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; + + switch (buffer->direction) { + case IIO_BUFFER_DIRECTION_IN: + return sprintf(buf, "in\n"); + case IIO_BUFFER_DIRECTION_OUT: + return sprintf(buf, "out\n"); + default: + return -EINVAL; + } +} + static DEVICE_ATTR(length, S_IRUGO | S_IWUSR, iio_buffer_read_length, iio_buffer_write_length); static struct device_attribute dev_attr_length_ro = __ATTR(length, @@ -1289,12 +1403,20 @@ static struct device_attribute dev_attr_watermark_ro = __ATTR(watermark, S_IRUGO, iio_buffer_show_watermark, NULL); static DEVICE_ATTR(data_available, S_IRUGO, iio_dma_show_data_available, NULL); +static DEVICE_ATTR_RO(direction); +/* + * When adding new attributes here, put the at the end, at least until + * the code that handles the length/length_ro & watermark/watermark_ro + * assignments gets cleaned up. Otherwise these can create some weird + * duplicate attributes errors under some setups. + */ static struct attribute *iio_buffer_attrs[] = { &dev_attr_length.attr, &dev_attr_enable.attr, &dev_attr_watermark.attr, &dev_attr_data_available.attr, + &dev_attr_direction.attr, }; #define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr) @@ -1397,6 +1519,7 @@ static const struct file_operations iio_buffer_chrdev_fileops = { .owner = THIS_MODULE, .llseek = noop_llseek, .read = iio_buffer_read, + .write = iio_buffer_write, .poll = iio_buffer_poll, .release = iio_buffer_chrdev_release, }; @@ -1732,6 +1855,52 @@ int iio_push_to_buffers(struct iio_dev *indio_dev, const void *data) EXPORT_SYMBOL_GPL(iio_push_to_buffers); /** + * iio_push_to_buffers_with_ts_unaligned() - push to registered buffer, + * no alignment or space requirements. + * @indio_dev: iio_dev structure for device. + * @data: channel data excluding the timestamp. + * @data_sz: size of data. + * @timestamp: timestamp for the sample data. + * + * This special variant of iio_push_to_buffers_with_timestamp() does + * not require space for the timestamp, or 8 byte alignment of data. + * It does however require an allocation on first call and additional + * copies on all calls, so should be avoided if possible. + */ +int iio_push_to_buffers_with_ts_unaligned(struct iio_dev *indio_dev, + const void *data, + size_t data_sz, + int64_t timestamp) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + + /* + * Conservative estimate - we can always safely copy the minimum + * of either the data provided or the length of the destination buffer. + * This relaxed limit allows the calling drivers to be lax about + * tracking the size of the data they are pushing, at the cost of + * unnecessary copying of padding. + */ + data_sz = min_t(size_t, indio_dev->scan_bytes, data_sz); + if (iio_dev_opaque->bounce_buffer_size != indio_dev->scan_bytes) { + void *bb; + + bb = devm_krealloc(&indio_dev->dev, + iio_dev_opaque->bounce_buffer, + indio_dev->scan_bytes, GFP_KERNEL); + if (!bb) + return -ENOMEM; + iio_dev_opaque->bounce_buffer = bb; + iio_dev_opaque->bounce_buffer_size = indio_dev->scan_bytes; + } + memcpy(iio_dev_opaque->bounce_buffer, data, data_sz); + return iio_push_to_buffers_with_timestamp(indio_dev, + iio_dev_opaque->bounce_buffer, + timestamp); +} +EXPORT_SYMBOL_GPL(iio_push_to_buffers_with_ts_unaligned); + +/** * iio_buffer_release() - Free a buffer's resources * @ref: Pointer to the kref embedded in the iio_buffer struct * diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 2dbb37e09b8c..537a08549a69 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1822,6 +1822,7 @@ static const struct file_operations iio_buffer_fileops = { .owner = THIS_MODULE, .llseek = noop_llseek, .read = iio_buffer_read_outer_addr, + .write = iio_buffer_write_outer_addr, .poll = iio_buffer_poll_addr, .unlocked_ioctl = iio_ioctl, .compat_ioctl = compat_ptr_ioctl, diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 391a3380a1d1..0222885b334c 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -85,6 +85,23 @@ int iio_map_array_unregister(struct iio_dev *indio_dev) } EXPORT_SYMBOL_GPL(iio_map_array_unregister); +static void iio_map_array_unregister_cb(void *indio_dev) +{ + iio_map_array_unregister(indio_dev); +} + +int devm_iio_map_array_register(struct device *dev, struct iio_dev *indio_dev, struct iio_map *maps) +{ + int ret; + + ret = iio_map_array_register(indio_dev, maps); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, iio_map_array_unregister_cb, indio_dev); +} +EXPORT_SYMBOL_GPL(devm_iio_map_array_register); + static const struct iio_chan_spec *iio_chan_spec_from_name(const struct iio_dev *indio_dev, const char *name) { diff --git a/drivers/iio/light/cm36651.c b/drivers/iio/light/cm36651.c index fd83a19929bc..89f5e48a6642 100644 --- a/drivers/iio/light/cm36651.c +++ b/drivers/iio/light/cm36651.c @@ -632,10 +632,9 @@ static int cm36651_probe(struct i2c_client *client, cm36651 = iio_priv(indio_dev); cm36651->vled_reg = devm_regulator_get(&client->dev, "vled"); - if (IS_ERR(cm36651->vled_reg)) { - dev_err(&client->dev, "get regulator vled failed\n"); - return PTR_ERR(cm36651->vled_reg); - } + if (IS_ERR(cm36651->vled_reg)) + return dev_err_probe(&client->dev, PTR_ERR(cm36651->vled_reg), + "get regulator vled failed\n"); ret = regulator_enable(cm36651->vled_reg); if (ret) { diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c index b8e721bced5b..85689dffbcbf 100644 --- a/drivers/iio/light/max44000.c +++ b/drivers/iio/light/max44000.c @@ -540,7 +540,6 @@ static int max44000_probe(struct i2c_client *client, return PTR_ERR(data->regmap); } - i2c_set_clientdata(client, indio_dev); mutex_init(&data->lock); indio_dev->info = &max44000_info; indio_dev->name = MAX44000_DRV_NAME; @@ -589,23 +588,14 @@ static int max44000_probe(struct i2c_client *client, return ret; } - ret = iio_triggered_buffer_setup(indio_dev, NULL, max44000_trigger_handler, NULL); + ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, + max44000_trigger_handler, NULL); if (ret < 0) { dev_err(&client->dev, "iio triggered buffer setup failed\n"); return ret; } - return iio_device_register(indio_dev); -} - -static int max44000_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - - return 0; + return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id max44000_id[] = { @@ -628,7 +618,6 @@ static struct i2c_driver max44000_driver = { .acpi_match_table = ACPI_PTR(max44000_acpi_match), }, .probe = max44000_probe, - .remove = max44000_remove, .id_table = max44000_id, }; diff --git a/drivers/iio/light/noa1305.c b/drivers/iio/light/noa1305.c index a308fbc2fc7b..ee81fe083e4c 100644 --- a/drivers/iio/light/noa1305.c +++ b/drivers/iio/light/noa1305.c @@ -217,10 +217,9 @@ static int noa1305_probe(struct i2c_client *client, priv = iio_priv(indio_dev); priv->vin_reg = devm_regulator_get(&client->dev, "vin"); - if (IS_ERR(priv->vin_reg)) { - dev_err(&client->dev, "get regulator vin failed\n"); - return PTR_ERR(priv->vin_reg); - } + if (IS_ERR(priv->vin_reg)) + return dev_err_probe(&client->dev, PTR_ERR(priv->vin_reg), + "get regulator vin failed\n"); ret = regulator_enable(priv->vin_reg); if (ret) { diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 74ad5701c6c2..565ee41ccb3a 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -28,7 +28,7 @@ config AK8975 select IIO_TRIGGERED_BUFFER help Say yes here to build support for Asahi Kasei AK8975, AK8963, - AK09911 or AK09912 3-Axis Magnetometer. + AK09911, AK09912 or AK09916 3-Axis Magnetometer. To compile this driver as a module, choose M here: the module will be called ak8975. diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 42b8a2680e3a..6e82dc54a417 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -78,6 +78,7 @@ */ #define AK09912_REG_WIA1 0x00 #define AK09912_REG_WIA2 0x01 +#define AK09916_DEVICE_ID 0x09 #define AK09912_DEVICE_ID 0x04 #define AK09911_DEVICE_ID 0x05 @@ -208,6 +209,7 @@ enum asahi_compass_chipset { AK8963, AK09911, AK09912, + AK09916, }; enum ak_ctrl_reg_addr { @@ -345,6 +347,31 @@ static const struct ak_def ak_def_array[] = { AK09912_REG_HXL, AK09912_REG_HYL, AK09912_REG_HZL}, + }, + { + .type = AK09916, + .raw_to_gauss = ak09912_raw_to_gauss, + .range = 32752, + .ctrl_regs = { + AK09912_REG_ST1, + AK09912_REG_ST2, + AK09912_REG_CNTL2, + AK09912_REG_ASAX, + AK09912_MAX_REGS}, + .ctrl_masks = { + AK09912_REG_ST1_DRDY_MASK, + AK09912_REG_ST2_HOFL_MASK, + 0, + AK09912_REG_CNTL2_MODE_MASK}, + .ctrl_modes = { + AK09912_REG_CNTL_MODE_POWER_DOWN, + AK09912_REG_CNTL_MODE_ONCE, + AK09912_REG_CNTL_MODE_SELF_TEST, + AK09912_REG_CNTL_MODE_FUSE_ROM}, + .data_regs = { + AK09912_REG_HXL, + AK09912_REG_HYL, + AK09912_REG_HZL}, } }; @@ -425,6 +452,7 @@ static int ak8975_who_i_am(struct i2c_client *client, /* * Signature for each device: * Device | WIA1 | WIA2 + * AK09916 | DEVICE_ID_| AK09916_DEVICE_ID * AK09912 | DEVICE_ID | AK09912_DEVICE_ID * AK09911 | DEVICE_ID | AK09911_DEVICE_ID * AK8975 | DEVICE_ID | NA @@ -452,6 +480,10 @@ static int ak8975_who_i_am(struct i2c_client *client, if (wia_val[1] == AK09912_DEVICE_ID) return 0; break; + case AK09916: + if (wia_val[1] == AK09916_DEVICE_ID) + return 0; + break; default: dev_err(&client->dev, "Type %d unknown\n", type); } @@ -1057,6 +1089,7 @@ static const struct i2c_device_id ak8975_id[] = { {"AK8963", AK8963}, {"ak09911", AK09911}, {"ak09912", AK09912}, + {"ak09916", AK09916}, {} }; @@ -1071,6 +1104,8 @@ static const struct of_device_id ak8975_of_match[] = { { .compatible = "ak09911", }, { .compatible = "asahi-kasei,ak09912", }, { .compatible = "ak09912", }, + { .compatible = "asahi-kasei,ak09916", }, + { .compatible = "ak09916", }, {} }; MODULE_DEVICE_TABLE(of, ak8975_of_match); diff --git a/drivers/iio/magnetometer/hmc5843.h b/drivers/iio/magnetometer/hmc5843.h index 242f742f2643..9120c8bbf3dd 100644 --- a/drivers/iio/magnetometer/hmc5843.h +++ b/drivers/iio/magnetometer/hmc5843.h @@ -50,7 +50,7 @@ struct hmc5843_data { int hmc5843_common_probe(struct device *dev, struct regmap *regmap, enum hmc5843_ids id, const char *name); -int hmc5843_common_remove(struct device *dev); +void hmc5843_common_remove(struct device *dev); int hmc5843_common_suspend(struct device *dev); int hmc5843_common_resume(struct device *dev); diff --git a/drivers/iio/magnetometer/hmc5843_core.c b/drivers/iio/magnetometer/hmc5843_core.c index cf62057480cf..f08726bf5ec3 100644 --- a/drivers/iio/magnetometer/hmc5843_core.c +++ b/drivers/iio/magnetometer/hmc5843_core.c @@ -671,7 +671,7 @@ buffer_setup_err: } EXPORT_SYMBOL(hmc5843_common_probe); -int hmc5843_common_remove(struct device *dev) +void hmc5843_common_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -680,8 +680,6 @@ int hmc5843_common_remove(struct device *dev) /* sleep mode to save power */ hmc5843_set_mode(iio_priv(indio_dev), HMC5843_MODE_SLEEP); - - return 0; } EXPORT_SYMBOL(hmc5843_common_remove); diff --git a/drivers/iio/magnetometer/hmc5843_i2c.c b/drivers/iio/magnetometer/hmc5843_i2c.c index 67fe657fdb3e..bc6e12f1d521 100644 --- a/drivers/iio/magnetometer/hmc5843_i2c.c +++ b/drivers/iio/magnetometer/hmc5843_i2c.c @@ -67,7 +67,9 @@ static int hmc5843_i2c_probe(struct i2c_client *cli, static int hmc5843_i2c_remove(struct i2c_client *client) { - return hmc5843_common_remove(&client->dev); + hmc5843_common_remove(&client->dev); + + return 0; } static const struct i2c_device_id hmc5843_id[] = { diff --git a/drivers/iio/magnetometer/hmc5843_spi.c b/drivers/iio/magnetometer/hmc5843_spi.c index d827554c346e..89cf59a62c28 100644 --- a/drivers/iio/magnetometer/hmc5843_spi.c +++ b/drivers/iio/magnetometer/hmc5843_spi.c @@ -76,7 +76,9 @@ static int hmc5843_spi_probe(struct spi_device *spi) static int hmc5843_spi_remove(struct spi_device *spi) { - return hmc5843_common_remove(&spi->dev); + hmc5843_common_remove(&spi->dev); + + return 0; } static const struct spi_device_id hmc5843_id[] = { diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 9ffd50d796bf..0806a1e65ce4 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -611,7 +611,8 @@ EXPORT_SYMBOL(st_magn_get_settings); int st_magn_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *mdata = iio_priv(indio_dev); - struct st_sensors_platform_data *pdata = dev_get_platdata(mdata->dev); + struct device *parent = indio_dev->dev.parent; + struct st_sensors_platform_data *pdata = dev_get_platdata(parent); int err; indio_dev->modes = INDIO_DIRECT_MODE; @@ -625,7 +626,7 @@ int st_magn_common_probe(struct iio_dev *indio_dev) indio_dev->channels = mdata->sensor_settings->ch; indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; - err = iio_read_mount_matrix(mdata->dev, &mdata->mount_matrix); + err = iio_read_mount_matrix(parent, &mdata->mount_matrix); if (err) return err; @@ -650,32 +651,10 @@ int st_magn_common_probe(struct iio_dev *indio_dev) return err; } - err = iio_device_register(indio_dev); - if (err) - goto st_magn_device_register_error; - - dev_info(&indio_dev->dev, "registered magnetometer %s\n", - indio_dev->name); - - return 0; - -st_magn_device_register_error: - if (mdata->irq > 0) - st_sensors_deallocate_trigger(indio_dev); - return err; + return devm_iio_device_register(parent, indio_dev); } EXPORT_SYMBOL(st_magn_common_probe); -void st_magn_common_remove(struct iio_dev *indio_dev) -{ - struct st_sensor_data *mdata = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (mdata->irq > 0) - st_sensors_deallocate_trigger(indio_dev); -} -EXPORT_SYMBOL(st_magn_common_remove); - MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics magnetometers driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index 2dfe4ee99591..7237711fc09b 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -86,27 +86,7 @@ static int st_magn_i2c_probe(struct i2c_client *client, if (err) return err; - err = st_magn_common_probe(indio_dev); - if (err < 0) - goto st_magn_power_off; - - return 0; - -st_magn_power_off: - st_sensors_power_disable(indio_dev); - - return err; -} - -static int st_magn_i2c_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - st_sensors_power_disable(indio_dev); - - st_magn_common_remove(indio_dev); - - return 0; + return st_magn_common_probe(indio_dev); } static const struct i2c_device_id st_magn_id_table[] = { @@ -128,7 +108,6 @@ static struct i2c_driver st_magn_driver = { .of_match_table = st_magn_of_match, }, .probe = st_magn_i2c_probe, - .remove = st_magn_i2c_remove, .id_table = st_magn_id_table, }; module_i2c_driver(st_magn_driver); diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index fba978796395..489d4462862f 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -80,27 +80,7 @@ static int st_magn_spi_probe(struct spi_device *spi) if (err) return err; - err = st_magn_common_probe(indio_dev); - if (err < 0) - goto st_magn_power_off; - - return 0; - -st_magn_power_off: - st_sensors_power_disable(indio_dev); - - return err; -} - -static int st_magn_spi_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - - st_sensors_power_disable(indio_dev); - - st_magn_common_remove(indio_dev); - - return 0; + return st_magn_common_probe(indio_dev); } static const struct spi_device_id st_magn_id_table[] = { @@ -119,7 +99,6 @@ static struct spi_driver st_magn_driver = { .of_match_table = st_magn_of_match, }, .probe = st_magn_spi_probe, - .remove = st_magn_spi_remove, .id_table = st_magn_id_table, }; module_spi_driver(st_magn_driver); diff --git a/drivers/iio/potentiometer/max5487.c b/drivers/iio/potentiometer/max5487.c index 1c0d46a96200..007c2bd324cb 100644 --- a/drivers/iio/potentiometer/max5487.c +++ b/drivers/iio/potentiometer/max5487.c @@ -115,11 +115,16 @@ static int max5487_spi_probe(struct spi_device *spi) static int max5487_spi_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); + int ret; iio_device_unregister(indio_dev); /* save both wiper regs to NV regs */ - return max5487_write_cmd(spi, MAX5487_COPY_AB_TO_NV); + ret = max5487_write_cmd(spi, MAX5487_COPY_AB_TO_NV); + if (ret) + dev_warn(&spi->dev, "Failed to save wiper regs to NV regs\n"); + + return 0; } static const struct spi_device_id max5487_id[] = { diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h index bc06271fa38b..86b1c4b1820d 100644 --- a/drivers/iio/pressure/ms5611.h +++ b/drivers/iio/pressure/ms5611.h @@ -61,6 +61,6 @@ struct ms5611_state { int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, const char *name, int type); -int ms5611_remove(struct iio_dev *indio_dev); +void ms5611_remove(struct iio_dev *indio_dev); #endif /* _MS5611_H */ diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index 214b0d25f598..ee75f08655c9 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -474,13 +474,11 @@ err_fini: } EXPORT_SYMBOL(ms5611_probe); -int ms5611_remove(struct iio_dev *indio_dev) +void ms5611_remove(struct iio_dev *indio_dev) { iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); ms5611_fini(indio_dev); - - return 0; } EXPORT_SYMBOL(ms5611_remove); diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c index 7c04f730430c..5c82d80f85b6 100644 --- a/drivers/iio/pressure/ms5611_i2c.c +++ b/drivers/iio/pressure/ms5611_i2c.c @@ -110,7 +110,9 @@ static int ms5611_i2c_probe(struct i2c_client *client, static int ms5611_i2c_remove(struct i2c_client *client) { - return ms5611_remove(i2c_get_clientdata(client)); + ms5611_remove(i2c_get_clientdata(client)); + + return 0; } static const struct of_device_id ms5611_i2c_matches[] = { diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c index 45d3a7d5be8e..79bed64c9b68 100644 --- a/drivers/iio/pressure/ms5611_spi.c +++ b/drivers/iio/pressure/ms5611_spi.c @@ -112,7 +112,9 @@ static int ms5611_spi_probe(struct spi_device *spi) static int ms5611_spi_remove(struct spi_device *spi) { - return ms5611_remove(spi_get_drvdata(spi)); + ms5611_remove(spi_get_drvdata(spi)); + + return 0; } static const struct of_device_id ms5611_spi_matches[] = { diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index ab1c17fac807..26a1ee43d56e 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -677,7 +677,8 @@ EXPORT_SYMBOL(st_press_get_settings); int st_press_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *press_data = iio_priv(indio_dev); - struct st_sensors_platform_data *pdata = dev_get_platdata(press_data->dev); + struct device *parent = indio_dev->dev.parent; + struct st_sensors_platform_data *pdata = dev_get_platdata(parent); int err; indio_dev->modes = INDIO_DIRECT_MODE; @@ -721,32 +722,10 @@ int st_press_common_probe(struct iio_dev *indio_dev) return err; } - err = iio_device_register(indio_dev); - if (err) - goto st_press_device_register_error; - - dev_info(&indio_dev->dev, "registered pressure sensor %s\n", - indio_dev->name); - - return err; - -st_press_device_register_error: - if (press_data->irq > 0) - st_sensors_deallocate_trigger(indio_dev); - return err; + return devm_iio_device_register(parent, indio_dev); } EXPORT_SYMBOL(st_press_common_probe); -void st_press_common_remove(struct iio_dev *indio_dev) -{ - struct st_sensor_data *press_data = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (press_data->irq > 0) - st_sensors_deallocate_trigger(indio_dev); -} -EXPORT_SYMBOL(st_press_common_remove); - MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); MODULE_DESCRIPTION("STMicroelectronics pressures driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 52fa98f24478..1939e999a427 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -103,27 +103,7 @@ static int st_press_i2c_probe(struct i2c_client *client, if (ret) return ret; - ret = st_press_common_probe(indio_dev); - if (ret < 0) - goto st_press_power_off; - - return 0; - -st_press_power_off: - st_sensors_power_disable(indio_dev); - - return ret; -} - -static int st_press_i2c_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - st_sensors_power_disable(indio_dev); - - st_press_common_remove(indio_dev); - - return 0; + return st_press_common_probe(indio_dev); } static struct i2c_driver st_press_driver = { @@ -133,7 +113,6 @@ static struct i2c_driver st_press_driver = { .acpi_match_table = ACPI_PTR(st_press_acpi_match), }, .probe = st_press_i2c_probe, - .remove = st_press_i2c_remove, .id_table = st_press_id_table, }; module_i2c_driver(st_press_driver); diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index ee393df54cee..9b2523c5bc94 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -86,27 +86,7 @@ static int st_press_spi_probe(struct spi_device *spi) if (err) return err; - err = st_press_common_probe(indio_dev); - if (err < 0) - goto st_press_power_off; - - return 0; - -st_press_power_off: - st_sensors_power_disable(indio_dev); - - return err; -} - -static int st_press_spi_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - - st_sensors_power_disable(indio_dev); - - st_press_common_remove(indio_dev); - - return 0; + return st_press_common_probe(indio_dev); } static const struct spi_device_id st_press_id_table[] = { @@ -127,7 +107,6 @@ static struct spi_driver st_press_driver = { .of_match_table = st_press_of_match, }, .probe = st_press_spi_probe, - .remove = st_press_spi_remove, .id_table = st_press_id_table, }; module_spi_driver(st_press_driver); diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig index f20ae3c963cb..e8ed849e3b76 100644 --- a/drivers/iio/temperature/Kconfig +++ b/drivers/iio/temperature/Kconfig @@ -138,4 +138,14 @@ config MAX31856 This driver can also be built as a module. If so, the module will be called max31856. +config MAX31865 + tristate "MAX31865 RTD to Digital converter" + depends on SPI + help + If you say yes here you get support for MAX31865 + thermocouple sensor chip connected via SPI. + + This driver can also be build as a module. If so, the module + will be called max31865. + endmenu diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile index e3392c4b29b4..dd08e562ffe0 100644 --- a/drivers/iio/temperature/Makefile +++ b/drivers/iio/temperature/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_LTC2983) += ltc2983.o obj-$(CONFIG_HID_SENSOR_TEMP) += hid-sensor-temperature.o obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o obj-$(CONFIG_MAX31856) += max31856.o +obj-$(CONFIG_MAX31865) += max31865.o obj-$(CONFIG_MLX90614) += mlx90614.o obj-$(CONFIG_MLX90632) += mlx90632.o obj-$(CONFIG_TMP006) += tmp006.o diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 3b4a0e60e605..301c3f13fb26 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -1275,6 +1275,11 @@ static int ltc2983_parse_dt(struct ltc2983_data *st) &st->filter_notch_freq); st->num_channels = of_get_available_child_count(dev->of_node); + if (!st->num_channels) { + dev_err(&st->spi->dev, "At least one channel must be given!"); + return -EINVAL; + } + st->sensors = devm_kcalloc(dev, st->num_channels, sizeof(*st->sensors), GFP_KERNEL); if (!st->sensors) @@ -1470,6 +1475,7 @@ static int ltc2983_probe(struct spi_device *spi) { struct ltc2983_data *st; struct iio_dev *indio_dev; + struct gpio_desc *gpio; const char *name = spi_get_device_id(spi)->name; int ret; @@ -1494,6 +1500,16 @@ static int ltc2983_probe(struct spi_device *spi) if (ret) return ret; + gpio = devm_gpiod_get_optional(&st->spi->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + if (gpio) { + /* bring the device out of reset */ + usleep_range(1000, 1200); + gpiod_set_value_cansleep(gpio, 0); + } + ret = ltc2983_setup(st, true); if (ret) return ret; diff --git a/drivers/iio/temperature/max31865.c b/drivers/iio/temperature/max31865.c new file mode 100644 index 000000000000..4c8d6e6cf677 --- /dev/null +++ b/drivers/iio/temperature/max31865.c @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (c) Linumiz 2021 + * + * max31865.c - Maxim MAX31865 RTD-to-Digital Converter sensor driver + * + * Author: Navin Sankar Velliangiri <navin@linumiz.com> + */ + +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/spi/spi.h> +#include <asm/unaligned.h> + +/* + * The MSB of the register value determines whether the following byte will + * be written or read. If it is 0, read will follow and if it is 1, write + * will follow. + */ +#define MAX31865_RD_WR_BIT BIT(7) + +#define MAX31865_CFG_VBIAS BIT(7) +#define MAX31865_CFG_1SHOT BIT(5) +#define MAX31865_3WIRE_RTD BIT(4) +#define MAX31865_FAULT_STATUS_CLEAR BIT(1) +#define MAX31865_FILTER_50HZ BIT(0) + +/* The MAX31865 registers */ +#define MAX31865_CFG_REG 0x00 +#define MAX31865_RTD_MSB 0x01 +#define MAX31865_FAULT_STATUS 0x07 + +#define MAX31865_FAULT_OVUV BIT(2) + +static const char max31865_show_samp_freq[] = "50 60"; + +static const struct iio_chan_spec max31865_channels[] = { + { /* RTD Temperature */ + .type = IIO_TEMP, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) + }, +}; + +struct max31865_data { + struct spi_device *spi; + struct mutex lock; + bool filter_50hz; + bool three_wire; + u8 buf[2] ____cacheline_aligned; +}; + +static int max31865_read(struct max31865_data *data, u8 reg, + unsigned int read_size) +{ + return spi_write_then_read(data->spi, ®, 1, data->buf, read_size); +} + +static int max31865_write(struct max31865_data *data, size_t len) +{ + return spi_write(data->spi, data->buf, len); +} + +static int enable_bias(struct max31865_data *data) +{ + u8 cfg; + int ret; + + ret = max31865_read(data, MAX31865_CFG_REG, 1); + if (ret) + return ret; + + cfg = data->buf[0]; + + data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT; + data->buf[1] = cfg | MAX31865_CFG_VBIAS; + + return max31865_write(data, 2); +} + +static int disable_bias(struct max31865_data *data) +{ + u8 cfg; + int ret; + + ret = max31865_read(data, MAX31865_CFG_REG, 1); + if (ret) + return ret; + + cfg = data->buf[0]; + cfg &= ~MAX31865_CFG_VBIAS; + + data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT; + data->buf[1] = cfg; + + return max31865_write(data, 2); +} + +static int max31865_rtd_read(struct max31865_data *data, int *val) +{ + u8 reg; + int ret; + + /* Enable BIAS to start the conversion */ + ret = enable_bias(data); + if (ret) + return ret; + + /* wait 10.5ms before initiating the conversion */ + msleep(11); + + ret = max31865_read(data, MAX31865_CFG_REG, 1); + if (ret) + return ret; + + reg = data->buf[0]; + reg |= MAX31865_CFG_1SHOT | MAX31865_FAULT_STATUS_CLEAR; + data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT; + data->buf[1] = reg; + + ret = max31865_write(data, 2); + if (ret) + return ret; + + if (data->filter_50hz) { + /* 50Hz filter mode requires 62.5ms to complete */ + msleep(63); + } else { + /* 60Hz filter mode requires 52ms to complete */ + msleep(52); + } + + ret = max31865_read(data, MAX31865_RTD_MSB, 2); + if (ret) + return ret; + + *val = get_unaligned_be16(&data->buf) >> 1; + + return disable_bias(data); +} + +static int max31865_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max31865_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->lock); + ret = max31865_rtd_read(data, val); + mutex_unlock(&data->lock); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* Temp. Data resolution is 0.03125 degree centigrade */ + *val = 31; + *val2 = 250000; /* 1000 * 0.03125 */ + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int max31865_init(struct max31865_data *data) +{ + u8 cfg; + int ret; + + ret = max31865_read(data, MAX31865_CFG_REG, 1); + if (ret) + return ret; + + cfg = data->buf[0]; + + if (data->three_wire) + /* 3-wire RTD connection */ + cfg |= MAX31865_3WIRE_RTD; + + if (data->filter_50hz) + /* 50Hz noise rejection filter */ + cfg |= MAX31865_FILTER_50HZ; + + data->buf[0] = MAX31865_CFG_REG | MAX31865_RD_WR_BIT; + data->buf[1] = cfg; + + return max31865_write(data, 2); +} + +static ssize_t show_fault(struct device *dev, u8 faultbit, char *buf) +{ + int ret; + bool fault; + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct max31865_data *data = iio_priv(indio_dev); + + ret = max31865_read(data, MAX31865_FAULT_STATUS, 1); + if (ret) + return ret; + + fault = data->buf[0] & faultbit; + + return sprintf(buf, "%d\n", fault); +} + +static ssize_t show_fault_ovuv(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return show_fault(dev, MAX31865_FAULT_OVUV, buf); +} + +static ssize_t show_filter(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct max31865_data *data = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", data->filter_50hz ? 50 : 60); +} + +static ssize_t set_filter(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct max31865_data *data = iio_priv(indio_dev); + unsigned int freq; + int ret; + + ret = kstrtouint(buf, 10, &freq); + if (ret) + return ret; + + switch (freq) { + case 50: + data->filter_50hz = true; + break; + case 60: + data->filter_50hz = false; + break; + default: + return -EINVAL; + } + + mutex_lock(&data->lock); + ret = max31865_init(data); + mutex_unlock(&data->lock); + if (ret) + return ret; + + return len; +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(max31865_show_samp_freq); +static IIO_DEVICE_ATTR(fault_ovuv, 0444, show_fault_ovuv, NULL, 0); +static IIO_DEVICE_ATTR(in_filter_notch_center_frequency, 0644, + show_filter, set_filter, 0); + +static struct attribute *max31865_attributes[] = { + &iio_dev_attr_fault_ovuv.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_filter_notch_center_frequency.dev_attr.attr, + NULL, +}; + +static const struct attribute_group max31865_group = { + .attrs = max31865_attributes, +}; + +static const struct iio_info max31865_info = { + .read_raw = max31865_read_raw, + .attrs = &max31865_group, +}; + +static int max31865_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct iio_dev *indio_dev; + struct max31865_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->spi = spi; + data->filter_50hz = false; + mutex_init(&data->lock); + + indio_dev->info = &max31865_info; + indio_dev->name = id->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max31865_channels; + indio_dev->num_channels = ARRAY_SIZE(max31865_channels); + + if (of_property_read_bool(spi->dev.of_node, "maxim,3-wire")) { + /* select 3 wire */ + data->three_wire = 1; + } else { + /* select 2 or 4 wire */ + data->three_wire = 0; + } + + ret = max31865_init(data); + if (ret) { + dev_err(&spi->dev, "error: Failed to configure max31865\n"); + return ret; + } + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id max31865_id[] = { + { "max31865", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, max31865_id); + +static const struct of_device_id max31865_of_match[] = { + { .compatible = "maxim,max31865" }, + { } +}; +MODULE_DEVICE_TABLE(of, max31865_of_match); + +static struct spi_driver max31865_driver = { + .driver = { + .name = "max31865", + .of_match_table = max31865_of_match, + }, + .probe = max31865_probe, + .id_table = max31865_id, +}; +module_spi_driver(max31865_driver); + +MODULE_AUTHOR("Navin Sankar Velliangiri <navin@linumiz.com>"); +MODULE_DESCRIPTION("Maxim MAX31865 RTD-to-Digital Converter sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c index 78ac720266e6..71c709771676 100644 --- a/drivers/staging/iio/cdc/ad7746.c +++ b/drivers/staging/iio/cdc/ad7746.c @@ -241,10 +241,8 @@ static int ad7746_select_channel(struct iio_dev *indio_dev, if (ret < 0) return ret; - if (chip->capdac_set != chan->channel) { - + if (chip->capdac_set != chan->channel) chip->capdac_set = chan->channel; - } break; case IIO_VOLTAGE: case IIO_TEMP: diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c index 3f1981e287f5..f43464db618a 100644 --- a/drivers/staging/iio/frequency/ad9832.c +++ b/drivers/staging/iio/frequency/ad9832.c @@ -294,6 +294,16 @@ static const struct iio_info ad9832_info = { .attrs = &ad9832_attribute_group, }; +static void ad9832_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static void ad9832_clk_disable(void *clk) +{ + clk_disable_unprepare(clk); +} + static int ad9832_probe(struct spi_device *spi) { struct ad9832_platform_data *pdata = dev_get_platdata(&spi->dev); @@ -310,7 +320,6 @@ static int ad9832_probe(struct spi_device *spi) if (!indio_dev) return -ENOMEM; - spi_set_drvdata(spi, indio_dev); st = iio_priv(indio_dev); st->avdd = devm_regulator_get(&spi->dev, "avdd"); @@ -323,27 +332,35 @@ static int ad9832_probe(struct spi_device *spi) return ret; } + ret = devm_add_action_or_reset(&spi->dev, ad9832_reg_disable, st->avdd); + if (ret) + return ret; + st->dvdd = devm_regulator_get(&spi->dev, "dvdd"); - if (IS_ERR(st->dvdd)) { - ret = PTR_ERR(st->dvdd); - goto error_disable_avdd; - } + if (IS_ERR(st->dvdd)) + return PTR_ERR(st->dvdd); ret = regulator_enable(st->dvdd); if (ret) { dev_err(&spi->dev, "Failed to enable specified DVDD supply\n"); - goto error_disable_avdd; + return ret; } + ret = devm_add_action_or_reset(&spi->dev, ad9832_reg_disable, st->dvdd); + if (ret) + return ret; + st->mclk = devm_clk_get(&spi->dev, "mclk"); - if (IS_ERR(st->mclk)) { - ret = PTR_ERR(st->mclk); - goto error_disable_dvdd; - } + if (IS_ERR(st->mclk)) + return PTR_ERR(st->mclk); ret = clk_prepare_enable(st->mclk); if (ret < 0) - goto error_disable_dvdd; + return ret; + + ret = devm_add_action_or_reset(&spi->dev, ad9832_clk_disable, st->mclk); + if (ret) + return ret; st->spi = spi; mutex_init(&st->lock); @@ -394,60 +411,34 @@ static int ad9832_probe(struct spi_device *spi) ret = spi_sync(st->spi, &st->msg); if (ret) { dev_err(&spi->dev, "device init failed\n"); - goto error_unprepare_mclk; + return ret; } ret = ad9832_write_frequency(st, AD9832_FREQ0HM, pdata->freq0); if (ret) - goto error_unprepare_mclk; + return ret; ret = ad9832_write_frequency(st, AD9832_FREQ1HM, pdata->freq1); if (ret) - goto error_unprepare_mclk; + return ret; ret = ad9832_write_phase(st, AD9832_PHASE0H, pdata->phase0); if (ret) - goto error_unprepare_mclk; + return ret; ret = ad9832_write_phase(st, AD9832_PHASE1H, pdata->phase1); if (ret) - goto error_unprepare_mclk; + return ret; ret = ad9832_write_phase(st, AD9832_PHASE2H, pdata->phase2); if (ret) - goto error_unprepare_mclk; + return ret; ret = ad9832_write_phase(st, AD9832_PHASE3H, pdata->phase3); if (ret) - goto error_unprepare_mclk; - - ret = iio_device_register(indio_dev); - if (ret) - goto error_unprepare_mclk; - - return 0; - -error_unprepare_mclk: - clk_disable_unprepare(st->mclk); -error_disable_dvdd: - regulator_disable(st->dvdd); -error_disable_avdd: - regulator_disable(st->avdd); - - return ret; -} - -static int ad9832_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad9832_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - clk_disable_unprepare(st->mclk); - regulator_disable(st->dvdd); - regulator_disable(st->avdd); + return ret; - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id ad9832_id[] = { @@ -462,7 +453,6 @@ static struct spi_driver ad9832_driver = { .name = "ad9832", }, .probe = ad9832_probe, - .remove = ad9832_remove, .id_table = ad9832_id, }; module_spi_driver(ad9832_driver); diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index b6928ac5c63d..418b1307d3f2 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -11,8 +11,15 @@ struct iio_buffer; +enum iio_buffer_direction { + IIO_BUFFER_DIRECTION_IN, + IIO_BUFFER_DIRECTION_OUT, +}; + int iio_push_to_buffers(struct iio_dev *indio_dev, const void *data); +int iio_pop_from_buffer(struct iio_buffer *buffer, void *data); + /** * iio_push_to_buffers_with_timestamp() - push data and timestamp to buffers * @indio_dev: iio_dev structure for device. @@ -38,6 +45,10 @@ static inline int iio_push_to_buffers_with_timestamp(struct iio_dev *indio_dev, return iio_push_to_buffers(indio_dev, data); } +int iio_push_to_buffers_with_ts_unaligned(struct iio_dev *indio_dev, + const void *data, size_t data_sz, + int64_t timestamp); + bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev, const unsigned long *mask); diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h index 245b32918ae1..e2ca8ea23e19 100644 --- a/include/linux/iio/buffer_impl.h +++ b/include/linux/iio/buffer_impl.h @@ -7,6 +7,7 @@ #ifdef CONFIG_IIO_BUFFER #include <uapi/linux/iio/buffer.h> +#include <linux/iio/buffer.h> struct iio_dev; struct iio_buffer; @@ -23,6 +24,10 @@ struct iio_buffer; * @read: try to get a specified number of bytes (must exist) * @data_available: indicates how much data is available for reading from * the buffer. + * @remove_from: remove scan from buffer. Drivers should calls this to + * remove a scan from a buffer. + * @write: try to write a number of bytes + * @space_available: returns the amount of bytes available in a buffer * @request_update: if a parameter change has been marked, update underlying * storage. * @set_bytes_per_datum:set number of bytes per datum @@ -49,6 +54,9 @@ struct iio_buffer_access_funcs { int (*store_to)(struct iio_buffer *buffer, const void *data); int (*read)(struct iio_buffer *buffer, size_t n, char __user *buf); size_t (*data_available)(struct iio_buffer *buffer); + int (*remove_from)(struct iio_buffer *buffer, void *data); + int (*write)(struct iio_buffer *buffer, size_t n, const char __user *buf); + size_t (*space_available)(struct iio_buffer *buffer); int (*request_update)(struct iio_buffer *buffer); @@ -80,6 +88,9 @@ struct iio_buffer { /** @bytes_per_datum: Size of individual datum including timestamp. */ size_t bytes_per_datum; + /* @direction: Direction of the data stream (in/out). */ + enum iio_buffer_direction direction; + /** * @access: Buffer access functions associated with the * implementation. diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index 8bdbaf3f3796..22f67845cdd3 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -220,7 +220,6 @@ struct st_sensor_settings { /** * struct st_sensor_data - ST sensor device status - * @dev: Pointer to instance of struct device (I2C or SPI). * @trig: The trigger in use by the core driver. * @mount_matrix: The mounting matrix of the sensor. * @sensor_settings: Pointer to the specific sensor settings in use. @@ -240,7 +239,6 @@ struct st_sensor_settings { * @buffer_data: Data used by buffer part. */ struct st_sensor_data { - struct device *dev; struct iio_trigger *trig; struct iio_mount_matrix mount_matrix; struct st_sensor_settings *sensor_settings; @@ -273,7 +271,6 @@ irqreturn_t st_sensors_trigger_handler(int irq, void *p); int st_sensors_allocate_trigger(struct iio_dev *indio_dev, const struct iio_trigger_ops *trigger_ops); -void st_sensors_deallocate_trigger(struct iio_dev *indio_dev); int st_sensors_validate_device(struct iio_trigger *trig, struct iio_dev *indio_dev); #else @@ -282,10 +279,6 @@ static inline int st_sensors_allocate_trigger(struct iio_dev *indio_dev, { return 0; } -static inline void st_sensors_deallocate_trigger(struct iio_dev *indio_dev) -{ - return; -} #define st_sensors_validate_device NULL #endif @@ -298,8 +291,6 @@ int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable); int st_sensors_power_enable(struct iio_dev *indio_dev); -void st_sensors_power_disable(struct iio_dev *indio_dev); - int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev, unsigned reg, unsigned writeval, unsigned *readval); @@ -330,21 +321,17 @@ void st_sensors_dev_name_probe(struct device *dev, char *name, int len); /* Accelerometer */ const struct st_sensor_settings *st_accel_get_settings(const char *name); int st_accel_common_probe(struct iio_dev *indio_dev); -void st_accel_common_remove(struct iio_dev *indio_dev); /* Gyroscope */ const struct st_sensor_settings *st_gyro_get_settings(const char *name); int st_gyro_common_probe(struct iio_dev *indio_dev); -void st_gyro_common_remove(struct iio_dev *indio_dev); /* Magnetometer */ const struct st_sensor_settings *st_magn_get_settings(const char *name); int st_magn_common_probe(struct iio_dev *indio_dev); -void st_magn_common_remove(struct iio_dev *indio_dev); /* Pressure */ const struct st_sensor_settings *st_press_get_settings(const char *name); int st_press_common_probe(struct iio_dev *indio_dev); -void st_press_common_remove(struct iio_dev *indio_dev); #endif /* ST_SENSORS_H */ diff --git a/include/linux/iio/driver.h b/include/linux/iio/driver.h index 36de60a5da7a..7a157ed218f6 100644 --- a/include/linux/iio/driver.h +++ b/include/linux/iio/driver.h @@ -8,6 +8,7 @@ #ifndef _IIO_INKERN_H_ #define _IIO_INKERN_H_ +struct device; struct iio_dev; struct iio_map; @@ -26,4 +27,17 @@ int iio_map_array_register(struct iio_dev *indio_dev, */ int iio_map_array_unregister(struct iio_dev *indio_dev); +/** + * devm_iio_map_array_register - device-managed version of iio_map_array_register + * @dev: Device object to which to bind the unwinding of this registration + * @indio_dev: Pointer to the iio_dev structure + * @maps: Pointer to an IIO map object which is to be registered to this IIO device + * + * This function will call iio_map_array_register() to register an IIO map object + * and will also hook a callback to the iio_map_array_unregister() function to + * handle de-registration of the IIO map object when the device's refcount goes to + * zero. + */ +int devm_iio_map_array_register(struct device *dev, struct iio_dev *indio_dev, struct iio_map *maps); + #endif diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h index c9504e9da571..2be12b7b5dc5 100644 --- a/include/linux/iio/iio-opaque.h +++ b/include/linux/iio/iio-opaque.h @@ -23,6 +23,8 @@ * @groupcounter: index of next attribute group * @legacy_scan_el_group: attribute group for legacy scan elements attribute group * @legacy_buffer_group: attribute group for legacy buffer attributes group + * @bounce_buffer: for devices that call iio_push_to_buffers_with_timestamp_unaligned() + * @bounce_buffer_size: size of currently allocate bounce buffer * @scan_index_timestamp: cache of the index to the timestamp * @clock_id: timestamping clock posix identifier * @chrdev: associated character device @@ -50,6 +52,8 @@ struct iio_dev_opaque { int groupcounter; struct attribute_group legacy_scan_el_group; struct attribute_group legacy_buffer_group; + void *bounce_buffer; + size_t bounce_buffer_size; unsigned int scan_index_timestamp; clockid_t clock_id; diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index cf49997d5903..7c02f5292eea 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -49,6 +49,7 @@ struct adis_timeout { * @status_error_mask: Bitmask of errors supported by the device * @timeouts: Chip specific delays * @enable_irq: Hook for ADIS devices that have a special IRQ enable/disable + * @unmasked_drdy: True for devices that cannot mask/unmask the data ready pin * @has_paging: True if ADIS device has paged registers * @burst_reg_cmd: Register command that triggers burst * @burst_len: Burst size in the SPI RX buffer. If @burst_max_len is defined, @@ -78,6 +79,7 @@ struct adis_data { unsigned int status_error_mask; int (*enable_irq)(struct adis *adis, bool enable); + bool unmasked_drdy; bool has_paging; diff --git a/include/linux/iio/triggered_buffer.h b/include/linux/iio/triggered_buffer.h index 7f154d1f8739..7490b05fc5b2 100644 --- a/include/linux/iio/triggered_buffer.h +++ b/include/linux/iio/triggered_buffer.h @@ -2,6 +2,7 @@ #ifndef _LINUX_IIO_TRIGGERED_BUFFER_H_ #define _LINUX_IIO_TRIGGERED_BUFFER_H_ +#include <linux/iio/buffer.h> #include <linux/interrupt.h> struct attribute; @@ -11,21 +12,27 @@ struct iio_buffer_setup_ops; int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev, irqreturn_t (*h)(int irq, void *p), irqreturn_t (*thread)(int irq, void *p), + enum iio_buffer_direction direction, const struct iio_buffer_setup_ops *setup_ops, const struct attribute **buffer_attrs); void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev); #define iio_triggered_buffer_setup(indio_dev, h, thread, setup_ops) \ - iio_triggered_buffer_setup_ext((indio_dev), (h), (thread), (setup_ops), NULL) + iio_triggered_buffer_setup_ext((indio_dev), (h), (thread), \ + IIO_BUFFER_DIRECTION_IN, (setup_ops), \ + NULL) int devm_iio_triggered_buffer_setup_ext(struct device *dev, struct iio_dev *indio_dev, irqreturn_t (*h)(int irq, void *p), irqreturn_t (*thread)(int irq, void *p), + enum iio_buffer_direction direction, const struct iio_buffer_setup_ops *ops, const struct attribute **buffer_attrs); #define devm_iio_triggered_buffer_setup(dev, indio_dev, h, thread, setup_ops) \ - devm_iio_triggered_buffer_setup_ext((dev), (indio_dev), (h), (thread), (setup_ops), NULL) + devm_iio_triggered_buffer_setup_ext((dev), (indio_dev), (h), (thread), \ + IIO_BUFFER_DIRECTION_IN, \ + (setup_ops), NULL) #endif |