IIO: 1st set of new device support, features and cleanup for 6.10
The Analog Device team (Paul Cercueil and Nuno Sa) have been working on improving high speed device handling. They have had some support in their own tree for many years, so it is great to see them bring it to upstream. Some of that is seen here, with the first output device using the IIO dmaengine infrastructure and a new DAC backend FPGA IP driver. This makes use of a new set of interfaces to allow backend and front end driver communication in a fashion that in theory at least allows for a single driver for a given ADC / DAC independent of the IP to which is being used to deal with the data bus and DMA aspects of working with these devices. It is early days for this new generic way of handling split devices, but as it's kernel internals only we can merrily change anything about it as a wider diversity of devices show up and we get a better feel for what works. Alongside the usual set of new drivers and features we have the automatic cleanup of fwnode_handle_put() which didn't quite make it in last cycle. The equivalent DT version was merged by Rob Herring via the DT binding tree and one patch using that in IIO can also be found in this pull request. Rob has been making extensive use of that infrastructure in the DT core which is good to see and provides more evidence this basic approach is useful. In some cases, the IIO driver was converted over from DT only to using the generic firmware description handling of property.h including using the new macros. The general preference for IIO is to use this more generic handling where possible - a bunch of other drivers have been converted this cycle as well. New device support ================== adi,ad7173 - New driver supporting AD7172-2, AD7172-4 AD7173-9, AD7175-2, AD7175-8, AD7176-2 and AD7177-2 ADCs. - Follow up fix for an accidental use of logic not instead of bitwise. adi,ad7944 - New driver supporting AD7944, AD7985 and AD7986 pin compatible ADCs. - Later patch added use of new spi_optimize_message() to reduce overheads of setting up a reused message. - Additional changes later in series reduced code duplication. adi,ad9739a RF DAC - New driver for this 14-bit 2.5 GSPS DAC via an LVDS interface. adi,axi-dac - Support for this FPGA IP used to send data to high performance DACs over an interface such as JESD204B/C or parallel interfaces. Used in conjunction with a DAC driver. The initial user is the ad9739a. The dmaengine-buffer needed various changes to make it bidirectional. avago,apds9306 - New driver for this ambient light sensor. - Fix much later in this pull for an off by 1 error. New device IDs ============== For these at most an ID and a instance of chip specific data was needed. Always nice to see manufacturers sticking to an existing software interface for new parts. allwinner,sun20i - Add support for h616. invensense,mpu6050 - Add support for ICM42688 maxim,max30102 - Add compatible for MAX30101 ti,dac5571 - Add compatible for DAC081C081 General ======= fwnode_handle - Support for cleanup.h based __free(fwnode_handle) - Loop macro using this for looping over child nodes without needing to call fwnode_handle_put() in ever early exit from the loop. - Used in: * adi,ad3552r * adi,ad4130 * adi,ad5770r * adi,ad74413r * adi,ad7173 * adi,adfm2000 * linear,ltc2688 * linear,ltc2983 * maxim,max11410 * microchip,pac1934 * qcom,spmi-adc * renesas,rz2gl * st,ab8500 * st,stm32 (Fix for failure to set return value precedes this patch, providing an example of why enabling direct returns makes bugs less likely) - Conversions to fwnode also using the cleanup logic * adi,ad7124 * adi,ad7292 * freescale,fsl-imx25-gcq - Other conversions to fwnode where the new cleanup handling isn't useful * adi,ad7192 * avia,hx711 * freescale,mma8452 * nxp,fxls8962af * st,spear * ti,twl4030 Features ======== adi,adxl345 - Support SPI_3WIRE mode. adi,ad9944 - Support 3-wire mode, note this isn't normal 3-wire SPI (unlike the adxl345 change above), but rather a wiring scheme where the SPI chip select is used to trigger conversions rather than using a separate pin. - Add some device specific documentation, mostly around the various wiring schemes. invensense,mpu6050 - Add Wake on Motion support as an IIO event and as a wake-up source. linear,ltc2983 - Add vdd-supply. ti,hdc3020 - Add power management using trigger on demand mode and adding suspend and resume handling. - Use reset GPIO if available. Cleanup and fixes ================ iio core - Use the various autocleanup and lock guards from cleanup.h to simplify the IIO core. - Don't set the pointer used for iio_priv() if it is zero sized as that points beyond the end of the allocation. No driver actually uses it in that case but good to clean this up. various drivers - Drop unnecessary casts of other pointer types to void * docs - Add missing ABI entry for in_temp_input. adi,adx345 - General cleanup prior to adding spi-3wire mode. adi,axi-adc - Be more flexible and allow minor version changes as these are expected to be backwards compatible. avago,apds9300/9600 - Merge near identical bindings. The drivers are quite different, but the bindings can be shared. The apds9306 binding introduced in this series uses this shared binding doc as well. - Add missing vdd-supply - Update binding to use IRQ_TYPE_LEVEL_LOW instead of 8. bosch,bmp280 - Organize headers freescale,fxl-imx25-gcq - Use devm_ for remaining probe() time setup allowing dropping of specific error handling and remove() functions. infineon,dps310 - Fix handling of negative temperatures - Bring style of other similar calls inline with the form needed for temperatures - Ensure error handling of regmap calls is consistent within the driver. - Simplify scale reading logic. invensense,mpu6050 - Flip logic in binding to exclude devices without i2c-gate instead of opting in. The list is expected to be much shorter as all recent devices support this feature. honeywell,hsc030pa - Use spi_read() instead of opening coding. renesas,rcar - Use device_for_each_child_of_node_scoped() to remove need to manually release. Left over from series the rest of which went in during 6.9. st,ab8500 - Fix naming of function parameters in kernel-doc -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAmYmqVoRHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0FogSaw/6A+8IHq9ox5wo9g2UUajFVXi6ToSs3DTv Dwi5OsPmuoimj8DDyxz6xuq/F6zbo+tbkVpbz7eu4L8/QNk07wgwyQrp7Z5EhVgx dAHxWOCrhKbkGzvcfyy+Epzvc1ETplfIZ6KdxgcB+k092jtQDknp9Os7NvAWYQp4 7zFM1kptUe/Bnwe8oaTOfjtvl3kPTH0++yANE30r76PCD0Ra5v2MKHRNhtx3q0Et D02MDQWjvjT3+HbfT7ft8CtMAFqlj5t8KneS9KUlXsifupLnQrZIx3rjL7ooACIb aJVb0Vj3nQMlVlijH38jbxPI6z5OX230ySfX1mQXEzAkBouaWM6k9xcSzMC/ed/E OeiG9b/a0blGkq15bVA/LHh/uQ8MjBYa0SfdbAsvy/I6O73UaEHtzDR4Wua2grCr OzLVKvl9032+b9XUge500gH2Veb9OEnN0viRm6ApD+Ew9yRCA2og0OypDAVqvULM T/+lTu7MeYAe1K3wGu1PekeC3LK0lX3nGEX0vBybBh6gFUMOHaveoQ0qjy0DLvHn 3Ua1ZlqZbT3pYe+2oHmV+ErRb7a7Fz5F2SkoqtHjtL6eF8/zHlywNb/l0Yp9FDgB W+yd+VrJC/XZOOFv6mcWOvTCK1VtY65cQRPdGbR2KHmIdtUpI0A4NGZ4rdrtD9iS TVtGiyb3hos= =dr8B -----END PGP SIGNATURE----- Merge tag 'iio-for-6.10a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into char-misc-next Jonathan writes: IIO: 1st set of new device support, features and cleanup for 6.10 The Analog Device team (Paul Cercueil and Nuno Sa) have been working on improving high speed device handling. They have had some support in their own tree for many years, so it is great to see them bring it to upstream. Some of that is seen here, with the first output device using the IIO dmaengine infrastructure and a new DAC backend FPGA IP driver. This makes use of a new set of interfaces to allow backend and front end driver communication in a fashion that in theory at least allows for a single driver for a given ADC / DAC independent of the IP to which is being used to deal with the data bus and DMA aspects of working with these devices. It is early days for this new generic way of handling split devices, but as it's kernel internals only we can merrily change anything about it as a wider diversity of devices show up and we get a better feel for what works. Alongside the usual set of new drivers and features we have the automatic cleanup of fwnode_handle_put() which didn't quite make it in last cycle. The equivalent DT version was merged by Rob Herring via the DT binding tree and one patch using that in IIO can also be found in this pull request. Rob has been making extensive use of that infrastructure in the DT core which is good to see and provides more evidence this basic approach is useful. In some cases, the IIO driver was converted over from DT only to using the generic firmware description handling of property.h including using the new macros. The general preference for IIO is to use this more generic handling where possible - a bunch of other drivers have been converted this cycle as well. New device support ================== adi,ad7173 - New driver supporting AD7172-2, AD7172-4 AD7173-9, AD7175-2, AD7175-8, AD7176-2 and AD7177-2 ADCs. - Follow up fix for an accidental use of logic not instead of bitwise. adi,ad7944 - New driver supporting AD7944, AD7985 and AD7986 pin compatible ADCs. - Later patch added use of new spi_optimize_message() to reduce overheads of setting up a reused message. - Additional changes later in series reduced code duplication. adi,ad9739a RF DAC - New driver for this 14-bit 2.5 GSPS DAC via an LVDS interface. adi,axi-dac - Support for this FPGA IP used to send data to high performance DACs over an interface such as JESD204B/C or parallel interfaces. Used in conjunction with a DAC driver. The initial user is the ad9739a. The dmaengine-buffer needed various changes to make it bidirectional. avago,apds9306 - New driver for this ambient light sensor. - Fix much later in this pull for an off by 1 error. New device IDs ============== For these at most an ID and a instance of chip specific data was needed. Always nice to see manufacturers sticking to an existing software interface for new parts. allwinner,sun20i - Add support for h616. invensense,mpu6050 - Add support for ICM42688 maxim,max30102 - Add compatible for MAX30101 ti,dac5571 - Add compatible for DAC081C081 General ======= fwnode_handle - Support for cleanup.h based __free(fwnode_handle) - Loop macro using this for looping over child nodes without needing to call fwnode_handle_put() in ever early exit from the loop. - Used in: * adi,ad3552r * adi,ad4130 * adi,ad5770r * adi,ad74413r * adi,ad7173 * adi,adfm2000 * linear,ltc2688 * linear,ltc2983 * maxim,max11410 * microchip,pac1934 * qcom,spmi-adc * renesas,rz2gl * st,ab8500 * st,stm32 (Fix for failure to set return value precedes this patch, providing an example of why enabling direct returns makes bugs less likely) - Conversions to fwnode also using the cleanup logic * adi,ad7124 * adi,ad7292 * freescale,fsl-imx25-gcq - Other conversions to fwnode where the new cleanup handling isn't useful * adi,ad7192 * avia,hx711 * freescale,mma8452 * nxp,fxls8962af * st,spear * ti,twl4030 Features ======== adi,adxl345 - Support SPI_3WIRE mode. adi,ad9944 - Support 3-wire mode, note this isn't normal 3-wire SPI (unlike the adxl345 change above), but rather a wiring scheme where the SPI chip select is used to trigger conversions rather than using a separate pin. - Add some device specific documentation, mostly around the various wiring schemes. invensense,mpu6050 - Add Wake on Motion support as an IIO event and as a wake-up source. linear,ltc2983 - Add vdd-supply. ti,hdc3020 - Add power management using trigger on demand mode and adding suspend and resume handling. - Use reset GPIO if available. Cleanup and fixes ================ iio core - Use the various autocleanup and lock guards from cleanup.h to simplify the IIO core. - Don't set the pointer used for iio_priv() if it is zero sized as that points beyond the end of the allocation. No driver actually uses it in that case but good to clean this up. various drivers - Drop unnecessary casts of other pointer types to void * docs - Add missing ABI entry for in_temp_input. adi,adx345 - General cleanup prior to adding spi-3wire mode. adi,axi-adc - Be more flexible and allow minor version changes as these are expected to be backwards compatible. avago,apds9300/9600 - Merge near identical bindings. The drivers are quite different, but the bindings can be shared. The apds9306 binding introduced in this series uses this shared binding doc as well. - Add missing vdd-supply - Update binding to use IRQ_TYPE_LEVEL_LOW instead of 8. bosch,bmp280 - Organize headers freescale,fxl-imx25-gcq - Use devm_ for remaining probe() time setup allowing dropping of specific error handling and remove() functions. infineon,dps310 - Fix handling of negative temperatures - Bring style of other similar calls inline with the form needed for temperatures - Ensure error handling of regmap calls is consistent within the driver. - Simplify scale reading logic. invensense,mpu6050 - Flip logic in binding to exclude devices without i2c-gate instead of opting in. The list is expected to be much shorter as all recent devices support this feature. honeywell,hsc030pa - Use spi_read() instead of opening coding. renesas,rcar - Use device_for_each_child_of_node_scoped() to remove need to manually release. Left over from series the rest of which went in during 6.9. st,ab8500 - Fix naming of function parameters in kernel-doc * tag 'iio-for-6.10a' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (108 commits) iio: adc: ti-ads131e08: Use device_for_each_child_node_scoped() to simplify error paths. iio: adc: adi-axi-adc: only error out in major version mismatch iio: dac: support the ad9739a RF DAC iio: dac: add support for AXI DAC IP core iio: backend: add new functionality dt-bindings: iio: dac: add docs for AD9739A dt-bindings: iio: dac: add docs for AXI DAC IP iio: buffer-dmaengine: Enable write support iio: buffer-dmaengine: Support specifying buffer direction iio: buffer-dma: Enable buffer write support iio: buffer-dma: Rename iio_dma_buffer_data_available() iio: buffer-dma: add iio_dmaengine_buffer_setup() iio: pressure: dps310: simplify scale factor reading iio: pressure: dps310: consistently check return value of `regmap_read` iio: pressure: dps310: introduce consistent error handling iio: pressure: dps310: support negative temperature values dt-bindings: iio: adc: Add GPADC for Allwinner H616 iio: dac: ad5755: make use of of_device_id table iio: imu: inv_icm42600: add support of ICM-42688-P dt-bindings: iio: imu: add icm42688 inside inv_icm42600 ...
This commit is contained in:
commit
7732ce06ed
@ -243,7 +243,8 @@ Description:
|
||||
less measurements. Units after application of scale and offset
|
||||
are milli degrees Celsius.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_tempX_input
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_tempY_input
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_temp_input
|
||||
KernelVersion: 2.6.38
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
|
19
Documentation/ABI/testing/sysfs-bus-iio-ad9739a
Normal file
19
Documentation/ABI/testing/sysfs-bus-iio-ad9739a
Normal file
@ -0,0 +1,19 @@
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode
|
||||
KernelVersion: 6.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
DAC operating mode. One of the following modes can be selected:
|
||||
|
||||
* normal: This is DAC normal mode.
|
||||
* mixed-mode: In this mode the output is effectively chopped at
|
||||
the DAC sample rate. This has the effect of
|
||||
reducing the power of the fundamental signal while
|
||||
increasing the power of the images centered around
|
||||
the DAC sample rate, thus improving the output
|
||||
power of these images.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode_available
|
||||
KernelVersion: 6.9
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Available operating modes.
|
@ -32,6 +32,8 @@ properties:
|
||||
|
||||
spi-cpol: true
|
||||
|
||||
spi-3wire: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
|
279
Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml
Normal file
279
Documentation/devicetree/bindings/iio/adc/adi,ad7173.yaml
Normal file
@ -0,0 +1,279 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2023 Analog Devices Inc.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/adi,ad7173.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD7173 ADC
|
||||
|
||||
maintainers:
|
||||
- Ceclan Dumitru <dumitru.ceclan@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices AD717x ADC's:
|
||||
The AD717x family offer a complete integrated Sigma-Delta ADC solution which
|
||||
can be used in high precision, low noise single channel applications
|
||||
(Life Science measurements) or higher speed multiplexed applications
|
||||
(Factory Automation PLC Input modules). The Sigma-Delta ADC is intended
|
||||
primarily for measurement of signals close to DC but also delivers
|
||||
outstanding performance with input bandwidths out to ~10kHz.
|
||||
|
||||
Datasheets for supported chips:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7172-2.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7172-4.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7173-8.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7175-2.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7175-8.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7176-2.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7177-2.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7172-2
|
||||
- adi,ad7172-4
|
||||
- adi,ad7173-8
|
||||
- adi,ad7175-2
|
||||
- adi,ad7175-8
|
||||
- adi,ad7176-2
|
||||
- adi,ad7177-2
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: |
|
||||
Ready: multiplexed with SPI data out. While SPI CS is low,
|
||||
can be used to indicate the completion of a conversion.
|
||||
|
||||
- description: |
|
||||
Error: The three error bits in the status register (ADC_ERROR, CRC_ERROR,
|
||||
and REG_ERROR) are OR'ed, inverted, and mapped to the ERROR pin.
|
||||
Therefore, the ERROR pin indicates that an error has occurred.
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: rdy
|
||||
- const: err
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 20000000
|
||||
|
||||
gpio-controller:
|
||||
description: Marks the device node as a GPIO controller.
|
||||
|
||||
'#gpio-cells':
|
||||
const: 2
|
||||
description:
|
||||
The first cell is the GPIO number and the second cell specifies
|
||||
GPIO flags, as defined in <dt-bindings/gpio/gpio.h>.
|
||||
|
||||
vref-supply:
|
||||
description: |
|
||||
Differential external reference supply used for conversion. The reference
|
||||
voltage (Vref) specified here must be the voltage difference between the
|
||||
REF+ and REF- pins: Vref = (REF+) - (REF-).
|
||||
|
||||
vref2-supply:
|
||||
description: |
|
||||
Differential external reference supply used for conversion. The reference
|
||||
voltage (Vref2) specified here must be the voltage difference between the
|
||||
REF2+ and REF2- pins: Vref2 = (REF2+) - (REF2-).
|
||||
|
||||
avdd-supply:
|
||||
description: Avdd supply, can be used as reference for conversion.
|
||||
This supply is referenced to AVSS, voltage specified here
|
||||
represents (AVDD1 - AVSS).
|
||||
|
||||
avdd2-supply:
|
||||
description: Avdd2 supply, used as the input to the internal voltage regulator.
|
||||
This supply is referenced to AVSS, voltage specified here
|
||||
represents (AVDD2 - AVSS).
|
||||
|
||||
iovdd-supply:
|
||||
description: iovdd supply, used for the chip digital interface.
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description: |
|
||||
Optional external clock source. Can include one clock source: external
|
||||
clock or external crystal.
|
||||
|
||||
clock-names:
|
||||
enum:
|
||||
- ext-clk
|
||||
- xtal
|
||||
|
||||
'#clock-cells':
|
||||
const: 0
|
||||
|
||||
patternProperties:
|
||||
"^channel@[0-9a-f]$":
|
||||
type: object
|
||||
$ref: adc.yaml
|
||||
unevaluatedProperties: false
|
||||
|
||||
properties:
|
||||
reg:
|
||||
minimum: 0
|
||||
maximum: 15
|
||||
|
||||
diff-channels:
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 31
|
||||
|
||||
adi,reference-select:
|
||||
description: |
|
||||
Select the reference source to use when converting on
|
||||
the specific channel. Valid values are:
|
||||
vref : REF+ /REF−
|
||||
vref2 : REF2+ /REF2−
|
||||
refout-avss: REFOUT/AVSS (Internal reference)
|
||||
avdd : AVDD /AVSS
|
||||
|
||||
External reference ref2 only available on ad7173-8 and ad7172-4.
|
||||
Internal reference refout-avss not available on ad7172-4.
|
||||
|
||||
If not specified, internal reference used (if available).
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum:
|
||||
- vref
|
||||
- vref2
|
||||
- refout-avss
|
||||
- avdd
|
||||
default: refout-avss
|
||||
|
||||
required:
|
||||
- reg
|
||||
- diff-channels
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
# Only ad7172-4, ad7173-8 and ad7175-8 support vref2
|
||||
# Other models have [0-3] channel registers
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
not:
|
||||
contains:
|
||||
enum:
|
||||
- adi,ad7172-4
|
||||
- adi,ad7173-8
|
||||
- adi,ad7175-8
|
||||
then:
|
||||
properties:
|
||||
vref2-supply: false
|
||||
patternProperties:
|
||||
"^channel@[0-9a-f]$":
|
||||
properties:
|
||||
adi,reference-select:
|
||||
enum:
|
||||
- vref
|
||||
- refout-avss
|
||||
- avdd
|
||||
reg:
|
||||
maximum: 3
|
||||
|
||||
# Model ad7172-4 does not support internal reference
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: adi,ad7172-4
|
||||
then:
|
||||
patternProperties:
|
||||
"^channel@[0-9a-f]$":
|
||||
properties:
|
||||
reg:
|
||||
maximum: 7
|
||||
adi,reference-select:
|
||||
enum:
|
||||
- vref
|
||||
- vref2
|
||||
- avdd
|
||||
required:
|
||||
- adi,reference-select
|
||||
|
||||
- if:
|
||||
anyOf:
|
||||
- required: [clock-names]
|
||||
- required: [clocks]
|
||||
then:
|
||||
properties:
|
||||
'#clock-cells': false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@0 {
|
||||
compatible = "adi,ad7173-8";
|
||||
reg = <0>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
|
||||
interrupt-names = "rdy";
|
||||
interrupt-parent = <&gpio>;
|
||||
spi-max-frequency = <5000000>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
#clock-cells = <0>;
|
||||
|
||||
vref-supply = <&dummy_regulator>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0>;
|
||||
bipolar;
|
||||
diff-channels = <0 1>;
|
||||
adi,reference-select = "vref";
|
||||
};
|
||||
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
diff-channels = <2 3>;
|
||||
};
|
||||
|
||||
channel@2 {
|
||||
reg = <2>;
|
||||
bipolar;
|
||||
diff-channels = <4 5>;
|
||||
};
|
||||
|
||||
channel@3 {
|
||||
reg = <3>;
|
||||
bipolar;
|
||||
diff-channels = <6 7>;
|
||||
};
|
||||
|
||||
channel@4 {
|
||||
reg = <4>;
|
||||
diff-channels = <8 9>;
|
||||
adi,reference-select = "avdd";
|
||||
};
|
||||
};
|
||||
};
|
213
Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml
Normal file
213
Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml
Normal file
@ -0,0 +1,213 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/adi,ad7944.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices PulSAR LFCSP Analog to Digital Converters
|
||||
|
||||
maintainers:
|
||||
- Michael Hennerich <Michael.Hennerich@analog.com>
|
||||
- Nuno Sá <nuno.sa@analog.com>
|
||||
|
||||
description: |
|
||||
A family of pin-compatible single channel differential analog to digital
|
||||
converters with SPI support in a LFCSP package.
|
||||
|
||||
* https://www.analog.com/en/products/ad7944.html
|
||||
* https://www.analog.com/en/products/ad7985.html
|
||||
* https://www.analog.com/en/products/ad7986.html
|
||||
|
||||
$ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7944
|
||||
- adi,ad7985
|
||||
- adi,ad7986
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency:
|
||||
maximum: 111111111
|
||||
|
||||
spi-cpol: true
|
||||
spi-cpha: true
|
||||
|
||||
adi,spi-mode:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [ single, chain ]
|
||||
description: |
|
||||
This property indicates the SPI wiring configuration.
|
||||
|
||||
When this property is omitted, it is assumed that the device is using what
|
||||
the datasheet calls "4-wire mode". This is the conventional SPI mode used
|
||||
when there are multiple devices on the same bus. In this mode, the CNV
|
||||
line is used to initiate the conversion and the SDI line is connected to
|
||||
CS on the SPI controller.
|
||||
|
||||
When this property is present, it indicates that the device is using one
|
||||
of the following alternative wiring configurations:
|
||||
|
||||
* single: The datasheet calls this "3-wire mode". (NOTE: The datasheet's
|
||||
definition of 3-wire mode is NOT at all related to the standard
|
||||
spi-3wire property!) This mode is often used when the ADC is the only
|
||||
device on the bus. In this mode, SDI is tied to VIO, and the CNV line
|
||||
can be connected to the CS line of the SPI controller or to a GPIO, in
|
||||
which case the CS line of the controller is unused.
|
||||
* chain: The datasheet calls this "chain mode". This mode is used to save
|
||||
on wiring when multiple ADCs are used. In this mode, the SDI line of
|
||||
one chip is tied to the SDO of the next chip in the chain and the SDI of
|
||||
the last chip in the chain is tied to GND. Only the first chip in the
|
||||
chain is connected to the SPI bus. The CNV line of all chips are tied
|
||||
together. The CS line of the SPI controller can be used as the CNV line
|
||||
only if it is active high.
|
||||
|
||||
'#daisy-chained-devices': true
|
||||
|
||||
avdd-supply:
|
||||
description: A 2.5V supply that powers the analog circuitry.
|
||||
|
||||
dvdd-supply:
|
||||
description: A 2.5V supply that powers the digital circuitry.
|
||||
|
||||
vio-supply:
|
||||
description:
|
||||
A 1.8V to 2.7V supply for the digital inputs and outputs.
|
||||
|
||||
bvdd-supply:
|
||||
description:
|
||||
A voltage supply for the buffered power. When using an external reference
|
||||
without an internal buffer (PDREF high, REFIN low), this should be
|
||||
connected to the same supply as ref-supply. Otherwise, when using an
|
||||
internal reference or an external reference with an internal buffer, this
|
||||
is connected to a 5V supply.
|
||||
|
||||
ref-supply:
|
||||
description:
|
||||
Voltage regulator for the external reference voltage (REF). This property
|
||||
is omitted when using an internal reference.
|
||||
|
||||
refin-supply:
|
||||
description:
|
||||
Voltage regulator for the reference buffer input (REFIN). When using an
|
||||
external buffer with internal reference, this should be connected to a
|
||||
1.2V external reference voltage supply. Otherwise, this property is
|
||||
omitted.
|
||||
|
||||
cnv-gpios:
|
||||
description:
|
||||
The Convert Input (CNV). This input has multiple functions. It initiates
|
||||
the conversions and selects the SPI mode of the device (chain or CS). In
|
||||
'single' mode, this property is omitted if the CNV pin is connected to the
|
||||
CS line of the SPI controller.
|
||||
maxItems: 1
|
||||
|
||||
turbo-gpios:
|
||||
description:
|
||||
GPIO connected to the TURBO line. If omitted, it is assumed that the TURBO
|
||||
line is hard-wired and the state is determined by the adi,always-turbo
|
||||
property.
|
||||
maxItems: 1
|
||||
|
||||
adi,always-turbo:
|
||||
type: boolean
|
||||
description:
|
||||
When present, this property indicates that the TURBO line is hard-wired
|
||||
and the state is always high. If neither this property nor turbo-gpios is
|
||||
present, the TURBO line is assumed to be hard-wired and the state is
|
||||
always low.
|
||||
|
||||
interrupts:
|
||||
description:
|
||||
The SDO pin can also function as a busy indicator. This node should be
|
||||
connected to an interrupt that is triggered when the SDO line goes low
|
||||
while the SDI line is high and the CNV line is low ('single' mode) or the
|
||||
SDI line is low and the CNV line is high ('multi' mode); or when the SDO
|
||||
line goes high while the SDI and CNV lines are high (chain mode),
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- avdd-supply
|
||||
- dvdd-supply
|
||||
- vio-supply
|
||||
- bvdd-supply
|
||||
|
||||
allOf:
|
||||
# ref-supply and refin-supply are mutually exclusive (neither is also valid)
|
||||
- if:
|
||||
required:
|
||||
- ref-supply
|
||||
then:
|
||||
properties:
|
||||
refin-supply: false
|
||||
- if:
|
||||
required:
|
||||
- refin-supply
|
||||
then:
|
||||
properties:
|
||||
ref-supply: false
|
||||
# in '4-wire' mode, cnv-gpios is required, for other modes it is optional
|
||||
- if:
|
||||
not:
|
||||
required:
|
||||
- adi,spi-mode
|
||||
then:
|
||||
required:
|
||||
- cnv-gpios
|
||||
# chain mode has lower SCLK max rate and doesn't work when TURBO is enabled
|
||||
- if:
|
||||
required:
|
||||
- adi,spi-mode
|
||||
properties:
|
||||
adi,spi-mode:
|
||||
const: chain
|
||||
then:
|
||||
properties:
|
||||
spi-max-frequency:
|
||||
maximum: 90909090
|
||||
adi,always-turbo: false
|
||||
required:
|
||||
- '#daisy-chained-devices'
|
||||
else:
|
||||
properties:
|
||||
'#daisy-chained-devices': false
|
||||
# turbo-gpios and adi,always-turbo are mutually exclusive
|
||||
- if:
|
||||
required:
|
||||
- turbo-gpios
|
||||
then:
|
||||
properties:
|
||||
adi,always-turbo: false
|
||||
- if:
|
||||
required:
|
||||
- adi,always-turbo
|
||||
then:
|
||||
properties:
|
||||
turbo-gpios: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
adc@0 {
|
||||
compatible = "adi,ad7944";
|
||||
reg = <0>;
|
||||
spi-cpha;
|
||||
spi-max-frequency = <111111111>;
|
||||
avdd-supply = <&supply_2_5V>;
|
||||
dvdd-supply = <&supply_2_5V>;
|
||||
vio-supply = <&supply_1_8V>;
|
||||
bvdd-supply = <&supply_5V>;
|
||||
cnv-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
|
||||
turbo-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
@ -11,8 +11,13 @@ maintainers:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- allwinner,sun20i-d1-gpadc
|
||||
oneOf:
|
||||
- enum:
|
||||
- allwinner,sun20i-d1-gpadc
|
||||
- items:
|
||||
- enum:
|
||||
- allwinner,sun50i-h616-gpadc
|
||||
- const: allwinner,sun20i-d1-gpadc
|
||||
|
||||
"#io-channel-cells":
|
||||
const: 1
|
||||
|
95
Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
Normal file
95
Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
Normal file
@ -0,0 +1,95 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/dac/adi,ad9739a.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD9739A RF DAC
|
||||
|
||||
maintainers:
|
||||
- Dragos Bogdan <dragos.bogdan@analog.com>
|
||||
- Nuno Sa <nuno.sa@analog.com>
|
||||
|
||||
description: |
|
||||
The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
|
||||
of synthesizing wideband signals from dc up to 3 GHz.
|
||||
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ad9737a_9739a.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad9739a
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-3p3-supply:
|
||||
description: 3.3V Digital input supply.
|
||||
|
||||
vdd-supply:
|
||||
description: 1.8V Digital input supply.
|
||||
|
||||
vdda-supply:
|
||||
description: 3.3V Analog input supply.
|
||||
|
||||
vddc-supply:
|
||||
description: 1.8V Clock input supply.
|
||||
|
||||
vref-supply:
|
||||
description: Input/Output reference supply.
|
||||
|
||||
io-backends:
|
||||
maxItems: 1
|
||||
|
||||
adi,full-scale-microamp:
|
||||
description: This property represents the DAC full scale current.
|
||||
minimum: 8580
|
||||
maximum: 31700
|
||||
default: 20000
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- io-backends
|
||||
- vdd-3p3-supply
|
||||
- vdd-supply
|
||||
- vdda-supply
|
||||
- vddc-supply
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
dac@0 {
|
||||
compatible = "adi,ad9739a";
|
||||
reg = <0>;
|
||||
|
||||
clocks = <&dac_clk>;
|
||||
|
||||
io-backends = <&iio_backend>;
|
||||
|
||||
vdd-3p3-supply = <&vdd_3_3>;
|
||||
vdd-supply = <&vdd>;
|
||||
vdda-supply = <&vdd_3_3>;
|
||||
vddc-supply = <&vdd>;
|
||||
};
|
||||
};
|
||||
...
|
62
Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
Normal file
62
Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
Normal file
@ -0,0 +1,62 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/dac/adi,axi-dac.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AXI DAC IP core
|
||||
|
||||
maintainers:
|
||||
- Nuno Sa <nuno.sa@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices Generic AXI DAC IP core for interfacing a DAC device
|
||||
with a high speed serial (JESD204B/C) or source synchronous parallel
|
||||
interface (LVDS/CMOS).
|
||||
Usually, some other interface type (i.e SPI) is used as a control
|
||||
interface for the actual DAC, while this IP core will interface
|
||||
to the data-lines of the DAC and handle the streaming of data from
|
||||
memory via DMA into the DAC.
|
||||
|
||||
https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,axi-dac-9.1.b
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
maxItems: 1
|
||||
|
||||
dma-names:
|
||||
items:
|
||||
- const: tx
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
'#io-backend-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- dmas
|
||||
- reg
|
||||
- clocks
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
dac@44a00000 {
|
||||
compatible = "adi,axi-dac-9.1.b";
|
||||
reg = <0x44a00000 0x10000>;
|
||||
dmas = <&tx_dma 0>;
|
||||
dma-names = "tx";
|
||||
#io-backend-cells = <0>;
|
||||
clocks = <&axi_clk>;
|
||||
};
|
||||
...
|
@ -21,6 +21,7 @@ properties:
|
||||
- ti,dac5573
|
||||
- ti,dac6573
|
||||
- ti,dac7573
|
||||
- ti,dac081c081
|
||||
- ti,dac121c081
|
||||
|
||||
reg:
|
||||
|
@ -4,16 +4,20 @@
|
||||
$id: http://devicetree.org/schemas/iio/health/maxim,max30102.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Maxim MAX30102 heart rate and pulse oximeter and MAX30105 particle-sensor
|
||||
title: Maxim MAX30101/2 heart rate and pulse oximeter and MAX30105 particle-sensor
|
||||
|
||||
maintainers:
|
||||
- Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- maxim,max30102
|
||||
- maxim,max30105
|
||||
oneOf:
|
||||
- enum:
|
||||
- maxim,max30102
|
||||
- maxim,max30105
|
||||
- items:
|
||||
- const: maxim,max30101
|
||||
- const: maxim,max30105
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -34,6 +34,9 @@ properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@ -43,6 +46,7 @@ additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
@ -54,5 +58,6 @@ examples:
|
||||
vdd-supply = <&vcc_3v3>;
|
||||
interrupt-parent = <&gpio3>;
|
||||
interrupts = <23 IRQ_TYPE_EDGE_RISING>;
|
||||
reset-gpios = <&gpio3 27 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
|
@ -32,6 +32,7 @@ properties:
|
||||
- invensense,icm42605
|
||||
- invensense,icm42622
|
||||
- invensense,icm42631
|
||||
- invensense,icm42688
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -62,14 +62,15 @@ properties:
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- invensense,mpu9150
|
||||
- invensense,mpu9250
|
||||
- invensense,mpu9255
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- invensense,iam20680
|
||||
- invensense,icm20602
|
||||
- invensense,icm20608
|
||||
- invensense,icm20609
|
||||
- invensense,icm20689
|
||||
then:
|
||||
properties:
|
||||
i2c-gate: false
|
||||
|
@ -4,17 +4,22 @@
|
||||
$id: http://devicetree.org/schemas/iio/light/avago,apds9300.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Avago APDS9300 ambient light sensor
|
||||
title: Avago Gesture/RGB/ALS/Proximity sensors
|
||||
|
||||
maintainers:
|
||||
- Jonathan Cameron <jic23@kernel.org>
|
||||
- Subhajit Ghosh <subhajit.ghosh@tweaklogic.com>
|
||||
|
||||
description: |
|
||||
Datasheet at https://www.avagotech.com/docs/AV02-1077EN
|
||||
Datasheet: https://www.avagotech.com/docs/AV02-1077EN
|
||||
Datasheet: https://www.avagotech.com/docs/AV02-4191EN
|
||||
Datasheet: https://www.avagotech.com/docs/AV02-4755EN
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: avago,apds9300
|
||||
enum:
|
||||
- avago,apds9300
|
||||
- avago,apds9306
|
||||
- avago,apds9960
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -22,6 +27,8 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
@ -30,6 +37,8 @@ required:
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
@ -38,7 +47,8 @@ examples:
|
||||
compatible = "avago,apds9300";
|
||||
reg = <0x39>;
|
||||
interrupt-parent = <&gpio2>;
|
||||
interrupts = <29 8>;
|
||||
interrupts = <29 IRQ_TYPE_LEVEL_LOW>;
|
||||
vdd-supply = <®ulator_3v3>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
@ -1,44 +0,0 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/avago,apds9960.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Avago APDS9960 gesture/RGB/ALS/proximity sensor
|
||||
|
||||
maintainers:
|
||||
- Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
|
||||
description: |
|
||||
Datasheet at https://www.avagotech.com/docs/AV02-4191EN
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: avago,apds9960
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
light-sensor@39 {
|
||||
compatible = "avago,apds9960";
|
||||
reg = <0x39>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <16 1>;
|
||||
};
|
||||
};
|
||||
...
|
@ -57,6 +57,8 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
adi,mux-delay-config-us:
|
||||
description: |
|
||||
Extra delay prior to each conversion, in addition to the internal 1ms
|
||||
@ -460,6 +462,7 @@ required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
@ -489,6 +492,7 @@ examples:
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
vdd-supply = <&supply>;
|
||||
interrupts = <20 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-parent = <&gpio>;
|
||||
|
||||
|
130
Documentation/iio/ad7944.rst
Normal file
130
Documentation/iio/ad7944.rst
Normal file
@ -0,0 +1,130 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
=============
|
||||
AD7944 driver
|
||||
=============
|
||||
|
||||
ADC driver for Analog Devices Inc. AD7944 and similar devices. The module name
|
||||
is ``ad7944``.
|
||||
|
||||
|
||||
Supported devices
|
||||
=================
|
||||
|
||||
The following chips are supported by this driver:
|
||||
|
||||
* `AD7944 <https://www.analog.com/AD7944>`_
|
||||
* `AD7985 <https://www.analog.com/AD7985>`_
|
||||
* `AD7986 <https://www.analog.com/AD7986>`_
|
||||
|
||||
|
||||
Supported features
|
||||
==================
|
||||
|
||||
SPI wiring modes
|
||||
----------------
|
||||
|
||||
The driver currently supports two of the many possible SPI wiring configurations.
|
||||
|
||||
CS mode, 3-wire, without busy indicator
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block::
|
||||
|
||||
+-------------+
|
||||
+--------------------| CS |
|
||||
v | |
|
||||
VIO +--------------------+ | HOST |
|
||||
| | CNV | | |
|
||||
+--->| SDI AD7944 SDO |-------->| SDI |
|
||||
| SCK | | |
|
||||
+--------------------+ | |
|
||||
^ | |
|
||||
+--------------------| SCLK |
|
||||
+-------------+
|
||||
|
||||
To select this mode in the device tree, set the ``adi,spi-mode`` property to
|
||||
``"single"`` and omit the ``cnv-gpios`` property.
|
||||
|
||||
CS mode, 4-wire, without busy indicator
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block::
|
||||
|
||||
+-------------+
|
||||
+-----------------------------------| CS |
|
||||
| | |
|
||||
| +--------------------| GPIO |
|
||||
| v | |
|
||||
| +--------------------+ | HOST |
|
||||
| | CNV | | |
|
||||
+--->| SDI AD7944 SDO |-------->| SDI |
|
||||
| SCK | | |
|
||||
+--------------------+ | |
|
||||
^ | |
|
||||
+--------------------| SCLK |
|
||||
+-------------+
|
||||
|
||||
To select this mode in the device tree, omit the ``adi,spi-mode`` property and
|
||||
provide the ``cnv-gpios`` property.
|
||||
|
||||
Reference voltage
|
||||
-----------------
|
||||
|
||||
All 3 possible reference voltage sources are supported:
|
||||
|
||||
- Internal reference
|
||||
- External 1.2V reference and internal buffer
|
||||
- External reference
|
||||
|
||||
The source is determined by the device tree. If ``ref-supply`` is present, then
|
||||
the external reference is used. If ``refin-supply`` is present, then the internal
|
||||
buffer is used. If neither is present, then the internal reference is used.
|
||||
|
||||
Unimplemented features
|
||||
----------------------
|
||||
|
||||
- ``BUSY`` indication
|
||||
- ``TURBO`` mode
|
||||
- Daisy chain mode
|
||||
|
||||
|
||||
Device attributes
|
||||
=================
|
||||
|
||||
There are two types of ADCs in this family, pseudo-differential and fully
|
||||
differential. The channel name is different depending on the type of ADC.
|
||||
|
||||
Pseudo-differential ADCs
|
||||
------------------------
|
||||
|
||||
AD7944 and AD7985 are pseudo-differential ADCs and have the following attributes:
|
||||
|
||||
+---------------------------------------+--------------------------------------------------------------+
|
||||
| Attribute | Description |
|
||||
+=======================================+==============================================================+
|
||||
| ``in_voltage0_raw`` | Raw ADC voltage value (*IN+* referenced to ground sense). |
|
||||
+---------------------------------------+--------------------------------------------------------------+
|
||||
| ``in_voltage0_scale`` | Scale factor to convert raw value to mV. |
|
||||
+---------------------------------------+--------------------------------------------------------------+
|
||||
|
||||
Fully-differential ADCs
|
||||
-----------------------
|
||||
|
||||
AD7986 is a fully-differential ADC and has the following attributes:
|
||||
|
||||
+---------------------------------------+--------------------------------------------------------------+
|
||||
| Attribute | Description |
|
||||
+=======================================+==============================================================+
|
||||
| ``in_voltage0-voltage1_raw`` | Raw ADC voltage value (*IN+* - *IN-*). |
|
||||
+---------------------------------------+--------------------------------------------------------------+
|
||||
| ``in_voltage0-voltage1_scale`` | Scale factor to convert raw value to mV. |
|
||||
+---------------------------------------+--------------------------------------------------------------+
|
||||
|
||||
|
||||
Device buffers
|
||||
==============
|
||||
|
||||
This driver supports IIO triggered buffers.
|
||||
|
||||
See :doc:`iio_devbuf` for more information.
|
@ -16,6 +16,7 @@ Industrial I/O Kernel Drivers
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
ad7944
|
||||
adis16475
|
||||
bno055
|
||||
ep93xx_adc
|
||||
|
28
MAINTAINERS
28
MAINTAINERS
@ -441,6 +441,16 @@ W: http://wiki.analog.com/AD7879
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: drivers/input/touchscreen/ad7879.c
|
||||
|
||||
AD7944 ADC DRIVER (AD7944/AD7985/AD7986)
|
||||
M: Michael Hennerich <michael.hennerich@analog.com>
|
||||
M: Nuno Sá <nuno.sa@analog.com>
|
||||
R: David Lechner <dlechner@baylibre.com>
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7944.yaml
|
||||
F: Documentation/iio/ad7944.rst
|
||||
F: drivers/iio/adc/ad7944.c
|
||||
|
||||
ADAFRUIT MINI I2C GAMEPAD
|
||||
M: Anshul Dalal <anshulusr@gmail.com>
|
||||
L: linux-input@vger.kernel.org
|
||||
@ -1224,6 +1234,15 @@ W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml
|
||||
F: drivers/iio/adc/ad7780.c
|
||||
|
||||
ANALOG DEVICES INC AD9739a DRIVER
|
||||
M: Nuno Sa <nuno.sa@analog.com>
|
||||
M: Dragos Bogdan <dragos.bogdan@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/dac/adi,ad9739a.yaml
|
||||
F: drivers/iio/dac/ad9739a.c
|
||||
|
||||
ANALOG DEVICES INC ADA4250 DRIVER
|
||||
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
@ -1389,6 +1408,14 @@ F: sound/soc/codecs/adav*
|
||||
F: sound/soc/codecs/sigmadsp.*
|
||||
F: sound/soc/codecs/ssm*
|
||||
|
||||
ANALOG DEVICES INC AXI DAC DRIVER
|
||||
M: Nuno Sa <nuno.sa@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/dac/adi,axi-dac.yaml
|
||||
F: drivers/iio/dac/adi-axi-dac.c
|
||||
|
||||
ANALOG DEVICES INC DMA DRIVERS
|
||||
M: Lars-Peter Clausen <lars@metafoo.de>
|
||||
S: Supported
|
||||
@ -10542,6 +10569,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git
|
||||
F: Documentation/ABI/testing/configfs-iio*
|
||||
F: Documentation/ABI/testing/sysfs-bus-iio*
|
||||
F: Documentation/devicetree/bindings/iio/
|
||||
F: Documentation/iio/
|
||||
F: drivers/iio/
|
||||
F: drivers/staging/iio/
|
||||
F: include/dt-bindings/iio/
|
||||
|
@ -868,20 +868,6 @@ struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_handle_get);
|
||||
|
||||
/**
|
||||
* fwnode_handle_put - Drop reference to a device node
|
||||
* @fwnode: Pointer to the device node to drop the reference to.
|
||||
*
|
||||
* This has to be used when terminating device_for_each_child_node() iteration
|
||||
* with break or return to prevent stale device node references from being left
|
||||
* behind.
|
||||
*/
|
||||
void fwnode_handle_put(struct fwnode_handle *fwnode)
|
||||
{
|
||||
fwnode_call_void_op(fwnode, put);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_handle_put);
|
||||
|
||||
/**
|
||||
* fwnode_device_is_available - check if a device is available for use
|
||||
* @fwnode: Pointer to the fwnode of the device.
|
||||
|
@ -8,6 +8,39 @@
|
||||
#ifndef _ADXL345_H_
|
||||
#define _ADXL345_H_
|
||||
|
||||
#define ADXL345_REG_DEVID 0x00
|
||||
#define ADXL345_REG_OFSX 0x1E
|
||||
#define ADXL345_REG_OFSY 0x1F
|
||||
#define ADXL345_REG_OFSZ 0x20
|
||||
#define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index))
|
||||
#define ADXL345_REG_BW_RATE 0x2C
|
||||
#define ADXL345_REG_POWER_CTL 0x2D
|
||||
#define ADXL345_REG_DATA_FORMAT 0x31
|
||||
#define ADXL345_REG_DATAX0 0x32
|
||||
#define ADXL345_REG_DATAY0 0x34
|
||||
#define ADXL345_REG_DATAZ0 0x36
|
||||
#define ADXL345_REG_DATA_AXIS(index) \
|
||||
(ADXL345_REG_DATAX0 + (index) * sizeof(__le16))
|
||||
|
||||
#define ADXL345_BW_RATE GENMASK(3, 0)
|
||||
#define ADXL345_BASE_RATE_NANO_HZ 97656250LL
|
||||
|
||||
#define ADXL345_POWER_CTL_MEASURE BIT(3)
|
||||
#define ADXL345_POWER_CTL_STANDBY 0x00
|
||||
|
||||
#define ADXL345_DATA_FORMAT_RANGE GENMASK(1, 0) /* Set the g range */
|
||||
#define ADXL345_DATA_FORMAT_JUSTIFY BIT(2) /* Left-justified (MSB) mode */
|
||||
#define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */
|
||||
#define ADXL345_DATA_FORMAT_SPI_3WIRE BIT(6) /* 3-wire SPI mode */
|
||||
#define ADXL345_DATA_FORMAT_SELF_TEST BIT(7) /* Enable a self test */
|
||||
|
||||
#define ADXL345_DATA_FORMAT_2G 0
|
||||
#define ADXL345_DATA_FORMAT_4G 1
|
||||
#define ADXL345_DATA_FORMAT_8G 2
|
||||
#define ADXL345_DATA_FORMAT_16G 3
|
||||
|
||||
#define ADXL345_DEVID 0xE5
|
||||
|
||||
/*
|
||||
* In full-resolution mode, scale factor is maintained at ~4 mg/LSB
|
||||
* in all g ranges.
|
||||
@ -28,6 +61,7 @@ struct adxl345_chip_info {
|
||||
int uscale;
|
||||
};
|
||||
|
||||
int adxl345_core_probe(struct device *dev, struct regmap *regmap);
|
||||
int adxl345_core_probe(struct device *dev, struct regmap *regmap,
|
||||
int (*setup)(struct device*, struct regmap*));
|
||||
|
||||
#endif /* _ADXL345_H_ */
|
||||
|
@ -17,38 +17,9 @@
|
||||
|
||||
#include "adxl345.h"
|
||||
|
||||
#define ADXL345_REG_DEVID 0x00
|
||||
#define ADXL345_REG_OFSX 0x1e
|
||||
#define ADXL345_REG_OFSY 0x1f
|
||||
#define ADXL345_REG_OFSZ 0x20
|
||||
#define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index))
|
||||
#define ADXL345_REG_BW_RATE 0x2C
|
||||
#define ADXL345_REG_POWER_CTL 0x2D
|
||||
#define ADXL345_REG_DATA_FORMAT 0x31
|
||||
#define ADXL345_REG_DATAX0 0x32
|
||||
#define ADXL345_REG_DATAY0 0x34
|
||||
#define ADXL345_REG_DATAZ0 0x36
|
||||
#define ADXL345_REG_DATA_AXIS(index) \
|
||||
(ADXL345_REG_DATAX0 + (index) * sizeof(__le16))
|
||||
|
||||
#define ADXL345_BW_RATE GENMASK(3, 0)
|
||||
#define ADXL345_BASE_RATE_NANO_HZ 97656250LL
|
||||
|
||||
#define ADXL345_POWER_CTL_MEASURE BIT(3)
|
||||
#define ADXL345_POWER_CTL_STANDBY 0x00
|
||||
|
||||
#define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */
|
||||
#define ADXL345_DATA_FORMAT_2G 0
|
||||
#define ADXL345_DATA_FORMAT_4G 1
|
||||
#define ADXL345_DATA_FORMAT_8G 2
|
||||
#define ADXL345_DATA_FORMAT_16G 3
|
||||
|
||||
#define ADXL345_DEVID 0xE5
|
||||
|
||||
struct adxl345_data {
|
||||
const struct adxl345_chip_info *info;
|
||||
struct regmap *regmap;
|
||||
u8 data_range;
|
||||
};
|
||||
|
||||
#define ADXL345_CHANNEL(index, axis) { \
|
||||
@ -197,44 +168,75 @@ static void adxl345_powerdown(void *regmap)
|
||||
regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_STANDBY);
|
||||
}
|
||||
|
||||
int adxl345_core_probe(struct device *dev, struct regmap *regmap)
|
||||
/**
|
||||
* adxl345_core_probe() - probe and setup for the adxl345 accelerometer,
|
||||
* also covers the adlx375 accelerometer
|
||||
* @dev: Driver model representation of the device
|
||||
* @regmap: Regmap instance for the device
|
||||
* @setup: Setup routine to be executed right before the standard device
|
||||
* setup
|
||||
*
|
||||
* Return: 0 on success, negative errno on error
|
||||
*/
|
||||
int adxl345_core_probe(struct device *dev, struct regmap *regmap,
|
||||
int (*setup)(struct device*, struct regmap*))
|
||||
{
|
||||
struct adxl345_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
u32 regval;
|
||||
unsigned int data_format_mask = (ADXL345_DATA_FORMAT_RANGE |
|
||||
ADXL345_DATA_FORMAT_JUSTIFY |
|
||||
ADXL345_DATA_FORMAT_FULL_RES |
|
||||
ADXL345_DATA_FORMAT_SELF_TEST);
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(regmap, ADXL345_REG_DEVID, ®val);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "Error reading device ID\n");
|
||||
|
||||
if (regval != ADXL345_DEVID)
|
||||
return dev_err_probe(dev, -ENODEV, "Invalid device ID: %x, expected %x\n",
|
||||
regval, ADXL345_DEVID);
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->regmap = regmap;
|
||||
/* Enable full-resolution mode */
|
||||
data->data_range = ADXL345_DATA_FORMAT_FULL_RES;
|
||||
data->info = device_get_match_data(dev);
|
||||
if (!data->info)
|
||||
return -ENODEV;
|
||||
|
||||
ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT,
|
||||
data->data_range);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "Failed to set data range\n");
|
||||
|
||||
indio_dev->name = data->info->name;
|
||||
indio_dev->info = &adxl345_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = adxl345_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adxl345_channels);
|
||||
|
||||
if (setup) {
|
||||
/* Perform optional initial bus specific configuration */
|
||||
ret = setup(dev, data->regmap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable full-resolution mode */
|
||||
ret = regmap_update_bits(data->regmap, ADXL345_REG_DATA_FORMAT,
|
||||
data_format_mask,
|
||||
ADXL345_DATA_FORMAT_FULL_RES);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to set data range\n");
|
||||
|
||||
} else {
|
||||
/* Enable full-resolution mode (init all data_format bits) */
|
||||
ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT,
|
||||
ADXL345_DATA_FORMAT_FULL_RES);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to set data range\n");
|
||||
}
|
||||
|
||||
ret = regmap_read(data->regmap, ADXL345_REG_DEVID, ®val);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "Error reading device ID\n");
|
||||
|
||||
if (regval != ADXL345_DEVID)
|
||||
return dev_err_probe(dev, -ENODEV, "Invalid device ID: %x, expected %x\n",
|
||||
regval, ADXL345_DEVID);
|
||||
|
||||
/* Enable measurement mode */
|
||||
ret = adxl345_powerup(data->regmap);
|
||||
if (ret < 0)
|
||||
|
@ -27,7 +27,7 @@ static int adxl345_i2c_probe(struct i2c_client *client)
|
||||
if (IS_ERR(regmap))
|
||||
return dev_err_probe(&client->dev, PTR_ERR(regmap), "Error initializing regmap\n");
|
||||
|
||||
return adxl345_core_probe(&client->dev, regmap);
|
||||
return adxl345_core_probe(&client->dev, regmap, NULL);
|
||||
}
|
||||
|
||||
static const struct adxl345_chip_info adxl345_i2c_info = {
|
||||
|
@ -20,6 +20,11 @@ static const struct regmap_config adxl345_spi_regmap_config = {
|
||||
.read_flag_mask = BIT(7) | BIT(6),
|
||||
};
|
||||
|
||||
static int adxl345_spi_setup(struct device *dev, struct regmap *regmap)
|
||||
{
|
||||
return regmap_write(regmap, ADXL345_REG_DATA_FORMAT, ADXL345_DATA_FORMAT_SPI_3WIRE);
|
||||
}
|
||||
|
||||
static int adxl345_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
@ -33,7 +38,10 @@ static int adxl345_spi_probe(struct spi_device *spi)
|
||||
if (IS_ERR(regmap))
|
||||
return dev_err_probe(&spi->dev, PTR_ERR(regmap), "Error initializing regmap\n");
|
||||
|
||||
return adxl345_core_probe(&spi->dev, regmap);
|
||||
if (spi->mode & SPI_3WIRE)
|
||||
return adxl345_core_probe(&spi->dev, regmap, adxl345_spi_setup);
|
||||
else
|
||||
return adxl345_core_probe(&spi->dev, regmap, NULL);
|
||||
}
|
||||
|
||||
static const struct adxl345_chip_info adxl345_spi_info = {
|
||||
|
@ -621,7 +621,7 @@ static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr)
|
||||
static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr)
|
||||
{
|
||||
iio_device_claim_direct_scoped(return -EBUSY, indio_dev) {
|
||||
struct adxl367_state *st = iio_priv(indio_dev);;
|
||||
struct adxl367_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
|
@ -15,9 +15,11 @@
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
@ -1062,12 +1064,12 @@ static void fxls8962af_pm_disable(void *dev_ptr)
|
||||
fxls8962af_standby(iio_priv(indio_dev));
|
||||
}
|
||||
|
||||
static void fxls8962af_get_irq(struct device_node *of_node,
|
||||
static void fxls8962af_get_irq(struct device *dev,
|
||||
enum fxls8962af_int_pin *pin)
|
||||
{
|
||||
int irq;
|
||||
|
||||
irq = of_irq_get_byname(of_node, "INT2");
|
||||
irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT2");
|
||||
if (irq > 0) {
|
||||
*pin = FXLS8962AF_PIN_INT2;
|
||||
return;
|
||||
@ -1086,7 +1088,7 @@ static int fxls8962af_irq_setup(struct iio_dev *indio_dev, int irq)
|
||||
u8 int_pin_sel;
|
||||
int ret;
|
||||
|
||||
fxls8962af_get_irq(dev->of_node, &int_pin);
|
||||
fxls8962af_get_irq(dev, &int_pin);
|
||||
switch (int_pin) {
|
||||
case FXLS8962AF_PIN_INT1:
|
||||
int_pin_sel = FXLS8962AF_INT_PIN_SEL_INT1;
|
||||
|
@ -19,6 +19,8 @@
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@ -28,8 +30,6 @@
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
@ -1642,7 +1642,7 @@ static int mma8452_probe(struct i2c_client *client)
|
||||
if (client->irq) {
|
||||
int irq2;
|
||||
|
||||
irq2 = of_irq_get_byname(client->dev.of_node, "INT2");
|
||||
irq2 = fwnode_irq_get_byname(dev_fwnode(&client->dev), "INT2");
|
||||
|
||||
if (irq2 == client->irq) {
|
||||
dev_dbg(&client->dev, "using interrupt line INT2\n");
|
||||
|
@ -70,6 +70,23 @@ config AD7124
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ad7124.
|
||||
|
||||
config AD7173
|
||||
tristate "Analog Devices AD7173 driver"
|
||||
depends on SPI_MASTER
|
||||
select AD_SIGMA_DELTA
|
||||
select GPIO_REGMAP if GPIOLIB
|
||||
select REGMAP_SPI if GPIOLIB
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7173 and similar ADC
|
||||
Currently supported models:
|
||||
- AD7172-2
|
||||
- AD7173-8
|
||||
- AD7175-2
|
||||
- AD7176-2
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ad7173.
|
||||
|
||||
config AD7192
|
||||
tristate "Analog Devices AD7190 AD7192 AD7193 AD7195 ADC driver"
|
||||
depends on SPI
|
||||
@ -264,6 +281,16 @@ config AD7923
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7923.
|
||||
|
||||
config AD7944
|
||||
tristate "Analog Devices AD7944 and similar ADCs driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices
|
||||
AD7944, AD7985, AD7986 ADCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7944
|
||||
|
||||
config AD7949
|
||||
tristate "Analog Devices AD7949 and similar ADCs driver"
|
||||
depends on SPI
|
||||
|
@ -11,6 +11,7 @@ obj-$(CONFIG_AD7091R) += ad7091r-base.o
|
||||
obj-$(CONFIG_AD7091R5) += ad7091r5.o
|
||||
obj-$(CONFIG_AD7091R8) += ad7091r8.o
|
||||
obj-$(CONFIG_AD7124) += ad7124.o
|
||||
obj-$(CONFIG_AD7173) += ad7173.o
|
||||
obj-$(CONFIG_AD7192) += ad7192.o
|
||||
obj-$(CONFIG_AD7266) += ad7266.o
|
||||
obj-$(CONFIG_AD7280) += ad7280a.o
|
||||
@ -28,6 +29,7 @@ obj-$(CONFIG_AD7780) += ad7780.o
|
||||
obj-$(CONFIG_AD7791) += ad7791.o
|
||||
obj-$(CONFIG_AD7793) += ad7793.o
|
||||
obj-$(CONFIG_AD7887) += ad7887.o
|
||||
obj-$(CONFIG_AD7944) += ad7944.o
|
||||
obj-$(CONFIG_AD7949) += ad7949.o
|
||||
obj-$(CONFIG_AD799X) += ad799x.o
|
||||
obj-$(CONFIG_AD9467) += ad9467.o
|
||||
|
@ -1021,14 +1021,13 @@ static int ab8500_gpadc_parse_channel(struct device *dev,
|
||||
/**
|
||||
* ab8500_gpadc_parse_channels() - Parse the GPADC channels from DT
|
||||
* @gpadc: the GPADC to configure the channels for
|
||||
* @chans: the IIO channels we parsed
|
||||
* @nchans: the number of IIO channels we parsed
|
||||
* @chans_parsed: the IIO channels we parsed
|
||||
* @nchans_parsed: the number of IIO channels we parsed
|
||||
*/
|
||||
static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc,
|
||||
struct iio_chan_spec **chans_parsed,
|
||||
unsigned int *nchans_parsed)
|
||||
{
|
||||
struct fwnode_handle *child;
|
||||
struct ab8500_gpadc_chan_info *ch;
|
||||
struct iio_chan_spec *iio_chans;
|
||||
unsigned int nchans;
|
||||
@ -1052,7 +1051,7 @@ static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc,
|
||||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
device_for_each_child_node(gpadc->dev, child) {
|
||||
device_for_each_child_node_scoped(gpadc->dev, child) {
|
||||
struct iio_chan_spec *iio_chan;
|
||||
int ret;
|
||||
|
||||
@ -1062,7 +1061,6 @@ static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc,
|
||||
ret = ab8500_gpadc_parse_channel(gpadc->dev, child, ch,
|
||||
iio_chan);
|
||||
if (ret) {
|
||||
fwnode_handle_put(child);
|
||||
return ret;
|
||||
}
|
||||
i++;
|
||||
|
@ -1600,17 +1600,14 @@ static int ad4130_parse_fw_children(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad4130_state *st = iio_priv(indio_dev);
|
||||
struct device *dev = &st->spi->dev;
|
||||
struct fwnode_handle *child;
|
||||
int ret;
|
||||
|
||||
indio_dev->channels = st->chans;
|
||||
|
||||
device_for_each_child_node(dev, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
ret = ad4130_parse_fw_channel(indio_dev, child);
|
||||
if (ret) {
|
||||
fwnode_handle_put(child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -14,7 +14,8 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
@ -807,22 +808,19 @@ static int ad7124_check_chip_id(struct ad7124_state *st)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
|
||||
struct device_node *np)
|
||||
static int ad7124_parse_channel_config(struct iio_dev *indio_dev,
|
||||
struct device *dev)
|
||||
{
|
||||
struct ad7124_state *st = iio_priv(indio_dev);
|
||||
struct ad7124_channel_config *cfg;
|
||||
struct ad7124_channel *channels;
|
||||
struct device_node *child;
|
||||
struct iio_chan_spec *chan;
|
||||
unsigned int ain[2], channel = 0, tmp;
|
||||
int ret;
|
||||
|
||||
st->num_channels = of_get_available_child_count(np);
|
||||
if (!st->num_channels) {
|
||||
dev_err(indio_dev->dev.parent, "no channel children\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
st->num_channels = device_get_child_node_count(dev);
|
||||
if (!st->num_channels)
|
||||
return dev_err_probe(dev, -ENODEV, "no channel children\n");
|
||||
|
||||
chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels,
|
||||
sizeof(*chan), GFP_KERNEL);
|
||||
@ -838,39 +836,38 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
|
||||
indio_dev->num_channels = st->num_channels;
|
||||
st->channels = channels;
|
||||
|
||||
for_each_available_child_of_node(np, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
cfg = &st->channels[channel].cfg;
|
||||
|
||||
ret = of_property_read_u32(child, "reg", &channel);
|
||||
ret = fwnode_property_read_u32(child, "reg", &channel);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
if (channel >= indio_dev->num_channels) {
|
||||
dev_err(indio_dev->dev.parent,
|
||||
if (channel >= indio_dev->num_channels)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Channel index >= number of channels\n");
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(child, "diff-channels",
|
||||
ain, 2);
|
||||
ret = fwnode_property_read_u32_array(child, "diff-channels",
|
||||
ain, 2);
|
||||
if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
|
||||
st->channels[channel].nr = channel;
|
||||
st->channels[channel].ain = AD7124_CHANNEL_AINP(ain[0]) |
|
||||
AD7124_CHANNEL_AINM(ain[1]);
|
||||
|
||||
cfg->bipolar = of_property_read_bool(child, "bipolar");
|
||||
cfg->bipolar = fwnode_property_read_bool(child, "bipolar");
|
||||
|
||||
ret = of_property_read_u32(child, "adi,reference-select", &tmp);
|
||||
ret = fwnode_property_read_u32(child, "adi,reference-select", &tmp);
|
||||
if (ret)
|
||||
cfg->refsel = AD7124_INT_REF;
|
||||
else
|
||||
cfg->refsel = tmp;
|
||||
|
||||
cfg->buf_positive = of_property_read_bool(child, "adi,buffered-positive");
|
||||
cfg->buf_negative = of_property_read_bool(child, "adi,buffered-negative");
|
||||
cfg->buf_positive =
|
||||
fwnode_property_read_bool(child, "adi,buffered-positive");
|
||||
cfg->buf_negative =
|
||||
fwnode_property_read_bool(child, "adi,buffered-negative");
|
||||
|
||||
chan[channel] = ad7124_channel_template;
|
||||
chan[channel].address = channel;
|
||||
@ -880,10 +877,6 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
of_node_put(child);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7124_setup(struct ad7124_state *st)
|
||||
@ -943,9 +936,7 @@ static int ad7124_probe(struct spi_device *spi)
|
||||
struct iio_dev *indio_dev;
|
||||
int i, ret;
|
||||
|
||||
info = of_device_get_match_data(&spi->dev);
|
||||
if (!info)
|
||||
info = (void *)spi_get_device_id(spi)->driver_data;
|
||||
info = spi_get_device_match_data(spi);
|
||||
if (!info)
|
||||
return -ENODEV;
|
||||
|
||||
@ -965,7 +956,7 @@ static int ad7124_probe(struct spi_device *spi)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = ad7124_of_parse_channel_config(indio_dev, spi->dev.of_node);
|
||||
ret = ad7124_parse_channel_config(indio_dev, &spi->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
1180
drivers/iio/adc/ad7173.c
Normal file
1180
drivers/iio/adc/ad7173.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,9 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@ -364,19 +366,19 @@ static inline bool ad7192_valid_external_frequency(u32 freq)
|
||||
freq <= AD7192_EXT_FREQ_MHZ_MAX);
|
||||
}
|
||||
|
||||
static int ad7192_of_clock_select(struct ad7192_state *st)
|
||||
static int ad7192_clock_select(struct ad7192_state *st)
|
||||
{
|
||||
struct device_node *np = st->sd.spi->dev.of_node;
|
||||
struct device *dev = &st->sd.spi->dev;
|
||||
unsigned int clock_sel;
|
||||
|
||||
clock_sel = AD7192_CLK_INT;
|
||||
|
||||
/* use internal clock */
|
||||
if (!st->mclk) {
|
||||
if (of_property_read_bool(np, "adi,int-clock-output-enable"))
|
||||
if (device_property_read_bool(dev, "adi,int-clock-output-enable"))
|
||||
clock_sel = AD7192_CLK_INT_CO;
|
||||
} else {
|
||||
if (of_property_read_bool(np, "adi,clock-xtal"))
|
||||
if (device_property_read_bool(dev, "adi,clock-xtal"))
|
||||
clock_sel = AD7192_CLK_EXT_MCLK1_2;
|
||||
else
|
||||
clock_sel = AD7192_CLK_EXT_MCLK2;
|
||||
@ -385,7 +387,7 @@ static int ad7192_of_clock_select(struct ad7192_state *st)
|
||||
return clock_sel;
|
||||
}
|
||||
|
||||
static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np)
|
||||
static int ad7192_setup(struct iio_dev *indio_dev, struct device *dev)
|
||||
{
|
||||
struct ad7192_state *st = iio_priv(indio_dev);
|
||||
bool rej60_en, refin2_en;
|
||||
@ -407,7 +409,7 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np)
|
||||
id = FIELD_GET(AD7192_ID_MASK, id);
|
||||
|
||||
if (id != st->chip_info->chip_id)
|
||||
dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X != 0x%X)\n",
|
||||
dev_warn(dev, "device ID query failed (0x%X != 0x%X)\n",
|
||||
id, st->chip_info->chip_id);
|
||||
|
||||
st->mode = FIELD_PREP(AD7192_MODE_SEL_MASK, AD7192_MODE_IDLE) |
|
||||
@ -416,30 +418,30 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np)
|
||||
|
||||
st->conf = FIELD_PREP(AD7192_CONF_GAIN_MASK, 0);
|
||||
|
||||
rej60_en = of_property_read_bool(np, "adi,rejection-60-Hz-enable");
|
||||
rej60_en = device_property_read_bool(dev, "adi,rejection-60-Hz-enable");
|
||||
if (rej60_en)
|
||||
st->mode |= AD7192_MODE_REJ60;
|
||||
|
||||
refin2_en = of_property_read_bool(np, "adi,refin2-pins-enable");
|
||||
refin2_en = device_property_read_bool(dev, "adi,refin2-pins-enable");
|
||||
if (refin2_en && st->chip_info->chip_id != CHIPID_AD7195)
|
||||
st->conf |= AD7192_CONF_REFSEL;
|
||||
|
||||
st->conf &= ~AD7192_CONF_CHOP;
|
||||
|
||||
buf_en = of_property_read_bool(np, "adi,buffer-enable");
|
||||
buf_en = device_property_read_bool(dev, "adi,buffer-enable");
|
||||
if (buf_en)
|
||||
st->conf |= AD7192_CONF_BUF;
|
||||
|
||||
bipolar = of_property_read_bool(np, "bipolar");
|
||||
bipolar = device_property_read_bool(dev, "bipolar");
|
||||
if (!bipolar)
|
||||
st->conf |= AD7192_CONF_UNIPOLAR;
|
||||
|
||||
burnout_curr_en = of_property_read_bool(np,
|
||||
"adi,burnout-currents-enable");
|
||||
burnout_curr_en = device_property_read_bool(dev,
|
||||
"adi,burnout-currents-enable");
|
||||
if (burnout_curr_en && buf_en) {
|
||||
st->conf |= AD7192_CONF_BURN;
|
||||
} else if (burnout_curr_en) {
|
||||
dev_warn(&st->sd.spi->dev,
|
||||
dev_warn(dev,
|
||||
"Can't enable burnout currents: see CHOP or buffer\n");
|
||||
}
|
||||
|
||||
@ -1117,9 +1119,7 @@ static int ad7192_probe(struct spi_device *spi)
|
||||
}
|
||||
st->int_vref_mv = ret / 1000;
|
||||
|
||||
st->chip_info = of_device_get_match_data(&spi->dev);
|
||||
if (!st->chip_info)
|
||||
st->chip_info = (void *)spi_get_device_id(spi)->driver_data;
|
||||
st->chip_info = spi_get_device_match_data(spi);
|
||||
indio_dev->name = st->chip_info->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
@ -1140,7 +1140,7 @@ static int ad7192_probe(struct spi_device *spi)
|
||||
if (IS_ERR(st->mclk))
|
||||
return PTR_ERR(st->mclk);
|
||||
|
||||
st->clock_sel = ad7192_of_clock_select(st);
|
||||
st->clock_sel = ad7192_clock_select(st);
|
||||
|
||||
if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 ||
|
||||
st->clock_sel == AD7192_CLK_EXT_MCLK2) {
|
||||
@ -1152,7 +1152,7 @@ static int ad7192_probe(struct spi_device *spi)
|
||||
}
|
||||
}
|
||||
|
||||
ret = ad7192_setup(indio_dev, spi->dev.of_node);
|
||||
ret = ad7192_setup(indio_dev, &spi->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -8,7 +8,8 @@
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
@ -260,7 +261,6 @@ static int ad7292_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7292_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
struct device_node *child;
|
||||
bool diff_channels = false;
|
||||
int ret;
|
||||
|
||||
@ -305,12 +305,11 @@ static int ad7292_probe(struct spi_device *spi)
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &ad7292_info;
|
||||
|
||||
for_each_available_child_of_node(spi->dev.of_node, child) {
|
||||
diff_channels = of_property_read_bool(child, "diff-channels");
|
||||
if (diff_channels) {
|
||||
of_node_put(child);
|
||||
device_for_each_child_node_scoped(&spi->dev, child) {
|
||||
diff_channels = fwnode_property_read_bool(child,
|
||||
"diff-channels");
|
||||
if (diff_channels)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (diff_channels) {
|
||||
|
524
drivers/iio/adc/ad7944.c
Normal file
524
drivers/iio/adc/ad7944.c
Normal file
@ -0,0 +1,524 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Analog Devices AD7944/85/86 PulSAR ADC family driver.
|
||||
*
|
||||
* Copyright 2024 Analog Devices, Inc.
|
||||
* Copyright 2024 BayLibre, SAS
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/string_helpers.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define AD7944_INTERNAL_REF_MV 4096
|
||||
|
||||
struct ad7944_timing_spec {
|
||||
/* Normal mode max conversion time (t_{CONV}). */
|
||||
unsigned int conv_ns;
|
||||
/* TURBO mode max conversion time (t_{CONV}). */
|
||||
unsigned int turbo_conv_ns;
|
||||
};
|
||||
|
||||
enum ad7944_spi_mode {
|
||||
/* datasheet calls this "4-wire mode" */
|
||||
AD7944_SPI_MODE_DEFAULT,
|
||||
/* datasheet calls this "3-wire mode" (not related to SPI_3WIRE!) */
|
||||
AD7944_SPI_MODE_SINGLE,
|
||||
/* datasheet calls this "chain mode" */
|
||||
AD7944_SPI_MODE_CHAIN,
|
||||
};
|
||||
|
||||
/* maps adi,spi-mode property value to enum */
|
||||
static const char * const ad7944_spi_modes[] = {
|
||||
[AD7944_SPI_MODE_DEFAULT] = "",
|
||||
[AD7944_SPI_MODE_SINGLE] = "single",
|
||||
[AD7944_SPI_MODE_CHAIN] = "chain",
|
||||
};
|
||||
|
||||
struct ad7944_adc {
|
||||
struct spi_device *spi;
|
||||
enum ad7944_spi_mode spi_mode;
|
||||
struct spi_transfer xfers[3];
|
||||
struct spi_message msg;
|
||||
/* Chip-specific timing specifications. */
|
||||
const struct ad7944_timing_spec *timing_spec;
|
||||
/* GPIO connected to CNV pin. */
|
||||
struct gpio_desc *cnv;
|
||||
/* Optional GPIO to enable turbo mode. */
|
||||
struct gpio_desc *turbo;
|
||||
/* Indicates TURBO is hard-wired to be always enabled. */
|
||||
bool always_turbo;
|
||||
/* Reference voltage (millivolts). */
|
||||
unsigned int ref_mv;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
struct {
|
||||
union {
|
||||
u16 u16;
|
||||
u32 u32;
|
||||
} raw;
|
||||
u64 timestamp __aligned(8);
|
||||
} sample __aligned(IIO_DMA_MINALIGN);
|
||||
};
|
||||
|
||||
/* quite time before CNV rising edge */
|
||||
#define T_QUIET_NS 20
|
||||
|
||||
static const struct ad7944_timing_spec ad7944_timing_spec = {
|
||||
.conv_ns = 420,
|
||||
.turbo_conv_ns = 320,
|
||||
};
|
||||
|
||||
static const struct ad7944_timing_spec ad7986_timing_spec = {
|
||||
.conv_ns = 500,
|
||||
.turbo_conv_ns = 400,
|
||||
};
|
||||
|
||||
struct ad7944_chip_info {
|
||||
const char *name;
|
||||
const struct ad7944_timing_spec *timing_spec;
|
||||
const struct iio_chan_spec channels[2];
|
||||
};
|
||||
|
||||
/*
|
||||
* AD7944_DEFINE_CHIP_INFO - Define a chip info structure for a specific chip
|
||||
* @_name: The name of the chip
|
||||
* @_ts: The timing specification for the chip
|
||||
* @_bits: The number of bits in the conversion result
|
||||
* @_diff: Whether the chip is true differential or not
|
||||
*/
|
||||
#define AD7944_DEFINE_CHIP_INFO(_name, _ts, _bits, _diff) \
|
||||
static const struct ad7944_chip_info _name##_chip_info = { \
|
||||
.name = #_name, \
|
||||
.timing_spec = &_ts##_timing_spec, \
|
||||
.channels = { \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.differential = _diff, \
|
||||
.channel = 0, \
|
||||
.channel2 = _diff ? 1 : 0, \
|
||||
.scan_index = 0, \
|
||||
.scan_type.sign = _diff ? 's' : 'u', \
|
||||
.scan_type.realbits = _bits, \
|
||||
.scan_type.storagebits = _bits > 16 ? 32 : 16, \
|
||||
.scan_type.endianness = IIO_CPU, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
|
||||
| BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}, \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1), \
|
||||
}, \
|
||||
}
|
||||
|
||||
/* pseudo-differential with ground sense */
|
||||
AD7944_DEFINE_CHIP_INFO(ad7944, ad7944, 14, 0);
|
||||
AD7944_DEFINE_CHIP_INFO(ad7985, ad7944, 16, 0);
|
||||
/* fully differential */
|
||||
AD7944_DEFINE_CHIP_INFO(ad7986, ad7986, 18, 1);
|
||||
|
||||
static void ad7944_unoptimize_msg(void *msg)
|
||||
{
|
||||
spi_unoptimize_message(msg);
|
||||
}
|
||||
|
||||
static int ad7944_3wire_cs_mode_init_msg(struct device *dev, struct ad7944_adc *adc,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
unsigned int t_conv_ns = adc->always_turbo ? adc->timing_spec->turbo_conv_ns
|
||||
: adc->timing_spec->conv_ns;
|
||||
struct spi_transfer *xfers = adc->xfers;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* NB: can get better performance from some SPI controllers if we use
|
||||
* the same bits_per_word in every transfer.
|
||||
*/
|
||||
xfers[0].bits_per_word = chan->scan_type.realbits;
|
||||
/*
|
||||
* CS is tied to CNV and we need a low to high transition to start the
|
||||
* conversion, so place CNV low for t_QUIET to prepare for this.
|
||||
*/
|
||||
xfers[0].delay.value = T_QUIET_NS;
|
||||
xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
|
||||
|
||||
/*
|
||||
* CS has to be high for full conversion time to avoid triggering the
|
||||
* busy indication.
|
||||
*/
|
||||
xfers[1].cs_off = 1;
|
||||
xfers[1].delay.value = t_conv_ns;
|
||||
xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS;
|
||||
xfers[1].bits_per_word = chan->scan_type.realbits;
|
||||
|
||||
/* Then we can read the data during the acquisition phase */
|
||||
xfers[2].rx_buf = &adc->sample.raw;
|
||||
xfers[2].len = BITS_TO_BYTES(chan->scan_type.storagebits);
|
||||
xfers[2].bits_per_word = chan->scan_type.realbits;
|
||||
|
||||
spi_message_init_with_transfers(&adc->msg, xfers, 3);
|
||||
|
||||
ret = spi_optimize_message(adc->spi, &adc->msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(dev, ad7944_unoptimize_msg, &adc->msg);
|
||||
}
|
||||
|
||||
static int ad7944_4wire_mode_init_msg(struct device *dev, struct ad7944_adc *adc,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
unsigned int t_conv_ns = adc->always_turbo ? adc->timing_spec->turbo_conv_ns
|
||||
: adc->timing_spec->conv_ns;
|
||||
struct spi_transfer *xfers = adc->xfers;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* NB: can get better performance from some SPI controllers if we use
|
||||
* the same bits_per_word in every transfer.
|
||||
*/
|
||||
xfers[0].bits_per_word = chan->scan_type.realbits;
|
||||
/*
|
||||
* CS has to be high for full conversion time to avoid triggering the
|
||||
* busy indication.
|
||||
*/
|
||||
xfers[0].cs_off = 1;
|
||||
xfers[0].delay.value = t_conv_ns;
|
||||
xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
|
||||
|
||||
xfers[1].rx_buf = &adc->sample.raw;
|
||||
xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits);
|
||||
xfers[1].bits_per_word = chan->scan_type.realbits;
|
||||
|
||||
spi_message_init_with_transfers(&adc->msg, xfers, 2);
|
||||
|
||||
ret = spi_optimize_message(adc->spi, &adc->msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(dev, ad7944_unoptimize_msg, &adc->msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* ad7944_convert_and_acquire - Perform a single conversion and acquisition
|
||||
* @adc: The ADC device structure
|
||||
* @chan: The channel specification
|
||||
* Return: 0 on success, a negative error code on failure
|
||||
*
|
||||
* Perform a conversion and acquisition of a single sample using the
|
||||
* pre-optimized adc->msg.
|
||||
*
|
||||
* Upon successful return adc->sample.raw will contain the conversion result.
|
||||
*/
|
||||
static int ad7944_convert_and_acquire(struct ad7944_adc *adc,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* In 4-wire mode, the CNV line is held high for the entire conversion
|
||||
* and acquisition process. In other modes adc->cnv is NULL and is
|
||||
* ignored (CS is wired to CNV in those cases).
|
||||
*/
|
||||
gpiod_set_value_cansleep(adc->cnv, 1);
|
||||
ret = spi_sync(adc->spi, &adc->msg);
|
||||
gpiod_set_value_cansleep(adc->cnv, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7944_single_conversion(struct ad7944_adc *adc,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad7944_convert_and_acquire(adc, chan);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (chan->scan_type.storagebits > 16)
|
||||
*val = adc->sample.raw.u32;
|
||||
else
|
||||
*val = adc->sample.raw.u16;
|
||||
|
||||
if (chan->scan_type.sign == 's')
|
||||
*val = sign_extend32(*val, chan->scan_type.realbits - 1);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int ad7944_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long info)
|
||||
{
|
||||
struct ad7944_adc *adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad7944_single_conversion(adc, chan, val);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
*val = adc->ref_mv;
|
||||
|
||||
if (chan->scan_type.sign == 's')
|
||||
*val2 = chan->scan_type.realbits - 1;
|
||||
else
|
||||
*val2 = chan->scan_type.realbits;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info ad7944_iio_info = {
|
||||
.read_raw = &ad7944_read_raw,
|
||||
};
|
||||
|
||||
static irqreturn_t ad7944_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7944_adc *adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = ad7944_convert_and_acquire(adc, &indio_dev->channels[0]);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &adc->sample.raw,
|
||||
pf->timestamp);
|
||||
|
||||
out:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const char * const ad7944_power_supplies[] = {
|
||||
"avdd", "dvdd", "bvdd", "vio"
|
||||
};
|
||||
|
||||
static void ad7944_ref_disable(void *ref)
|
||||
{
|
||||
regulator_disable(ref);
|
||||
}
|
||||
|
||||
static int ad7944_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct ad7944_chip_info *chip_info;
|
||||
struct device *dev = &spi->dev;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad7944_adc *adc;
|
||||
bool have_refin = false;
|
||||
struct regulator *ref;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->spi = spi;
|
||||
|
||||
chip_info = spi_get_device_match_data(spi);
|
||||
if (!chip_info)
|
||||
return dev_err_probe(dev, -EINVAL, "no chip info\n");
|
||||
|
||||
adc->timing_spec = chip_info->timing_spec;
|
||||
|
||||
ret = device_property_match_property_string(dev, "adi,spi-mode",
|
||||
ad7944_spi_modes,
|
||||
ARRAY_SIZE(ad7944_spi_modes));
|
||||
/* absence of adi,spi-mode property means default mode */
|
||||
if (ret == -EINVAL)
|
||||
adc->spi_mode = AD7944_SPI_MODE_DEFAULT;
|
||||
else if (ret < 0)
|
||||
return dev_err_probe(dev, ret,
|
||||
"getting adi,spi-mode property failed\n");
|
||||
else
|
||||
adc->spi_mode = ret;
|
||||
|
||||
/*
|
||||
* Some chips use unusual word sizes, so check now instead of waiting
|
||||
* for the first xfer.
|
||||
*/
|
||||
if (!spi_is_bpw_supported(spi, chip_info->channels[0].scan_type.realbits))
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"SPI host does not support %d bits per word\n",
|
||||
chip_info->channels[0].scan_type.realbits);
|
||||
|
||||
ret = devm_regulator_bulk_get_enable(dev,
|
||||
ARRAY_SIZE(ad7944_power_supplies),
|
||||
ad7944_power_supplies);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to get and enable supplies\n");
|
||||
|
||||
/*
|
||||
* Sort out what is being used for the reference voltage. Options are:
|
||||
* - internal reference: neither REF or REFIN is connected
|
||||
* - internal reference with external buffer: REF not connected, REFIN
|
||||
* is connected
|
||||
* - external reference: REF is connected, REFIN is not connected
|
||||
*/
|
||||
|
||||
ref = devm_regulator_get_optional(dev, "ref");
|
||||
if (IS_ERR(ref)) {
|
||||
if (PTR_ERR(ref) != -ENODEV)
|
||||
return dev_err_probe(dev, PTR_ERR(ref),
|
||||
"failed to get REF supply\n");
|
||||
|
||||
ref = NULL;
|
||||
}
|
||||
|
||||
ret = devm_regulator_get_enable_optional(dev, "refin");
|
||||
if (ret == 0)
|
||||
have_refin = true;
|
||||
else if (ret != -ENODEV)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to get and enable REFIN supply\n");
|
||||
|
||||
if (have_refin && ref)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"cannot have both refin and ref supplies\n");
|
||||
|
||||
if (ref) {
|
||||
ret = regulator_enable(ref);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to enable REF supply\n");
|
||||
|
||||
ret = devm_add_action_or_reset(dev, ad7944_ref_disable, ref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(ref);
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to get REF voltage\n");
|
||||
|
||||
/* external reference */
|
||||
adc->ref_mv = ret / 1000;
|
||||
} else {
|
||||
/* internal reference */
|
||||
adc->ref_mv = AD7944_INTERNAL_REF_MV;
|
||||
}
|
||||
|
||||
adc->cnv = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(adc->cnv))
|
||||
return dev_err_probe(dev, PTR_ERR(adc->cnv),
|
||||
"failed to get CNV GPIO\n");
|
||||
|
||||
if (!adc->cnv && adc->spi_mode == AD7944_SPI_MODE_DEFAULT)
|
||||
return dev_err_probe(&spi->dev, -EINVAL, "CNV GPIO is required\n");
|
||||
if (adc->cnv && adc->spi_mode != AD7944_SPI_MODE_DEFAULT)
|
||||
return dev_err_probe(&spi->dev, -EINVAL,
|
||||
"CNV GPIO in single and chain mode is not currently supported\n");
|
||||
|
||||
adc->turbo = devm_gpiod_get_optional(dev, "turbo", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(adc->turbo))
|
||||
return dev_err_probe(dev, PTR_ERR(adc->turbo),
|
||||
"failed to get TURBO GPIO\n");
|
||||
|
||||
adc->always_turbo = device_property_present(dev, "adi,always-turbo");
|
||||
|
||||
if (adc->turbo && adc->always_turbo)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"cannot have both turbo-gpios and adi,always-turbo\n");
|
||||
|
||||
if (adc->spi_mode == AD7944_SPI_MODE_CHAIN && adc->always_turbo)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"cannot have both chain mode and always turbo\n");
|
||||
|
||||
switch (adc->spi_mode) {
|
||||
case AD7944_SPI_MODE_DEFAULT:
|
||||
ret = ad7944_4wire_mode_init_msg(dev, adc, &chip_info->channels[0]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
case AD7944_SPI_MODE_SINGLE:
|
||||
ret = ad7944_3wire_cs_mode_init_msg(dev, adc, &chip_info->channels[0]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
case AD7944_SPI_MODE_CHAIN:
|
||||
return dev_err_probe(dev, -EINVAL, "chain mode is not implemented\n");
|
||||
}
|
||||
|
||||
indio_dev->name = chip_info->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &ad7944_iio_info;
|
||||
indio_dev->channels = chip_info->channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(chip_info->channels);
|
||||
|
||||
ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
|
||||
iio_pollfunc_store_time,
|
||||
ad7944_trigger_handler, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id ad7944_of_match[] = {
|
||||
{ .compatible = "adi,ad7944", .data = &ad7944_chip_info },
|
||||
{ .compatible = "adi,ad7985", .data = &ad7985_chip_info },
|
||||
{ .compatible = "adi,ad7986", .data = &ad7986_chip_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad7944_of_match);
|
||||
|
||||
static const struct spi_device_id ad7944_spi_id[] = {
|
||||
{ "ad7944", (kernel_ulong_t)&ad7944_chip_info },
|
||||
{ "ad7985", (kernel_ulong_t)&ad7985_chip_info },
|
||||
{ "ad7986", (kernel_ulong_t)&ad7986_chip_info },
|
||||
{ }
|
||||
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7944_spi_id);
|
||||
|
||||
static struct spi_driver ad7944_driver = {
|
||||
.driver = {
|
||||
.name = "ad7944",
|
||||
.of_match_table = ad7944_of_match,
|
||||
},
|
||||
.probe = ad7944_probe,
|
||||
.id_table = ad7944_spi_id,
|
||||
};
|
||||
module_spi_driver(ad7944_driver);
|
||||
|
||||
MODULE_AUTHOR("David Lechner <dlechner@baylibre.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7944 PulSAR ADC family driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -222,11 +222,11 @@ int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
|
||||
goto out;
|
||||
|
||||
sigma_delta->irq_dis = false;
|
||||
enable_irq(sigma_delta->spi->irq);
|
||||
enable_irq(sigma_delta->irq_line);
|
||||
timeout = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ);
|
||||
if (timeout == 0) {
|
||||
sigma_delta->irq_dis = true;
|
||||
disable_irq_nosync(sigma_delta->spi->irq);
|
||||
disable_irq_nosync(sigma_delta->irq_line);
|
||||
ret = -EIO;
|
||||
} else {
|
||||
ret = 0;
|
||||
@ -295,7 +295,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
|
||||
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE);
|
||||
|
||||
sigma_delta->irq_dis = false;
|
||||
enable_irq(sigma_delta->spi->irq);
|
||||
enable_irq(sigma_delta->irq_line);
|
||||
ret = wait_for_completion_interruptible_timeout(
|
||||
&sigma_delta->completion, HZ);
|
||||
|
||||
@ -315,7 +315,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
|
||||
|
||||
out:
|
||||
if (!sigma_delta->irq_dis) {
|
||||
disable_irq_nosync(sigma_delta->spi->irq);
|
||||
disable_irq_nosync(sigma_delta->irq_line);
|
||||
sigma_delta->irq_dis = true;
|
||||
}
|
||||
|
||||
@ -396,7 +396,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
|
||||
goto err_unlock;
|
||||
|
||||
sigma_delta->irq_dis = false;
|
||||
enable_irq(sigma_delta->spi->irq);
|
||||
enable_irq(sigma_delta->irq_line);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -414,7 +414,7 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
wait_for_completion_timeout(&sigma_delta->completion, HZ);
|
||||
|
||||
if (!sigma_delta->irq_dis) {
|
||||
disable_irq_nosync(sigma_delta->spi->irq);
|
||||
disable_irq_nosync(sigma_delta->irq_line);
|
||||
sigma_delta->irq_dis = true;
|
||||
}
|
||||
|
||||
@ -516,7 +516,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
|
||||
irq_handled:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
sigma_delta->irq_dis = false;
|
||||
enable_irq(sigma_delta->spi->irq);
|
||||
enable_irq(sigma_delta->irq_line);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -587,13 +587,13 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de
|
||||
sigma_delta->irq_dis = true;
|
||||
|
||||
/* the IRQ core clears IRQ_DISABLE_UNLAZY flag when freeing an IRQ */
|
||||
irq_set_status_flags(sigma_delta->spi->irq, IRQ_DISABLE_UNLAZY);
|
||||
irq_set_status_flags(sigma_delta->irq_line, IRQ_DISABLE_UNLAZY);
|
||||
|
||||
/* Allow overwriting the flags from firmware */
|
||||
if (!irq_flags)
|
||||
irq_flags = sigma_delta->info->irq_flags;
|
||||
|
||||
ret = devm_request_irq(dev, sigma_delta->spi->irq,
|
||||
ret = devm_request_irq(dev, sigma_delta->irq_line,
|
||||
ad_sd_data_rdy_trig_poll,
|
||||
irq_flags | IRQF_NO_AUTOEN,
|
||||
indio_dev->name,
|
||||
@ -673,6 +673,11 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
|
||||
}
|
||||
}
|
||||
|
||||
if (info->irq_line)
|
||||
sigma_delta->irq_line = info->irq_line;
|
||||
else
|
||||
sigma_delta->irq_line = spi->irq;
|
||||
|
||||
iio_device_set_drvdata(indio_dev, sigma_delta);
|
||||
|
||||
return 0;
|
||||
|
@ -124,26 +124,12 @@ static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back,
|
||||
struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adi_axi_adc_state *st = iio_backend_get_priv(back);
|
||||
struct iio_buffer *buffer;
|
||||
const char *dma_name;
|
||||
int ret;
|
||||
|
||||
if (device_property_read_string(st->dev, "dma-names", &dma_name))
|
||||
dma_name = "rx";
|
||||
|
||||
buffer = iio_dmaengine_buffer_alloc(st->dev, dma_name);
|
||||
if (IS_ERR(buffer)) {
|
||||
dev_err(st->dev, "Could not get DMA buffer, %ld\n",
|
||||
PTR_ERR(buffer));
|
||||
return ERR_CAST(buffer);
|
||||
}
|
||||
|
||||
indio_dev->modes |= INDIO_BUFFER_HARDWARE;
|
||||
ret = iio_device_attach_buffer(indio_dev, buffer);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return buffer;
|
||||
return iio_dmaengine_buffer_setup(st->dev, indio_dev, dma_name);
|
||||
}
|
||||
|
||||
static void axi_adc_free_buffer(struct iio_backend *back,
|
||||
@ -207,9 +193,9 @@ static int adi_axi_adc_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (*expected_ver > ver) {
|
||||
if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) {
|
||||
dev_err(&pdev->dev,
|
||||
"IP core version is too old. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
|
||||
"Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
|
||||
ADI_AXI_PCORE_VER_MAJOR(*expected_ver),
|
||||
ADI_AXI_PCORE_VER_MINOR(*expected_ver),
|
||||
ADI_AXI_PCORE_VER_PATCH(*expected_ver),
|
||||
|
@ -12,8 +12,9 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/imx25-tsadc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
@ -198,8 +199,6 @@ static int mx25_gcq_ext_regulator_setup(struct device *dev,
|
||||
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;
|
||||
int ret, i;
|
||||
|
||||
@ -216,37 +215,30 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
|
||||
MX25_ADCQ_CFG_IN(i) |
|
||||
MX25_ADCQ_CFG_REFN_NGND2);
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
u32 reg;
|
||||
u32 refp = MX25_ADCQ_CFG_REFP_INT;
|
||||
u32 refn = MX25_ADCQ_CFG_REFN_NGND2;
|
||||
|
||||
ret = of_property_read_u32(child, "reg", ®);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to get reg property\n");
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
ret = fwnode_property_read_u32(child, "reg", ®);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to get reg property\n");
|
||||
|
||||
if (reg >= MX25_NUM_CFGS) {
|
||||
dev_err(dev,
|
||||
if (reg >= MX25_NUM_CFGS)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"reg value is greater than the number of available configuration registers\n");
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
of_property_read_u32(child, "fsl,adc-refp", &refp);
|
||||
of_property_read_u32(child, "fsl,adc-refn", &refn);
|
||||
fwnode_property_read_u32(child, "fsl,adc-refp", &refp);
|
||||
fwnode_property_read_u32(child, "fsl,adc-refn", &refn);
|
||||
|
||||
switch (refp) {
|
||||
case MX25_ADC_REFP_EXT:
|
||||
case MX25_ADC_REFP_XP:
|
||||
case MX25_ADC_REFP_YP:
|
||||
ret = mx25_gcq_ext_regulator_setup(&pdev->dev, priv, refp);
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
priv->channel_vref_mv[reg] =
|
||||
regulator_get_voltage(priv->vref[refp]);
|
||||
/* Conversion from uV to mV */
|
||||
@ -256,9 +248,8 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
|
||||
priv->channel_vref_mv[reg] = 2500;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid positive reference %d\n", refp);
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid positive reference %d\n", refp);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -268,16 +259,13 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
|
||||
refp = MX25_ADCQ_CFG_REFP(refp);
|
||||
refn = MX25_ADCQ_CFG_REFN(refn);
|
||||
|
||||
if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) {
|
||||
dev_err(dev, "Invalid fsl,adc-refp property value\n");
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) {
|
||||
dev_err(dev, "Invalid fsl,adc-refn property value\n");
|
||||
of_node_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid fsl,adc-refp property value\n");
|
||||
|
||||
if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid fsl,adc-refn property value\n");
|
||||
|
||||
regmap_update_bits(priv->regs, MX25_ADCQ_CFG(reg),
|
||||
MX25_ADCQ_CFG_REFP_MASK |
|
||||
@ -294,6 +282,17 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mx25_gcq_reg_disable(void *reg)
|
||||
{
|
||||
regulator_disable(reg);
|
||||
}
|
||||
|
||||
/* Custom handling needed as this driver doesn't own the clock */
|
||||
static void mx25_gcq_clk_disable(void *clk)
|
||||
{
|
||||
clk_disable_unprepare(clk);
|
||||
}
|
||||
|
||||
static int mx25_gcq_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
@ -315,10 +314,9 @@ static int mx25_gcq_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(mem);
|
||||
|
||||
priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_gcq_regconfig);
|
||||
if (IS_ERR(priv->regs)) {
|
||||
dev_err(dev, "Failed to initialize regmap\n");
|
||||
return PTR_ERR(priv->regs);
|
||||
}
|
||||
if (IS_ERR(priv->regs))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->regs),
|
||||
"Failed to initialize regmap\n");
|
||||
|
||||
mutex_init(&priv->lock);
|
||||
|
||||
@ -334,69 +332,44 @@ static int mx25_gcq_probe(struct platform_device *pdev)
|
||||
|
||||
ret = regulator_enable(priv->vref[i]);
|
||||
if (ret)
|
||||
goto err_regulator_disable;
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, mx25_gcq_reg_disable,
|
||||
priv->vref[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->clk = tsadc->clk;
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable clock\n");
|
||||
goto err_vref_disable;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to enable clock\n");
|
||||
|
||||
ret = devm_add_action_or_reset(dev, mx25_gcq_clk_disable,
|
||||
priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
goto err_clk_unprepare;
|
||||
return ret;
|
||||
|
||||
priv->irq = ret;
|
||||
ret = request_irq(priv->irq, mx25_gcq_irq, 0, pdev->name, priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed requesting IRQ\n");
|
||||
goto err_clk_unprepare;
|
||||
}
|
||||
ret = devm_request_irq(dev, priv->irq, mx25_gcq_irq, 0, pdev->name,
|
||||
priv);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed requesting IRQ\n");
|
||||
|
||||
indio_dev->channels = mx25_gcq_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mx25_gcq_channels);
|
||||
indio_dev->info = &mx25_gcq_iio_info;
|
||||
indio_dev->name = driver_name;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register iio device\n");
|
||||
goto err_irq_free;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
ret = devm_iio_device_register(dev, indio_dev);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to register iio device\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_irq_free:
|
||||
free_irq(priv->irq, priv);
|
||||
err_clk_unprepare:
|
||||
clk_disable_unprepare(priv->clk);
|
||||
err_vref_disable:
|
||||
i = 4;
|
||||
err_regulator_disable:
|
||||
for (; i-- > 0;) {
|
||||
if (priv->vref[i])
|
||||
regulator_disable(priv->vref[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mx25_gcq_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct mx25_gcq_priv *priv = iio_priv(indio_dev);
|
||||
int i;
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
free_irq(priv->irq, priv);
|
||||
clk_disable_unprepare(priv->clk);
|
||||
for (i = 4; i-- > 0;) {
|
||||
if (priv->vref[i])
|
||||
regulator_disable(priv->vref[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct of_device_id mx25_gcq_ids[] = {
|
||||
@ -411,7 +384,6 @@ static struct platform_driver mx25_gcq_driver = {
|
||||
.of_match_table = mx25_gcq_ids,
|
||||
},
|
||||
.probe = mx25_gcq_probe,
|
||||
.remove_new = mx25_gcq_remove,
|
||||
};
|
||||
module_platform_driver(mx25_gcq_driver);
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
@ -459,7 +459,6 @@ static const struct iio_chan_spec hx711_chan_spec[] = {
|
||||
static int hx711_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct hx711_data *hx711_data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
@ -533,7 +532,7 @@ static int hx711_probe(struct platform_device *pdev)
|
||||
hx711_data->gain_chan_a = 128;
|
||||
|
||||
hx711_data->clock_frequency = 400000;
|
||||
ret = of_property_read_u32(np, "clock-frequency",
|
||||
ret = device_property_read_u32(&pdev->dev, "clock-frequency",
|
||||
&hx711_data->clock_frequency);
|
||||
|
||||
/*
|
||||
|
@ -696,7 +696,6 @@ static int max11410_parse_channels(struct max11410_state *st,
|
||||
struct device *dev = &st->spi_dev->dev;
|
||||
struct max11410_channel_config *cfg;
|
||||
struct iio_chan_spec *channels;
|
||||
struct fwnode_handle *child;
|
||||
u32 reference, sig_path;
|
||||
const char *node_name;
|
||||
u32 inputs[2], scale;
|
||||
@ -720,7 +719,7 @@ static int max11410_parse_channels(struct max11410_state *st,
|
||||
if (!st->channels)
|
||||
return -ENOMEM;
|
||||
|
||||
device_for_each_child_node(dev, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
node_name = fwnode_get_name(child);
|
||||
if (fwnode_property_present(child, "diff-channels")) {
|
||||
ret = fwnode_property_read_u32_array(child,
|
||||
@ -735,47 +734,37 @@ static int max11410_parse_channels(struct max11410_state *st,
|
||||
inputs[1] = 0;
|
||||
chanspec.differential = 0;
|
||||
}
|
||||
if (ret) {
|
||||
fwnode_handle_put(child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (inputs[0] > MAX11410_CHANNEL_INDEX_MAX ||
|
||||
inputs[1] > MAX11410_CHANNEL_INDEX_MAX) {
|
||||
fwnode_handle_put(child);
|
||||
inputs[1] > MAX11410_CHANNEL_INDEX_MAX)
|
||||
return dev_err_probe(&indio_dev->dev, -EINVAL,
|
||||
"Invalid channel index for %s, should be less than %d\n",
|
||||
node_name,
|
||||
MAX11410_CHANNEL_INDEX_MAX + 1);
|
||||
}
|
||||
|
||||
cfg = &st->channels[chan_idx];
|
||||
|
||||
reference = MAX11410_REFSEL_AVDD_AGND;
|
||||
fwnode_property_read_u32(child, "adi,reference", &reference);
|
||||
if (reference > MAX11410_REFSEL_MAX) {
|
||||
fwnode_handle_put(child);
|
||||
if (reference > MAX11410_REFSEL_MAX)
|
||||
return dev_err_probe(&indio_dev->dev, -EINVAL,
|
||||
"Invalid adi,reference value for %s, should be less than %d.\n",
|
||||
node_name, MAX11410_REFSEL_MAX + 1);
|
||||
}
|
||||
|
||||
if (!max11410_get_vrefp(st, reference) ||
|
||||
(!max11410_get_vrefn(st, reference) && reference <= 2)) {
|
||||
fwnode_handle_put(child);
|
||||
(!max11410_get_vrefn(st, reference) && reference <= 2))
|
||||
return dev_err_probe(&indio_dev->dev, -EINVAL,
|
||||
"Invalid VREF configuration for %s, either specify corresponding VREF regulators or change adi,reference property.\n",
|
||||
node_name);
|
||||
}
|
||||
|
||||
sig_path = MAX11410_PGA_SIG_PATH_BUFFERED;
|
||||
fwnode_property_read_u32(child, "adi,input-mode", &sig_path);
|
||||
if (sig_path > MAX11410_SIG_PATH_MAX) {
|
||||
fwnode_handle_put(child);
|
||||
if (sig_path > MAX11410_SIG_PATH_MAX)
|
||||
return dev_err_probe(&indio_dev->dev, -EINVAL,
|
||||
"Invalid adi,input-mode value for %s, should be less than %d.\n",
|
||||
node_name, MAX11410_SIG_PATH_MAX + 1);
|
||||
}
|
||||
|
||||
fwnode_property_read_u32(child, "settling-time-us",
|
||||
&cfg->settling_time_us);
|
||||
@ -793,10 +782,8 @@ static int max11410_parse_channels(struct max11410_state *st,
|
||||
cfg->scale_avail = devm_kcalloc(dev, MAX11410_SCALE_AVAIL_SIZE * 2,
|
||||
sizeof(*cfg->scale_avail),
|
||||
GFP_KERNEL);
|
||||
if (!cfg->scale_avail) {
|
||||
fwnode_handle_put(child);
|
||||
if (!cfg->scale_avail)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
scale = max11410_get_scale(st, *cfg);
|
||||
for (i = 0; i < MAX11410_SCALE_AVAIL_SIZE; i++) {
|
||||
|
@ -1079,8 +1079,8 @@ static int pac1934_chip_identify(struct pac1934_chip_info *info)
|
||||
* documentation related to the ACPI device definition
|
||||
* https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC1934-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf
|
||||
*/
|
||||
static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
|
||||
struct pac1934_chip_info *info)
|
||||
static int pac1934_acpi_parse_channel_config(struct i2c_client *client,
|
||||
struct pac1934_chip_info *info)
|
||||
{
|
||||
acpi_handle handle;
|
||||
union acpi_object *rez;
|
||||
@ -1095,7 +1095,7 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
|
||||
|
||||
rez = acpi_evaluate_dsm(handle, &guid, 0, PAC1934_ACPI_GET_NAMES_AND_MOHMS_VALS, NULL);
|
||||
if (!rez)
|
||||
return false;
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < rez->package.count; i += 2) {
|
||||
idx = i / 2;
|
||||
@ -1118,7 +1118,7 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
|
||||
* and assign the default sampling rate
|
||||
*/
|
||||
info->sample_rate_value = PAC1934_DEFAULT_CHIP_SAMP_SPEED_HZ;
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < rez->package.count; i++) {
|
||||
@ -1131,7 +1131,7 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
|
||||
|
||||
rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_BIPOLAR_SETTINGS, NULL);
|
||||
if (!rez)
|
||||
return false;
|
||||
return -EINVAL;
|
||||
|
||||
bi_dir_mask = rez->package.elements[0].integer.value;
|
||||
info->bi_dir[0] = ((bi_dir_mask & (1 << 3)) | (bi_dir_mask & (1 << 7))) != 0;
|
||||
@ -1143,19 +1143,18 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client,
|
||||
|
||||
rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_SAMP, NULL);
|
||||
if (!rez)
|
||||
return false;
|
||||
return -EINVAL;
|
||||
|
||||
info->sample_rate_value = rez->package.elements[0].integer.value;
|
||||
|
||||
ACPI_FREE(rez);
|
||||
|
||||
return true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool pac1934_of_parse_channel_config(struct i2c_client *client,
|
||||
struct pac1934_chip_info *info)
|
||||
static int pac1934_fw_parse_channel_config(struct i2c_client *client,
|
||||
struct pac1934_chip_info *info)
|
||||
{
|
||||
struct fwnode_handle *node, *fwnode;
|
||||
struct device *dev = &client->dev;
|
||||
unsigned int current_channel;
|
||||
int idx, ret;
|
||||
@ -1163,46 +1162,38 @@ static bool pac1934_of_parse_channel_config(struct i2c_client *client,
|
||||
info->sample_rate_value = 1024;
|
||||
current_channel = 1;
|
||||
|
||||
fwnode = dev_fwnode(dev);
|
||||
fwnode_for_each_available_child_node(fwnode, node) {
|
||||
device_for_each_child_node_scoped(dev, node) {
|
||||
ret = fwnode_property_read_u32(node, "reg", &idx);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret,
|
||||
"reading invalid channel index\n");
|
||||
goto err_fwnode;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"reading invalid channel index\n");
|
||||
|
||||
/* adjust idx to match channel index (1 to 4) from the datasheet */
|
||||
idx--;
|
||||
|
||||
if (current_channel >= (info->phys_channels + 1) ||
|
||||
idx >= info->phys_channels || idx < 0) {
|
||||
dev_err_probe(dev, -EINVAL,
|
||||
"%s: invalid channel_index %d value\n",
|
||||
fwnode_get_name(node), idx);
|
||||
goto err_fwnode;
|
||||
}
|
||||
idx >= info->phys_channels || idx < 0)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"%s: invalid channel_index %d value\n",
|
||||
fwnode_get_name(node), idx);
|
||||
|
||||
/* enable channel */
|
||||
info->active_channels[idx] = true;
|
||||
|
||||
ret = fwnode_property_read_u32(node, "shunt-resistor-micro-ohms",
|
||||
&info->shunts[idx]);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret,
|
||||
"%s: invalid shunt-resistor value: %d\n",
|
||||
fwnode_get_name(node), info->shunts[idx]);
|
||||
goto err_fwnode;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"%s: invalid shunt-resistor value: %d\n",
|
||||
fwnode_get_name(node), info->shunts[idx]);
|
||||
|
||||
if (fwnode_property_present(node, "label")) {
|
||||
ret = fwnode_property_read_string(node, "label",
|
||||
(const char **)&info->labels[idx]);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret,
|
||||
"%s: invalid rail-name value\n",
|
||||
fwnode_get_name(node));
|
||||
goto err_fwnode;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"%s: invalid rail-name value\n",
|
||||
fwnode_get_name(node));
|
||||
}
|
||||
|
||||
info->bi_dir[idx] = fwnode_property_read_bool(node, "bipolar");
|
||||
@ -1210,12 +1201,7 @@ static bool pac1934_of_parse_channel_config(struct i2c_client *client,
|
||||
current_channel++;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
err_fwnode:
|
||||
fwnode_handle_put(node);
|
||||
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pac1934_cancel_delayed_work(void *dwork)
|
||||
@ -1485,7 +1471,6 @@ static int pac1934_probe(struct i2c_client *client)
|
||||
const struct pac1934_features *chip;
|
||||
struct iio_dev *indio_dev;
|
||||
int cnt, ret;
|
||||
bool match = false;
|
||||
struct device *dev = &client->dev;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
|
||||
@ -1519,16 +1504,16 @@ static int pac1934_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
if (acpi_match_device(dev->driver->acpi_match_table, dev))
|
||||
match = pac1934_acpi_parse_channel_config(client, info);
|
||||
ret = pac1934_acpi_parse_channel_config(client, info);
|
||||
else
|
||||
/*
|
||||
* This makes it possible to use also ACPI PRP0001 for
|
||||
* registering the device using device tree properties.
|
||||
*/
|
||||
match = pac1934_of_parse_channel_config(client, info);
|
||||
ret = pac1934_fw_parse_channel_config(client, info);
|
||||
|
||||
if (!match)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"parameter parsing returned an error\n");
|
||||
|
||||
mutex_init(&info->lock);
|
||||
|
@ -825,7 +825,6 @@ static int adc5_get_fw_data(struct adc5_chip *adc)
|
||||
const struct adc5_channels *adc_chan;
|
||||
struct iio_chan_spec *iio_chan;
|
||||
struct adc5_channel_prop prop, *chan_props;
|
||||
struct fwnode_handle *child;
|
||||
unsigned int index = 0;
|
||||
int ret;
|
||||
|
||||
@ -849,12 +848,10 @@ static int adc5_get_fw_data(struct adc5_chip *adc)
|
||||
if (!adc->data)
|
||||
adc->data = &adc5_data_pmic;
|
||||
|
||||
device_for_each_child_node(adc->dev, child) {
|
||||
device_for_each_child_node_scoped(adc->dev, child) {
|
||||
ret = adc5_get_fw_channel_data(adc, &prop, child, adc->data);
|
||||
if (ret) {
|
||||
fwnode_handle_put(child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
prop.scale_fn_type =
|
||||
adc->data->adc_chans[prop.channel].scale_fn_type;
|
||||
|
@ -318,7 +318,6 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
|
||||
struct rcar_gyroadc *priv = iio_priv(indio_dev);
|
||||
struct device *dev = priv->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *child;
|
||||
struct regulator *vref;
|
||||
unsigned int reg;
|
||||
unsigned int adcmode = -1, childmode;
|
||||
@ -326,7 +325,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
|
||||
unsigned int num_channels;
|
||||
int ret, first = 1;
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
for_each_available_child_of_node_scoped(np, child) {
|
||||
of_id = of_match_node(rcar_gyroadc_child_match, child);
|
||||
if (!of_id) {
|
||||
dev_err(dev, "Ignoring unsupported ADC \"%pOFn\".",
|
||||
@ -352,7 +351,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
|
||||
num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_3);
|
||||
break;
|
||||
default:
|
||||
goto err_e_inval;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -369,7 +368,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
|
||||
dev_err(dev,
|
||||
"Failed to get child reg property of ADC \"%pOFn\".\n",
|
||||
child);
|
||||
goto err_of_node_put;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Channel number is too high. */
|
||||
@ -377,7 +376,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
|
||||
dev_err(dev,
|
||||
"Only %i channels supported with %pOFn, but reg = <%i>.\n",
|
||||
num_channels, child, reg);
|
||||
goto err_e_inval;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -386,7 +385,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
|
||||
dev_err(dev,
|
||||
"Channel %i uses different ADC mode than the rest.\n",
|
||||
reg);
|
||||
goto err_e_inval;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Channel is valid, grab the regulator. */
|
||||
@ -396,8 +395,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
|
||||
if (IS_ERR(vref)) {
|
||||
dev_dbg(dev, "Channel %i 'vref' supply not connected.\n",
|
||||
reg);
|
||||
ret = PTR_ERR(vref);
|
||||
goto err_of_node_put;
|
||||
return PTR_ERR(vref);
|
||||
}
|
||||
|
||||
priv->vref[reg] = vref;
|
||||
@ -422,7 +420,6 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
|
||||
* we can stop parsing here.
|
||||
*/
|
||||
if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) {
|
||||
of_node_put(child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -433,12 +430,6 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_e_inval:
|
||||
ret = -EINVAL;
|
||||
err_of_node_put:
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rcar_gyroadc_deinit_supplies(struct iio_dev *indio_dev)
|
||||
|
@ -302,7 +302,6 @@ static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id)
|
||||
static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l_adc *adc)
|
||||
{
|
||||
struct iio_chan_spec *chan_array;
|
||||
struct fwnode_handle *fwnode;
|
||||
struct rzg2l_adc_data *data;
|
||||
unsigned int channel;
|
||||
int num_channels;
|
||||
@ -330,17 +329,13 @@ static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l
|
||||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
device_for_each_child_node(&pdev->dev, fwnode) {
|
||||
device_for_each_child_node_scoped(&pdev->dev, fwnode) {
|
||||
ret = fwnode_property_read_u32(fwnode, "reg", &channel);
|
||||
if (ret) {
|
||||
fwnode_handle_put(fwnode);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (channel >= RZG2L_ADC_MAX_CHANNELS) {
|
||||
fwnode_handle_put(fwnode);
|
||||
if (channel >= RZG2L_ADC_MAX_CHANNELS)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chan_array[i].type = IIO_VOLTAGE;
|
||||
chan_array[i].indexed = 1;
|
||||
|
@ -5,8 +5,10 @@
|
||||
* Copyright 2012 Stefan Roese <sr@denx.de>
|
||||
*/
|
||||
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -15,8 +17,6 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@ -70,7 +70,7 @@ struct adc_regs_spear6xx {
|
||||
};
|
||||
|
||||
struct spear_adc_state {
|
||||
struct device_node *np;
|
||||
struct device *dev;
|
||||
struct adc_regs_spear3xx __iomem *adc_base_spear3xx;
|
||||
struct adc_regs_spear6xx __iomem *adc_base_spear6xx;
|
||||
struct clk *clk;
|
||||
@ -123,7 +123,7 @@ static void spear_adc_set_ctrl(struct spear_adc_state *st, int n,
|
||||
|
||||
static u32 spear_adc_get_average(struct spear_adc_state *st)
|
||||
{
|
||||
if (of_device_is_compatible(st->np, "st,spear600-adc")) {
|
||||
if (device_is_compatible(st->dev, "st,spear600-adc")) {
|
||||
return __raw_readl(&st->adc_base_spear6xx->average.msb) &
|
||||
SPEAR_ADC_DATA_MASK;
|
||||
} else {
|
||||
@ -134,7 +134,7 @@ static u32 spear_adc_get_average(struct spear_adc_state *st)
|
||||
|
||||
static void spear_adc_set_scanrate(struct spear_adc_state *st, u32 rate)
|
||||
{
|
||||
if (of_device_is_compatible(st->np, "st,spear600-adc")) {
|
||||
if (device_is_compatible(st->dev, "st,spear600-adc")) {
|
||||
__raw_writel(SPEAR600_ADC_SCAN_RATE_LO(rate),
|
||||
&st->adc_base_spear6xx->scan_rate_lo);
|
||||
__raw_writel(SPEAR600_ADC_SCAN_RATE_HI(rate),
|
||||
@ -266,7 +266,6 @@ static const struct iio_info spear_adc_info = {
|
||||
|
||||
static int spear_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct spear_adc_state *st;
|
||||
struct iio_dev *indio_dev = NULL;
|
||||
@ -279,11 +278,10 @@ static int spear_adc_probe(struct platform_device *pdev)
|
||||
"failed allocating iio device\n");
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
st->dev = dev;
|
||||
|
||||
mutex_init(&st->lock);
|
||||
|
||||
st->np = np;
|
||||
|
||||
/*
|
||||
* SPEAr600 has a different register layout than other SPEAr SoC's
|
||||
* (e.g. SPEAr3xx). Let's provide two register base addresses
|
||||
@ -310,8 +308,7 @@ static int spear_adc_probe(struct platform_device *pdev)
|
||||
if (ret < 0)
|
||||
return dev_err_probe(dev, ret, "failed requesting interrupt\n");
|
||||
|
||||
if (of_property_read_u32(np, "sampling-frequency",
|
||||
&st->sampling_freq))
|
||||
if (device_property_read_u32(dev, "sampling-frequency", &st->sampling_freq))
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"sampling-frequency missing in DT\n");
|
||||
|
||||
@ -319,13 +316,13 @@ static int spear_adc_probe(struct platform_device *pdev)
|
||||
* Optional avg_samples defaults to 0, resulting in single data
|
||||
* conversion
|
||||
*/
|
||||
of_property_read_u32(np, "average-samples", &st->avg_samples);
|
||||
device_property_read_u32(dev, "average-samples", &st->avg_samples);
|
||||
|
||||
/*
|
||||
* Optional vref_external defaults to 0, resulting in internal vref
|
||||
* selection
|
||||
*/
|
||||
of_property_read_u32(np, "vref-external", &st->vref_external);
|
||||
device_property_read_u32(dev, "vref-external", &st->vref_external);
|
||||
|
||||
spear_adc_configure(st);
|
||||
|
||||
@ -346,19 +343,17 @@ static int spear_adc_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id spear_adc_dt_ids[] = {
|
||||
{ .compatible = "st,spear600-adc", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spear_adc_dt_ids);
|
||||
#endif
|
||||
|
||||
static struct platform_driver spear_adc_driver = {
|
||||
.probe = spear_adc_probe,
|
||||
.driver = {
|
||||
.name = SPEAR_ADC_MOD_NAME,
|
||||
.of_match_table = of_match_ptr(spear_adc_dt_ids),
|
||||
.of_match_table = spear_adc_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -2187,58 +2187,52 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec *channels)
|
||||
{
|
||||
const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
|
||||
struct fwnode_handle *child;
|
||||
struct device *dev = &indio_dev->dev;
|
||||
const char *name;
|
||||
int val, scan_index = 0, ret;
|
||||
bool differential;
|
||||
u32 vin[2];
|
||||
|
||||
device_for_each_child_node(&indio_dev->dev, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
ret = fwnode_property_read_u32(child, "reg", &val);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "Missing channel index %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Missing channel index\n");
|
||||
|
||||
ret = fwnode_property_read_string(child, "label", &name);
|
||||
/* label is optional */
|
||||
if (!ret) {
|
||||
if (strlen(name) >= STM32_ADC_CH_SZ) {
|
||||
dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n",
|
||||
name, STM32_ADC_CH_SZ);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
if (strlen(name) >= STM32_ADC_CH_SZ)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Label %s exceeds %d characters\n",
|
||||
name, STM32_ADC_CH_SZ);
|
||||
|
||||
strscpy(adc->chan_name[val], name, STM32_ADC_CH_SZ);
|
||||
ret = stm32_adc_populate_int_ch(indio_dev, name, val);
|
||||
if (ret == -ENOENT)
|
||||
continue;
|
||||
else if (ret)
|
||||
goto err;
|
||||
return ret;
|
||||
} else if (ret != -EINVAL) {
|
||||
dev_err(&indio_dev->dev, "Invalid label %d\n", ret);
|
||||
goto err;
|
||||
return dev_err_probe(dev, ret, "Invalid label\n");
|
||||
}
|
||||
|
||||
if (val >= adc_info->max_channels) {
|
||||
dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
if (val >= adc_info->max_channels)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid channel %d\n", val);
|
||||
|
||||
differential = false;
|
||||
ret = fwnode_property_read_u32_array(child, "diff-channels", vin, 2);
|
||||
/* diff-channels is optional */
|
||||
if (!ret) {
|
||||
differential = true;
|
||||
if (vin[0] != val || vin[1] >= adc_info->max_channels) {
|
||||
dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n",
|
||||
vin[0], vin[1]);
|
||||
goto err;
|
||||
}
|
||||
if (vin[0] != val || vin[1] >= adc_info->max_channels)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid channel in%d-in%d\n",
|
||||
vin[0], vin[1]);
|
||||
} else if (ret != -EINVAL) {
|
||||
dev_err(&indio_dev->dev, "Invalid diff-channels property %d\n", ret);
|
||||
goto err;
|
||||
return dev_err_probe(dev, ret,
|
||||
"Invalid diff-channels property\n");
|
||||
}
|
||||
|
||||
stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
|
||||
@ -2247,11 +2241,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
|
||||
val = 0;
|
||||
ret = fwnode_property_read_u32(child, "st,min-sample-time-ns", &val);
|
||||
/* st,min-sample-time-ns is optional */
|
||||
if (ret && ret != -EINVAL) {
|
||||
dev_err(&indio_dev->dev, "Invalid st,min-sample-time-ns property %d\n",
|
||||
ret);
|
||||
goto err;
|
||||
}
|
||||
if (ret && ret != -EINVAL)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Invalid st,min-sample-time-ns property\n");
|
||||
|
||||
stm32_adc_smpr_init(adc, channels[scan_index].channel, val);
|
||||
if (differential)
|
||||
@ -2261,11 +2253,6 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
|
||||
}
|
||||
|
||||
return scan_index;
|
||||
|
||||
err:
|
||||
fwnode_handle_put(child);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stm32_adc_chan_fw_init(struct iio_dev *indio_dev, bool timestamping)
|
||||
|
@ -694,7 +694,6 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
|
||||
struct ads131e08_channel_config *channel_config;
|
||||
struct device *dev = &st->spi->dev;
|
||||
struct iio_chan_spec *channels;
|
||||
struct fwnode_handle *node;
|
||||
unsigned int channel, tmp;
|
||||
int num_channels, i, ret;
|
||||
|
||||
@ -736,10 +735,10 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
i = 0;
|
||||
device_for_each_child_node(dev, node) {
|
||||
device_for_each_child_node_scoped(dev, node) {
|
||||
ret = fwnode_property_read_u32(node, "reg", &channel);
|
||||
if (ret)
|
||||
goto err_child_out;
|
||||
return ret;
|
||||
|
||||
ret = fwnode_property_read_u32(node, "ti,gain", &tmp);
|
||||
if (ret) {
|
||||
@ -747,7 +746,7 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
|
||||
} else {
|
||||
ret = ads131e08_pga_gain_to_field_value(st, tmp);
|
||||
if (ret < 0)
|
||||
goto err_child_out;
|
||||
return ret;
|
||||
|
||||
channel_config[i].pga_gain = tmp;
|
||||
}
|
||||
@ -758,7 +757,7 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
|
||||
} else {
|
||||
ret = ads131e08_validate_channel_mux(st, tmp);
|
||||
if (ret)
|
||||
goto err_child_out;
|
||||
return ret;
|
||||
|
||||
channel_config[i].mux = tmp;
|
||||
}
|
||||
@ -785,9 +784,6 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev)
|
||||
|
||||
return 0;
|
||||
|
||||
err_child_out:
|
||||
fwnode_handle_put(node);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ads131e08_regulator_disable(void *data)
|
||||
|
@ -19,10 +19,12 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mfd/twl.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/bitops.h>
|
||||
@ -30,7 +32,6 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
@ -744,14 +745,14 @@ static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
|
||||
*/
|
||||
static int twl4030_madc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct twl4030_madc_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct twl4030_madc_data *madc;
|
||||
struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int irq, ret;
|
||||
u8 regval;
|
||||
struct iio_dev *iio_dev = NULL;
|
||||
|
||||
if (!pdata && !np) {
|
||||
if (!pdata && !dev_fwnode(dev)) {
|
||||
dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -779,7 +780,7 @@ static int twl4030_madc_probe(struct platform_device *pdev)
|
||||
if (pdata)
|
||||
madc->use_second_irq = (pdata->irq_line != 1);
|
||||
else
|
||||
madc->use_second_irq = of_property_read_bool(np,
|
||||
madc->use_second_irq = device_property_read_bool(dev,
|
||||
"ti,system-uses-second-madc-irq");
|
||||
|
||||
madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 :
|
||||
@ -905,20 +906,18 @@ static void twl4030_madc_remove(struct platform_device *pdev)
|
||||
regulator_disable(madc->usb3v1);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id twl_madc_of_match[] = {
|
||||
{ .compatible = "ti,twl4030-madc", },
|
||||
{ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, twl_madc_of_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver twl4030_madc_driver = {
|
||||
.probe = twl4030_madc_probe,
|
||||
.remove_new = twl4030_madc_remove,
|
||||
.driver = {
|
||||
.name = "twl4030_madc",
|
||||
.of_match_table = of_match_ptr(twl_madc_of_match),
|
||||
.of_match_table = twl_madc_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1255,21 +1255,15 @@ static int ad74413r_parse_channel_config(struct iio_dev *indio_dev,
|
||||
static int ad74413r_parse_channel_configs(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad74413r_state *st = iio_priv(indio_dev);
|
||||
struct fwnode_handle *channel_node = NULL;
|
||||
int ret;
|
||||
|
||||
fwnode_for_each_available_child_node(dev_fwnode(st->dev), channel_node) {
|
||||
device_for_each_child_node_scoped(st->dev, channel_node) {
|
||||
ret = ad74413r_parse_channel_config(indio_dev, channel_node);
|
||||
if (ret)
|
||||
goto put_channel_node;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
put_channel_node:
|
||||
fwnode_handle_put(channel_node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad74413r_setup_channels(struct iio_dev *indio_dev)
|
||||
|
@ -195,6 +195,18 @@ static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
|
||||
block->state = IIO_BLOCK_STATE_DONE;
|
||||
}
|
||||
|
||||
static void iio_dma_buffer_queue_wake(struct iio_dma_buffer_queue *queue)
|
||||
{
|
||||
__poll_t flags;
|
||||
|
||||
if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN)
|
||||
flags = EPOLLIN | EPOLLRDNORM;
|
||||
else
|
||||
flags = EPOLLOUT | EPOLLWRNORM;
|
||||
|
||||
wake_up_interruptible_poll(&queue->buffer.pollq, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_dma_buffer_block_done() - Indicate that a block has been completed
|
||||
* @block: The completed block
|
||||
@ -212,7 +224,7 @@ void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block)
|
||||
spin_unlock_irqrestore(&queue->list_lock, flags);
|
||||
|
||||
iio_buffer_block_put_atomic(block);
|
||||
wake_up_interruptible_poll(&queue->buffer.pollq, EPOLLIN | EPOLLRDNORM);
|
||||
iio_dma_buffer_queue_wake(queue);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_dma_buffer_block_done);
|
||||
|
||||
@ -241,7 +253,7 @@ void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue,
|
||||
}
|
||||
spin_unlock_irqrestore(&queue->list_lock, flags);
|
||||
|
||||
wake_up_interruptible_poll(&queue->buffer.pollq, EPOLLIN | EPOLLRDNORM);
|
||||
iio_dma_buffer_queue_wake(queue);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_dma_buffer_block_list_abort);
|
||||
|
||||
@ -335,8 +347,24 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer)
|
||||
queue->fileio.blocks[i] = block;
|
||||
}
|
||||
|
||||
block->state = IIO_BLOCK_STATE_QUEUED;
|
||||
list_add_tail(&block->head, &queue->incoming);
|
||||
/*
|
||||
* block->bytes_used may have been modified previously, e.g. by
|
||||
* iio_dma_buffer_block_list_abort(). Reset it here to the
|
||||
* block's so that iio_dma_buffer_io() will work.
|
||||
*/
|
||||
block->bytes_used = block->size;
|
||||
|
||||
/*
|
||||
* If it's an input buffer, mark the block as queued, and
|
||||
* iio_dma_buffer_enable() will submit it. Otherwise mark it as
|
||||
* done, which means it's ready to be dequeued.
|
||||
*/
|
||||
if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) {
|
||||
block->state = IIO_BLOCK_STATE_QUEUED;
|
||||
list_add_tail(&block->head, &queue->incoming);
|
||||
} else {
|
||||
block->state = IIO_BLOCK_STATE_DONE;
|
||||
}
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
@ -488,20 +516,12 @@ static struct iio_dma_buffer_block *iio_dma_buffer_dequeue(
|
||||
return block;
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_dma_buffer_read() - DMA buffer read callback
|
||||
* @buffer: Buffer to read form
|
||||
* @n: Number of bytes to read
|
||||
* @user_buffer: Userspace buffer to copy the data to
|
||||
*
|
||||
* Should be used as the read callback for iio_buffer_access_ops
|
||||
* struct for DMA buffers.
|
||||
*/
|
||||
int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
|
||||
char __user *user_buffer)
|
||||
static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n,
|
||||
char __user *user_buffer, bool is_from_user)
|
||||
{
|
||||
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer);
|
||||
struct iio_dma_buffer_block *block;
|
||||
void *addr;
|
||||
int ret;
|
||||
|
||||
if (n < buffer->bytes_per_datum)
|
||||
@ -524,8 +544,13 @@ int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
|
||||
n = rounddown(n, buffer->bytes_per_datum);
|
||||
if (n > block->bytes_used - queue->fileio.pos)
|
||||
n = block->bytes_used - queue->fileio.pos;
|
||||
addr = block->vaddr + queue->fileio.pos;
|
||||
|
||||
if (copy_to_user(user_buffer, block->vaddr + queue->fileio.pos, n)) {
|
||||
if (is_from_user)
|
||||
ret = copy_from_user(addr, user_buffer, n);
|
||||
else
|
||||
ret = copy_to_user(user_buffer, addr, n);
|
||||
if (ret) {
|
||||
ret = -EFAULT;
|
||||
goto out_unlock;
|
||||
}
|
||||
@ -544,16 +569,49 @@ out_unlock:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_dma_buffer_read() - DMA buffer read callback
|
||||
* @buffer: Buffer to read form
|
||||
* @n: Number of bytes to read
|
||||
* @user_buffer: Userspace buffer to copy the data to
|
||||
*
|
||||
* Should be used as the read callback for iio_buffer_access_ops
|
||||
* struct for DMA buffers.
|
||||
*/
|
||||
int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
|
||||
char __user *user_buffer)
|
||||
{
|
||||
return iio_dma_buffer_io(buffer, n, user_buffer, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_dma_buffer_read);
|
||||
|
||||
/**
|
||||
* iio_dma_buffer_data_available() - DMA buffer data_available callback
|
||||
* @buf: Buffer to check for data availability
|
||||
* iio_dma_buffer_write() - DMA buffer write callback
|
||||
* @buffer: Buffer to read form
|
||||
* @n: Number of bytes to read
|
||||
* @user_buffer: Userspace buffer to copy the data from
|
||||
*
|
||||
* Should be used as the data_available callback for iio_buffer_access_ops
|
||||
* Should be used as the write callback for iio_buffer_access_ops
|
||||
* struct for DMA buffers.
|
||||
*/
|
||||
size_t iio_dma_buffer_data_available(struct iio_buffer *buf)
|
||||
int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n,
|
||||
const char __user *user_buffer)
|
||||
{
|
||||
return iio_dma_buffer_io(buffer, n,
|
||||
(__force __user char *)user_buffer, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_dma_buffer_write);
|
||||
|
||||
/**
|
||||
* iio_dma_buffer_usage() - DMA buffer data_available and
|
||||
* space_available callback
|
||||
* @buf: Buffer to check for data availability
|
||||
*
|
||||
* Should be used as the data_available and space_available callbacks for
|
||||
* iio_buffer_access_ops struct for DMA buffers.
|
||||
*/
|
||||
size_t iio_dma_buffer_usage(struct iio_buffer *buf)
|
||||
{
|
||||
struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buf);
|
||||
struct iio_dma_buffer_block *block;
|
||||
@ -586,7 +644,7 @@ size_t iio_dma_buffer_data_available(struct iio_buffer *buf)
|
||||
|
||||
return data_available;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_dma_buffer_data_available);
|
||||
EXPORT_SYMBOL_GPL(iio_dma_buffer_usage);
|
||||
|
||||
/**
|
||||
* iio_dma_buffer_set_bytes_per_datum() - DMA buffer set_bytes_per_datum callback
|
||||
|
@ -64,14 +64,25 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue,
|
||||
struct dmaengine_buffer *dmaengine_buffer =
|
||||
iio_buffer_to_dmaengine_buffer(&queue->buffer);
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
enum dma_transfer_direction dma_dir;
|
||||
size_t max_size;
|
||||
dma_cookie_t cookie;
|
||||
|
||||
block->bytes_used = min(block->size, dmaengine_buffer->max_size);
|
||||
block->bytes_used = round_down(block->bytes_used,
|
||||
dmaengine_buffer->align);
|
||||
max_size = min(block->size, dmaengine_buffer->max_size);
|
||||
max_size = round_down(max_size, dmaengine_buffer->align);
|
||||
|
||||
if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) {
|
||||
block->bytes_used = max_size;
|
||||
dma_dir = DMA_DEV_TO_MEM;
|
||||
} else {
|
||||
dma_dir = DMA_MEM_TO_DEV;
|
||||
}
|
||||
|
||||
if (!block->bytes_used || block->bytes_used > max_size)
|
||||
return -EINVAL;
|
||||
|
||||
desc = dmaengine_prep_slave_single(dmaengine_buffer->chan,
|
||||
block->phys_addr, block->bytes_used, DMA_DEV_TO_MEM,
|
||||
block->phys_addr, block->bytes_used, dma_dir,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!desc)
|
||||
return -ENOMEM;
|
||||
@ -112,12 +123,14 @@ static void iio_dmaengine_buffer_release(struct iio_buffer *buf)
|
||||
|
||||
static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = {
|
||||
.read = iio_dma_buffer_read,
|
||||
.write = iio_dma_buffer_write,
|
||||
.set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum,
|
||||
.set_length = iio_dma_buffer_set_length,
|
||||
.request_update = iio_dma_buffer_request_update,
|
||||
.enable = iio_dma_buffer_enable,
|
||||
.disable = iio_dma_buffer_disable,
|
||||
.data_available = iio_dma_buffer_data_available,
|
||||
.data_available = iio_dma_buffer_usage,
|
||||
.space_available = iio_dma_buffer_usage,
|
||||
.release = iio_dmaengine_buffer_release,
|
||||
|
||||
.modes = INDIO_BUFFER_HARDWARE,
|
||||
@ -159,7 +172,7 @@ static const struct iio_dev_attr *iio_dmaengine_buffer_attrs[] = {
|
||||
* Once done using the buffer iio_dmaengine_buffer_free() should be used to
|
||||
* release it.
|
||||
*/
|
||||
struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
|
||||
static struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
|
||||
const char *channel)
|
||||
{
|
||||
struct dmaengine_buffer *dmaengine_buffer;
|
||||
@ -210,7 +223,6 @@ err_free:
|
||||
kfree(dmaengine_buffer);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_alloc, IIO_DMAENGINE_BUFFER);
|
||||
|
||||
/**
|
||||
* iio_dmaengine_buffer_free() - Free dmaengine buffer
|
||||
@ -230,66 +242,64 @@ void iio_dmaengine_buffer_free(struct iio_buffer *buffer)
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_free, IIO_DMAENGINE_BUFFER);
|
||||
|
||||
static void __devm_iio_dmaengine_buffer_free(void *buffer)
|
||||
{
|
||||
iio_dmaengine_buffer_free(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_iio_dmaengine_buffer_alloc() - Resource-managed iio_dmaengine_buffer_alloc()
|
||||
* @dev: Parent device for the buffer
|
||||
* @channel: DMA channel name, typically "rx".
|
||||
*
|
||||
* This allocates a new IIO buffer which internally uses the DMAengine framework
|
||||
* to perform its transfers. The parent device will be used to request the DMA
|
||||
* channel.
|
||||
*
|
||||
* The buffer will be automatically de-allocated once the device gets destroyed.
|
||||
*/
|
||||
static struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev,
|
||||
const char *channel)
|
||||
struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev,
|
||||
struct iio_dev *indio_dev,
|
||||
const char *channel,
|
||||
enum iio_buffer_direction dir)
|
||||
{
|
||||
struct iio_buffer *buffer;
|
||||
int ret;
|
||||
|
||||
buffer = iio_dmaengine_buffer_alloc(dev, channel);
|
||||
if (IS_ERR(buffer))
|
||||
return buffer;
|
||||
return ERR_CAST(buffer);
|
||||
|
||||
ret = devm_add_action_or_reset(dev, __devm_iio_dmaengine_buffer_free,
|
||||
buffer);
|
||||
if (ret)
|
||||
indio_dev->modes |= INDIO_BUFFER_HARDWARE;
|
||||
|
||||
buffer->direction = dir;
|
||||
|
||||
ret = iio_device_attach_buffer(indio_dev, buffer);
|
||||
if (ret) {
|
||||
iio_dmaengine_buffer_free(buffer);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_setup_ext, IIO_DMAENGINE_BUFFER);
|
||||
|
||||
static void __devm_iio_dmaengine_buffer_free(void *buffer)
|
||||
{
|
||||
iio_dmaengine_buffer_free(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_iio_dmaengine_buffer_setup() - Setup a DMA buffer for an IIO device
|
||||
* devm_iio_dmaengine_buffer_setup_ext() - Setup a DMA buffer for an IIO device
|
||||
* @dev: Parent device for the buffer
|
||||
* @indio_dev: IIO device to which to attach this buffer.
|
||||
* @channel: DMA channel name, typically "rx".
|
||||
* @dir: Direction of buffer (in or out)
|
||||
*
|
||||
* This allocates a new IIO buffer with devm_iio_dmaengine_buffer_alloc()
|
||||
* and attaches it to an IIO device with iio_device_attach_buffer().
|
||||
* It also appends the INDIO_BUFFER_HARDWARE mode to the supported modes of the
|
||||
* IIO device.
|
||||
*/
|
||||
int devm_iio_dmaengine_buffer_setup(struct device *dev,
|
||||
struct iio_dev *indio_dev,
|
||||
const char *channel)
|
||||
int devm_iio_dmaengine_buffer_setup_ext(struct device *dev,
|
||||
struct iio_dev *indio_dev,
|
||||
const char *channel,
|
||||
enum iio_buffer_direction dir)
|
||||
{
|
||||
struct iio_buffer *buffer;
|
||||
|
||||
buffer = devm_iio_dmaengine_buffer_alloc(dev, channel);
|
||||
buffer = iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, dir);
|
||||
if (IS_ERR(buffer))
|
||||
return PTR_ERR(buffer);
|
||||
|
||||
indio_dev->modes |= INDIO_BUFFER_HARDWARE;
|
||||
|
||||
return iio_device_attach_buffer(indio_dev, buffer);
|
||||
return devm_add_action_or_reset(dev, __devm_iio_dmaengine_buffer_free,
|
||||
buffer);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup, IIO_DMAENGINE_BUFFER);
|
||||
EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup_ext, IIO_DMAENGINE_BUFFER);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("DMA buffer for the IIO framework");
|
||||
|
@ -131,6 +131,43 @@ config AD5624R_SPI
|
||||
Say yes here to build support for Analog Devices AD5624R, AD5644R and
|
||||
AD5664R converters (DAC). This driver uses the common SPI interface.
|
||||
|
||||
config AD9739A
|
||||
tristate "Analog Devices AD9739A RF DAC spi driver"
|
||||
depends on SPI || COMPILE_TEST
|
||||
select REGMAP_SPI
|
||||
select IIO_BACKEND
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD9739A Digital-to
|
||||
Analog Converter.
|
||||
|
||||
The driver requires the assistance of the AXI DAC IP core to operate,
|
||||
since SPI is used for configuration only, while data has to be
|
||||
streamed into memory via DMA.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ad9739a.
|
||||
|
||||
config ADI_AXI_DAC
|
||||
tristate "Analog Devices Generic AXI DAC IP core driver"
|
||||
select IIO_BUFFER
|
||||
select IIO_BUFFER_DMAENGINE
|
||||
select REGMAP_MMIO
|
||||
select IIO_BACKEND
|
||||
help
|
||||
Say yes here to build support for Analog Devices Generic
|
||||
AXI DAC IP core. The IP core is used for interfacing with
|
||||
digital-to-analog (DAC) converters that require either a high-speed
|
||||
serial interface (JESD204B/C) or a source synchronous parallel
|
||||
interface (LVDS/CMOS).
|
||||
Typically (for such devices) SPI will be used for configuration only,
|
||||
while this IP core handles the streaming of data into memory via DMA.
|
||||
|
||||
Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
|
||||
If unsure, say N (but it's safe to say "Y").
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adi-axi-dac.
|
||||
|
||||
config LTC2688
|
||||
tristate "Analog Devices LTC2688 DAC spi driver"
|
||||
depends on SPI
|
||||
|
@ -29,6 +29,8 @@ obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o
|
||||
obj-$(CONFIG_AD7293) += ad7293.o
|
||||
obj-$(CONFIG_AD7303) += ad7303.o
|
||||
obj-$(CONFIG_AD8801) += ad8801.o
|
||||
obj-$(CONFIG_AD9739A) += ad9739a.o
|
||||
obj-$(CONFIG_ADI_AXI_DAC) += adi-axi-dac.o
|
||||
obj-$(CONFIG_CIO_DAC) += cio-dac.o
|
||||
obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
|
||||
obj-$(CONFIG_DS4424) += ds4424.o
|
||||
|
@ -801,51 +801,45 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
|
||||
u32 ch)
|
||||
{
|
||||
struct device *dev = &dac->spi->dev;
|
||||
struct fwnode_handle *gain_child;
|
||||
u32 val;
|
||||
int err;
|
||||
u8 addr;
|
||||
u16 reg = 0, offset;
|
||||
|
||||
gain_child = fwnode_get_named_child_node(child,
|
||||
"custom-output-range-config");
|
||||
if (!gain_child) {
|
||||
dev_err(dev,
|
||||
"mandatory custom-output-range-config property missing\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
struct fwnode_handle *gain_child __free(fwnode_handle)
|
||||
= fwnode_get_named_child_node(child,
|
||||
"custom-output-range-config");
|
||||
if (!gain_child)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"mandatory custom-output-range-config property missing\n");
|
||||
|
||||
dac->ch_data[ch].range_override = 1;
|
||||
reg |= ad3552r_field_prep(1, AD3552R_MASK_CH_RANGE_OVERRIDE);
|
||||
|
||||
err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val);
|
||||
if (err) {
|
||||
dev_err(dev, "mandatory adi,gain-scaling-p property missing\n");
|
||||
goto put_child;
|
||||
}
|
||||
if (err)
|
||||
return dev_err_probe(dev, err,
|
||||
"mandatory adi,gain-scaling-p property missing\n");
|
||||
reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_P);
|
||||
dac->ch_data[ch].p = val;
|
||||
|
||||
err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val);
|
||||
if (err) {
|
||||
dev_err(dev, "mandatory adi,gain-scaling-n property missing\n");
|
||||
goto put_child;
|
||||
}
|
||||
if (err)
|
||||
return dev_err_probe(dev, err,
|
||||
"mandatory adi,gain-scaling-n property missing\n");
|
||||
reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_N);
|
||||
dac->ch_data[ch].n = val;
|
||||
|
||||
err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val);
|
||||
if (err) {
|
||||
dev_err(dev, "mandatory adi,rfb-ohms property missing\n");
|
||||
goto put_child;
|
||||
}
|
||||
if (err)
|
||||
return dev_err_probe(dev, err,
|
||||
"mandatory adi,rfb-ohms property missing\n");
|
||||
dac->ch_data[ch].rfb = val;
|
||||
|
||||
err = fwnode_property_read_u32(gain_child, "adi,gain-offset", &val);
|
||||
if (err) {
|
||||
dev_err(dev, "mandatory adi,gain-offset property missing\n");
|
||||
goto put_child;
|
||||
}
|
||||
if (err)
|
||||
return dev_err_probe(dev, err,
|
||||
"mandatory adi,gain-offset property missing\n");
|
||||
dac->ch_data[ch].gain_offset = val;
|
||||
|
||||
offset = abs((s32)val);
|
||||
@ -855,21 +849,14 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
|
||||
addr = AD3552R_REG_ADDR_CH_GAIN(ch);
|
||||
err = ad3552r_write_reg(dac, addr,
|
||||
offset & AD3552R_MASK_CH_OFFSET_BITS_0_7);
|
||||
if (err) {
|
||||
dev_err(dev, "Error writing register\n");
|
||||
goto put_child;
|
||||
}
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "Error writing register\n");
|
||||
|
||||
err = ad3552r_write_reg(dac, addr, reg);
|
||||
if (err) {
|
||||
dev_err(dev, "Error writing register\n");
|
||||
goto put_child;
|
||||
}
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "Error writing register\n");
|
||||
|
||||
put_child:
|
||||
fwnode_handle_put(gain_child);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ad3552r_reg_disable(void *reg)
|
||||
@ -880,7 +867,6 @@ static void ad3552r_reg_disable(void *reg)
|
||||
static int ad3552r_configure_device(struct ad3552r_desc *dac)
|
||||
{
|
||||
struct device *dev = &dac->spi->dev;
|
||||
struct fwnode_handle *child;
|
||||
struct regulator *vref;
|
||||
int err, cnt = 0, voltage, delta = 100000;
|
||||
u32 vals[2], val, ch;
|
||||
@ -949,53 +935,45 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
device_for_each_child_node(dev, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
err = fwnode_property_read_u32(child, "reg", &ch);
|
||||
if (err) {
|
||||
dev_err(dev, "mandatory reg property missing\n");
|
||||
goto put_child;
|
||||
}
|
||||
if (ch >= AD3552R_NUM_CH) {
|
||||
dev_err(dev, "reg must be less than %d\n",
|
||||
AD3552R_NUM_CH);
|
||||
err = -EINVAL;
|
||||
goto put_child;
|
||||
}
|
||||
if (err)
|
||||
return dev_err_probe(dev, err,
|
||||
"mandatory reg property missing\n");
|
||||
if (ch >= AD3552R_NUM_CH)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"reg must be less than %d\n",
|
||||
AD3552R_NUM_CH);
|
||||
|
||||
if (fwnode_property_present(child, "adi,output-range-microvolt")) {
|
||||
err = fwnode_property_read_u32_array(child,
|
||||
"adi,output-range-microvolt",
|
||||
vals,
|
||||
2);
|
||||
if (err) {
|
||||
dev_err(dev,
|
||||
if (err)
|
||||
return dev_err_probe(dev, err,
|
||||
"adi,output-range-microvolt property could not be parsed\n");
|
||||
goto put_child;
|
||||
}
|
||||
|
||||
err = ad3552r_find_range(dac->chip_id, vals);
|
||||
if (err < 0) {
|
||||
dev_err(dev,
|
||||
"Invalid adi,output-range-microvolt value\n");
|
||||
goto put_child;
|
||||
}
|
||||
if (err < 0)
|
||||
return dev_err_probe(dev, err,
|
||||
"Invalid adi,output-range-microvolt value\n");
|
||||
|
||||
val = err;
|
||||
err = ad3552r_set_ch_value(dac,
|
||||
AD3552R_CH_OUTPUT_RANGE_SEL,
|
||||
ch, val);
|
||||
if (err)
|
||||
goto put_child;
|
||||
return err;
|
||||
|
||||
dac->ch_data[ch].range = val;
|
||||
} else if (dac->chip_id == AD3542R_ID) {
|
||||
dev_err(dev,
|
||||
"adi,output-range-microvolt is required for ad3542r\n");
|
||||
err = -EINVAL;
|
||||
goto put_child;
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"adi,output-range-microvolt is required for ad3542r\n");
|
||||
} else {
|
||||
err = ad3552r_configure_custom_gain(dac, child, ch);
|
||||
if (err)
|
||||
goto put_child;
|
||||
return err;
|
||||
}
|
||||
|
||||
ad3552r_calc_gain_and_offset(dac, ch);
|
||||
@ -1003,7 +981,7 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
|
||||
|
||||
err = ad3552r_set_ch_value(dac, AD3552R_CH_SELECT, ch, 1);
|
||||
if (err < 0)
|
||||
goto put_child;
|
||||
return err;
|
||||
|
||||
dac->channels[cnt] = AD3552R_CH_DAC(ch);
|
||||
++cnt;
|
||||
@ -1021,10 +999,6 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
|
||||
dac->num_ch = cnt;
|
||||
|
||||
return 0;
|
||||
put_child:
|
||||
fwnode_handle_put(child);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ad3552r_init(struct ad3552r_desc *dac)
|
||||
|
@ -809,7 +809,6 @@ static struct ad5755_platform_data *ad5755_parse_fw(struct device *dev)
|
||||
|
||||
static int ad5755_probe(struct spi_device *spi)
|
||||
{
|
||||
enum ad5755_type type = spi_get_device_id(spi)->driver_data;
|
||||
const struct ad5755_platform_data *pdata;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5755_state *st;
|
||||
@ -824,7 +823,7 @@ static int ad5755_probe(struct spi_device *spi)
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->chip_info = &ad5755_chip_info_tbl[type];
|
||||
st->chip_info = spi_get_device_match_data(spi);
|
||||
st->spi = spi;
|
||||
st->pwr_down = 0xf;
|
||||
|
||||
@ -854,21 +853,21 @@ static int ad5755_probe(struct spi_device *spi)
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5755_id[] = {
|
||||
{ "ad5755", ID_AD5755 },
|
||||
{ "ad5755-1", ID_AD5755 },
|
||||
{ "ad5757", ID_AD5757 },
|
||||
{ "ad5735", ID_AD5735 },
|
||||
{ "ad5737", ID_AD5737 },
|
||||
{ "ad5755", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] },
|
||||
{ "ad5755-1", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] },
|
||||
{ "ad5757", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5757] },
|
||||
{ "ad5735", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5735] },
|
||||
{ "ad5737", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5737] },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5755_id);
|
||||
|
||||
static const struct of_device_id ad5755_of_match[] = {
|
||||
{ .compatible = "adi,ad5755" },
|
||||
{ .compatible = "adi,ad5755-1" },
|
||||
{ .compatible = "adi,ad5757" },
|
||||
{ .compatible = "adi,ad5735" },
|
||||
{ .compatible = "adi,ad5737" },
|
||||
{ .compatible = "adi,ad5755", &ad5755_chip_info_tbl[ID_AD5755] },
|
||||
{ .compatible = "adi,ad5755-1", &ad5755_chip_info_tbl[ID_AD5755] },
|
||||
{ .compatible = "adi,ad5757", &ad5755_chip_info_tbl[ID_AD5757] },
|
||||
{ .compatible = "adi,ad5735", &ad5755_chip_info_tbl[ID_AD5735] },
|
||||
{ .compatible = "adi,ad5737", &ad5755_chip_info_tbl[ID_AD5737] },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad5755_of_match);
|
||||
@ -876,6 +875,7 @@ MODULE_DEVICE_TABLE(of, ad5755_of_match);
|
||||
static struct spi_driver ad5755_driver = {
|
||||
.driver = {
|
||||
.name = "ad5755",
|
||||
.of_match_table = ad5755_of_match,
|
||||
},
|
||||
.probe = ad5755_probe,
|
||||
.id_table = ad5755_id,
|
||||
|
@ -515,39 +515,32 @@ static int ad5770r_channel_config(struct ad5770r_state *st)
|
||||
{
|
||||
int ret, tmp[2], min, max;
|
||||
unsigned int num;
|
||||
struct fwnode_handle *child;
|
||||
|
||||
num = device_get_child_node_count(&st->spi->dev);
|
||||
if (num != AD5770R_MAX_CHANNELS)
|
||||
return -EINVAL;
|
||||
|
||||
device_for_each_child_node(&st->spi->dev, child) {
|
||||
device_for_each_child_node_scoped(&st->spi->dev, child) {
|
||||
ret = fwnode_property_read_u32(child, "reg", &num);
|
||||
if (ret)
|
||||
goto err_child_out;
|
||||
if (num >= AD5770R_MAX_CHANNELS) {
|
||||
ret = -EINVAL;
|
||||
goto err_child_out;
|
||||
}
|
||||
return ret;
|
||||
if (num >= AD5770R_MAX_CHANNELS)
|
||||
return -EINVAL;
|
||||
|
||||
ret = fwnode_property_read_u32_array(child,
|
||||
"adi,range-microamp",
|
||||
tmp, 2);
|
||||
if (ret)
|
||||
goto err_child_out;
|
||||
return ret;
|
||||
|
||||
min = tmp[0] / 1000;
|
||||
max = tmp[1] / 1000;
|
||||
ret = ad5770r_store_output_range(st, min, max, num);
|
||||
if (ret)
|
||||
goto err_child_out;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_child_out:
|
||||
fwnode_handle_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5770r_init(struct ad5770r_state *st)
|
||||
|
463
drivers/iio/dac/ad9739a.c
Normal file
463
drivers/iio/dac/ad9739a.c
Normal file
@ -0,0 +1,463 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Analog Devices AD9739a SPI DAC driver
|
||||
*
|
||||
* Copyright 2015-2024 Analog Devices Inc.
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include <linux/iio/backend.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/types.h>
|
||||
|
||||
#define AD9739A_REG_MODE 0
|
||||
#define AD9739A_RESET_MASK BIT(5)
|
||||
#define AD9739A_REG_FSC_1 0x06
|
||||
#define AD9739A_REG_FSC_2 0x07
|
||||
#define AD9739A_FSC_MSB GENMASK(1, 0)
|
||||
#define AD9739A_REG_DEC_CNT 0x8
|
||||
#define AD9739A_NORMAL_MODE 0
|
||||
#define AD9739A_MIXED_MODE 2
|
||||
#define AD9739A_DAC_DEC GENMASK(1, 0)
|
||||
#define AD9739A_REG_LVDS_REC_CNT1 0x10
|
||||
#define AD9739A_RCVR_LOOP_EN_MASK GENMASK(1, 0)
|
||||
#define AD9739A_REG_LVDS_REC_CNT4 0x13
|
||||
#define AD9739A_FINE_DEL_SKW_MASK GENMASK(3, 0)
|
||||
#define AD9739A_REG_LVDS_REC_STAT9 0x21
|
||||
#define AD9739A_RCVR_TRACK_AND_LOCK (BIT(3) | BIT(0))
|
||||
#define AD9739A_REG_CROSS_CNT1 0x22
|
||||
#define AD9739A_REG_CROSS_CNT2 0x23
|
||||
#define AD9739A_REG_PHS_DET 0x24
|
||||
#define AD9739A_REG_MU_DUTY 0x25
|
||||
#define AD9739A_REG_MU_CNT1 0x26
|
||||
#define AD9739A_MU_EN_MASK BIT(0)
|
||||
#define AD9739A_REG_MU_CNT2 0x27
|
||||
#define AD9739A_REG_MU_CNT3 0x28
|
||||
#define AD9739A_REG_MU_CNT4 0x29
|
||||
#define AD9739A_MU_CNT4_DEFAULT 0xcb
|
||||
#define AD9739A_REG_MU_STAT1 0x2A
|
||||
#define AD9739A_MU_LOCK_MASK BIT(0)
|
||||
#define AD9739A_REG_ANA_CNT_1 0x32
|
||||
#define AD9739A_REG_ID 0x35
|
||||
|
||||
#define AD9739A_ID 0x24
|
||||
#define AD9739A_REG_IS_RESERVED(reg) \
|
||||
((reg) == 0x5 || (reg) == 0x9 || (reg) == 0x0E || (reg) == 0x0D || \
|
||||
(reg) == 0x2B || (reg) == 0x2C || (reg) == 0x34)
|
||||
|
||||
#define AD9739A_FSC_MIN 8580
|
||||
#define AD9739A_FSC_MAX 31700
|
||||
#define AD9739A_FSC_RANGE (AD9739A_FSC_MAX - AD9739A_FSC_MIN + 1)
|
||||
|
||||
#define AD9739A_MIN_DAC_CLK (1600 * MEGA)
|
||||
#define AD9739A_MAX_DAC_CLK (2500 * MEGA)
|
||||
#define AD9739A_DAC_CLK_RANGE (AD9739A_MAX_DAC_CLK - AD9739A_MIN_DAC_CLK + 1)
|
||||
/* as recommended by the datasheet */
|
||||
#define AD9739A_LOCK_N_TRIES 3
|
||||
|
||||
struct ad9739a_state {
|
||||
struct iio_backend *back;
|
||||
struct regmap *regmap;
|
||||
unsigned long sample_rate;
|
||||
};
|
||||
|
||||
static int ad9739a_oper_mode_get(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad9739a_state *st = iio_priv(indio_dev);
|
||||
u32 mode;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(st->regmap, AD9739A_REG_DEC_CNT, &mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mode = FIELD_GET(AD9739A_DAC_DEC, mode);
|
||||
/* sanity check we get valid values from the HW */
|
||||
if (mode != AD9739A_NORMAL_MODE && mode != AD9739A_MIXED_MODE)
|
||||
return -EIO;
|
||||
if (!mode)
|
||||
return AD9739A_NORMAL_MODE;
|
||||
|
||||
/*
|
||||
* We get 2 from the device but for IIO modes, that means 1. Hence the
|
||||
* minus 1.
|
||||
*/
|
||||
return AD9739A_MIXED_MODE - 1;
|
||||
}
|
||||
|
||||
static int ad9739a_oper_mode_set(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, u32 mode)
|
||||
{
|
||||
struct ad9739a_state *st = iio_priv(indio_dev);
|
||||
|
||||
/*
|
||||
* On the IIO interface we have 0 and 1 for mode. But for mixed_mode, we
|
||||
* need to write 2 in the device. That's what the below check is about.
|
||||
*/
|
||||
if (mode == AD9739A_MIXED_MODE - 1)
|
||||
mode = AD9739A_MIXED_MODE;
|
||||
|
||||
return regmap_update_bits(st->regmap, AD9739A_REG_DEC_CNT,
|
||||
AD9739A_DAC_DEC, mode);
|
||||
}
|
||||
|
||||
static int ad9739a_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct ad9739a_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = st->sample_rate;
|
||||
*val2 = 0;
|
||||
return IIO_VAL_INT_64;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad9739a_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad9739a_state *st = iio_priv(indio_dev);
|
||||
|
||||
return iio_backend_data_source_set(st->back, 0, IIO_BACKEND_EXTERNAL);
|
||||
}
|
||||
|
||||
static int ad9739a_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad9739a_state *st = iio_priv(indio_dev);
|
||||
|
||||
return iio_backend_data_source_set(st->back, 0,
|
||||
IIO_BACKEND_INTERNAL_CONTINUOS_WAVE);
|
||||
}
|
||||
|
||||
static bool ad9739a_reg_accessible(struct device *dev, unsigned int reg)
|
||||
{
|
||||
if (AD9739A_REG_IS_RESERVED(reg))
|
||||
return false;
|
||||
if (reg > AD9739A_REG_MU_STAT1 && reg < AD9739A_REG_ANA_CNT_1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int ad9739a_reset(struct device *dev, const struct ad9739a_state *st)
|
||||
{
|
||||
struct gpio_desc *gpio;
|
||||
int ret;
|
||||
|
||||
gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
if (gpio) {
|
||||
/* minimum pulse width of 40ns */
|
||||
ndelay(40);
|
||||
gpiod_set_value_cansleep(gpio, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* bring all registers to their default state */
|
||||
ret = regmap_set_bits(st->regmap, AD9739A_REG_MODE, AD9739A_RESET_MASK);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ndelay(40);
|
||||
|
||||
return regmap_clear_bits(st->regmap, AD9739A_REG_MODE,
|
||||
AD9739A_RESET_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Recommended values (as per datasheet) for the dac clk common mode voltage
|
||||
* and Mu controller. Look at table 29.
|
||||
*/
|
||||
static const struct reg_sequence ad9739a_clk_mu_ctrl[] = {
|
||||
/* DAC clk common mode voltage */
|
||||
{ AD9739A_REG_CROSS_CNT1, 0x0f },
|
||||
{ AD9739A_REG_CROSS_CNT2, 0x0f },
|
||||
/* Mu controller configuration */
|
||||
{ AD9739A_REG_PHS_DET, 0x30 },
|
||||
{ AD9739A_REG_MU_DUTY, 0x80 },
|
||||
{ AD9739A_REG_MU_CNT2, 0x44 },
|
||||
{ AD9739A_REG_MU_CNT3, 0x6c },
|
||||
};
|
||||
|
||||
static int ad9739a_init(struct device *dev, const struct ad9739a_state *st)
|
||||
{
|
||||
unsigned int i = 0, lock, fsc;
|
||||
u32 fsc_raw;
|
||||
int ret;
|
||||
|
||||
ret = regmap_multi_reg_write(st->regmap, ad9739a_clk_mu_ctrl,
|
||||
ARRAY_SIZE(ad9739a_clk_mu_ctrl));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Try to get the Mu lock. Repeat the below steps AD9739A_LOCK_N_TRIES
|
||||
* (as specified by the datasheet) until we get the lock.
|
||||
*/
|
||||
do {
|
||||
ret = regmap_write(st->regmap, AD9739A_REG_MU_CNT4,
|
||||
AD9739A_MU_CNT4_DEFAULT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable the Mu controller search and track mode. */
|
||||
ret = regmap_set_bits(st->regmap, AD9739A_REG_MU_CNT1,
|
||||
AD9739A_MU_EN_MASK);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Ensure the DLL loop is locked */
|
||||
ret = regmap_read_poll_timeout(st->regmap, AD9739A_REG_MU_STAT1,
|
||||
lock, lock & AD9739A_MU_LOCK_MASK,
|
||||
0, 1000);
|
||||
if (ret && ret != -ETIMEDOUT)
|
||||
return ret;
|
||||
} while (ret && ++i < AD9739A_LOCK_N_TRIES);
|
||||
|
||||
if (i == AD9739A_LOCK_N_TRIES)
|
||||
return dev_err_probe(dev, ret, "Mu lock timeout\n");
|
||||
|
||||
/* Receiver tracking and lock. Same deal as the Mu controller */
|
||||
i = 0;
|
||||
do {
|
||||
ret = regmap_update_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT4,
|
||||
AD9739A_FINE_DEL_SKW_MASK,
|
||||
FIELD_PREP(AD9739A_FINE_DEL_SKW_MASK, 2));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disable the receiver and the loop. */
|
||||
ret = regmap_write(st->regmap, AD9739A_REG_LVDS_REC_CNT1, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Re-enable the loop so it falls out of lock and begins the
|
||||
* search/track routine again.
|
||||
*/
|
||||
ret = regmap_set_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT1,
|
||||
AD9739A_RCVR_LOOP_EN_MASK);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Ensure the DLL loop is locked */
|
||||
ret = regmap_read_poll_timeout(st->regmap,
|
||||
AD9739A_REG_LVDS_REC_STAT9, lock,
|
||||
lock == AD9739A_RCVR_TRACK_AND_LOCK,
|
||||
0, 1000);
|
||||
if (ret && ret != -ETIMEDOUT)
|
||||
return ret;
|
||||
} while (ret && ++i < AD9739A_LOCK_N_TRIES);
|
||||
|
||||
if (i == AD9739A_LOCK_N_TRIES)
|
||||
return dev_err_probe(dev, ret, "Receiver lock timeout\n");
|
||||
|
||||
ret = device_property_read_u32(dev, "adi,full-scale-microamp", &fsc);
|
||||
if (ret && ret == -EINVAL)
|
||||
return 0;
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!in_range(fsc, AD9739A_FSC_MIN, AD9739A_FSC_RANGE))
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid full scale current(%u) [%u %u]\n",
|
||||
fsc, AD9739A_FSC_MIN, AD9739A_FSC_MAX);
|
||||
/*
|
||||
* IOUTFS is given by
|
||||
* Ioutfs = 0.0226 * FSC + 8.58
|
||||
* and is given in mA. Hence we'll have to multiply by 10 * MILLI in
|
||||
* order to get rid of the fractional.
|
||||
*/
|
||||
fsc_raw = DIV_ROUND_CLOSEST(fsc * 10 - 85800, 226);
|
||||
|
||||
ret = regmap_write(st->regmap, AD9739A_REG_FSC_1, fsc_raw & 0xff);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_update_bits(st->regmap, AD9739A_REG_FSC_2,
|
||||
AD9739A_FSC_MSB, fsc_raw >> 8);
|
||||
}
|
||||
|
||||
static const char * const ad9739a_modes_avail[] = { "normal", "mixed-mode" };
|
||||
|
||||
static const struct iio_enum ad9739a_modes = {
|
||||
.items = ad9739a_modes_avail,
|
||||
.num_items = ARRAY_SIZE(ad9739a_modes_avail),
|
||||
.get = ad9739a_oper_mode_get,
|
||||
.set = ad9739a_oper_mode_set,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad9739a_ext_info[] = {
|
||||
IIO_ENUM_AVAILABLE("operating_mode", IIO_SEPARATE, &ad9739a_modes),
|
||||
IIO_ENUM("operating_mode", IIO_SEPARATE, &ad9739a_modes),
|
||||
{ }
|
||||
};
|
||||
|
||||
/*
|
||||
* The reason for having two different channels is because we have, in reality,
|
||||
* two sources of data:
|
||||
* ALTVOLTAGE: It's a Continuous Wave that's internally generated by the
|
||||
* backend device.
|
||||
* VOLTAGE: It's the typical data we can have in a DAC device and the source
|
||||
* of it has nothing to do with the backend. The backend will only
|
||||
* forward it into our data interface to be sent out.
|
||||
*/
|
||||
static struct iio_chan_spec ad9739a_channels[] = {
|
||||
{
|
||||
.type = IIO_ALTVOLTAGE,
|
||||
.indexed = 1,
|
||||
.output = 1,
|
||||
.scan_index = -1,
|
||||
},
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.output = 1,
|
||||
.ext_info = ad9739a_ext_info,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.storagebits = 16,
|
||||
.realbits = 16,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_info ad9739a_info = {
|
||||
.read_raw = ad9739a_read_raw,
|
||||
};
|
||||
|
||||
static const struct iio_buffer_setup_ops ad9739a_buffer_setup_ops = {
|
||||
.preenable = &ad9739a_buffer_preenable,
|
||||
.postdisable = &ad9739a_buffer_postdisable,
|
||||
};
|
||||
|
||||
static const struct regmap_config ad9739a_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.readable_reg = ad9739a_reg_accessible,
|
||||
.writeable_reg = ad9739a_reg_accessible,
|
||||
.max_register = AD9739A_REG_ID,
|
||||
};
|
||||
|
||||
static int ad9739a_probe(struct spi_device *spi)
|
||||
{
|
||||
struct device *dev = &spi->dev;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad9739a_state *st;
|
||||
unsigned int id;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
clk = devm_clk_get_enabled(dev, NULL);
|
||||
if (IS_ERR(clk))
|
||||
return dev_err_probe(dev, PTR_ERR(clk), "Could not get clkin\n");
|
||||
|
||||
st->sample_rate = clk_get_rate(clk);
|
||||
if (!in_range(st->sample_rate, AD9739A_MIN_DAC_CLK,
|
||||
AD9739A_DAC_CLK_RANGE))
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid dac clk range(%lu) [%lu %lu]\n",
|
||||
st->sample_rate, AD9739A_MIN_DAC_CLK,
|
||||
AD9739A_MAX_DAC_CLK);
|
||||
|
||||
st->regmap = devm_regmap_init_spi(spi, &ad9739a_regmap_config);
|
||||
if (IS_ERR(st->regmap))
|
||||
return PTR_ERR(st->regmap);
|
||||
|
||||
ret = regmap_read(st->regmap, AD9739A_REG_ID, &id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (id != AD9739A_ID)
|
||||
dev_warn(dev, "Unrecognized CHIP_ID 0x%X", id);
|
||||
|
||||
ret = ad9739a_reset(dev, st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad9739a_init(dev, st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->back = devm_iio_backend_get(dev, NULL);
|
||||
if (IS_ERR(st->back))
|
||||
return PTR_ERR(st->back);
|
||||
|
||||
ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_backend_extend_chan_spec(indio_dev, st->back,
|
||||
&ad9739a_channels[0]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_backend_set_sampling_freq(st->back, 0, st->sample_rate);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_iio_backend_enable(dev, st->back);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->name = "ad9739a";
|
||||
indio_dev->info = &ad9739a_info;
|
||||
indio_dev->channels = ad9739a_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad9739a_channels);
|
||||
indio_dev->setup_ops = &ad9739a_buffer_setup_ops;
|
||||
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id ad9739a_of_match[] = {
|
||||
{ .compatible = "adi,ad9739a" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad9739a_of_match);
|
||||
|
||||
static const struct spi_device_id ad9739a_id[] = {
|
||||
{"ad9739a"},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad9739a_id);
|
||||
|
||||
static struct spi_driver ad9739a_driver = {
|
||||
.driver = {
|
||||
.name = "ad9739a",
|
||||
.of_match_table = ad9739a_of_match,
|
||||
},
|
||||
.probe = ad9739a_probe,
|
||||
.id_table = ad9739a_id,
|
||||
};
|
||||
module_spi_driver(ad9739a_driver);
|
||||
|
||||
MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>");
|
||||
MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD9739 DAC");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(IIO_BACKEND);
|
635
drivers/iio/dac/adi-axi-dac.c
Normal file
635
drivers/iio/dac/adi-axi-dac.c
Normal file
@ -0,0 +1,635 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Analog Devices Generic AXI DAC IP core
|
||||
* Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip
|
||||
*
|
||||
* Copyright 2016-2024 Analog Devices Inc.
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include <linux/fpga/adi-axi-common.h>
|
||||
#include <linux/iio/backend.h>
|
||||
#include <linux/iio/buffer-dmaengine.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
/*
|
||||
* Register definitions:
|
||||
* https://wiki.analog.com/resources/fpga/docs/axi_dac_ip#register_map
|
||||
*/
|
||||
|
||||
/* Base controls */
|
||||
#define AXI_DAC_REG_CONFIG 0x0c
|
||||
#define AXI_DDS_DISABLE BIT(6)
|
||||
|
||||
/* DAC controls */
|
||||
#define AXI_DAC_REG_RSTN 0x0040
|
||||
#define AXI_DAC_RSTN_CE_N BIT(2)
|
||||
#define AXI_DAC_RSTN_MMCM_RSTN BIT(1)
|
||||
#define AXI_DAC_RSTN_RSTN BIT(0)
|
||||
#define AXI_DAC_REG_CNTRL_1 0x0044
|
||||
#define AXI_DAC_SYNC BIT(0)
|
||||
#define AXI_DAC_REG_CNTRL_2 0x0048
|
||||
#define ADI_DAC_R1_MODE BIT(4)
|
||||
#define AXI_DAC_DRP_STATUS 0x0074
|
||||
#define AXI_DAC_DRP_LOCKED BIT(17)
|
||||
/* DAC Channel controls */
|
||||
#define AXI_DAC_REG_CHAN_CNTRL_1(c) (0x0400 + (c) * 0x40)
|
||||
#define AXI_DAC_REG_CHAN_CNTRL_3(c) (0x0408 + (c) * 0x40)
|
||||
#define AXI_DAC_SCALE_SIGN BIT(15)
|
||||
#define AXI_DAC_SCALE_INT BIT(14)
|
||||
#define AXI_DAC_SCALE GENMASK(14, 0)
|
||||
#define AXI_DAC_REG_CHAN_CNTRL_2(c) (0x0404 + (c) * 0x40)
|
||||
#define AXI_DAC_REG_CHAN_CNTRL_4(c) (0x040c + (c) * 0x40)
|
||||
#define AXI_DAC_PHASE GENMASK(31, 16)
|
||||
#define AXI_DAC_FREQUENCY GENMASK(15, 0)
|
||||
#define AXI_DAC_REG_CHAN_CNTRL_7(c) (0x0418 + (c) * 0x40)
|
||||
#define AXI_DAC_DATA_SEL GENMASK(3, 0)
|
||||
|
||||
/* 360 degrees in rad */
|
||||
#define AXI_DAC_2_PI_MEGA 6283190
|
||||
enum {
|
||||
AXI_DAC_DATA_INTERNAL_TONE,
|
||||
AXI_DAC_DATA_DMA = 2,
|
||||
};
|
||||
|
||||
struct axi_dac_state {
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
/*
|
||||
* lock to protect multiple accesses to the device registers and global
|
||||
* data/variables.
|
||||
*/
|
||||
struct mutex lock;
|
||||
u64 dac_clk;
|
||||
u32 reg_config;
|
||||
bool int_tone;
|
||||
};
|
||||
|
||||
static int axi_dac_enable(struct iio_backend *back)
|
||||
{
|
||||
struct axi_dac_state *st = iio_backend_get_priv(back);
|
||||
unsigned int __val;
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
ret = regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN,
|
||||
AXI_DAC_RSTN_MMCM_RSTN);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* Make sure the DRP (Dynamic Reconfiguration Port) is locked. Not all
|
||||
* designs really use it but if they don't we still get the lock bit
|
||||
* set. So let's do it all the time so the code is generic.
|
||||
*/
|
||||
ret = regmap_read_poll_timeout(st->regmap, AXI_DAC_DRP_STATUS, __val,
|
||||
__val & AXI_DAC_DRP_LOCKED, 100, 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN,
|
||||
AXI_DAC_RSTN_RSTN | AXI_DAC_RSTN_MMCM_RSTN);
|
||||
}
|
||||
|
||||
static void axi_dac_disable(struct iio_backend *back)
|
||||
{
|
||||
struct axi_dac_state *st = iio_backend_get_priv(back);
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0);
|
||||
}
|
||||
|
||||
static struct iio_buffer *axi_dac_request_buffer(struct iio_backend *back,
|
||||
struct iio_dev *indio_dev)
|
||||
{
|
||||
struct axi_dac_state *st = iio_backend_get_priv(back);
|
||||
const char *dma_name;
|
||||
|
||||
if (device_property_read_string(st->dev, "dma-names", &dma_name))
|
||||
dma_name = "tx";
|
||||
|
||||
return iio_dmaengine_buffer_setup_ext(st->dev, indio_dev, dma_name,
|
||||
IIO_BUFFER_DIRECTION_OUT);
|
||||
}
|
||||
|
||||
static void axi_dac_free_buffer(struct iio_backend *back,
|
||||
struct iio_buffer *buffer)
|
||||
{
|
||||
iio_dmaengine_buffer_free(buffer);
|
||||
}
|
||||
|
||||
enum {
|
||||
AXI_DAC_FREQ_TONE_1,
|
||||
AXI_DAC_FREQ_TONE_2,
|
||||
AXI_DAC_SCALE_TONE_1,
|
||||
AXI_DAC_SCALE_TONE_2,
|
||||
AXI_DAC_PHASE_TONE_1,
|
||||
AXI_DAC_PHASE_TONE_2,
|
||||
};
|
||||
|
||||
static int __axi_dac_frequency_get(struct axi_dac_state *st, unsigned int chan,
|
||||
unsigned int tone_2, unsigned int *freq)
|
||||
{
|
||||
u32 reg, raw;
|
||||
int ret;
|
||||
|
||||
if (!st->dac_clk) {
|
||||
dev_err(st->dev, "Sampling rate is 0...\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (tone_2)
|
||||
reg = AXI_DAC_REG_CHAN_CNTRL_4(chan);
|
||||
else
|
||||
reg = AXI_DAC_REG_CHAN_CNTRL_2(chan);
|
||||
|
||||
ret = regmap_read(st->regmap, reg, &raw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
raw = FIELD_GET(AXI_DAC_FREQUENCY, raw);
|
||||
*freq = DIV_ROUND_CLOSEST_ULL(raw * st->dac_clk, BIT(16));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int axi_dac_frequency_get(struct axi_dac_state *st,
|
||||
const struct iio_chan_spec *chan, char *buf,
|
||||
unsigned int tone_2)
|
||||
{
|
||||
unsigned int freq;
|
||||
int ret;
|
||||
|
||||
scoped_guard(mutex, &st->lock) {
|
||||
ret = __axi_dac_frequency_get(st, chan->channel, tone_2, &freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return sysfs_emit(buf, "%u\n", freq);
|
||||
}
|
||||
|
||||
static int axi_dac_scale_get(struct axi_dac_state *st,
|
||||
const struct iio_chan_spec *chan, char *buf,
|
||||
unsigned int tone_2)
|
||||
{
|
||||
unsigned int scale, sign;
|
||||
int ret, vals[2];
|
||||
u32 reg, raw;
|
||||
|
||||
if (tone_2)
|
||||
reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
|
||||
else
|
||||
reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
|
||||
|
||||
ret = regmap_read(st->regmap, reg, &raw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sign = FIELD_GET(AXI_DAC_SCALE_SIGN, raw);
|
||||
raw = FIELD_GET(AXI_DAC_SCALE, raw);
|
||||
scale = DIV_ROUND_CLOSEST_ULL((u64)raw * MEGA, AXI_DAC_SCALE_INT);
|
||||
|
||||
vals[0] = scale / MEGA;
|
||||
vals[1] = scale % MEGA;
|
||||
|
||||
if (sign) {
|
||||
vals[0] *= -1;
|
||||
if (!vals[0])
|
||||
vals[1] *= -1;
|
||||
}
|
||||
|
||||
return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(vals),
|
||||
vals);
|
||||
}
|
||||
|
||||
static int axi_dac_phase_get(struct axi_dac_state *st,
|
||||
const struct iio_chan_spec *chan, char *buf,
|
||||
unsigned int tone_2)
|
||||
{
|
||||
u32 reg, raw, phase;
|
||||
int ret, vals[2];
|
||||
|
||||
if (tone_2)
|
||||
reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
|
||||
else
|
||||
reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
|
||||
|
||||
ret = regmap_read(st->regmap, reg, &raw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
raw = FIELD_GET(AXI_DAC_PHASE, raw);
|
||||
phase = DIV_ROUND_CLOSEST_ULL((u64)raw * AXI_DAC_2_PI_MEGA, U16_MAX);
|
||||
|
||||
vals[0] = phase / MEGA;
|
||||
vals[1] = phase % MEGA;
|
||||
|
||||
return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(vals),
|
||||
vals);
|
||||
}
|
||||
|
||||
static int __axi_dac_frequency_set(struct axi_dac_state *st, unsigned int chan,
|
||||
u64 sample_rate, unsigned int freq,
|
||||
unsigned int tone_2)
|
||||
{
|
||||
u32 reg;
|
||||
u16 raw;
|
||||
int ret;
|
||||
|
||||
if (!sample_rate || freq > sample_rate / 2) {
|
||||
dev_err(st->dev, "Invalid frequency(%u) dac_clk(%llu)\n",
|
||||
freq, sample_rate);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (tone_2)
|
||||
reg = AXI_DAC_REG_CHAN_CNTRL_4(chan);
|
||||
else
|
||||
reg = AXI_DAC_REG_CHAN_CNTRL_2(chan);
|
||||
|
||||
raw = DIV64_U64_ROUND_CLOSEST((u64)freq * BIT(16), sample_rate);
|
||||
|
||||
ret = regmap_update_bits(st->regmap, reg, AXI_DAC_FREQUENCY, raw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* synchronize channels */
|
||||
return regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
|
||||
}
|
||||
|
||||
static int axi_dac_frequency_set(struct axi_dac_state *st,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len, unsigned int tone_2)
|
||||
{
|
||||
unsigned int freq;
|
||||
int ret;
|
||||
|
||||
ret = kstrtou32(buf, 10, &freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
ret = __axi_dac_frequency_set(st, chan->channel, st->dac_clk, freq,
|
||||
tone_2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int axi_dac_scale_set(struct axi_dac_state *st,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len, unsigned int tone_2)
|
||||
{
|
||||
int integer, frac, scale;
|
||||
u32 raw = 0, reg;
|
||||
int ret;
|
||||
|
||||
ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scale = integer * MEGA + frac;
|
||||
if (scale <= -2 * (int)MEGA || scale >= 2 * (int)MEGA)
|
||||
return -EINVAL;
|
||||
|
||||
/* format is 1.1.14 (sign, integer and fractional bits) */
|
||||
if (scale < 0) {
|
||||
raw = FIELD_PREP(AXI_DAC_SCALE_SIGN, 1);
|
||||
scale *= -1;
|
||||
}
|
||||
|
||||
raw |= div_u64((u64)scale * AXI_DAC_SCALE_INT, MEGA);
|
||||
|
||||
if (tone_2)
|
||||
reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel);
|
||||
else
|
||||
reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel);
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
ret = regmap_write(st->regmap, reg, raw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* synchronize channels */
|
||||
ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int axi_dac_phase_set(struct axi_dac_state *st,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len, unsigned int tone_2)
|
||||
{
|
||||
int integer, frac, phase;
|
||||
u32 raw, reg;
|
||||
int ret;
|
||||
|
||||
ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
phase = integer * MEGA + frac;
|
||||
if (phase < 0 || phase > AXI_DAC_2_PI_MEGA)
|
||||
return -EINVAL;
|
||||
|
||||
raw = DIV_ROUND_CLOSEST_ULL((u64)phase * U16_MAX, AXI_DAC_2_PI_MEGA);
|
||||
|
||||
if (tone_2)
|
||||
reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel);
|
||||
else
|
||||
reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel);
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
ret = regmap_update_bits(st->regmap, reg, AXI_DAC_PHASE,
|
||||
FIELD_PREP(AXI_DAC_PHASE, raw));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* synchronize channels */
|
||||
ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int axi_dac_ext_info_set(struct iio_backend *back, uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct axi_dac_state *st = iio_backend_get_priv(back);
|
||||
|
||||
switch (private) {
|
||||
case AXI_DAC_FREQ_TONE_1:
|
||||
case AXI_DAC_FREQ_TONE_2:
|
||||
return axi_dac_frequency_set(st, chan, buf, len,
|
||||
private - AXI_DAC_FREQ_TONE_1);
|
||||
case AXI_DAC_SCALE_TONE_1:
|
||||
case AXI_DAC_SCALE_TONE_2:
|
||||
return axi_dac_scale_set(st, chan, buf, len,
|
||||
private - AXI_DAC_SCALE_TONE_1);
|
||||
case AXI_DAC_PHASE_TONE_1:
|
||||
case AXI_DAC_PHASE_TONE_2:
|
||||
return axi_dac_phase_set(st, chan, buf, len,
|
||||
private - AXI_DAC_PHASE_TONE_2);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int axi_dac_ext_info_get(struct iio_backend *back, uintptr_t private,
|
||||
const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct axi_dac_state *st = iio_backend_get_priv(back);
|
||||
|
||||
switch (private) {
|
||||
case AXI_DAC_FREQ_TONE_1:
|
||||
case AXI_DAC_FREQ_TONE_2:
|
||||
return axi_dac_frequency_get(st, chan, buf,
|
||||
private - AXI_DAC_FREQ_TONE_1);
|
||||
case AXI_DAC_SCALE_TONE_1:
|
||||
case AXI_DAC_SCALE_TONE_2:
|
||||
return axi_dac_scale_get(st, chan, buf,
|
||||
private - AXI_DAC_SCALE_TONE_1);
|
||||
case AXI_DAC_PHASE_TONE_1:
|
||||
case AXI_DAC_PHASE_TONE_2:
|
||||
return axi_dac_phase_get(st, chan, buf,
|
||||
private - AXI_DAC_PHASE_TONE_1);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info axi_dac_ext_info[] = {
|
||||
IIO_BACKEND_EX_INFO("frequency0", IIO_SEPARATE, AXI_DAC_FREQ_TONE_1),
|
||||
IIO_BACKEND_EX_INFO("frequency1", IIO_SEPARATE, AXI_DAC_FREQ_TONE_2),
|
||||
IIO_BACKEND_EX_INFO("scale0", IIO_SEPARATE, AXI_DAC_SCALE_TONE_1),
|
||||
IIO_BACKEND_EX_INFO("scale1", IIO_SEPARATE, AXI_DAC_SCALE_TONE_2),
|
||||
IIO_BACKEND_EX_INFO("phase0", IIO_SEPARATE, AXI_DAC_PHASE_TONE_1),
|
||||
IIO_BACKEND_EX_INFO("phase1", IIO_SEPARATE, AXI_DAC_PHASE_TONE_2),
|
||||
{}
|
||||
};
|
||||
|
||||
static int axi_dac_extend_chan(struct iio_backend *back,
|
||||
struct iio_chan_spec *chan)
|
||||
{
|
||||
struct axi_dac_state *st = iio_backend_get_priv(back);
|
||||
|
||||
if (chan->type != IIO_ALTVOLTAGE)
|
||||
return -EINVAL;
|
||||
if (st->reg_config & AXI_DDS_DISABLE)
|
||||
/* nothing to extend */
|
||||
return 0;
|
||||
|
||||
chan->ext_info = axi_dac_ext_info;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int axi_dac_data_source_set(struct iio_backend *back, unsigned int chan,
|
||||
enum iio_backend_data_source data)
|
||||
{
|
||||
struct axi_dac_state *st = iio_backend_get_priv(back);
|
||||
|
||||
switch (data) {
|
||||
case IIO_BACKEND_INTERNAL_CONTINUOS_WAVE:
|
||||
return regmap_update_bits(st->regmap,
|
||||
AXI_DAC_REG_CHAN_CNTRL_7(chan),
|
||||
AXI_DAC_DATA_SEL,
|
||||
AXI_DAC_DATA_INTERNAL_TONE);
|
||||
case IIO_BACKEND_EXTERNAL:
|
||||
return regmap_update_bits(st->regmap,
|
||||
AXI_DAC_REG_CHAN_CNTRL_7(chan),
|
||||
AXI_DAC_DATA_SEL, AXI_DAC_DATA_DMA);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int axi_dac_set_sample_rate(struct iio_backend *back, unsigned int chan,
|
||||
u64 sample_rate)
|
||||
{
|
||||
struct axi_dac_state *st = iio_backend_get_priv(back);
|
||||
unsigned int freq;
|
||||
int ret, tone;
|
||||
|
||||
if (!sample_rate)
|
||||
return -EINVAL;
|
||||
if (st->reg_config & AXI_DDS_DISABLE)
|
||||
/* sample_rate has no meaning if DDS is disabled */
|
||||
return 0;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
/*
|
||||
* If dac_clk is 0 then this must be the first time we're being notified
|
||||
* about the interface sample rate. Hence, just update our internal
|
||||
* variable and bail... If it's not 0, then we get the current DDS
|
||||
* frequency (for the old rate) and update the registers for the new
|
||||
* sample rate.
|
||||
*/
|
||||
if (!st->dac_clk) {
|
||||
st->dac_clk = sample_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (tone = 0; tone <= AXI_DAC_FREQ_TONE_2; tone++) {
|
||||
ret = __axi_dac_frequency_get(st, chan, tone, &freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = __axi_dac_frequency_set(st, chan, sample_rate, tone, freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->dac_clk = sample_rate;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_backend_ops axi_dac_generic = {
|
||||
.enable = axi_dac_enable,
|
||||
.disable = axi_dac_disable,
|
||||
.request_buffer = axi_dac_request_buffer,
|
||||
.free_buffer = axi_dac_free_buffer,
|
||||
.extend_chan_spec = axi_dac_extend_chan,
|
||||
.ext_info_set = axi_dac_ext_info_set,
|
||||
.ext_info_get = axi_dac_ext_info_get,
|
||||
.data_source_set = axi_dac_data_source_set,
|
||||
.set_sample_rate = axi_dac_set_sample_rate,
|
||||
};
|
||||
|
||||
static const struct regmap_config axi_dac_regmap_config = {
|
||||
.val_bits = 32,
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = 0x0800,
|
||||
};
|
||||
|
||||
static int axi_dac_probe(struct platform_device *pdev)
|
||||
{
|
||||
const unsigned int *expected_ver;
|
||||
struct axi_dac_state *st;
|
||||
void __iomem *base;
|
||||
unsigned int ver;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL);
|
||||
if (!st)
|
||||
return -ENOMEM;
|
||||
|
||||
expected_ver = device_get_match_data(&pdev->dev);
|
||||
if (!expected_ver)
|
||||
return -ENODEV;
|
||||
|
||||
clk = devm_clk_get_enabled(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
st->dev = &pdev->dev;
|
||||
st->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&axi_dac_regmap_config);
|
||||
if (IS_ERR(st->regmap))
|
||||
return PTR_ERR(st->regmap);
|
||||
|
||||
/*
|
||||
* Force disable the core. Up to the frontend to enable us. And we can
|
||||
* still read/write registers...
|
||||
*/
|
||||
ret = regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(st->regmap, ADI_AXI_REG_VERSION, &ver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) {
|
||||
dev_err(&pdev->dev,
|
||||
"Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
|
||||
ADI_AXI_PCORE_VER_MAJOR(*expected_ver),
|
||||
ADI_AXI_PCORE_VER_MINOR(*expected_ver),
|
||||
ADI_AXI_PCORE_VER_PATCH(*expected_ver),
|
||||
ADI_AXI_PCORE_VER_MAJOR(ver),
|
||||
ADI_AXI_PCORE_VER_MINOR(ver),
|
||||
ADI_AXI_PCORE_VER_PATCH(ver));
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Let's get the core read only configuration */
|
||||
ret = regmap_read(st->regmap, AXI_DAC_REG_CONFIG, &st->reg_config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* In some designs, setting the R1_MODE bit to 0 (which is the default
|
||||
* value) causes all channels of the frontend to be routed to the same
|
||||
* DMA (so they are sampled together). This is for things like
|
||||
* Multiple-Input and Multiple-Output (MIMO). As most of the times we
|
||||
* want independent channels let's override the core's default value and
|
||||
* set the R1_MODE bit.
|
||||
*/
|
||||
ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_2, ADI_DAC_R1_MODE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_init(&st->lock);
|
||||
ret = devm_iio_backend_register(&pdev->dev, &axi_dac_generic, st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(&pdev->dev, "AXI DAC IP core (%d.%.2d.%c) probed\n",
|
||||
ADI_AXI_PCORE_VER_MAJOR(ver),
|
||||
ADI_AXI_PCORE_VER_MINOR(ver),
|
||||
ADI_AXI_PCORE_VER_PATCH(ver));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int axi_dac_9_1_b_info = ADI_AXI_PCORE_VER(9, 1, 'b');
|
||||
|
||||
static const struct of_device_id axi_dac_of_match[] = {
|
||||
{ .compatible = "adi,axi-dac-9.1.b", .data = &axi_dac_9_1_b_info },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, axi_dac_of_match);
|
||||
|
||||
static struct platform_driver axi_dac_driver = {
|
||||
.driver = {
|
||||
.name = "adi-axi-dac",
|
||||
.of_match_table = axi_dac_of_match,
|
||||
},
|
||||
.probe = axi_dac_probe,
|
||||
};
|
||||
module_platform_driver(axi_dac_driver);
|
||||
|
||||
MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices Generic AXI DAC IP core driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(IIO_DMAENGINE_BUFFER);
|
||||
MODULE_IMPORT_NS(IIO_BACKEND);
|
@ -746,26 +746,21 @@ static int ltc2688_span_lookup(const struct ltc2688_state *st, int min, int max)
|
||||
static int ltc2688_channel_config(struct ltc2688_state *st)
|
||||
{
|
||||
struct device *dev = &st->spi->dev;
|
||||
struct fwnode_handle *child;
|
||||
u32 reg, clk_input, val, tmp[2];
|
||||
int ret, span;
|
||||
|
||||
device_for_each_child_node(dev, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
struct ltc2688_chan *chan;
|
||||
|
||||
ret = fwnode_property_read_u32(child, "reg", ®);
|
||||
if (ret) {
|
||||
fwnode_handle_put(child);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to get reg property\n");
|
||||
}
|
||||
|
||||
if (reg >= LTC2688_DAC_CHANNELS) {
|
||||
fwnode_handle_put(child);
|
||||
if (reg >= LTC2688_DAC_CHANNELS)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"reg bigger than: %d\n",
|
||||
LTC2688_DAC_CHANNELS);
|
||||
}
|
||||
|
||||
val = 0;
|
||||
chan = &st->channels[reg];
|
||||
@ -786,12 +781,10 @@ static int ltc2688_channel_config(struct ltc2688_state *st)
|
||||
if (!ret) {
|
||||
span = ltc2688_span_lookup(st, (int)tmp[0] / 1000,
|
||||
tmp[1] / 1000);
|
||||
if (span < 0) {
|
||||
fwnode_handle_put(child);
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
if (span < 0)
|
||||
return dev_err_probe(dev, span,
|
||||
"output range not valid:[%d %d]\n",
|
||||
tmp[0], tmp[1]);
|
||||
}
|
||||
|
||||
val |= FIELD_PREP(LTC2688_CH_SPAN_MSK, span);
|
||||
}
|
||||
@ -800,17 +793,14 @@ static int ltc2688_channel_config(struct ltc2688_state *st)
|
||||
&clk_input);
|
||||
if (!ret) {
|
||||
if (clk_input >= LTC2688_CH_TGP_MAX) {
|
||||
fwnode_handle_put(child);
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"toggle-dither-input inv value(%d)\n",
|
||||
clk_input);
|
||||
}
|
||||
|
||||
ret = ltc2688_tgp_clk_setup(st, chan, child, clk_input);
|
||||
if (ret) {
|
||||
fwnode_handle_put(child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* 0 means software toggle which is the default mode.
|
||||
@ -844,11 +834,9 @@ static int ltc2688_channel_config(struct ltc2688_state *st)
|
||||
|
||||
ret = regmap_write(st->regmap, LTC2688_CMD_CH_SETTING(reg),
|
||||
val);
|
||||
if (ret) {
|
||||
fwnode_handle_put(child);
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to set chan settings\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -13,6 +13,7 @@
|
||||
* https://www.ti.com/lit/ds/symlink/dac5573.pdf
|
||||
* https://www.ti.com/lit/ds/symlink/dac6573.pdf
|
||||
* https://www.ti.com/lit/ds/symlink/dac7573.pdf
|
||||
* https://www.ti.com/lit/ds/symlink/dac081c081.pdf
|
||||
* https://www.ti.com/lit/ds/symlink/dac121c081.pdf
|
||||
*/
|
||||
|
||||
@ -386,6 +387,7 @@ static void dac5571_remove(struct i2c_client *i2c)
|
||||
}
|
||||
|
||||
static const struct of_device_id dac5571_of_id[] = {
|
||||
{.compatible = "ti,dac081c081", .data = &dac5571_spec[single_8bit] },
|
||||
{.compatible = "ti,dac121c081", .data = &dac5571_spec[single_12bit] },
|
||||
{.compatible = "ti,dac5571", .data = &dac5571_spec[single_8bit] },
|
||||
{.compatible = "ti,dac6571", .data = &dac5571_spec[single_10bit] },
|
||||
@ -401,6 +403,7 @@ static const struct of_device_id dac5571_of_id[] = {
|
||||
MODULE_DEVICE_TABLE(of, dac5571_of_id);
|
||||
|
||||
static const struct i2c_device_id dac5571_id[] = {
|
||||
{"dac081c081", (kernel_ulong_t)&dac5571_spec[single_8bit] },
|
||||
{"dac121c081", (kernel_ulong_t)&dac5571_spec[single_12bit] },
|
||||
{"dac5571", (kernel_ulong_t)&dac5571_spec[single_8bit] },
|
||||
{"dac6571", (kernel_ulong_t)&dac5571_spec[single_10bit] },
|
||||
|
@ -160,26 +160,21 @@ static int admfm2000_channel_config(struct admfm2000_state *st,
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(indio_dev->dev.parent);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct fwnode_handle *child;
|
||||
struct gpio_desc **dsa;
|
||||
struct gpio_desc **sw;
|
||||
int ret, i;
|
||||
bool mode;
|
||||
u32 reg;
|
||||
|
||||
device_for_each_child_node(dev, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
ret = fwnode_property_read_u32(child, "reg", ®);
|
||||
if (ret) {
|
||||
fwnode_handle_put(child);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to get reg property\n");
|
||||
}
|
||||
|
||||
if (reg >= indio_dev->num_channels) {
|
||||
fwnode_handle_put(child);
|
||||
if (reg >= indio_dev->num_channels)
|
||||
return dev_err_probe(dev, -EINVAL, "reg bigger than: %d\n",
|
||||
indio_dev->num_channels);
|
||||
}
|
||||
|
||||
if (fwnode_property_present(child, "adi,mixer-mode"))
|
||||
mode = ADMFM2000_MIXER_MODE;
|
||||
@ -196,36 +191,29 @@ static int admfm2000_channel_config(struct admfm2000_state *st,
|
||||
dsa = st->dsa2_gpios;
|
||||
break;
|
||||
default:
|
||||
fwnode_handle_put(child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) {
|
||||
sw[i] = devm_fwnode_gpiod_get_index(dev, child, "switch",
|
||||
i, GPIOD_OUT_LOW, NULL);
|
||||
if (IS_ERR(sw[i])) {
|
||||
fwnode_handle_put(child);
|
||||
if (IS_ERR(sw[i]))
|
||||
return dev_err_probe(dev, PTR_ERR(sw[i]),
|
||||
"Failed to get gpios\n");
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ADMFM2000_DSA_GPIOS; i++) {
|
||||
dsa[i] = devm_fwnode_gpiod_get_index(dev, child,
|
||||
"attenuation", i,
|
||||
GPIOD_OUT_LOW, NULL);
|
||||
if (IS_ERR(dsa[i])) {
|
||||
fwnode_handle_put(child);
|
||||
if (IS_ERR(dsa[i]))
|
||||
return dev_err_probe(dev, PTR_ERR(dsa[i]),
|
||||
"Failed to get gpios\n");
|
||||
}
|
||||
}
|
||||
|
||||
ret = admfm2000_mode(indio_dev, reg, mode);
|
||||
if (ret) {
|
||||
fwnode_handle_put(child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -613,6 +613,7 @@ static void max30102_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max30102_id[] = {
|
||||
{ "max30101", max30105 },
|
||||
{ "max30102", max30102 },
|
||||
{ "max30105", max30105 },
|
||||
{}
|
||||
@ -620,6 +621,7 @@ static const struct i2c_device_id max30102_id[] = {
|
||||
MODULE_DEVICE_TABLE(i2c, max30102_id);
|
||||
|
||||
static const struct of_device_id max30102_dt_ids[] = {
|
||||
{ .compatible = "maxim,max30101" },
|
||||
{ .compatible = "maxim,max30102" },
|
||||
{ .compatible = "maxim,max30105" },
|
||||
{ }
|
||||
|
@ -15,11 +15,14 @@
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/crc8.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
@ -68,6 +71,8 @@
|
||||
|
||||
struct hdc3020_data {
|
||||
struct i2c_client *client;
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct regulator *vdd_supply;
|
||||
/*
|
||||
* Ensure that the sensor configuration (currently only heater is
|
||||
* supported) will not be changed during the process of reading
|
||||
@ -551,9 +556,53 @@ static const struct iio_info hdc3020_info = {
|
||||
.write_event_value = hdc3020_write_thresh,
|
||||
};
|
||||
|
||||
static void hdc3020_stop(void *data)
|
||||
static int hdc3020_power_off(struct hdc3020_data *data)
|
||||
{
|
||||
hdc3020_exec_cmd((struct hdc3020_data *)data, HDC3020_EXIT_AUTO);
|
||||
hdc3020_exec_cmd(data, HDC3020_EXIT_AUTO);
|
||||
|
||||
if (data->reset_gpio)
|
||||
gpiod_set_value_cansleep(data->reset_gpio, 1);
|
||||
|
||||
return regulator_disable(data->vdd_supply);
|
||||
}
|
||||
|
||||
static int hdc3020_power_on(struct hdc3020_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(data->vdd_supply);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fsleep(5000);
|
||||
|
||||
if (data->reset_gpio) {
|
||||
gpiod_set_value_cansleep(data->reset_gpio, 0);
|
||||
fsleep(3000);
|
||||
}
|
||||
|
||||
if (data->client->irq) {
|
||||
/*
|
||||
* The alert output is activated by default upon power up,
|
||||
* hardware reset, and soft reset. Clear the status register.
|
||||
*/
|
||||
ret = hdc3020_exec_cmd(data, HDC3020_S_STATUS);
|
||||
if (ret) {
|
||||
hdc3020_power_off(data);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = hdc3020_exec_cmd(data, HDC3020_S_AUTO_10HZ_MOD0);
|
||||
if (ret)
|
||||
hdc3020_power_off(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hdc3020_exit(void *data)
|
||||
{
|
||||
hdc3020_power_off(data);
|
||||
}
|
||||
|
||||
static int hdc3020_probe(struct i2c_client *client)
|
||||
@ -569,6 +618,8 @@ static int hdc3020_probe(struct i2c_client *client)
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&client->dev, indio_dev);
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
@ -580,6 +631,26 @@ static int hdc3020_probe(struct i2c_client *client)
|
||||
indio_dev->info = &hdc3020_info;
|
||||
indio_dev->channels = hdc3020_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(hdc3020_channels);
|
||||
|
||||
data->vdd_supply = devm_regulator_get(&client->dev, "vdd");
|
||||
if (IS_ERR(data->vdd_supply))
|
||||
return dev_err_probe(&client->dev, PTR_ERR(data->vdd_supply),
|
||||
"Unable to get VDD regulator\n");
|
||||
|
||||
data->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(data->reset_gpio))
|
||||
return dev_err_probe(&client->dev, PTR_ERR(data->reset_gpio),
|
||||
"Cannot get reset GPIO\n");
|
||||
|
||||
ret = hdc3020_power_on(data);
|
||||
if (ret)
|
||||
return dev_err_probe(&client->dev, ret, "Power on failed\n");
|
||||
|
||||
ret = devm_add_action_or_reset(&data->client->dev, hdc3020_exit, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (client->irq) {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, hdc3020_interrupt_handler,
|
||||
@ -588,25 +659,8 @@ static int hdc3020_probe(struct i2c_client *client)
|
||||
if (ret)
|
||||
return dev_err_probe(&client->dev, ret,
|
||||
"Failed to request IRQ\n");
|
||||
|
||||
/*
|
||||
* The alert output is activated by default upon power up,
|
||||
* hardware reset, and soft reset. Clear the status register.
|
||||
*/
|
||||
ret = hdc3020_exec_cmd(data, HDC3020_S_STATUS);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hdc3020_exec_cmd(data, HDC3020_S_AUTO_10HZ_MOD0);
|
||||
if (ret)
|
||||
return dev_err_probe(&client->dev, ret,
|
||||
"Unable to set up measurement\n");
|
||||
|
||||
ret = devm_add_action_or_reset(&data->client->dev, hdc3020_stop, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_iio_device_register(&data->client->dev, indio_dev);
|
||||
if (ret)
|
||||
return dev_err_probe(&client->dev, ret, "Failed to add device");
|
||||
@ -614,6 +668,24 @@ static int hdc3020_probe(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdc3020_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct hdc3020_data *data = iio_priv(iio_dev);
|
||||
|
||||
return hdc3020_power_off(data);
|
||||
}
|
||||
|
||||
static int hdc3020_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
||||
struct hdc3020_data *data = iio_priv(iio_dev);
|
||||
|
||||
return hdc3020_power_on(data);
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(hdc3020_pm_ops, hdc3020_suspend, hdc3020_resume);
|
||||
|
||||
static const struct i2c_device_id hdc3020_id[] = {
|
||||
{ "hdc3020" },
|
||||
{ "hdc3021" },
|
||||
@ -633,6 +705,7 @@ MODULE_DEVICE_TABLE(of, hdc3020_dt_ids);
|
||||
static struct i2c_driver hdc3020_driver = {
|
||||
.driver = {
|
||||
.name = "hdc3020",
|
||||
.pm = pm_sleep_ptr(&hdc3020_pm_ops),
|
||||
.of_match_table = hdc3020_dt_ids,
|
||||
},
|
||||
.probe = hdc3020_probe,
|
||||
|
@ -573,7 +573,7 @@ int hts221_probe(struct device *dev, int irq, const char *name,
|
||||
if (!iio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, (void *)iio_dev);
|
||||
dev_set_drvdata(dev, iio_dev);
|
||||
|
||||
hw = iio_priv(iio_dev);
|
||||
hw->name = name;
|
||||
|
@ -22,6 +22,7 @@ enum inv_icm42600_chip {
|
||||
INV_CHIP_ICM42602,
|
||||
INV_CHIP_ICM42605,
|
||||
INV_CHIP_ICM42622,
|
||||
INV_CHIP_ICM42688,
|
||||
INV_CHIP_ICM42631,
|
||||
INV_CHIP_NB,
|
||||
};
|
||||
@ -304,6 +305,7 @@ struct inv_icm42600_state {
|
||||
#define INV_ICM42600_WHOAMI_ICM42602 0x41
|
||||
#define INV_ICM42600_WHOAMI_ICM42605 0x42
|
||||
#define INV_ICM42600_WHOAMI_ICM42622 0x46
|
||||
#define INV_ICM42600_WHOAMI_ICM42688 0x47
|
||||
#define INV_ICM42600_WHOAMI_ICM42631 0x5C
|
||||
|
||||
/* User bank 1 (MSB 0x10) */
|
||||
|
@ -87,6 +87,11 @@ static const struct inv_icm42600_hw inv_icm42600_hw[INV_CHIP_NB] = {
|
||||
.name = "icm42622",
|
||||
.conf = &inv_icm42600_default_conf,
|
||||
},
|
||||
[INV_CHIP_ICM42688] = {
|
||||
.whoami = INV_ICM42600_WHOAMI_ICM42688,
|
||||
.name = "icm42688",
|
||||
.conf = &inv_icm42600_default_conf,
|
||||
},
|
||||
[INV_CHIP_ICM42631] = {
|
||||
.whoami = INV_ICM42600_WHOAMI_ICM42631,
|
||||
.name = "icm42631",
|
||||
|
@ -84,6 +84,9 @@ static const struct of_device_id inv_icm42600_of_matches[] = {
|
||||
}, {
|
||||
.compatible = "invensense,icm42622",
|
||||
.data = (void *)INV_CHIP_ICM42622,
|
||||
}, {
|
||||
.compatible = "invensense,icm42688",
|
||||
.data = (void *)INV_CHIP_ICM42688,
|
||||
}, {
|
||||
.compatible = "invensense,icm42631",
|
||||
.data = (void *)INV_CHIP_ICM42631,
|
||||
|
@ -80,6 +80,9 @@ static const struct of_device_id inv_icm42600_of_matches[] = {
|
||||
}, {
|
||||
.compatible = "invensense,icm42622",
|
||||
.data = (void *)INV_CHIP_ICM42622,
|
||||
}, {
|
||||
.compatible = "invensense,icm42688",
|
||||
.data = (void *)INV_CHIP_ICM42688,
|
||||
}, {
|
||||
.compatible = "invensense,icm42631",
|
||||
.data = (void *)INV_CHIP_ICM42631,
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/property.h>
|
||||
@ -287,7 +289,7 @@ static const struct inv_mpu6050_hw hw_info[] = {
|
||||
};
|
||||
|
||||
static int inv_mpu6050_pwr_mgmt_1_write(struct inv_mpu6050_state *st, bool sleep,
|
||||
int clock, int temp_dis)
|
||||
bool cycle, int clock, int temp_dis)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
@ -301,6 +303,8 @@ static int inv_mpu6050_pwr_mgmt_1_write(struct inv_mpu6050_state *st, bool sleep
|
||||
val |= INV_MPU6050_BIT_TEMP_DIS;
|
||||
if (sleep)
|
||||
val |= INV_MPU6050_BIT_SLEEP;
|
||||
if (cycle)
|
||||
val |= INV_MPU6050_BIT_CYCLE;
|
||||
|
||||
dev_dbg(regmap_get_device(st->map), "pwr_mgmt_1: 0x%x\n", val);
|
||||
return regmap_write(st->map, st->reg->pwr_mgmt_1, val);
|
||||
@ -316,7 +320,7 @@ static int inv_mpu6050_clock_switch(struct inv_mpu6050_state *st,
|
||||
case INV_MPU6000:
|
||||
case INV_MPU9150:
|
||||
/* old chips: switch clock manually */
|
||||
ret = inv_mpu6050_pwr_mgmt_1_write(st, false, clock, -1);
|
||||
ret = inv_mpu6050_pwr_mgmt_1_write(st, false, false, clock, -1);
|
||||
if (ret)
|
||||
return ret;
|
||||
st->chip_config.clk = clock;
|
||||
@ -332,7 +336,7 @@ static int inv_mpu6050_clock_switch(struct inv_mpu6050_state *st,
|
||||
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en,
|
||||
unsigned int mask)
|
||||
{
|
||||
unsigned int sleep;
|
||||
unsigned int sleep, val;
|
||||
u8 pwr_mgmt2, user_ctrl;
|
||||
int ret;
|
||||
|
||||
@ -345,12 +349,20 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en,
|
||||
mask &= ~INV_MPU6050_SENSOR_TEMP;
|
||||
if (mask & INV_MPU6050_SENSOR_MAGN && en == st->chip_config.magn_en)
|
||||
mask &= ~INV_MPU6050_SENSOR_MAGN;
|
||||
if (mask & INV_MPU6050_SENSOR_WOM && en == st->chip_config.wom_en)
|
||||
mask &= ~INV_MPU6050_SENSOR_WOM;
|
||||
|
||||
/* force accel on if WoM is on and not going off */
|
||||
if (!en && (mask & INV_MPU6050_SENSOR_ACCL) && st->chip_config.wom_en &&
|
||||
!(mask & INV_MPU6050_SENSOR_WOM))
|
||||
mask &= ~INV_MPU6050_SENSOR_ACCL;
|
||||
|
||||
if (mask == 0)
|
||||
return 0;
|
||||
|
||||
/* turn on/off temperature sensor */
|
||||
if (mask & INV_MPU6050_SENSOR_TEMP) {
|
||||
ret = inv_mpu6050_pwr_mgmt_1_write(st, false, -1, !en);
|
||||
ret = inv_mpu6050_pwr_mgmt_1_write(st, false, false, -1, !en);
|
||||
if (ret)
|
||||
return ret;
|
||||
st->chip_config.temp_en = en;
|
||||
@ -439,6 +451,16 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en,
|
||||
}
|
||||
}
|
||||
|
||||
/* enable/disable accel intelligence control */
|
||||
if (mask & INV_MPU6050_SENSOR_WOM) {
|
||||
val = en ? INV_MPU6500_BIT_ACCEL_INTEL_EN |
|
||||
INV_MPU6500_BIT_ACCEL_INTEL_MODE : 0;
|
||||
ret = regmap_write(st->map, INV_MPU6500_REG_ACCEL_INTEL_CTRL, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
st->chip_config.wom_en = en;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -447,7 +469,7 @@ static int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st,
|
||||
{
|
||||
int result;
|
||||
|
||||
result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, -1, -1);
|
||||
result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, false, -1, -1);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
@ -477,22 +499,9 @@ static int inv_mpu6050_set_gyro_fsr(struct inv_mpu6050_state *st,
|
||||
return regmap_write(st->map, st->reg->gyro_config, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent
|
||||
*
|
||||
* MPU60xx/MPU9150 use only 1 register for accelerometer + gyroscope
|
||||
* MPU6500 and above have a dedicated register for accelerometer
|
||||
*/
|
||||
static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st,
|
||||
enum inv_mpu6050_filter_e val)
|
||||
static int inv_mpu6050_set_accel_lpf_regs(struct inv_mpu6050_state *st,
|
||||
enum inv_mpu6050_filter_e val)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = regmap_write(st->map, st->reg->lpf, val);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/* set accel lpf */
|
||||
switch (st->chip_type) {
|
||||
case INV_MPU6050:
|
||||
case INV_MPU6000:
|
||||
@ -511,6 +520,25 @@ static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st,
|
||||
return regmap_write(st->map, st->reg->accel_lpf, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent
|
||||
*
|
||||
* MPU60xx/MPU9150 use only 1 register for accelerometer + gyroscope
|
||||
* MPU6500 and above have a dedicated register for accelerometer
|
||||
*/
|
||||
static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st,
|
||||
enum inv_mpu6050_filter_e val)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = regmap_write(st->map, st->reg->lpf, val);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/* set accel lpf */
|
||||
return inv_mpu6050_set_accel_lpf_regs(st, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* inv_mpu6050_init_config() - Initialize hardware, disable FIFO.
|
||||
*
|
||||
@ -893,6 +921,317 @@ error_write_raw_unlock:
|
||||
return result;
|
||||
}
|
||||
|
||||
static u64 inv_mpu6050_convert_wom_to_roc(unsigned int threshold, unsigned int freq_div)
|
||||
{
|
||||
/* 4mg per LSB converted in m/s² in micro (1000000) */
|
||||
const unsigned int convert = 4U * 9807U;
|
||||
u64 value;
|
||||
|
||||
value = threshold * convert;
|
||||
|
||||
/* compute the differential by multiplying by the frequency */
|
||||
return div_u64(value * INV_MPU6050_INTERNAL_FREQ_HZ, freq_div);
|
||||
}
|
||||
|
||||
static unsigned int inv_mpu6050_convert_roc_to_wom(u64 roc, unsigned int freq_div)
|
||||
{
|
||||
/* 4mg per LSB converted in m/s² in micro (1000000) */
|
||||
const unsigned int convert = 4U * 9807U;
|
||||
u64 value;
|
||||
|
||||
/* return 0 only if roc is 0 */
|
||||
if (roc == 0)
|
||||
return 0;
|
||||
|
||||
value = div_u64(roc * freq_div, convert * INV_MPU6050_INTERNAL_FREQ_HZ);
|
||||
|
||||
/* limit value to 8 bits and prevent 0 */
|
||||
return min(255, max(1, value));
|
||||
}
|
||||
|
||||
static int inv_mpu6050_set_wom_int(struct inv_mpu6050_state *st, bool on)
|
||||
{
|
||||
unsigned int reg_val, val;
|
||||
|
||||
switch (st->chip_type) {
|
||||
case INV_MPU6050:
|
||||
case INV_MPU6500:
|
||||
case INV_MPU6515:
|
||||
case INV_MPU6880:
|
||||
case INV_MPU6000:
|
||||
case INV_MPU9150:
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
reg_val = INV_MPU6500_BIT_WOM_INT_EN;
|
||||
break;
|
||||
default:
|
||||
reg_val = INV_ICM20608_BIT_WOM_INT_EN;
|
||||
break;
|
||||
}
|
||||
|
||||
val = on ? reg_val : 0;
|
||||
|
||||
return regmap_update_bits(st->map, st->reg->int_enable, reg_val, val);
|
||||
}
|
||||
|
||||
static int inv_mpu6050_set_wom_threshold(struct inv_mpu6050_state *st, u64 value,
|
||||
unsigned int freq_div)
|
||||
{
|
||||
unsigned int threshold;
|
||||
int result;
|
||||
|
||||
/* convert roc to wom threshold and convert back to handle clipping */
|
||||
threshold = inv_mpu6050_convert_roc_to_wom(value, freq_div);
|
||||
value = inv_mpu6050_convert_wom_to_roc(threshold, freq_div);
|
||||
|
||||
dev_dbg(regmap_get_device(st->map), "wom_threshold: 0x%x\n", threshold);
|
||||
|
||||
switch (st->chip_type) {
|
||||
case INV_ICM20609:
|
||||
case INV_ICM20689:
|
||||
case INV_ICM20600:
|
||||
case INV_ICM20602:
|
||||
case INV_ICM20690:
|
||||
st->data[0] = threshold;
|
||||
st->data[1] = threshold;
|
||||
st->data[2] = threshold;
|
||||
result = regmap_bulk_write(st->map, INV_ICM20609_REG_ACCEL_WOM_X_THR,
|
||||
st->data, 3);
|
||||
break;
|
||||
default:
|
||||
result = regmap_write(st->map, INV_MPU6500_REG_WOM_THRESHOLD, threshold);
|
||||
break;
|
||||
}
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
st->chip_config.roc_threshold = value;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int inv_mpu6050_set_lp_odr(struct inv_mpu6050_state *st, unsigned int freq_div,
|
||||
unsigned int *lp_div)
|
||||
{
|
||||
static const unsigned int freq_dividers[] = {2, 4, 8, 16, 32, 64, 128, 256};
|
||||
static const unsigned int reg_values[] = {
|
||||
INV_MPU6050_LPOSC_500HZ, INV_MPU6050_LPOSC_250HZ,
|
||||
INV_MPU6050_LPOSC_125HZ, INV_MPU6050_LPOSC_62HZ,
|
||||
INV_MPU6050_LPOSC_31HZ, INV_MPU6050_LPOSC_16HZ,
|
||||
INV_MPU6050_LPOSC_8HZ, INV_MPU6050_LPOSC_4HZ,
|
||||
};
|
||||
unsigned int val, i;
|
||||
|
||||
switch (st->chip_type) {
|
||||
case INV_ICM20609:
|
||||
case INV_ICM20689:
|
||||
case INV_ICM20600:
|
||||
case INV_ICM20602:
|
||||
case INV_ICM20690:
|
||||
/* nothing to do */
|
||||
*lp_div = INV_MPU6050_FREQ_DIVIDER(st);
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* found the nearest superior frequency divider */
|
||||
i = ARRAY_SIZE(reg_values) - 1;
|
||||
val = reg_values[i];
|
||||
*lp_div = freq_dividers[i];
|
||||
for (i = 0; i < ARRAY_SIZE(freq_dividers); ++i) {
|
||||
if (freq_div <= freq_dividers[i]) {
|
||||
val = reg_values[i];
|
||||
*lp_div = freq_dividers[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(regmap_get_device(st->map), "lp_odr: 0x%x\n", val);
|
||||
return regmap_write(st->map, INV_MPU6500_REG_LP_ODR, val);
|
||||
}
|
||||
|
||||
static int inv_mpu6050_set_wom_lp(struct inv_mpu6050_state *st, bool on)
|
||||
{
|
||||
unsigned int lp_div;
|
||||
int result;
|
||||
|
||||
if (on) {
|
||||
/* set low power ODR */
|
||||
result = inv_mpu6050_set_lp_odr(st, INV_MPU6050_FREQ_DIVIDER(st), &lp_div);
|
||||
if (result)
|
||||
return result;
|
||||
/* disable accel low pass filter */
|
||||
result = inv_mpu6050_set_accel_lpf_regs(st, INV_MPU6050_FILTER_NOLPF);
|
||||
if (result)
|
||||
return result;
|
||||
/* update wom threshold with new low-power frequency divider */
|
||||
result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold, lp_div);
|
||||
if (result)
|
||||
return result;
|
||||
/* set cycle mode */
|
||||
result = inv_mpu6050_pwr_mgmt_1_write(st, false, true, -1, -1);
|
||||
} else {
|
||||
/* disable cycle mode */
|
||||
result = inv_mpu6050_pwr_mgmt_1_write(st, false, false, -1, -1);
|
||||
if (result)
|
||||
return result;
|
||||
/* restore wom threshold */
|
||||
result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold,
|
||||
INV_MPU6050_FREQ_DIVIDER(st));
|
||||
if (result)
|
||||
return result;
|
||||
/* restore accel low pass filter */
|
||||
result = inv_mpu6050_set_accel_lpf_regs(st, st->chip_config.lpf);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int inv_mpu6050_enable_wom(struct inv_mpu6050_state *st, bool en)
|
||||
{
|
||||
struct device *pdev = regmap_get_device(st->map);
|
||||
unsigned int mask;
|
||||
int result;
|
||||
|
||||
if (en) {
|
||||
result = pm_runtime_resume_and_get(pdev);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
mask = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_WOM;
|
||||
result = inv_mpu6050_switch_engine(st, true, mask);
|
||||
if (result)
|
||||
goto error_suspend;
|
||||
|
||||
result = inv_mpu6050_set_wom_int(st, true);
|
||||
if (result)
|
||||
goto error_suspend;
|
||||
} else {
|
||||
result = inv_mpu6050_set_wom_int(st, false);
|
||||
if (result)
|
||||
dev_err(pdev, "error %d disabling WoM interrupt bit", result);
|
||||
|
||||
/* disable only WoM and let accel be disabled by autosuspend */
|
||||
result = inv_mpu6050_switch_engine(st, false, INV_MPU6050_SENSOR_WOM);
|
||||
if (result) {
|
||||
dev_err(pdev, "error %d disabling WoM force off", result);
|
||||
/* force WoM off */
|
||||
st->chip_config.wom_en = false;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(pdev);
|
||||
pm_runtime_put_autosuspend(pdev);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
error_suspend:
|
||||
pm_runtime_mark_last_busy(pdev);
|
||||
pm_runtime_put_autosuspend(pdev);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int inv_mpu6050_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 inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
/* support only WoM (accel roc rising) event */
|
||||
if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
|
||||
dir != IIO_EV_DIR_RISING)
|
||||
return -EINVAL;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
|
||||
return st->chip_config.wom_en ? 1 : 0;
|
||||
}
|
||||
|
||||
static int inv_mpu6050_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 inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
int enable;
|
||||
|
||||
/* support only WoM (accel roc rising) event */
|
||||
if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
|
||||
dir != IIO_EV_DIR_RISING)
|
||||
return -EINVAL;
|
||||
|
||||
enable = !!state;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
|
||||
if (st->chip_config.wom_en == enable)
|
||||
return 0;
|
||||
|
||||
return inv_mpu6050_enable_wom(st, enable);
|
||||
}
|
||||
|
||||
static int inv_mpu6050_read_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int *val, int *val2)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
u32 rem;
|
||||
|
||||
/* support only WoM (accel roc rising) event value */
|
||||
if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
|
||||
dir != IIO_EV_DIR_RISING || info != IIO_EV_INFO_VALUE)
|
||||
return -EINVAL;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
|
||||
/* return value in micro */
|
||||
*val = div_u64_rem(st->chip_config.roc_threshold, 1000000U, &rem);
|
||||
*val2 = rem;
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
|
||||
static int inv_mpu6050_write_event_value(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info,
|
||||
int val, int val2)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
struct device *pdev = regmap_get_device(st->map);
|
||||
u64 value;
|
||||
int result;
|
||||
|
||||
/* support only WoM (accel roc rising) event value */
|
||||
if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC ||
|
||||
dir != IIO_EV_DIR_RISING || info != IIO_EV_INFO_VALUE)
|
||||
return -EINVAL;
|
||||
|
||||
if (val < 0 || val2 < 0)
|
||||
return -EINVAL;
|
||||
|
||||
guard(mutex)(&st->lock);
|
||||
|
||||
result = pm_runtime_resume_and_get(pdev);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
value = (u64)val * 1000000ULL + (u64)val2;
|
||||
result = inv_mpu6050_set_wom_threshold(st, value, INV_MPU6050_FREQ_DIVIDER(st));
|
||||
|
||||
pm_runtime_mark_last_busy(pdev);
|
||||
pm_runtime_put_autosuspend(pdev);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* inv_mpu6050_set_lpf() - set low pass filer based on fifo rate.
|
||||
*
|
||||
@ -989,6 +1328,12 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
|
||||
if (result)
|
||||
goto fifo_rate_fail_power_off;
|
||||
|
||||
/* update wom threshold since roc is dependent on sampling frequency */
|
||||
result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold,
|
||||
INV_MPU6050_FREQ_DIVIDER(st));
|
||||
if (result)
|
||||
goto fifo_rate_fail_power_off;
|
||||
|
||||
pm_runtime_mark_last_busy(pdev);
|
||||
fifo_rate_fail_power_off:
|
||||
pm_runtime_put_autosuspend(pdev);
|
||||
@ -1089,6 +1434,15 @@ static const struct iio_chan_spec_ext_info inv_ext_info[] = {
|
||||
{ }
|
||||
};
|
||||
|
||||
static const struct iio_event_spec inv_wom_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_ROC,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
|
||||
BIT(IIO_EV_INFO_VALUE),
|
||||
},
|
||||
};
|
||||
|
||||
#define INV_MPU6050_CHAN(_type, _channel2, _index) \
|
||||
{ \
|
||||
.type = _type, \
|
||||
@ -1124,7 +1478,17 @@ static const struct iio_chan_spec_ext_info inv_ext_info[] = {
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec inv_mpu_channels[] = {
|
||||
#define INV_MPU6050_EVENT_CHAN(_type, _channel2, _events, _events_nb) \
|
||||
{ \
|
||||
.type = _type, \
|
||||
.modified = 1, \
|
||||
.channel2 = _channel2, \
|
||||
.event_spec = _events, \
|
||||
.num_event_specs = _events_nb, \
|
||||
.scan_index = -1, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec inv_mpu6050_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP),
|
||||
|
||||
INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP),
|
||||
@ -1138,6 +1502,23 @@ static const struct iio_chan_spec inv_mpu_channels[] = {
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec inv_mpu6500_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP),
|
||||
|
||||
INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP),
|
||||
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X),
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y),
|
||||
INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z),
|
||||
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X),
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y),
|
||||
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
|
||||
|
||||
INV_MPU6050_EVENT_CHAN(IIO_ACCEL, IIO_MOD_X_OR_Y_OR_Z,
|
||||
inv_wom_events, ARRAY_SIZE(inv_wom_events)),
|
||||
};
|
||||
|
||||
#define INV_MPU6050_SCAN_MASK_3AXIS_ACCEL \
|
||||
(BIT(INV_MPU6050_SCAN_ACCL_X) \
|
||||
| BIT(INV_MPU6050_SCAN_ACCL_Y) \
|
||||
@ -1326,6 +1707,10 @@ static const struct iio_info mpu_info = {
|
||||
.write_raw = &inv_mpu6050_write_raw,
|
||||
.write_raw_get_fmt = &inv_write_raw_get_fmt,
|
||||
.attrs = &inv_attribute_group,
|
||||
.read_event_config = inv_mpu6050_read_event_config,
|
||||
.write_event_config = inv_mpu6050_write_event_config,
|
||||
.read_event_value = inv_mpu6050_read_event_value,
|
||||
.write_event_value = inv_mpu6050_write_event_value,
|
||||
.validate_trigger = inv_mpu6050_validate_trigger,
|
||||
.debugfs_reg_access = &inv_mpu6050_reg_access,
|
||||
};
|
||||
@ -1537,6 +1922,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
irq_type);
|
||||
return -EINVAL;
|
||||
}
|
||||
device_set_wakeup_capable(dev, true);
|
||||
|
||||
st->vdd_supply = devm_regulator_get(dev, "vdd");
|
||||
if (IS_ERR(st->vdd_supply))
|
||||
@ -1613,6 +1999,12 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
return result;
|
||||
|
||||
switch (chip_type) {
|
||||
case INV_MPU6000:
|
||||
case INV_MPU6050:
|
||||
indio_dev->channels = inv_mpu6050_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu6050_channels);
|
||||
indio_dev->available_scan_masks = inv_mpu_scan_masks;
|
||||
break;
|
||||
case INV_MPU9150:
|
||||
indio_dev->channels = inv_mpu9150_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu9150_channels);
|
||||
@ -1626,13 +2018,13 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
break;
|
||||
case INV_ICM20600:
|
||||
case INV_ICM20602:
|
||||
indio_dev->channels = inv_mpu_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
|
||||
indio_dev->channels = inv_mpu6500_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu6500_channels);
|
||||
indio_dev->available_scan_masks = inv_icm20602_scan_masks;
|
||||
break;
|
||||
default:
|
||||
indio_dev->channels = inv_mpu_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
|
||||
indio_dev->channels = inv_mpu6500_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu6500_channels);
|
||||
indio_dev->available_scan_masks = inv_mpu_scan_masks;
|
||||
break;
|
||||
}
|
||||
@ -1641,9 +2033,18 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
* auxiliary device in use. Otherwise Going back to 6-axis only.
|
||||
*/
|
||||
if (st->magn_disabled) {
|
||||
indio_dev->channels = inv_mpu_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
|
||||
indio_dev->available_scan_masks = inv_mpu_scan_masks;
|
||||
switch (chip_type) {
|
||||
case INV_MPU9150:
|
||||
indio_dev->channels = inv_mpu6050_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu6050_channels);
|
||||
indio_dev->available_scan_masks = inv_mpu_scan_masks;
|
||||
break;
|
||||
default:
|
||||
indio_dev->channels = inv_mpu6500_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(inv_mpu6500_channels);
|
||||
indio_dev->available_scan_masks = inv_mpu_scan_masks;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
indio_dev->info = &mpu_info;
|
||||
@ -1687,16 +2088,27 @@ static int inv_mpu_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
bool wakeup;
|
||||
int result;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
result = inv_mpu_core_enable_regulator_vddio(st);
|
||||
if (result)
|
||||
goto out_unlock;
|
||||
guard(mutex)(&st->lock);
|
||||
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
goto out_unlock;
|
||||
wakeup = device_may_wakeup(dev) && st->chip_config.wom_en;
|
||||
|
||||
if (wakeup) {
|
||||
enable_irq(st->irq);
|
||||
disable_irq_wake(st->irq);
|
||||
result = inv_mpu6050_set_wom_lp(st, false);
|
||||
if (result)
|
||||
return result;
|
||||
} else {
|
||||
result = inv_mpu_core_enable_regulator_vddio(st);
|
||||
if (result)
|
||||
return result;
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
@ -1704,14 +2116,17 @@ static int inv_mpu_resume(struct device *dev)
|
||||
|
||||
result = inv_mpu6050_switch_engine(st, true, st->suspended_sensors);
|
||||
if (result)
|
||||
goto out_unlock;
|
||||
return result;
|
||||
|
||||
if (st->chip_config.wom_en && !wakeup) {
|
||||
result = inv_mpu6050_set_wom_int(st, true);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
result = inv_mpu6050_prepare_fifo(st, true);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1719,23 +2134,30 @@ static int inv_mpu_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
bool wakeup;
|
||||
int result;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
guard(mutex)(&st->lock);
|
||||
|
||||
st->suspended_sensors = 0;
|
||||
if (pm_runtime_suspended(dev)) {
|
||||
result = 0;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (pm_runtime_suspended(dev))
|
||||
return 0;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
result = inv_mpu6050_prepare_fifo(st, false);
|
||||
if (result)
|
||||
goto out_unlock;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (st->chip_config.accl_en)
|
||||
wakeup = device_may_wakeup(dev) && st->chip_config.wom_en;
|
||||
|
||||
if (st->chip_config.wom_en && !wakeup) {
|
||||
result = inv_mpu6050_set_wom_int(st, false);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
|
||||
if (st->chip_config.accl_en && !wakeup)
|
||||
st->suspended_sensors |= INV_MPU6050_SENSOR_ACCL;
|
||||
if (st->chip_config.gyro_en)
|
||||
st->suspended_sensors |= INV_MPU6050_SENSOR_GYRO;
|
||||
@ -1743,19 +2165,26 @@ static int inv_mpu_suspend(struct device *dev)
|
||||
st->suspended_sensors |= INV_MPU6050_SENSOR_TEMP;
|
||||
if (st->chip_config.magn_en)
|
||||
st->suspended_sensors |= INV_MPU6050_SENSOR_MAGN;
|
||||
if (st->chip_config.wom_en && !wakeup)
|
||||
st->suspended_sensors |= INV_MPU6050_SENSOR_WOM;
|
||||
result = inv_mpu6050_switch_engine(st, false, st->suspended_sensors);
|
||||
if (result)
|
||||
goto out_unlock;
|
||||
return result;
|
||||
|
||||
result = inv_mpu6050_set_power_itg(st, false);
|
||||
if (result)
|
||||
goto out_unlock;
|
||||
if (wakeup) {
|
||||
result = inv_mpu6050_set_wom_lp(st, true);
|
||||
if (result)
|
||||
return result;
|
||||
enable_irq_wake(st->irq);
|
||||
disable_irq(st->irq);
|
||||
} else {
|
||||
result = inv_mpu6050_set_power_itg(st, false);
|
||||
if (result)
|
||||
return result;
|
||||
inv_mpu_core_disable_regulator_vddio(st);
|
||||
}
|
||||
|
||||
inv_mpu_core_disable_regulator_vddio(st);
|
||||
out_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int inv_mpu_runtime_suspend(struct device *dev)
|
||||
@ -1767,7 +2196,8 @@ static int inv_mpu_runtime_suspend(struct device *dev)
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
sensors = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_GYRO |
|
||||
INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN;
|
||||
INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN |
|
||||
INV_MPU6050_SENSOR_WOM;
|
||||
ret = inv_mpu6050_switch_engine(st, false, sensors);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
@ -88,11 +88,12 @@ enum inv_devices {
|
||||
INV_NUM_PARTS
|
||||
};
|
||||
|
||||
/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer */
|
||||
/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer, WoM */
|
||||
#define INV_MPU6050_SENSOR_ACCL BIT(0)
|
||||
#define INV_MPU6050_SENSOR_GYRO BIT(1)
|
||||
#define INV_MPU6050_SENSOR_TEMP BIT(2)
|
||||
#define INV_MPU6050_SENSOR_MAGN BIT(3)
|
||||
#define INV_MPU6050_SENSOR_WOM BIT(4)
|
||||
|
||||
/**
|
||||
* struct inv_mpu6050_chip_config - Cached chip configuration data.
|
||||
@ -104,11 +105,13 @@ enum inv_devices {
|
||||
* @gyro_en: gyro engine enabled
|
||||
* @temp_en: temperature sensor enabled
|
||||
* @magn_en: magn engine (i2c master) enabled
|
||||
* @wom_en: Wake-on-Motion enabled
|
||||
* @accl_fifo_enable: enable accel data output
|
||||
* @gyro_fifo_enable: enable gyro data output
|
||||
* @temp_fifo_enable: enable temp data output
|
||||
* @magn_fifo_enable: enable magn data output
|
||||
* @divider: chip sample rate divider (sample rate divider - 1)
|
||||
* @roc_threshold: save ROC threshold (WoM) set value
|
||||
*/
|
||||
struct inv_mpu6050_chip_config {
|
||||
unsigned int clk:3;
|
||||
@ -119,12 +122,14 @@ struct inv_mpu6050_chip_config {
|
||||
unsigned int gyro_en:1;
|
||||
unsigned int temp_en:1;
|
||||
unsigned int magn_en:1;
|
||||
unsigned int wom_en:1;
|
||||
unsigned int accl_fifo_enable:1;
|
||||
unsigned int gyro_fifo_enable:1;
|
||||
unsigned int temp_fifo_enable:1;
|
||||
unsigned int magn_fifo_enable:1;
|
||||
u8 divider;
|
||||
u8 user_ctrl;
|
||||
u64 roc_threshold;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -180,6 +185,7 @@ struct inv_mpu6050_hw {
|
||||
* @magn_orient: magnetometer sensor chip orientation if available.
|
||||
* @suspended_sensors: sensors mask of sensors turned off for suspend
|
||||
* @data: read buffer used for bulk reads.
|
||||
* @it_timestamp: interrupt timestamp.
|
||||
*/
|
||||
struct inv_mpu6050_state {
|
||||
struct mutex lock;
|
||||
@ -205,6 +211,7 @@ struct inv_mpu6050_state {
|
||||
unsigned int suspended_sensors;
|
||||
bool level_shifter;
|
||||
u8 *data;
|
||||
s64 it_timestamp;
|
||||
};
|
||||
|
||||
/*register and associated bit definition*/
|
||||
@ -256,12 +263,16 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU6050_REG_INT_ENABLE 0x38
|
||||
#define INV_MPU6050_BIT_DATA_RDY_EN 0x01
|
||||
#define INV_MPU6050_BIT_DMP_INT_EN 0x02
|
||||
#define INV_MPU6500_BIT_WOM_INT_EN BIT(6)
|
||||
#define INV_ICM20608_BIT_WOM_INT_EN GENMASK(7, 5)
|
||||
|
||||
#define INV_MPU6050_REG_RAW_ACCEL 0x3B
|
||||
#define INV_MPU6050_REG_TEMPERATURE 0x41
|
||||
#define INV_MPU6050_REG_RAW_GYRO 0x43
|
||||
|
||||
#define INV_MPU6050_REG_INT_STATUS 0x3A
|
||||
#define INV_MPU6500_BIT_WOM_INT BIT(6)
|
||||
#define INV_ICM20608_BIT_WOM_INT GENMASK(7, 5)
|
||||
#define INV_MPU6050_BIT_FIFO_OVERFLOW_INT 0x10
|
||||
#define INV_MPU6050_BIT_RAW_DATA_RDY_INT 0x01
|
||||
|
||||
@ -294,6 +305,7 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU6050_REG_PWR_MGMT_1 0x6B
|
||||
#define INV_MPU6050_BIT_H_RESET 0x80
|
||||
#define INV_MPU6050_BIT_SLEEP 0x40
|
||||
#define INV_MPU6050_BIT_CYCLE 0x20
|
||||
#define INV_MPU6050_BIT_TEMP_DIS 0x08
|
||||
#define INV_MPU6050_BIT_CLK_MASK 0x7
|
||||
|
||||
@ -301,6 +313,11 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38
|
||||
#define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07
|
||||
|
||||
/* ICM20609 registers */
|
||||
#define INV_ICM20609_REG_ACCEL_WOM_X_THR 0x20
|
||||
#define INV_ICM20609_REG_ACCEL_WOM_Y_THR 0x21
|
||||
#define INV_ICM20609_REG_ACCEL_WOM_Z_THR 0x22
|
||||
|
||||
/* ICM20602 register */
|
||||
#define INV_ICM20602_REG_I2C_IF 0x70
|
||||
#define INV_ICM20602_BIT_I2C_IF_DIS 0x40
|
||||
@ -320,6 +337,11 @@ struct inv_mpu6050_state {
|
||||
/* mpu6500 registers */
|
||||
#define INV_MPU6500_REG_ACCEL_CONFIG_2 0x1D
|
||||
#define INV_ICM20689_BITS_FIFO_SIZE_MAX 0xC0
|
||||
#define INV_MPU6500_REG_LP_ODR 0x1E
|
||||
#define INV_MPU6500_REG_WOM_THRESHOLD 0x1F
|
||||
#define INV_MPU6500_REG_ACCEL_INTEL_CTRL 0x69
|
||||
#define INV_MPU6500_BIT_ACCEL_INTEL_EN BIT(7)
|
||||
#define INV_MPU6500_BIT_ACCEL_INTEL_MODE BIT(6)
|
||||
#define INV_MPU6500_REG_ACCEL_OFFSET 0x77
|
||||
|
||||
/* delay time in milliseconds */
|
||||
@ -432,6 +454,18 @@ enum inv_mpu6050_filter_e {
|
||||
NUM_MPU6050_FILTER
|
||||
};
|
||||
|
||||
enum inv_mpu6050_lposc_e {
|
||||
INV_MPU6050_LPOSC_4HZ = 4,
|
||||
INV_MPU6050_LPOSC_8HZ,
|
||||
INV_MPU6050_LPOSC_16HZ,
|
||||
INV_MPU6050_LPOSC_31HZ,
|
||||
INV_MPU6050_LPOSC_62HZ,
|
||||
INV_MPU6050_LPOSC_125HZ,
|
||||
INV_MPU6050_LPOSC_250HZ,
|
||||
INV_MPU6050_LPOSC_500HZ,
|
||||
NUM_MPU6050_LPOSC,
|
||||
};
|
||||
|
||||
/* IIO attribute address */
|
||||
enum INV_MPU6050_IIO_ATTR_ADDR {
|
||||
ATTR_GYRO_MATRIX,
|
||||
|
@ -33,10 +33,8 @@ static int inv_reset_fifo(struct iio_dev *indio_dev)
|
||||
|
||||
reset_fifo_fail:
|
||||
dev_err(regmap_get_device(st->map), "reset fifo failed %d\n", result);
|
||||
result = regmap_write(st->map, st->reg->int_enable,
|
||||
INV_MPU6050_BIT_DATA_RDY_EN);
|
||||
|
||||
return result;
|
||||
return regmap_update_bits(st->map, st->reg->int_enable,
|
||||
INV_MPU6050_BIT_DATA_RDY_EN, INV_MPU6050_BIT_DATA_RDY_EN);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -53,21 +51,10 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
|
||||
u32 fifo_period;
|
||||
s64 timestamp;
|
||||
u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];
|
||||
int int_status;
|
||||
size_t i, nb;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
/* ack interrupt and check status */
|
||||
result = regmap_read(st->map, st->reg->int_status, &int_status);
|
||||
if (result) {
|
||||
dev_err(regmap_get_device(st->map),
|
||||
"failed to ack interrupt\n");
|
||||
goto flush_fifo;
|
||||
}
|
||||
if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT))
|
||||
goto end_session;
|
||||
|
||||
if (!(st->chip_config.accl_fifo_enable |
|
||||
st->chip_config.gyro_fifo_enable |
|
||||
st->chip_config.magn_fifo_enable))
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/iio/common/inv_sensors_timestamp.h>
|
||||
#include <linux/iio/events.h>
|
||||
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
@ -135,11 +136,13 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable)
|
||||
ret = regmap_write(st->map, st->reg->user_ctrl, d);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* enable interrupt */
|
||||
ret = regmap_write(st->map, st->reg->int_enable,
|
||||
INV_MPU6050_BIT_DATA_RDY_EN);
|
||||
/* enable data interrupt */
|
||||
ret = regmap_update_bits(st->map, st->reg->int_enable,
|
||||
INV_MPU6050_BIT_DATA_RDY_EN, INV_MPU6050_BIT_DATA_RDY_EN);
|
||||
} else {
|
||||
ret = regmap_write(st->map, st->reg->int_enable, 0);
|
||||
/* disable data interrupt */
|
||||
ret = regmap_update_bits(st->map, st->reg->int_enable,
|
||||
INV_MPU6050_BIT_DATA_RDY_EN, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(st->map, st->reg->fifo_en, 0);
|
||||
@ -172,9 +175,9 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
|
||||
return result;
|
||||
/*
|
||||
* In case autosuspend didn't trigger, turn off first not
|
||||
* required sensors.
|
||||
* required sensors excepted WoM
|
||||
*/
|
||||
result = inv_mpu6050_switch_engine(st, false, ~scan);
|
||||
result = inv_mpu6050_switch_engine(st, false, ~scan & ~INV_MPU6050_SENSOR_WOM);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
result = inv_mpu6050_switch_engine(st, true, scan);
|
||||
@ -226,6 +229,65 @@ static const struct iio_trigger_ops inv_mpu_trigger_ops = {
|
||||
.set_trigger_state = &inv_mpu_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
static irqreturn_t inv_mpu6050_interrupt_timestamp(int irq, void *p)
|
||||
{
|
||||
struct iio_dev *indio_dev = p;
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->it_timestamp = iio_get_time_ns(indio_dev);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t inv_mpu6050_interrupt_handle(int irq, void *p)
|
||||
{
|
||||
struct iio_dev *indio_dev = p;
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
unsigned int int_status, wom_bits;
|
||||
u64 ev_code;
|
||||
int result;
|
||||
|
||||
switch (st->chip_type) {
|
||||
case INV_MPU6050:
|
||||
case INV_MPU6500:
|
||||
case INV_MPU6515:
|
||||
case INV_MPU6880:
|
||||
case INV_MPU6000:
|
||||
case INV_MPU9150:
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
wom_bits = INV_MPU6500_BIT_WOM_INT;
|
||||
break;
|
||||
default:
|
||||
wom_bits = INV_ICM20608_BIT_WOM_INT;
|
||||
break;
|
||||
}
|
||||
|
||||
scoped_guard(mutex, &st->lock) {
|
||||
/* ack interrupt and check status */
|
||||
result = regmap_read(st->map, st->reg->int_status, &int_status);
|
||||
if (result) {
|
||||
dev_err(regmap_get_device(st->map), "failed to ack interrupt\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* handle WoM event */
|
||||
if (st->chip_config.wom_en && (int_status & wom_bits)) {
|
||||
ev_code = IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z,
|
||||
IIO_EV_TYPE_ROC, IIO_EV_DIR_RISING);
|
||||
iio_push_event(indio_dev, ev_code, st->it_timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
/* handle raw data interrupt */
|
||||
if (int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT) {
|
||||
indio_dev->pollfunc->timestamp = st->it_timestamp;
|
||||
iio_trigger_poll_nested(st->trig);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type)
|
||||
{
|
||||
int ret;
|
||||
@ -238,11 +300,10 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type)
|
||||
if (!st->trig)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = devm_request_irq(&indio_dev->dev, st->irq,
|
||||
&iio_trigger_generic_data_rdy_poll,
|
||||
irq_type,
|
||||
"inv_mpu",
|
||||
st->trig);
|
||||
ret = devm_request_threaded_irq(&indio_dev->dev, st->irq,
|
||||
&inv_mpu6050_interrupt_timestamp,
|
||||
&inv_mpu6050_interrupt_handle,
|
||||
irq_type, "inv_mpu", indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -2726,7 +2726,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
|
||||
if (!hw)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, (void *)hw);
|
||||
dev_set_drvdata(dev, hw);
|
||||
|
||||
mutex_init(&hw->fifo_lock);
|
||||
mutex_init(&hw->conf_lock);
|
||||
|
@ -43,10 +43,12 @@
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/iio/backend.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
struct iio_backend {
|
||||
struct list_head entry;
|
||||
const struct iio_backend_ops *ops;
|
||||
struct device *frontend_dev;
|
||||
struct device *dev;
|
||||
struct module *owner;
|
||||
void *priv;
|
||||
@ -186,6 +188,44 @@ int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan,
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(iio_backend_data_format_set, IIO_BACKEND);
|
||||
|
||||
/**
|
||||
* iio_backend_data_source_set - Select data source
|
||||
* @back: Backend device
|
||||
* @chan: Channel number
|
||||
* @data: Data source
|
||||
*
|
||||
* A given backend may have different sources to stream/sync data. This allows
|
||||
* to choose that source.
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 on success, negative error number on failure.
|
||||
*/
|
||||
int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan,
|
||||
enum iio_backend_data_source data)
|
||||
{
|
||||
if (data >= IIO_BACKEND_DATA_SOURCE_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
return iio_backend_op_call(back, data_source_set, chan, data);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(iio_backend_data_source_set, IIO_BACKEND);
|
||||
|
||||
/**
|
||||
* iio_backend_set_sampling_freq - Set channel sampling rate
|
||||
* @back: Backend device
|
||||
* @chan: Channel number
|
||||
* @sample_rate_hz: Sample rate
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 on success, negative error number on failure.
|
||||
*/
|
||||
int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan,
|
||||
u64 sample_rate_hz)
|
||||
{
|
||||
return iio_backend_op_call(back, set_sample_rate, chan, sample_rate_hz);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(iio_backend_set_sampling_freq, IIO_BACKEND);
|
||||
|
||||
static void iio_backend_free_buffer(void *arg)
|
||||
{
|
||||
struct iio_backend_buffer_pair *pair = arg;
|
||||
@ -231,6 +271,143 @@ int devm_iio_backend_request_buffer(struct device *dev,
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(devm_iio_backend_request_buffer, IIO_BACKEND);
|
||||
|
||||
static struct iio_backend *iio_backend_from_indio_dev_parent(const struct device *dev)
|
||||
{
|
||||
struct iio_backend *back = ERR_PTR(-ENODEV), *iter;
|
||||
|
||||
/*
|
||||
* We deliberately go through all backends even after finding a match.
|
||||
* The reason is that we want to catch frontend devices which have more
|
||||
* than one backend in which case returning the first we find is bogus.
|
||||
* For those cases, frontends need to explicitly define
|
||||
* get_iio_backend() in struct iio_info.
|
||||
*/
|
||||
guard(mutex)(&iio_back_lock);
|
||||
list_for_each_entry(iter, &iio_back_list, entry) {
|
||||
if (dev == iter->frontend_dev) {
|
||||
if (!IS_ERR(back)) {
|
||||
dev_warn(dev,
|
||||
"Multiple backends! get_iio_backend() needs to be implemented");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
back = iter;
|
||||
}
|
||||
}
|
||||
|
||||
return back;
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_backend_ext_info_get - IIO ext_info read callback
|
||||
* @indio_dev: IIO device
|
||||
* @private: Data private to the driver
|
||||
* @chan: IIO channel
|
||||
* @buf: Buffer where to place the attribute data
|
||||
*
|
||||
* This helper is intended to be used by backends that extend an IIO channel
|
||||
* (through iio_backend_extend_chan_spec()) with extended info. In that case,
|
||||
* backends are not supposed to give their own callbacks (as they would not have
|
||||
* a way to get the backend from indio_dev). This is the getter.
|
||||
*
|
||||
* RETURNS:
|
||||
* Number of bytes written to buf, negative error number on failure.
|
||||
*/
|
||||
ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private,
|
||||
const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct iio_backend *back;
|
||||
|
||||
/*
|
||||
* The below should work for the majority of the cases. It will not work
|
||||
* when one frontend has multiple backends in which case we'll need a
|
||||
* new callback in struct iio_info so we can directly request the proper
|
||||
* backend from the frontend. Anyways, let's only introduce new options
|
||||
* when really needed...
|
||||
*/
|
||||
back = iio_backend_from_indio_dev_parent(indio_dev->dev.parent);
|
||||
if (IS_ERR(back))
|
||||
return PTR_ERR(back);
|
||||
|
||||
return iio_backend_op_call(back, ext_info_get, private, chan, buf);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_get, IIO_BACKEND);
|
||||
|
||||
/**
|
||||
* iio_backend_ext_info_set - IIO ext_info write callback
|
||||
* @indio_dev: IIO device
|
||||
* @private: Data private to the driver
|
||||
* @chan: IIO channel
|
||||
* @buf: Buffer holding the sysfs attribute
|
||||
* @len: Buffer length
|
||||
*
|
||||
* This helper is intended to be used by backends that extend an IIO channel
|
||||
* (trough iio_backend_extend_chan_spec()) with extended info. In that case,
|
||||
* backends are not supposed to give their own callbacks (as they would not have
|
||||
* a way to get the backend from indio_dev). This is the setter.
|
||||
*
|
||||
* RETURNS:
|
||||
* Buffer length on success, negative error number on failure.
|
||||
*/
|
||||
ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct iio_backend *back;
|
||||
|
||||
back = iio_backend_from_indio_dev_parent(indio_dev->dev.parent);
|
||||
if (IS_ERR(back))
|
||||
return PTR_ERR(back);
|
||||
|
||||
return iio_backend_op_call(back, ext_info_set, private, chan, buf, len);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_set, IIO_BACKEND);
|
||||
|
||||
/**
|
||||
* iio_backend_extend_chan_spec - Extend an IIO channel
|
||||
* @indio_dev: IIO device
|
||||
* @back: Backend device
|
||||
* @chan: IIO channel
|
||||
*
|
||||
* Some backends may have their own functionalities and hence capable of
|
||||
* extending a frontend's channel.
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 on success, negative error number on failure.
|
||||
*/
|
||||
int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
|
||||
struct iio_backend *back,
|
||||
struct iio_chan_spec *chan)
|
||||
{
|
||||
const struct iio_chan_spec_ext_info *frontend_ext_info = chan->ext_info;
|
||||
const struct iio_chan_spec_ext_info *back_ext_info;
|
||||
int ret;
|
||||
|
||||
ret = iio_backend_op_call(back, extend_chan_spec, chan);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* Let's keep things simple for now. Don't allow to overwrite the
|
||||
* frontend's extended info. If ever needed, we can support appending
|
||||
* it.
|
||||
*/
|
||||
if (frontend_ext_info && chan->ext_info != frontend_ext_info)
|
||||
return -EOPNOTSUPP;
|
||||
if (!chan->ext_info)
|
||||
return 0;
|
||||
|
||||
/* Don't allow backends to get creative and force their own handlers */
|
||||
for (back_ext_info = chan->ext_info; back_ext_info->name; back_ext_info++) {
|
||||
if (back_ext_info->read != iio_backend_ext_info_get)
|
||||
return -EINVAL;
|
||||
if (back_ext_info->write != iio_backend_ext_info_set)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND);
|
||||
|
||||
static void iio_backend_release(void *arg)
|
||||
{
|
||||
struct iio_backend *back = arg;
|
||||
@ -263,6 +440,8 @@ static int __devm_iio_backend_get(struct device *dev, struct iio_backend *back)
|
||||
"Could not link to supplier(%s)\n",
|
||||
dev_name(back->dev));
|
||||
|
||||
back->frontend_dev = dev;
|
||||
|
||||
dev_dbg(dev, "Found backend(%s) device\n", dev_name(back->dev));
|
||||
|
||||
return 0;
|
||||
|
@ -10,6 +10,7 @@
|
||||
* - Alternative access techniques?
|
||||
*/
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/device.h>
|
||||
@ -533,28 +534,26 @@ static ssize_t iio_scan_el_store(struct device *dev,
|
||||
ret = kstrtobool(buf, &state);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
mutex_lock(&iio_dev_opaque->mlock);
|
||||
if (iio_buffer_is_active(buffer)) {
|
||||
ret = -EBUSY;
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
guard(mutex)(&iio_dev_opaque->mlock);
|
||||
if (iio_buffer_is_active(buffer))
|
||||
return -EBUSY;
|
||||
|
||||
ret = iio_scan_mask_query(indio_dev, buffer, this_attr->address);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
if (!state && ret) {
|
||||
ret = iio_scan_mask_clear(buffer, this_attr->address);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
} else if (state && !ret) {
|
||||
return ret;
|
||||
|
||||
if (state && ret)
|
||||
return len;
|
||||
|
||||
if (state)
|
||||
ret = iio_scan_mask_set(indio_dev, buffer, this_attr->address);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
}
|
||||
else
|
||||
ret = iio_scan_mask_clear(buffer, this_attr->address);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
error_ret:
|
||||
mutex_unlock(&iio_dev_opaque->mlock);
|
||||
|
||||
return ret < 0 ? ret : len;
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t iio_scan_el_ts_show(struct device *dev,
|
||||
@ -581,16 +580,13 @@ static ssize_t iio_scan_el_ts_store(struct device *dev,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->mlock);
|
||||
if (iio_buffer_is_active(buffer)) {
|
||||
ret = -EBUSY;
|
||||
goto error_ret;
|
||||
}
|
||||
buffer->scan_timestamp = state;
|
||||
error_ret:
|
||||
mutex_unlock(&iio_dev_opaque->mlock);
|
||||
guard(mutex)(&iio_dev_opaque->mlock);
|
||||
if (iio_buffer_is_active(buffer))
|
||||
return -EBUSY;
|
||||
|
||||
return ret ? ret : len;
|
||||
buffer->scan_timestamp = state;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
|
||||
@ -674,21 +670,16 @@ static ssize_t length_store(struct device *dev, struct device_attribute *attr,
|
||||
if (val == buffer->length)
|
||||
return len;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->mlock);
|
||||
if (iio_buffer_is_active(buffer)) {
|
||||
ret = -EBUSY;
|
||||
} else {
|
||||
buffer->access->set_length(buffer, val);
|
||||
ret = 0;
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
guard(mutex)(&iio_dev_opaque->mlock);
|
||||
if (iio_buffer_is_active(buffer))
|
||||
return -EBUSY;
|
||||
|
||||
buffer->access->set_length(buffer, val);
|
||||
|
||||
if (buffer->length && buffer->length < buffer->watermark)
|
||||
buffer->watermark = buffer->length;
|
||||
out:
|
||||
mutex_unlock(&iio_dev_opaque->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
|
||||
@ -1268,7 +1259,6 @@ int iio_update_buffers(struct iio_dev *indio_dev,
|
||||
struct iio_buffer *remove_buffer)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (insert_buffer == remove_buffer)
|
||||
return 0;
|
||||
@ -1277,8 +1267,8 @@ int iio_update_buffers(struct iio_dev *indio_dev,
|
||||
insert_buffer->direction == IIO_BUFFER_DIRECTION_OUT)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->info_exist_lock);
|
||||
mutex_lock(&iio_dev_opaque->mlock);
|
||||
guard(mutex)(&iio_dev_opaque->info_exist_lock);
|
||||
guard(mutex)(&iio_dev_opaque->mlock);
|
||||
|
||||
if (insert_buffer && iio_buffer_is_active(insert_buffer))
|
||||
insert_buffer = NULL;
|
||||
@ -1286,23 +1276,13 @@ int iio_update_buffers(struct iio_dev *indio_dev,
|
||||
if (remove_buffer && !iio_buffer_is_active(remove_buffer))
|
||||
remove_buffer = NULL;
|
||||
|
||||
if (!insert_buffer && !remove_buffer) {
|
||||
ret = 0;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (!insert_buffer && !remove_buffer)
|
||||
return 0;
|
||||
|
||||
if (!indio_dev->info) {
|
||||
ret = -ENODEV;
|
||||
goto out_unlock;
|
||||
}
|
||||
if (!indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
ret = __iio_update_buffers(indio_dev, insert_buffer, remove_buffer);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&iio_dev_opaque->mlock);
|
||||
mutex_unlock(&iio_dev_opaque->info_exist_lock);
|
||||
|
||||
return ret;
|
||||
return __iio_update_buffers(indio_dev, insert_buffer, remove_buffer);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_update_buffers);
|
||||
|
||||
@ -1326,22 +1306,22 @@ static ssize_t enable_store(struct device *dev, struct device_attribute *attr,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->mlock);
|
||||
guard(mutex)(&iio_dev_opaque->mlock);
|
||||
|
||||
/* Find out if it is in the list */
|
||||
inlist = iio_buffer_is_active(buffer);
|
||||
/* Already in desired state */
|
||||
if (inlist == requested_state)
|
||||
goto done;
|
||||
return len;
|
||||
|
||||
if (requested_state)
|
||||
ret = __iio_update_buffers(indio_dev, buffer, NULL);
|
||||
else
|
||||
ret = __iio_update_buffers(indio_dev, NULL, buffer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
done:
|
||||
mutex_unlock(&iio_dev_opaque->mlock);
|
||||
return (ret < 0) ? ret : len;
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t watermark_show(struct device *dev, struct device_attribute *attr,
|
||||
@ -1368,23 +1348,17 @@ static ssize_t watermark_store(struct device *dev,
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->mlock);
|
||||
guard(mutex)(&iio_dev_opaque->mlock);
|
||||
|
||||
if (val > buffer->length) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (val > buffer->length)
|
||||
return -EINVAL;
|
||||
|
||||
if (iio_buffer_is_active(buffer)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
if (iio_buffer_is_active(buffer))
|
||||
return -EBUSY;
|
||||
|
||||
buffer->watermark = val;
|
||||
out:
|
||||
mutex_unlock(&iio_dev_opaque->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t data_available_show(struct device *dev,
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
@ -1643,19 +1644,20 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv)
|
||||
struct iio_dev *indio_dev;
|
||||
size_t alloc_size;
|
||||
|
||||
alloc_size = sizeof(struct iio_dev_opaque);
|
||||
if (sizeof_priv) {
|
||||
alloc_size = ALIGN(alloc_size, IIO_DMA_MINALIGN);
|
||||
alloc_size += sizeof_priv;
|
||||
}
|
||||
if (sizeof_priv)
|
||||
alloc_size = ALIGN(sizeof(*iio_dev_opaque), IIO_DMA_MINALIGN) + sizeof_priv;
|
||||
else
|
||||
alloc_size = sizeof(*iio_dev_opaque);
|
||||
|
||||
iio_dev_opaque = kzalloc(alloc_size, GFP_KERNEL);
|
||||
if (!iio_dev_opaque)
|
||||
return NULL;
|
||||
|
||||
indio_dev = &iio_dev_opaque->indio_dev;
|
||||
indio_dev->priv = (char *)iio_dev_opaque +
|
||||
ALIGN(sizeof(struct iio_dev_opaque), IIO_DMA_MINALIGN);
|
||||
|
||||
if (sizeof_priv)
|
||||
indio_dev->priv = (char *)iio_dev_opaque +
|
||||
ALIGN(sizeof(*iio_dev_opaque), IIO_DMA_MINALIGN);
|
||||
|
||||
indio_dev->dev.parent = parent;
|
||||
indio_dev->dev.type = &iio_device_type;
|
||||
@ -1809,31 +1811,24 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
struct iio_dev *indio_dev = ib->indio_dev;
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
|
||||
struct iio_ioctl_handler *h;
|
||||
int ret = -ENODEV;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->info_exist_lock);
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&iio_dev_opaque->info_exist_lock);
|
||||
/*
|
||||
* The NULL check here is required to prevent crashing when a device
|
||||
* is being removed while userspace would still have open file handles
|
||||
* to try to access this device.
|
||||
*/
|
||||
if (!indio_dev->info)
|
||||
goto out_unlock;
|
||||
return -ENODEV;
|
||||
|
||||
list_for_each_entry(h, &iio_dev_opaque->ioctl_handlers, entry) {
|
||||
ret = h->ioctl(indio_dev, filp, cmd, arg);
|
||||
if (ret != IIO_IOCTL_UNHANDLED)
|
||||
break;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret == IIO_IOCTL_UNHANDLED)
|
||||
ret = -ENODEV;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&iio_dev_opaque->info_exist_lock);
|
||||
|
||||
return ret;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static const struct file_operations iio_buffer_fileops = {
|
||||
@ -2061,18 +2056,16 @@ void iio_device_unregister(struct iio_dev *indio_dev)
|
||||
|
||||
cdev_device_del(&iio_dev_opaque->chrdev, &indio_dev->dev);
|
||||
|
||||
mutex_lock(&iio_dev_opaque->info_exist_lock);
|
||||
scoped_guard(mutex, &iio_dev_opaque->info_exist_lock) {
|
||||
iio_device_unregister_debugfs(indio_dev);
|
||||
|
||||
iio_device_unregister_debugfs(indio_dev);
|
||||
iio_disable_all_buffers(indio_dev);
|
||||
|
||||
iio_disable_all_buffers(indio_dev);
|
||||
indio_dev->info = NULL;
|
||||
|
||||
indio_dev->info = NULL;
|
||||
|
||||
iio_device_wakeup_eventset(indio_dev);
|
||||
iio_buffer_wakeup_poll(indio_dev);
|
||||
|
||||
mutex_unlock(&iio_dev_opaque->info_exist_lock);
|
||||
iio_device_wakeup_eventset(indio_dev);
|
||||
iio_buffer_wakeup_poll(indio_dev);
|
||||
}
|
||||
|
||||
iio_buffers_free_sysfs_and_mask(indio_dev);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Copyright (c) 2008 Jonathan Cameron
|
||||
*/
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/err.h>
|
||||
@ -80,19 +81,18 @@ int iio_trigger_register(struct iio_trigger *trig_info)
|
||||
goto error_unregister_id;
|
||||
|
||||
/* Add to list of available triggers held by the IIO core */
|
||||
mutex_lock(&iio_trigger_list_lock);
|
||||
if (__iio_trigger_find_by_name(trig_info->name)) {
|
||||
pr_err("Duplicate trigger name '%s'\n", trig_info->name);
|
||||
ret = -EEXIST;
|
||||
goto error_device_del;
|
||||
scoped_guard(mutex, &iio_trigger_list_lock) {
|
||||
if (__iio_trigger_find_by_name(trig_info->name)) {
|
||||
pr_err("Duplicate trigger name '%s'\n", trig_info->name);
|
||||
ret = -EEXIST;
|
||||
goto error_device_del;
|
||||
}
|
||||
list_add_tail(&trig_info->list, &iio_trigger_list);
|
||||
}
|
||||
list_add_tail(&trig_info->list, &iio_trigger_list);
|
||||
mutex_unlock(&iio_trigger_list_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
error_device_del:
|
||||
mutex_unlock(&iio_trigger_list_lock);
|
||||
device_del(&trig_info->dev);
|
||||
error_unregister_id:
|
||||
ida_free(&iio_trigger_ida, trig_info->id);
|
||||
@ -102,9 +102,8 @@ EXPORT_SYMBOL(iio_trigger_register);
|
||||
|
||||
void iio_trigger_unregister(struct iio_trigger *trig_info)
|
||||
{
|
||||
mutex_lock(&iio_trigger_list_lock);
|
||||
list_del(&trig_info->list);
|
||||
mutex_unlock(&iio_trigger_list_lock);
|
||||
scoped_guard(mutex, &iio_trigger_list_lock)
|
||||
list_del(&trig_info->list);
|
||||
|
||||
ida_free(&iio_trigger_ida, trig_info->id);
|
||||
/* Possible issue in here */
|
||||
@ -120,12 +119,11 @@ int iio_trigger_set_immutable(struct iio_dev *indio_dev, struct iio_trigger *tri
|
||||
return -EINVAL;
|
||||
|
||||
iio_dev_opaque = to_iio_dev_opaque(indio_dev);
|
||||
mutex_lock(&iio_dev_opaque->mlock);
|
||||
guard(mutex)(&iio_dev_opaque->mlock);
|
||||
WARN_ON(iio_dev_opaque->trig_readonly);
|
||||
|
||||
indio_dev->trig = iio_trigger_get(trig);
|
||||
iio_dev_opaque->trig_readonly = true;
|
||||
mutex_unlock(&iio_dev_opaque->mlock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -145,18 +143,14 @@ static struct iio_trigger *__iio_trigger_find_by_name(const char *name)
|
||||
|
||||
static struct iio_trigger *iio_trigger_acquire_by_name(const char *name)
|
||||
{
|
||||
struct iio_trigger *trig = NULL, *iter;
|
||||
struct iio_trigger *iter;
|
||||
|
||||
mutex_lock(&iio_trigger_list_lock);
|
||||
guard(mutex)(&iio_trigger_list_lock);
|
||||
list_for_each_entry(iter, &iio_trigger_list, list)
|
||||
if (sysfs_streq(iter->name, name)) {
|
||||
trig = iter;
|
||||
iio_trigger_get(trig);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&iio_trigger_list_lock);
|
||||
if (sysfs_streq(iter->name, name))
|
||||
return iio_trigger_get(iter);
|
||||
|
||||
return trig;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void iio_reenable_work_fn(struct work_struct *work)
|
||||
@ -259,22 +253,21 @@ static int iio_trigger_get_irq(struct iio_trigger *trig)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&trig->pool_lock);
|
||||
ret = bitmap_find_free_region(trig->pool,
|
||||
CONFIG_IIO_CONSUMERS_PER_TRIGGER,
|
||||
ilog2(1));
|
||||
mutex_unlock(&trig->pool_lock);
|
||||
if (ret >= 0)
|
||||
ret += trig->subirq_base;
|
||||
scoped_guard(mutex, &trig->pool_lock) {
|
||||
ret = bitmap_find_free_region(trig->pool,
|
||||
CONFIG_IIO_CONSUMERS_PER_TRIGGER,
|
||||
ilog2(1));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return ret + trig->subirq_base;
|
||||
}
|
||||
|
||||
static void iio_trigger_put_irq(struct iio_trigger *trig, int irq)
|
||||
{
|
||||
mutex_lock(&trig->pool_lock);
|
||||
guard(mutex)(&trig->pool_lock);
|
||||
clear_bit(irq - trig->subirq_base, trig->pool);
|
||||
mutex_unlock(&trig->pool_lock);
|
||||
}
|
||||
|
||||
/* Complexity in here. With certain triggers (datardy) an acknowledgement
|
||||
@ -451,16 +444,12 @@ static ssize_t current_trigger_store(struct device *dev,
|
||||
struct iio_trigger *trig;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->mlock);
|
||||
if (iio_dev_opaque->currentmode == INDIO_BUFFER_TRIGGERED) {
|
||||
mutex_unlock(&iio_dev_opaque->mlock);
|
||||
return -EBUSY;
|
||||
scoped_guard(mutex, &iio_dev_opaque->mlock) {
|
||||
if (iio_dev_opaque->currentmode == INDIO_BUFFER_TRIGGERED)
|
||||
return -EBUSY;
|
||||
if (iio_dev_opaque->trig_readonly)
|
||||
return -EPERM;
|
||||
}
|
||||
if (iio_dev_opaque->trig_readonly) {
|
||||
mutex_unlock(&iio_dev_opaque->mlock);
|
||||
return -EPERM;
|
||||
}
|
||||
mutex_unlock(&iio_dev_opaque->mlock);
|
||||
|
||||
trig = iio_trigger_acquire_by_name(buf);
|
||||
if (oldtrig == trig) {
|
||||
|
@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2011 Jonathan Cameron
|
||||
*/
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/minmax.h>
|
||||
@ -43,13 +44,14 @@ static int iio_map_array_unregister_locked(struct iio_dev *indio_dev)
|
||||
|
||||
int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps)
|
||||
{
|
||||
int i = 0, ret = 0;
|
||||
struct iio_map_internal *mapi;
|
||||
int i = 0;
|
||||
int ret;
|
||||
|
||||
if (!maps)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&iio_map_list_lock);
|
||||
guard(mutex)(&iio_map_list_lock);
|
||||
while (maps[i].consumer_dev_name) {
|
||||
mapi = kzalloc(sizeof(*mapi), GFP_KERNEL);
|
||||
if (!mapi) {
|
||||
@ -61,11 +63,10 @@ int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps)
|
||||
list_add_tail(&mapi->l, &iio_map_list);
|
||||
i++;
|
||||
}
|
||||
error_ret:
|
||||
if (ret)
|
||||
iio_map_array_unregister_locked(indio_dev);
|
||||
mutex_unlock(&iio_map_list_lock);
|
||||
|
||||
return 0;
|
||||
error_ret:
|
||||
iio_map_array_unregister_locked(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_map_array_register);
|
||||
@ -75,13 +76,8 @@ EXPORT_SYMBOL_GPL(iio_map_array_register);
|
||||
*/
|
||||
int iio_map_array_unregister(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&iio_map_list_lock);
|
||||
ret = iio_map_array_unregister_locked(indio_dev);
|
||||
mutex_unlock(&iio_map_list_lock);
|
||||
|
||||
return ret;
|
||||
guard(mutex)(&iio_map_list_lock);
|
||||
return iio_map_array_unregister_locked(indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_map_array_unregister);
|
||||
|
||||
@ -183,25 +179,21 @@ err_put:
|
||||
static struct iio_channel *fwnode_iio_channel_get(struct fwnode_handle *fwnode,
|
||||
int index)
|
||||
{
|
||||
struct iio_channel *channel;
|
||||
int err;
|
||||
|
||||
if (index < 0)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
channel = kzalloc(sizeof(*channel), GFP_KERNEL);
|
||||
struct iio_channel *channel __free(kfree) =
|
||||
kzalloc(sizeof(*channel), GFP_KERNEL);
|
||||
if (!channel)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = __fwnode_iio_channel_get(channel, fwnode, index);
|
||||
if (err)
|
||||
goto err_free_channel;
|
||||
return ERR_PTR(err);
|
||||
|
||||
return channel;
|
||||
|
||||
err_free_channel:
|
||||
kfree(channel);
|
||||
return ERR_PTR(err);
|
||||
return_ptr(channel);
|
||||
}
|
||||
|
||||
static struct iio_channel *
|
||||
@ -291,7 +283,6 @@ EXPORT_SYMBOL_GPL(fwnode_iio_channel_get_by_name);
|
||||
static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev)
|
||||
{
|
||||
struct fwnode_handle *fwnode = dev_fwnode(dev);
|
||||
struct iio_channel *chans;
|
||||
int i, mapind, nummaps = 0;
|
||||
int ret;
|
||||
|
||||
@ -307,7 +298,8 @@ static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
/* NULL terminated array to save passing size */
|
||||
chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
|
||||
struct iio_channel *chans __free(kfree) =
|
||||
kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
|
||||
if (!chans)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
@ -317,12 +309,11 @@ static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev)
|
||||
if (ret)
|
||||
goto error_free_chans;
|
||||
}
|
||||
return chans;
|
||||
return_ptr(chans);
|
||||
|
||||
error_free_chans:
|
||||
for (i = 0; i < mapind; i++)
|
||||
iio_device_put(chans[i].indio_dev);
|
||||
kfree(chans);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
@ -330,28 +321,28 @@ static struct iio_channel *iio_channel_get_sys(const char *name,
|
||||
const char *channel_name)
|
||||
{
|
||||
struct iio_map_internal *c_i = NULL, *c = NULL;
|
||||
struct iio_channel *channel;
|
||||
int err;
|
||||
|
||||
if (!(name || channel_name))
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
/* first find matching entry the channel map */
|
||||
mutex_lock(&iio_map_list_lock);
|
||||
list_for_each_entry(c_i, &iio_map_list, l) {
|
||||
if ((name && strcmp(name, c_i->map->consumer_dev_name) != 0) ||
|
||||
(channel_name &&
|
||||
strcmp(channel_name, c_i->map->consumer_channel) != 0))
|
||||
continue;
|
||||
c = c_i;
|
||||
iio_device_get(c->indio_dev);
|
||||
break;
|
||||
scoped_guard(mutex, &iio_map_list_lock) {
|
||||
list_for_each_entry(c_i, &iio_map_list, l) {
|
||||
if ((name && strcmp(name, c_i->map->consumer_dev_name) != 0) ||
|
||||
(channel_name &&
|
||||
strcmp(channel_name, c_i->map->consumer_channel) != 0))
|
||||
continue;
|
||||
c = c_i;
|
||||
iio_device_get(c->indio_dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&iio_map_list_lock);
|
||||
if (!c)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
channel = kzalloc(sizeof(*channel), GFP_KERNEL);
|
||||
struct iio_channel *channel __free(kfree) =
|
||||
kzalloc(sizeof(*channel), GFP_KERNEL);
|
||||
if (!channel) {
|
||||
err = -ENOMEM;
|
||||
goto error_no_mem;
|
||||
@ -366,14 +357,12 @@ static struct iio_channel *iio_channel_get_sys(const char *name,
|
||||
|
||||
if (!channel->channel) {
|
||||
err = -EINVAL;
|
||||
goto error_no_chan;
|
||||
goto error_no_mem;
|
||||
}
|
||||
}
|
||||
|
||||
return channel;
|
||||
return_ptr(channel);
|
||||
|
||||
error_no_chan:
|
||||
kfree(channel);
|
||||
error_no_mem:
|
||||
iio_device_put(c->indio_dev);
|
||||
return ERR_PTR(err);
|
||||
@ -450,8 +439,8 @@ EXPORT_SYMBOL_GPL(devm_fwnode_iio_channel_get_by_name);
|
||||
struct iio_channel *iio_channel_get_all(struct device *dev)
|
||||
{
|
||||
const char *name;
|
||||
struct iio_channel *chans;
|
||||
struct iio_map_internal *c = NULL;
|
||||
struct iio_channel *fw_chans;
|
||||
int nummaps = 0;
|
||||
int mapind = 0;
|
||||
int i, ret;
|
||||
@ -459,17 +448,17 @@ struct iio_channel *iio_channel_get_all(struct device *dev)
|
||||
if (!dev)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
chans = fwnode_iio_channel_get_all(dev);
|
||||
fw_chans = fwnode_iio_channel_get_all(dev);
|
||||
/*
|
||||
* We only want to carry on if the error is -ENODEV. Anything else
|
||||
* should be reported up the stack.
|
||||
*/
|
||||
if (!IS_ERR(chans) || PTR_ERR(chans) != -ENODEV)
|
||||
return chans;
|
||||
if (!IS_ERR(fw_chans) || PTR_ERR(fw_chans) != -ENODEV)
|
||||
return fw_chans;
|
||||
|
||||
name = dev_name(dev);
|
||||
|
||||
mutex_lock(&iio_map_list_lock);
|
||||
guard(mutex)(&iio_map_list_lock);
|
||||
/* first count the matching maps */
|
||||
list_for_each_entry(c, &iio_map_list, l)
|
||||
if (name && strcmp(name, c->map->consumer_dev_name) != 0)
|
||||
@ -477,17 +466,14 @@ struct iio_channel *iio_channel_get_all(struct device *dev)
|
||||
else
|
||||
nummaps++;
|
||||
|
||||
if (nummaps == 0) {
|
||||
ret = -ENODEV;
|
||||
goto error_ret;
|
||||
}
|
||||
if (nummaps == 0)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
/* NULL terminated array to save passing size */
|
||||
chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
|
||||
if (!chans) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
struct iio_channel *chans __free(kfree) =
|
||||
kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
|
||||
if (!chans)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* for each map fill in the chans element */
|
||||
list_for_each_entry(c, &iio_map_list, l) {
|
||||
@ -509,17 +495,12 @@ struct iio_channel *iio_channel_get_all(struct device *dev)
|
||||
ret = -ENODEV;
|
||||
goto error_free_chans;
|
||||
}
|
||||
mutex_unlock(&iio_map_list_lock);
|
||||
|
||||
return chans;
|
||||
return_ptr(chans);
|
||||
|
||||
error_free_chans:
|
||||
for (i = 0; i < nummaps; i++)
|
||||
iio_device_put(chans[i].indio_dev);
|
||||
kfree(chans);
|
||||
error_ret:
|
||||
mutex_unlock(&iio_map_list_lock);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_get_all);
|
||||
@ -590,38 +571,24 @@ static int iio_channel_read(struct iio_channel *chan, int *val, int *val2,
|
||||
int iio_read_channel_raw(struct iio_channel *chan, int *val)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info) {
|
||||
ret = -ENODEV;
|
||||
goto err_unlock;
|
||||
}
|
||||
guard(mutex)(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
|
||||
err_unlock:
|
||||
mutex_unlock(&iio_dev_opaque->info_exist_lock);
|
||||
|
||||
return ret;
|
||||
return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_read_channel_raw);
|
||||
|
||||
int iio_read_channel_average_raw(struct iio_channel *chan, int *val)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info) {
|
||||
ret = -ENODEV;
|
||||
goto err_unlock;
|
||||
}
|
||||
guard(mutex)(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW);
|
||||
err_unlock:
|
||||
mutex_unlock(&iio_dev_opaque->info_exist_lock);
|
||||
|
||||
return ret;
|
||||
return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_read_channel_average_raw);
|
||||
|
||||
@ -708,20 +675,13 @@ int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
|
||||
int *processed, unsigned int scale)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info) {
|
||||
ret = -ENODEV;
|
||||
goto err_unlock;
|
||||
}
|
||||
guard(mutex)(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
ret = iio_convert_raw_to_processed_unlocked(chan, raw, processed,
|
||||
scale);
|
||||
err_unlock:
|
||||
mutex_unlock(&iio_dev_opaque->info_exist_lock);
|
||||
|
||||
return ret;
|
||||
return iio_convert_raw_to_processed_unlocked(chan, raw, processed,
|
||||
scale);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
|
||||
|
||||
@ -729,19 +689,12 @@ int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2,
|
||||
enum iio_chan_info_enum attribute)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info) {
|
||||
ret = -ENODEV;
|
||||
goto err_unlock;
|
||||
}
|
||||
guard(mutex)(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
ret = iio_channel_read(chan, val, val2, attribute);
|
||||
err_unlock:
|
||||
mutex_unlock(&iio_dev_opaque->info_exist_lock);
|
||||
|
||||
return ret;
|
||||
return iio_channel_read(chan, val, val2, attribute);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_read_channel_attribute);
|
||||
|
||||
@ -757,30 +710,26 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val,
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info) {
|
||||
ret = -ENODEV;
|
||||
goto err_unlock;
|
||||
}
|
||||
guard(mutex)(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
if (iio_channel_has_info(chan->channel, IIO_CHAN_INFO_PROCESSED)) {
|
||||
ret = iio_channel_read(chan, val, NULL,
|
||||
IIO_CHAN_INFO_PROCESSED);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
return ret;
|
||||
*val *= scale;
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
ret = iio_convert_raw_to_processed_unlocked(chan, *val, val,
|
||||
scale);
|
||||
return ret;
|
||||
|
||||
return iio_convert_raw_to_processed_unlocked(chan, *val, val,
|
||||
scale);
|
||||
}
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&iio_dev_opaque->info_exist_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_read_channel_processed_scale);
|
||||
|
||||
@ -813,19 +762,12 @@ int iio_read_avail_channel_attribute(struct iio_channel *chan,
|
||||
enum iio_chan_info_enum attribute)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info) {
|
||||
ret = -ENODEV;
|
||||
goto err_unlock;
|
||||
}
|
||||
guard(mutex)(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
ret = iio_channel_read_avail(chan, vals, type, length, attribute);
|
||||
err_unlock:
|
||||
mutex_unlock(&iio_dev_opaque->info_exist_lock);
|
||||
|
||||
return ret;
|
||||
return iio_channel_read_avail(chan, vals, type, length, attribute);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_read_avail_channel_attribute);
|
||||
|
||||
@ -892,20 +834,13 @@ static int iio_channel_read_max(struct iio_channel *chan,
|
||||
int iio_read_max_channel_raw(struct iio_channel *chan, int *val)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
|
||||
int ret;
|
||||
int type;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info) {
|
||||
ret = -ENODEV;
|
||||
goto err_unlock;
|
||||
}
|
||||
guard(mutex)(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
ret = iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
|
||||
err_unlock:
|
||||
mutex_unlock(&iio_dev_opaque->info_exist_lock);
|
||||
|
||||
return ret;
|
||||
return iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_read_max_channel_raw);
|
||||
|
||||
@ -955,40 +890,27 @@ static int iio_channel_read_min(struct iio_channel *chan,
|
||||
int iio_read_min_channel_raw(struct iio_channel *chan, int *val)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
|
||||
int ret;
|
||||
int type;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info) {
|
||||
ret = -ENODEV;
|
||||
goto err_unlock;
|
||||
}
|
||||
guard(mutex)(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
ret = iio_channel_read_min(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
|
||||
err_unlock:
|
||||
mutex_unlock(&iio_dev_opaque->info_exist_lock);
|
||||
|
||||
return ret;
|
||||
return iio_channel_read_min(chan, val, NULL, &type, IIO_CHAN_INFO_RAW);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_read_min_channel_raw);
|
||||
|
||||
int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
|
||||
int ret = 0;
|
||||
/* Need to verify underlying driver has not gone away */
|
||||
|
||||
mutex_lock(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info) {
|
||||
ret = -ENODEV;
|
||||
goto err_unlock;
|
||||
}
|
||||
guard(mutex)(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
*type = chan->channel->type;
|
||||
err_unlock:
|
||||
mutex_unlock(&iio_dev_opaque->info_exist_lock);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_get_channel_type);
|
||||
|
||||
@ -1003,19 +925,12 @@ int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2,
|
||||
enum iio_chan_info_enum attribute)
|
||||
{
|
||||
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info) {
|
||||
ret = -ENODEV;
|
||||
goto err_unlock;
|
||||
}
|
||||
guard(mutex)(&iio_dev_opaque->info_exist_lock);
|
||||
if (!chan->indio_dev->info)
|
||||
return -ENODEV;
|
||||
|
||||
ret = iio_channel_write(chan, val, val2, attribute);
|
||||
err_unlock:
|
||||
mutex_unlock(&iio_dev_opaque->info_exist_lock);
|
||||
|
||||
return ret;
|
||||
return iio_channel_write(chan, val, val2, attribute);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_write_channel_attribute);
|
||||
|
||||
|
@ -73,6 +73,18 @@ config APDS9300
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called apds9300.
|
||||
|
||||
config APDS9306
|
||||
tristate "Avago APDS9306 Ambient Light Sensor"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
select IIO_GTS_HELPER
|
||||
help
|
||||
If you say Y or M here, you get support for Avago APDS9306
|
||||
Ambient Light Sensor.
|
||||
|
||||
If built as a dynamically linked module, it will be called
|
||||
apds9306.
|
||||
|
||||
config APDS9960
|
||||
tristate "Avago APDS9960 gesture/RGB/ALS/proximity sensor"
|
||||
select REGMAP_I2C
|
||||
|
@ -10,6 +10,7 @@ obj-$(CONFIG_ADUX1020) += adux1020.o
|
||||
obj-$(CONFIG_AL3010) += al3010.o
|
||||
obj-$(CONFIG_AL3320A) += al3320a.o
|
||||
obj-$(CONFIG_APDS9300) += apds9300.o
|
||||
obj-$(CONFIG_APDS9306) += apds9306.o
|
||||
obj-$(CONFIG_APDS9960) += apds9960.o
|
||||
obj-$(CONFIG_AS73211) += as73211.o
|
||||
obj-$(CONFIG_BH1750) += bh1750.o
|
||||
|
1361
drivers/iio/light/apds9306.c
Normal file
1361
drivers/iio/light/apds9306.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -291,7 +291,7 @@ int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap)
|
||||
if (!iio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, (void *)iio_dev);
|
||||
dev_set_drvdata(dev, iio_dev);
|
||||
|
||||
hw = iio_priv(iio_dev);
|
||||
hw->irq = irq;
|
||||
|
@ -27,20 +27,20 @@
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-provider.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h> /* For irq_get_irq_data() */
|
||||
#include <linux/completion.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-provider.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
|
@ -171,7 +171,7 @@ static int dps310_temp_workaround(struct dps310_data *data)
|
||||
int reg;
|
||||
|
||||
rc = regmap_read(data->regmap, 0x32, ®);
|
||||
if (rc)
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
@ -256,24 +256,24 @@ static int dps310_startup(struct dps310_data *data)
|
||||
return dps310_temp_workaround(data);
|
||||
}
|
||||
|
||||
static int dps310_get_pres_precision(struct dps310_data *data)
|
||||
static int dps310_get_pres_precision(struct dps310_data *data, int *val)
|
||||
{
|
||||
int rc;
|
||||
int val;
|
||||
int reg_val, rc;
|
||||
|
||||
rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val);
|
||||
rc = regmap_read(data->regmap, DPS310_PRS_CFG, ®_val);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return BIT(val & GENMASK(2, 0));
|
||||
*val = BIT(reg_val & GENMASK(2, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dps310_get_temp_precision(struct dps310_data *data)
|
||||
static int dps310_get_temp_precision(struct dps310_data *data, int *val)
|
||||
{
|
||||
int rc;
|
||||
int val;
|
||||
int reg_val, rc;
|
||||
|
||||
rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val);
|
||||
rc = regmap_read(data->regmap, DPS310_TMP_CFG, ®_val);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
@ -281,7 +281,9 @@ static int dps310_get_temp_precision(struct dps310_data *data)
|
||||
* Scale factor is bottom 4 bits of the register, but 1111 is
|
||||
* reserved so just grab bottom three
|
||||
*/
|
||||
return BIT(val & GENMASK(2, 0));
|
||||
*val = BIT(reg_val & GENMASK(2, 0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called with lock held */
|
||||
@ -350,48 +352,56 @@ static int dps310_set_temp_samp_freq(struct dps310_data *data, int freq)
|
||||
DPS310_TMP_RATE_BITS, val);
|
||||
}
|
||||
|
||||
static int dps310_get_pres_samp_freq(struct dps310_data *data)
|
||||
static int dps310_get_pres_samp_freq(struct dps310_data *data, int *val)
|
||||
{
|
||||
int rc;
|
||||
int val;
|
||||
int reg_val, rc;
|
||||
|
||||
rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val);
|
||||
rc = regmap_read(data->regmap, DPS310_PRS_CFG, ®_val);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return BIT((val & DPS310_PRS_RATE_BITS) >> 4);
|
||||
*val = BIT((reg_val & DPS310_PRS_RATE_BITS) >> 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dps310_get_temp_samp_freq(struct dps310_data *data)
|
||||
static int dps310_get_temp_samp_freq(struct dps310_data *data, int *val)
|
||||
{
|
||||
int rc;
|
||||
int val;
|
||||
int reg_val, rc;
|
||||
|
||||
rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val);
|
||||
rc = regmap_read(data->regmap, DPS310_TMP_CFG, ®_val);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return BIT((val & DPS310_TMP_RATE_BITS) >> 4);
|
||||
*val = BIT((reg_val & DPS310_TMP_RATE_BITS) >> 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dps310_get_pres_k(struct dps310_data *data)
|
||||
static int dps310_get_pres_k(struct dps310_data *data, int *val)
|
||||
{
|
||||
int rc = dps310_get_pres_precision(data);
|
||||
int reg_val, rc;
|
||||
|
||||
rc = regmap_read(data->regmap, DPS310_PRS_CFG, ®_val);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return scale_factors[ilog2(rc)];
|
||||
*val = scale_factors[reg_val & GENMASK(2, 0)];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dps310_get_temp_k(struct dps310_data *data)
|
||||
static int dps310_get_temp_k(struct dps310_data *data, int *val)
|
||||
{
|
||||
int rc = dps310_get_temp_precision(data);
|
||||
int reg_val, rc;
|
||||
|
||||
rc = regmap_read(data->regmap, DPS310_TMP_CFG, ®_val);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return scale_factors[ilog2(rc)];
|
||||
*val = scale_factors[reg_val & GENMASK(2, 0)];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dps310_reset_wait(struct dps310_data *data)
|
||||
@ -464,7 +474,10 @@ static int dps310_read_pres_raw(struct dps310_data *data)
|
||||
if (mutex_lock_interruptible(&data->lock))
|
||||
return -EINTR;
|
||||
|
||||
rate = dps310_get_pres_samp_freq(data);
|
||||
rc = dps310_get_pres_samp_freq(data, &rate);
|
||||
if (rc)
|
||||
goto done;
|
||||
|
||||
timeout = DPS310_POLL_TIMEOUT_US(rate);
|
||||
|
||||
/* Poll for sensor readiness; base the timeout upon the sample rate. */
|
||||
@ -510,7 +523,10 @@ static int dps310_read_temp_raw(struct dps310_data *data)
|
||||
if (mutex_lock_interruptible(&data->lock))
|
||||
return -EINTR;
|
||||
|
||||
rate = dps310_get_temp_samp_freq(data);
|
||||
rc = dps310_get_temp_samp_freq(data, &rate);
|
||||
if (rc)
|
||||
goto done;
|
||||
|
||||
timeout = DPS310_POLL_TIMEOUT_US(rate);
|
||||
|
||||
/* Poll for sensor readiness; base the timeout upon the sample rate. */
|
||||
@ -612,13 +628,13 @@ static int dps310_write_raw(struct iio_dev *iio,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dps310_calculate_pressure(struct dps310_data *data)
|
||||
static int dps310_calculate_pressure(struct dps310_data *data, int *val)
|
||||
{
|
||||
int i;
|
||||
int rc;
|
||||
int t_ready;
|
||||
int kpi = dps310_get_pres_k(data);
|
||||
int kti = dps310_get_temp_k(data);
|
||||
int kpi;
|
||||
int kti;
|
||||
s64 rem = 0ULL;
|
||||
s64 pressure = 0ULL;
|
||||
s64 p;
|
||||
@ -629,11 +645,13 @@ static int dps310_calculate_pressure(struct dps310_data *data)
|
||||
s64 kp;
|
||||
s64 kt;
|
||||
|
||||
if (kpi < 0)
|
||||
return kpi;
|
||||
rc = dps310_get_pres_k(data, &kpi);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (kti < 0)
|
||||
return kti;
|
||||
rc = dps310_get_temp_k(data, &kti);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
kp = (s64)kpi;
|
||||
kt = (s64)kti;
|
||||
@ -687,7 +705,9 @@ static int dps310_calculate_pressure(struct dps310_data *data)
|
||||
if (pressure < 0LL)
|
||||
return -ERANGE;
|
||||
|
||||
return (int)min_t(s64, pressure, INT_MAX);
|
||||
*val = (int)min_t(s64, pressure, INT_MAX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2,
|
||||
@ -697,11 +717,10 @@ static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2,
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
rc = dps310_get_pres_samp_freq(data);
|
||||
if (rc < 0)
|
||||
rc = dps310_get_pres_samp_freq(data, val);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*val = rc;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
@ -709,20 +728,17 @@ static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = dps310_calculate_pressure(data);
|
||||
if (rc < 0)
|
||||
rc = dps310_calculate_pressure(data, val);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*val = rc;
|
||||
*val2 = 1000; /* Convert Pa to KPa per IIO ABI */
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
rc = dps310_get_pres_precision(data);
|
||||
if (rc < 0)
|
||||
rc = dps310_get_pres_precision(data, val);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*val = rc;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
@ -730,14 +746,15 @@ static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2,
|
||||
}
|
||||
}
|
||||
|
||||
static int dps310_calculate_temp(struct dps310_data *data)
|
||||
static int dps310_calculate_temp(struct dps310_data *data, int *val)
|
||||
{
|
||||
s64 c0;
|
||||
s64 t;
|
||||
int kt = dps310_get_temp_k(data);
|
||||
int kt, rc;
|
||||
|
||||
if (kt < 0)
|
||||
return kt;
|
||||
rc = dps310_get_temp_k(data, &kt);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Obtain inverse-scaled offset */
|
||||
c0 = div_s64((s64)kt * (s64)data->c0, 2);
|
||||
@ -746,7 +763,9 @@ static int dps310_calculate_temp(struct dps310_data *data)
|
||||
t = c0 + ((s64)data->temp_raw * (s64)data->c1);
|
||||
|
||||
/* Convert to milliCelsius and scale the temperature */
|
||||
return (int)div_s64(t * 1000LL, kt);
|
||||
*val = (int)div_s64(t * 1000LL, kt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dps310_read_temp(struct dps310_data *data, int *val, int *val2,
|
||||
@ -756,11 +775,10 @@ static int dps310_read_temp(struct dps310_data *data, int *val, int *val2,
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
rc = dps310_get_temp_samp_freq(data);
|
||||
if (rc < 0)
|
||||
rc = dps310_get_temp_samp_freq(data, val);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*val = rc;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
@ -768,19 +786,17 @@ static int dps310_read_temp(struct dps310_data *data, int *val, int *val2,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = dps310_calculate_temp(data);
|
||||
if (rc < 0)
|
||||
rc = dps310_calculate_temp(data, val);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*val = rc;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
rc = dps310_get_temp_precision(data);
|
||||
if (rc < 0)
|
||||
rc = dps310_get_temp_precision(data, val);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*val = rc;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
|
@ -23,14 +23,9 @@
|
||||
static int hsc_spi_recv(struct hsc_data *data)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(data->dev);
|
||||
struct spi_transfer xfer = {
|
||||
.tx_buf = NULL,
|
||||
.rx_buf = data->buffer,
|
||||
.len = HSC_REG_MEASUREMENT_RD_SIZE,
|
||||
};
|
||||
|
||||
msleep_interruptible(HSC_RESP_TIME_MS);
|
||||
return spi_sync_transfer(spi, &xfer, 1);
|
||||
return spi_read(spi, data->buffer, HSC_REG_MEASUREMENT_RD_SIZE);
|
||||
}
|
||||
|
||||
static int hsc_spi_probe(struct spi_device *spi)
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
@ -657,7 +658,6 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data
|
||||
const struct ltc2983_sensor *sensor)
|
||||
{
|
||||
struct ltc2983_thermocouple *thermo;
|
||||
struct fwnode_handle *ref;
|
||||
u32 oc_current;
|
||||
int ret;
|
||||
|
||||
@ -704,7 +704,8 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
ref = fwnode_find_reference(child, "adi,cold-junction-handle", 0);
|
||||
struct fwnode_handle *ref __free(fwnode_handle) =
|
||||
fwnode_find_reference(child, "adi,cold-junction-handle", 0);
|
||||
if (IS_ERR(ref)) {
|
||||
ref = NULL;
|
||||
} else {
|
||||
@ -715,7 +716,7 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data
|
||||
* the error right away.
|
||||
*/
|
||||
dev_err(&st->spi->dev, "Property reg must be given\n");
|
||||
goto fail;
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
}
|
||||
|
||||
@ -726,22 +727,15 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data
|
||||
thermo->custom = __ltc2983_custom_sensor_new(st, child,
|
||||
propname, false,
|
||||
16384, true);
|
||||
if (IS_ERR(thermo->custom)) {
|
||||
ret = PTR_ERR(thermo->custom);
|
||||
goto fail;
|
||||
}
|
||||
if (IS_ERR(thermo->custom))
|
||||
return ERR_CAST(thermo->custom);
|
||||
}
|
||||
|
||||
/* set common parameters */
|
||||
thermo->sensor.fault_handler = ltc2983_thermocouple_fault_handler;
|
||||
thermo->sensor.assign_chan = ltc2983_thermocouple_assign_chan;
|
||||
|
||||
fwnode_handle_put(ref);
|
||||
return &thermo->sensor;
|
||||
|
||||
fail:
|
||||
fwnode_handle_put(ref);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static struct ltc2983_sensor *
|
||||
@ -751,14 +745,14 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
|
||||
struct ltc2983_rtd *rtd;
|
||||
int ret = 0;
|
||||
struct device *dev = &st->spi->dev;
|
||||
struct fwnode_handle *ref;
|
||||
u32 excitation_current = 0, n_wires = 0;
|
||||
|
||||
rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL);
|
||||
if (!rtd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ref = fwnode_find_reference(child, "adi,rsense-handle", 0);
|
||||
struct fwnode_handle *ref __free(fwnode_handle) =
|
||||
fwnode_find_reference(child, "adi,rsense-handle", 0);
|
||||
if (IS_ERR(ref)) {
|
||||
dev_err(dev, "Property adi,rsense-handle missing or invalid");
|
||||
return ERR_CAST(ref);
|
||||
@ -767,7 +761,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
|
||||
ret = fwnode_property_read_u32(ref, "reg", &rtd->r_sense_chan);
|
||||
if (ret) {
|
||||
dev_err(dev, "Property reg must be given\n");
|
||||
goto fail;
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = fwnode_property_read_u32(child, "adi,number-of-wires", &n_wires);
|
||||
@ -788,8 +782,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Invalid number of wires:%u\n", n_wires);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -799,8 +792,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
|
||||
if (n_wires == 2 || n_wires == 3) {
|
||||
dev_err(dev,
|
||||
"Rotation not allowed for 2/3 Wire RTDs");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
rtd->sensor_config |= LTC2983_RTD_C_ROTATE(1);
|
||||
} else {
|
||||
@ -830,16 +822,14 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
|
||||
"Invalid rsense chann:%d to use in kelvin rsense",
|
||||
rtd->r_sense_chan);
|
||||
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (sensor->chan < min || sensor->chan > max) {
|
||||
dev_err(dev, "Invalid chann:%d for the rtd config",
|
||||
sensor->chan);
|
||||
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
} else {
|
||||
/* same as differential case */
|
||||
@ -847,8 +837,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
|
||||
dev_err(&st->spi->dev,
|
||||
"Invalid chann:%d for RTD", sensor->chan);
|
||||
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -857,10 +846,8 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
|
||||
rtd->custom = __ltc2983_custom_sensor_new(st, child,
|
||||
"adi,custom-rtd",
|
||||
false, 2048, false);
|
||||
if (IS_ERR(rtd->custom)) {
|
||||
ret = PTR_ERR(rtd->custom);
|
||||
goto fail;
|
||||
}
|
||||
if (IS_ERR(rtd->custom))
|
||||
return ERR_CAST(rtd->custom);
|
||||
}
|
||||
|
||||
/* set common parameters */
|
||||
@ -902,18 +889,13 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st,
|
||||
dev_err(&st->spi->dev,
|
||||
"Invalid value for excitation current(%u)",
|
||||
excitation_current);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
fwnode_property_read_u32(child, "adi,rtd-curve", &rtd->rtd_curve);
|
||||
|
||||
fwnode_handle_put(ref);
|
||||
return &rtd->sensor;
|
||||
fail:
|
||||
fwnode_handle_put(ref);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static struct ltc2983_sensor *
|
||||
@ -922,7 +904,6 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
|
||||
{
|
||||
struct ltc2983_thermistor *thermistor;
|
||||
struct device *dev = &st->spi->dev;
|
||||
struct fwnode_handle *ref;
|
||||
u32 excitation_current = 0;
|
||||
int ret = 0;
|
||||
|
||||
@ -930,7 +911,8 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
|
||||
if (!thermistor)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ref = fwnode_find_reference(child, "adi,rsense-handle", 0);
|
||||
struct fwnode_handle *ref __free(fwnode_handle) =
|
||||
fwnode_find_reference(child, "adi,rsense-handle", 0);
|
||||
if (IS_ERR(ref)) {
|
||||
dev_err(dev, "Property adi,rsense-handle missing or invalid");
|
||||
return ERR_CAST(ref);
|
||||
@ -939,7 +921,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
|
||||
ret = fwnode_property_read_u32(ref, "reg", &thermistor->r_sense_chan);
|
||||
if (ret) {
|
||||
dev_err(dev, "rsense channel must be configured...\n");
|
||||
goto fail;
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (fwnode_property_read_bool(child, "adi,single-ended")) {
|
||||
@ -959,8 +941,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
|
||||
dev_err(&st->spi->dev,
|
||||
"Invalid chann:%d for differential thermistor",
|
||||
sensor->chan);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
/* check custom sensor */
|
||||
@ -979,10 +960,8 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
|
||||
propname,
|
||||
steinhart,
|
||||
64, false);
|
||||
if (IS_ERR(thermistor->custom)) {
|
||||
ret = PTR_ERR(thermistor->custom);
|
||||
goto fail;
|
||||
}
|
||||
if (IS_ERR(thermistor->custom))
|
||||
return ERR_CAST(thermistor->custom);
|
||||
}
|
||||
/* set common parameters */
|
||||
thermistor->sensor.fault_handler = ltc2983_common_fault_handler;
|
||||
@ -1006,8 +985,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
|
||||
LTC2983_SENSOR_THERMISTOR_STEINHART) {
|
||||
dev_err(&st->spi->dev,
|
||||
"Auto Range not allowed for custom sensors\n");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
thermistor->excitation_current = 0x0c;
|
||||
break;
|
||||
@ -1048,16 +1026,11 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s
|
||||
dev_err(&st->spi->dev,
|
||||
"Invalid value for excitation current(%u)",
|
||||
excitation_current);
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
fwnode_handle_put(ref);
|
||||
return &thermistor->sensor;
|
||||
fail:
|
||||
fwnode_handle_put(ref);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static struct ltc2983_sensor *
|
||||
@ -1350,8 +1323,7 @@ static irqreturn_t ltc2983_irq_handler(int irq, void *data)
|
||||
static int ltc2983_parse_fw(struct ltc2983_data *st)
|
||||
{
|
||||
struct device *dev = &st->spi->dev;
|
||||
struct fwnode_handle *child;
|
||||
int ret = 0, chan = 0, channel_avail_mask = 0;
|
||||
int ret, chan = 0, channel_avail_mask = 0;
|
||||
|
||||
device_property_read_u32(dev, "adi,mux-delay-config-us", &st->mux_delay_config);
|
||||
|
||||
@ -1369,38 +1341,35 @@ static int ltc2983_parse_fw(struct ltc2983_data *st)
|
||||
return -ENOMEM;
|
||||
|
||||
st->iio_channels = st->num_channels;
|
||||
device_for_each_child_node(dev, child) {
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
struct ltc2983_sensor sensor;
|
||||
|
||||
ret = fwnode_property_read_u32(child, "reg", &sensor.chan);
|
||||
if (ret) {
|
||||
dev_err(dev, "reg property must given for child nodes\n");
|
||||
goto put_child;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"reg property must given for child nodes\n");
|
||||
|
||||
/* check if we have a valid channel */
|
||||
if (sensor.chan < LTC2983_MIN_CHANNELS_NR ||
|
||||
sensor.chan > st->info->max_channels_nr) {
|
||||
ret = -EINVAL;
|
||||
dev_err(dev, "chan:%d must be from %u to %u\n", sensor.chan,
|
||||
LTC2983_MIN_CHANNELS_NR, st->info->max_channels_nr);
|
||||
goto put_child;
|
||||
} else if (channel_avail_mask & BIT(sensor.chan)) {
|
||||
ret = -EINVAL;
|
||||
dev_err(dev, "chan:%d already in use\n", sensor.chan);
|
||||
goto put_child;
|
||||
}
|
||||
sensor.chan > st->info->max_channels_nr)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"chan:%d must be from %u to %u\n",
|
||||
sensor.chan,
|
||||
LTC2983_MIN_CHANNELS_NR,
|
||||
st->info->max_channels_nr);
|
||||
|
||||
if (channel_avail_mask & BIT(sensor.chan))
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"chan:%d already in use\n",
|
||||
sensor.chan);
|
||||
|
||||
ret = fwnode_property_read_u32(child, "adi,sensor-type", &sensor.type);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"adi,sensor-type property must given for child nodes\n");
|
||||
goto put_child;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Create new sensor, type %u, chann %u",
|
||||
sensor.type,
|
||||
sensor.chan);
|
||||
sensor.type, sensor.chan);
|
||||
|
||||
if (sensor.type >= LTC2983_SENSOR_THERMOCOUPLE &&
|
||||
sensor.type <= LTC2983_SENSOR_THERMOCOUPLE_CUSTOM) {
|
||||
@ -1427,17 +1396,15 @@ static int ltc2983_parse_fw(struct ltc2983_data *st)
|
||||
sensor.type == LTC2983_SENSOR_ACTIVE_TEMP) {
|
||||
st->sensors[chan] = ltc2983_temp_new(child, st, &sensor);
|
||||
} else {
|
||||
dev_err(dev, "Unknown sensor type %d\n", sensor.type);
|
||||
ret = -EINVAL;
|
||||
goto put_child;
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Unknown sensor type %d\n",
|
||||
sensor.type);
|
||||
}
|
||||
|
||||
if (IS_ERR(st->sensors[chan])) {
|
||||
dev_err(dev, "Failed to create sensor %ld",
|
||||
PTR_ERR(st->sensors[chan]));
|
||||
ret = PTR_ERR(st->sensors[chan]);
|
||||
goto put_child;
|
||||
}
|
||||
if (IS_ERR(st->sensors[chan]))
|
||||
return dev_err_probe(dev, PTR_ERR(st->sensors[chan]),
|
||||
"Failed to create sensor\n");
|
||||
|
||||
/* set generic sensor parameters */
|
||||
st->sensors[chan]->chan = sensor.chan;
|
||||
st->sensors[chan]->type = sensor.type;
|
||||
@ -1447,9 +1414,6 @@ static int ltc2983_parse_fw(struct ltc2983_data *st)
|
||||
}
|
||||
|
||||
return 0;
|
||||
put_child:
|
||||
fwnode_handle_put(child);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ltc2983_eeprom_cmd(struct ltc2983_data *st, unsigned int cmd,
|
||||
@ -1634,6 +1598,10 @@ static int ltc2983_probe(struct spi_device *spi)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_regulator_get_enable(&spi->dev, "vdd");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gpio = devm_gpiod_get_optional(&st->spi->dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
@ -48,6 +48,7 @@ struct iio_dev;
|
||||
* be used.
|
||||
* @irq_flags: flags for the interrupt used by the triggered buffer
|
||||
* @num_slots: Number of sequencer slots
|
||||
* @irq_line: IRQ for reading conversions. If 0, spi->irq will be used
|
||||
*/
|
||||
struct ad_sigma_delta_info {
|
||||
int (*set_channel)(struct ad_sigma_delta *, unsigned int channel);
|
||||
@ -62,6 +63,7 @@ struct ad_sigma_delta_info {
|
||||
unsigned int data_reg;
|
||||
unsigned long irq_flags;
|
||||
unsigned int num_slots;
|
||||
int irq_line;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -89,6 +91,7 @@ struct ad_sigma_delta {
|
||||
unsigned int active_slots;
|
||||
unsigned int current_slot;
|
||||
unsigned int num_slots;
|
||||
int irq_line;
|
||||
bool status_appended;
|
||||
/* map slots to channels in order to know what to expect from devices */
|
||||
unsigned int *slots;
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct iio_chan_spec;
|
||||
struct fwnode_handle;
|
||||
struct iio_backend;
|
||||
struct device;
|
||||
@ -15,6 +16,26 @@ enum iio_backend_data_type {
|
||||
IIO_BACKEND_DATA_TYPE_MAX
|
||||
};
|
||||
|
||||
enum iio_backend_data_source {
|
||||
IIO_BACKEND_INTERNAL_CONTINUOS_WAVE,
|
||||
IIO_BACKEND_EXTERNAL,
|
||||
IIO_BACKEND_DATA_SOURCE_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* IIO_BACKEND_EX_INFO - Helper for an IIO extended channel attribute
|
||||
* @_name: Attribute name
|
||||
* @_shared: Whether the attribute is shared between all channels
|
||||
* @_what: Data private to the driver
|
||||
*/
|
||||
#define IIO_BACKEND_EX_INFO(_name, _shared, _what) { \
|
||||
.name = (_name), \
|
||||
.shared = (_shared), \
|
||||
.read = iio_backend_ext_info_get, \
|
||||
.write = iio_backend_ext_info_set, \
|
||||
.private = (_what), \
|
||||
}
|
||||
|
||||
/**
|
||||
* struct iio_backend_data_fmt - Backend data format
|
||||
* @type: Data type.
|
||||
@ -35,8 +56,13 @@ struct iio_backend_data_fmt {
|
||||
* @chan_enable: Enable one channel.
|
||||
* @chan_disable: Disable one channel.
|
||||
* @data_format_set: Configure the data format for a specific channel.
|
||||
* @data_source_set: Configure the data source for a specific channel.
|
||||
* @set_sample_rate: Configure the sampling rate for a specific channel.
|
||||
* @request_buffer: Request an IIO buffer.
|
||||
* @free_buffer: Free an IIO buffer.
|
||||
* @extend_chan_spec: Extend an IIO channel.
|
||||
* @ext_info_set: Extended info setter.
|
||||
* @ext_info_get: Extended info getter.
|
||||
**/
|
||||
struct iio_backend_ops {
|
||||
int (*enable)(struct iio_backend *back);
|
||||
@ -45,10 +71,21 @@ struct iio_backend_ops {
|
||||
int (*chan_disable)(struct iio_backend *back, unsigned int chan);
|
||||
int (*data_format_set)(struct iio_backend *back, unsigned int chan,
|
||||
const struct iio_backend_data_fmt *data);
|
||||
int (*data_source_set)(struct iio_backend *back, unsigned int chan,
|
||||
enum iio_backend_data_source data);
|
||||
int (*set_sample_rate)(struct iio_backend *back, unsigned int chan,
|
||||
u64 sample_rate_hz);
|
||||
struct iio_buffer *(*request_buffer)(struct iio_backend *back,
|
||||
struct iio_dev *indio_dev);
|
||||
void (*free_buffer)(struct iio_backend *back,
|
||||
struct iio_buffer *buffer);
|
||||
int (*extend_chan_spec)(struct iio_backend *back,
|
||||
struct iio_chan_spec *chan);
|
||||
int (*ext_info_set)(struct iio_backend *back, uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len);
|
||||
int (*ext_info_get)(struct iio_backend *back, uintptr_t private,
|
||||
const struct iio_chan_spec *chan, char *buf);
|
||||
};
|
||||
|
||||
int iio_backend_chan_enable(struct iio_backend *back, unsigned int chan);
|
||||
@ -56,10 +93,22 @@ int iio_backend_chan_disable(struct iio_backend *back, unsigned int chan);
|
||||
int devm_iio_backend_enable(struct device *dev, struct iio_backend *back);
|
||||
int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan,
|
||||
const struct iio_backend_data_fmt *data);
|
||||
int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan,
|
||||
enum iio_backend_data_source data);
|
||||
int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan,
|
||||
u64 sample_rate_hz);
|
||||
int devm_iio_backend_request_buffer(struct device *dev,
|
||||
struct iio_backend *back,
|
||||
struct iio_dev *indio_dev);
|
||||
ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len);
|
||||
ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private,
|
||||
const struct iio_chan_spec *chan, char *buf);
|
||||
|
||||
int iio_backend_extend_chan_spec(struct iio_dev *indio_dev,
|
||||
struct iio_backend *back,
|
||||
struct iio_chan_spec *chan);
|
||||
void *iio_backend_get_priv(const struct iio_backend *conv);
|
||||
struct iio_backend *devm_iio_backend_get(struct device *dev, const char *name);
|
||||
struct iio_backend *
|
||||
|
@ -132,7 +132,9 @@ int iio_dma_buffer_disable(struct iio_buffer *buffer,
|
||||
struct iio_dev *indio_dev);
|
||||
int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n,
|
||||
char __user *user_buffer);
|
||||
size_t iio_dma_buffer_data_available(struct iio_buffer *buffer);
|
||||
int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n,
|
||||
const char __user *user_buffer);
|
||||
size_t iio_dma_buffer_usage(struct iio_buffer *buffer);
|
||||
int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd);
|
||||
int iio_dma_buffer_set_length(struct iio_buffer *buffer, unsigned int length);
|
||||
int iio_dma_buffer_request_update(struct iio_buffer *buffer);
|
||||
|
@ -7,14 +7,28 @@
|
||||
#ifndef __IIO_DMAENGINE_H__
|
||||
#define __IIO_DMAENGINE_H__
|
||||
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
struct iio_dev;
|
||||
struct device;
|
||||
|
||||
struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev,
|
||||
const char *channel);
|
||||
void iio_dmaengine_buffer_free(struct iio_buffer *buffer);
|
||||
int devm_iio_dmaengine_buffer_setup(struct device *dev,
|
||||
struct iio_dev *indio_dev,
|
||||
const char *channel);
|
||||
struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev,
|
||||
struct iio_dev *indio_dev,
|
||||
const char *channel,
|
||||
enum iio_buffer_direction dir);
|
||||
|
||||
#define iio_dmaengine_buffer_setup(dev, indio_dev, channel) \
|
||||
iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, \
|
||||
IIO_BUFFER_DIRECTION_IN)
|
||||
|
||||
int devm_iio_dmaengine_buffer_setup_ext(struct device *dev,
|
||||
struct iio_dev *indio_dev,
|
||||
const char *channel,
|
||||
enum iio_buffer_direction dir);
|
||||
|
||||
#define devm_iio_dmaengine_buffer_setup(dev, indio_dev, channel) \
|
||||
devm_iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, \
|
||||
IIO_BUFFER_DIRECTION_IN)
|
||||
|
||||
#endif
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/args.h>
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/fwnode.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
@ -174,13 +175,32 @@ struct fwnode_handle *device_get_next_child_node(const struct device *dev,
|
||||
for (child = device_get_next_child_node(dev, NULL); child; \
|
||||
child = device_get_next_child_node(dev, child))
|
||||
|
||||
#define device_for_each_child_node_scoped(dev, child) \
|
||||
for (struct fwnode_handle *child __free(fwnode_handle) = \
|
||||
device_get_next_child_node(dev, NULL); \
|
||||
child; child = device_get_next_child_node(dev, child))
|
||||
|
||||
struct fwnode_handle *fwnode_get_named_child_node(const struct fwnode_handle *fwnode,
|
||||
const char *childname);
|
||||
struct fwnode_handle *device_get_named_child_node(const struct device *dev,
|
||||
const char *childname);
|
||||
|
||||
struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode);
|
||||
void fwnode_handle_put(struct fwnode_handle *fwnode);
|
||||
|
||||
/**
|
||||
* fwnode_handle_put - Drop reference to a device node
|
||||
* @fwnode: Pointer to the device node to drop the reference to.
|
||||
*
|
||||
* This has to be used when terminating device_for_each_child_node() iteration
|
||||
* with break or return to prevent stale device node references from being left
|
||||
* behind.
|
||||
*/
|
||||
static inline void fwnode_handle_put(struct fwnode_handle *fwnode)
|
||||
{
|
||||
fwnode_call_void_op(fwnode, put);
|
||||
}
|
||||
|
||||
DEFINE_FREE(fwnode_handle, struct fwnode_handle *, fwnode_handle_put(_T))
|
||||
|
||||
int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index);
|
||||
int fwnode_irq_get_byname(const struct fwnode_handle *fwnode, const char *name);
|
||||
|
Loading…
x
Reference in New Issue
Block a user