mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-06 02:24:14 +08:00
First set of new IIO device support, fatures and cleanups for the 5.7 cycle
Includes changes for the counter subsystem Core Feature * Explicitly handle sysfs values in dB, including correctly handling the needed postfix dB. * Add a TODO to suggest suitable activities for new contributors to IIO now the vast majority of drivers are out of staging (and the remaining ones there are 'hard'). Also update the TODO in staging to remove stale entries. Staging graduations * ad7192 ADC. New device support * ad5770r - New driver for this 6 channel DAC including DT bindings. * ad8366 - Add supprot for the hmc1119 attenuator. * al3010 - New driver supporting this Dyna-image light sensors. - Power management and DT bindings added in additional patches. * atlas-sensor - Add support for atlas DO-SM device. Reads disolved oxygen in a solution. * gpap002x00f - New driver and bindings to support the GP2AP002A00F and GP2AP002S00F light and proximity sensors. There is some limited existing support in input. The intent is to drop this driver once IIO driver is in place. * hmc425a - New driver for this attenuator. * icp10100 - New driver for this presure sensor. * ltc2632 - Add support for the ltc2636 8 channel DAC. Includes bindings and some tidying up of the driver. * inv_mpu6050 - Support IAM20680, ICM20609, ICM20689 and ICM20690. Includes related tidy up and rework of low pass filter bandwidth handling to give suitable values for all chips. Binding conversions to yaml or missing bindings docs. * atlas-sensor, including consolidation of previous 3 separate docs into 1. * ad7923, previously no doc. * max1363, split into max1238 and max1363 to simplify yaml. * stm32-adc Features * (counter) 104-quad-8 - Support a filter clock prescaler. - Support reporting of encoder cable status. * ad7124 - Low pass filter support. - Debugfs interface to access registers directly. * ad8366 - Support control of hardware gain. * inv_mpu6050 - Runtime pm with autosuspend. * npcm adc - Add reset support. This is a breaking change if DT is not in sync, however this device is a BMC so the ecosystem is closed enought that this should not be a problem. * srf04 - Add power management with DT bindings for the GPIO. * stm32-timer-trigger - Power management. * (counter) stm32-timer-cnt - Power management. * vcnl4000 - Enable runtime PM for devices that don't use on demand measurement. Cleanups and minor fixes * core - Avoid double read when using debugfs. Whilst we provide no guarantees on lack of side effects using the debugfs interfaces, this one is generate unexpected results so let us tidy it up. * dac/Kconfig - Alphabetic order. * ad5755 - Grammar and minor other fixes. * ad7124 - Fail probe if get_voltage fails as no meaningful readings can be had without knowing the external reference. - Switch to selection between different channel attributes rather than building the arrays at runtime. - Remove the spi_device_id table as the driver cannot be probled without more information that can be provided without dt. - Update sysfs docs to provide more inormation and bring remaining docs for this part out of staging. * ad9292 - Use new SPI transfer delay structure. * adis library - Add unlocked version of adis_initial_startup and refactor the function. - Add a product ID santiy check. - Add support for different self test registers. - Use new SPI delay structure. - Add new docs and tidy up existing. * adis16136 - Initialize adis_data statically. * adis16400 - Initialize adis_data statically. * adis16460 - Use core __adis_initial_Startup now it supports everything needed. * adis16480 - Initialize adis_data statically. - Use core __adis_initial_startup now it supports everything needed. * al3320a - Add missing DT binding docs. - Tidy up code formatting. - Simplify error paths using devm_add_action_or_reset. - Ensure autoloading works by adding the of_match_table. * atlas-sensor - Drop false requirement for interrupt line, the value can be polled using a sysfs or hrtimer type trigger. * exynos-adc - Silence warning message on deferring probe. * gp2ap002 - Greatly simplify the Lux LUT. - Reorder actions around buffer setup and tear down as part of a sub-system wide standardization of these. * inv_mpu6050 - Various lttle tidyups. - Simpliy I2C aux MUX handling by enabling it only at startup. It never needs to be disabled. - Simplify polling rate when magnetometer enabled by putting only under control of userspace. - Always execute full reset on devices supporting spi. It does no harm when using i2c and makes for simpler code. - Reduce over the top sleep times for vddio regulator power up. - Greatly simplify power and engine management. - Fix some delays in polled reads (only visible due to other changes) - Stop preventing sampling rate changes whilst running as there is no adverse consequence of doing so. - Prevent attempting to read the temperature if neither accel nor gyro is enabled. * lmp9100 - Reorder actions around buffer setup and tear down as part of a sub-system wide standardization of these. * max1118 - Use new SPI transfer delay structure. * mcp320x - Use new SPI transfer delay structure. * si1133 - Read full 24 bit signed integer instead o dropping last 8 bits of value. Not a critical fix as just adds precision. * st_sensors - Use st_sensors_dev_name_probe instead of open coded version in st_accel - Handle potential memory allocation failure. * st_lsm6dsx - Fix some wrong structure element naming in documentation. - Add missing return value check. * stm32_timer_cnt - Drop some unused left over IIO headers from this count subsystem driver. - Ensure the clock is enabled in master mode. Theoretical issue rather than one known to happen in the wild. * tlc4541 - Use new SPI delay structure. -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAl5lNK8RHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0Fohm+g/9FY7R4nPV2LoLPgbS2U4Z9f6KiPrMlv7i 6XjYgRgwg3w40R/pQHC5rZ/TTQ13/ZEaT+uZHiZ+gWl6Tr2oqbSGroigvF5zqRtv vuoJ+RaVbkL5EJgqS9Ek2fHmxmUAXJuWjJPUAFk+nt9csKqMykmlYxQcUPqDr5hF bPXm5l55V75QElzBHwYfBdue5CJgOCa/cHjwYd0IskT3vXom6XJzZEr0vr4/EIPT hM8IZHl8d2jDhU74TZEysCB/rCfPLKRugFta/uCm5LYVn0kSfFFrGI8uFzwdth5p GD7eTqaP37fFl2CyH8c1MOJinQFesv28gxIdSZDXxonmZ1Kvjl8DfRz3wbh0WZpi EWCutYzuRDgzqwLUxN4TrUuHwlVJLgkMBeK+EwlKmmguqW5b61m0N7mUXOpp27tU Wm97Pt6cLcBj3xF5yD/CQRW1alW0shK0JhdDhIwDK2MhEX8kxRRfxaUC7SUFdank f4fNqHXTkNuJiOXz6KHDcQJge7tJsXTd9PWYgMM3fqm3vGvC3XKobr0sN4jzbyMy ebt5tnwphytIaKr76DQXgeVDFXSpRpI2IPDlI+Bfl4qAQ8MxSH4tOzFvcZBx4eTA KL384H2TfiFTAV5n/Qxil9jD1RZbo1YNDHpmCQo5V1kwBBPGGSryz6ClVVHML1+/ WpfJOLLc+/U= =20Uc -----END PGP SIGNATURE----- Merge tag 'iio-5.7a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: First set of new IIO device support, fatures and cleanups for the 5.7 cycle Includes changes for the counter subsystem Core Feature * Explicitly handle sysfs values in dB, including correctly handling the needed postfix dB. * Add a TODO to suggest suitable activities for new contributors to IIO now the vast majority of drivers are out of staging (and the remaining ones there are 'hard'). Also update the TODO in staging to remove stale entries. Staging graduations * ad7192 ADC. New device support * ad5770r - New driver for this 6 channel DAC including DT bindings. * ad8366 - Add supprot for the hmc1119 attenuator. * al3010 - New driver supporting this Dyna-image light sensors. - Power management and DT bindings added in additional patches. * atlas-sensor - Add support for atlas DO-SM device. Reads disolved oxygen in a solution. * gpap002x00f - New driver and bindings to support the GP2AP002A00F and GP2AP002S00F light and proximity sensors. There is some limited existing support in input. The intent is to drop this driver once IIO driver is in place. * hmc425a - New driver for this attenuator. * icp10100 - New driver for this presure sensor. * ltc2632 - Add support for the ltc2636 8 channel DAC. Includes bindings and some tidying up of the driver. * inv_mpu6050 - Support IAM20680, ICM20609, ICM20689 and ICM20690. Includes related tidy up and rework of low pass filter bandwidth handling to give suitable values for all chips. Binding conversions to yaml or missing bindings docs. * atlas-sensor, including consolidation of previous 3 separate docs into 1. * ad7923, previously no doc. * max1363, split into max1238 and max1363 to simplify yaml. * stm32-adc Features * (counter) 104-quad-8 - Support a filter clock prescaler. - Support reporting of encoder cable status. * ad7124 - Low pass filter support. - Debugfs interface to access registers directly. * ad8366 - Support control of hardware gain. * inv_mpu6050 - Runtime pm with autosuspend. * npcm adc - Add reset support. This is a breaking change if DT is not in sync, however this device is a BMC so the ecosystem is closed enought that this should not be a problem. * srf04 - Add power management with DT bindings for the GPIO. * stm32-timer-trigger - Power management. * (counter) stm32-timer-cnt - Power management. * vcnl4000 - Enable runtime PM for devices that don't use on demand measurement. Cleanups and minor fixes * core - Avoid double read when using debugfs. Whilst we provide no guarantees on lack of side effects using the debugfs interfaces, this one is generate unexpected results so let us tidy it up. * dac/Kconfig - Alphabetic order. * ad5755 - Grammar and minor other fixes. * ad7124 - Fail probe if get_voltage fails as no meaningful readings can be had without knowing the external reference. - Switch to selection between different channel attributes rather than building the arrays at runtime. - Remove the spi_device_id table as the driver cannot be probled without more information that can be provided without dt. - Update sysfs docs to provide more inormation and bring remaining docs for this part out of staging. * ad9292 - Use new SPI transfer delay structure. * adis library - Add unlocked version of adis_initial_startup and refactor the function. - Add a product ID santiy check. - Add support for different self test registers. - Use new SPI delay structure. - Add new docs and tidy up existing. * adis16136 - Initialize adis_data statically. * adis16400 - Initialize adis_data statically. * adis16460 - Use core __adis_initial_Startup now it supports everything needed. * adis16480 - Initialize adis_data statically. - Use core __adis_initial_startup now it supports everything needed. * al3320a - Add missing DT binding docs. - Tidy up code formatting. - Simplify error paths using devm_add_action_or_reset. - Ensure autoloading works by adding the of_match_table. * atlas-sensor - Drop false requirement for interrupt line, the value can be polled using a sysfs or hrtimer type trigger. * exynos-adc - Silence warning message on deferring probe. * gp2ap002 - Greatly simplify the Lux LUT. - Reorder actions around buffer setup and tear down as part of a sub-system wide standardization of these. * inv_mpu6050 - Various lttle tidyups. - Simpliy I2C aux MUX handling by enabling it only at startup. It never needs to be disabled. - Simplify polling rate when magnetometer enabled by putting only under control of userspace. - Always execute full reset on devices supporting spi. It does no harm when using i2c and makes for simpler code. - Reduce over the top sleep times for vddio regulator power up. - Greatly simplify power and engine management. - Fix some delays in polled reads (only visible due to other changes) - Stop preventing sampling rate changes whilst running as there is no adverse consequence of doing so. - Prevent attempting to read the temperature if neither accel nor gyro is enabled. * lmp9100 - Reorder actions around buffer setup and tear down as part of a sub-system wide standardization of these. * max1118 - Use new SPI transfer delay structure. * mcp320x - Use new SPI transfer delay structure. * si1133 - Read full 24 bit signed integer instead o dropping last 8 bits of value. Not a critical fix as just adds precision. * st_sensors - Use st_sensors_dev_name_probe instead of open coded version in st_accel - Handle potential memory allocation failure. * st_lsm6dsx - Fix some wrong structure element naming in documentation. - Add missing return value check. * stm32_timer_cnt - Drop some unused left over IIO headers from this count subsystem driver. - Ensure the clock is enabled in master mode. Theoretical issue rather than one known to happen in the wild. * tlc4541 - Use new SPI delay structure. * tag 'iio-5.7a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (98 commits) iio: dac: Kconfig: sort symbols alphabetically iio: light: gp2ap020a00f: fix iio_triggered_buffer_{predisable,postenable} positions iio: potentiostat: lmp9100: fix iio_triggered_buffer_{predisable,postenable} positions iio: trigger: stm32-timer: add power management support iio: trigger: stm32-timer: rename enabled flag iio: add a TODO counter: 104-quad-8: Support Differential Encoder Cable Status counter: 104-quad-8: Support Filter Clock Prescaler iio: pressure: icp10100: add driver for InvenSense ICP-101xx iio: industrialio-core: Fix debugfs read iio: imu: adis: add a note better explaining state_lock iio: imu: adis: update 'adis_data' struct doc-string iio: imu: adis: add doc-string for 'adis' struct iio: imu: adis_buffer: Use new structure for SPI transfer delays iio: adc: ti-tlc4541: Use new structure for SPI transfer delays iio: adc: mcp320x: Use new structure for SPI transfer delays iio: adc: max1118: Use new structure for SPI transfer delays iio: adc: ad9292: Use new structure for SPI transfer delays iio: adc: exynos: Silence warning about regulators during deferred probe staging: iio: update TODO ...
This commit is contained in:
commit
281d90e24f
@ -1,3 +1,28 @@
|
||||
What: /sys/bus/counter/devices/counterX/signalY/cable_fault
|
||||
KernelVersion: 5.7
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Read-only attribute that indicates whether a differential
|
||||
encoder cable fault (not connected or loose wires) is detected
|
||||
for the respective channel of Signal Y. Valid attribute values
|
||||
are boolean. Detection must first be enabled via the
|
||||
corresponding cable_fault_enable attribute.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/signalY/cable_fault_enable
|
||||
KernelVersion: 5.7
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Whether detection of differential encoder cable faults for the
|
||||
respective channel of Signal Y is enabled. Valid attribute
|
||||
values are boolean.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/signalY/filter_clock_prescaler
|
||||
KernelVersion: 5.7
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Filter clock factor for input Signal Y. This prescaler value
|
||||
affects the inputs of both quadrature pair signals.
|
||||
|
||||
What: /sys/bus/counter/devices/counterX/signalY/index_polarity
|
||||
KernelVersion: 5.2
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
|
@ -2,17 +2,22 @@ What: /sys/bus/iio/devices/iio:deviceX/ac_excitation_en
|
||||
KernelVersion:
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Reading gives the state of AC excitation.
|
||||
Writing '1' enables AC excitation.
|
||||
This attribute, if available, is used to enable the AC
|
||||
excitation mode found on some converters. In ac excitation mode,
|
||||
the polarity of the excitation voltage is reversed on
|
||||
alternate cycles, to eliminate DC errors.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/bridge_switch_en
|
||||
KernelVersion:
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This bridge switch is used to disconnect it when there is a
|
||||
need to minimize the system current consumption.
|
||||
Reading gives the state of the bridge switch.
|
||||
Writing '1' enables the bridge switch.
|
||||
This attribute, if available, is used to close or open the
|
||||
bridge power down switch found on some converters.
|
||||
In bridge applications, such as strain gauges and load cells,
|
||||
the bridge itself consumes the majority of the current in the
|
||||
system. To minimize the current consumption of the system,
|
||||
the bridge can be disconnected (when it is not being used
|
||||
using the bridge_switch_en attribute.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration
|
||||
KernelVersion:
|
||||
@ -21,6 +26,13 @@ Description:
|
||||
Initiates the system calibration procedure. This is done on a
|
||||
single channel at a time. Write '1' to start the calibration.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltage2-voltage2_shorted_raw
|
||||
KernelVersion:
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
Measure voltage from AIN2 pin connected to AIN(+)
|
||||
and AIN(-) shorted.
|
||||
|
||||
What: /sys/bus/iio/devices/iio:deviceX/in_voltagex_sys_calibration_mode_available
|
||||
KernelVersion:
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
|
65
Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml
Normal file
65
Documentation/devicetree/bindings/iio/adc/adi,ad7923.yaml
Normal file
@ -0,0 +1,65 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/adi,ad7923.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD7923 and similars with 4 and 8 Channel ADCs.
|
||||
|
||||
maintainers:
|
||||
- Michael Hennerich <michael.hennerich@analog.com>
|
||||
- Patrick Vasseur <patrick.vasseur@c-s.fr>
|
||||
|
||||
description: |
|
||||
Analog Devices AD7904, AD7914, AD7923, AD7924 4 Channel ADCs, and AD7908,
|
||||
AD7918, AD7928 8 Channels ADCs.
|
||||
|
||||
Specifications about the part can be found at:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7923.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7904_7914_7924.pdf
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD7908_7918_7928.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad7904
|
||||
- adi,ad7914
|
||||
- adi,ad7923
|
||||
- adi,ad7924
|
||||
- adi,ad7908
|
||||
- adi,ad7918
|
||||
- adi,ad7928
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
refin-supply:
|
||||
description: |
|
||||
The regulator supply for ADC reference voltage.
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ad7928: adc@0 {
|
||||
compatible = "adi,ad7928";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <25000000>;
|
||||
refin-supply = <&adc_vref>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
};
|
@ -1,63 +0,0 @@
|
||||
* Maxim 1x3x/136x/116xx Analog to Digital Converter (ADC)
|
||||
|
||||
The node for this driver must be a child node of a I2C controller, hence
|
||||
all mandatory properties for your controller must be specified. See directory:
|
||||
|
||||
Documentation/devicetree/bindings/i2c
|
||||
|
||||
for more details.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of
|
||||
"maxim,max1361"
|
||||
"maxim,max1362"
|
||||
"maxim,max1363"
|
||||
"maxim,max1364"
|
||||
"maxim,max1036"
|
||||
"maxim,max1037"
|
||||
"maxim,max1038"
|
||||
"maxim,max1039"
|
||||
"maxim,max1136"
|
||||
"maxim,max1137"
|
||||
"maxim,max1138"
|
||||
"maxim,max1139"
|
||||
"maxim,max1236"
|
||||
"maxim,max1237"
|
||||
"maxim,max1238"
|
||||
"maxim,max1239"
|
||||
"maxim,max11600"
|
||||
"maxim,max11601"
|
||||
"maxim,max11602"
|
||||
"maxim,max11603"
|
||||
"maxim,max11604"
|
||||
"maxim,max11605"
|
||||
"maxim,max11606"
|
||||
"maxim,max11607"
|
||||
"maxim,max11608"
|
||||
"maxim,max11609"
|
||||
"maxim,max11610"
|
||||
"maxim,max11611"
|
||||
"maxim,max11612"
|
||||
"maxim,max11613"
|
||||
"maxim,max11614"
|
||||
"maxim,max11615"
|
||||
"maxim,max11616"
|
||||
"maxim,max11617"
|
||||
"maxim,max11644"
|
||||
"maxim,max11645"
|
||||
"maxim,max11646"
|
||||
"maxim,max11647"
|
||||
- reg: Should contain the ADC I2C address
|
||||
|
||||
Optional properties:
|
||||
- vcc-supply: phandle to the regulator that provides power to the ADC.
|
||||
- vref-supply: phandle to the regulator for ADC reference voltage.
|
||||
- interrupts: IRQ line for the ADC. If not used the driver will use
|
||||
polling.
|
||||
|
||||
Example:
|
||||
adc: max11644@36 {
|
||||
compatible = "maxim,max11644";
|
||||
reg = <0x36>;
|
||||
vref-supply = <&adc_vref>;
|
||||
};
|
76
Documentation/devicetree/bindings/iio/adc/maxim,max1238.yaml
Normal file
76
Documentation/devicetree/bindings/iio/adc/maxim,max1238.yaml
Normal file
@ -0,0 +1,76 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/maxim,max1238.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Maxim MAX1238 and similar ADCs
|
||||
|
||||
maintainers:
|
||||
- Jonathan Cameron <jic23@kernel.org>
|
||||
|
||||
description: |
|
||||
Family of simple ADCs with i2c inteface and internal references.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- maxim,max1036
|
||||
- maxim,max1037
|
||||
- maxim,max1038
|
||||
- maxim,max1039
|
||||
- maxim,max1136
|
||||
- maxim,max1137
|
||||
- maxim,max1138
|
||||
- maxim,max1139
|
||||
- maxim,max1236
|
||||
- maxim,max1237
|
||||
- maxim,max1238
|
||||
- maxim,max1239
|
||||
- maxim,max11600
|
||||
- maxim,max11601
|
||||
- maxim,max11602
|
||||
- maxim,max11603
|
||||
- maxim,max11604
|
||||
- maxim,max11605
|
||||
- maxim,max11606
|
||||
- maxim,max11607
|
||||
- maxim,max11608
|
||||
- maxim,max11609
|
||||
- maxim,max11610
|
||||
- maxim,max11611
|
||||
- maxim,max11612
|
||||
- maxim,max11613
|
||||
- maxim,max11614
|
||||
- maxim,max11615
|
||||
- maxim,max11616
|
||||
- maxim,max11617
|
||||
- maxim,max11644
|
||||
- maxim,max11645
|
||||
- maxim,max11646
|
||||
- maxim,max11647
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vcc-supply: true
|
||||
vref-supply:
|
||||
description: Optional external reference. If not supplied, internal
|
||||
reference will be used.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@36 {
|
||||
compatible = "maxim,max1238";
|
||||
reg = <0x36>;
|
||||
};
|
||||
};
|
||||
...
|
50
Documentation/devicetree/bindings/iio/adc/maxim,max1363.yaml
Normal file
50
Documentation/devicetree/bindings/iio/adc/maxim,max1363.yaml
Normal file
@ -0,0 +1,50 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/adc/maxim,max1363.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Maxim MAX1363 and similar ADCs
|
||||
|
||||
maintainers:
|
||||
- Jonathan Cameron <jic23@kernel.org>
|
||||
|
||||
description: |
|
||||
Family of ADCs with i2c inteface, internal references and threshold
|
||||
monitoring.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- maxim,max1361
|
||||
- maxim,max1362
|
||||
- maxim,max1363
|
||||
- maxim,max1364
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vcc-supply: true
|
||||
vref-supply:
|
||||
description: Optional external reference. If not supplied, internal
|
||||
reference will be used.
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@36 {
|
||||
compatible = "maxim,max1363";
|
||||
reg = <0x36>;
|
||||
};
|
||||
};
|
||||
...
|
@ -6,6 +6,7 @@ Required properties:
|
||||
- compatible: "nuvoton,npcm750-adc" for the NPCM7XX BMC.
|
||||
- reg: specifies physical base address and size of the registers.
|
||||
- interrupts: Contain the ADC interrupt with flags for falling edge.
|
||||
- resets : phandle to the reset control for this device.
|
||||
|
||||
Optional properties:
|
||||
- clocks: phandle of ADC reference clock, in case the clock is not
|
||||
@ -21,4 +22,5 @@ adc: adc@f000c000 {
|
||||
reg = <0xf000c000 0x8>;
|
||||
interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clk NPCM7XX_CLK_ADC>;
|
||||
resets = <&rstc NPCM7XX_RESET_IPSRST1 NPCM7XX_RESET_ADC>;
|
||||
};
|
||||
|
@ -1,149 +0,0 @@
|
||||
STMicroelectronics STM32 ADC device driver
|
||||
|
||||
STM32 ADC is a successive approximation analog-to-digital converter.
|
||||
It has several multiplexed input channels. Conversions can be performed
|
||||
in single, continuous, scan or discontinuous mode. Result of the ADC is
|
||||
stored in a left-aligned or right-aligned 32-bit data register.
|
||||
Conversions can be launched in software or using hardware triggers.
|
||||
|
||||
The analog watchdog feature allows the application to detect if the input
|
||||
voltage goes beyond the user-defined, higher or lower thresholds.
|
||||
|
||||
Each STM32 ADC block can have up to 3 ADC instances.
|
||||
|
||||
Each instance supports two contexts to manage conversions, each one has its
|
||||
own configurable sequence and trigger:
|
||||
- regular conversion can be done in sequence, running in background
|
||||
- injected conversions have higher priority, and so have the ability to
|
||||
interrupt regular conversion sequence (either triggered in SW or HW).
|
||||
Regular sequence is resumed, in case it has been interrupted.
|
||||
|
||||
Contents of a stm32 adc root node:
|
||||
-----------------------------------
|
||||
Required properties:
|
||||
- compatible: Should be one of:
|
||||
"st,stm32f4-adc-core"
|
||||
"st,stm32h7-adc-core"
|
||||
"st,stm32mp1-adc-core"
|
||||
- reg: Offset and length of the ADC block register set.
|
||||
- interrupts: One or more interrupts for ADC block. Some parts like stm32f4
|
||||
and stm32h7 share a common ADC interrupt line. stm32mp1 has two separate
|
||||
interrupt lines, one for each ADC within ADC block.
|
||||
- clocks: Core can use up to two clocks, depending on part used:
|
||||
- "adc" clock: for the analog circuitry, common to all ADCs.
|
||||
It's required on stm32f4.
|
||||
It's optional on stm32h7.
|
||||
- "bus" clock: for registers access, common to all ADCs.
|
||||
It's not present on stm32f4.
|
||||
It's required on stm32h7.
|
||||
- clock-names: Must be "adc" and/or "bus" depending on part used.
|
||||
- interrupt-controller: Identifies the controller node as interrupt-parent
|
||||
- vdda-supply: Phandle to the vdda input analog voltage.
|
||||
- vref-supply: Phandle to the vref input analog reference voltage.
|
||||
- #interrupt-cells = <1>;
|
||||
- #address-cells = <1>;
|
||||
- #size-cells = <0>;
|
||||
|
||||
Optional properties:
|
||||
- A pinctrl state named "default" for each ADC channel may be defined to set
|
||||
inX ADC pins in mode of operation for analog input on external pin.
|
||||
- booster-supply: Phandle to the embedded booster regulator that can be used
|
||||
to supply ADC analog input switches on stm32h7 and stm32mp1.
|
||||
- vdd-supply: Phandle to the vdd input voltage. It can be used to supply ADC
|
||||
analog input switches on stm32mp1.
|
||||
- st,syscfg: Phandle to system configuration controller. It can be used to
|
||||
control the analog circuitry on stm32mp1.
|
||||
- st,max-clk-rate-hz: Allow to specify desired max clock rate used by analog
|
||||
circuitry.
|
||||
|
||||
Contents of a stm32 adc child node:
|
||||
-----------------------------------
|
||||
An ADC block node should contain at least one subnode, representing an
|
||||
ADC instance available on the machine.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be one of:
|
||||
"st,stm32f4-adc"
|
||||
"st,stm32h7-adc"
|
||||
"st,stm32mp1-adc"
|
||||
- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
|
||||
- clocks: Input clock private to this ADC instance. It's required only on
|
||||
stm32f4, that has per instance clock input for registers access.
|
||||
- interrupts: IRQ Line for the ADC (e.g. may be 0 for adc@0, 1 for adc@100 or
|
||||
2 for adc@200).
|
||||
- st,adc-channels: List of single-ended channels muxed for this ADC.
|
||||
It can have up to 16 channels on stm32f4 or 20 channels on stm32h7, numbered
|
||||
from 0 to 15 or 19 (resp. for in0..in15 or in0..in19).
|
||||
- st,adc-diff-channels: List of differential channels muxed for this ADC.
|
||||
Depending on part used, some channels can be configured as differential
|
||||
instead of single-ended (e.g. stm32h7). List here positive and negative
|
||||
inputs pairs as <vinp vinn>, <vinp vinn>,... vinp and vinn are numbered
|
||||
from 0 to 19 on stm32h7)
|
||||
Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is required.
|
||||
Both properties can be used together. Some channels can be used as
|
||||
single-ended and some other ones as differential (mixed). But channels
|
||||
can't be configured both as single-ended and differential (invalid).
|
||||
- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
|
||||
Documentation/devicetree/bindings/iio/iio-bindings.txt
|
||||
|
||||
Optional properties:
|
||||
- dmas: Phandle to dma channel for this ADC instance.
|
||||
See ../../dma/dma.txt for details.
|
||||
- dma-names: Must be "rx" when dmas property is being used.
|
||||
- assigned-resolution-bits: Resolution (bits) to use for conversions. Must
|
||||
match device available resolutions:
|
||||
* can be 6, 8, 10 or 12 on stm32f4
|
||||
* can be 8, 10, 12, 14 or 16 on stm32h7
|
||||
Default is maximum resolution if unset.
|
||||
- st,min-sample-time-nsecs: Minimum sampling time in nanoseconds.
|
||||
Depending on hardware (board) e.g. high/low analog input source impedance,
|
||||
fine tune of ADC sampling time may be recommended.
|
||||
This can be either one value or an array that matches 'st,adc-channels' list,
|
||||
to set sample time resp. for all channels, or independently for each channel.
|
||||
|
||||
Example:
|
||||
adc: adc@40012000 {
|
||||
compatible = "st,stm32f4-adc-core";
|
||||
reg = <0x40012000 0x400>;
|
||||
interrupts = <18>;
|
||||
clocks = <&rcc 0 168>;
|
||||
clock-names = "adc";
|
||||
vref-supply = <®_vref>;
|
||||
interrupt-controller;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&adc3_in8_pin>;
|
||||
|
||||
#interrupt-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
adc@0 {
|
||||
compatible = "st,stm32f4-adc";
|
||||
#io-channel-cells = <1>;
|
||||
reg = <0x0>;
|
||||
clocks = <&rcc 0 168>;
|
||||
interrupt-parent = <&adc>;
|
||||
interrupts = <0>;
|
||||
st,adc-channels = <8>;
|
||||
dmas = <&dma2 0 0 0x400 0x0>;
|
||||
dma-names = "rx";
|
||||
assigned-resolution-bits = <8>;
|
||||
};
|
||||
...
|
||||
other adc child nodes follow...
|
||||
};
|
||||
|
||||
Example to setup:
|
||||
- channel 1 as single-ended
|
||||
- channels 2 & 3 as differential (with resp. 6 & 7 negative inputs)
|
||||
|
||||
adc: adc@40022000 {
|
||||
compatible = "st,stm32h7-adc-core";
|
||||
...
|
||||
adc1: adc@0 {
|
||||
compatible = "st,stm32h7-adc";
|
||||
...
|
||||
st,adc-channels = <1>;
|
||||
st,adc-diff-channels = <2 6>, <3 7>;
|
||||
};
|
||||
};
|
458
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
Normal file
458
Documentation/devicetree/bindings/iio/adc/st,stm32-adc.yaml
Normal file
@ -0,0 +1,458 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: "http://devicetree.org/schemas/bindings/iio/adc/st,stm32-adc.yaml#"
|
||||
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
|
||||
|
||||
title: STMicroelectronics STM32 ADC bindings
|
||||
|
||||
description: |
|
||||
STM32 ADC is a successive approximation analog-to-digital converter.
|
||||
It has several multiplexed input channels. Conversions can be performed
|
||||
in single, continuous, scan or discontinuous mode. Result of the ADC is
|
||||
stored in a left-aligned or right-aligned 32-bit data register.
|
||||
Conversions can be launched in software or using hardware triggers.
|
||||
|
||||
The analog watchdog feature allows the application to detect if the input
|
||||
voltage goes beyond the user-defined, higher or lower thresholds.
|
||||
|
||||
Each STM32 ADC block can have up to 3 ADC instances.
|
||||
|
||||
maintainers:
|
||||
- Fabrice Gasnier <fabrice.gasnier@st.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- st,stm32f4-adc-core
|
||||
- st,stm32h7-adc-core
|
||||
- st,stm32mp1-adc-core
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
description: |
|
||||
One or more interrupts for ADC block, depending on part used:
|
||||
- stm32f4 and stm32h7 share a common ADC interrupt line.
|
||||
- stm32mp1 has two separate interrupt lines, one for each ADC within
|
||||
ADC block.
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clocks:
|
||||
description: |
|
||||
Core can use up to two clocks, depending on part used:
|
||||
- "adc" clock: for the analog circuitry, common to all ADCs.
|
||||
It's required on stm32f4.
|
||||
It's optional on stm32h7 and stm32mp1.
|
||||
- "bus" clock: for registers access, common to all ADCs.
|
||||
It's not present on stm32f4.
|
||||
It's required on stm32h7 and stm32mp1.
|
||||
|
||||
clock-names: true
|
||||
|
||||
st,max-clk-rate-hz:
|
||||
description:
|
||||
Allow to specify desired max clock rate used by analog circuitry.
|
||||
|
||||
vdda-supply:
|
||||
description: Phandle to the vdda input analog voltage.
|
||||
|
||||
vref-supply:
|
||||
description: Phandle to the vref input analog reference voltage.
|
||||
|
||||
booster-supply:
|
||||
description:
|
||||
Phandle to the embedded booster regulator that can be used to supply ADC
|
||||
analog input switches on stm32h7 and stm32mp1.
|
||||
|
||||
vdd-supply:
|
||||
description:
|
||||
Phandle to the vdd input voltage. It can be used to supply ADC analog
|
||||
input switches on stm32mp1.
|
||||
|
||||
st,syscfg:
|
||||
description:
|
||||
Phandle to system configuration controller. It can be used to control the
|
||||
analog circuitry on stm32mp1.
|
||||
allOf:
|
||||
- $ref: "/schemas/types.yaml#/definitions/phandle-array"
|
||||
|
||||
interrupt-controller: true
|
||||
|
||||
'#interrupt-cells':
|
||||
const: 1
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: st,stm32f4-adc-core
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
const: adc
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: interrupt line common for all ADCs
|
||||
|
||||
st,max-clk-rate-hz:
|
||||
minimum: 600000
|
||||
maximum: 36000000
|
||||
default: 36000000
|
||||
|
||||
booster-supply: false
|
||||
|
||||
vdd-supply: false
|
||||
|
||||
st,syscfg: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: st,stm32h7-adc-core
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: bus
|
||||
- const: adc
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: interrupt line common for all ADCs
|
||||
|
||||
st,max-clk-rate-hz:
|
||||
minimum: 120000
|
||||
maximum: 36000000
|
||||
default: 36000000
|
||||
|
||||
vdd-supply: false
|
||||
|
||||
st,syscfg: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: st,stm32mp1-adc-core
|
||||
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: bus
|
||||
- const: adc
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: interrupt line for ADC1
|
||||
- description: interrupt line for ADC2
|
||||
|
||||
st,max-clk-rate-hz:
|
||||
minimum: 120000
|
||||
maximum: 36000000
|
||||
default: 36000000
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- vdda-supply
|
||||
- vref-supply
|
||||
- interrupt-controller
|
||||
- '#interrupt-cells'
|
||||
- '#address-cells'
|
||||
- '#size-cells'
|
||||
|
||||
patternProperties:
|
||||
"^adc@[0-9]+$":
|
||||
type: object
|
||||
description:
|
||||
An ADC block node should contain at least one subnode, representing an
|
||||
ADC instance available on the machine.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- st,stm32f4-adc
|
||||
- st,stm32h7-adc
|
||||
- st,stm32mp1-adc
|
||||
|
||||
reg:
|
||||
description: |
|
||||
Offset of ADC instance in ADC block. Valid values are:
|
||||
- 0x0: ADC1
|
||||
- 0x100: ADC2
|
||||
- 0x200: ADC3 (stm32f4 only)
|
||||
maxItems: 1
|
||||
|
||||
'#io-channel-cells':
|
||||
const: 1
|
||||
|
||||
interrupts:
|
||||
description: |
|
||||
IRQ Line for the ADC instance. Valid values are:
|
||||
- 0 for adc@0
|
||||
- 1 for adc@100
|
||||
- 2 for adc@200 (stm32f4 only)
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
description:
|
||||
Input clock private to this ADC instance. It's required only on
|
||||
stm32f4, that has per instance clock input for registers access.
|
||||
maxItems: 1
|
||||
|
||||
dmas:
|
||||
description: RX DMA Channel
|
||||
maxItems: 1
|
||||
|
||||
dma-names:
|
||||
const: rx
|
||||
|
||||
assigned-resolution-bits:
|
||||
description: |
|
||||
Resolution (bits) to use for conversions:
|
||||
- can be 6, 8, 10 or 12 on stm32f4
|
||||
- can be 8, 10, 12, 14 or 16 on stm32h7 and stm32mp1
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
st,adc-channels:
|
||||
description: |
|
||||
List of single-ended channels muxed for this ADC. It can have up to:
|
||||
- 16 channels, numbered from 0 to 15 (for in0..in15) on stm32f4
|
||||
- 20 channels, numbered from 0 to 19 (for in0..in19) on stm32h7 and
|
||||
stm32mp1.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
|
||||
st,adc-diff-channels:
|
||||
description: |
|
||||
List of differential channels muxed for this ADC. Some channels can
|
||||
be configured as differential instead of single-ended on stm32h7 and
|
||||
on stm32mp1. Positive and negative inputs pairs are listed:
|
||||
<vinp vinn>, <vinp vinn>,... vinp and vinn are numbered from 0 to 19.
|
||||
|
||||
Note: At least one of "st,adc-channels" or "st,adc-diff-channels" is
|
||||
required. Both properties can be used together. Some channels can be
|
||||
used as single-ended and some other ones as differential (mixed). But
|
||||
channels can't be configured both as single-ended and differential.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32-matrix
|
||||
- items:
|
||||
items:
|
||||
- description: |
|
||||
"vinp" indicates positive input number
|
||||
minimum: 0
|
||||
maximum: 19
|
||||
- description: |
|
||||
"vinn" indicates negative input number
|
||||
minimum: 0
|
||||
maximum: 19
|
||||
|
||||
st,min-sample-time-nsecs:
|
||||
description:
|
||||
Minimum sampling time in nanoseconds. Depending on hardware (board)
|
||||
e.g. high/low analog input source impedance, fine tune of ADC
|
||||
sampling time may be recommended. This can be either one value or an
|
||||
array that matches "st,adc-channels" and/or "st,adc-diff-channels"
|
||||
list, to set sample time resp. for all channels, or independently for
|
||||
each channel.
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: st,stm32f4-adc
|
||||
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
enum:
|
||||
- 0x0
|
||||
- 0x100
|
||||
- 0x200
|
||||
|
||||
interrupts:
|
||||
minimum: 0
|
||||
maximum: 2
|
||||
|
||||
assigned-resolution-bits:
|
||||
enum: [6, 8, 10, 12]
|
||||
default: 12
|
||||
|
||||
st,adc-channels:
|
||||
minItems: 1
|
||||
maxItems: 16
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 15
|
||||
|
||||
st,adc-diff-channels: false
|
||||
|
||||
st,min-sample-time-nsecs:
|
||||
minItems: 1
|
||||
maxItems: 16
|
||||
items:
|
||||
minimum: 80
|
||||
|
||||
required:
|
||||
- clocks
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- st,stm32h7-adc
|
||||
- st,stm32mp1-adc
|
||||
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
enum:
|
||||
- 0x0
|
||||
- 0x100
|
||||
|
||||
interrupts:
|
||||
minimum: 0
|
||||
maximum: 1
|
||||
|
||||
assigned-resolution-bits:
|
||||
enum: [8, 10, 12, 14, 16]
|
||||
default: 16
|
||||
|
||||
st,adc-channels:
|
||||
minItems: 1
|
||||
maxItems: 20
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 19
|
||||
|
||||
st,min-sample-time-nsecs:
|
||||
minItems: 1
|
||||
maxItems: 20
|
||||
items:
|
||||
minimum: 40
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
anyOf:
|
||||
- required:
|
||||
- st,adc-channels
|
||||
- required:
|
||||
- st,adc-diff-channels
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- '#io-channel-cells'
|
||||
|
||||
examples:
|
||||
- |
|
||||
// Example 1: with stm32f429, ADC1, single-ended channel 8
|
||||
adc123: adc@40012000 {
|
||||
compatible = "st,stm32f4-adc-core";
|
||||
reg = <0x40012000 0x400>;
|
||||
interrupts = <18>;
|
||||
clocks = <&rcc 0 168>;
|
||||
clock-names = "adc";
|
||||
st,max-clk-rate-hz = <36000000>;
|
||||
vdda-supply = <&vdda>;
|
||||
vref-supply = <&vref>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
adc@0 {
|
||||
compatible = "st,stm32f4-adc";
|
||||
#io-channel-cells = <1>;
|
||||
reg = <0x0>;
|
||||
clocks = <&rcc 0 168>;
|
||||
interrupt-parent = <&adc123>;
|
||||
interrupts = <0>;
|
||||
st,adc-channels = <8>;
|
||||
dmas = <&dma2 0 0 0x400 0x0>;
|
||||
dma-names = "rx";
|
||||
assigned-resolution-bits = <8>;
|
||||
};
|
||||
// ...
|
||||
// other adc child nodes follow...
|
||||
};
|
||||
|
||||
- |
|
||||
// Example 2: with stm32mp157c to setup ADC1 with:
|
||||
// - channels 0 & 1 as single-ended
|
||||
// - channels 2 & 3 as differential (with resp. 6 & 7 negative inputs)
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/clock/stm32mp1-clks.h>
|
||||
adc12: adc@48003000 {
|
||||
compatible = "st,stm32mp1-adc-core";
|
||||
reg = <0x48003000 0x400>;
|
||||
interrupts = <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&rcc ADC12>, <&rcc ADC12_K>;
|
||||
clock-names = "bus", "adc";
|
||||
booster-supply = <&booster>;
|
||||
vdd-supply = <&vdd>;
|
||||
vdda-supply = <&vdda>;
|
||||
vref-supply = <&vref>;
|
||||
st,syscfg = <&syscfg>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
adc@0 {
|
||||
compatible = "st,stm32mp1-adc";
|
||||
#io-channel-cells = <1>;
|
||||
reg = <0x0>;
|
||||
interrupt-parent = <&adc12>;
|
||||
interrupts = <0>;
|
||||
st,adc-channels = <0 1>;
|
||||
st,adc-diff-channels = <2 6>, <3 7>;
|
||||
st,min-sample-time-nsecs = <5000>;
|
||||
dmas = <&dmamux1 9 0x400 0x05>;
|
||||
dma-names = "rx";
|
||||
};
|
||||
// ...
|
||||
// other adc child node follow...
|
||||
};
|
||||
|
||||
...
|
@ -0,0 +1,49 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/amplifiers/adi,hmc425a.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: HMC425A 6-bit Digital Step Attenuator
|
||||
|
||||
maintainers:
|
||||
- Michael Hennerich <michael.hennerich@analog.com>
|
||||
- Beniamin Bia <beniamin.bia@analog.com>
|
||||
|
||||
description: |
|
||||
Digital Step Attenuator IIO device with gpio interface.
|
||||
HMC425A 0.5 dB LSB GaAs MMIC 6-BIT DIGITAL POSITIVE CONTROL ATTENUATOR, 2.2 - 8.0 GHz
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/hmc425A.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,hmc425a
|
||||
|
||||
vcc-supply: true
|
||||
|
||||
ctrl-gpios:
|
||||
description:
|
||||
Must contain an array of 6 GPIO specifiers, referring to the GPIO pins
|
||||
connected to the control pins V1-V6.
|
||||
minItems: 6
|
||||
maxItems: 6
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- ctrl-gpios
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
gpio_hmc425a: hmc425a {
|
||||
compatible = "adi,hmc425a";
|
||||
ctrl-gpios = <&gpio 40 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio 39 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio 38 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio 37 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio 36 GPIO_ACTIVE_HIGH>,
|
||||
<&gpio 35 GPIO_ACTIVE_HIGH>;
|
||||
vcc-supply = <&foo>;
|
||||
};
|
||||
...
|
@ -1,21 +0,0 @@
|
||||
* Atlas Scientific EC-SM OEM sensor
|
||||
|
||||
http://www.atlas-scientific.com/_files/_datasheets/_oem/EC_oem_datasheet.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be "atlas,ec-sm"
|
||||
- reg: the I2C address of the sensor
|
||||
- interrupts: the sole interrupt generated by the device
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt client
|
||||
node bindings.
|
||||
|
||||
Example:
|
||||
|
||||
atlas@64 {
|
||||
compatible = "atlas,ec-sm";
|
||||
reg = <0x64>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <16 2>;
|
||||
};
|
@ -1,21 +0,0 @@
|
||||
* Atlas Scientific ORP-SM OEM sensor
|
||||
|
||||
https://www.atlas-scientific.com/_files/_datasheets/_oem/ORP_oem_datasheet.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be "atlas,orp-sm"
|
||||
- reg: the I2C address of the sensor
|
||||
- interrupts: the sole interrupt generated by the device
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt client
|
||||
node bindings.
|
||||
|
||||
Example:
|
||||
|
||||
atlas@66 {
|
||||
compatible = "atlas,orp-sm";
|
||||
reg = <0x66>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <16 2>;
|
||||
};
|
@ -1,21 +0,0 @@
|
||||
* Atlas Scientific pH-SM OEM sensor
|
||||
|
||||
http://www.atlas-scientific.com/_files/_datasheets/_oem/pH_oem_datasheet.pdf
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be "atlas,ph-sm"
|
||||
- reg: the I2C address of the sensor
|
||||
- interrupts: the sole interrupt generated by the device
|
||||
|
||||
Refer to interrupt-controller/interrupts.txt for generic interrupt client
|
||||
node bindings.
|
||||
|
||||
Example:
|
||||
|
||||
atlas@65 {
|
||||
compatible = "atlas,ph-sm";
|
||||
reg = <0x65>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <16 2>;
|
||||
};
|
@ -0,0 +1,53 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/chemical/atlas,sensor.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Atlas Scientific OEM sensors
|
||||
|
||||
maintainers:
|
||||
- Matt Ranostay <matt.ranostay@konsulko.com>
|
||||
|
||||
description: |
|
||||
Atlas Scientific OEM sensors connected via I2C
|
||||
|
||||
Datasheets:
|
||||
http://www.atlas-scientific.com/_files/_datasheets/_oem/DO_oem_datasheet.pdf
|
||||
http://www.atlas-scientific.com/_files/_datasheets/_oem/EC_oem_datasheet.pdf
|
||||
http://www.atlas-scientific.com/_files/_datasheets/_oem/ORP_oem_datasheet.pdf
|
||||
http://www.atlas-scientific.com/_files/_datasheets/_oem/pH_oem_datasheet.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- atlas,do-sm
|
||||
- atlas,ec-sm
|
||||
- atlas,orp-sm
|
||||
- atlas,ph-sm
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
atlas@66 {
|
||||
compatible = "atlas,orp-sm";
|
||||
reg = <0x66>;
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <16 2>;
|
||||
};
|
||||
};
|
185
Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml
Normal file
185
Documentation/devicetree/bindings/iio/dac/adi,ad5770r.yaml
Normal file
@ -0,0 +1,185 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
# Copyright 2020 Analog Devices Inc.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/bindings/iio/dac/adi,ad5770r.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices AD5770R DAC device driver
|
||||
|
||||
maintainers:
|
||||
- Mircea Caprioru <mircea.caprioru@analog.com>
|
||||
|
||||
description: |
|
||||
Bindings for the Analog Devices AD5770R current DAC device. Datasheet can be
|
||||
found here:
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/AD5770R.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ad5770r
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
avdd-supply:
|
||||
description:
|
||||
AVdd voltage supply. Represents two different supplies in the datasheet
|
||||
that are in fact the same.
|
||||
|
||||
iovdd-supply:
|
||||
description:
|
||||
Voltage supply for the chip interface.
|
||||
|
||||
vref-supply:
|
||||
description: Specify the voltage of the external reference used.
|
||||
Available reference options are 1.25 V or 2.5 V. If no
|
||||
external reference declared then the device will use the
|
||||
internal reference of 1.25 V.
|
||||
|
||||
adi,external-resistor:
|
||||
description: Specify if an external 2.5k ohm resistor is used. If not
|
||||
specified the device will use an internal 2.5k ohm resistor.
|
||||
The precision resistor is used for reference current generation.
|
||||
type: boolean
|
||||
|
||||
reset-gpios:
|
||||
description: GPIO spec for the RESET pin. If specified, it will be
|
||||
asserted during driver probe.
|
||||
maxItems: 1
|
||||
|
||||
channel0:
|
||||
description: Represents an external channel which are
|
||||
connected to the DAC. Channel 0 can act both as a current
|
||||
source and sink.
|
||||
type: object
|
||||
|
||||
properties:
|
||||
num:
|
||||
description: This represents the channel number.
|
||||
items:
|
||||
const: 0
|
||||
|
||||
adi,range-microamp:
|
||||
description: Output range of the channel.
|
||||
oneOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/int32-array
|
||||
- items:
|
||||
- enum: [0 300000]
|
||||
- enum: [-60000 0]
|
||||
- enum: [-60000 300000]
|
||||
|
||||
channel1:
|
||||
description: Represents an external channel which are
|
||||
connected to the DAC.
|
||||
type: object
|
||||
|
||||
properties:
|
||||
num:
|
||||
description: This represents the channel number.
|
||||
items:
|
||||
const: 1
|
||||
|
||||
adi,range-microamp:
|
||||
description: Output range of the channel.
|
||||
oneOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
- items:
|
||||
- enum: [0 140000]
|
||||
- enum: [0 250000]
|
||||
|
||||
channel2:
|
||||
description: Represents an external channel which are
|
||||
connected to the DAC.
|
||||
type: object
|
||||
|
||||
properties:
|
||||
num:
|
||||
description: This represents the channel number.
|
||||
items:
|
||||
const: 2
|
||||
|
||||
adi,range-microamp:
|
||||
description: Output range of the channel.
|
||||
oneOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
- items:
|
||||
- enum: [0 140000]
|
||||
- enum: [0 250000]
|
||||
|
||||
patternProperties:
|
||||
"^channel@([3-5])$":
|
||||
type: object
|
||||
description: Represents the external channels which are connected to the DAC.
|
||||
properties:
|
||||
num:
|
||||
description: This represents the channel number.
|
||||
items:
|
||||
minimum: 3
|
||||
maximum: 5
|
||||
|
||||
adi,range-microamp:
|
||||
description: Output range of the channel.
|
||||
oneOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
- items:
|
||||
- enum: [0 45000]
|
||||
- enum: [0 100000]
|
||||
|
||||
required:
|
||||
- reg
|
||||
- diff-channels
|
||||
- channel0
|
||||
- channel1
|
||||
- channel2
|
||||
- channel3
|
||||
- channel4
|
||||
- channel5
|
||||
|
||||
examples:
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ad5770r@0 {
|
||||
compatible = "ad5770r";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <1000000>;
|
||||
vref-supply = <&vref>;
|
||||
adi,external-resistor;
|
||||
reset-gpios = <&gpio 22 0>;
|
||||
|
||||
channel@0 {
|
||||
num = <0>;
|
||||
adi,range-microamp = <(-60000) 300000>;
|
||||
};
|
||||
|
||||
channel@1 {
|
||||
num = <1>;
|
||||
adi,range-microamp = <0 140000>;
|
||||
};
|
||||
|
||||
channel@2 {
|
||||
num = <2>;
|
||||
adi,range-microamp = <0 55000>;
|
||||
};
|
||||
|
||||
channel@3 {
|
||||
num = <3>;
|
||||
adi,range-microamp = <0 45000>;
|
||||
};
|
||||
|
||||
channel@4 {
|
||||
num = <4>;
|
||||
adi,range-microamp = <0 45000>;
|
||||
};
|
||||
|
||||
channel@5 {
|
||||
num = <5>;
|
||||
adi,range-microamp = <0 45000>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -1,4 +1,4 @@
|
||||
Linear Technology LTC2632 DAC device driver
|
||||
Linear Technology LTC2632/2636 DAC
|
||||
|
||||
Required properties:
|
||||
- compatible: Has to contain one of the following:
|
||||
@ -8,6 +8,12 @@ Required properties:
|
||||
lltc,ltc2632-h12
|
||||
lltc,ltc2632-h10
|
||||
lltc,ltc2632-h8
|
||||
lltc,ltc2636-l12
|
||||
lltc,ltc2636-l10
|
||||
lltc,ltc2636-l8
|
||||
lltc,ltc2636-h12
|
||||
lltc,ltc2636-h10
|
||||
lltc,ltc2636-h8
|
||||
|
||||
Property rules described in Documentation/devicetree/bindings/spi/spi-bus.txt
|
||||
apply. In particular, "reg" and "spi-max-frequency" properties must be given.
|
||||
|
@ -4,6 +4,7 @@ http://www.invensense.com/mems/gyro/mpu6050.html
|
||||
|
||||
Required properties:
|
||||
- compatible : should be one of
|
||||
"invensense,mpu6000"
|
||||
"invensense,mpu6050"
|
||||
"invensense,mpu6500"
|
||||
"invensense,mpu6515"
|
||||
@ -11,7 +12,11 @@ Required properties:
|
||||
"invensense,mpu9250"
|
||||
"invensense,mpu9255"
|
||||
"invensense,icm20608"
|
||||
"invensense,icm20609"
|
||||
"invensense,icm20689"
|
||||
"invensense,icm20602"
|
||||
"invensense,icm20690"
|
||||
"invensense,iam20680"
|
||||
- reg : the I2C address of the sensor
|
||||
- interrupts: interrupt mapping for IRQ. It should be configured with flags
|
||||
IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW or
|
||||
|
@ -0,0 +1,43 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/dynaimage,al3010.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Dyna-Image AL3010 sensor
|
||||
|
||||
maintainers:
|
||||
- David Heidelberg <david@ixit.cz>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: dynaimage,al3010
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply:
|
||||
description: Regulator that provides power to the sensor
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
light-sensor@1c {
|
||||
compatible = "dynaimage,al3010";
|
||||
reg = <0x1c>;
|
||||
vdd-supply = <&vdd_reg>;
|
||||
interrupts = <0 99 4>;
|
||||
};
|
||||
};
|
@ -0,0 +1,43 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/dynaimage,al3320a.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Dyna-Image AL3320A sensor
|
||||
|
||||
maintainers:
|
||||
- David Heidelberg <david@ixit.cz>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: dynaimage,al3320a
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply:
|
||||
description: Regulator that provides power to the sensor
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
light-sensor@1c {
|
||||
compatible = "dynaimage,al3320a";
|
||||
reg = <0x1c>;
|
||||
vdd-supply = <&vdd_reg>;
|
||||
interrupts = <0 99 4>;
|
||||
};
|
||||
};
|
@ -0,0 +1,85 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/iio/light/sharp,gp2ap002.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Sharp GP2AP002A00F and GP2AP002S00F proximity and ambient light sensors
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
|
||||
description: |
|
||||
Proximity and ambient light sensor with IR LED for the proximity
|
||||
sensing and an analog output for light intensity. The ambient light
|
||||
sensor output is not available on the GP2AP002S00F variant.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- sharp,gp2ap002a00f
|
||||
- sharp,gp2ap002s00f
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description: an interrupt for proximity, usually a GPIO line
|
||||
|
||||
vdd-supply:
|
||||
description: VDD power supply a phandle to a regulator
|
||||
|
||||
vio-supply:
|
||||
description: VIO power supply a phandle to a regulator
|
||||
|
||||
io-channels:
|
||||
maxItems: 1
|
||||
description: ALSOUT ADC channel to read the ambient light
|
||||
|
||||
io-channel-names:
|
||||
const: alsout
|
||||
|
||||
sharp,proximity-far-hysteresis:
|
||||
$ref: /schemas/types.yaml#/definitions/uint8
|
||||
description: |
|
||||
Hysteresis setting for "far" object detection, this setting is
|
||||
device-unique and adjust the optical setting for proximity detection
|
||||
of a "far away" object in front of the sensor.
|
||||
|
||||
sharp,proximity-close-hysteresis:
|
||||
$ref: /schemas/types.yaml#/definitions/uint8
|
||||
description: |
|
||||
Hysteresis setting for "close" object detection, this setting is
|
||||
device-unique and adjust the optical setting for proximity detection
|
||||
of a "close" object in front of the sensor.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- sharp,proximity-far-hysteresis
|
||||
- sharp,proximity-close-hysteresis
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
light-sensor@44 {
|
||||
compatible = "sharp,gp2ap002a00f";
|
||||
reg = <0x44>;
|
||||
interrupts = <18 IRQ_TYPE_EDGE_FALLING>;
|
||||
vdd-supply = <&vdd_regulator>;
|
||||
vio-supply = <&vio_regulator>;
|
||||
io-channels = <&adc_channel>;
|
||||
io-channel-names = "alsout";
|
||||
sharp,proximity-far-hysteresis = /bits/ 8 <0x2f>;
|
||||
sharp,proximity-close-hysteresis = /bits/ 8 <0x0f>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -51,6 +51,24 @@ properties:
|
||||
the time between two interrupts is measured in the driver.
|
||||
maxItems: 1
|
||||
|
||||
power-gpios:
|
||||
description:
|
||||
Definition of the GPIO for power management of connected peripheral
|
||||
(output).
|
||||
This GPIO can be used by the external hardware for power management.
|
||||
When the device gets suspended it's switched off and when it resumes
|
||||
it's switched on again. After some period of inactivity the driver
|
||||
get suspended automatically (autosuspend feature).
|
||||
maxItems: 1
|
||||
|
||||
startup-time-ms:
|
||||
description:
|
||||
This is the startup time the device needs after a resume to be up and
|
||||
running.
|
||||
minimum: 0
|
||||
maximum: 1000
|
||||
default: 100
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- trig-gpios
|
||||
|
@ -267,6 +267,8 @@ patternProperties:
|
||||
description: Dragino Technology Co., Limited
|
||||
"^dserve,.*":
|
||||
description: dServe Technology B.V.
|
||||
"^dynaimage,.*":
|
||||
description: Dyna-Image
|
||||
"^ea,.*":
|
||||
description: Embedded Artists AB
|
||||
"^ebs-systart,.*":
|
||||
|
25
MAINTAINERS
25
MAINTAINERS
@ -931,6 +931,14 @@ S: Supported
|
||||
F: drivers/iio/adc/ad7124.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7124.yaml
|
||||
|
||||
ANALOG DEVICES INC AD7192 DRIVER
|
||||
M: Alexandru Tachici <alexandru.tachici@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
S: Supported
|
||||
F: drivers/iio/adc/ad7192.c
|
||||
F: Documentation/devicetree/bindings/iio/adc/adi,ad7192.yaml
|
||||
|
||||
ANALOG DEVICES INC AD7292 DRIVER
|
||||
M: Marcelo Schmitt <marcelo.schmitt1@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
@ -1081,6 +1089,15 @@ F: drivers/iio/adc/ltc249*
|
||||
X: drivers/iio/*/adjd*
|
||||
F: drivers/staging/iio/*/ad*
|
||||
|
||||
ANALOG DEVICES INC HMC425A DRIVER
|
||||
M: Beniamin Bia <beniamin.bia@analog.com>
|
||||
M: Michael Hennerich <michael.hennerich@analog.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
S: Supported
|
||||
W: http://ez.analog.com/community/linux-device-drivers
|
||||
F: Documentation/devicetree/bindings/iio/amplifiers/adi,hmc425a.yaml
|
||||
F: drivers/iio/amplifiers/hmc425a.c
|
||||
|
||||
ANALOGBITS PLL LIBRARIES
|
||||
M: Paul Walmsley <paul.walmsley@sifive.com>
|
||||
S: Supported
|
||||
@ -15055,6 +15072,14 @@ W: http://www.ibm.com/developerworks/linux/linux390/
|
||||
S: Supported
|
||||
F: net/smc/
|
||||
|
||||
SHARP GP2AP002A00F/GP2AP002S00F SENSOR DRIVER
|
||||
M: Linus Walleij <linus.walleij@linaro.org>
|
||||
L: linux-iio@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git
|
||||
S: Maintained
|
||||
F: drivers/iio/light/gp2ap002.c
|
||||
F: Documentation/devicetree/bindings/iio/light/sharp,gp2ap002.yaml
|
||||
|
||||
SHARP RJ54N1CB0C SENSOR DRIVER
|
||||
M: Jacopo Mondi <jacopo@jmondi.org>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -31,6 +31,7 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
|
||||
/**
|
||||
* struct quad8_iio - IIO device private data structure
|
||||
* @counter: instance of the counter_device
|
||||
* @fck_prescaler: array of filter clock prescaler configurations
|
||||
* @preset: array of preset values
|
||||
* @count_mode: array of count mode configurations
|
||||
* @quadrature_mode: array of quadrature mode configurations
|
||||
@ -39,10 +40,12 @@ MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
|
||||
* @preset_enable: array of set_to_preset_on_index attribute configurations
|
||||
* @synchronous_mode: array of index function synchronous mode configurations
|
||||
* @index_polarity: array of index function polarity configurations
|
||||
* @cable_fault_enable: differential encoder cable status enable configurations
|
||||
* @base: base port address of the IIO device
|
||||
*/
|
||||
struct quad8_iio {
|
||||
struct counter_device counter;
|
||||
unsigned int fck_prescaler[QUAD8_NUM_COUNTERS];
|
||||
unsigned int preset[QUAD8_NUM_COUNTERS];
|
||||
unsigned int count_mode[QUAD8_NUM_COUNTERS];
|
||||
unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
|
||||
@ -51,11 +54,13 @@ struct quad8_iio {
|
||||
unsigned int preset_enable[QUAD8_NUM_COUNTERS];
|
||||
unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
|
||||
unsigned int index_polarity[QUAD8_NUM_COUNTERS];
|
||||
unsigned int cable_fault_enable;
|
||||
unsigned int base;
|
||||
};
|
||||
|
||||
#define QUAD8_REG_CHAN_OP 0x11
|
||||
#define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
|
||||
#define QUAD8_DIFF_ENCODER_CABLE_STATUS 0x17
|
||||
/* Borrow Toggle flip-flop */
|
||||
#define QUAD8_FLAG_BT BIT(0)
|
||||
/* Carry Toggle flip-flop */
|
||||
@ -84,6 +89,8 @@ struct quad8_iio {
|
||||
#define QUAD8_RLD_PRESET_CNTR 0x08
|
||||
/* Transfer Counter to Output Latch */
|
||||
#define QUAD8_RLD_CNTR_OUT 0x10
|
||||
/* Transfer Preset Register LSB to FCK Prescaler */
|
||||
#define QUAD8_RLD_PRESET_PSC 0x18
|
||||
#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00
|
||||
#define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
|
||||
#define QUAD8_CMR_QUADRATURE_X1 0x08
|
||||
@ -1140,6 +1147,119 @@ static ssize_t quad8_count_preset_enable_write(struct counter_device *counter,
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t quad8_signal_cable_fault_read(struct counter_device *counter,
|
||||
struct counter_signal *signal,
|
||||
void *private, char *buf)
|
||||
{
|
||||
const struct quad8_iio *const priv = counter->priv;
|
||||
const size_t channel_id = signal->id / 2;
|
||||
const bool disabled = !(priv->cable_fault_enable & BIT(channel_id));
|
||||
unsigned int status;
|
||||
unsigned int fault;
|
||||
|
||||
if (disabled)
|
||||
return -EINVAL;
|
||||
|
||||
/* Logic 0 = cable fault */
|
||||
status = inb(priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
|
||||
|
||||
/* Mask respective channel and invert logic */
|
||||
fault = !(status & BIT(channel_id));
|
||||
|
||||
return sprintf(buf, "%u\n", fault);
|
||||
}
|
||||
|
||||
static ssize_t quad8_signal_cable_fault_enable_read(
|
||||
struct counter_device *counter, struct counter_signal *signal,
|
||||
void *private, char *buf)
|
||||
{
|
||||
const struct quad8_iio *const priv = counter->priv;
|
||||
const size_t channel_id = signal->id / 2;
|
||||
const unsigned int enb = !!(priv->cable_fault_enable & BIT(channel_id));
|
||||
|
||||
return sprintf(buf, "%u\n", enb);
|
||||
}
|
||||
|
||||
static ssize_t quad8_signal_cable_fault_enable_write(
|
||||
struct counter_device *counter, struct counter_signal *signal,
|
||||
void *private, const char *buf, size_t len)
|
||||
{
|
||||
struct quad8_iio *const priv = counter->priv;
|
||||
const size_t channel_id = signal->id / 2;
|
||||
bool enable;
|
||||
int ret;
|
||||
unsigned int cable_fault_enable;
|
||||
|
||||
ret = kstrtobool(buf, &enable);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (enable)
|
||||
priv->cable_fault_enable |= BIT(channel_id);
|
||||
else
|
||||
priv->cable_fault_enable &= ~BIT(channel_id);
|
||||
|
||||
/* Enable is active low in Differential Encoder Cable Status register */
|
||||
cable_fault_enable = ~priv->cable_fault_enable;
|
||||
|
||||
outb(cable_fault_enable, priv->base + QUAD8_DIFF_ENCODER_CABLE_STATUS);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t quad8_signal_fck_prescaler_read(struct counter_device *counter,
|
||||
struct counter_signal *signal, void *private, char *buf)
|
||||
{
|
||||
const struct quad8_iio *const priv = counter->priv;
|
||||
const size_t channel_id = signal->id / 2;
|
||||
|
||||
return sprintf(buf, "%u\n", priv->fck_prescaler[channel_id]);
|
||||
}
|
||||
|
||||
static ssize_t quad8_signal_fck_prescaler_write(struct counter_device *counter,
|
||||
struct counter_signal *signal, void *private, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct quad8_iio *const priv = counter->priv;
|
||||
const size_t channel_id = signal->id / 2;
|
||||
const int base_offset = priv->base + 2 * channel_id;
|
||||
u8 prescaler;
|
||||
int ret;
|
||||
|
||||
ret = kstrtou8(buf, 0, &prescaler);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->fck_prescaler[channel_id] = prescaler;
|
||||
|
||||
/* Reset Byte Pointer */
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
|
||||
|
||||
/* Set filter clock factor */
|
||||
outb(prescaler, base_offset);
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
|
||||
base_offset + 1);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct counter_signal_ext quad8_signal_ext[] = {
|
||||
{
|
||||
.name = "cable_fault",
|
||||
.read = quad8_signal_cable_fault_read
|
||||
},
|
||||
{
|
||||
.name = "cable_fault_enable",
|
||||
.read = quad8_signal_cable_fault_enable_read,
|
||||
.write = quad8_signal_cable_fault_enable_write
|
||||
},
|
||||
{
|
||||
.name = "filter_clock_prescaler",
|
||||
.read = quad8_signal_fck_prescaler_read,
|
||||
.write = quad8_signal_fck_prescaler_write
|
||||
}
|
||||
};
|
||||
|
||||
static const struct counter_signal_ext quad8_index_ext[] = {
|
||||
COUNTER_SIGNAL_ENUM("index_polarity", &quad8_index_pol_enum),
|
||||
COUNTER_SIGNAL_ENUM_AVAILABLE("index_polarity", &quad8_index_pol_enum),
|
||||
@ -1147,9 +1267,11 @@ static const struct counter_signal_ext quad8_index_ext[] = {
|
||||
COUNTER_SIGNAL_ENUM_AVAILABLE("synchronous_mode", &quad8_syn_mode_enum)
|
||||
};
|
||||
|
||||
#define QUAD8_QUAD_SIGNAL(_id, _name) { \
|
||||
.id = (_id), \
|
||||
.name = (_name) \
|
||||
#define QUAD8_QUAD_SIGNAL(_id, _name) { \
|
||||
.id = (_id), \
|
||||
.name = (_name), \
|
||||
.ext = quad8_signal_ext, \
|
||||
.num_ext = ARRAY_SIZE(quad8_signal_ext) \
|
||||
}
|
||||
|
||||
#define QUAD8_INDEX_SIGNAL(_id, _name) { \
|
||||
@ -1314,6 +1436,12 @@ static int quad8_probe(struct device *dev, unsigned int id)
|
||||
base_offset = base[id] + 2 * i;
|
||||
/* Reset Byte Pointer */
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
|
||||
/* Reset filter clock factor */
|
||||
outb(0, base_offset);
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_PRESET_PSC,
|
||||
base_offset + 1);
|
||||
/* Reset Byte Pointer */
|
||||
outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
|
||||
/* Reset Preset Register */
|
||||
for (j = 0; j < 3; j++)
|
||||
outb(0x00, base_offset);
|
||||
@ -1328,6 +1456,8 @@ static int quad8_probe(struct device *dev, unsigned int id)
|
||||
/* Disable index function; negative index polarity */
|
||||
outb(QUAD8_CTR_IDR, base_offset + 1);
|
||||
}
|
||||
/* Disable Differential Encoder Cable Status for all channels */
|
||||
outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS);
|
||||
/* Enable all counters */
|
||||
outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
|
||||
|
||||
|
@ -8,10 +8,10 @@
|
||||
*
|
||||
*/
|
||||
#include <linux/counter.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/types.h>
|
||||
#include <linux/mfd/stm32-timers.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define TIM_CCMR_CCXS (BIT(8) | BIT(0))
|
||||
@ -20,11 +20,20 @@
|
||||
#define TIM_CCER_MASK (TIM_CCER_CC1P | TIM_CCER_CC1NP | \
|
||||
TIM_CCER_CC2P | TIM_CCER_CC2NP)
|
||||
|
||||
struct stm32_timer_regs {
|
||||
u32 cr1;
|
||||
u32 cnt;
|
||||
u32 smcr;
|
||||
u32 arr;
|
||||
};
|
||||
|
||||
struct stm32_timer_cnt {
|
||||
struct counter_device counter;
|
||||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
u32 ceiling;
|
||||
bool enabled;
|
||||
struct stm32_timer_regs bak;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -224,6 +233,9 @@ static ssize_t stm32_count_enable_write(struct counter_device *counter,
|
||||
clk_disable(priv->clk);
|
||||
}
|
||||
|
||||
/* Keep enabled state to properly handle low power states */
|
||||
priv->enabled = enable;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -358,10 +370,59 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev)
|
||||
priv->counter.num_signals = ARRAY_SIZE(stm32_signals);
|
||||
priv->counter.priv = priv;
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
/* Register Counter device */
|
||||
return devm_counter_register(dev, &priv->counter);
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_timer_cnt_suspend(struct device *dev)
|
||||
{
|
||||
struct stm32_timer_cnt *priv = dev_get_drvdata(dev);
|
||||
|
||||
/* Only take care of enabled counter: don't disturb other MFD child */
|
||||
if (priv->enabled) {
|
||||
/* Backup registers that may get lost in low power mode */
|
||||
regmap_read(priv->regmap, TIM_SMCR, &priv->bak.smcr);
|
||||
regmap_read(priv->regmap, TIM_ARR, &priv->bak.arr);
|
||||
regmap_read(priv->regmap, TIM_CNT, &priv->bak.cnt);
|
||||
regmap_read(priv->regmap, TIM_CR1, &priv->bak.cr1);
|
||||
|
||||
/* Disable the counter */
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
|
||||
clk_disable(priv->clk);
|
||||
}
|
||||
|
||||
return pinctrl_pm_select_sleep_state(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_timer_cnt_resume(struct device *dev)
|
||||
{
|
||||
struct stm32_timer_cnt *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = pinctrl_pm_select_default_state(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv->enabled) {
|
||||
clk_enable(priv->clk);
|
||||
|
||||
/* Restore registers that may have been lost */
|
||||
regmap_write(priv->regmap, TIM_SMCR, priv->bak.smcr);
|
||||
regmap_write(priv->regmap, TIM_ARR, priv->bak.arr);
|
||||
regmap_write(priv->regmap, TIM_CNT, priv->bak.cnt);
|
||||
|
||||
/* Also re-enables the counter */
|
||||
regmap_write(priv->regmap, TIM_CR1, priv->bak.cr1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(stm32_timer_cnt_pm_ops, stm32_timer_cnt_suspend,
|
||||
stm32_timer_cnt_resume);
|
||||
|
||||
static const struct of_device_id stm32_timer_cnt_of_match[] = {
|
||||
{ .compatible = "st,stm32-timer-counter", },
|
||||
{},
|
||||
@ -373,6 +434,7 @@ static struct platform_driver stm32_timer_cnt_driver = {
|
||||
.driver = {
|
||||
.name = "stm32-timer-counter",
|
||||
.of_match_table = stm32_timer_cnt_of_match,
|
||||
.pm = &stm32_timer_cnt_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(stm32_timer_cnt_driver);
|
||||
|
19
drivers/iio/TODO
Normal file
19
drivers/iio/TODO
Normal file
@ -0,0 +1,19 @@
|
||||
2020-02-29
|
||||
|
||||
Documentation
|
||||
- Binding docs for devices that are obviously used via device
|
||||
tree
|
||||
- Yaml conversions for abandoned drivers
|
||||
- ABI Documentation
|
||||
- Audit driviers/iio/staging/Documentation
|
||||
|
||||
- Replace iio_dev->mlock by either a local lock or use
|
||||
iio_claim_direct.(Requires analysis of the purpose of the lock.)
|
||||
|
||||
- Converting drivers from device tree centric to more generic
|
||||
property handlers.
|
||||
|
||||
- Refactor old platform_data constructs from drivers and convert it
|
||||
to state struct and using property handlers and readers.
|
||||
|
||||
Mailing list: linux-iio@vger.kernel.org
|
@ -246,6 +246,7 @@ static const struct adis_data adis16201_data = {
|
||||
.diag_stat_reg = ADIS16201_DIAG_STAT_REG,
|
||||
|
||||
.self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN,
|
||||
.self_test_reg = ADIS16201_MSC_CTRL_REG,
|
||||
.self_test_no_autoclear = true,
|
||||
.timeouts = &adis16201_timeouts,
|
||||
|
||||
|
@ -256,6 +256,7 @@ static const struct adis_data adis16209_data = {
|
||||
.diag_stat_reg = ADIS16209_STAT_REG,
|
||||
|
||||
.self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN,
|
||||
.self_test_reg = ADIS16209_MSC_CTRL_REG,
|
||||
.self_test_no_autoclear = true,
|
||||
.timeouts = &adis16209_timeouts,
|
||||
|
||||
|
@ -147,12 +147,9 @@ static int st_accel_i2c_probe(struct i2c_client *client)
|
||||
const struct st_sensor_settings *settings;
|
||||
struct st_sensor_data *adata;
|
||||
struct iio_dev *indio_dev;
|
||||
const char *match;
|
||||
int ret;
|
||||
|
||||
match = device_get_match_data(&client->dev);
|
||||
if (match)
|
||||
strlcpy(client->name, match, sizeof(client->name));
|
||||
st_sensors_dev_name_probe(&client->dev, client->name, sizeof(client->name));
|
||||
|
||||
settings = st_accel_get_settings(client->name);
|
||||
if (!settings) {
|
||||
|
@ -39,6 +39,18 @@ config AD7124
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ad7124.
|
||||
|
||||
config AD7192
|
||||
tristate "Analog Devices AD7190 AD7192 AD7193 AD7195 ADC driver"
|
||||
depends on SPI
|
||||
select AD_SIGMA_DELTA
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7190,
|
||||
AD7192, AD7193 or AD7195 SPI analog to digital converters (ADC).
|
||||
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 ad7192.
|
||||
|
||||
config AD7266
|
||||
tristate "Analog Devices AD7265/AD7266 ADC driver"
|
||||
depends on SPI_MASTER
|
||||
|
@ -8,6 +8,7 @@ obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o
|
||||
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
|
||||
obj-$(CONFIG_AD7091R5) += ad7091r5.o ad7091r-base.o
|
||||
obj-$(CONFIG_AD7124) += ad7124.o
|
||||
obj-$(CONFIG_AD7192) += ad7192.o
|
||||
obj-$(CONFIG_AD7266) += ad7266.o
|
||||
obj-$(CONFIG_AD7291) += ad7291.o
|
||||
obj-$(CONFIG_AD7292) += ad7292.o
|
||||
|
@ -70,6 +70,11 @@
|
||||
/* AD7124_FILTER_X */
|
||||
#define AD7124_FILTER_FS_MSK GENMASK(10, 0)
|
||||
#define AD7124_FILTER_FS(x) FIELD_PREP(AD7124_FILTER_FS_MSK, x)
|
||||
#define AD7124_FILTER_TYPE_MSK GENMASK(23, 21)
|
||||
#define AD7124_FILTER_TYPE_SEL(x) FIELD_PREP(AD7124_FILTER_TYPE_MSK, x)
|
||||
|
||||
#define AD7124_SINC3_FILTER 2
|
||||
#define AD7124_SINC4_FILTER 0
|
||||
|
||||
enum ad7124_ids {
|
||||
ID_AD7124_4,
|
||||
@ -93,6 +98,14 @@ static const unsigned int ad7124_gain[8] = {
|
||||
1, 2, 4, 8, 16, 32, 64, 128
|
||||
};
|
||||
|
||||
static const unsigned int ad7124_reg_size[] = {
|
||||
1, 2, 3, 3, 2, 1, 3, 3, 1, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3
|
||||
};
|
||||
|
||||
static const int ad7124_master_clk_freq_hz[3] = {
|
||||
[AD7124_LOW_POWER] = 76800,
|
||||
[AD7124_MID_POWER] = 153600,
|
||||
@ -119,6 +132,7 @@ struct ad7124_channel_config {
|
||||
unsigned int vref_mv;
|
||||
unsigned int pga_bits;
|
||||
unsigned int odr;
|
||||
unsigned int filter_type;
|
||||
};
|
||||
|
||||
struct ad7124_state {
|
||||
@ -138,7 +152,8 @@ static const struct iio_chan_spec ad7124_channel_template = {
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 24,
|
||||
@ -281,6 +296,58 @@ static int ad7124_set_channel_gain(struct ad7124_state *st,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7124_get_3db_filter_freq(struct ad7124_state *st,
|
||||
unsigned int channel)
|
||||
{
|
||||
unsigned int fadc;
|
||||
|
||||
fadc = st->channel_config[channel].odr;
|
||||
|
||||
switch (st->channel_config[channel].filter_type) {
|
||||
case AD7124_SINC3_FILTER:
|
||||
return DIV_ROUND_CLOSEST(fadc * 230, 1000);
|
||||
case AD7124_SINC4_FILTER:
|
||||
return DIV_ROUND_CLOSEST(fadc * 262, 1000);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad7124_set_3db_filter_freq(struct ad7124_state *st,
|
||||
unsigned int channel,
|
||||
unsigned int freq)
|
||||
{
|
||||
unsigned int sinc4_3db_odr;
|
||||
unsigned int sinc3_3db_odr;
|
||||
unsigned int new_filter;
|
||||
unsigned int new_odr;
|
||||
|
||||
sinc4_3db_odr = DIV_ROUND_CLOSEST(freq * 1000, 230);
|
||||
sinc3_3db_odr = DIV_ROUND_CLOSEST(freq * 1000, 262);
|
||||
|
||||
if (sinc4_3db_odr > sinc3_3db_odr) {
|
||||
new_filter = AD7124_SINC3_FILTER;
|
||||
new_odr = sinc4_3db_odr;
|
||||
} else {
|
||||
new_filter = AD7124_SINC4_FILTER;
|
||||
new_odr = sinc3_3db_odr;
|
||||
}
|
||||
|
||||
if (st->channel_config[channel].filter_type != new_filter) {
|
||||
int ret;
|
||||
|
||||
st->channel_config[channel].filter_type = new_filter;
|
||||
ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel),
|
||||
AD7124_FILTER_TYPE_MSK,
|
||||
AD7124_FILTER_TYPE_SEL(new_filter),
|
||||
3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ad7124_set_channel_odr(st, channel, new_odr);
|
||||
}
|
||||
|
||||
static int ad7124_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long info)
|
||||
@ -322,6 +389,9 @@ static int ad7124_read_raw(struct iio_dev *indio_dev,
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = st->channel_config[chan->address].odr;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
*val = ad7124_get_3db_filter_freq(st, chan->scan_index);
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -355,11 +425,37 @@ static int ad7124_write_raw(struct iio_dev *indio_dev,
|
||||
gain = DIV_ROUND_CLOSEST(res, val2);
|
||||
|
||||
return ad7124_set_channel_gain(st, chan->address, gain);
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
if (val2 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad7124_set_3db_filter_freq(st, chan->address, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad7124_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg,
|
||||
unsigned int writeval,
|
||||
unsigned int *readval)
|
||||
{
|
||||
struct ad7124_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (reg >= ARRAY_SIZE(ad7124_reg_size))
|
||||
return -EINVAL;
|
||||
|
||||
if (readval)
|
||||
ret = ad_sd_read_reg(&st->sd, reg, ad7124_reg_size[reg],
|
||||
readval);
|
||||
else
|
||||
ret = ad_sd_write_reg(&st->sd, reg, ad7124_reg_size[reg],
|
||||
writeval);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR(in_voltage_scale_available,
|
||||
"0.000001164 0.000002328 0.000004656 0.000009313 0.000018626 0.000037252 0.000074505 0.000149011 0.000298023");
|
||||
|
||||
@ -375,6 +471,7 @@ static const struct attribute_group ad7124_attrs_group = {
|
||||
static const struct iio_info ad7124_info = {
|
||||
.read_raw = ad7124_read_raw,
|
||||
.write_raw = ad7124_write_raw,
|
||||
.debugfs_reg_access = &ad7124_reg_access,
|
||||
.validate_trigger = ad_sd_validate_trigger,
|
||||
.attrs = &ad7124_attrs_group,
|
||||
};
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@ -786,76 +787,105 @@ static const struct iio_info ad7195_info = {
|
||||
.validate_trigger = ad_sd_validate_trigger,
|
||||
};
|
||||
|
||||
#define __AD719x_CHANNEL(_si, _channel1, _channel2, _address, _extend_name, \
|
||||
_type, _mask_type_av, _ext_info) \
|
||||
{ \
|
||||
.type = (_type), \
|
||||
.differential = ((_channel2) == -1 ? 0 : 1), \
|
||||
.indexed = 1, \
|
||||
.channel = (_channel1), \
|
||||
.channel2 = (_channel2), \
|
||||
.address = (_address), \
|
||||
.extend_name = (_extend_name), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
|
||||
.info_mask_shared_by_type_available = (_mask_type_av), \
|
||||
.ext_info = (_ext_info), \
|
||||
.scan_index = (_si), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 24, \
|
||||
.storagebits = 32, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define AD719x_DIFF_CHANNEL(_si, _channel1, _channel2, _address) \
|
||||
__AD719x_CHANNEL(_si, _channel1, _channel2, _address, NULL, \
|
||||
IIO_VOLTAGE, BIT(IIO_CHAN_INFO_SCALE), \
|
||||
ad7192_calibsys_ext_info)
|
||||
|
||||
#define AD719x_CHANNEL(_si, _channel1, _address) \
|
||||
__AD719x_CHANNEL(_si, _channel1, -1, _address, NULL, IIO_VOLTAGE, \
|
||||
BIT(IIO_CHAN_INFO_SCALE), ad7192_calibsys_ext_info)
|
||||
|
||||
#define AD719x_SHORTED_CHANNEL(_si, _channel1, _address) \
|
||||
__AD719x_CHANNEL(_si, _channel1, -1, _address, "shorted", IIO_VOLTAGE, \
|
||||
BIT(IIO_CHAN_INFO_SCALE), ad7192_calibsys_ext_info)
|
||||
|
||||
#define AD719x_TEMP_CHANNEL(_si, _address) \
|
||||
__AD719x_CHANNEL(_si, 0, -1, _address, NULL, IIO_TEMP, 0, NULL)
|
||||
|
||||
static const struct iio_chan_spec ad7192_channels[] = {
|
||||
AD_SD_DIFF_CHANNEL(0, 1, 2, AD7192_CH_AIN1P_AIN2M, 24, 32, 0),
|
||||
AD_SD_DIFF_CHANNEL(1, 3, 4, AD7192_CH_AIN3P_AIN4M, 24, 32, 0),
|
||||
AD_SD_TEMP_CHANNEL(2, AD7192_CH_TEMP, 24, 32, 0),
|
||||
AD_SD_SHORTED_CHANNEL(3, 2, AD7192_CH_AIN2P_AIN2M, 24, 32, 0),
|
||||
AD_SD_CHANNEL(4, 1, AD7192_CH_AIN1, 24, 32, 0),
|
||||
AD_SD_CHANNEL(5, 2, AD7192_CH_AIN2, 24, 32, 0),
|
||||
AD_SD_CHANNEL(6, 3, AD7192_CH_AIN3, 24, 32, 0),
|
||||
AD_SD_CHANNEL(7, 4, AD7192_CH_AIN4, 24, 32, 0),
|
||||
AD719x_DIFF_CHANNEL(0, 1, 2, AD7192_CH_AIN1P_AIN2M),
|
||||
AD719x_DIFF_CHANNEL(1, 3, 4, AD7192_CH_AIN3P_AIN4M),
|
||||
AD719x_TEMP_CHANNEL(2, AD7192_CH_TEMP),
|
||||
AD719x_SHORTED_CHANNEL(3, 2, AD7192_CH_AIN2P_AIN2M),
|
||||
AD719x_CHANNEL(4, 1, AD7192_CH_AIN1),
|
||||
AD719x_CHANNEL(5, 2, AD7192_CH_AIN2),
|
||||
AD719x_CHANNEL(6, 3, AD7192_CH_AIN3),
|
||||
AD719x_CHANNEL(7, 4, AD7192_CH_AIN4),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ad7193_channels[] = {
|
||||
AD_SD_DIFF_CHANNEL(0, 1, 2, AD7193_CH_AIN1P_AIN2M, 24, 32, 0),
|
||||
AD_SD_DIFF_CHANNEL(1, 3, 4, AD7193_CH_AIN3P_AIN4M, 24, 32, 0),
|
||||
AD_SD_DIFF_CHANNEL(2, 5, 6, AD7193_CH_AIN5P_AIN6M, 24, 32, 0),
|
||||
AD_SD_DIFF_CHANNEL(3, 7, 8, AD7193_CH_AIN7P_AIN8M, 24, 32, 0),
|
||||
AD_SD_TEMP_CHANNEL(4, AD7193_CH_TEMP, 24, 32, 0),
|
||||
AD_SD_SHORTED_CHANNEL(5, 2, AD7193_CH_AIN2P_AIN2M, 24, 32, 0),
|
||||
AD_SD_CHANNEL(6, 1, AD7193_CH_AIN1, 24, 32, 0),
|
||||
AD_SD_CHANNEL(7, 2, AD7193_CH_AIN2, 24, 32, 0),
|
||||
AD_SD_CHANNEL(8, 3, AD7193_CH_AIN3, 24, 32, 0),
|
||||
AD_SD_CHANNEL(9, 4, AD7193_CH_AIN4, 24, 32, 0),
|
||||
AD_SD_CHANNEL(10, 5, AD7193_CH_AIN5, 24, 32, 0),
|
||||
AD_SD_CHANNEL(11, 6, AD7193_CH_AIN6, 24, 32, 0),
|
||||
AD_SD_CHANNEL(12, 7, AD7193_CH_AIN7, 24, 32, 0),
|
||||
AD_SD_CHANNEL(13, 8, AD7193_CH_AIN8, 24, 32, 0),
|
||||
AD719x_DIFF_CHANNEL(0, 1, 2, AD7193_CH_AIN1P_AIN2M),
|
||||
AD719x_DIFF_CHANNEL(1, 3, 4, AD7193_CH_AIN3P_AIN4M),
|
||||
AD719x_DIFF_CHANNEL(2, 5, 6, AD7193_CH_AIN5P_AIN6M),
|
||||
AD719x_DIFF_CHANNEL(3, 7, 8, AD7193_CH_AIN7P_AIN8M),
|
||||
AD719x_TEMP_CHANNEL(4, AD7193_CH_TEMP),
|
||||
AD719x_SHORTED_CHANNEL(5, 2, AD7193_CH_AIN2P_AIN2M),
|
||||
AD719x_CHANNEL(6, 1, AD7193_CH_AIN1),
|
||||
AD719x_CHANNEL(7, 2, AD7193_CH_AIN2),
|
||||
AD719x_CHANNEL(8, 3, AD7193_CH_AIN3),
|
||||
AD719x_CHANNEL(9, 4, AD7193_CH_AIN4),
|
||||
AD719x_CHANNEL(10, 5, AD7193_CH_AIN5),
|
||||
AD719x_CHANNEL(11, 6, AD7193_CH_AIN6),
|
||||
AD719x_CHANNEL(12, 7, AD7193_CH_AIN7),
|
||||
AD719x_CHANNEL(13, 8, AD7193_CH_AIN8),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(14),
|
||||
};
|
||||
|
||||
static int ad7192_channels_config(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7192_state *st = iio_priv(indio_dev);
|
||||
const struct iio_chan_spec *channels;
|
||||
struct iio_chan_spec *chan;
|
||||
int i;
|
||||
|
||||
switch (st->devid) {
|
||||
case ID_AD7193:
|
||||
channels = ad7193_channels;
|
||||
indio_dev->channels = ad7193_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad7193_channels);
|
||||
break;
|
||||
default:
|
||||
channels = ad7192_channels;
|
||||
indio_dev->channels = ad7192_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad7192_channels);
|
||||
break;
|
||||
}
|
||||
|
||||
chan = devm_kcalloc(indio_dev->dev.parent, indio_dev->num_channels,
|
||||
sizeof(*chan), GFP_KERNEL);
|
||||
if (!chan)
|
||||
return -ENOMEM;
|
||||
|
||||
indio_dev->channels = chan;
|
||||
|
||||
for (i = 0; i < indio_dev->num_channels; i++) {
|
||||
*chan = channels[i];
|
||||
chan->info_mask_shared_by_all |=
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY);
|
||||
if (chan->type != IIO_TEMP) {
|
||||
chan->info_mask_shared_by_type_available |=
|
||||
BIT(IIO_CHAN_INFO_SCALE);
|
||||
chan->ext_info = ad7192_calibsys_ext_info;
|
||||
}
|
||||
chan++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ad7192_of_match[] = {
|
||||
{ .compatible = "adi,ad7190", .data = (void *)ID_AD7190 },
|
||||
{ .compatible = "adi,ad7192", .data = (void *)ID_AD7192 },
|
||||
{ .compatible = "adi,ad7193", .data = (void *)ID_AD7193 },
|
||||
{ .compatible = "adi,ad7195", .data = (void *)ID_AD7195 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad7192_of_match);
|
||||
|
||||
static int ad7192_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7192_state *st;
|
||||
@ -899,13 +929,16 @@ static int ad7192_probe(struct spi_device *spi)
|
||||
|
||||
voltage_uv = regulator_get_voltage(st->avdd);
|
||||
|
||||
if (voltage_uv)
|
||||
if (voltage_uv > 0) {
|
||||
st->int_vref_mv = voltage_uv / 1000;
|
||||
else
|
||||
} else {
|
||||
ret = voltage_uv;
|
||||
dev_err(&spi->dev, "Device tree error, reference voltage undefined\n");
|
||||
goto error_disable_avdd;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st->devid = spi_get_device_id(spi)->driver_data;
|
||||
st->devid = (unsigned long)of_device_get_match_data(&spi->dev);
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
@ -986,26 +1019,6 @@ static int ad7192_remove(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7192_id[] = {
|
||||
{"ad7190", ID_AD7190},
|
||||
{"ad7192", ID_AD7192},
|
||||
{"ad7193", ID_AD7193},
|
||||
{"ad7195", ID_AD7195},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(spi, ad7192_id);
|
||||
|
||||
static const struct of_device_id ad7192_of_match[] = {
|
||||
{ .compatible = "adi,ad7190" },
|
||||
{ .compatible = "adi,ad7192" },
|
||||
{ .compatible = "adi,ad7193" },
|
||||
{ .compatible = "adi,ad7195" },
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, ad7192_of_match);
|
||||
|
||||
static struct spi_driver ad7192_driver = {
|
||||
.driver = {
|
||||
.name = "ad7192",
|
||||
@ -1013,7 +1026,6 @@ static struct spi_driver ad7192_driver = {
|
||||
},
|
||||
.probe = ad7192_probe,
|
||||
.remove = ad7192_remove,
|
||||
.id_table = ad7192_id,
|
||||
};
|
||||
module_spi_driver(ad7192_driver);
|
||||
|
@ -122,7 +122,10 @@ static int ad7292_single_conversion(struct ad7292_state *st,
|
||||
{
|
||||
.tx_buf = &st->d8,
|
||||
.len = 4,
|
||||
.delay_usecs = 6,
|
||||
.delay = {
|
||||
.value = 6,
|
||||
.unit = SPI_DELAY_UNIT_USECS
|
||||
},
|
||||
}, {
|
||||
.rx_buf = &st->d16,
|
||||
.len = 2,
|
||||
|
@ -836,8 +836,10 @@ static int exynos_adc_probe(struct platform_device *pdev)
|
||||
|
||||
info->vdd = devm_regulator_get(&pdev->dev, "vdd");
|
||||
if (IS_ERR(info->vdd)) {
|
||||
dev_err(&pdev->dev, "failed getting regulator, err = %ld\n",
|
||||
PTR_ERR(info->vdd));
|
||||
if (PTR_ERR(info->vdd) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev,
|
||||
"failed getting regulator, err = %ld\n",
|
||||
PTR_ERR(info->vdd));
|
||||
return PTR_ERR(info->vdd);
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,10 @@ static int max1118_read(struct spi_device *spi, int channel)
|
||||
*/
|
||||
{
|
||||
.len = 0,
|
||||
.delay_usecs = 1, /* > CNVST Low Time 100 ns */
|
||||
.delay = { /* > CNVST Low Time 100 ns */
|
||||
.value = 1,
|
||||
.unit = SPI_DELAY_UNIT_USECS
|
||||
},
|
||||
.cs_change = 1,
|
||||
},
|
||||
/*
|
||||
@ -81,7 +84,10 @@ static int max1118_read(struct spi_device *spi, int channel)
|
||||
*/
|
||||
{
|
||||
.len = 0,
|
||||
.delay_usecs = 8,
|
||||
.delay = {
|
||||
.value = 8,
|
||||
.unit = SPI_DELAY_UNIT_USECS
|
||||
},
|
||||
},
|
||||
{
|
||||
.rx_buf = &adc->data,
|
||||
|
@ -421,7 +421,8 @@ static int mcp320x_probe(struct spi_device *spi)
|
||||
adc->transfer[1].len++;
|
||||
|
||||
/* conversions are started by asserting CS pin for 8 usec */
|
||||
adc->start_conv_transfer.delay_usecs = 8;
|
||||
adc->start_conv_transfer.delay.value = 8;
|
||||
adc->start_conv_transfer.delay.unit = SPI_DELAY_UNIT_USECS;
|
||||
spi_message_init_with_transfers(&adc->start_conv_msg,
|
||||
&adc->start_conv_transfer, 1);
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
struct npcm_adc {
|
||||
bool int_status;
|
||||
@ -23,13 +24,9 @@ struct npcm_adc {
|
||||
struct clk *adc_clk;
|
||||
wait_queue_head_t wq;
|
||||
struct regulator *vref;
|
||||
struct regmap *rst_regmap;
|
||||
struct reset_control *reset;
|
||||
};
|
||||
|
||||
/* NPCM7xx reset module */
|
||||
#define NPCM7XX_IPSRST1_OFFSET 0x020
|
||||
#define NPCM7XX_IPSRST1_ADC_RST BIT(27)
|
||||
|
||||
/* ADC registers */
|
||||
#define NPCM_ADCCON 0x00
|
||||
#define NPCM_ADCDATA 0x04
|
||||
@ -106,13 +103,11 @@ static int npcm_adc_read(struct npcm_adc *info, int *val, u8 channel)
|
||||
msecs_to_jiffies(10));
|
||||
if (ret == 0) {
|
||||
regtemp = ioread32(info->regs + NPCM_ADCCON);
|
||||
if ((regtemp & NPCM_ADCCON_ADC_CONV) && info->rst_regmap) {
|
||||
if (regtemp & NPCM_ADCCON_ADC_CONV) {
|
||||
/* if conversion failed - reset ADC module */
|
||||
regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET,
|
||||
NPCM7XX_IPSRST1_ADC_RST);
|
||||
reset_control_assert(info->reset);
|
||||
msleep(100);
|
||||
regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET,
|
||||
0x0);
|
||||
reset_control_deassert(info->reset);
|
||||
msleep(100);
|
||||
|
||||
/* Enable ADC and start conversion module */
|
||||
@ -186,7 +181,6 @@ static int npcm_adc_probe(struct platform_device *pdev)
|
||||
struct npcm_adc *info;
|
||||
struct iio_dev *indio_dev;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
|
||||
if (!indio_dev)
|
||||
@ -199,6 +193,10 @@ static int npcm_adc_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
info->reset = devm_reset_control_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(info->reset))
|
||||
return PTR_ERR(info->reset);
|
||||
|
||||
info->adc_clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(info->adc_clk)) {
|
||||
dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n");
|
||||
@ -211,16 +209,6 @@ static int npcm_adc_probe(struct platform_device *pdev)
|
||||
div = div >> NPCM_ADCCON_DIV_SHIFT;
|
||||
info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2);
|
||||
|
||||
if (of_device_is_compatible(np, "nuvoton,npcm750-adc")) {
|
||||
info->rst_regmap = syscon_regmap_lookup_by_compatible
|
||||
("nuvoton,npcm750-rst");
|
||||
if (IS_ERR(info->rst_regmap)) {
|
||||
dev_err(&pdev->dev, "Failed to find nuvoton,npcm750-rst\n");
|
||||
ret = PTR_ERR(info->rst_regmap);
|
||||
goto err_disable_clk;
|
||||
}
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
ret = -EINVAL;
|
||||
|
@ -189,7 +189,8 @@ static int tlc4541_probe(struct spi_device *spi)
|
||||
/* Setup default message */
|
||||
st->scan_single_xfer[0].rx_buf = &st->rx_buf[0];
|
||||
st->scan_single_xfer[0].len = 3;
|
||||
st->scan_single_xfer[1].delay_usecs = 3;
|
||||
st->scan_single_xfer[1].delay.value = 3;
|
||||
st->scan_single_xfer[1].delay.unit = SPI_DELAY_UNIT_NSECS;
|
||||
st->scan_single_xfer[2].rx_buf = &st->rx_buf[0];
|
||||
st->scan_single_xfer[2].len = 2;
|
||||
|
||||
|
@ -22,4 +22,14 @@ config AD8366
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad8366.
|
||||
|
||||
config HMC425
|
||||
tristate "Analog Devices HMC425A and similar GPIO Gain Amplifiers"
|
||||
depends on GPIOLIB
|
||||
help
|
||||
Say yes here to build support for Analog Devices HMC425A and similar
|
||||
gain amplifiers or step attenuators.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called hmc425a.
|
||||
|
||||
endmenu
|
||||
|
@ -5,3 +5,4 @@
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AD8366) += ad8366.o
|
||||
obj-$(CONFIG_HMC425) += hmc425a.o
|
||||
|
@ -5,6 +5,7 @@
|
||||
* AD8366 Dual-Digital Variable Gain Amplifier (VGA)
|
||||
* ADA4961 BiCMOS RF Digital Gain Amplifier (DGA)
|
||||
* ADL5240 Digitally controlled variable gain amplifier (VGA)
|
||||
* HMC1119 0.25 dB LSB, 7-Bit, Silicon Digital Attenuator
|
||||
*
|
||||
* Copyright 2012-2019 Analog Devices Inc.
|
||||
*/
|
||||
@ -27,6 +28,7 @@ enum ad8366_type {
|
||||
ID_AD8366,
|
||||
ID_ADA4961,
|
||||
ID_ADL5240,
|
||||
ID_HMC1119,
|
||||
};
|
||||
|
||||
struct ad8366_info {
|
||||
@ -62,6 +64,10 @@ static struct ad8366_info ad8366_infos[] = {
|
||||
.gain_min = -11500,
|
||||
.gain_max = 20000,
|
||||
},
|
||||
[ID_HMC1119] = {
|
||||
.gain_min = -31750,
|
||||
.gain_max = 0,
|
||||
},
|
||||
};
|
||||
|
||||
static int ad8366_write(struct iio_dev *indio_dev,
|
||||
@ -84,6 +90,9 @@ static int ad8366_write(struct iio_dev *indio_dev,
|
||||
case ID_ADL5240:
|
||||
st->data[0] = (ch_a & 0x3F);
|
||||
break;
|
||||
case ID_HMC1119:
|
||||
st->data[0] = ch_a;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = spi_write(st->spi, st->data, indio_dev->num_channels);
|
||||
@ -118,6 +127,9 @@ static int ad8366_read_raw(struct iio_dev *indio_dev,
|
||||
case ID_ADL5240:
|
||||
gain = 20000 - 31500 + code * 500;
|
||||
break;
|
||||
case ID_HMC1119:
|
||||
gain = -1 * code * 250;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Values in dB */
|
||||
@ -164,6 +176,9 @@ static int ad8366_write_raw(struct iio_dev *indio_dev,
|
||||
case ID_ADL5240:
|
||||
code = ((gain - 500 - 20000) / 500) & 0x3F;
|
||||
break;
|
||||
case ID_HMC1119:
|
||||
code = (abs(gain) / 250) & 0x7F;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
@ -180,9 +195,22 @@ static int ad8366_write_raw(struct iio_dev *indio_dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad8366_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_HARDWAREGAIN:
|
||||
return IIO_VAL_INT_PLUS_MICRO_DB;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info ad8366_info = {
|
||||
.read_raw = &ad8366_read_raw,
|
||||
.write_raw = &ad8366_write_raw,
|
||||
.write_raw_get_fmt = &ad8366_write_raw_get_fmt,
|
||||
};
|
||||
|
||||
#define AD8366_CHAN(_channel) { \
|
||||
@ -233,6 +261,7 @@ static int ad8366_probe(struct spi_device *spi)
|
||||
break;
|
||||
case ID_ADA4961:
|
||||
case ID_ADL5240:
|
||||
case ID_HMC1119:
|
||||
st->reset_gpio = devm_gpiod_get(&spi->dev, "reset",
|
||||
GPIOD_OUT_HIGH);
|
||||
indio_dev->channels = ada4961_channels;
|
||||
@ -285,6 +314,7 @@ static const struct spi_device_id ad8366_id[] = {
|
||||
{"ad8366", ID_AD8366},
|
||||
{"ada4961", ID_ADA4961},
|
||||
{"adl5240", ID_ADL5240},
|
||||
{"hmc1119", ID_HMC1119},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad8366_id);
|
||||
|
248
drivers/iio/amplifiers/hmc425a.c
Normal file
248
drivers/iio/amplifiers/hmc425a.c
Normal file
@ -0,0 +1,248 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* HMC425A and similar Gain Amplifiers
|
||||
*
|
||||
* Copyright 2020 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
enum hmc425a_type {
|
||||
ID_HMC425A,
|
||||
};
|
||||
|
||||
struct hmc425a_chip_info {
|
||||
const char *name;
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
unsigned int num_gpios;
|
||||
int gain_min;
|
||||
int gain_max;
|
||||
int default_gain;
|
||||
};
|
||||
|
||||
struct hmc425a_state {
|
||||
struct regulator *reg;
|
||||
struct mutex lock; /* protect sensor state */
|
||||
struct hmc425a_chip_info *chip_info;
|
||||
struct gpio_descs *gpios;
|
||||
enum hmc425a_type type;
|
||||
u32 gain;
|
||||
};
|
||||
|
||||
static int hmc425a_write(struct iio_dev *indio_dev, u32 value)
|
||||
{
|
||||
struct hmc425a_state *st = iio_priv(indio_dev);
|
||||
DECLARE_BITMAP(values, BITS_PER_TYPE(value));
|
||||
|
||||
values[0] = value;
|
||||
|
||||
gpiod_set_array_value_cansleep(st->gpios->ndescs, st->gpios->desc,
|
||||
NULL, values);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hmc425a_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long m)
|
||||
{
|
||||
struct hmc425a_state *st = iio_priv(indio_dev);
|
||||
int code, gain = 0;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_HARDWAREGAIN:
|
||||
code = st->gain;
|
||||
|
||||
switch (st->type) {
|
||||
case ID_HMC425A:
|
||||
gain = ~code * -500;
|
||||
break;
|
||||
}
|
||||
|
||||
*val = gain / 1000;
|
||||
*val2 = (gain % 1000) * 1000;
|
||||
|
||||
ret = IIO_VAL_INT_PLUS_MICRO_DB;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
static int hmc425a_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct hmc425a_state *st = iio_priv(indio_dev);
|
||||
struct hmc425a_chip_info *inf = st->chip_info;
|
||||
int code = 0, gain;
|
||||
int ret;
|
||||
|
||||
if (val < 0)
|
||||
gain = (val * 1000) - (val2 / 1000);
|
||||
else
|
||||
gain = (val * 1000) + (val2 / 1000);
|
||||
|
||||
if (gain > inf->gain_max || gain < inf->gain_min)
|
||||
return -EINVAL;
|
||||
|
||||
switch (st->type) {
|
||||
case ID_HMC425A:
|
||||
code = ~((abs(gain) / 500) & 0x3F);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_HARDWAREGAIN:
|
||||
st->gain = code;
|
||||
|
||||
ret = hmc425a_write(indio_dev, st->gain);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hmc425a_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_HARDWAREGAIN:
|
||||
return IIO_VAL_INT_PLUS_MICRO_DB;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info hmc425a_info = {
|
||||
.read_raw = &hmc425a_read_raw,
|
||||
.write_raw = &hmc425a_write_raw,
|
||||
.write_raw_get_fmt = &hmc425a_write_raw_get_fmt,
|
||||
};
|
||||
|
||||
#define HMC425A_CHAN(_channel) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.output = 1, \
|
||||
.indexed = 1, \
|
||||
.channel = _channel, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec hmc425a_channels[] = {
|
||||
HMC425A_CHAN(0),
|
||||
};
|
||||
|
||||
/* Match table for of_platform binding */
|
||||
static const struct of_device_id hmc425a_of_match[] = {
|
||||
{ .compatible = "adi,hmc425a", .data = (void *)ID_HMC425A },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hmc425a_of_match);
|
||||
|
||||
static void hmc425a_reg_disable(void *data)
|
||||
{
|
||||
struct hmc425a_state *st = data;
|
||||
|
||||
regulator_disable(st->reg);
|
||||
}
|
||||
|
||||
static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = {
|
||||
[ID_HMC425A] = {
|
||||
.name = "hmc425a",
|
||||
.channels = hmc425a_channels,
|
||||
.num_channels = ARRAY_SIZE(hmc425a_channels),
|
||||
.num_gpios = 6,
|
||||
.gain_min = -31500,
|
||||
.gain_max = 0,
|
||||
.default_gain = -0x40, /* set default gain -31.5db*/
|
||||
},
|
||||
};
|
||||
|
||||
static int hmc425a_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct hmc425a_state *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
st->type = (enum hmc425a_type)of_device_get_match_data(&pdev->dev);
|
||||
|
||||
st->chip_info = &hmc425a_chip_info_tbl[st->type];
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->name = st->chip_info->name;
|
||||
st->gain = st->chip_info->default_gain;
|
||||
|
||||
st->gpios = devm_gpiod_get_array(&pdev->dev, "ctrl", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(st->gpios)) {
|
||||
ret = PTR_ERR(st->gpios);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "failed to get gpios\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (st->gpios->ndescs != st->chip_info->num_gpios) {
|
||||
dev_err(&pdev->dev, "%d GPIOs needed to operate\n",
|
||||
st->chip_info->num_gpios);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
st->reg = devm_regulator_get(&pdev->dev, "vcc-supply");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = devm_add_action_or_reset(&pdev->dev, hmc425a_reg_disable, st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_init(&st->lock);
|
||||
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &hmc425a_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
return devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct platform_driver hmc425a_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.of_match_table = hmc425a_of_match,
|
||||
},
|
||||
.probe = hmc425a_probe,
|
||||
};
|
||||
module_platform_driver(hmc425a_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices HMC425A and similar GPIO control Gain Amplifiers");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -48,6 +48,11 @@
|
||||
#define ATLAS_REG_EC_CALIB_STATUS_LOW BIT(2)
|
||||
#define ATLAS_REG_EC_CALIB_STATUS_HIGH BIT(3)
|
||||
|
||||
#define ATLAS_REG_DO_CALIB_STATUS 0x09
|
||||
#define ATLAS_REG_DO_CALIB_STATUS_MASK 0x03
|
||||
#define ATLAS_REG_DO_CALIB_STATUS_PRESSURE BIT(0)
|
||||
#define ATLAS_REG_DO_CALIB_STATUS_DO BIT(1)
|
||||
|
||||
#define ATLAS_REG_PH_TEMP_DATA 0x0e
|
||||
#define ATLAS_REG_PH_DATA 0x16
|
||||
|
||||
@ -60,14 +65,19 @@
|
||||
#define ATLAS_REG_ORP_CALIB_STATUS 0x0d
|
||||
#define ATLAS_REG_ORP_DATA 0x0e
|
||||
|
||||
#define ATLAS_REG_DO_TEMP_DATA 0x12
|
||||
#define ATLAS_REG_DO_DATA 0x22
|
||||
|
||||
#define ATLAS_PH_INT_TIME_IN_MS 450
|
||||
#define ATLAS_EC_INT_TIME_IN_MS 650
|
||||
#define ATLAS_ORP_INT_TIME_IN_MS 450
|
||||
#define ATLAS_DO_INT_TIME_IN_MS 450
|
||||
|
||||
enum {
|
||||
ATLAS_PH_SM,
|
||||
ATLAS_EC_SM,
|
||||
ATLAS_ORP_SM,
|
||||
ATLAS_DO_SM,
|
||||
};
|
||||
|
||||
struct atlas_data {
|
||||
@ -76,6 +86,7 @@ struct atlas_data {
|
||||
struct atlas_device *chip;
|
||||
struct regmap *regmap;
|
||||
struct irq_work work;
|
||||
unsigned int interrupt_enabled;
|
||||
|
||||
__be32 buffer[6]; /* 96-bit data + 32-bit pad + 64-bit timestamp */
|
||||
};
|
||||
@ -121,7 +132,7 @@ static const struct iio_chan_spec atlas_ph_channels[] = {
|
||||
},
|
||||
};
|
||||
|
||||
#define ATLAS_EC_CHANNEL(_idx, _addr) \
|
||||
#define ATLAS_CONCENTRATION_CHANNEL(_idx, _addr) \
|
||||
{\
|
||||
.type = IIO_CONCENTRATION, \
|
||||
.indexed = 1, \
|
||||
@ -152,8 +163,8 @@ static const struct iio_chan_spec atlas_ec_channels[] = {
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
ATLAS_EC_CHANNEL(0, ATLAS_REG_TDS_DATA),
|
||||
ATLAS_EC_CHANNEL(1, ATLAS_REG_PSS_DATA),
|
||||
ATLAS_CONCENTRATION_CHANNEL(0, ATLAS_REG_TDS_DATA),
|
||||
ATLAS_CONCENTRATION_CHANNEL(1, ATLAS_REG_PSS_DATA),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
@ -182,6 +193,19 @@ static const struct iio_chan_spec atlas_orp_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec atlas_do_channels[] = {
|
||||
ATLAS_CONCENTRATION_CHANNEL(0, ATLAS_REG_DO_DATA),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.address = ATLAS_REG_DO_TEMP_DATA,
|
||||
.info_mask_separate =
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
.output = 1,
|
||||
.scan_index = -1
|
||||
},
|
||||
};
|
||||
|
||||
static int atlas_check_ph_calibration(struct atlas_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
@ -262,7 +286,31 @@ static int atlas_check_orp_calibration(struct atlas_data *data)
|
||||
dev_warn(dev, "device has not been calibrated\n");
|
||||
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
static int atlas_check_do_calibration(struct atlas_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(data->regmap, ATLAS_REG_DO_CALIB_STATUS, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(val & ATLAS_REG_DO_CALIB_STATUS_MASK)) {
|
||||
dev_warn(dev, "device has not been calibrated\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(val & ATLAS_REG_DO_CALIB_STATUS_PRESSURE))
|
||||
dev_warn(dev, "device missing atmospheric pressure calibration\n");
|
||||
|
||||
if (!(val & ATLAS_REG_DO_CALIB_STATUS_DO))
|
||||
dev_warn(dev, "device missing dissolved oxygen calibration\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct atlas_device {
|
||||
const struct iio_chan_spec *channels;
|
||||
@ -295,6 +343,13 @@ static struct atlas_device atlas_devices[] = {
|
||||
.calibration = &atlas_check_orp_calibration,
|
||||
.delay = ATLAS_ORP_INT_TIME_IN_MS,
|
||||
},
|
||||
[ATLAS_DO_SM] = {
|
||||
.channels = atlas_do_channels,
|
||||
.num_channels = 3,
|
||||
.data_reg = ATLAS_REG_DO_DATA,
|
||||
.calibration = &atlas_check_do_calibration,
|
||||
.delay = ATLAS_DO_INT_TIME_IN_MS,
|
||||
},
|
||||
};
|
||||
|
||||
static int atlas_set_powermode(struct atlas_data *data, int on)
|
||||
@ -304,6 +359,9 @@ static int atlas_set_powermode(struct atlas_data *data, int on)
|
||||
|
||||
static int atlas_set_interrupt(struct atlas_data *data, bool state)
|
||||
{
|
||||
if (!data->interrupt_enabled)
|
||||
return 0;
|
||||
|
||||
return regmap_update_bits(data->regmap, ATLAS_REG_INT_CONTROL,
|
||||
ATLAS_REG_INT_CONTROL_EN,
|
||||
state ? ATLAS_REG_INT_CONTROL_EN : 0);
|
||||
@ -507,6 +565,7 @@ static const struct i2c_device_id atlas_id[] = {
|
||||
{ "atlas-ph-sm", ATLAS_PH_SM},
|
||||
{ "atlas-ec-sm", ATLAS_EC_SM},
|
||||
{ "atlas-orp-sm", ATLAS_ORP_SM},
|
||||
{ "atlas-do-sm", ATLAS_DO_SM},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, atlas_id);
|
||||
@ -515,6 +574,7 @@ static const struct of_device_id atlas_dt_ids[] = {
|
||||
{ .compatible = "atlas,ph-sm", .data = (void *)ATLAS_PH_SM, },
|
||||
{ .compatible = "atlas,ec-sm", .data = (void *)ATLAS_EC_SM, },
|
||||
{ .compatible = "atlas,orp-sm", .data = (void *)ATLAS_ORP_SM, },
|
||||
{ .compatible = "atlas,do-sm", .data = (void *)ATLAS_DO_SM, },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, atlas_dt_ids);
|
||||
@ -572,11 +632,6 @@ static int atlas_probe(struct i2c_client *client,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (client->irq <= 0) {
|
||||
dev_err(&client->dev, "no valid irq defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = chip->calibration(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -596,16 +651,20 @@ static int atlas_probe(struct i2c_client *client,
|
||||
|
||||
init_irq_work(&data->work, atlas_work_handler);
|
||||
|
||||
/* interrupt pin toggles on new conversion */
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, atlas_interrupt_handler,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"atlas_irq",
|
||||
indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "request irq (%d) failed\n", client->irq);
|
||||
goto unregister_buffer;
|
||||
if (client->irq > 0) {
|
||||
/* interrupt pin toggles on new conversion */
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, atlas_interrupt_handler,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"atlas_irq",
|
||||
indio_dev);
|
||||
|
||||
if (ret)
|
||||
dev_warn(&client->dev,
|
||||
"request irq (%d) failed\n", client->irq);
|
||||
else
|
||||
data->interrupt_enabled = 1;
|
||||
}
|
||||
|
||||
ret = atlas_set_powermode(data, 1);
|
||||
|
@ -328,6 +328,8 @@ static struct st_sensors_platform_data *st_sensors_dev_probe(struct device *dev,
|
||||
return NULL;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (!device_property_read_u32(dev, "st,drdy-int-pin", &val) && (val <= 2))
|
||||
pdata->drdy_int_pin = (u8) val;
|
||||
else
|
||||
@ -371,6 +373,8 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,
|
||||
|
||||
/* If OF/DT pdata exists, it will take precedence of anything else */
|
||||
of_pdata = st_sensors_dev_probe(indio_dev->dev.parent, pdata);
|
||||
if (IS_ERR(of_pdata))
|
||||
return PTR_ERR(of_pdata);
|
||||
if (of_pdata)
|
||||
pdata = of_pdata;
|
||||
|
||||
|
@ -121,26 +121,6 @@ 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 LTC1660
|
||||
tristate "Linear Technology LTC1660/LTC1665 DAC SPI driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Linear Technology
|
||||
LTC1660 and LTC1665 Digital to Analog Converters.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ltc1660.
|
||||
|
||||
config LTC2632
|
||||
tristate "Linear Technology LTC2632-12/10/8 DAC spi driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Linear Technology
|
||||
LTC2632-12, LTC2632-10, LTC2632-8 converters (DAC).
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ltc2632.
|
||||
|
||||
config AD5686
|
||||
tristate
|
||||
|
||||
@ -208,6 +188,16 @@ config AD5764
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5764.
|
||||
|
||||
config AD5770R
|
||||
tristate "Analog Devices AD5770R IDAC driver"
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5770R Digital to
|
||||
Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5770r.
|
||||
|
||||
config AD5791
|
||||
tristate "Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC SPI driver"
|
||||
depends on SPI
|
||||
@ -229,16 +219,6 @@ config AD7303
|
||||
To compile this driver as module choose M here: the module will be called
|
||||
ad7303.
|
||||
|
||||
config CIO_DAC
|
||||
tristate "Measurement Computing CIO-DAC IIO driver"
|
||||
depends on X86 && (ISA_BUS || PC104)
|
||||
select ISA_BUS_API
|
||||
help
|
||||
Say yes here to build support for the Measurement Computing CIO-DAC
|
||||
analog output device family (CIO-DAC16, CIO-DAC08, PC104-DAC06). The
|
||||
base port addresses for the devices may be configured via the base
|
||||
array module parameter.
|
||||
|
||||
config AD8801
|
||||
tristate "Analog Devices AD8801/AD8803 DAC driver"
|
||||
depends on SPI_MASTER
|
||||
@ -249,6 +229,16 @@ config AD8801
|
||||
To compile this driver as a module choose M here: the module will be called
|
||||
ad8801.
|
||||
|
||||
config CIO_DAC
|
||||
tristate "Measurement Computing CIO-DAC IIO driver"
|
||||
depends on X86 && (ISA_BUS || PC104)
|
||||
select ISA_BUS_API
|
||||
help
|
||||
Say yes here to build support for the Measurement Computing CIO-DAC
|
||||
analog output device family (CIO-DAC16, CIO-DAC08, PC104-DAC06). The
|
||||
base port addresses for the devices may be configured via the base
|
||||
array module parameter.
|
||||
|
||||
config DPOT_DAC
|
||||
tristate "DAC emulation using a DPOT"
|
||||
depends on OF
|
||||
@ -278,6 +268,27 @@ config LPC18XX_DAC
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called lpc18xx_dac.
|
||||
|
||||
config LTC1660
|
||||
tristate "Linear Technology LTC1660/LTC1665 DAC SPI driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Linear Technology
|
||||
LTC1660 and LTC1665 Digital to Analog Converters.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ltc1660.
|
||||
|
||||
config LTC2632
|
||||
tristate "Linear Technology LTC2632-12/10/8 and LTC2636-12/10/8 DAC spi driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Linear Technology
|
||||
LTC2632-12, LTC2632-10, LTC2632-8, LTC2636-12, LTC2636-10 and
|
||||
LTC2636-8 converters (DAC).
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ltc2632.
|
||||
|
||||
config M62332
|
||||
tristate "Mitsubishi M62332 DAC driver"
|
||||
depends on I2C
|
||||
|
@ -19,6 +19,7 @@ obj-$(CONFIG_AD5755) += ad5755.o
|
||||
obj-$(CONFIG_AD5755) += ad5758.o
|
||||
obj-$(CONFIG_AD5761) += ad5761.o
|
||||
obj-$(CONFIG_AD5764) += ad5764.o
|
||||
obj-$(CONFIG_AD5770R) += ad5770r.o
|
||||
obj-$(CONFIG_AD5791) += ad5791.o
|
||||
obj-$(CONFIG_AD5686) += ad5686.o
|
||||
obj-$(CONFIG_AD5686_SPI) += ad5686-spi.o
|
||||
|
@ -631,10 +631,9 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(ad5755_dcdc_freq_table)) {
|
||||
if (i == ARRAY_SIZE(ad5755_dcdc_freq_table))
|
||||
dev_err(dev,
|
||||
"adi,dc-dc-freq out of range selecting 410kHz");
|
||||
}
|
||||
"adi,dc-dc-freq out of range selecting 410kHz\n");
|
||||
}
|
||||
|
||||
pdata->dc_dc_maxv = AD5755_DC_DC_MAXV_23V;
|
||||
@ -645,17 +644,16 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ARRAY_SIZE(ad5755_dcdc_maxv_table)) {
|
||||
if (i == ARRAY_SIZE(ad5755_dcdc_maxv_table))
|
||||
dev_err(dev,
|
||||
"adi,dc-dc-maxv out of range selecting 23V");
|
||||
}
|
||||
"adi,dc-dc-maxv out of range selecting 23V\n");
|
||||
}
|
||||
|
||||
devnr = 0;
|
||||
for_each_child_of_node(np, pp) {
|
||||
if (devnr >= AD5755_NUM_CHANNELS) {
|
||||
dev_err(dev,
|
||||
"There is to many channels defined in DT\n");
|
||||
"There are too many channels defined in DT\n");
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
@ -681,11 +679,10 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ARRAY_SIZE(ad5755_slew_rate_table)) {
|
||||
if (i == ARRAY_SIZE(ad5755_slew_rate_table))
|
||||
dev_err(dev,
|
||||
"channel %d slew rate out of range selecting 64kHz",
|
||||
"channel %d slew rate out of range selecting 64kHz\n",
|
||||
devnr);
|
||||
}
|
||||
|
||||
pdata->dac[devnr].slew.step_size = AD5755_SLEW_STEP_SIZE_1;
|
||||
for (i = 0; i < ARRAY_SIZE(ad5755_slew_step_table); i++) {
|
||||
@ -695,11 +692,10 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ARRAY_SIZE(ad5755_slew_step_table)) {
|
||||
if (i == ARRAY_SIZE(ad5755_slew_step_table))
|
||||
dev_err(dev,
|
||||
"channel %d slew step size out of range selecting 1 LSB",
|
||||
"channel %d slew step size out of range selecting 1 LSB\n",
|
||||
devnr);
|
||||
}
|
||||
} else {
|
||||
pdata->dac[devnr].slew.enable = false;
|
||||
pdata->dac[devnr].slew.rate = AD5755_SLEW_RATE_64k;
|
||||
|
695
drivers/iio/dac/ad5770r.c
Normal file
695
drivers/iio/dac/ad5770r.c
Normal file
@ -0,0 +1,695 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* AD5770R Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2018 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#define ADI_SPI_IF_CONFIG_A 0x00
|
||||
#define ADI_SPI_IF_CONFIG_B 0x01
|
||||
#define ADI_SPI_IF_DEVICE_CONFIG 0x02
|
||||
#define ADI_SPI_IF_CHIP_TYPE 0x03
|
||||
#define ADI_SPI_IF_PRODUCT_ID_L 0x04
|
||||
#define ADI_SPI_IF_PRODUCT_ID_H 0x05
|
||||
#define ADI_SPI_IF_CHIP_GRADE 0x06
|
||||
#define ADI_SPI_IF_SCRACTH_PAD 0x0A
|
||||
#define ADI_SPI_IF_SPI_REVISION 0x0B
|
||||
#define ADI_SPI_IF_SPI_VENDOR_L 0x0C
|
||||
#define ADI_SPI_IF_SPI_VENDOR_H 0x0D
|
||||
#define ADI_SPI_IF_SPI_STREAM_MODE 0x0E
|
||||
#define ADI_SPI_IF_CONFIG_C 0x10
|
||||
#define ADI_SPI_IF_STATUS_A 0x11
|
||||
|
||||
/* ADI_SPI_IF_CONFIG_A */
|
||||
#define ADI_SPI_IF_SW_RESET_MSK (BIT(0) | BIT(7))
|
||||
#define ADI_SPI_IF_SW_RESET_SEL(x) ((x) & ADI_SPI_IF_SW_RESET_MSK)
|
||||
#define ADI_SPI_IF_ADDR_ASC_MSK (BIT(2) | BIT(5))
|
||||
#define ADI_SPI_IF_ADDR_ASC_SEL(x) (((x) << 2) & ADI_SPI_IF_ADDR_ASC_MSK)
|
||||
|
||||
/* ADI_SPI_IF_CONFIG_B */
|
||||
#define ADI_SPI_IF_SINGLE_INS_MSK BIT(7)
|
||||
#define ADI_SPI_IF_SINGLE_INS_SEL(x) FIELD_PREP(ADI_SPI_IF_SINGLE_INS_MSK, x)
|
||||
#define ADI_SPI_IF_SHORT_INS_MSK BIT(7)
|
||||
#define ADI_SPI_IF_SHORT_INS_SEL(x) FIELD_PREP(ADI_SPI_IF_SINGLE_INS_MSK, x)
|
||||
|
||||
/* ADI_SPI_IF_CONFIG_C */
|
||||
#define ADI_SPI_IF_STRICT_REG_MSK BIT(5)
|
||||
#define ADI_SPI_IF_STRICT_REG_GET(x) FIELD_GET(ADI_SPI_IF_STRICT_REG_MSK, x)
|
||||
|
||||
/* AD5770R configuration registers */
|
||||
#define AD5770R_CHANNEL_CONFIG 0x14
|
||||
#define AD5770R_OUTPUT_RANGE(ch) (0x15 + (ch))
|
||||
#define AD5770R_FILTER_RESISTOR(ch) (0x1D + (ch))
|
||||
#define AD5770R_REFERENCE 0x1B
|
||||
#define AD5770R_DAC_LSB(ch) (0x26 + 2 * (ch))
|
||||
#define AD5770R_DAC_MSB(ch) (0x27 + 2 * (ch))
|
||||
#define AD5770R_CH_SELECT 0x34
|
||||
#define AD5770R_CH_ENABLE 0x44
|
||||
|
||||
/* AD5770R_CHANNEL_CONFIG */
|
||||
#define AD5770R_CFG_CH0_SINK_EN(x) (((x) & 0x1) << 7)
|
||||
#define AD5770R_CFG_SHUTDOWN_B(x, ch) (((x) & 0x1) << (ch))
|
||||
|
||||
/* AD5770R_OUTPUT_RANGE */
|
||||
#define AD5770R_RANGE_OUTPUT_SCALING(x) (((x) & GENMASK(5, 0)) << 2)
|
||||
#define AD5770R_RANGE_MODE(x) ((x) & GENMASK(1, 0))
|
||||
|
||||
/* AD5770R_REFERENCE */
|
||||
#define AD5770R_REF_RESISTOR_SEL(x) (((x) & 0x1) << 2)
|
||||
#define AD5770R_REF_SEL(x) ((x) & GENMASK(1, 0))
|
||||
|
||||
/* AD5770R_CH_ENABLE */
|
||||
#define AD5770R_CH_SET(x, ch) (((x) & 0x1) << (ch))
|
||||
|
||||
#define AD5770R_MAX_CHANNELS 6
|
||||
#define AD5770R_MAX_CH_MODES 14
|
||||
#define AD5770R_LOW_VREF_mV 1250
|
||||
#define AD5770R_HIGH_VREF_mV 2500
|
||||
|
||||
enum ad5770r_ch0_modes {
|
||||
AD5770R_CH0_0_300 = 0,
|
||||
AD5770R_CH0_NEG_60_0,
|
||||
AD5770R_CH0_NEG_60_300
|
||||
};
|
||||
|
||||
enum ad5770r_ch1_modes {
|
||||
AD5770R_CH1_0_140_LOW_HEAD = 1,
|
||||
AD5770R_CH1_0_140_LOW_NOISE,
|
||||
AD5770R_CH1_0_250
|
||||
};
|
||||
|
||||
enum ad5770r_ch2_5_modes {
|
||||
AD5770R_CH_LOW_RANGE = 0,
|
||||
AD5770R_CH_HIGH_RANGE
|
||||
};
|
||||
|
||||
enum ad5770r_ref_v {
|
||||
AD5770R_EXT_2_5_V = 0,
|
||||
AD5770R_INT_1_25_V_OUT_ON,
|
||||
AD5770R_EXT_1_25_V,
|
||||
AD5770R_INT_1_25_V_OUT_OFF
|
||||
};
|
||||
|
||||
enum ad5770r_output_filter_resistor {
|
||||
AD5770R_FILTER_60_OHM = 0x0,
|
||||
AD5770R_FILTER_5_6_KOHM = 0x5,
|
||||
AD5770R_FILTER_11_2_KOHM,
|
||||
AD5770R_FILTER_22_2_KOHM,
|
||||
AD5770R_FILTER_44_4_KOHM,
|
||||
AD5770R_FILTER_104_KOHM,
|
||||
};
|
||||
|
||||
struct ad5770r_out_range {
|
||||
u8 out_scale;
|
||||
u8 out_range_mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5770R_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @regmap: regmap
|
||||
* @vref_reg: fixed regulator for reference configuration
|
||||
* @gpio_reset: gpio descriptor
|
||||
* @output_mode: array contains channels output ranges
|
||||
* @vref: reference value
|
||||
* @ch_pwr_down: powerdown flags
|
||||
* @internal_ref: internal reference flag
|
||||
* @external_res: external 2.5k resistor flag
|
||||
* @transf_buf: cache aligned buffer for spi read/write
|
||||
*/
|
||||
struct ad5770r_state {
|
||||
struct spi_device *spi;
|
||||
struct regmap *regmap;
|
||||
struct regulator *vref_reg;
|
||||
struct gpio_desc *gpio_reset;
|
||||
struct ad5770r_out_range output_mode[AD5770R_MAX_CHANNELS];
|
||||
int vref;
|
||||
bool ch_pwr_down[AD5770R_MAX_CHANNELS];
|
||||
bool internal_ref;
|
||||
bool external_res;
|
||||
u8 transf_buf[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static const struct regmap_config ad5770r_spi_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.read_flag_mask = BIT(7),
|
||||
};
|
||||
|
||||
struct ad5770r_output_modes {
|
||||
unsigned int ch;
|
||||
u8 mode;
|
||||
int min;
|
||||
int max;
|
||||
};
|
||||
|
||||
static struct ad5770r_output_modes ad5770r_rng_tbl[] = {
|
||||
{ 0, AD5770R_CH0_0_300, 0, 300 },
|
||||
{ 0, AD5770R_CH0_NEG_60_0, -60, 0 },
|
||||
{ 0, AD5770R_CH0_NEG_60_300, -60, 300 },
|
||||
{ 1, AD5770R_CH1_0_140_LOW_HEAD, 0, 140 },
|
||||
{ 1, AD5770R_CH1_0_140_LOW_NOISE, 0, 140 },
|
||||
{ 1, AD5770R_CH1_0_250, 0, 250 },
|
||||
{ 2, AD5770R_CH_LOW_RANGE, 0, 55 },
|
||||
{ 2, AD5770R_CH_HIGH_RANGE, 0, 150 },
|
||||
{ 3, AD5770R_CH_LOW_RANGE, 0, 45 },
|
||||
{ 3, AD5770R_CH_HIGH_RANGE, 0, 100 },
|
||||
{ 4, AD5770R_CH_LOW_RANGE, 0, 45 },
|
||||
{ 4, AD5770R_CH_HIGH_RANGE, 0, 100 },
|
||||
{ 5, AD5770R_CH_LOW_RANGE, 0, 45 },
|
||||
{ 5, AD5770R_CH_HIGH_RANGE, 0, 100 },
|
||||
};
|
||||
|
||||
static const unsigned int ad5770r_filter_freqs[] = {
|
||||
153, 357, 715, 1400, 2800, 262000,
|
||||
};
|
||||
|
||||
static const unsigned int ad5770r_filter_reg_vals[] = {
|
||||
AD5770R_FILTER_104_KOHM,
|
||||
AD5770R_FILTER_44_4_KOHM,
|
||||
AD5770R_FILTER_22_2_KOHM,
|
||||
AD5770R_FILTER_11_2_KOHM,
|
||||
AD5770R_FILTER_5_6_KOHM,
|
||||
AD5770R_FILTER_60_OHM
|
||||
};
|
||||
|
||||
static int ad5770r_set_output_mode(struct ad5770r_state *st,
|
||||
const struct ad5770r_out_range *out_mode,
|
||||
int channel)
|
||||
{
|
||||
unsigned int regval;
|
||||
|
||||
regval = AD5770R_RANGE_OUTPUT_SCALING(out_mode->out_scale) |
|
||||
AD5770R_RANGE_MODE(out_mode->out_range_mode);
|
||||
|
||||
return regmap_write(st->regmap,
|
||||
AD5770R_OUTPUT_RANGE(channel), regval);
|
||||
}
|
||||
|
||||
static int ad5770r_set_reference(struct ad5770r_state *st)
|
||||
{
|
||||
unsigned int regval;
|
||||
|
||||
regval = AD5770R_REF_RESISTOR_SEL(st->external_res);
|
||||
|
||||
if (st->internal_ref) {
|
||||
regval |= AD5770R_REF_SEL(AD5770R_INT_1_25_V_OUT_OFF);
|
||||
} else {
|
||||
switch (st->vref) {
|
||||
case AD5770R_LOW_VREF_mV:
|
||||
regval |= AD5770R_REF_SEL(AD5770R_EXT_1_25_V);
|
||||
break;
|
||||
case AD5770R_HIGH_VREF_mV:
|
||||
regval |= AD5770R_REF_SEL(AD5770R_EXT_2_5_V);
|
||||
break;
|
||||
default:
|
||||
regval = AD5770R_REF_SEL(AD5770R_INT_1_25_V_OUT_OFF);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return regmap_write(st->regmap, AD5770R_REFERENCE, regval);
|
||||
}
|
||||
|
||||
static int ad5770r_soft_reset(struct ad5770r_state *st)
|
||||
{
|
||||
return regmap_write(st->regmap, ADI_SPI_IF_CONFIG_A,
|
||||
ADI_SPI_IF_SW_RESET_SEL(1));
|
||||
}
|
||||
|
||||
static int ad5770r_reset(struct ad5770r_state *st)
|
||||
{
|
||||
/* Perform software reset if no GPIO provided */
|
||||
if (!st->gpio_reset)
|
||||
return ad5770r_soft_reset(st);
|
||||
|
||||
gpiod_set_value_cansleep(st->gpio_reset, 0);
|
||||
usleep_range(10, 20);
|
||||
gpiod_set_value_cansleep(st->gpio_reset, 1);
|
||||
|
||||
/* data must not be written during reset timeframe */
|
||||
usleep_range(100, 200);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5770r_get_range(struct ad5770r_state *st,
|
||||
int ch, int *min, int *max)
|
||||
{
|
||||
int i;
|
||||
u8 tbl_ch, tbl_mode, out_range;
|
||||
|
||||
out_range = st->output_mode[ch].out_range_mode;
|
||||
|
||||
for (i = 0; i < AD5770R_MAX_CH_MODES; i++) {
|
||||
tbl_ch = ad5770r_rng_tbl[i].ch;
|
||||
tbl_mode = ad5770r_rng_tbl[i].mode;
|
||||
if (tbl_ch == ch && tbl_mode == out_range) {
|
||||
*min = ad5770r_rng_tbl[i].min;
|
||||
*max = ad5770r_rng_tbl[i].max;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5770r_get_filter_freq(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *freq)
|
||||
{
|
||||
struct ad5770r_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
unsigned int regval, i;
|
||||
|
||||
ret = regmap_read(st->regmap,
|
||||
AD5770R_FILTER_RESISTOR(chan->channel), ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ad5770r_filter_reg_vals); i++)
|
||||
if (regval == ad5770r_filter_reg_vals[i])
|
||||
break;
|
||||
if (i == ARRAY_SIZE(ad5770r_filter_reg_vals))
|
||||
return -EINVAL;
|
||||
|
||||
*freq = ad5770r_filter_freqs[i];
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int ad5770r_set_filter_freq(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int freq)
|
||||
{
|
||||
struct ad5770r_state *st = iio_priv(indio_dev);
|
||||
unsigned int regval, i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ad5770r_filter_freqs); i++)
|
||||
if (ad5770r_filter_freqs[i] >= freq)
|
||||
break;
|
||||
if (i == ARRAY_SIZE(ad5770r_filter_freqs))
|
||||
return -EINVAL;
|
||||
|
||||
regval = ad5770r_filter_reg_vals[i];
|
||||
|
||||
return regmap_write(st->regmap, AD5770R_FILTER_RESISTOR(chan->channel),
|
||||
regval);
|
||||
}
|
||||
|
||||
static int ad5770r_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long info)
|
||||
{
|
||||
struct ad5770r_state *st = iio_priv(indio_dev);
|
||||
int max, min, ret;
|
||||
u16 buf16;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = regmap_bulk_read(st->regmap,
|
||||
chan->address,
|
||||
st->transf_buf, 2);
|
||||
if (ret)
|
||||
return 0;
|
||||
|
||||
buf16 = st->transf_buf[0] + (st->transf_buf[1] << 8);
|
||||
*val = buf16 >> 2;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = ad5770r_get_range(st, chan->channel, &min, &max);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = max - min;
|
||||
/* There is no sign bit. (negative current is mapped from 0)
|
||||
* (sourced/sinked) current = raw * scale + offset
|
||||
* where offset in case of CH0 can be negative.
|
||||
*/
|
||||
*val2 = 14;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
return ad5770r_get_filter_freq(indio_dev, chan, val);
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
ret = ad5770r_get_range(st, chan->channel, &min, &max);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = min;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad5770r_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long info)
|
||||
{
|
||||
struct ad5770r_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
st->transf_buf[0] = ((u16)val >> 6);
|
||||
st->transf_buf[1] = (val & GENMASK(5, 0)) << 2;
|
||||
return regmap_bulk_write(st->regmap, chan->address,
|
||||
st->transf_buf, 2);
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
return ad5770r_set_filter_freq(indio_dev, chan, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad5770r_read_freq_avail(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals, int *type, int *length,
|
||||
long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
*type = IIO_VAL_INT;
|
||||
*vals = ad5770r_filter_freqs;
|
||||
*length = ARRAY_SIZE(ad5770r_filter_freqs);
|
||||
return IIO_AVAIL_LIST;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5770r_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned int reg,
|
||||
unsigned int writeval,
|
||||
unsigned int *readval)
|
||||
{
|
||||
struct ad5770r_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (readval)
|
||||
return regmap_read(st->regmap, reg, readval);
|
||||
else
|
||||
return regmap_write(st->regmap, reg, writeval);
|
||||
}
|
||||
|
||||
static const struct iio_info ad5770r_info = {
|
||||
.read_raw = ad5770r_read_raw,
|
||||
.write_raw = ad5770r_write_raw,
|
||||
.read_avail = ad5770r_read_freq_avail,
|
||||
.debugfs_reg_access = &ad5770r_reg_access,
|
||||
};
|
||||
|
||||
static int ad5770r_store_output_range(struct ad5770r_state *st,
|
||||
int min, int max, int index)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < AD5770R_MAX_CH_MODES; i++) {
|
||||
if (ad5770r_rng_tbl[i].ch != index)
|
||||
continue;
|
||||
if (ad5770r_rng_tbl[i].min != min ||
|
||||
ad5770r_rng_tbl[i].max != max)
|
||||
continue;
|
||||
st->output_mode[index].out_range_mode = ad5770r_rng_tbl[i].mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t ad5770r_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct ad5770r_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->ch_pwr_down[chan->channel]);
|
||||
}
|
||||
|
||||
static ssize_t ad5770r_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct ad5770r_state *st = iio_priv(indio_dev);
|
||||
unsigned int regval;
|
||||
unsigned int mask;
|
||||
bool readin;
|
||||
int ret;
|
||||
|
||||
ret = kstrtobool(buf, &readin);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
readin = !readin;
|
||||
|
||||
regval = AD5770R_CFG_SHUTDOWN_B(readin, chan->channel);
|
||||
if (chan->channel == 0 &&
|
||||
st->output_mode[0].out_range_mode > AD5770R_CH0_0_300) {
|
||||
regval |= AD5770R_CFG_CH0_SINK_EN(readin);
|
||||
mask = BIT(chan->channel) + BIT(7);
|
||||
} else {
|
||||
mask = BIT(chan->channel);
|
||||
}
|
||||
ret = regmap_update_bits(st->regmap, AD5770R_CHANNEL_CONFIG, mask,
|
||||
regval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regval = AD5770R_CH_SET(readin, chan->channel);
|
||||
ret = regmap_update_bits(st->regmap, AD5770R_CH_ENABLE,
|
||||
BIT(chan->channel), regval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->ch_pwr_down[chan->channel] = !readin;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5770r_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5770r_read_dac_powerdown,
|
||||
.write = ad5770r_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
#define AD5770R_IDAC_CHANNEL(index, reg) { \
|
||||
.type = IIO_CURRENT, \
|
||||
.address = reg, \
|
||||
.indexed = 1, \
|
||||
.channel = index, \
|
||||
.output = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | \
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
|
||||
.info_mask_shared_by_type_available = \
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
|
||||
.ext_info = ad5770r_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad5770r_channels[] = {
|
||||
AD5770R_IDAC_CHANNEL(0, AD5770R_DAC_MSB(0)),
|
||||
AD5770R_IDAC_CHANNEL(1, AD5770R_DAC_MSB(1)),
|
||||
AD5770R_IDAC_CHANNEL(2, AD5770R_DAC_MSB(2)),
|
||||
AD5770R_IDAC_CHANNEL(3, AD5770R_DAC_MSB(3)),
|
||||
AD5770R_IDAC_CHANNEL(4, AD5770R_DAC_MSB(4)),
|
||||
AD5770R_IDAC_CHANNEL(5, AD5770R_DAC_MSB(5)),
|
||||
};
|
||||
|
||||
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) {
|
||||
ret = fwnode_property_read_u32(child, "num", &num);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (num > AD5770R_MAX_CHANNELS)
|
||||
return -EINVAL;
|
||||
|
||||
ret = fwnode_property_read_u32_array(child,
|
||||
"adi,range-microamp",
|
||||
tmp, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
min = tmp[0] / 1000;
|
||||
max = tmp[1] / 1000;
|
||||
ret = ad5770r_store_output_range(st, min, max, num);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5770r_init(struct ad5770r_state *st)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(st->gpio_reset))
|
||||
return PTR_ERR(st->gpio_reset);
|
||||
|
||||
/* Perform a reset */
|
||||
ret = ad5770r_reset(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set output range */
|
||||
ret = ad5770r_channel_config(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < AD5770R_MAX_CHANNELS; i++) {
|
||||
ret = ad5770r_set_output_mode(st, &st->output_mode[i], i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->external_res = fwnode_property_read_bool(st->spi->dev.fwnode,
|
||||
"adi,external-resistor");
|
||||
|
||||
ret = ad5770r_set_reference(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set outputs off */
|
||||
ret = regmap_write(st->regmap, AD5770R_CHANNEL_CONFIG, 0x00);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(st->regmap, AD5770R_CH_ENABLE, 0x00);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < AD5770R_MAX_CHANNELS; i++)
|
||||
st->ch_pwr_down[i] = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ad5770r_disable_regulator(void *data)
|
||||
{
|
||||
struct ad5770r_state *st = data;
|
||||
|
||||
regulator_disable(st->vref_reg);
|
||||
}
|
||||
|
||||
static int ad5770r_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad5770r_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &ad5770r_spi_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&spi->dev, "Error initializing spi regmap: %ld\n",
|
||||
PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
st->regmap = regmap;
|
||||
|
||||
st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref");
|
||||
if (!IS_ERR(st->vref_reg)) {
|
||||
ret = regulator_enable(st->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev,
|
||||
"Failed to enable vref regulators: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(&spi->dev,
|
||||
ad5770r_disable_regulator,
|
||||
st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(st->vref_reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
st->vref = ret / 1000;
|
||||
} else {
|
||||
if (PTR_ERR(st->vref_reg) == -ENODEV) {
|
||||
st->vref = AD5770R_LOW_VREF_mV;
|
||||
st->internal_ref = true;
|
||||
} else {
|
||||
return PTR_ERR(st->vref_reg);
|
||||
}
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad5770r_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ad5770r_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad5770r_channels);
|
||||
|
||||
ret = ad5770r_init(st);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "AD5770R init failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(&st->spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id ad5770r_of_id[] = {
|
||||
{ .compatible = "adi,ad5770r", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ad5770r_of_id);
|
||||
|
||||
static const struct spi_device_id ad5770r_id[] = {
|
||||
{ "ad5770r", 0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5770r_id);
|
||||
|
||||
static struct spi_driver ad5770r_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.of_match_table = ad5770r_of_id,
|
||||
},
|
||||
.probe = ad5770r_probe,
|
||||
.id_table = ad5770r_id,
|
||||
};
|
||||
|
||||
module_spi_driver(ad5770r_driver);
|
||||
|
||||
MODULE_AUTHOR("Mircea Caprioru <mircea.caprioru@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5770R IDAC");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -12,11 +12,6 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define LTC2632_DAC_CHANNELS 2
|
||||
|
||||
#define LTC2632_ADDR_DAC0 0x0
|
||||
#define LTC2632_ADDR_DAC1 0x1
|
||||
|
||||
#define LTC2632_CMD_WRITE_INPUT_N 0x0
|
||||
#define LTC2632_CMD_UPDATE_DAC_N 0x1
|
||||
#define LTC2632_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2
|
||||
@ -33,6 +28,7 @@
|
||||
*/
|
||||
struct ltc2632_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
const size_t num_channels;
|
||||
const int vref_mv;
|
||||
};
|
||||
|
||||
@ -57,6 +53,12 @@ enum ltc2632_supported_device_ids {
|
||||
ID_LTC2632H12,
|
||||
ID_LTC2632H10,
|
||||
ID_LTC2632H8,
|
||||
ID_LTC2636L12,
|
||||
ID_LTC2636L10,
|
||||
ID_LTC2636L8,
|
||||
ID_LTC2636H12,
|
||||
ID_LTC2636H10,
|
||||
ID_LTC2636H8,
|
||||
};
|
||||
|
||||
static int ltc2632_spi_write(struct spi_device *spi,
|
||||
@ -190,39 +192,77 @@ static const struct iio_chan_spec_ext_info ltc2632_ext_info[] = {
|
||||
const struct iio_chan_spec _name ## _channels[] = { \
|
||||
LTC2632_CHANNEL(0, _bits), \
|
||||
LTC2632_CHANNEL(1, _bits), \
|
||||
LTC2632_CHANNEL(2, _bits), \
|
||||
LTC2632_CHANNEL(3, _bits), \
|
||||
LTC2632_CHANNEL(4, _bits), \
|
||||
LTC2632_CHANNEL(5, _bits), \
|
||||
LTC2632_CHANNEL(6, _bits), \
|
||||
LTC2632_CHANNEL(7, _bits), \
|
||||
}
|
||||
|
||||
static DECLARE_LTC2632_CHANNELS(ltc2632l12, 12);
|
||||
static DECLARE_LTC2632_CHANNELS(ltc2632l10, 10);
|
||||
static DECLARE_LTC2632_CHANNELS(ltc2632l8, 8);
|
||||
|
||||
static DECLARE_LTC2632_CHANNELS(ltc2632h12, 12);
|
||||
static DECLARE_LTC2632_CHANNELS(ltc2632h10, 10);
|
||||
static DECLARE_LTC2632_CHANNELS(ltc2632h8, 8);
|
||||
static DECLARE_LTC2632_CHANNELS(ltc2632x12, 12);
|
||||
static DECLARE_LTC2632_CHANNELS(ltc2632x10, 10);
|
||||
static DECLARE_LTC2632_CHANNELS(ltc2632x8, 8);
|
||||
|
||||
static const struct ltc2632_chip_info ltc2632_chip_info_tbl[] = {
|
||||
[ID_LTC2632L12] = {
|
||||
.channels = ltc2632l12_channels,
|
||||
.channels = ltc2632x12_channels,
|
||||
.num_channels = 2,
|
||||
.vref_mv = 2500,
|
||||
},
|
||||
[ID_LTC2632L10] = {
|
||||
.channels = ltc2632l10_channels,
|
||||
.channels = ltc2632x10_channels,
|
||||
.num_channels = 2,
|
||||
.vref_mv = 2500,
|
||||
},
|
||||
[ID_LTC2632L8] = {
|
||||
.channels = ltc2632l8_channels,
|
||||
.channels = ltc2632x8_channels,
|
||||
.num_channels = 2,
|
||||
.vref_mv = 2500,
|
||||
},
|
||||
[ID_LTC2632H12] = {
|
||||
.channels = ltc2632h12_channels,
|
||||
.channels = ltc2632x12_channels,
|
||||
.num_channels = 2,
|
||||
.vref_mv = 4096,
|
||||
},
|
||||
[ID_LTC2632H10] = {
|
||||
.channels = ltc2632h10_channels,
|
||||
.channels = ltc2632x10_channels,
|
||||
.num_channels = 2,
|
||||
.vref_mv = 4096,
|
||||
},
|
||||
[ID_LTC2632H8] = {
|
||||
.channels = ltc2632h8_channels,
|
||||
.channels = ltc2632x8_channels,
|
||||
.num_channels = 2,
|
||||
.vref_mv = 4096,
|
||||
},
|
||||
[ID_LTC2636L12] = {
|
||||
.channels = ltc2632x12_channels,
|
||||
.num_channels = 8,
|
||||
.vref_mv = 2500,
|
||||
},
|
||||
[ID_LTC2636L10] = {
|
||||
.channels = ltc2632x10_channels,
|
||||
.num_channels = 8,
|
||||
.vref_mv = 2500,
|
||||
},
|
||||
[ID_LTC2636L8] = {
|
||||
.channels = ltc2632x8_channels,
|
||||
.num_channels = 8,
|
||||
.vref_mv = 2500,
|
||||
},
|
||||
[ID_LTC2636H12] = {
|
||||
.channels = ltc2632x12_channels,
|
||||
.num_channels = 8,
|
||||
.vref_mv = 4096,
|
||||
},
|
||||
[ID_LTC2636H10] = {
|
||||
.channels = ltc2632x10_channels,
|
||||
.num_channels = 8,
|
||||
.vref_mv = 4096,
|
||||
},
|
||||
[ID_LTC2636H8] = {
|
||||
.channels = ltc2632x8_channels,
|
||||
.num_channels = 8,
|
||||
.vref_mv = 4096,
|
||||
},
|
||||
};
|
||||
@ -291,7 +331,7 @@ static int ltc2632_probe(struct spi_device *spi)
|
||||
indio_dev->info = <c2632_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = chip_info->channels;
|
||||
indio_dev->num_channels = LTC2632_DAC_CHANNELS;
|
||||
indio_dev->num_channels = chip_info->num_channels;
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
@ -316,6 +356,12 @@ static const struct spi_device_id ltc2632_id[] = {
|
||||
{ "ltc2632-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H12] },
|
||||
{ "ltc2632-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H10] },
|
||||
{ "ltc2632-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2632H8] },
|
||||
{ "ltc2636-l12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L12] },
|
||||
{ "ltc2636-l10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L10] },
|
||||
{ "ltc2636-l8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636L8] },
|
||||
{ "ltc2636-h12", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H12] },
|
||||
{ "ltc2636-h10", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H10] },
|
||||
{ "ltc2636-h8", (kernel_ulong_t)<c2632_chip_info_tbl[ID_LTC2636H8] },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ltc2632_id);
|
||||
@ -339,6 +385,24 @@ static const struct of_device_id ltc2632_of_match[] = {
|
||||
}, {
|
||||
.compatible = "lltc,ltc2632-h8",
|
||||
.data = <c2632_chip_info_tbl[ID_LTC2632H8]
|
||||
}, {
|
||||
.compatible = "lltc,ltc2636-l12",
|
||||
.data = <c2632_chip_info_tbl[ID_LTC2636L12]
|
||||
}, {
|
||||
.compatible = "lltc,ltc2636-l10",
|
||||
.data = <c2632_chip_info_tbl[ID_LTC2636L10]
|
||||
}, {
|
||||
.compatible = "lltc,ltc2636-l8",
|
||||
.data = <c2632_chip_info_tbl[ID_LTC2636L8]
|
||||
}, {
|
||||
.compatible = "lltc,ltc2636-h12",
|
||||
.data = <c2632_chip_info_tbl[ID_LTC2636H12]
|
||||
}, {
|
||||
.compatible = "lltc,ltc2636-h10",
|
||||
.data = <c2632_chip_info_tbl[ID_LTC2636H10]
|
||||
}, {
|
||||
.compatible = "lltc,ltc2636-h8",
|
||||
.data = <c2632_chip_info_tbl[ID_LTC2636H8]
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
@ -59,7 +59,7 @@
|
||||
struct adis16136_chip_info {
|
||||
unsigned int precision;
|
||||
unsigned int fullscale;
|
||||
const struct adis_timeout *timeouts;
|
||||
const struct adis_data adis_data;
|
||||
};
|
||||
|
||||
struct adis16136 {
|
||||
@ -466,22 +466,22 @@ static const char * const adis16136_status_error_msgs[] = {
|
||||
[ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL] = "Flash checksum error",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16136_data = {
|
||||
.diag_stat_reg = ADIS16136_REG_DIAG_STAT,
|
||||
.glob_cmd_reg = ADIS16136_REG_GLOB_CMD,
|
||||
.msc_ctrl_reg = ADIS16136_REG_MSC_CTRL,
|
||||
|
||||
.self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST,
|
||||
|
||||
.read_delay = 10,
|
||||
.write_delay = 10,
|
||||
|
||||
.status_error_msgs = adis16136_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL) |
|
||||
BIT(ADIS16136_DIAG_STAT_SPI_FAIL) |
|
||||
BIT(ADIS16136_DIAG_STAT_SELF_TEST_FAIL) |
|
||||
BIT(ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL),
|
||||
};
|
||||
#define ADIS16136_DATA(_timeouts) \
|
||||
{ \
|
||||
.diag_stat_reg = ADIS16136_REG_DIAG_STAT, \
|
||||
.glob_cmd_reg = ADIS16136_REG_GLOB_CMD, \
|
||||
.msc_ctrl_reg = ADIS16136_REG_MSC_CTRL, \
|
||||
.self_test_reg = ADIS16136_REG_MSC_CTRL, \
|
||||
.self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST, \
|
||||
.read_delay = 10, \
|
||||
.write_delay = 10, \
|
||||
.status_error_msgs = adis16136_status_error_msgs, \
|
||||
.status_error_mask = BIT(ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL) | \
|
||||
BIT(ADIS16136_DIAG_STAT_SPI_FAIL) | \
|
||||
BIT(ADIS16136_DIAG_STAT_SELF_TEST_FAIL) | \
|
||||
BIT(ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL), \
|
||||
.timeouts = (_timeouts), \
|
||||
}
|
||||
|
||||
enum adis16136_id {
|
||||
ID_ADIS16133,
|
||||
@ -506,41 +506,25 @@ static const struct adis16136_chip_info adis16136_chip_info[] = {
|
||||
[ID_ADIS16133] = {
|
||||
.precision = IIO_DEGREE_TO_RAD(1200),
|
||||
.fullscale = 24000,
|
||||
.timeouts = &adis16133_timeouts,
|
||||
.adis_data = ADIS16136_DATA(&adis16133_timeouts),
|
||||
},
|
||||
[ID_ADIS16135] = {
|
||||
.precision = IIO_DEGREE_TO_RAD(300),
|
||||
.fullscale = 24000,
|
||||
.timeouts = &adis16133_timeouts,
|
||||
.adis_data = ADIS16136_DATA(&adis16133_timeouts),
|
||||
},
|
||||
[ID_ADIS16136] = {
|
||||
.precision = IIO_DEGREE_TO_RAD(450),
|
||||
.fullscale = 24623,
|
||||
.timeouts = &adis16136_timeouts,
|
||||
.adis_data = ADIS16136_DATA(&adis16136_timeouts),
|
||||
},
|
||||
[ID_ADIS16137] = {
|
||||
.precision = IIO_DEGREE_TO_RAD(1000),
|
||||
.fullscale = 24609,
|
||||
.timeouts = &adis16136_timeouts,
|
||||
.adis_data = ADIS16136_DATA(&adis16136_timeouts),
|
||||
},
|
||||
};
|
||||
|
||||
static struct adis_data *adis16136_adis_data_alloc(struct adis16136 *st,
|
||||
struct device *dev)
|
||||
{
|
||||
struct adis_data *data;
|
||||
|
||||
data = devm_kmalloc(dev, sizeof(struct adis_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memcpy(data, &adis16136_data, sizeof(*data));
|
||||
|
||||
data->timeouts = st->chip_info->timeouts;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int adis16136_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
@ -565,9 +549,7 @@ static int adis16136_probe(struct spi_device *spi)
|
||||
indio_dev->info = &adis16136_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
adis16136_data = adis16136_adis_data_alloc(adis16136, &spi->dev);
|
||||
if (IS_ERR(adis16136_data))
|
||||
return PTR_ERR(adis16136_data);
|
||||
adis16136_data = &adis16136->chip_info->adis_data;
|
||||
|
||||
ret = adis_init(&adis16136->adis, indio_dev, spi, adis16136_data);
|
||||
if (ret)
|
||||
|
@ -346,6 +346,7 @@ static const struct adis_data adis16260_data = {
|
||||
.diag_stat_reg = ADIS16260_DIAG_STAT,
|
||||
|
||||
.self_test_mask = ADIS16260_MSC_CTRL_MEM_TEST,
|
||||
.self_test_reg = ADIS16260_MSC_CTRL,
|
||||
.timeouts = &adis16260_timeouts,
|
||||
|
||||
.status_error_msgs = adis1620_status_error_msgs,
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -346,8 +347,8 @@ static int adis_self_test(struct adis *adis)
|
||||
int ret;
|
||||
const struct adis_timeout *timeouts = adis->data->timeouts;
|
||||
|
||||
ret = __adis_write_reg_16(adis, adis->data->msc_ctrl_reg,
|
||||
adis->data->self_test_mask);
|
||||
ret = __adis_write_reg_16(adis, adis->data->self_test_reg,
|
||||
adis->data->self_test_mask);
|
||||
if (ret) {
|
||||
dev_err(&adis->spi->dev, "Failed to initiate self test: %d\n",
|
||||
ret);
|
||||
@ -359,42 +360,71 @@ static int adis_self_test(struct adis *adis)
|
||||
ret = __adis_check_status(adis);
|
||||
|
||||
if (adis->data->self_test_no_autoclear)
|
||||
__adis_write_reg_16(adis, adis->data->msc_ctrl_reg, 0x00);
|
||||
__adis_write_reg_16(adis, adis->data->self_test_reg, 0x00);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* adis_inital_startup() - Performs device self-test
|
||||
* __adis_initial_startup() - Device initial setup
|
||||
* @adis: The adis device
|
||||
*
|
||||
* The function performs a HW reset via a reset pin that should be specified
|
||||
* via GPIOLIB. If no pin is configured a SW reset will be performed.
|
||||
* The RST pin for the ADIS devices should be configured as ACTIVE_LOW.
|
||||
*
|
||||
* After the self-test operation is performed, the function will also check
|
||||
* that the product ID is as expected. This assumes that drivers providing
|
||||
* 'prod_id_reg' will also provide the 'prod_id'.
|
||||
*
|
||||
* Returns 0 if the device is operational, a negative error code otherwise.
|
||||
*
|
||||
* This function should be called early on in the device initialization sequence
|
||||
* to ensure that the device is in a sane and known state and that it is usable.
|
||||
*/
|
||||
int adis_initial_startup(struct adis *adis)
|
||||
int __adis_initial_startup(struct adis *adis)
|
||||
{
|
||||
const struct adis_timeout *timeouts = adis->data->timeouts;
|
||||
struct gpio_desc *gpio;
|
||||
uint16_t prod_id;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&adis->state_lock);
|
||||
/* check if the device has rst pin low */
|
||||
gpio = devm_gpiod_get_optional(&adis->spi->dev, "reset", GPIOD_ASIS);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
ret = adis_self_test(adis);
|
||||
if (ret) {
|
||||
dev_err(&adis->spi->dev, "Self-test failed, trying reset.\n");
|
||||
__adis_reset(adis);
|
||||
ret = adis_self_test(adis);
|
||||
if (ret) {
|
||||
dev_err(&adis->spi->dev, "Second self-test failed, giving up.\n");
|
||||
goto out_unlock;
|
||||
}
|
||||
if (gpio) {
|
||||
gpiod_set_value_cansleep(gpio, 1);
|
||||
msleep(10);
|
||||
/* bring device out of reset */
|
||||
gpiod_set_value_cansleep(gpio, 0);
|
||||
msleep(timeouts->reset_ms);
|
||||
} else {
|
||||
ret = __adis_reset(adis);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&adis->state_lock);
|
||||
return ret;
|
||||
ret = adis_self_test(adis);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!adis->data->prod_id_reg)
|
||||
return 0;
|
||||
|
||||
ret = adis_read_reg_16(adis, adis->data->prod_id_reg, &prod_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (prod_id != adis->data->prod_id)
|
||||
dev_warn(&adis->spi->dev,
|
||||
"Device ID(%u) and product ID(%u) do not match.",
|
||||
adis->data->prod_id, prod_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adis_initial_startup);
|
||||
EXPORT_SYMBOL_GPL(__adis_initial_startup);
|
||||
|
||||
/**
|
||||
* adis_single_conversion() - Performs a single sample conversion
|
||||
|
@ -156,7 +156,7 @@ struct adis16400_state;
|
||||
|
||||
struct adis16400_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
const struct adis_timeout *timeouts;
|
||||
const struct adis_data adis_data;
|
||||
const int num_channels;
|
||||
const long flags;
|
||||
unsigned int gyro_scale_micro;
|
||||
@ -930,12 +930,64 @@ static const struct iio_chan_spec adis16334_channels[] = {
|
||||
IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),
|
||||
};
|
||||
|
||||
static const char * const adis16400_status_error_msgs[] = {
|
||||
[ADIS16400_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure",
|
||||
[ADIS16400_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure",
|
||||
[ADIS16400_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure",
|
||||
[ADIS16400_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure",
|
||||
[ADIS16400_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure",
|
||||
[ADIS16400_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure",
|
||||
[ADIS16400_DIAG_STAT_ALARM2] = "Alarm 2 active",
|
||||
[ADIS16400_DIAG_STAT_ALARM1] = "Alarm 1 active",
|
||||
[ADIS16400_DIAG_STAT_FLASH_CHK] = "Flash checksum error",
|
||||
[ADIS16400_DIAG_STAT_SELF_TEST] = "Self test error",
|
||||
[ADIS16400_DIAG_STAT_OVERFLOW] = "Sensor overrange",
|
||||
[ADIS16400_DIAG_STAT_SPI_FAIL] = "SPI failure",
|
||||
[ADIS16400_DIAG_STAT_FLASH_UPT] = "Flash update failed",
|
||||
[ADIS16400_DIAG_STAT_POWER_HIGH] = "Power supply above 5.25V",
|
||||
[ADIS16400_DIAG_STAT_POWER_LOW] = "Power supply below 4.75V",
|
||||
};
|
||||
|
||||
#define ADIS16400_DATA(_timeouts) \
|
||||
{ \
|
||||
.msc_ctrl_reg = ADIS16400_MSC_CTRL, \
|
||||
.glob_cmd_reg = ADIS16400_GLOB_CMD, \
|
||||
.diag_stat_reg = ADIS16400_DIAG_STAT, \
|
||||
.read_delay = 50, \
|
||||
.write_delay = 50, \
|
||||
.self_test_mask = ADIS16400_MSC_CTRL_MEM_TEST, \
|
||||
.self_test_reg = ADIS16400_MSC_CTRL, \
|
||||
.status_error_msgs = adis16400_status_error_msgs, \
|
||||
.status_error_mask = BIT(ADIS16400_DIAG_STAT_ZACCL_FAIL) | \
|
||||
BIT(ADIS16400_DIAG_STAT_YACCL_FAIL) | \
|
||||
BIT(ADIS16400_DIAG_STAT_XACCL_FAIL) | \
|
||||
BIT(ADIS16400_DIAG_STAT_XGYRO_FAIL) | \
|
||||
BIT(ADIS16400_DIAG_STAT_YGYRO_FAIL) | \
|
||||
BIT(ADIS16400_DIAG_STAT_ZGYRO_FAIL) | \
|
||||
BIT(ADIS16400_DIAG_STAT_ALARM2) | \
|
||||
BIT(ADIS16400_DIAG_STAT_ALARM1) | \
|
||||
BIT(ADIS16400_DIAG_STAT_FLASH_CHK) | \
|
||||
BIT(ADIS16400_DIAG_STAT_SELF_TEST) | \
|
||||
BIT(ADIS16400_DIAG_STAT_OVERFLOW) | \
|
||||
BIT(ADIS16400_DIAG_STAT_SPI_FAIL) | \
|
||||
BIT(ADIS16400_DIAG_STAT_FLASH_UPT) | \
|
||||
BIT(ADIS16400_DIAG_STAT_POWER_HIGH) | \
|
||||
BIT(ADIS16400_DIAG_STAT_POWER_LOW), \
|
||||
.timeouts = (_timeouts), \
|
||||
}
|
||||
|
||||
static const struct adis_timeout adis16300_timeouts = {
|
||||
.reset_ms = ADIS16400_STARTUP_DELAY,
|
||||
.sw_reset_ms = ADIS16400_STARTUP_DELAY,
|
||||
.self_test_ms = ADIS16400_STARTUP_DELAY,
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16334_timeouts = {
|
||||
.reset_ms = 60,
|
||||
.sw_reset_ms = 60,
|
||||
.self_test_ms = 14,
|
||||
};
|
||||
|
||||
static const struct adis_timeout adis16362_timeouts = {
|
||||
.reset_ms = 130,
|
||||
.sw_reset_ms = 130,
|
||||
@ -972,7 +1024,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
.timeouts = &adis16300_timeouts,
|
||||
.adis_data = ADIS16400_DATA(&adis16300_timeouts),
|
||||
},
|
||||
[ADIS16334] = {
|
||||
.channels = adis16334_channels,
|
||||
@ -985,6 +1037,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 25000000 / 67850, /* 25 C = 0x00 */
|
||||
.set_freq = adis16334_set_freq,
|
||||
.get_freq = adis16334_get_freq,
|
||||
.adis_data = ADIS16400_DATA(&adis16334_timeouts),
|
||||
},
|
||||
[ADIS16350] = {
|
||||
.channels = adis16350_channels,
|
||||
@ -996,7 +1049,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.flags = ADIS16400_NO_BURST | ADIS16400_HAS_SLOW_MODE,
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
.timeouts = &adis16300_timeouts,
|
||||
.adis_data = ADIS16400_DATA(&adis16300_timeouts),
|
||||
},
|
||||
[ADIS16360] = {
|
||||
.channels = adis16350_channels,
|
||||
@ -1009,7 +1062,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
.timeouts = &adis16300_timeouts,
|
||||
.adis_data = ADIS16400_DATA(&adis16300_timeouts),
|
||||
},
|
||||
[ADIS16362] = {
|
||||
.channels = adis16350_channels,
|
||||
@ -1022,7 +1075,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
.timeouts = &adis16362_timeouts,
|
||||
.adis_data = ADIS16400_DATA(&adis16362_timeouts),
|
||||
},
|
||||
[ADIS16364] = {
|
||||
.channels = adis16350_channels,
|
||||
@ -1035,7 +1088,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
.timeouts = &adis16362_timeouts,
|
||||
.adis_data = ADIS16400_DATA(&adis16362_timeouts),
|
||||
},
|
||||
[ADIS16367] = {
|
||||
.channels = adis16350_channels,
|
||||
@ -1048,7 +1101,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 25000000 / 136000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
.timeouts = &adis16300_timeouts,
|
||||
.adis_data = ADIS16400_DATA(&adis16300_timeouts),
|
||||
},
|
||||
[ADIS16400] = {
|
||||
.channels = adis16400_channels,
|
||||
@ -1060,7 +1113,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 25000000 / 140000, /* 25 C = 0x00 */
|
||||
.set_freq = adis16400_set_freq,
|
||||
.get_freq = adis16400_get_freq,
|
||||
.timeouts = &adis16400_timeouts,
|
||||
.adis_data = ADIS16400_DATA(&adis16400_timeouts),
|
||||
},
|
||||
[ADIS16445] = {
|
||||
.channels = adis16445_channels,
|
||||
@ -1074,7 +1127,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 31000000 / 73860, /* 31 C = 0x00 */
|
||||
.set_freq = adis16334_set_freq,
|
||||
.get_freq = adis16334_get_freq,
|
||||
.timeouts = &adis16445_timeouts,
|
||||
.adis_data = ADIS16400_DATA(&adis16445_timeouts),
|
||||
},
|
||||
[ADIS16448] = {
|
||||
.channels = adis16448_channels,
|
||||
@ -1088,7 +1141,7 @@ static struct adis16400_chip_info adis16400_chips[] = {
|
||||
.temp_offset = 31000000 / 73860, /* 31 C = 0x00 */
|
||||
.set_freq = adis16334_set_freq,
|
||||
.get_freq = adis16334_get_freq,
|
||||
.timeouts = &adis16448_timeouts,
|
||||
.adis_data = ADIS16400_DATA(&adis16448_timeouts),
|
||||
}
|
||||
};
|
||||
|
||||
@ -1099,52 +1152,6 @@ static const struct iio_info adis16400_info = {
|
||||
.debugfs_reg_access = adis_debugfs_reg_access,
|
||||
};
|
||||
|
||||
static const char * const adis16400_status_error_msgs[] = {
|
||||
[ADIS16400_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure",
|
||||
[ADIS16400_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure",
|
||||
[ADIS16400_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure",
|
||||
[ADIS16400_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure",
|
||||
[ADIS16400_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure",
|
||||
[ADIS16400_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure",
|
||||
[ADIS16400_DIAG_STAT_ALARM2] = "Alarm 2 active",
|
||||
[ADIS16400_DIAG_STAT_ALARM1] = "Alarm 1 active",
|
||||
[ADIS16400_DIAG_STAT_FLASH_CHK] = "Flash checksum error",
|
||||
[ADIS16400_DIAG_STAT_SELF_TEST] = "Self test error",
|
||||
[ADIS16400_DIAG_STAT_OVERFLOW] = "Sensor overrange",
|
||||
[ADIS16400_DIAG_STAT_SPI_FAIL] = "SPI failure",
|
||||
[ADIS16400_DIAG_STAT_FLASH_UPT] = "Flash update failed",
|
||||
[ADIS16400_DIAG_STAT_POWER_HIGH] = "Power supply above 5.25V",
|
||||
[ADIS16400_DIAG_STAT_POWER_LOW] = "Power supply below 4.75V",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16400_data = {
|
||||
.msc_ctrl_reg = ADIS16400_MSC_CTRL,
|
||||
.glob_cmd_reg = ADIS16400_GLOB_CMD,
|
||||
.diag_stat_reg = ADIS16400_DIAG_STAT,
|
||||
|
||||
.read_delay = 50,
|
||||
.write_delay = 50,
|
||||
|
||||
.self_test_mask = ADIS16400_MSC_CTRL_MEM_TEST,
|
||||
|
||||
.status_error_msgs = adis16400_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16400_DIAG_STAT_ZACCL_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_YACCL_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_XACCL_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_XGYRO_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_YGYRO_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_ZGYRO_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_ALARM2) |
|
||||
BIT(ADIS16400_DIAG_STAT_ALARM1) |
|
||||
BIT(ADIS16400_DIAG_STAT_FLASH_CHK) |
|
||||
BIT(ADIS16400_DIAG_STAT_SELF_TEST) |
|
||||
BIT(ADIS16400_DIAG_STAT_OVERFLOW) |
|
||||
BIT(ADIS16400_DIAG_STAT_SPI_FAIL) |
|
||||
BIT(ADIS16400_DIAG_STAT_FLASH_UPT) |
|
||||
BIT(ADIS16400_DIAG_STAT_POWER_HIGH) |
|
||||
BIT(ADIS16400_DIAG_STAT_POWER_LOW),
|
||||
};
|
||||
|
||||
static void adis16400_setup_chan_mask(struct adis16400_state *st)
|
||||
{
|
||||
const struct adis16400_chip_info *chip_info = st->variant;
|
||||
@ -1158,23 +1165,6 @@ static void adis16400_setup_chan_mask(struct adis16400_state *st)
|
||||
st->avail_scan_mask[0] |= BIT(ch->scan_index);
|
||||
}
|
||||
}
|
||||
|
||||
static struct adis_data *adis16400_adis_data_alloc(struct adis16400_state *st,
|
||||
struct device *dev)
|
||||
{
|
||||
struct adis_data *data;
|
||||
|
||||
data = devm_kmalloc(dev, sizeof(struct adis_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memcpy(data, &adis16400_data, sizeof(*data));
|
||||
|
||||
data->timeouts = st->variant->timeouts;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int adis16400_probe(struct spi_device *spi)
|
||||
{
|
||||
struct adis16400_state *st;
|
||||
@ -1207,9 +1197,7 @@ static int adis16400_probe(struct spi_device *spi)
|
||||
st->adis.burst->extra_len = sizeof(u16);
|
||||
}
|
||||
|
||||
adis16400_data = adis16400_adis_data_alloc(st, &spi->dev);
|
||||
if (IS_ERR(adis16400_data))
|
||||
return PTR_ERR(adis16400_data);
|
||||
adis16400_data = &st->variant->adis_data;
|
||||
|
||||
ret = adis_init(&st->adis, indio_dev, spi, adis16400_data);
|
||||
if (ret)
|
||||
|
@ -333,40 +333,6 @@ static int adis16460_enable_irq(struct adis *adis, bool enable)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adis16460_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16460 *st = iio_priv(indio_dev);
|
||||
uint16_t prod_id;
|
||||
unsigned int device_id;
|
||||
int ret;
|
||||
|
||||
adis_reset(&st->adis);
|
||||
msleep(222);
|
||||
|
||||
ret = adis_write_reg_16(&st->adis, ADIS16460_REG_GLOB_CMD, BIT(1));
|
||||
if (ret)
|
||||
return ret;
|
||||
msleep(75);
|
||||
|
||||
ret = adis_check_status(&st->adis);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16460_REG_PROD_ID, &prod_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sscanf(indio_dev->name, "adis%u\n", &device_id);
|
||||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (prod_id != device_id)
|
||||
dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
|
||||
device_id, prod_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ADIS16460_DIAG_STAT_IN_CLK_OOS 7
|
||||
#define ADIS16460_DIAG_STAT_FLASH_MEM 6
|
||||
#define ADIS16460_DIAG_STAT_SELF_TEST 5
|
||||
@ -392,6 +358,10 @@ static const struct adis_timeout adis16460_timeouts = {
|
||||
static const struct adis_data adis16460_data = {
|
||||
.diag_stat_reg = ADIS16460_REG_DIAG_STAT,
|
||||
.glob_cmd_reg = ADIS16460_REG_GLOB_CMD,
|
||||
.prod_id_reg = ADIS16460_REG_PROD_ID,
|
||||
.prod_id = 16460,
|
||||
.self_test_mask = BIT(2),
|
||||
.self_test_reg = ADIS16460_REG_GLOB_CMD,
|
||||
.has_paging = false,
|
||||
.read_delay = 5,
|
||||
.write_delay = 5,
|
||||
@ -439,7 +409,7 @@ static int adis16460_probe(struct spi_device *spi)
|
||||
|
||||
adis16460_enable_irq(&st->adis, 0);
|
||||
|
||||
ret = adis16460_initial_setup(indio_dev);
|
||||
ret = __adis_initial_startup(&st->adis);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
|
||||
|
@ -138,7 +138,7 @@ struct adis16480_chip_info {
|
||||
unsigned int max_dec_rate;
|
||||
const unsigned int *filter_freqs;
|
||||
bool has_pps_clk_mode;
|
||||
const struct adis_timeout *timeouts;
|
||||
const struct adis_data adis_data;
|
||||
};
|
||||
|
||||
enum adis16480_int_pin {
|
||||
@ -796,6 +796,58 @@ enum adis16480_variant {
|
||||
ADIS16497_3,
|
||||
};
|
||||
|
||||
#define ADIS16480_DIAG_STAT_XGYRO_FAIL 0
|
||||
#define ADIS16480_DIAG_STAT_YGYRO_FAIL 1
|
||||
#define ADIS16480_DIAG_STAT_ZGYRO_FAIL 2
|
||||
#define ADIS16480_DIAG_STAT_XACCL_FAIL 3
|
||||
#define ADIS16480_DIAG_STAT_YACCL_FAIL 4
|
||||
#define ADIS16480_DIAG_STAT_ZACCL_FAIL 5
|
||||
#define ADIS16480_DIAG_STAT_XMAGN_FAIL 8
|
||||
#define ADIS16480_DIAG_STAT_YMAGN_FAIL 9
|
||||
#define ADIS16480_DIAG_STAT_ZMAGN_FAIL 10
|
||||
#define ADIS16480_DIAG_STAT_BARO_FAIL 11
|
||||
|
||||
static const char * const adis16480_status_error_msgs[] = {
|
||||
[ADIS16480_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure",
|
||||
[ADIS16480_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure",
|
||||
[ADIS16480_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure",
|
||||
[ADIS16480_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_XMAGN_FAIL] = "X-axis magnetometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_YMAGN_FAIL] = "Y-axis magnetometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_ZMAGN_FAIL] = "Z-axis magnetometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_BARO_FAIL] = "Barometer self-test failure",
|
||||
};
|
||||
|
||||
static int adis16480_enable_irq(struct adis *adis, bool enable);
|
||||
|
||||
#define ADIS16480_DATA(_prod_id, _timeouts) \
|
||||
{ \
|
||||
.diag_stat_reg = ADIS16480_REG_DIAG_STS, \
|
||||
.glob_cmd_reg = ADIS16480_REG_GLOB_CMD, \
|
||||
.prod_id_reg = ADIS16480_REG_PROD_ID, \
|
||||
.prod_id = (_prod_id), \
|
||||
.has_paging = true, \
|
||||
.read_delay = 5, \
|
||||
.write_delay = 5, \
|
||||
.self_test_mask = BIT(1), \
|
||||
.self_test_reg = ADIS16480_REG_GLOB_CMD, \
|
||||
.status_error_msgs = adis16480_status_error_msgs, \
|
||||
.status_error_mask = BIT(ADIS16480_DIAG_STAT_XGYRO_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_YGYRO_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_ZGYRO_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_XACCL_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_YACCL_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_ZACCL_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_XMAGN_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_YMAGN_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_ZMAGN_FAIL) | \
|
||||
BIT(ADIS16480_DIAG_STAT_BARO_FAIL), \
|
||||
.enable_irq = adis16480_enable_irq, \
|
||||
.timeouts = (_timeouts), \
|
||||
}
|
||||
|
||||
static const struct adis_timeout adis16485_timeouts = {
|
||||
.reset_ms = 560,
|
||||
.sw_reset_ms = 120,
|
||||
@ -838,7 +890,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.int_clk = 2460000,
|
||||
.max_dec_rate = 2048,
|
||||
.filter_freqs = adis16480_def_filter_freqs,
|
||||
.timeouts = &adis16485_timeouts,
|
||||
.adis_data = ADIS16480_DATA(16375, &adis16485_timeouts),
|
||||
},
|
||||
[ADIS16480] = {
|
||||
.channels = adis16480_channels,
|
||||
@ -851,7 +903,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.int_clk = 2460000,
|
||||
.max_dec_rate = 2048,
|
||||
.filter_freqs = adis16480_def_filter_freqs,
|
||||
.timeouts = &adis16480_timeouts,
|
||||
.adis_data = ADIS16480_DATA(16480, &adis16480_timeouts),
|
||||
},
|
||||
[ADIS16485] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -864,7 +916,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.int_clk = 2460000,
|
||||
.max_dec_rate = 2048,
|
||||
.filter_freqs = adis16480_def_filter_freqs,
|
||||
.timeouts = &adis16485_timeouts,
|
||||
.adis_data = ADIS16480_DATA(16485, &adis16485_timeouts),
|
||||
},
|
||||
[ADIS16488] = {
|
||||
.channels = adis16480_channels,
|
||||
@ -877,7 +929,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.int_clk = 2460000,
|
||||
.max_dec_rate = 2048,
|
||||
.filter_freqs = adis16480_def_filter_freqs,
|
||||
.timeouts = &adis16485_timeouts,
|
||||
.adis_data = ADIS16480_DATA(16488, &adis16485_timeouts),
|
||||
},
|
||||
[ADIS16490] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -891,7 +943,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.timeouts = &adis16495_timeouts,
|
||||
.adis_data = ADIS16480_DATA(16490, &adis16495_timeouts),
|
||||
},
|
||||
[ADIS16495_1] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -905,7 +957,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.timeouts = &adis16495_1_timeouts,
|
||||
.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts),
|
||||
},
|
||||
[ADIS16495_2] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -919,7 +971,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.timeouts = &adis16495_1_timeouts,
|
||||
.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts),
|
||||
},
|
||||
[ADIS16495_3] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -933,7 +985,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.timeouts = &adis16495_1_timeouts,
|
||||
.adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts),
|
||||
},
|
||||
[ADIS16497_1] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -947,7 +999,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.timeouts = &adis16495_1_timeouts,
|
||||
.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts),
|
||||
},
|
||||
[ADIS16497_2] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -961,7 +1013,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.timeouts = &adis16495_1_timeouts,
|
||||
.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts),
|
||||
},
|
||||
[ADIS16497_3] = {
|
||||
.channels = adis16485_channels,
|
||||
@ -975,7 +1027,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = {
|
||||
.max_dec_rate = 4250,
|
||||
.filter_freqs = adis16495_def_filter_freqs,
|
||||
.has_pps_clk_mode = true,
|
||||
.timeouts = &adis16495_1_timeouts,
|
||||
.adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts),
|
||||
},
|
||||
};
|
||||
|
||||
@ -1014,87 +1066,6 @@ static int adis16480_enable_irq(struct adis *adis, bool enable)
|
||||
return __adis_write_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL, val);
|
||||
}
|
||||
|
||||
static int adis16480_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16480 *st = iio_priv(indio_dev);
|
||||
uint16_t prod_id;
|
||||
unsigned int device_id;
|
||||
int ret;
|
||||
|
||||
adis_reset(&st->adis);
|
||||
msleep(70);
|
||||
|
||||
ret = adis_write_reg_16(&st->adis, ADIS16480_REG_GLOB_CMD, BIT(1));
|
||||
if (ret)
|
||||
return ret;
|
||||
msleep(30);
|
||||
|
||||
ret = adis_check_status(&st->adis);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&st->adis, ADIS16480_REG_PROD_ID, &prod_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sscanf(indio_dev->name, "adis%u\n", &device_id);
|
||||
if (ret != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (prod_id != device_id)
|
||||
dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
|
||||
device_id, prod_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ADIS16480_DIAG_STAT_XGYRO_FAIL 0
|
||||
#define ADIS16480_DIAG_STAT_YGYRO_FAIL 1
|
||||
#define ADIS16480_DIAG_STAT_ZGYRO_FAIL 2
|
||||
#define ADIS16480_DIAG_STAT_XACCL_FAIL 3
|
||||
#define ADIS16480_DIAG_STAT_YACCL_FAIL 4
|
||||
#define ADIS16480_DIAG_STAT_ZACCL_FAIL 5
|
||||
#define ADIS16480_DIAG_STAT_XMAGN_FAIL 8
|
||||
#define ADIS16480_DIAG_STAT_YMAGN_FAIL 9
|
||||
#define ADIS16480_DIAG_STAT_ZMAGN_FAIL 10
|
||||
#define ADIS16480_DIAG_STAT_BARO_FAIL 11
|
||||
|
||||
static const char * const adis16480_status_error_msgs[] = {
|
||||
[ADIS16480_DIAG_STAT_XGYRO_FAIL] = "X-axis gyroscope self-test failure",
|
||||
[ADIS16480_DIAG_STAT_YGYRO_FAIL] = "Y-axis gyroscope self-test failure",
|
||||
[ADIS16480_DIAG_STAT_ZGYRO_FAIL] = "Z-axis gyroscope self-test failure",
|
||||
[ADIS16480_DIAG_STAT_XACCL_FAIL] = "X-axis accelerometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_YACCL_FAIL] = "Y-axis accelerometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_ZACCL_FAIL] = "Z-axis accelerometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_XMAGN_FAIL] = "X-axis magnetometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_YMAGN_FAIL] = "Y-axis magnetometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_ZMAGN_FAIL] = "Z-axis magnetometer self-test failure",
|
||||
[ADIS16480_DIAG_STAT_BARO_FAIL] = "Barometer self-test failure",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16480_data = {
|
||||
.diag_stat_reg = ADIS16480_REG_DIAG_STS,
|
||||
.glob_cmd_reg = ADIS16480_REG_GLOB_CMD,
|
||||
.has_paging = true,
|
||||
|
||||
.read_delay = 5,
|
||||
.write_delay = 5,
|
||||
|
||||
.status_error_msgs = adis16480_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16480_DIAG_STAT_XGYRO_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_YGYRO_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_ZGYRO_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_XACCL_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_YACCL_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_ZACCL_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_XMAGN_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_YMAGN_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_ZMAGN_FAIL) |
|
||||
BIT(ADIS16480_DIAG_STAT_BARO_FAIL),
|
||||
|
||||
.enable_irq = adis16480_enable_irq,
|
||||
};
|
||||
|
||||
static int adis16480_config_irq_pin(struct device_node *of_node,
|
||||
struct adis16480 *st)
|
||||
{
|
||||
@ -1245,22 +1216,6 @@ static int adis16480_get_ext_clocks(struct adis16480 *st)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct adis_data *adis16480_adis_data_alloc(struct adis16480 *st,
|
||||
struct device *dev)
|
||||
{
|
||||
struct adis_data *data;
|
||||
|
||||
data = devm_kmalloc(dev, sizeof(struct adis_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memcpy(data, &adis16480_data, sizeof(*data));
|
||||
|
||||
data->timeouts = st->chip_info->timeouts;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static int adis16480_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
@ -1285,26 +1240,28 @@ static int adis16480_probe(struct spi_device *spi)
|
||||
indio_dev->info = &adis16480_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
adis16480_data = adis16480_adis_data_alloc(st, &spi->dev);
|
||||
if (IS_ERR(adis16480_data))
|
||||
return PTR_ERR(adis16480_data);
|
||||
adis16480_data = &st->chip_info->adis_data;
|
||||
|
||||
ret = adis_init(&st->adis, indio_dev, spi, adis16480_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis16480_config_irq_pin(spi->dev.of_node, st);
|
||||
ret = __adis_initial_startup(&st->adis);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis16480_config_irq_pin(spi->dev.of_node, st);
|
||||
if (ret)
|
||||
goto error_stop_device;
|
||||
|
||||
ret = adis16480_get_ext_clocks(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto error_stop_device;
|
||||
|
||||
if (!IS_ERR_OR_NULL(st->ext_clk)) {
|
||||
ret = adis16480_ext_clk_config(st, spi->dev.of_node, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto error_stop_device;
|
||||
|
||||
st->clk_freq = clk_get_rate(st->ext_clk);
|
||||
st->clk_freq *= 1000; /* micro */
|
||||
@ -1316,24 +1273,20 @@ static int adis16480_probe(struct spi_device *spi)
|
||||
if (ret)
|
||||
goto error_clk_disable_unprepare;
|
||||
|
||||
ret = adis16480_initial_setup(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_stop_device;
|
||||
goto error_cleanup_buffer;
|
||||
|
||||
adis16480_debugfs_init(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
error_stop_device:
|
||||
adis16480_stop_device(indio_dev);
|
||||
error_cleanup_buffer:
|
||||
adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
|
||||
error_clk_disable_unprepare:
|
||||
clk_disable_unprepare(st->ext_clk);
|
||||
error_stop_device:
|
||||
adis16480_stop_device(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,8 @@ int adis_update_scan_mode(struct iio_dev *indio_dev,
|
||||
if (j != scan_count)
|
||||
adis->xfer[j].cs_change = 1;
|
||||
adis->xfer[j].len = 2;
|
||||
adis->xfer[j].delay_usecs = adis->data->read_delay;
|
||||
adis->xfer[j].delay.value = adis->data->read_delay;
|
||||
adis->xfer[j].delay.unit = SPI_DELAY_UNIT_USECS;
|
||||
if (j < scan_count)
|
||||
adis->xfer[j].tx_buf = &tx[j];
|
||||
if (j >= 1)
|
||||
|
@ -15,9 +15,9 @@ config INV_MPU6050_I2C
|
||||
select INV_MPU6050_IIO
|
||||
select REGMAP_I2C
|
||||
help
|
||||
This driver supports the Invensense MPU6050/6500/6515,
|
||||
MPU9150/9250/9255 and ICM20608/20602 motion tracking devices
|
||||
over I2C.
|
||||
This driver supports the Invensense MPU6050/9150,
|
||||
MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690 and
|
||||
IAM20680 motion tracking devices over I2C.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050-i2c.
|
||||
|
||||
@ -27,8 +27,8 @@ config INV_MPU6050_SPI
|
||||
select INV_MPU6050_IIO
|
||||
select REGMAP_SPI
|
||||
help
|
||||
This driver supports the Invensense MPU6000/6500/6515,
|
||||
MPU9250/9255 and ICM20608/20602 motion tracking devices
|
||||
over SPI.
|
||||
This driver supports the Invensense MPU6000,
|
||||
MPU6500/6515/9250/9255, ICM20608/20609/20689, ICM20602/ICM20690 and
|
||||
IAM20680 motion tracking devices over SPI.
|
||||
This driver can be built as a module. The module will be called
|
||||
inv-mpu6050-spi.
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include "inv_mpu_iio.h"
|
||||
#include "inv_mpu_magn.h"
|
||||
|
||||
@ -99,9 +101,31 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = {
|
||||
};
|
||||
|
||||
static const struct inv_mpu6050_chip_config chip_config_6050 = {
|
||||
.clk = INV_CLK_INTERNAL,
|
||||
.fsr = INV_MPU6050_FSR_2000DPS,
|
||||
.lpf = INV_MPU6050_FILTER_20HZ,
|
||||
.divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE),
|
||||
.divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(50),
|
||||
.gyro_en = true,
|
||||
.accl_en = true,
|
||||
.temp_en = true,
|
||||
.magn_en = false,
|
||||
.gyro_fifo_enable = false,
|
||||
.accl_fifo_enable = false,
|
||||
.temp_fifo_enable = false,
|
||||
.magn_fifo_enable = false,
|
||||
.accl_fs = INV_MPU6050_FS_02G,
|
||||
.user_ctrl = 0,
|
||||
};
|
||||
|
||||
static const struct inv_mpu6050_chip_config chip_config_6500 = {
|
||||
.clk = INV_CLK_PLL,
|
||||
.fsr = INV_MPU6050_FSR_2000DPS,
|
||||
.lpf = INV_MPU6050_FILTER_20HZ,
|
||||
.divider = INV_MPU6050_FIFO_RATE_TO_DIVIDER(50),
|
||||
.gyro_en = true,
|
||||
.accl_en = true,
|
||||
.temp_en = true,
|
||||
.magn_en = false,
|
||||
.gyro_fifo_enable = false,
|
||||
.accl_fifo_enable = false,
|
||||
.temp_fifo_enable = false,
|
||||
@ -124,7 +148,7 @@ static const struct inv_mpu6050_hw hw_info[] = {
|
||||
.whoami = INV_MPU6500_WHOAMI_VALUE,
|
||||
.name = "MPU6500",
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6050,
|
||||
.config = &chip_config_6500,
|
||||
.fifo_size = 512,
|
||||
.temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
|
||||
},
|
||||
@ -132,7 +156,7 @@ static const struct inv_mpu6050_hw hw_info[] = {
|
||||
.whoami = INV_MPU6515_WHOAMI_VALUE,
|
||||
.name = "MPU6515",
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6050,
|
||||
.config = &chip_config_6500,
|
||||
.fifo_size = 512,
|
||||
.temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
|
||||
},
|
||||
@ -156,7 +180,7 @@ static const struct inv_mpu6050_hw hw_info[] = {
|
||||
.whoami = INV_MPU9250_WHOAMI_VALUE,
|
||||
.name = "MPU9250",
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6050,
|
||||
.config = &chip_config_6500,
|
||||
.fifo_size = 512,
|
||||
.temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
|
||||
},
|
||||
@ -164,7 +188,7 @@ static const struct inv_mpu6050_hw hw_info[] = {
|
||||
.whoami = INV_MPU9255_WHOAMI_VALUE,
|
||||
.name = "MPU9255",
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6050,
|
||||
.config = &chip_config_6500,
|
||||
.fifo_size = 512,
|
||||
.temp = {INV_MPU6500_TEMP_OFFSET, INV_MPU6500_TEMP_SCALE},
|
||||
},
|
||||
@ -172,104 +196,242 @@ static const struct inv_mpu6050_hw hw_info[] = {
|
||||
.whoami = INV_ICM20608_WHOAMI_VALUE,
|
||||
.name = "ICM20608",
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6050,
|
||||
.config = &chip_config_6500,
|
||||
.fifo_size = 512,
|
||||
.temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
|
||||
},
|
||||
{
|
||||
.whoami = INV_ICM20609_WHOAMI_VALUE,
|
||||
.name = "ICM20609",
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6500,
|
||||
.fifo_size = 4 * 1024,
|
||||
.temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
|
||||
},
|
||||
{
|
||||
.whoami = INV_ICM20689_WHOAMI_VALUE,
|
||||
.name = "ICM20689",
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6500,
|
||||
.fifo_size = 4 * 1024,
|
||||
.temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
|
||||
},
|
||||
{
|
||||
.whoami = INV_ICM20602_WHOAMI_VALUE,
|
||||
.name = "ICM20602",
|
||||
.reg = ®_set_icm20602,
|
||||
.config = &chip_config_6050,
|
||||
.config = &chip_config_6500,
|
||||
.fifo_size = 1008,
|
||||
.temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
|
||||
},
|
||||
{
|
||||
.whoami = INV_ICM20690_WHOAMI_VALUE,
|
||||
.name = "ICM20690",
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6500,
|
||||
.fifo_size = 1024,
|
||||
.temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
|
||||
},
|
||||
{
|
||||
.whoami = INV_IAM20680_WHOAMI_VALUE,
|
||||
.name = "IAM20680",
|
||||
.reg = ®_set_6500,
|
||||
.config = &chip_config_6500,
|
||||
.fifo_size = 512,
|
||||
.temp = {INV_ICM20608_TEMP_OFFSET, INV_ICM20608_TEMP_SCALE},
|
||||
},
|
||||
};
|
||||
|
||||
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
|
||||
static int inv_mpu6050_pwr_mgmt_1_write(struct inv_mpu6050_state *st, bool sleep,
|
||||
int clock, int temp_dis)
|
||||
{
|
||||
unsigned int d, mgmt_1;
|
||||
int result;
|
||||
/*
|
||||
* switch clock needs to be careful. Only when gyro is on, can
|
||||
* clock source be switched to gyro. Otherwise, it must be set to
|
||||
* internal clock
|
||||
*/
|
||||
if (mask == INV_MPU6050_BIT_PWR_GYRO_STBY) {
|
||||
result = regmap_read(st->map, st->reg->pwr_mgmt_1, &mgmt_1);
|
||||
if (result)
|
||||
return result;
|
||||
u8 val;
|
||||
|
||||
mgmt_1 &= ~INV_MPU6050_BIT_CLK_MASK;
|
||||
if (clock < 0)
|
||||
clock = st->chip_config.clk;
|
||||
if (temp_dis < 0)
|
||||
temp_dis = !st->chip_config.temp_en;
|
||||
|
||||
val = clock & INV_MPU6050_BIT_CLK_MASK;
|
||||
if (temp_dis)
|
||||
val |= INV_MPU6050_BIT_TEMP_DIS;
|
||||
if (sleep)
|
||||
val |= INV_MPU6050_BIT_SLEEP;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int inv_mpu6050_clock_switch(struct inv_mpu6050_state *st,
|
||||
unsigned int clock)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (st->chip_type) {
|
||||
case INV_MPU6050:
|
||||
case INV_MPU6000:
|
||||
case INV_MPU9150:
|
||||
/* old chips: switch clock manually */
|
||||
ret = inv_mpu6050_pwr_mgmt_1_write(st, false, clock, -1);
|
||||
if (ret)
|
||||
return ret;
|
||||
st->chip_config.clk = clock;
|
||||
break;
|
||||
default:
|
||||
/* automatic clock switching, nothing to do */
|
||||
break;
|
||||
}
|
||||
|
||||
if ((mask == INV_MPU6050_BIT_PWR_GYRO_STBY) && (!en)) {
|
||||
/*
|
||||
* turning off gyro requires switch to internal clock first.
|
||||
* Then turn off gyro engine
|
||||
*/
|
||||
mgmt_1 |= INV_CLK_INTERNAL;
|
||||
result = regmap_write(st->map, st->reg->pwr_mgmt_1, mgmt_1);
|
||||
if (result)
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en,
|
||||
unsigned int mask)
|
||||
{
|
||||
unsigned int sleep;
|
||||
u8 pwr_mgmt2, user_ctrl;
|
||||
int ret;
|
||||
|
||||
/* delete useless requests */
|
||||
if (mask & INV_MPU6050_SENSOR_ACCL && en == st->chip_config.accl_en)
|
||||
mask &= ~INV_MPU6050_SENSOR_ACCL;
|
||||
if (mask & INV_MPU6050_SENSOR_GYRO && en == st->chip_config.gyro_en)
|
||||
mask &= ~INV_MPU6050_SENSOR_GYRO;
|
||||
if (mask & INV_MPU6050_SENSOR_TEMP && en == st->chip_config.temp_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 == 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);
|
||||
if (ret)
|
||||
return ret;
|
||||
st->chip_config.temp_en = en;
|
||||
}
|
||||
|
||||
result = regmap_read(st->map, st->reg->pwr_mgmt_2, &d);
|
||||
if (result)
|
||||
return result;
|
||||
if (en)
|
||||
d &= ~mask;
|
||||
else
|
||||
d |= mask;
|
||||
result = regmap_write(st->map, st->reg->pwr_mgmt_2, d);
|
||||
if (result)
|
||||
return result;
|
||||
/* update user_crtl for driving magnetometer */
|
||||
if (mask & INV_MPU6050_SENSOR_MAGN) {
|
||||
user_ctrl = st->chip_config.user_ctrl;
|
||||
if (en)
|
||||
user_ctrl |= INV_MPU6050_BIT_I2C_MST_EN;
|
||||
else
|
||||
user_ctrl &= ~INV_MPU6050_BIT_I2C_MST_EN;
|
||||
ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
st->chip_config.user_ctrl = user_ctrl;
|
||||
st->chip_config.magn_en = en;
|
||||
}
|
||||
|
||||
if (en) {
|
||||
/* Wait for output to stabilize */
|
||||
msleep(INV_MPU6050_TEMP_UP_TIME);
|
||||
if (mask == INV_MPU6050_BIT_PWR_GYRO_STBY) {
|
||||
/* switch internal clock to PLL */
|
||||
mgmt_1 |= INV_CLK_PLL;
|
||||
result = regmap_write(st->map,
|
||||
st->reg->pwr_mgmt_1, mgmt_1);
|
||||
if (result)
|
||||
return result;
|
||||
/* manage accel & gyro engines */
|
||||
if (mask & (INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_GYRO)) {
|
||||
/* compute power management 2 current value */
|
||||
pwr_mgmt2 = 0;
|
||||
if (!st->chip_config.accl_en)
|
||||
pwr_mgmt2 |= INV_MPU6050_BIT_PWR_ACCL_STBY;
|
||||
if (!st->chip_config.gyro_en)
|
||||
pwr_mgmt2 |= INV_MPU6050_BIT_PWR_GYRO_STBY;
|
||||
|
||||
/* update to new requested value */
|
||||
if (mask & INV_MPU6050_SENSOR_ACCL) {
|
||||
if (en)
|
||||
pwr_mgmt2 &= ~INV_MPU6050_BIT_PWR_ACCL_STBY;
|
||||
else
|
||||
pwr_mgmt2 |= INV_MPU6050_BIT_PWR_ACCL_STBY;
|
||||
}
|
||||
if (mask & INV_MPU6050_SENSOR_GYRO) {
|
||||
if (en)
|
||||
pwr_mgmt2 &= ~INV_MPU6050_BIT_PWR_GYRO_STBY;
|
||||
else
|
||||
pwr_mgmt2 |= INV_MPU6050_BIT_PWR_GYRO_STBY;
|
||||
}
|
||||
|
||||
/* switch clock to internal when turning gyro off */
|
||||
if (mask & INV_MPU6050_SENSOR_GYRO && !en) {
|
||||
ret = inv_mpu6050_clock_switch(st, INV_CLK_INTERNAL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* update sensors engine */
|
||||
dev_dbg(regmap_get_device(st->map), "pwr_mgmt_2: 0x%x\n",
|
||||
pwr_mgmt2);
|
||||
ret = regmap_write(st->map, st->reg->pwr_mgmt_2, pwr_mgmt2);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (mask & INV_MPU6050_SENSOR_ACCL)
|
||||
st->chip_config.accl_en = en;
|
||||
if (mask & INV_MPU6050_SENSOR_GYRO)
|
||||
st->chip_config.gyro_en = en;
|
||||
|
||||
/* compute required time to have sensors stabilized */
|
||||
sleep = 0;
|
||||
if (en) {
|
||||
if (mask & INV_MPU6050_SENSOR_ACCL) {
|
||||
if (sleep < INV_MPU6050_ACCEL_UP_TIME)
|
||||
sleep = INV_MPU6050_ACCEL_UP_TIME;
|
||||
}
|
||||
if (mask & INV_MPU6050_SENSOR_GYRO) {
|
||||
if (sleep < INV_MPU6050_GYRO_UP_TIME)
|
||||
sleep = INV_MPU6050_GYRO_UP_TIME;
|
||||
}
|
||||
} else {
|
||||
if (mask & INV_MPU6050_SENSOR_GYRO) {
|
||||
if (sleep < INV_MPU6050_GYRO_DOWN_TIME)
|
||||
sleep = INV_MPU6050_GYRO_DOWN_TIME;
|
||||
}
|
||||
}
|
||||
if (sleep)
|
||||
msleep(sleep);
|
||||
|
||||
/* switch clock to PLL when turning gyro on */
|
||||
if (mask & INV_MPU6050_SENSOR_GYRO && en) {
|
||||
ret = inv_mpu6050_clock_switch(st, INV_CLK_PLL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on)
|
||||
static int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st,
|
||||
bool power_on)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (power_on) {
|
||||
if (!st->powerup_count) {
|
||||
result = regmap_write(st->map, st->reg->pwr_mgmt_1, 0);
|
||||
if (result)
|
||||
return result;
|
||||
usleep_range(INV_MPU6050_REG_UP_TIME_MIN,
|
||||
INV_MPU6050_REG_UP_TIME_MAX);
|
||||
}
|
||||
st->powerup_count++;
|
||||
} else {
|
||||
if (st->powerup_count == 1) {
|
||||
result = regmap_write(st->map, st->reg->pwr_mgmt_1,
|
||||
INV_MPU6050_BIT_SLEEP);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
st->powerup_count--;
|
||||
}
|
||||
result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, -1, -1);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
dev_dbg(regmap_get_device(st->map), "set power %d, count=%u\n",
|
||||
power_on, st->powerup_count);
|
||||
if (power_on)
|
||||
usleep_range(INV_MPU6050_REG_UP_TIME_MIN,
|
||||
INV_MPU6050_REG_UP_TIME_MAX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(inv_mpu6050_set_power_itg);
|
||||
|
||||
static int inv_mpu6050_set_gyro_fsr(struct inv_mpu6050_state *st,
|
||||
enum inv_mpu6050_fsr_e val)
|
||||
{
|
||||
unsigned int gyro_shift;
|
||||
u8 data;
|
||||
|
||||
switch (st->chip_type) {
|
||||
case INV_ICM20690:
|
||||
gyro_shift = INV_ICM20690_GYRO_CONFIG_FSR_SHIFT;
|
||||
break;
|
||||
default:
|
||||
gyro_shift = INV_MPU6050_GYRO_CONFIG_FSR_SHIFT;
|
||||
break;
|
||||
}
|
||||
|
||||
data = val << gyro_shift;
|
||||
return regmap_write(st->map, st->reg->gyro_config, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent
|
||||
@ -286,20 +448,23 @@ static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st,
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/* set accel lpf */
|
||||
switch (st->chip_type) {
|
||||
case INV_MPU6050:
|
||||
case INV_MPU6000:
|
||||
case INV_MPU9150:
|
||||
/* old chips, nothing to do */
|
||||
result = 0;
|
||||
return 0;
|
||||
case INV_ICM20689:
|
||||
case INV_ICM20690:
|
||||
/* set FIFO size to maximum value */
|
||||
val |= INV_ICM20689_BITS_FIFO_SIZE_MAX;
|
||||
break;
|
||||
default:
|
||||
/* set accel lpf */
|
||||
result = regmap_write(st->map, st->reg->accel_lpf, val);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
return regmap_write(st->map, st->reg->accel_lpf, val);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -317,35 +482,28 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
|
||||
u8 d;
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
result = inv_mpu6050_set_gyro_fsr(st, st->chip_config.fsr);
|
||||
if (result)
|
||||
return result;
|
||||
d = (INV_MPU6050_FSR_2000DPS << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
|
||||
result = regmap_write(st->map, st->reg->gyro_config, d);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
|
||||
result = inv_mpu6050_set_lpf_regs(st, INV_MPU6050_FILTER_20HZ);
|
||||
result = inv_mpu6050_set_lpf_regs(st, st->chip_config.lpf);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
return result;
|
||||
|
||||
d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU6050_INIT_FIFO_RATE);
|
||||
d = st->chip_config.divider;
|
||||
result = regmap_write(st->map, st->reg->sample_rate_div, d);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
return result;
|
||||
|
||||
d = (INV_MPU6050_FS_02G << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
|
||||
d = (st->chip_config.accl_fs << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
|
||||
result = regmap_write(st->map, st->reg->accl_config, d);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
return result;
|
||||
|
||||
result = regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
memcpy(&st->chip_config, hw_info[st->chip_type].config,
|
||||
sizeof(struct inv_mpu6050_chip_config));
|
||||
|
||||
/*
|
||||
* Internal chip period is 1ms (1kHz).
|
||||
* Let's use at the beginning the theorical value before measuring
|
||||
@ -356,13 +514,9 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
|
||||
/* magn chip init, noop if not present in the chip */
|
||||
result = inv_mpu_magn_probe(st);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
return result;
|
||||
|
||||
return inv_mpu6050_set_power_itg(st, false);
|
||||
|
||||
error_power_off:
|
||||
inv_mpu6050_set_power_itg(st, false);
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int inv_mpu6050_sensor_set(struct inv_mpu6050_state *st, int reg,
|
||||
@ -399,45 +553,85 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev,
|
||||
int *val)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
struct device *pdev = regmap_get_device(st->map);
|
||||
unsigned int freq_hz, period_us, min_sleep_us, max_sleep_us;
|
||||
int result;
|
||||
int ret;
|
||||
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
/* compute sample period */
|
||||
freq_hz = INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
|
||||
period_us = 1000000 / freq_hz;
|
||||
|
||||
result = pm_runtime_get_sync(pdev);
|
||||
if (result < 0) {
|
||||
pm_runtime_put_noidle(pdev);
|
||||
return result;
|
||||
}
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
if (!st->chip_config.gyro_en) {
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_SENSOR_GYRO);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
/* need to wait 2 periods to have first valid sample */
|
||||
min_sleep_us = 2 * period_us;
|
||||
max_sleep_us = 2 * (period_us + period_us / 2);
|
||||
usleep_range(min_sleep_us, max_sleep_us);
|
||||
}
|
||||
ret = inv_mpu6050_sensor_show(st, st->reg->raw_gyro,
|
||||
chan->channel2, val);
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
break;
|
||||
case IIO_ACCEL:
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
if (!st->chip_config.accl_en) {
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_SENSOR_ACCL);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
/* wait 1 period for first sample availability */
|
||||
min_sleep_us = period_us;
|
||||
max_sleep_us = period_us + period_us / 2;
|
||||
usleep_range(min_sleep_us, max_sleep_us);
|
||||
}
|
||||
ret = inv_mpu6050_sensor_show(st, st->reg->raw_accl,
|
||||
chan->channel2, val);
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
/* wait for stablization */
|
||||
msleep(INV_MPU6050_SENSOR_UP_TIME);
|
||||
/* temperature sensor work only with accel and/or gyro */
|
||||
if (!st->chip_config.accl_en && !st->chip_config.gyro_en) {
|
||||
result = -EBUSY;
|
||||
goto error_power_off;
|
||||
}
|
||||
if (!st->chip_config.temp_en) {
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_SENSOR_TEMP);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
/* wait 1 period for first sample availability */
|
||||
min_sleep_us = period_us;
|
||||
max_sleep_us = period_us + period_us / 2;
|
||||
usleep_range(min_sleep_us, max_sleep_us);
|
||||
}
|
||||
ret = inv_mpu6050_sensor_show(st, st->reg->temperature,
|
||||
IIO_MOD_X, val);
|
||||
break;
|
||||
case IIO_MAGN:
|
||||
if (!st->chip_config.magn_en) {
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_SENSOR_MAGN);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
/* frequency is limited for magnetometer */
|
||||
if (freq_hz > INV_MPU_MAGN_FREQ_HZ_MAX) {
|
||||
freq_hz = INV_MPU_MAGN_FREQ_HZ_MAX;
|
||||
period_us = 1000000 / freq_hz;
|
||||
}
|
||||
/* need to wait 2 periods to have first valid sample */
|
||||
min_sleep_us = 2 * period_us;
|
||||
max_sleep_us = 2 * (period_us + period_us / 2);
|
||||
usleep_range(min_sleep_us, max_sleep_us);
|
||||
}
|
||||
ret = inv_mpu_magn_read(st, chan->channel2, val);
|
||||
break;
|
||||
default:
|
||||
@ -445,14 +639,13 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev,
|
||||
break;
|
||||
}
|
||||
|
||||
result = inv_mpu6050_set_power_itg(st, false);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
pm_runtime_mark_last_busy(pdev);
|
||||
pm_runtime_put_autosuspend(pdev);
|
||||
|
||||
return ret;
|
||||
|
||||
error_power_off:
|
||||
inv_mpu6050_set_power_itg(st, false);
|
||||
pm_runtime_put_autosuspend(pdev);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -533,12 +726,10 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev,
|
||||
static int inv_mpu6050_write_gyro_scale(struct inv_mpu6050_state *st, int val)
|
||||
{
|
||||
int result, i;
|
||||
u8 d;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(gyro_scale_6050); ++i) {
|
||||
if (gyro_scale_6050[i] == val) {
|
||||
d = (i << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
|
||||
result = regmap_write(st->map, st->reg->gyro_config, d);
|
||||
result = inv_mpu6050_set_gyro_fsr(st, i);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
@ -593,6 +784,7 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
struct device *pdev = regmap_get_device(st->map);
|
||||
int result;
|
||||
|
||||
/*
|
||||
@ -604,9 +796,11 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
|
||||
return result;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
result = pm_runtime_get_sync(pdev);
|
||||
if (result < 0) {
|
||||
pm_runtime_put_noidle(pdev);
|
||||
goto error_write_raw_unlock;
|
||||
}
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
@ -644,7 +838,8 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
|
||||
break;
|
||||
}
|
||||
|
||||
result |= inv_mpu6050_set_power_itg(st, false);
|
||||
pm_runtime_mark_last_busy(pdev);
|
||||
pm_runtime_put_autosuspend(pdev);
|
||||
error_write_raw_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
@ -655,30 +850,32 @@ error_write_raw_unlock:
|
||||
/**
|
||||
* inv_mpu6050_set_lpf() - set low pass filer based on fifo rate.
|
||||
*
|
||||
* Based on the Nyquist principle, the sampling rate must
|
||||
* exceed twice of the bandwidth of the signal, or there
|
||||
* would be alising. This function basically search for the
|
||||
* correct low pass parameters based on the fifo rate, e.g,
|
||||
* sampling frequency.
|
||||
* Based on the Nyquist principle, the bandwidth of the low
|
||||
* pass filter must not exceed the signal sampling rate divided
|
||||
* by 2, or there would be aliasing.
|
||||
* This function basically search for the correct low pass
|
||||
* parameters based on the fifo rate, e.g, sampling frequency.
|
||||
*
|
||||
* lpf is set automatically when setting sampling rate to avoid any aliases.
|
||||
*/
|
||||
static int inv_mpu6050_set_lpf(struct inv_mpu6050_state *st, int rate)
|
||||
{
|
||||
static const int hz[] = {188, 98, 42, 20, 10, 5};
|
||||
static const int hz[] = {400, 200, 90, 40, 20, 10};
|
||||
static const int d[] = {
|
||||
INV_MPU6050_FILTER_188HZ, INV_MPU6050_FILTER_98HZ,
|
||||
INV_MPU6050_FILTER_42HZ, INV_MPU6050_FILTER_20HZ,
|
||||
INV_MPU6050_FILTER_200HZ, INV_MPU6050_FILTER_100HZ,
|
||||
INV_MPU6050_FILTER_45HZ, INV_MPU6050_FILTER_20HZ,
|
||||
INV_MPU6050_FILTER_10HZ, INV_MPU6050_FILTER_5HZ
|
||||
};
|
||||
int i, h, result;
|
||||
int i, result;
|
||||
u8 data;
|
||||
|
||||
h = (rate >> 1);
|
||||
i = 0;
|
||||
while ((h < hz[i]) && (i < ARRAY_SIZE(d) - 1))
|
||||
i++;
|
||||
data = d[i];
|
||||
data = INV_MPU6050_FILTER_5HZ;
|
||||
for (i = 0; i < ARRAY_SIZE(hz); ++i) {
|
||||
if (rate >= hz[i]) {
|
||||
data = d[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
result = inv_mpu6050_set_lpf_regs(st, data);
|
||||
if (result)
|
||||
return result;
|
||||
@ -699,6 +896,7 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
|
||||
int result;
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
struct device *pdev = regmap_get_device(st->map);
|
||||
|
||||
if (kstrtoint(buf, 10, &fifo_rate))
|
||||
return -EINVAL;
|
||||
@ -706,10 +904,6 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
|
||||
fifo_rate > INV_MPU6050_MAX_FIFO_RATE)
|
||||
return -EINVAL;
|
||||
|
||||
result = iio_device_claim_direct_mode(indio_dev);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/* compute the chip sample rate divider */
|
||||
d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(fifo_rate);
|
||||
/* compute back the fifo rate to handle truncation cases */
|
||||
@ -720,9 +914,11 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
|
||||
result = 0;
|
||||
goto fifo_rate_fail_unlock;
|
||||
}
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
result = pm_runtime_get_sync(pdev);
|
||||
if (result < 0) {
|
||||
pm_runtime_put_noidle(pdev);
|
||||
goto fifo_rate_fail_unlock;
|
||||
}
|
||||
|
||||
result = regmap_write(st->map, st->reg->sample_rate_div, d);
|
||||
if (result)
|
||||
@ -738,11 +934,11 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr,
|
||||
if (result)
|
||||
goto fifo_rate_fail_power_off;
|
||||
|
||||
pm_runtime_mark_last_busy(pdev);
|
||||
fifo_rate_fail_power_off:
|
||||
result |= inv_mpu6050_set_power_itg(st, false);
|
||||
pm_runtime_put_autosuspend(pdev);
|
||||
fifo_rate_fail_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
@ -1066,11 +1262,13 @@ static const struct iio_info mpu_info = {
|
||||
static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
|
||||
{
|
||||
int result;
|
||||
unsigned int regval;
|
||||
unsigned int regval, mask;
|
||||
int i;
|
||||
|
||||
st->hw = &hw_info[st->chip_type];
|
||||
st->reg = hw_info[st->chip_type].reg;
|
||||
memcpy(&st->chip_config, hw_info[st->chip_type].config,
|
||||
sizeof(st->chip_config));
|
||||
|
||||
/* check chip self-identification */
|
||||
result = regmap_read(st->map, INV_MPU6050_REG_WHOAMI, ®val);
|
||||
@ -1102,6 +1300,24 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
|
||||
if (result)
|
||||
return result;
|
||||
msleep(INV_MPU6050_POWER_UP_TIME);
|
||||
switch (st->chip_type) {
|
||||
case INV_MPU6000:
|
||||
case INV_MPU6500:
|
||||
case INV_MPU6515:
|
||||
case INV_MPU9250:
|
||||
case INV_MPU9255:
|
||||
/* reset signal path (required for spi connection) */
|
||||
regval = INV_MPU6050_BIT_TEMP_RST | INV_MPU6050_BIT_ACCEL_RST |
|
||||
INV_MPU6050_BIT_GYRO_RST;
|
||||
result = regmap_write(st->map, INV_MPU6050_REG_SIGNAL_PATH_RESET,
|
||||
regval);
|
||||
if (result)
|
||||
return result;
|
||||
msleep(INV_MPU6050_POWER_UP_TIME);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn power on. After reset, the sleep bit could be on
|
||||
@ -1112,17 +1328,13 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st)
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
mask = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_GYRO |
|
||||
INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN;
|
||||
result = inv_mpu6050_switch_engine(st, false, mask);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
|
||||
return inv_mpu6050_set_power_itg(st, false);
|
||||
return 0;
|
||||
|
||||
error_power_off:
|
||||
inv_mpu6050_set_power_itg(st, false);
|
||||
@ -1139,7 +1351,7 @@ static int inv_mpu_core_enable_regulator_vddio(struct inv_mpu6050_state *st)
|
||||
"Failed to enable vddio regulator: %d\n", result);
|
||||
} else {
|
||||
/* Give the device a little bit of time to start up. */
|
||||
usleep_range(35000, 70000);
|
||||
usleep_range(3000, 5000);
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -1170,6 +1382,14 @@ static void inv_mpu_core_disable_regulator_action(void *_data)
|
||||
inv_mpu_core_disable_regulator_vddio(st);
|
||||
}
|
||||
|
||||
static void inv_mpu_pm_disable(void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
|
||||
pm_runtime_put_sync_suspend(dev);
|
||||
pm_runtime_disable(dev);
|
||||
}
|
||||
|
||||
int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type)
|
||||
{
|
||||
@ -1194,7 +1414,6 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
st = iio_priv(indio_dev);
|
||||
mutex_init(&st->lock);
|
||||
st->chip_type = chip_type;
|
||||
st->powerup_count = 0;
|
||||
st->irq = irq;
|
||||
st->map = regmap;
|
||||
|
||||
@ -1259,6 +1478,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
dev_err(dev, "Failed to enable vdd regulator: %d\n", result);
|
||||
return result;
|
||||
}
|
||||
msleep(INV_MPU6050_POWER_UP_TIME);
|
||||
|
||||
result = inv_mpu_core_enable_regulator_vddio(st);
|
||||
if (result) {
|
||||
@ -1287,7 +1507,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
result = inv_mpu6050_init_config(indio_dev);
|
||||
if (result) {
|
||||
dev_err(dev, "Could not initialize device.\n");
|
||||
return result;
|
||||
goto error_power_off;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
@ -1299,8 +1519,24 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
indio_dev->name = dev_name(dev);
|
||||
|
||||
/* requires parent device set in indio_dev */
|
||||
if (inv_mpu_bus_setup)
|
||||
inv_mpu_bus_setup(indio_dev);
|
||||
if (inv_mpu_bus_setup) {
|
||||
result = inv_mpu_bus_setup(indio_dev);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
}
|
||||
|
||||
/* chip init is done, turning on runtime power management */
|
||||
result = pm_runtime_set_active(dev);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
pm_runtime_get_noresume(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, INV_MPU6050_SUSPEND_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_put(dev);
|
||||
result = devm_add_action_or_reset(dev, inv_mpu_pm_disable, dev);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
switch (chip_type) {
|
||||
case INV_MPU9150:
|
||||
@ -1359,14 +1595,17 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_power_off:
|
||||
inv_mpu6050_set_power_itg(st, false);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(inv_mpu_core_probe);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int inv_mpu_resume(struct device *dev)
|
||||
static int __maybe_unused inv_mpu_resume(struct device *dev)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
int result;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
@ -1375,27 +1614,101 @@ static int inv_mpu_resume(struct device *dev)
|
||||
goto out_unlock;
|
||||
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
goto out_unlock;
|
||||
|
||||
result = inv_mpu6050_switch_engine(st, true, st->suspended_sensors);
|
||||
if (result)
|
||||
goto out_unlock;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
result = inv_mpu6050_prepare_fifo(st, true);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int inv_mpu_suspend(struct device *dev)
|
||||
static int __maybe_unused inv_mpu_suspend(struct device *dev)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
int result;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
result = inv_mpu6050_prepare_fifo(st, false);
|
||||
if (result)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
st->suspended_sensors = 0;
|
||||
if (st->chip_config.accl_en)
|
||||
st->suspended_sensors |= INV_MPU6050_SENSOR_ACCL;
|
||||
if (st->chip_config.gyro_en)
|
||||
st->suspended_sensors |= INV_MPU6050_SENSOR_GYRO;
|
||||
if (st->chip_config.temp_en)
|
||||
st->suspended_sensors |= INV_MPU6050_SENSOR_TEMP;
|
||||
if (st->chip_config.magn_en)
|
||||
st->suspended_sensors |= INV_MPU6050_SENSOR_MAGN;
|
||||
result = inv_mpu6050_switch_engine(st, false, st->suspended_sensors);
|
||||
if (result)
|
||||
goto out_unlock;
|
||||
|
||||
result = inv_mpu6050_set_power_itg(st, false);
|
||||
if (result)
|
||||
goto out_unlock;
|
||||
|
||||
inv_mpu_core_disable_regulator_vddio(st);
|
||||
out_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
SIMPLE_DEV_PM_OPS(inv_mpu_pmops, inv_mpu_suspend, inv_mpu_resume);
|
||||
static int __maybe_unused inv_mpu_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
|
||||
unsigned int sensors;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
sensors = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_GYRO |
|
||||
INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN;
|
||||
ret = inv_mpu6050_switch_engine(st, false, sensors);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
ret = inv_mpu6050_set_power_itg(st, false);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
inv_mpu_core_disable_regulator_vddio(st);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused inv_mpu_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
|
||||
int ret;
|
||||
|
||||
ret = inv_mpu_core_enable_regulator_vddio(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return inv_mpu6050_set_power_itg(st, true);
|
||||
}
|
||||
|
||||
const struct dev_pm_ops inv_mpu_pmops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(inv_mpu_suspend, inv_mpu_resume)
|
||||
SET_RUNTIME_PM_OPS(inv_mpu_runtime_suspend, inv_mpu_runtime_resume, NULL)
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(inv_mpu_pmops);
|
||||
|
||||
MODULE_AUTHOR("Invensense Corporation");
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/property.h>
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
static const struct regmap_config inv_mpu_regmap_config = {
|
||||
@ -19,62 +20,19 @@ static const struct regmap_config inv_mpu_regmap_config = {
|
||||
|
||||
static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_mux_priv(muxc);
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
ret = inv_mpu6050_set_power_itg(st, true);
|
||||
if (ret)
|
||||
goto error_unlock;
|
||||
|
||||
ret = regmap_write(st->map, st->reg->int_pin_cfg,
|
||||
st->irq_mask | INV_MPU6050_BIT_BYPASS_EN);
|
||||
|
||||
error_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_mux_priv(muxc);
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
/* It doesn't really matter if any of the calls fail */
|
||||
regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask);
|
||||
inv_mpu6050_set_power_itg(st, false);
|
||||
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *inv_mpu_match_acpi_device(struct device *dev,
|
||||
enum inv_devices *chip_id)
|
||||
{
|
||||
const struct acpi_device_id *id;
|
||||
|
||||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||
if (!id)
|
||||
return NULL;
|
||||
|
||||
*chip_id = (int)id->driver_data;
|
||||
|
||||
return dev_name(dev);
|
||||
}
|
||||
|
||||
static bool inv_mpu_i2c_aux_bus(struct device *dev)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(dev_get_drvdata(dev));
|
||||
|
||||
switch (st->chip_type) {
|
||||
case INV_ICM20608:
|
||||
case INV_ICM20609:
|
||||
case INV_ICM20689:
|
||||
case INV_ICM20602:
|
||||
case INV_IAM20680:
|
||||
/* no i2c auxiliary bus on the chip */
|
||||
return false;
|
||||
case INV_MPU9150:
|
||||
@ -89,19 +47,20 @@ static bool inv_mpu_i2c_aux_bus(struct device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* MPU9xxx magnetometer support requires to disable i2c auxiliary bus support.
|
||||
* To ensure backward compatibility with existing setups, do not disable
|
||||
* i2c auxiliary bus if it used.
|
||||
* Check for i2c-gate node in devicetree and set magnetometer disabled.
|
||||
* Only MPU6500 is supported by ACPI, no need to check.
|
||||
*/
|
||||
static int inv_mpu_magn_disable(struct iio_dev *indio_dev)
|
||||
static int inv_mpu_i2c_aux_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
struct device *dev = indio_dev->dev.parent;
|
||||
struct device_node *mux_node;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* MPU9xxx magnetometer support requires to disable i2c auxiliary bus.
|
||||
* To ensure backward compatibility with existing setups, do not disable
|
||||
* i2c auxiliary bus if it used.
|
||||
* Check for i2c-gate node in devicetree and set magnetometer disabled.
|
||||
* Only MPU6500 is supported by ACPI, no need to check.
|
||||
*/
|
||||
switch (st->chip_type) {
|
||||
case INV_MPU9150:
|
||||
case INV_MPU9250:
|
||||
@ -117,6 +76,14 @@ static int inv_mpu_magn_disable(struct iio_dev *indio_dev)
|
||||
break;
|
||||
}
|
||||
|
||||
/* enable i2c bypass when using i2c auxiliary bus */
|
||||
if (inv_mpu_i2c_aux_bus(dev)) {
|
||||
ret = regmap_write(st->map, st->reg->int_pin_cfg,
|
||||
st->irq_mask | INV_MPU6050_BIT_BYPASS_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -130,6 +97,7 @@ static int inv_mpu_magn_disable(struct iio_dev *indio_dev)
|
||||
static int inv_mpu_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const void *match;
|
||||
struct inv_mpu6050_state *st;
|
||||
int result;
|
||||
enum inv_devices chip_type;
|
||||
@ -140,18 +108,14 @@ static int inv_mpu_probe(struct i2c_client *client,
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (client->dev.of_node) {
|
||||
chip_type = (enum inv_devices)
|
||||
of_device_get_match_data(&client->dev);
|
||||
match = device_get_match_data(&client->dev);
|
||||
if (match) {
|
||||
chip_type = (enum inv_devices)match;
|
||||
name = client->name;
|
||||
} else if (id) {
|
||||
chip_type = (enum inv_devices)
|
||||
id->driver_data;
|
||||
name = id->name;
|
||||
} else if (ACPI_HANDLE(&client->dev)) {
|
||||
name = inv_mpu_match_acpi_device(&client->dev, &chip_type);
|
||||
if (!name)
|
||||
return -ENODEV;
|
||||
} else {
|
||||
return -ENOSYS;
|
||||
}
|
||||
@ -164,7 +128,7 @@ static int inv_mpu_probe(struct i2c_client *client,
|
||||
}
|
||||
|
||||
result = inv_mpu_core_probe(regmap, client->irq, name,
|
||||
inv_mpu_magn_disable, chip_type);
|
||||
inv_mpu_i2c_aux_setup, chip_type);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
@ -173,8 +137,7 @@ static int inv_mpu_probe(struct i2c_client *client,
|
||||
/* declare i2c auxiliary bus */
|
||||
st->muxc = i2c_mux_alloc(client->adapter, &client->dev,
|
||||
1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
|
||||
inv_mpu6050_select_bypass,
|
||||
inv_mpu6050_deselect_bypass);
|
||||
inv_mpu6050_select_bypass, NULL);
|
||||
if (!st->muxc)
|
||||
return -ENOMEM;
|
||||
st->muxc->priv = dev_get_drvdata(&client->dev);
|
||||
@ -218,7 +181,11 @@ static const struct i2c_device_id inv_mpu_id[] = {
|
||||
{"mpu9250", INV_MPU9250},
|
||||
{"mpu9255", INV_MPU9255},
|
||||
{"icm20608", INV_ICM20608},
|
||||
{"icm20609", INV_ICM20609},
|
||||
{"icm20689", INV_ICM20689},
|
||||
{"icm20602", INV_ICM20602},
|
||||
{"icm20690", INV_ICM20690},
|
||||
{"iam20680", INV_IAM20680},
|
||||
{}
|
||||
};
|
||||
|
||||
@ -253,10 +220,26 @@ static const struct of_device_id inv_of_match[] = {
|
||||
.compatible = "invensense,icm20608",
|
||||
.data = (void *)INV_ICM20608
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,icm20609",
|
||||
.data = (void *)INV_ICM20609
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,icm20689",
|
||||
.data = (void *)INV_ICM20689
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,icm20602",
|
||||
.data = (void *)INV_ICM20602
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,icm20690",
|
||||
.data = (void *)INV_ICM20690
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,iam20680",
|
||||
.data = (void *)INV_IAM20680
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, inv_of_match);
|
||||
|
@ -75,15 +75,30 @@ enum inv_devices {
|
||||
INV_MPU9250,
|
||||
INV_MPU9255,
|
||||
INV_ICM20608,
|
||||
INV_ICM20609,
|
||||
INV_ICM20689,
|
||||
INV_ICM20602,
|
||||
INV_ICM20690,
|
||||
INV_IAM20680,
|
||||
INV_NUM_PARTS
|
||||
};
|
||||
|
||||
/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer */
|
||||
#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)
|
||||
|
||||
/**
|
||||
* struct inv_mpu6050_chip_config - Cached chip configuration data.
|
||||
* @clk: selected chip clock
|
||||
* @fsr: Full scale range.
|
||||
* @lpf: Digital low pass filter frequency.
|
||||
* @accl_fs: accel full scale range.
|
||||
* @accl_en: accel engine enabled
|
||||
* @gyro_en: gyro engine enabled
|
||||
* @temp_en: temperature sensor enabled
|
||||
* @magn_en: magn engine (i2c master) enabled
|
||||
* @accl_fifo_enable: enable accel data output
|
||||
* @gyro_fifo_enable: enable gyro data output
|
||||
* @temp_fifo_enable: enable temp data output
|
||||
@ -91,9 +106,14 @@ enum inv_devices {
|
||||
* @divider: chip sample rate divider (sample rate divider - 1)
|
||||
*/
|
||||
struct inv_mpu6050_chip_config {
|
||||
unsigned int clk:3;
|
||||
unsigned int fsr:2;
|
||||
unsigned int lpf:3;
|
||||
unsigned int accl_fs:2;
|
||||
unsigned int accl_en:1;
|
||||
unsigned int gyro_en:1;
|
||||
unsigned int temp_en:1;
|
||||
unsigned int magn_en:1;
|
||||
unsigned int accl_fifo_enable:1;
|
||||
unsigned int gyro_fifo_enable:1;
|
||||
unsigned int temp_fifo_enable:1;
|
||||
@ -144,6 +164,7 @@ struct inv_mpu6050_hw {
|
||||
* @magn_disabled: magnetometer disabled for backward compatibility reason.
|
||||
* @magn_raw_to_gauss: coefficient to convert mag raw value to Gauss.
|
||||
* @magn_orient: magnetometer sensor chip orientation if available.
|
||||
* @suspended_sensors: sensors mask of sensors turned off for suspend
|
||||
*/
|
||||
struct inv_mpu6050_state {
|
||||
struct mutex lock;
|
||||
@ -154,7 +175,6 @@ struct inv_mpu6050_state {
|
||||
enum inv_devices chip_type;
|
||||
struct i2c_mux_core *muxc;
|
||||
struct i2c_client *mux_client;
|
||||
unsigned int powerup_count;
|
||||
struct inv_mpu6050_platform_data plat_data;
|
||||
struct iio_mount_matrix orientation;
|
||||
struct regmap *map;
|
||||
@ -169,6 +189,7 @@ struct inv_mpu6050_state {
|
||||
bool magn_disabled;
|
||||
s32 magn_raw_to_gauss[3];
|
||||
struct iio_mount_matrix magn_orient;
|
||||
unsigned int suspended_sensors;
|
||||
};
|
||||
|
||||
/*register and associated bit definition*/
|
||||
@ -241,7 +262,13 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU6050_BIT_I2C_SLV3_DLY_EN 0x08
|
||||
#define INV_MPU6050_BIT_DELAY_ES_SHADOW 0x80
|
||||
|
||||
#define INV_MPU6050_REG_SIGNAL_PATH_RESET 0x68
|
||||
#define INV_MPU6050_BIT_TEMP_RST BIT(0)
|
||||
#define INV_MPU6050_BIT_ACCEL_RST BIT(1)
|
||||
#define INV_MPU6050_BIT_GYRO_RST BIT(2)
|
||||
|
||||
#define INV_MPU6050_REG_USER_CTRL 0x6A
|
||||
#define INV_MPU6050_BIT_SIG_COND_RST 0x01
|
||||
#define INV_MPU6050_BIT_FIFO_RST 0x04
|
||||
#define INV_MPU6050_BIT_DMP_RST 0x08
|
||||
#define INV_MPU6050_BIT_I2C_MST_EN 0x20
|
||||
@ -252,6 +279,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_TEMP_DIS 0x08
|
||||
#define INV_MPU6050_BIT_CLK_MASK 0x7
|
||||
|
||||
#define INV_MPU6050_REG_PWR_MGMT_2 0x6C
|
||||
@ -276,12 +304,16 @@ 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_ACCEL_OFFSET 0x77
|
||||
|
||||
/* delay time in milliseconds */
|
||||
#define INV_MPU6050_POWER_UP_TIME 100
|
||||
#define INV_MPU6050_TEMP_UP_TIME 100
|
||||
#define INV_MPU6050_SENSOR_UP_TIME 30
|
||||
#define INV_MPU6050_ACCEL_UP_TIME 20
|
||||
#define INV_MPU6050_GYRO_UP_TIME 35
|
||||
#define INV_MPU6050_GYRO_DOWN_TIME 150
|
||||
#define INV_MPU6050_SUSPEND_DELAY_MS 2000
|
||||
|
||||
/* delay time in microseconds */
|
||||
#define INV_MPU6050_REG_UP_TIME_MIN 5000
|
||||
@ -293,6 +325,7 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU6050_MAX_ACCL_FS_PARAM 3
|
||||
#define INV_MPU6050_THREE_AXIS 3
|
||||
#define INV_MPU6050_GYRO_CONFIG_FSR_SHIFT 3
|
||||
#define INV_ICM20690_GYRO_CONFIG_FSR_SHIFT 2
|
||||
#define INV_MPU6050_ACCL_CONFIG_FSR_SHIFT 3
|
||||
|
||||
#define INV_MPU6500_TEMP_OFFSET 7011
|
||||
@ -315,7 +348,6 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU6050_TS_PERIOD_JITTER 4
|
||||
|
||||
/* init parameters */
|
||||
#define INV_MPU6050_INIT_FIFO_RATE 50
|
||||
#define INV_MPU6050_MAX_FIFO_RATE 1000
|
||||
#define INV_MPU6050_MIN_FIFO_RATE 4
|
||||
|
||||
@ -340,7 +372,11 @@ struct inv_mpu6050_state {
|
||||
#define INV_MPU9255_WHOAMI_VALUE 0x73
|
||||
#define INV_MPU6515_WHOAMI_VALUE 0x74
|
||||
#define INV_ICM20608_WHOAMI_VALUE 0xAF
|
||||
#define INV_ICM20609_WHOAMI_VALUE 0xA6
|
||||
#define INV_ICM20689_WHOAMI_VALUE 0x98
|
||||
#define INV_ICM20602_WHOAMI_VALUE 0x12
|
||||
#define INV_ICM20690_WHOAMI_VALUE 0x20
|
||||
#define INV_IAM20680_WHOAMI_VALUE 0xA9
|
||||
|
||||
/* scan element definition for generic MPU6xxx devices */
|
||||
enum inv_mpu6050_scan {
|
||||
@ -360,14 +396,14 @@ enum inv_mpu6050_scan {
|
||||
};
|
||||
|
||||
enum inv_mpu6050_filter_e {
|
||||
INV_MPU6050_FILTER_256HZ_NOLPF2 = 0,
|
||||
INV_MPU6050_FILTER_188HZ,
|
||||
INV_MPU6050_FILTER_98HZ,
|
||||
INV_MPU6050_FILTER_42HZ,
|
||||
INV_MPU6050_FILTER_NOLPF2 = 0,
|
||||
INV_MPU6050_FILTER_200HZ,
|
||||
INV_MPU6050_FILTER_100HZ,
|
||||
INV_MPU6050_FILTER_45HZ,
|
||||
INV_MPU6050_FILTER_20HZ,
|
||||
INV_MPU6050_FILTER_10HZ,
|
||||
INV_MPU6050_FILTER_5HZ,
|
||||
INV_MPU6050_FILTER_2100HZ_NOLPF,
|
||||
INV_MPU6050_FILTER_NOLPF,
|
||||
NUM_MPU6050_FILTER
|
||||
};
|
||||
|
||||
@ -401,10 +437,10 @@ enum inv_mpu6050_clock_sel_e {
|
||||
|
||||
irqreturn_t inv_mpu6050_read_fifo(int irq, void *p);
|
||||
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type);
|
||||
int inv_reset_fifo(struct iio_dev *indio_dev);
|
||||
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask);
|
||||
int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable);
|
||||
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en,
|
||||
unsigned int mask);
|
||||
int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val);
|
||||
int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on);
|
||||
int inv_mpu_acpi_create_mux_client(struct i2c_client *client);
|
||||
void inv_mpu_acpi_delete_mux_client(struct i2c_client *client);
|
||||
int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
|
||||
|
@ -44,9 +44,6 @@
|
||||
#define INV_MPU_MAGN_REG_ASAY 0x11
|
||||
#define INV_MPU_MAGN_REG_ASAZ 0x12
|
||||
|
||||
/* Magnetometer maximum frequency */
|
||||
#define INV_MPU_MAGN_FREQ_HZ_MAX 50
|
||||
|
||||
static bool inv_magn_supported(const struct inv_mpu6050_state *st)
|
||||
{
|
||||
switch (st->chip_type) {
|
||||
@ -316,59 +313,32 @@ int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st)
|
||||
*
|
||||
* Returns 0 on success, a negative error code otherwise
|
||||
*/
|
||||
int inv_mpu_magn_read(const struct inv_mpu6050_state *st, int axis, int *val)
|
||||
int inv_mpu_magn_read(struct inv_mpu6050_state *st, int axis, int *val)
|
||||
{
|
||||
unsigned int user_ctrl, status;
|
||||
__be16 data[3];
|
||||
unsigned int status;
|
||||
__be16 data;
|
||||
uint8_t addr;
|
||||
uint8_t d;
|
||||
unsigned int period_ms;
|
||||
int ret;
|
||||
|
||||
/* quit if chip is not supported */
|
||||
if (!inv_magn_supported(st))
|
||||
return -ENODEV;
|
||||
|
||||
/* Mag data: X - Y - Z */
|
||||
/* Mag data: XH,XL,YH,YL,ZH,ZL */
|
||||
switch (axis) {
|
||||
case IIO_MOD_X:
|
||||
addr = 0;
|
||||
break;
|
||||
case IIO_MOD_Y:
|
||||
addr = 1;
|
||||
addr = 2;
|
||||
break;
|
||||
case IIO_MOD_Z:
|
||||
addr = 2;
|
||||
addr = 4;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* set sample rate to max mag freq */
|
||||
d = INV_MPU6050_FIFO_RATE_TO_DIVIDER(INV_MPU_MAGN_FREQ_HZ_MAX);
|
||||
ret = regmap_write(st->map, st->reg->sample_rate_div, d);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* start i2c master, wait for xfer, stop */
|
||||
user_ctrl = st->chip_config.user_ctrl | INV_MPU6050_BIT_I2C_MST_EN;
|
||||
ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* need to wait 2 periods + half-period margin */
|
||||
period_ms = 1000 / INV_MPU_MAGN_FREQ_HZ_MAX;
|
||||
msleep(period_ms * 2 + period_ms / 2);
|
||||
user_ctrl = st->chip_config.user_ctrl;
|
||||
ret = regmap_write(st->map, st->reg->user_ctrl, user_ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* restore sample rate */
|
||||
d = st->chip_config.divider;
|
||||
ret = regmap_write(st->map, st->reg->sample_rate_div, d);
|
||||
if (ret)
|
||||
return ret;
|
||||
addr += INV_MPU6050_REG_EXT_SENS_DATA;
|
||||
|
||||
/* check i2c status and read raw data */
|
||||
ret = regmap_read(st->map, INV_MPU6050_REG_I2C_MST_STATUS, &status);
|
||||
@ -379,12 +349,11 @@ int inv_mpu_magn_read(const struct inv_mpu6050_state *st, int axis, int *val)
|
||||
status & INV_MPU6050_BIT_I2C_SLV1_NACK)
|
||||
return -EIO;
|
||||
|
||||
ret = regmap_bulk_read(st->map, INV_MPU6050_REG_EXT_SENS_DATA,
|
||||
data, sizeof(data));
|
||||
ret = regmap_bulk_read(st->map, addr, &data, sizeof(data));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = (int16_t)be16_to_cpu(data[addr]);
|
||||
*val = (int16_t)be16_to_cpu(data);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
@ -10,6 +10,9 @@
|
||||
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
/* Magnetometer maximum frequency */
|
||||
#define INV_MPU_MAGN_FREQ_HZ_MAX 50
|
||||
|
||||
int inv_mpu_magn_probe(struct inv_mpu6050_state *st);
|
||||
|
||||
/**
|
||||
@ -31,6 +34,6 @@ int inv_mpu_magn_set_rate(const struct inv_mpu6050_state *st, int fifo_rate);
|
||||
|
||||
int inv_mpu_magn_set_orient(struct inv_mpu6050_state *st);
|
||||
|
||||
int inv_mpu_magn_read(const struct inv_mpu6050_state *st, int axis, int *val);
|
||||
int inv_mpu_magn_read(struct inv_mpu6050_state *st, int axis, int *val);
|
||||
|
||||
#endif /* INV_MPU_MAGN_H_ */
|
||||
|
@ -90,63 +90,14 @@ static s64 inv_mpu6050_get_timestamp(struct inv_mpu6050_state *st)
|
||||
return ts;
|
||||
}
|
||||
|
||||
int inv_reset_fifo(struct iio_dev *indio_dev)
|
||||
static int inv_reset_fifo(struct iio_dev *indio_dev)
|
||||
{
|
||||
int result;
|
||||
u8 d;
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
/* reset it timestamp validation */
|
||||
st->it_timestamp = 0;
|
||||
|
||||
/* disable interrupt */
|
||||
result = regmap_write(st->map, st->reg->int_enable, 0);
|
||||
if (result) {
|
||||
dev_err(regmap_get_device(st->map), "int_enable failed %d\n",
|
||||
result);
|
||||
return result;
|
||||
}
|
||||
/* disable the sensor output to FIFO */
|
||||
result = regmap_write(st->map, st->reg->fifo_en, 0);
|
||||
if (result)
|
||||
goto reset_fifo_fail;
|
||||
/* disable fifo reading */
|
||||
result = regmap_write(st->map, st->reg->user_ctrl,
|
||||
st->chip_config.user_ctrl);
|
||||
if (result)
|
||||
goto reset_fifo_fail;
|
||||
|
||||
/* reset FIFO*/
|
||||
d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST;
|
||||
result = regmap_write(st->map, st->reg->user_ctrl, d);
|
||||
if (result)
|
||||
goto reset_fifo_fail;
|
||||
|
||||
/* enable interrupt */
|
||||
if (st->chip_config.accl_fifo_enable ||
|
||||
st->chip_config.gyro_fifo_enable ||
|
||||
st->chip_config.magn_fifo_enable) {
|
||||
result = regmap_write(st->map, st->reg->int_enable,
|
||||
INV_MPU6050_BIT_DATA_RDY_EN);
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
/* enable FIFO reading */
|
||||
d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_EN;
|
||||
result = regmap_write(st->map, st->reg->user_ctrl, d);
|
||||
if (result)
|
||||
goto reset_fifo_fail;
|
||||
/* enable sensor output to FIFO */
|
||||
d = 0;
|
||||
if (st->chip_config.gyro_fifo_enable)
|
||||
d |= INV_MPU6050_BITS_GYRO_OUT;
|
||||
if (st->chip_config.accl_fifo_enable)
|
||||
d |= INV_MPU6050_BIT_ACCEL_OUT;
|
||||
if (st->chip_config.temp_fifo_enable)
|
||||
d |= INV_MPU6050_BIT_TEMP_OUT;
|
||||
if (st->chip_config.magn_fifo_enable)
|
||||
d |= INV_MPU6050_BIT_SLAVE_0;
|
||||
result = regmap_write(st->map, st->reg->fifo_en, d);
|
||||
/* disable fifo and reenable it */
|
||||
inv_mpu6050_prepare_fifo(st, false);
|
||||
result = inv_mpu6050_prepare_fifo(st, true);
|
||||
if (result)
|
||||
goto reset_fifo_fail;
|
||||
|
||||
|
@ -4,6 +4,8 @@
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
@ -19,10 +21,6 @@ static int inv_mpu_i2c_disable(struct iio_dev *indio_dev)
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
ret = inv_mpu6050_set_power_itg(st, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (st->reg->i2c_if) {
|
||||
ret = regmap_write(st->map, st->reg->i2c_if,
|
||||
INV_ICM20602_BIT_I2C_IF_DIS);
|
||||
@ -31,27 +29,24 @@ static int inv_mpu_i2c_disable(struct iio_dev *indio_dev)
|
||||
ret = regmap_write(st->map, st->reg->user_ctrl,
|
||||
st->chip_config.user_ctrl);
|
||||
}
|
||||
if (ret) {
|
||||
inv_mpu6050_set_power_itg(st, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return inv_mpu6050_set_power_itg(st, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int inv_mpu_probe(struct spi_device *spi)
|
||||
{
|
||||
const void *match;
|
||||
struct regmap *regmap;
|
||||
const struct spi_device_id *spi_id;
|
||||
const struct acpi_device_id *acpi_id;
|
||||
const char *name = NULL;
|
||||
enum inv_devices chip_type;
|
||||
|
||||
if ((spi_id = spi_get_device_id(spi))) {
|
||||
chip_type = (enum inv_devices)spi_id->driver_data;
|
||||
name = spi_id->name;
|
||||
} else if ((acpi_id = acpi_match_device(spi->dev.driver->acpi_match_table, &spi->dev))) {
|
||||
chip_type = (enum inv_devices)acpi_id->driver_data;
|
||||
} else if ((match = device_get_match_data(&spi->dev))) {
|
||||
chip_type = (enum inv_devices)match;
|
||||
name = dev_name(&spi->dev);
|
||||
} else {
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -74,15 +69,69 @@ static int inv_mpu_probe(struct spi_device *spi)
|
||||
static const struct spi_device_id inv_mpu_id[] = {
|
||||
{"mpu6000", INV_MPU6000},
|
||||
{"mpu6500", INV_MPU6500},
|
||||
{"mpu6515", INV_MPU6515},
|
||||
{"mpu9250", INV_MPU9250},
|
||||
{"mpu9255", INV_MPU9255},
|
||||
{"icm20608", INV_ICM20608},
|
||||
{"icm20609", INV_ICM20609},
|
||||
{"icm20689", INV_ICM20689},
|
||||
{"icm20602", INV_ICM20602},
|
||||
{"icm20690", INV_ICM20690},
|
||||
{"iam20680", INV_IAM20680},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(spi, inv_mpu_id);
|
||||
|
||||
static const struct of_device_id inv_of_match[] = {
|
||||
{
|
||||
.compatible = "invensense,mpu6000",
|
||||
.data = (void *)INV_MPU6000
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu6500",
|
||||
.data = (void *)INV_MPU6500
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu6515",
|
||||
.data = (void *)INV_MPU6515
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu9250",
|
||||
.data = (void *)INV_MPU9250
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,mpu9255",
|
||||
.data = (void *)INV_MPU9255
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,icm20608",
|
||||
.data = (void *)INV_ICM20608
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,icm20609",
|
||||
.data = (void *)INV_ICM20609
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,icm20689",
|
||||
.data = (void *)INV_ICM20689
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,icm20602",
|
||||
.data = (void *)INV_ICM20602
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,icm20690",
|
||||
.data = (void *)INV_ICM20690
|
||||
},
|
||||
{
|
||||
.compatible = "invensense,iam20680",
|
||||
.data = (void *)INV_IAM20680
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, inv_of_match);
|
||||
|
||||
static const struct acpi_device_id inv_acpi_match[] = {
|
||||
{"INVN6000", INV_MPU6000},
|
||||
{ },
|
||||
@ -93,6 +142,7 @@ static struct spi_driver inv_mpu_driver = {
|
||||
.probe = inv_mpu_probe,
|
||||
.id_table = inv_mpu_id,
|
||||
.driver = {
|
||||
.of_match_table = inv_of_match,
|
||||
.acpi_match_table = ACPI_PTR(inv_acpi_match),
|
||||
.name = "inv-mpu6000-spi",
|
||||
.pm = &inv_mpu_pmops,
|
||||
|
@ -3,11 +3,13 @@
|
||||
* Copyright (C) 2012 Invensense, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/pm_runtime.h>
|
||||
#include "inv_mpu_iio.h"
|
||||
|
||||
static void inv_scan_query_mpu6050(struct iio_dev *indio_dev)
|
||||
static unsigned int inv_scan_query_mpu6050(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
unsigned int mask;
|
||||
|
||||
st->chip_config.gyro_fifo_enable =
|
||||
test_bit(INV_MPU6050_SCAN_GYRO_X,
|
||||
@ -27,17 +29,28 @@ static void inv_scan_query_mpu6050(struct iio_dev *indio_dev)
|
||||
|
||||
st->chip_config.temp_fifo_enable =
|
||||
test_bit(INV_MPU6050_SCAN_TEMP, indio_dev->active_scan_mask);
|
||||
|
||||
mask = 0;
|
||||
if (st->chip_config.gyro_fifo_enable)
|
||||
mask |= INV_MPU6050_SENSOR_GYRO;
|
||||
if (st->chip_config.accl_fifo_enable)
|
||||
mask |= INV_MPU6050_SENSOR_ACCL;
|
||||
if (st->chip_config.temp_fifo_enable)
|
||||
mask |= INV_MPU6050_SENSOR_TEMP;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void inv_scan_query_mpu9x50(struct iio_dev *indio_dev)
|
||||
static unsigned int inv_scan_query_mpu9x50(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
unsigned int mask;
|
||||
|
||||
inv_scan_query_mpu6050(indio_dev);
|
||||
mask = inv_scan_query_mpu6050(indio_dev);
|
||||
|
||||
/* no magnetometer if i2c auxiliary bus is used */
|
||||
if (st->magn_disabled)
|
||||
return;
|
||||
return mask;
|
||||
|
||||
st->chip_config.magn_fifo_enable =
|
||||
test_bit(INV_MPU9X50_SCAN_MAGN_X,
|
||||
@ -46,9 +59,13 @@ static void inv_scan_query_mpu9x50(struct iio_dev *indio_dev)
|
||||
indio_dev->active_scan_mask) ||
|
||||
test_bit(INV_MPU9X50_SCAN_MAGN_Z,
|
||||
indio_dev->active_scan_mask);
|
||||
if (st->chip_config.magn_fifo_enable)
|
||||
mask |= INV_MPU6050_SENSOR_MAGN;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void inv_scan_query(struct iio_dev *indio_dev)
|
||||
static unsigned int inv_scan_query(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
|
||||
@ -84,6 +101,54 @@ static unsigned int inv_compute_skip_samples(const struct inv_mpu6050_state *st)
|
||||
return skip_samples;
|
||||
}
|
||||
|
||||
int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable)
|
||||
{
|
||||
uint8_t d;
|
||||
int ret;
|
||||
|
||||
if (enable) {
|
||||
st->it_timestamp = 0;
|
||||
/* reset FIFO */
|
||||
d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST;
|
||||
ret = regmap_write(st->map, st->reg->user_ctrl, d);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* enable sensor output to FIFO */
|
||||
d = 0;
|
||||
if (st->chip_config.gyro_fifo_enable)
|
||||
d |= INV_MPU6050_BITS_GYRO_OUT;
|
||||
if (st->chip_config.accl_fifo_enable)
|
||||
d |= INV_MPU6050_BIT_ACCEL_OUT;
|
||||
if (st->chip_config.temp_fifo_enable)
|
||||
d |= INV_MPU6050_BIT_TEMP_OUT;
|
||||
if (st->chip_config.magn_fifo_enable)
|
||||
d |= INV_MPU6050_BIT_SLAVE_0;
|
||||
ret = regmap_write(st->map, st->reg->fifo_en, d);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* enable FIFO reading */
|
||||
d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_EN;
|
||||
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);
|
||||
} else {
|
||||
ret = regmap_write(st->map, st->reg->int_enable, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_write(st->map, st->reg->fifo_en, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* restore user_ctrl for disabling FIFO reading */
|
||||
ret = regmap_write(st->map, st->reg->user_ctrl,
|
||||
st->chip_config.user_ctrl);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* inv_mpu6050_set_enable() - enable chip functions.
|
||||
* @indio_dev: Device driver instance.
|
||||
@ -92,84 +157,43 @@ static unsigned int inv_compute_skip_samples(const struct inv_mpu6050_state *st)
|
||||
static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable)
|
||||
{
|
||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||
uint8_t d;
|
||||
struct device *pdev = regmap_get_device(st->map);
|
||||
unsigned int scan;
|
||||
int result;
|
||||
|
||||
if (enable) {
|
||||
result = inv_mpu6050_set_power_itg(st, true);
|
||||
if (result)
|
||||
scan = inv_scan_query(indio_dev);
|
||||
result = pm_runtime_get_sync(pdev);
|
||||
if (result < 0) {
|
||||
pm_runtime_put_noidle(pdev);
|
||||
return result;
|
||||
inv_scan_query(indio_dev);
|
||||
if (st->chip_config.gyro_fifo_enable) {
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
}
|
||||
if (st->chip_config.accl_fifo_enable) {
|
||||
result = inv_mpu6050_switch_engine(st, true,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
goto error_gyro_off;
|
||||
}
|
||||
if (st->chip_config.magn_fifo_enable) {
|
||||
d = st->chip_config.user_ctrl |
|
||||
INV_MPU6050_BIT_I2C_MST_EN;
|
||||
result = regmap_write(st->map, st->reg->user_ctrl, d);
|
||||
if (result)
|
||||
goto error_accl_off;
|
||||
st->chip_config.user_ctrl = d;
|
||||
}
|
||||
st->skip_samples = inv_compute_skip_samples(st);
|
||||
result = inv_reset_fifo(indio_dev);
|
||||
if (result)
|
||||
goto error_magn_off;
|
||||
} else {
|
||||
result = regmap_write(st->map, st->reg->fifo_en, 0);
|
||||
if (result)
|
||||
goto error_magn_off;
|
||||
|
||||
result = regmap_write(st->map, st->reg->int_enable, 0);
|
||||
if (result)
|
||||
goto error_magn_off;
|
||||
|
||||
d = st->chip_config.user_ctrl & ~INV_MPU6050_BIT_I2C_MST_EN;
|
||||
result = regmap_write(st->map, st->reg->user_ctrl, d);
|
||||
if (result)
|
||||
goto error_magn_off;
|
||||
st->chip_config.user_ctrl = d;
|
||||
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
if (result)
|
||||
goto error_accl_off;
|
||||
|
||||
result = inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
if (result)
|
||||
goto error_gyro_off;
|
||||
|
||||
result = inv_mpu6050_set_power_itg(st, false);
|
||||
/*
|
||||
* In case autosuspend didn't trigger, turn off first not
|
||||
* required sensors.
|
||||
*/
|
||||
result = inv_mpu6050_switch_engine(st, false, ~scan);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
result = inv_mpu6050_switch_engine(st, true, scan);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
st->skip_samples = inv_compute_skip_samples(st);
|
||||
result = inv_mpu6050_prepare_fifo(st, true);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
} else {
|
||||
result = inv_mpu6050_prepare_fifo(st, false);
|
||||
if (result)
|
||||
goto error_power_off;
|
||||
pm_runtime_mark_last_busy(pdev);
|
||||
pm_runtime_put_autosuspend(pdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_magn_off:
|
||||
/* always restore user_ctrl to disable fifo properly */
|
||||
st->chip_config.user_ctrl &= ~INV_MPU6050_BIT_I2C_MST_EN;
|
||||
regmap_write(st->map, st->reg->user_ctrl, st->chip_config.user_ctrl);
|
||||
error_accl_off:
|
||||
if (st->chip_config.accl_fifo_enable)
|
||||
inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_ACCL_STBY);
|
||||
error_gyro_off:
|
||||
if (st->chip_config.gyro_fifo_enable)
|
||||
inv_mpu6050_switch_engine(st, false,
|
||||
INV_MPU6050_BIT_PWR_GYRO_STBY);
|
||||
error_power_off:
|
||||
inv_mpu6050_set_power_itg(st, false);
|
||||
pm_runtime_put_autosuspend(pdev);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -230,8 +230,8 @@ enum st_lsm6dsx_ext_sensor_id {
|
||||
* @i2c_addr: I2c slave address list.
|
||||
* @wai: Wai address info.
|
||||
* @id: external sensor id.
|
||||
* @odr: Output data rate of the sensor [Hz].
|
||||
* @gain: Configured sensor sensitivity.
|
||||
* @odr_table: Output data rate of the sensor [Hz].
|
||||
* @fs_table: Configured sensor sensitivity table depending on full scale.
|
||||
* @temp_comp: Temperature compensation register info (addr + mask).
|
||||
* @pwr_table: Power on register info (addr + mask).
|
||||
* @off_canc: Offset cancellation register info (addr + mask).
|
||||
|
@ -464,9 +464,10 @@ st_lsm6dsx_shub_read_oneshot(struct st_lsm6dsx_sensor *sensor,
|
||||
|
||||
len = min_t(int, sizeof(data), ch->scan_type.realbits >> 3);
|
||||
err = st_lsm6dsx_shub_read(sensor, ch->address, data, len);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
st_lsm6dsx_shub_set_enable(sensor, false);
|
||||
|
||||
err = st_lsm6dsx_shub_set_enable(sensor, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
@ -301,11 +301,14 @@ static ssize_t iio_debugfs_read_reg(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct iio_dev *indio_dev = file->private_data;
|
||||
char buf[20];
|
||||
unsigned val = 0;
|
||||
ssize_t len;
|
||||
int ret;
|
||||
|
||||
if (*ppos > 0)
|
||||
return simple_read_from_buffer(userbuf, count, ppos,
|
||||
indio_dev->read_buf,
|
||||
indio_dev->read_buf_len);
|
||||
|
||||
ret = indio_dev->info->debugfs_reg_access(indio_dev,
|
||||
indio_dev->cached_reg_addr,
|
||||
0, &val);
|
||||
@ -314,9 +317,13 @@ static ssize_t iio_debugfs_read_reg(struct file *file, char __user *userbuf,
|
||||
return ret;
|
||||
}
|
||||
|
||||
len = snprintf(buf, sizeof(buf), "0x%X\n", val);
|
||||
indio_dev->read_buf_len = snprintf(indio_dev->read_buf,
|
||||
sizeof(indio_dev->read_buf),
|
||||
"0x%X\n", val);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
return simple_read_from_buffer(userbuf, count, ppos,
|
||||
indio_dev->read_buf,
|
||||
indio_dev->read_buf_len);
|
||||
}
|
||||
|
||||
static ssize_t iio_debugfs_write_reg(struct file *file,
|
||||
@ -769,17 +776,18 @@ static ssize_t iio_read_channel_info_avail(struct device *dev,
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_str_to_fixpoint() - Parse a fixed-point number from a string
|
||||
* __iio_str_to_fixpoint() - Parse a fixed-point number from a string
|
||||
* @str: The string to parse
|
||||
* @fract_mult: Multiplier for the first decimal place, should be a power of 10
|
||||
* @integer: The integer part of the number
|
||||
* @fract: The fractional part of the number
|
||||
* @scale_db: True if this should parse as dB
|
||||
*
|
||||
* Returns 0 on success, or a negative error code if the string could not be
|
||||
* parsed.
|
||||
*/
|
||||
int iio_str_to_fixpoint(const char *str, int fract_mult,
|
||||
int *integer, int *fract)
|
||||
static int __iio_str_to_fixpoint(const char *str, int fract_mult,
|
||||
int *integer, int *fract, bool scale_db)
|
||||
{
|
||||
int i = 0, f = 0;
|
||||
bool integer_part = true, negative = false;
|
||||
@ -810,6 +818,14 @@ int iio_str_to_fixpoint(const char *str, int fract_mult,
|
||||
break;
|
||||
else
|
||||
return -EINVAL;
|
||||
} else if (!strncmp(str, " dB", sizeof(" dB") - 1) && scale_db) {
|
||||
/* Ignore the dB suffix */
|
||||
str += sizeof(" dB") - 1;
|
||||
continue;
|
||||
} else if (!strncmp(str, "dB", sizeof("dB") - 1) && scale_db) {
|
||||
/* Ignore the dB suffix */
|
||||
str += sizeof("dB") - 1;
|
||||
continue;
|
||||
} else if (*str == '.' && integer_part) {
|
||||
integer_part = false;
|
||||
} else {
|
||||
@ -830,6 +846,22 @@ int iio_str_to_fixpoint(const char *str, int fract_mult,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* iio_str_to_fixpoint() - Parse a fixed-point number from a string
|
||||
* @str: The string to parse
|
||||
* @fract_mult: Multiplier for the first decimal place, should be a power of 10
|
||||
* @integer: The integer part of the number
|
||||
* @fract: The fractional part of the number
|
||||
*
|
||||
* Returns 0 on success, or a negative error code if the string could not be
|
||||
* parsed.
|
||||
*/
|
||||
int iio_str_to_fixpoint(const char *str, int fract_mult,
|
||||
int *integer, int *fract)
|
||||
{
|
||||
return __iio_str_to_fixpoint(str, fract_mult, integer, fract, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_str_to_fixpoint);
|
||||
|
||||
static ssize_t iio_write_channel_info(struct device *dev,
|
||||
@ -842,6 +874,7 @@ static ssize_t iio_write_channel_info(struct device *dev,
|
||||
int ret, fract_mult = 100000;
|
||||
int integer, fract = 0;
|
||||
bool is_char = false;
|
||||
bool scale_db = false;
|
||||
|
||||
/* Assumes decimal - precision based on number of digits */
|
||||
if (!indio_dev->info->write_raw)
|
||||
@ -853,6 +886,9 @@ static ssize_t iio_write_channel_info(struct device *dev,
|
||||
case IIO_VAL_INT:
|
||||
fract_mult = 0;
|
||||
break;
|
||||
case IIO_VAL_INT_PLUS_MICRO_DB:
|
||||
scale_db = true;
|
||||
/* fall through */
|
||||
case IIO_VAL_INT_PLUS_MICRO:
|
||||
fract_mult = 100000;
|
||||
break;
|
||||
@ -877,6 +913,10 @@ static ssize_t iio_write_channel_info(struct device *dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = __iio_str_to_fixpoint(buf, fract_mult, &integer, &fract,
|
||||
scale_db);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = indio_dev->info->write_raw(indio_dev, this_attr->c,
|
||||
integer, fract, this_attr->address);
|
||||
|
@ -43,6 +43,16 @@ config ADUX1020
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adux1020.
|
||||
|
||||
config AL3010
|
||||
tristate "AL3010 ambient light sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to build a driver for the Dyna Image AL3010
|
||||
ambient light sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called al3010.
|
||||
|
||||
config AL3320A
|
||||
tristate "AL3320A ambient light sensor"
|
||||
depends on I2C
|
||||
@ -159,6 +169,17 @@ config IIO_CROS_EC_LIGHT_PROX
|
||||
To compile this driver as a module, choose M here:
|
||||
the module will be called cros_ec_light_prox.
|
||||
|
||||
config GP2AP002
|
||||
tristate "Sharp GP2AP002 Proximity/ALS sensor"
|
||||
depends on I2C
|
||||
select REGMAP
|
||||
help
|
||||
Say Y here if you have a Sharp GP2AP002 proximity/ALS combo-chip
|
||||
hooked to an I2C bus.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called gp2ap002.
|
||||
|
||||
config GP2AP020A00F
|
||||
tristate "Sharp GP2AP020A00F Proximity/ALS sensor"
|
||||
depends on I2C
|
||||
|
@ -7,6 +7,7 @@
|
||||
obj-$(CONFIG_ACPI_ALS) += acpi-als.o
|
||||
obj-$(CONFIG_ADJD_S311) += adjd_s311.o
|
||||
obj-$(CONFIG_ADUX1020) += adux1020.o
|
||||
obj-$(CONFIG_AL3010) += al3010.o
|
||||
obj-$(CONFIG_AL3320A) += al3320a.o
|
||||
obj-$(CONFIG_APDS9300) += apds9300.o
|
||||
obj-$(CONFIG_APDS9960) += apds9960.o
|
||||
@ -18,6 +19,7 @@ obj-$(CONFIG_CM3323) += cm3323.o
|
||||
obj-$(CONFIG_CM3605) += cm3605.o
|
||||
obj-$(CONFIG_CM36651) += cm36651.o
|
||||
obj-$(CONFIG_IIO_CROS_EC_LIGHT_PROX) += cros_ec_light_prox.o
|
||||
obj-$(CONFIG_GP2AP002) += gp2ap002.o
|
||||
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
|
||||
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
|
||||
obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
|
||||
|
242
drivers/iio/light/al3010.c
Normal file
242
drivers/iio/light/al3010.c
Normal file
@ -0,0 +1,242 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* AL3010 - Dyna Image Ambient Light Sensor
|
||||
*
|
||||
* Copyright (c) 2014, Intel Corporation.
|
||||
* Copyright (c) 2016, Dyna-Image Corp.
|
||||
* Copyright (c) 2020, David Heidelberg, Michał Mirosław, Dmitry Osipenko
|
||||
*
|
||||
* IIO driver for AL3010 (7-bit I2C slave address 0x1C).
|
||||
*
|
||||
* TODO: interrupt support, thresholds
|
||||
* When the driver will get support for interrupt handling, then interrupt
|
||||
* will need to be disabled before turning sensor OFF in order to avoid
|
||||
* potential races with the interrupt handling.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define AL3010_DRV_NAME "al3010"
|
||||
|
||||
#define AL3010_REG_SYSTEM 0x00
|
||||
#define AL3010_REG_DATA_LOW 0x0c
|
||||
#define AL3010_REG_CONFIG 0x10
|
||||
|
||||
#define AL3010_CONFIG_DISABLE 0x00
|
||||
#define AL3010_CONFIG_ENABLE 0x01
|
||||
|
||||
#define AL3010_GAIN_MASK GENMASK(6,4)
|
||||
|
||||
#define AL3010_SCALE_AVAILABLE "1.1872 0.2968 0.0742 0.018"
|
||||
|
||||
enum al3xxxx_range {
|
||||
AL3XXX_RANGE_1, /* 77806 lx */
|
||||
AL3XXX_RANGE_2, /* 19542 lx */
|
||||
AL3XXX_RANGE_3, /* 4863 lx */
|
||||
AL3XXX_RANGE_4 /* 1216 lx */
|
||||
};
|
||||
|
||||
static const int al3010_scales[][2] = {
|
||||
{0, 1187200}, {0, 296800}, {0, 74200}, {0, 18600}
|
||||
};
|
||||
|
||||
struct al3010_data {
|
||||
struct i2c_client *client;
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec al3010_channels[] = {
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
}
|
||||
};
|
||||
|
||||
static IIO_CONST_ATTR(in_illuminance_scale_available, AL3010_SCALE_AVAILABLE);
|
||||
|
||||
static struct attribute *al3010_attributes[] = {
|
||||
&iio_const_attr_in_illuminance_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group al3010_attribute_group = {
|
||||
.attrs = al3010_attributes,
|
||||
};
|
||||
|
||||
static int al3010_set_pwr(struct i2c_client *client, bool pwr)
|
||||
{
|
||||
u8 val = pwr ? AL3010_CONFIG_ENABLE : AL3010_CONFIG_DISABLE;
|
||||
return i2c_smbus_write_byte_data(client, AL3010_REG_SYSTEM, val);
|
||||
}
|
||||
|
||||
static void al3010_set_pwr_off(void *_data)
|
||||
{
|
||||
struct al3010_data *data = _data;
|
||||
|
||||
al3010_set_pwr(data->client, false);
|
||||
}
|
||||
|
||||
static int al3010_init(struct al3010_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = al3010_set_pwr(data->client, true);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, AL3010_REG_CONFIG,
|
||||
FIELD_PREP(AL3010_GAIN_MASK,
|
||||
AL3XXX_RANGE_3));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int al3010_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct al3010_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
/*
|
||||
* ALS ADC value is stored in two adjacent registers:
|
||||
* - low byte of output is stored at AL3010_REG_DATA_LOW
|
||||
* - high byte of output is stored at AL3010_REG_DATA_LOW + 1
|
||||
*/
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
AL3010_REG_DATA_LOW);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = i2c_smbus_read_byte_data(data->client,
|
||||
AL3010_REG_CONFIG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = FIELD_GET(AL3010_GAIN_MASK, ret);
|
||||
*val = al3010_scales[ret][0];
|
||||
*val2 = al3010_scales[ret][1];
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int al3010_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct al3010_data *data = iio_priv(indio_dev);
|
||||
int i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
for (i = 0; i < ARRAY_SIZE(al3010_scales); i++) {
|
||||
if (val != al3010_scales[i][0] ||
|
||||
val2 != al3010_scales[i][1])
|
||||
continue;
|
||||
|
||||
return i2c_smbus_write_byte_data(data->client,
|
||||
AL3010_REG_CONFIG,
|
||||
FIELD_PREP(AL3010_GAIN_MASK, i));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info al3010_info = {
|
||||
.read_raw = al3010_read_raw,
|
||||
.write_raw = al3010_write_raw,
|
||||
.attrs = &al3010_attribute_group,
|
||||
};
|
||||
|
||||
static int al3010_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct al3010_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &al3010_info;
|
||||
indio_dev->name = AL3010_DRV_NAME;
|
||||
indio_dev->channels = al3010_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(al3010_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = al3010_init(data);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "al3010 chip init failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(&client->dev,
|
||||
al3010_set_pwr_off,
|
||||
data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused al3010_suspend(struct device *dev)
|
||||
{
|
||||
return al3010_set_pwr(to_i2c_client(dev), false);
|
||||
}
|
||||
|
||||
static int __maybe_unused al3010_resume(struct device *dev)
|
||||
{
|
||||
return al3010_set_pwr(to_i2c_client(dev), true);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(al3010_pm_ops, al3010_suspend, al3010_resume);
|
||||
|
||||
static const struct i2c_device_id al3010_id[] = {
|
||||
{"al3010", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, al3010_id);
|
||||
|
||||
static const struct of_device_id al3010_of_match[] = {
|
||||
{ .compatible = "dynaimage,al3010", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, al3010_of_match);
|
||||
|
||||
static struct i2c_driver al3010_driver = {
|
||||
.driver = {
|
||||
.name = AL3010_DRV_NAME,
|
||||
.of_match_table = al3010_of_match,
|
||||
.pm = &al3010_pm_ops,
|
||||
},
|
||||
.probe = al3010_probe,
|
||||
.id_table = al3010_id,
|
||||
};
|
||||
module_i2c_driver(al3010_driver);
|
||||
|
||||
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@nxp.com>");
|
||||
MODULE_AUTHOR("David Heidelberg <david@ixit.cz>");
|
||||
MODULE_DESCRIPTION("AL3010 Ambient Light Sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -7,11 +7,15 @@
|
||||
* IIO driver for AL3320A (7-bit I2C slave address 0x1C).
|
||||
*
|
||||
* TODO: interrupt support, thresholds
|
||||
* When the driver will get support for interrupt handling, then interrupt
|
||||
* will need to be disabled before turning sensor OFF in order to avoid
|
||||
* potential races with the interrupt handling.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@ -36,8 +40,7 @@
|
||||
#define AL3320A_CONFIG_DISABLE 0x00
|
||||
#define AL3320A_CONFIG_ENABLE 0x01
|
||||
|
||||
#define AL3320A_GAIN_SHIFT 1
|
||||
#define AL3320A_GAIN_MASK (BIT(2) | BIT(1))
|
||||
#define AL3320A_GAIN_MASK GENMASK(2, 1)
|
||||
|
||||
/* chip params default values */
|
||||
#define AL3320A_DEFAULT_MEAN_TIME 4
|
||||
@ -79,18 +82,31 @@ static const struct attribute_group al3320a_attribute_group = {
|
||||
.attrs = al3320a_attributes,
|
||||
};
|
||||
|
||||
static int al3320a_set_pwr(struct i2c_client *client, bool pwr)
|
||||
{
|
||||
u8 val = pwr ? AL3320A_CONFIG_ENABLE : AL3320A_CONFIG_DISABLE;
|
||||
return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG, val);
|
||||
}
|
||||
|
||||
static void al3320a_set_pwr_off(void *_data)
|
||||
{
|
||||
struct al3320a_data *data = _data;
|
||||
|
||||
al3320a_set_pwr(data->client, false);
|
||||
}
|
||||
|
||||
static int al3320a_init(struct al3320a_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* power on */
|
||||
ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG,
|
||||
AL3320A_CONFIG_ENABLE);
|
||||
ret = al3320a_set_pwr(data->client, true);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, AL3320A_REG_CONFIG_RANGE,
|
||||
AL3320A_RANGE_3 << AL3320A_GAIN_SHIFT);
|
||||
FIELD_PREP(AL3320A_GAIN_MASK,
|
||||
AL3320A_RANGE_3));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -133,7 +149,7 @@ static int al3320a_read_raw(struct iio_dev *indio_dev,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = (ret & AL3320A_GAIN_MASK) >> AL3320A_GAIN_SHIFT;
|
||||
ret = FIELD_GET(AL3320A_GAIN_MASK, ret);
|
||||
*val = al3320a_scales[ret][0];
|
||||
*val2 = al3320a_scales[ret][1];
|
||||
|
||||
@ -152,11 +168,13 @@ static int al3320a_write_raw(struct iio_dev *indio_dev,
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
for (i = 0; i < ARRAY_SIZE(al3320a_scales); i++) {
|
||||
if (val == al3320a_scales[i][0] &&
|
||||
val2 == al3320a_scales[i][1])
|
||||
return i2c_smbus_write_byte_data(data->client,
|
||||
if (val != al3320a_scales[i][0] ||
|
||||
val2 != al3320a_scales[i][1])
|
||||
continue;
|
||||
|
||||
return i2c_smbus_write_byte_data(data->client,
|
||||
AL3320A_REG_CONFIG_RANGE,
|
||||
i << AL3320A_GAIN_SHIFT);
|
||||
FIELD_PREP(AL3320A_GAIN_MASK, i));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -196,27 +214,47 @@ static int al3320a_probe(struct i2c_client *client,
|
||||
dev_err(&client->dev, "al3320a chip init failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_add_action_or_reset(&client->dev,
|
||||
al3320a_set_pwr_off,
|
||||
data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static int al3320a_remove(struct i2c_client *client)
|
||||
static int __maybe_unused al3320a_suspend(struct device *dev)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(client, AL3320A_REG_CONFIG,
|
||||
AL3320A_CONFIG_DISABLE);
|
||||
return al3320a_set_pwr(to_i2c_client(dev), false);
|
||||
}
|
||||
|
||||
static int __maybe_unused al3320a_resume(struct device *dev)
|
||||
{
|
||||
return al3320a_set_pwr(to_i2c_client(dev), true);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(al3320a_pm_ops, al3320a_suspend, al3320a_resume);
|
||||
|
||||
static const struct i2c_device_id al3320a_id[] = {
|
||||
{"al3320a", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, al3320a_id);
|
||||
|
||||
static const struct of_device_id al3320a_of_match[] = {
|
||||
{ .compatible = "dynaimage,al3320a", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, al3320a_of_match);
|
||||
|
||||
static struct i2c_driver al3320a_driver = {
|
||||
.driver = {
|
||||
.name = AL3320A_DRV_NAME,
|
||||
.of_match_table = al3320a_of_match,
|
||||
.pm = &al3320a_pm_ops,
|
||||
},
|
||||
.probe = al3320a_probe,
|
||||
.remove = al3320a_remove,
|
||||
.id_table = al3320a_id,
|
||||
};
|
||||
|
||||
|
720
drivers/iio/light/gp2ap002.c
Normal file
720
drivers/iio/light/gp2ap002.c
Normal file
@ -0,0 +1,720 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* These are the two Sharp GP2AP002 variants supported by this driver:
|
||||
* GP2AP002A00F Ambient Light and Proximity Sensor
|
||||
* GP2AP002S00F Proximity Sensor
|
||||
*
|
||||
* Copyright (C) 2020 Linaro Ltd.
|
||||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||
*
|
||||
* Based partly on the code in Sony Ericssons GP2AP00200F driver by
|
||||
* Courtney Cavin and Oskar Andero in drivers/input/misc/gp2ap002a00f.c
|
||||
* Based partly on a Samsung misc driver submitted by
|
||||
* Donggeun Kim & Minkyu Kang in 2011:
|
||||
* https://lore.kernel.org/lkml/1315556546-7445-1-git-send-email-dg77.kim@samsung.com/
|
||||
* Based partly on a submission by
|
||||
* Jonathan Bakker and Paweł Chmiel in january 2019:
|
||||
* https://lore.kernel.org/linux-input/20190125175045.22576-1-pawel.mikolaj.chmiel@gmail.com/
|
||||
* Based partly on code from the Samsung GT-S7710 by <mjchen@sta.samsung.com>
|
||||
* Based partly on the code in LG Electronics GP2AP00200F driver by
|
||||
* Kenobi Lee <sungyoung.lee@lge.com> and EunYoung Cho <ey.cho@lge.com>
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/consumer.h> /* To get our ADC channel */
|
||||
#include <linux/iio/types.h> /* To deal with our ADC channel */
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#define GP2AP002_PROX_CHANNEL 0
|
||||
#define GP2AP002_ALS_CHANNEL 1
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* ADDRESS SYMBOL DATA Init R/W */
|
||||
/* D7 D6 D5 D4 D3 D2 D1 D0 */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* 0 PROX X X X X X X X VO H'00 R */
|
||||
/* 1 GAIN X X X X LED0 X X X H'00 W */
|
||||
/* 2 HYS HYSD HYSC1 HYSC0 X HYSF3 HYSF2 HYSF1 HYSF0 H'00 W */
|
||||
/* 3 CYCLE X X CYCL2 CYCL1 CYCL0 OSC2 X X H'00 W */
|
||||
/* 4 OPMOD X X X ASD X X VCON SSD H'00 W */
|
||||
/* 6 CON X X X OCON1 OCON0 X X X H'00 W */
|
||||
/* ------------------------------------------------------------------------ */
|
||||
/* VO :Proximity sensing result(0: no detection, 1: detection) */
|
||||
/* LED0 :Select switch for LED driver's On-registence(0:2x higher, 1:normal)*/
|
||||
/* HYSD/HYSF :Adjusts the receiver sensitivity */
|
||||
/* OSC :Select switch internal clocl frequency hoppling(0:effective) */
|
||||
/* CYCL :Determine the detection cycle(typically 8ms, up to 128x) */
|
||||
/* SSD :Software Shutdown function(0:shutdown, 1:operating) */
|
||||
/* VCON :VOUT output method control(0:normal, 1:interrupt) */
|
||||
/* ASD :Select switch for analog sleep function(0:ineffective, 1:effective)*/
|
||||
/* OCON :Select switch for enabling/disabling VOUT (00:enable, 11:disable) */
|
||||
|
||||
#define GP2AP002_PROX 0x00
|
||||
#define GP2AP002_GAIN 0x01
|
||||
#define GP2AP002_HYS 0x02
|
||||
#define GP2AP002_CYCLE 0x03
|
||||
#define GP2AP002_OPMOD 0x04
|
||||
#define GP2AP002_CON 0x06
|
||||
|
||||
#define GP2AP002_PROX_VO_DETECT BIT(0)
|
||||
|
||||
/* Setting this bit to 0 means 2x higher LED resistance */
|
||||
#define GP2AP002_GAIN_LED_NORMAL BIT(3)
|
||||
|
||||
/*
|
||||
* These bits adjusts the proximity sensitivity, determining characteristics
|
||||
* of the detection distance and its hysteresis.
|
||||
*/
|
||||
#define GP2AP002_HYS_HYSD_SHIFT 7
|
||||
#define GP2AP002_HYS_HYSD_MASK BIT(7)
|
||||
#define GP2AP002_HYS_HYSC_SHIFT 5
|
||||
#define GP2AP002_HYS_HYSC_MASK GENMASK(6, 5)
|
||||
#define GP2AP002_HYS_HYSF_SHIFT 0
|
||||
#define GP2AP002_HYS_HYSF_MASK GENMASK(3, 0)
|
||||
#define GP2AP002_HYS_MASK (GP2AP002_HYS_HYSD_MASK | \
|
||||
GP2AP002_HYS_HYSC_MASK | \
|
||||
GP2AP002_HYS_HYSF_MASK)
|
||||
|
||||
/*
|
||||
* These values determine the detection cycle response time
|
||||
* 0: 8ms, 1: 16ms, 2: 32ms, 3: 64ms, 4: 128ms,
|
||||
* 5: 256ms, 6: 512ms, 7: 1024ms
|
||||
*/
|
||||
#define GP2AP002_CYCLE_CYCL_SHIFT 3
|
||||
#define GP2AP002_CYCLE_CYCL_MASK GENMASK(5, 3)
|
||||
|
||||
/*
|
||||
* Select switch for internal clock frequency hopping
|
||||
* 0: effective,
|
||||
* 1: ineffective
|
||||
*/
|
||||
#define GP2AP002_CYCLE_OSC_EFFECTIVE 0
|
||||
#define GP2AP002_CYCLE_OSC_INEFFECTIVE BIT(2)
|
||||
#define GP2AP002_CYCLE_OSC_MASK BIT(2)
|
||||
|
||||
/* Analog sleep effective */
|
||||
#define GP2AP002_OPMOD_ASD BIT(4)
|
||||
/* Enable chip */
|
||||
#define GP2AP002_OPMOD_SSD_OPERATING BIT(0)
|
||||
/* IRQ mode */
|
||||
#define GP2AP002_OPMOD_VCON_IRQ BIT(1)
|
||||
#define GP2AP002_OPMOD_MASK (BIT(0) | BIT(1) | BIT(4))
|
||||
|
||||
/*
|
||||
* Select switch for enabling/disabling Vout pin
|
||||
* 0: enable
|
||||
* 2: force to go Low
|
||||
* 3: force to go High
|
||||
*/
|
||||
#define GP2AP002_CON_OCON_SHIFT 3
|
||||
#define GP2AP002_CON_OCON_ENABLE (0x0 << GP2AP002_CON_OCON_SHIFT)
|
||||
#define GP2AP002_CON_OCON_LOW (0x2 << GP2AP002_CON_OCON_SHIFT)
|
||||
#define GP2AP002_CON_OCON_HIGH (0x3 << GP2AP002_CON_OCON_SHIFT)
|
||||
#define GP2AP002_CON_OCON_MASK (0x3 << GP2AP002_CON_OCON_SHIFT)
|
||||
|
||||
/**
|
||||
* struct gp2ap002 - GP2AP002 state
|
||||
* @map: regmap pointer for the i2c regmap
|
||||
* @dev: pointer to parent device
|
||||
* @vdd: regulator controlling VDD
|
||||
* @vio: regulator controlling VIO
|
||||
* @alsout: IIO ADC channel to convert the ALSOUT signal
|
||||
* @hys_far: hysteresis control from device tree
|
||||
* @hys_close: hysteresis control from device tree
|
||||
* @is_gp2ap002s00f: this is the GP2AP002F variant of the chip
|
||||
* @irq: the IRQ line used by this device
|
||||
* @enabled: we cannot read the status of the hardware so we need to
|
||||
* keep track of whether the event is enabled using this state variable
|
||||
*/
|
||||
struct gp2ap002 {
|
||||
struct regmap *map;
|
||||
struct device *dev;
|
||||
struct regulator *vdd;
|
||||
struct regulator *vio;
|
||||
struct iio_channel *alsout;
|
||||
u8 hys_far;
|
||||
u8 hys_close;
|
||||
bool is_gp2ap002s00f;
|
||||
int irq;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static irqreturn_t gp2ap002_prox_irq(int irq, void *d)
|
||||
{
|
||||
struct iio_dev *indio_dev = d;
|
||||
struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
|
||||
u64 ev;
|
||||
int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(gp2ap002->map, GP2AP002_PROX, &val);
|
||||
if (ret) {
|
||||
dev_err(gp2ap002->dev, "error reading proximity\n");
|
||||
goto err_retrig;
|
||||
}
|
||||
|
||||
if (val & GP2AP002_PROX_VO_DETECT) {
|
||||
/* Close */
|
||||
dev_dbg(gp2ap002->dev, "close\n");
|
||||
ret = regmap_write(gp2ap002->map, GP2AP002_HYS,
|
||||
gp2ap002->hys_far);
|
||||
if (ret)
|
||||
dev_err(gp2ap002->dev,
|
||||
"error setting up proximity hysteresis\n");
|
||||
ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, GP2AP002_PROX_CHANNEL,
|
||||
IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING);
|
||||
} else {
|
||||
/* Far */
|
||||
dev_dbg(gp2ap002->dev, "far\n");
|
||||
ret = regmap_write(gp2ap002->map, GP2AP002_HYS,
|
||||
gp2ap002->hys_close);
|
||||
if (ret)
|
||||
dev_err(gp2ap002->dev,
|
||||
"error setting up proximity hysteresis\n");
|
||||
ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, GP2AP002_PROX_CHANNEL,
|
||||
IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING);
|
||||
}
|
||||
iio_push_event(indio_dev, ev, iio_get_time_ns(indio_dev));
|
||||
|
||||
/*
|
||||
* After changing hysteresis, we need to wait for one detection
|
||||
* cycle to see if anything changed, or we will just trigger the
|
||||
* previous interrupt again. A detection cycle depends on the CYCLE
|
||||
* register, we are hard-coding ~8 ms in probe() so wait some more
|
||||
* than this, 20-30 ms.
|
||||
*/
|
||||
usleep_range(20000, 30000);
|
||||
|
||||
err_retrig:
|
||||
ret = regmap_write(gp2ap002->map, GP2AP002_CON,
|
||||
GP2AP002_CON_OCON_ENABLE);
|
||||
if (ret)
|
||||
dev_err(gp2ap002->dev, "error setting up VOUT control\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* This array maps current and lux.
|
||||
*
|
||||
* Ambient light sensing range is 3 to 55000 lux.
|
||||
*
|
||||
* This mapping is based on the following formula.
|
||||
* illuminance = 10 ^ (current[mA] / 10)
|
||||
*
|
||||
* When the ADC measures 0, return 0 lux.
|
||||
*/
|
||||
static const u16 gp2ap002_illuminance_table[] = {
|
||||
0, 1, 1, 2, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 25, 32, 40, 50, 63, 79,
|
||||
100, 126, 158, 200, 251, 316, 398, 501, 631, 794, 1000, 1259, 1585,
|
||||
1995, 2512, 3162, 3981, 5012, 6310, 7943, 10000, 12589, 15849, 19953,
|
||||
25119, 31623, 39811, 50119,
|
||||
};
|
||||
|
||||
static int gp2ap002_get_lux(struct gp2ap002 *gp2ap002)
|
||||
{
|
||||
int ret, res;
|
||||
u16 lux;
|
||||
|
||||
ret = iio_read_channel_processed(gp2ap002->alsout, &res);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(gp2ap002->dev, "read %d mA from ADC\n", res);
|
||||
|
||||
/* ensure we don't under/overflow */
|
||||
res = clamp(res, 0, (int)ARRAY_SIZE(gp2ap002_illuminance_table) - 1);
|
||||
lux = gp2ap002_illuminance_table[res];
|
||||
|
||||
return (int)lux;
|
||||
}
|
||||
|
||||
static int gp2ap002_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
ret = gp2ap002_get_lux(gp2ap002);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int gp2ap002_init(struct gp2ap002 *gp2ap002)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Set up the IR LED resistance */
|
||||
ret = regmap_write(gp2ap002->map, GP2AP002_GAIN,
|
||||
GP2AP002_GAIN_LED_NORMAL);
|
||||
if (ret) {
|
||||
dev_err(gp2ap002->dev, "error setting up LED gain\n");
|
||||
return ret;
|
||||
}
|
||||
ret = regmap_write(gp2ap002->map, GP2AP002_HYS, gp2ap002->hys_far);
|
||||
if (ret) {
|
||||
dev_err(gp2ap002->dev,
|
||||
"error setting up proximity hysteresis\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Disable internal frequency hopping */
|
||||
ret = regmap_write(gp2ap002->map, GP2AP002_CYCLE,
|
||||
GP2AP002_CYCLE_OSC_INEFFECTIVE);
|
||||
if (ret) {
|
||||
dev_err(gp2ap002->dev,
|
||||
"error setting up internal frequency hopping\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable chip and IRQ, disable analog sleep */
|
||||
ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD,
|
||||
GP2AP002_OPMOD_SSD_OPERATING |
|
||||
GP2AP002_OPMOD_VCON_IRQ);
|
||||
if (ret) {
|
||||
dev_err(gp2ap002->dev, "error setting up operation mode\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Interrupt on VOUT enabled */
|
||||
ret = regmap_write(gp2ap002->map, GP2AP002_CON,
|
||||
GP2AP002_CON_OCON_ENABLE);
|
||||
if (ret)
|
||||
dev_err(gp2ap002->dev, "error setting up VOUT control\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gp2ap002_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 gp2ap002 *gp2ap002 = iio_priv(indio_dev);
|
||||
|
||||
/*
|
||||
* We just keep track of this internally, as it is not possible to
|
||||
* query the hardware.
|
||||
*/
|
||||
return gp2ap002->enabled;
|
||||
}
|
||||
|
||||
static int gp2ap002_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 gp2ap002 *gp2ap002 = iio_priv(indio_dev);
|
||||
|
||||
if (state) {
|
||||
/*
|
||||
* This will bring the regulators up (unless they are on
|
||||
* already) and reintialize the sensor by using runtime_pm
|
||||
* callbacks.
|
||||
*/
|
||||
pm_runtime_get_sync(gp2ap002->dev);
|
||||
gp2ap002->enabled = true;
|
||||
} else {
|
||||
pm_runtime_mark_last_busy(gp2ap002->dev);
|
||||
pm_runtime_put_autosuspend(gp2ap002->dev);
|
||||
gp2ap002->enabled = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info gp2ap002_info = {
|
||||
.read_raw = gp2ap002_read_raw,
|
||||
.read_event_config = gp2ap002_read_event_config,
|
||||
.write_event_config = gp2ap002_write_event_config,
|
||||
};
|
||||
|
||||
static const struct iio_event_spec gp2ap002_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate = BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec gp2ap002_channels[] = {
|
||||
{
|
||||
.type = IIO_PROXIMITY,
|
||||
.event_spec = gp2ap002_events,
|
||||
.num_event_specs = ARRAY_SIZE(gp2ap002_events),
|
||||
},
|
||||
{
|
||||
.type = IIO_LIGHT,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.channel = GP2AP002_ALS_CHANNEL,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* We need a special regmap because this hardware expects to
|
||||
* write single bytes to registers but read a 16bit word on some
|
||||
* variants and discard the lower 8 bits so combine
|
||||
* i2c_smbus_read_word_data() with i2c_smbus_write_byte_data()
|
||||
* selectively like this.
|
||||
*/
|
||||
static int gp2ap002_regmap_i2c_read(void *context, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_word_data(i2c, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = (ret >> 8) & 0xFF;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gp2ap002_regmap_i2c_write(void *context, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
|
||||
return i2c_smbus_write_byte_data(i2c, reg, val);
|
||||
}
|
||||
|
||||
static struct regmap_bus gp2ap002_regmap_bus = {
|
||||
.reg_read = gp2ap002_regmap_i2c_read,
|
||||
.reg_write = gp2ap002_regmap_i2c_write,
|
||||
};
|
||||
|
||||
static int gp2ap002_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct gp2ap002 *gp2ap002;
|
||||
struct iio_dev *indio_dev;
|
||||
struct device *dev = &client->dev;
|
||||
enum iio_chan_type ch_type;
|
||||
static const struct regmap_config config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = GP2AP002_CON,
|
||||
};
|
||||
struct regmap *regmap;
|
||||
int num_chan;
|
||||
const char *compat;
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*gp2ap002));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
gp2ap002 = iio_priv(indio_dev);
|
||||
gp2ap002->dev = dev;
|
||||
|
||||
/*
|
||||
* Check the device compatible like this makes it possible to use
|
||||
* ACPI PRP0001 for registering the sensor using device tree
|
||||
* properties.
|
||||
*/
|
||||
ret = device_property_read_string(dev, "compatible", &compat);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot check compatible\n");
|
||||
return ret;
|
||||
}
|
||||
gp2ap002->is_gp2ap002s00f = !strcmp(compat, "sharp,gp2ap002s00f");
|
||||
|
||||
regmap = devm_regmap_init(dev, &gp2ap002_regmap_bus, dev, &config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(dev, "Failed to register i2c regmap %d\n",
|
||||
(int)PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
gp2ap002->map = regmap;
|
||||
|
||||
/*
|
||||
* The hysteresis settings are coded into the device tree as values
|
||||
* to be written into the hysteresis register. The datasheet defines
|
||||
* modes "A", "B1" and "B2" with fixed values to be use but vendor
|
||||
* code trees for actual devices are tweaking these values and refer to
|
||||
* modes named things like "B1.5". To be able to support any devices,
|
||||
* we allow passing an arbitrary hysteresis setting for "near" and
|
||||
* "far".
|
||||
*/
|
||||
|
||||
/* Check the device tree for the IR LED hysteresis */
|
||||
ret = device_property_read_u8(dev, "sharp,proximity-far-hysteresis",
|
||||
&val);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to obtain proximity far setting\n");
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "proximity far setting %02x\n", val);
|
||||
gp2ap002->hys_far = val;
|
||||
|
||||
ret = device_property_read_u8(dev, "sharp,proximity-close-hysteresis",
|
||||
&val);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to obtain proximity close setting\n");
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "proximity close setting %02x\n", val);
|
||||
gp2ap002->hys_close = val;
|
||||
|
||||
/* The GP2AP002A00F has a light sensor too */
|
||||
if (!gp2ap002->is_gp2ap002s00f) {
|
||||
gp2ap002->alsout = devm_iio_channel_get(dev, "alsout");
|
||||
if (IS_ERR(gp2ap002->alsout)) {
|
||||
if (PTR_ERR(gp2ap002->alsout) == -ENODEV) {
|
||||
dev_err(dev, "no ADC, deferring...\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
dev_err(dev, "failed to get ALSOUT ADC channel\n");
|
||||
return PTR_ERR(gp2ap002->alsout);
|
||||
}
|
||||
ret = iio_get_channel_type(gp2ap002->alsout, &ch_type);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ch_type != IIO_CURRENT) {
|
||||
dev_err(dev,
|
||||
"wrong type of IIO channel specified for ALSOUT\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
gp2ap002->vdd = devm_regulator_get(dev, "vdd");
|
||||
if (IS_ERR(gp2ap002->vdd)) {
|
||||
dev_err(dev, "failed to get VDD regulator\n");
|
||||
return PTR_ERR(gp2ap002->vdd);
|
||||
}
|
||||
gp2ap002->vio = devm_regulator_get(dev, "vio");
|
||||
if (IS_ERR(gp2ap002->vio)) {
|
||||
dev_err(dev, "failed to get VIO regulator\n");
|
||||
return PTR_ERR(gp2ap002->vio);
|
||||
}
|
||||
|
||||
/* Operating voltage 2.4V .. 3.6V according to datasheet */
|
||||
ret = regulator_set_voltage(gp2ap002->vdd, 2400000, 3600000);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to sett VDD voltage\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* VIO should be between 1.65V and VDD */
|
||||
ret = regulator_get_voltage(gp2ap002->vdd);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to get VDD voltage\n");
|
||||
return ret;
|
||||
}
|
||||
ret = regulator_set_voltage(gp2ap002->vio, 1650000, ret);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set VIO voltage\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_enable(gp2ap002->vdd);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable VDD regulator\n");
|
||||
return ret;
|
||||
}
|
||||
ret = regulator_enable(gp2ap002->vio);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable VIO regulator\n");
|
||||
goto out_disable_vdd;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
|
||||
/*
|
||||
* Initialize the device and signal to runtime PM that now we are
|
||||
* definately up and using power.
|
||||
*/
|
||||
ret = gp2ap002_init(gp2ap002);
|
||||
if (ret) {
|
||||
dev_err(dev, "initialization failed\n");
|
||||
goto out_disable_vio;
|
||||
}
|
||||
pm_runtime_get_noresume(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
gp2ap002->enabled = false;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, client->irq, NULL,
|
||||
gp2ap002_prox_irq, IRQF_ONESHOT,
|
||||
"gp2ap002", indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to request IRQ\n");
|
||||
goto out_disable_vio;
|
||||
}
|
||||
gp2ap002->irq = client->irq;
|
||||
|
||||
/*
|
||||
* As the device takes 20 ms + regulator delay to come up with a fresh
|
||||
* measurement after power-on, do not shut it down unnecessarily.
|
||||
* Set autosuspend to a one second.
|
||||
*/
|
||||
pm_runtime_set_autosuspend_delay(dev, 1000);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->info = &gp2ap002_info;
|
||||
indio_dev->name = "gp2ap002";
|
||||
indio_dev->channels = gp2ap002_channels;
|
||||
/* Skip light channel for the proximity-only sensor */
|
||||
num_chan = ARRAY_SIZE(gp2ap002_channels);
|
||||
if (gp2ap002->is_gp2ap002s00f)
|
||||
num_chan--;
|
||||
indio_dev->num_channels = num_chan;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto out_disable_pm;
|
||||
dev_dbg(dev, "Sharp GP2AP002 probed successfully\n");
|
||||
|
||||
return 0;
|
||||
|
||||
out_disable_pm:
|
||||
pm_runtime_put_noidle(dev);
|
||||
pm_runtime_disable(dev);
|
||||
out_disable_vio:
|
||||
regulator_disable(gp2ap002->vio);
|
||||
out_disable_vdd:
|
||||
regulator_disable(gp2ap002->vdd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gp2ap002_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
|
||||
struct device *dev = &client->dev;
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
pm_runtime_put_noidle(dev);
|
||||
pm_runtime_disable(dev);
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(gp2ap002->vio);
|
||||
regulator_disable(gp2ap002->vdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused gp2ap002_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
/* Deactivate the IRQ */
|
||||
disable_irq(gp2ap002->irq);
|
||||
|
||||
/* Disable chip and IRQ, everything off */
|
||||
ret = regmap_write(gp2ap002->map, GP2AP002_OPMOD, 0x00);
|
||||
if (ret) {
|
||||
dev_err(gp2ap002->dev, "error setting up operation mode\n");
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* As these regulators may be shared, at least we are now in
|
||||
* sleep even if the regulators aren't really turned off.
|
||||
*/
|
||||
regulator_disable(gp2ap002->vio);
|
||||
regulator_disable(gp2ap002->vdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused gp2ap002_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct gp2ap002 *gp2ap002 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(gp2ap002->vdd);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable VDD regulator in resume path\n");
|
||||
return ret;
|
||||
}
|
||||
ret = regulator_enable(gp2ap002->vio);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable VIO regulator in resume path\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
|
||||
ret = gp2ap002_init(gp2ap002);
|
||||
if (ret) {
|
||||
dev_err(dev, "re-initialization failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Re-activate the IRQ */
|
||||
enable_irq(gp2ap002->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops gp2ap002_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(gp2ap002_runtime_suspend,
|
||||
gp2ap002_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static const struct i2c_device_id gp2ap002_id_table[] = {
|
||||
{ "gp2ap002", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, gp2ap002_id_table);
|
||||
|
||||
static const struct of_device_id gp2ap002_of_match[] = {
|
||||
{ .compatible = "sharp,gp2ap002a00f" },
|
||||
{ .compatible = "sharp,gp2ap002s00f" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gp2ap002_of_match);
|
||||
|
||||
static struct i2c_driver gp2ap002_driver = {
|
||||
.driver = {
|
||||
.name = "gp2ap002",
|
||||
.of_match_table = gp2ap002_of_match,
|
||||
.pm = &gp2ap002_dev_pm_ops,
|
||||
},
|
||||
.probe = gp2ap002_probe,
|
||||
.remove = gp2ap002_remove,
|
||||
.id_table = gp2ap002_id_table,
|
||||
};
|
||||
module_i2c_driver(gp2ap002_driver);
|
||||
|
||||
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
|
||||
MODULE_DESCRIPTION("GP2AP002 ambient light and proximity sensor driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -1390,6 +1390,12 @@ static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev)
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
err = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (err < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable triggers according to the scan_mask. Enabling either
|
||||
* LIGHT_CLEAR or LIGHT_IR scan mode results in enabling ALS
|
||||
@ -1420,14 +1426,12 @@ static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev)
|
||||
goto error_unlock;
|
||||
|
||||
data->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (!data->buffer) {
|
||||
if (!data->buffer)
|
||||
err = -ENOMEM;
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
err = iio_triggered_buffer_postenable(indio_dev);
|
||||
|
||||
error_unlock:
|
||||
if (err < 0)
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return err;
|
||||
@ -1436,14 +1440,10 @@ error_unlock:
|
||||
static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct gp2ap020a00f_data *data = iio_priv(indio_dev);
|
||||
int i, err;
|
||||
int i, err = 0;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
err = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (err < 0)
|
||||
goto error_unlock;
|
||||
|
||||
for_each_set_bit(i, indio_dev->active_scan_mask,
|
||||
indio_dev->masklength) {
|
||||
switch (i) {
|
||||
@ -1465,7 +1465,8 @@ static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev)
|
||||
if (err == 0)
|
||||
kfree(data->buffer);
|
||||
|
||||
error_unlock:
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return err;
|
||||
|
@ -102,6 +102,9 @@
|
||||
#define SI1133_INPUT_FRACTION_LOW 15
|
||||
#define SI1133_LUX_OUTPUT_FRACTION 12
|
||||
#define SI1133_LUX_BUFFER_SIZE 9
|
||||
#define SI1133_MEASURE_BUFFER_SIZE 3
|
||||
|
||||
#define SI1133_SIGN_BIT_INDEX 23
|
||||
|
||||
static const int si1133_scale_available[] = {
|
||||
1, 2, 4, 8, 16, 32, 64, 128};
|
||||
@ -234,13 +237,13 @@ static const struct si1133_lux_coeff lux_coeff = {
|
||||
}
|
||||
};
|
||||
|
||||
static int si1133_calculate_polynomial_inner(u32 input, u8 fraction, u16 mag,
|
||||
static int si1133_calculate_polynomial_inner(s32 input, u8 fraction, u16 mag,
|
||||
s8 shift)
|
||||
{
|
||||
return ((input << fraction) / mag) << shift;
|
||||
}
|
||||
|
||||
static int si1133_calculate_output(u32 x, u32 y, u8 x_order, u8 y_order,
|
||||
static int si1133_calculate_output(s32 x, s32 y, u8 x_order, u8 y_order,
|
||||
u8 input_fraction, s8 sign,
|
||||
const struct si1133_coeff *coeffs)
|
||||
{
|
||||
@ -276,7 +279,7 @@ static int si1133_calculate_output(u32 x, u32 y, u8 x_order, u8 y_order,
|
||||
* The algorithm is from:
|
||||
* https://siliconlabs.github.io/Gecko_SDK_Doc/efm32zg/html/si1133_8c_source.html#l00716
|
||||
*/
|
||||
static int si1133_calc_polynomial(u32 x, u32 y, u8 input_fraction, u8 num_coeff,
|
||||
static int si1133_calc_polynomial(s32 x, s32 y, u8 input_fraction, u8 num_coeff,
|
||||
const struct si1133_coeff *coeffs)
|
||||
{
|
||||
u8 x_order, y_order;
|
||||
@ -614,7 +617,7 @@ static int si1133_measure(struct si1133_data *data,
|
||||
{
|
||||
int err;
|
||||
|
||||
__be16 resp;
|
||||
u8 buffer[SI1133_MEASURE_BUFFER_SIZE];
|
||||
|
||||
err = si1133_set_adcmux(data, 0, chan->channel);
|
||||
if (err)
|
||||
@ -625,12 +628,13 @@ static int si1133_measure(struct si1133_data *data,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = si1133_bulk_read(data, SI1133_REG_HOSTOUT(0), sizeof(resp),
|
||||
(u8 *)&resp);
|
||||
err = si1133_bulk_read(data, SI1133_REG_HOSTOUT(0), sizeof(buffer),
|
||||
buffer);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*val = be16_to_cpu(resp);
|
||||
*val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
|
||||
SI1133_SIGN_BIT_INDEX);
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -704,9 +708,9 @@ static int si1133_get_lux(struct si1133_data *data, int *val)
|
||||
{
|
||||
int err;
|
||||
int lux;
|
||||
u32 high_vis;
|
||||
u32 low_vis;
|
||||
u32 ir;
|
||||
s32 high_vis;
|
||||
s32 low_vis;
|
||||
s32 ir;
|
||||
u8 buffer[SI1133_LUX_BUFFER_SIZE];
|
||||
|
||||
/* Activate lux channels */
|
||||
@ -719,9 +723,16 @@ static int si1133_get_lux(struct si1133_data *data, int *val)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
high_vis = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
|
||||
low_vis = (buffer[3] << 16) | (buffer[4] << 8) | buffer[5];
|
||||
ir = (buffer[6] << 16) | (buffer[7] << 8) | buffer[8];
|
||||
high_vis =
|
||||
sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
|
||||
SI1133_SIGN_BIT_INDEX);
|
||||
|
||||
low_vis =
|
||||
sign_extend32((buffer[3] << 16) | (buffer[4] << 8) | buffer[5],
|
||||
SI1133_SIGN_BIT_INDEX);
|
||||
|
||||
ir = sign_extend32((buffer[6] << 16) | (buffer[7] << 8) | buffer[8],
|
||||
SI1133_SIGN_BIT_INDEX);
|
||||
|
||||
if (high_vis > SI1133_ADC_THRESHOLD || ir > SI1133_ADC_THRESHOLD)
|
||||
lux = si1133_calc_polynomial(high_vis, ir,
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
@ -57,6 +58,8 @@
|
||||
#define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */
|
||||
#define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */
|
||||
|
||||
#define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */
|
||||
|
||||
enum vcnl4000_device_ids {
|
||||
VCNL4000,
|
||||
VCNL4010,
|
||||
@ -87,6 +90,7 @@ struct vcnl4000_chip_spec {
|
||||
int (*init)(struct vcnl4000_data *data);
|
||||
int (*measure_light)(struct vcnl4000_data *data, int *val);
|
||||
int (*measure_proximity)(struct vcnl4000_data *data, int *val);
|
||||
int (*set_power_state)(struct vcnl4000_data *data, bool on);
|
||||
};
|
||||
|
||||
static const struct i2c_device_id vcnl4000_id[] = {
|
||||
@ -99,6 +103,12 @@ static const struct i2c_device_id vcnl4000_id[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
|
||||
|
||||
static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on)
|
||||
{
|
||||
/* no suspend op */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vcnl4000_init(struct vcnl4000_data *data)
|
||||
{
|
||||
int ret, prod_id;
|
||||
@ -127,9 +137,31 @@ static int vcnl4000_init(struct vcnl4000_data *data)
|
||||
data->al_scale = 250000;
|
||||
mutex_init(&data->vcnl4000_lock);
|
||||
|
||||
return 0;
|
||||
return data->chip_spec->set_power_state(data, true);
|
||||
};
|
||||
|
||||
static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on)
|
||||
{
|
||||
u16 val = on ? 0 /* power on */ : 1 /* shut down */;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (on) {
|
||||
/* Wait at least one integration cycle before fetching data */
|
||||
data->vcnl4200_al.last_measurement = ktime_get();
|
||||
data->vcnl4200_ps.last_measurement = ktime_get();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vcnl4200_init(struct vcnl4000_data *data)
|
||||
{
|
||||
int ret, id;
|
||||
@ -155,14 +187,6 @@ static int vcnl4200_init(struct vcnl4000_data *data)
|
||||
|
||||
data->rev = (ret >> 8) & 0xf;
|
||||
|
||||
/* Set defaults and enable both channels */
|
||||
ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->vcnl4200_al.reg = VCNL4200_AL_DATA;
|
||||
data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
|
||||
switch (id) {
|
||||
@ -180,11 +204,13 @@ static int vcnl4200_init(struct vcnl4000_data *data)
|
||||
data->al_scale = 120000;
|
||||
break;
|
||||
}
|
||||
data->vcnl4200_al.last_measurement = ktime_set(0, 0);
|
||||
data->vcnl4200_ps.last_measurement = ktime_set(0, 0);
|
||||
mutex_init(&data->vcnl4200_al.lock);
|
||||
mutex_init(&data->vcnl4200_ps.lock);
|
||||
|
||||
ret = data->chip_spec->set_power_state(data, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
@ -291,24 +317,28 @@ static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
|
||||
.init = vcnl4000_init,
|
||||
.measure_light = vcnl4000_measure_light,
|
||||
.measure_proximity = vcnl4000_measure_proximity,
|
||||
.set_power_state = vcnl4000_set_power_state,
|
||||
},
|
||||
[VCNL4010] = {
|
||||
.prod = "VCNL4010/4020",
|
||||
.init = vcnl4000_init,
|
||||
.measure_light = vcnl4000_measure_light,
|
||||
.measure_proximity = vcnl4000_measure_proximity,
|
||||
.set_power_state = vcnl4000_set_power_state,
|
||||
},
|
||||
[VCNL4040] = {
|
||||
.prod = "VCNL4040",
|
||||
.init = vcnl4200_init,
|
||||
.measure_light = vcnl4200_measure_light,
|
||||
.measure_proximity = vcnl4200_measure_proximity,
|
||||
.set_power_state = vcnl4200_set_power_state,
|
||||
},
|
||||
[VCNL4200] = {
|
||||
.prod = "VCNL4200",
|
||||
.init = vcnl4200_init,
|
||||
.measure_light = vcnl4200_measure_light,
|
||||
.measure_proximity = vcnl4200_measure_proximity,
|
||||
.set_power_state = vcnl4200_set_power_state,
|
||||
},
|
||||
};
|
||||
|
||||
@ -323,6 +353,23 @@ static const struct iio_chan_spec vcnl4000_channels[] = {
|
||||
}
|
||||
};
|
||||
|
||||
static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
int ret;
|
||||
|
||||
if (on) {
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0)
|
||||
pm_runtime_put_noidle(dev);
|
||||
} else {
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
ret = pm_runtime_put_autosuspend(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vcnl4000_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
@ -332,20 +379,26 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = vcnl4000_set_pm_runtime_state(data, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_LIGHT:
|
||||
ret = data->chip_spec->measure_light(data, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
if (!ret)
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_PROXIMITY:
|
||||
ret = data->chip_spec->measure_proximity(data, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return IIO_VAL_INT;
|
||||
if (!ret)
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
}
|
||||
vcnl4000_set_pm_runtime_state(data, false);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (chan->type != IIO_LIGHT)
|
||||
return -EINVAL;
|
||||
@ -393,7 +446,22 @@ static int vcnl4000_probe(struct i2c_client *client,
|
||||
indio_dev->name = VCNL4000_DRV_NAME;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
ret = pm_runtime_set_active(&client->dev);
|
||||
if (ret < 0)
|
||||
goto fail_poweroff;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto fail_poweroff;
|
||||
|
||||
pm_runtime_enable(&client->dev);
|
||||
pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&client->dev);
|
||||
|
||||
return 0;
|
||||
fail_poweroff:
|
||||
data->chip_spec->set_power_state(data, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id vcnl_4000_of_match[] = {
|
||||
@ -421,13 +489,51 @@ static const struct of_device_id vcnl_4000_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vcnl_4000_of_match);
|
||||
|
||||
static int vcnl4000_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct vcnl4000_data *data = iio_priv(indio_dev);
|
||||
|
||||
pm_runtime_dont_use_autosuspend(&client->dev);
|
||||
pm_runtime_disable(&client->dev);
|
||||
iio_device_unregister(indio_dev);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
|
||||
return data->chip_spec->set_power_state(data, false);
|
||||
}
|
||||
|
||||
static int __maybe_unused vcnl4000_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct vcnl4000_data *data = iio_priv(indio_dev);
|
||||
|
||||
return data->chip_spec->set_power_state(data, false);
|
||||
}
|
||||
|
||||
static int __maybe_unused vcnl4000_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct vcnl4000_data *data = iio_priv(indio_dev);
|
||||
|
||||
return data->chip_spec->set_power_state(data, true);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops vcnl4000_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(vcnl4000_runtime_suspend,
|
||||
vcnl4000_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct i2c_driver vcnl4000_driver = {
|
||||
.driver = {
|
||||
.name = VCNL4000_DRV_NAME,
|
||||
.pm = &vcnl4000_pm_ops,
|
||||
.of_match_table = vcnl_4000_of_match,
|
||||
},
|
||||
.probe = vcnl4000_probe,
|
||||
.id_table = vcnl4000_id,
|
||||
.remove = vcnl4000_remove,
|
||||
};
|
||||
|
||||
module_i2c_driver(vcnl4000_driver);
|
||||
|
@ -275,11 +275,20 @@ static int lmp91000_buffer_cb(const void *val, void *private)
|
||||
static const struct iio_trigger_ops lmp91000_trigger_ops = {
|
||||
};
|
||||
|
||||
static int lmp91000_buffer_preenable(struct iio_dev *indio_dev)
|
||||
static int lmp91000_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct lmp91000_data *data = iio_priv(indio_dev);
|
||||
int err;
|
||||
|
||||
return iio_channel_start_all_cb(data->cb_buffer);
|
||||
err = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = iio_channel_start_all_cb(data->cb_buffer);
|
||||
if (err)
|
||||
iio_triggered_buffer_predisable(indio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int lmp91000_buffer_predisable(struct iio_dev *indio_dev)
|
||||
@ -288,12 +297,11 @@ static int lmp91000_buffer_predisable(struct iio_dev *indio_dev)
|
||||
|
||||
iio_channel_stop_all_cb(data->cb_buffer);
|
||||
|
||||
return 0;
|
||||
return iio_triggered_buffer_predisable(indio_dev);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops lmp91000_buffer_setup_ops = {
|
||||
.preenable = lmp91000_buffer_preenable,
|
||||
.postenable = iio_triggered_buffer_postenable,
|
||||
.postenable = lmp91000_buffer_postenable,
|
||||
.predisable = lmp91000_buffer_predisable,
|
||||
};
|
||||
|
||||
|
@ -101,6 +101,17 @@ config HP03
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called hp03.
|
||||
|
||||
config ICP10100
|
||||
tristate "InvenSense ICP-101xx pressure and temperature sensor"
|
||||
depends on I2C
|
||||
select CRC8
|
||||
help
|
||||
Say yes here to build support for InvenSense ICP-101xx barometric
|
||||
pressure and temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called icp10100.
|
||||
|
||||
config MPL115
|
||||
tristate
|
||||
|
||||
|
@ -14,6 +14,7 @@ obj-$(CONFIG_DPS310) += dps310.o
|
||||
obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o
|
||||
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
|
||||
obj-$(CONFIG_HP03) += hp03.o
|
||||
obj-$(CONFIG_ICP10100) += icp10100.o
|
||||
obj-$(CONFIG_MPL115) += mpl115.o
|
||||
obj-$(CONFIG_MPL115_I2C) += mpl115_i2c.o
|
||||
obj-$(CONFIG_MPL115_SPI) += mpl115_spi.o
|
||||
|
658
drivers/iio/pressure/icp10100.c
Normal file
658
drivers/iio/pressure/icp10100.c
Normal file
@ -0,0 +1,658 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2020 InvenSense, Inc.
|
||||
*
|
||||
* Driver for InvenSense ICP-1010xx barometric pressure and temperature sensor.
|
||||
*
|
||||
* Datasheet:
|
||||
* http://www.invensense.com/wp-content/uploads/2018/01/DS-000186-ICP-101xx-v1.2.pdf
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/crc8.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define ICP10100_ID_REG_GET(_reg) ((_reg) & 0x003F)
|
||||
#define ICP10100_ID_REG 0x08
|
||||
#define ICP10100_RESPONSE_WORD_LENGTH 3
|
||||
#define ICP10100_CRC8_WORD_LENGTH 2
|
||||
#define ICP10100_CRC8_POLYNOMIAL 0x31
|
||||
#define ICP10100_CRC8_INIT 0xFF
|
||||
|
||||
enum icp10100_mode {
|
||||
ICP10100_MODE_LP, /* Low power mode: 1x sampling */
|
||||
ICP10100_MODE_N, /* Normal mode: 2x sampling */
|
||||
ICP10100_MODE_LN, /* Low noise mode: 4x sampling */
|
||||
ICP10100_MODE_ULN, /* Ultra low noise mode: 8x sampling */
|
||||
ICP10100_MODE_NB,
|
||||
};
|
||||
|
||||
struct icp10100_state {
|
||||
struct mutex lock;
|
||||
struct i2c_client *client;
|
||||
struct regulator *vdd;
|
||||
enum icp10100_mode mode;
|
||||
int16_t cal[4];
|
||||
};
|
||||
|
||||
struct icp10100_command {
|
||||
__be16 cmd;
|
||||
unsigned long wait_us;
|
||||
unsigned long wait_max_us;
|
||||
size_t response_word_nb;
|
||||
};
|
||||
|
||||
static const struct icp10100_command icp10100_cmd_soft_reset = {
|
||||
.cmd = cpu_to_be16(0x805D),
|
||||
.wait_us = 170,
|
||||
.wait_max_us = 200,
|
||||
.response_word_nb = 0,
|
||||
};
|
||||
|
||||
static const struct icp10100_command icp10100_cmd_read_id = {
|
||||
.cmd = cpu_to_be16(0xEFC8),
|
||||
.wait_us = 0,
|
||||
.response_word_nb = 1,
|
||||
};
|
||||
|
||||
static const struct icp10100_command icp10100_cmd_read_otp = {
|
||||
.cmd = cpu_to_be16(0xC7F7),
|
||||
.wait_us = 0,
|
||||
.response_word_nb = 1,
|
||||
};
|
||||
|
||||
static const struct icp10100_command icp10100_cmd_measure[] = {
|
||||
[ICP10100_MODE_LP] = {
|
||||
.cmd = cpu_to_be16(0x401A),
|
||||
.wait_us = 1800,
|
||||
.wait_max_us = 2000,
|
||||
.response_word_nb = 3,
|
||||
},
|
||||
[ICP10100_MODE_N] = {
|
||||
.cmd = cpu_to_be16(0x48A3),
|
||||
.wait_us = 6300,
|
||||
.wait_max_us = 6500,
|
||||
.response_word_nb = 3,
|
||||
},
|
||||
[ICP10100_MODE_LN] = {
|
||||
.cmd = cpu_to_be16(0x5059),
|
||||
.wait_us = 23800,
|
||||
.wait_max_us = 24000,
|
||||
.response_word_nb = 3,
|
||||
},
|
||||
[ICP10100_MODE_ULN] = {
|
||||
.cmd = cpu_to_be16(0x58E0),
|
||||
.wait_us = 94500,
|
||||
.wait_max_us = 94700,
|
||||
.response_word_nb = 3,
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t icp10100_switch_mode_otp[] =
|
||||
{0xC5, 0x95, 0x00, 0x66, 0x9c};
|
||||
|
||||
DECLARE_CRC8_TABLE(icp10100_crc8_table);
|
||||
|
||||
static inline int icp10100_i2c_xfer(struct i2c_adapter *adap,
|
||||
struct i2c_msg *msgs, int num)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_transfer(adap, msgs, num);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret != num)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icp10100_send_cmd(struct icp10100_state *st,
|
||||
const struct icp10100_command *cmd,
|
||||
__be16 *buf, size_t buf_len)
|
||||
{
|
||||
size_t size = cmd->response_word_nb * ICP10100_RESPONSE_WORD_LENGTH;
|
||||
uint8_t data[16];
|
||||
uint8_t *ptr;
|
||||
uint8_t *buf_ptr = (uint8_t *)buf;
|
||||
struct i2c_msg msgs[2] = {
|
||||
{
|
||||
.addr = st->client->addr,
|
||||
.flags = 0,
|
||||
.len = 2,
|
||||
.buf = (uint8_t *)&cmd->cmd,
|
||||
}, {
|
||||
.addr = st->client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = size,
|
||||
.buf = data,
|
||||
},
|
||||
};
|
||||
uint8_t crc;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (size > sizeof(data))
|
||||
return -EINVAL;
|
||||
|
||||
if (cmd->response_word_nb > 0 &&
|
||||
(buf == NULL || buf_len < (cmd->response_word_nb * 2)))
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(&st->client->dev, "sending cmd %#x\n", be16_to_cpu(cmd->cmd));
|
||||
|
||||
if (cmd->response_word_nb > 0 && cmd->wait_us == 0) {
|
||||
/* direct command-response without waiting */
|
||||
ret = icp10100_i2c_xfer(st->client->adapter, msgs,
|
||||
ARRAY_SIZE(msgs));
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
/* transfer command write */
|
||||
ret = icp10100_i2c_xfer(st->client->adapter, &msgs[0], 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (cmd->wait_us > 0)
|
||||
usleep_range(cmd->wait_us, cmd->wait_max_us);
|
||||
/* transfer response read if needed */
|
||||
if (cmd->response_word_nb > 0) {
|
||||
ret = icp10100_i2c_xfer(st->client->adapter, &msgs[1], 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* process read words with crc checking */
|
||||
for (i = 0; i < cmd->response_word_nb; ++i) {
|
||||
ptr = &data[i * ICP10100_RESPONSE_WORD_LENGTH];
|
||||
crc = crc8(icp10100_crc8_table, ptr, ICP10100_CRC8_WORD_LENGTH,
|
||||
ICP10100_CRC8_INIT);
|
||||
if (crc != ptr[ICP10100_CRC8_WORD_LENGTH]) {
|
||||
dev_err(&st->client->dev, "crc error recv=%#x calc=%#x\n",
|
||||
ptr[ICP10100_CRC8_WORD_LENGTH], crc);
|
||||
return -EIO;
|
||||
}
|
||||
*buf_ptr++ = ptr[0];
|
||||
*buf_ptr++ = ptr[1];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icp10100_read_cal_otp(struct icp10100_state *st)
|
||||
{
|
||||
__be16 val;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
/* switch into OTP read mode */
|
||||
ret = i2c_master_send(st->client, icp10100_switch_mode_otp,
|
||||
ARRAY_SIZE(icp10100_switch_mode_otp));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != ARRAY_SIZE(icp10100_switch_mode_otp))
|
||||
return -EIO;
|
||||
|
||||
/* read 4 calibration values */
|
||||
for (i = 0; i < 4; ++i) {
|
||||
ret = icp10100_send_cmd(st, &icp10100_cmd_read_otp,
|
||||
&val, sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
st->cal[i] = be16_to_cpu(val);
|
||||
dev_dbg(&st->client->dev, "cal[%d] = %d\n", i, st->cal[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icp10100_init_chip(struct icp10100_state *st)
|
||||
{
|
||||
__be16 val;
|
||||
uint16_t id;
|
||||
int ret;
|
||||
|
||||
/* read and check id */
|
||||
ret = icp10100_send_cmd(st, &icp10100_cmd_read_id, &val, sizeof(val));
|
||||
if (ret)
|
||||
return ret;
|
||||
id = ICP10100_ID_REG_GET(be16_to_cpu(val));
|
||||
if (id != ICP10100_ID_REG) {
|
||||
dev_err(&st->client->dev, "invalid id %#x\n", id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* read calibration data from OTP */
|
||||
ret = icp10100_read_cal_otp(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* reset chip */
|
||||
return icp10100_send_cmd(st, &icp10100_cmd_soft_reset, NULL, 0);
|
||||
}
|
||||
|
||||
static int icp10100_get_measures(struct icp10100_state *st,
|
||||
uint32_t *pressure, uint16_t *temperature)
|
||||
{
|
||||
const struct icp10100_command *cmd;
|
||||
__be16 measures[3];
|
||||
int ret;
|
||||
|
||||
pm_runtime_get_sync(&st->client->dev);
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
cmd = &icp10100_cmd_measure[st->mode];
|
||||
ret = icp10100_send_cmd(st, cmd, measures, sizeof(measures));
|
||||
mutex_unlock(&st->lock);
|
||||
if (ret)
|
||||
goto error_measure;
|
||||
|
||||
*pressure = (be16_to_cpu(measures[0]) << 8) |
|
||||
(be16_to_cpu(measures[1]) >> 8);
|
||||
*temperature = be16_to_cpu(measures[2]);
|
||||
|
||||
pm_runtime_mark_last_busy(&st->client->dev);
|
||||
error_measure:
|
||||
pm_runtime_put_autosuspend(&st->client->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t icp10100_get_pressure(struct icp10100_state *st,
|
||||
uint32_t raw_pressure, uint16_t raw_temp)
|
||||
{
|
||||
static int32_t p_calib[] = {45000, 80000, 105000};
|
||||
static int32_t lut_lower = 3670016;
|
||||
static int32_t lut_upper = 12058624;
|
||||
static int32_t inv_quadr_factor = 16777216;
|
||||
static int32_t offset_factor = 2048;
|
||||
int64_t val1, val2;
|
||||
int32_t p_lut[3];
|
||||
int32_t t, t_square;
|
||||
int64_t a, b, c;
|
||||
uint32_t pressure_mPa;
|
||||
|
||||
dev_dbg(&st->client->dev, "raw: pressure = %u, temp = %u\n",
|
||||
raw_pressure, raw_temp);
|
||||
|
||||
/* compute p_lut values */
|
||||
t = (int32_t)raw_temp - 32768;
|
||||
t_square = t * t;
|
||||
val1 = (int64_t)st->cal[0] * (int64_t)t_square;
|
||||
p_lut[0] = lut_lower + (int32_t)div_s64(val1, inv_quadr_factor);
|
||||
val1 = (int64_t)st->cal[1] * (int64_t)t_square;
|
||||
p_lut[1] = offset_factor * st->cal[3] +
|
||||
(int32_t)div_s64(val1, inv_quadr_factor);
|
||||
val1 = (int64_t)st->cal[2] * (int64_t)t_square;
|
||||
p_lut[2] = lut_upper + (int32_t)div_s64(val1, inv_quadr_factor);
|
||||
dev_dbg(&st->client->dev, "p_lut = [%d, %d, %d]\n",
|
||||
p_lut[0], p_lut[1], p_lut[2]);
|
||||
|
||||
/* compute a, b, c factors */
|
||||
val1 = (int64_t)p_lut[0] * (int64_t)p_lut[1] *
|
||||
(int64_t)(p_calib[0] - p_calib[1]) +
|
||||
(int64_t)p_lut[1] * (int64_t)p_lut[2] *
|
||||
(int64_t)(p_calib[1] - p_calib[2]) +
|
||||
(int64_t)p_lut[2] * (int64_t)p_lut[0] *
|
||||
(int64_t)(p_calib[2] - p_calib[0]);
|
||||
val2 = (int64_t)p_lut[2] * (int64_t)(p_calib[0] - p_calib[1]) +
|
||||
(int64_t)p_lut[0] * (int64_t)(p_calib[1] - p_calib[2]) +
|
||||
(int64_t)p_lut[1] * (int64_t)(p_calib[2] - p_calib[0]);
|
||||
c = div64_s64(val1, val2);
|
||||
dev_dbg(&st->client->dev, "val1 = %lld, val2 = %lld, c = %lld\n",
|
||||
val1, val2, c);
|
||||
val1 = (int64_t)p_calib[0] * (int64_t)p_lut[0] -
|
||||
(int64_t)p_calib[1] * (int64_t)p_lut[1] -
|
||||
(int64_t)(p_calib[1] - p_calib[0]) * c;
|
||||
val2 = (int64_t)p_lut[0] - (int64_t)p_lut[1];
|
||||
a = div64_s64(val1, val2);
|
||||
dev_dbg(&st->client->dev, "val1 = %lld, val2 = %lld, a = %lld\n",
|
||||
val1, val2, a);
|
||||
b = ((int64_t)p_calib[0] - a) * ((int64_t)p_lut[0] + c);
|
||||
dev_dbg(&st->client->dev, "b = %lld\n", b);
|
||||
|
||||
/*
|
||||
* pressure_Pa = a + (b / (c + raw_pressure))
|
||||
* pressure_mPa = 1000 * pressure_Pa
|
||||
*/
|
||||
pressure_mPa = 1000LL * a + div64_s64(1000LL * b, c + raw_pressure);
|
||||
|
||||
return pressure_mPa;
|
||||
}
|
||||
|
||||
static int icp10100_read_raw_measures(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2)
|
||||
{
|
||||
struct icp10100_state *st = iio_priv(indio_dev);
|
||||
uint32_t raw_pressure;
|
||||
uint16_t raw_temp;
|
||||
uint32_t pressure_mPa;
|
||||
int ret;
|
||||
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = icp10100_get_measures(st, &raw_pressure, &raw_temp);
|
||||
if (ret)
|
||||
goto error_release;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_PRESSURE:
|
||||
pressure_mPa = icp10100_get_pressure(st, raw_pressure,
|
||||
raw_temp);
|
||||
/* mPa to kPa */
|
||||
*val = pressure_mPa / 1000000;
|
||||
*val2 = pressure_mPa % 1000000;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
*val = raw_temp;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
error_release:
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int icp10100_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct icp10100_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
case IIO_CHAN_INFO_PROCESSED:
|
||||
return icp10100_read_raw_measures(indio_dev, chan, val, val2);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
/* 1000 * 175°C / 65536 in m°C */
|
||||
*val = 2;
|
||||
*val2 = 670288;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
/* 1000 * -45°C in m°C */
|
||||
*val = -45000;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
mutex_lock(&st->lock);
|
||||
*val = 1 << st->mode;
|
||||
mutex_unlock(&st->lock);
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int icp10100_read_avail(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
const int **vals, int *type, int *length,
|
||||
long mask)
|
||||
{
|
||||
static int oversamplings[] = {1, 2, 4, 8};
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
*vals = oversamplings;
|
||||
*type = IIO_VAL_INT;
|
||||
*length = ARRAY_SIZE(oversamplings);
|
||||
return IIO_AVAIL_LIST;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int icp10100_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct icp10100_state *st = iio_priv(indio_dev);
|
||||
unsigned int mode;
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
/* oversampling is always positive and a power of 2 */
|
||||
if (val <= 0 || !is_power_of_2(val))
|
||||
return -EINVAL;
|
||||
mode = ilog2(val);
|
||||
if (mode >= ICP10100_MODE_NB)
|
||||
return -EINVAL;
|
||||
ret = iio_device_claim_direct_mode(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
mutex_lock(&st->lock);
|
||||
st->mode = mode;
|
||||
mutex_unlock(&st->lock);
|
||||
iio_device_release_direct_mode(indio_dev);
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int icp10100_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info icp10100_info = {
|
||||
.read_raw = icp10100_read_raw,
|
||||
.read_avail = icp10100_read_avail,
|
||||
.write_raw = icp10100_write_raw,
|
||||
.write_raw_get_fmt = icp10100_write_raw_get_fmt,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec icp10100_channels[] = {
|
||||
{
|
||||
.type = IIO_PRESSURE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
|
||||
.info_mask_shared_by_all =
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
.info_mask_shared_by_all_available =
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.info_mask_shared_by_all =
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
.info_mask_shared_by_all_available =
|
||||
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
|
||||
},
|
||||
};
|
||||
|
||||
static int icp10100_enable_regulator(struct icp10100_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(st->vdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
msleep(100);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void icp10100_disable_regulator_action(void *data)
|
||||
{
|
||||
struct icp10100_state *st = data;
|
||||
int ret;
|
||||
|
||||
ret = regulator_disable(st->vdd);
|
||||
if (ret)
|
||||
dev_err(&st->client->dev, "error %d disabling vdd\n", ret);
|
||||
}
|
||||
|
||||
static void icp10100_pm_disable(void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
|
||||
pm_runtime_put_sync_suspend(dev);
|
||||
pm_runtime_disable(dev);
|
||||
}
|
||||
|
||||
static int icp10100_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct icp10100_state *st;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev, "plain i2c transactions not supported\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = client->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = icp10100_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(icp10100_channels);
|
||||
indio_dev->info = &icp10100_info;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
mutex_init(&st->lock);
|
||||
st->client = client;
|
||||
st->mode = ICP10100_MODE_N;
|
||||
|
||||
st->vdd = devm_regulator_get(&client->dev, "vdd");
|
||||
if (IS_ERR(st->vdd))
|
||||
return PTR_ERR(st->vdd);
|
||||
|
||||
ret = icp10100_enable_regulator(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(&client->dev,
|
||||
icp10100_disable_regulator_action, st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* has to be done before the first i2c communication */
|
||||
crc8_populate_msb(icp10100_crc8_table, ICP10100_CRC8_POLYNOMIAL);
|
||||
|
||||
ret = icp10100_init_chip(st);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "init chip error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* enable runtime pm with autosuspend delay of 2s */
|
||||
pm_runtime_get_noresume(&client->dev);
|
||||
pm_runtime_set_active(&client->dev);
|
||||
pm_runtime_enable(&client->dev);
|
||||
pm_runtime_set_autosuspend_delay(&client->dev, 2000);
|
||||
pm_runtime_use_autosuspend(&client->dev);
|
||||
pm_runtime_put(&client->dev);
|
||||
ret = devm_add_action_or_reset(&client->dev, icp10100_pm_disable,
|
||||
&client->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&client->dev, indio_dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused icp10100_suspend(struct device *dev)
|
||||
{
|
||||
struct icp10100_state *st = iio_priv(dev_get_drvdata(dev));
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
ret = regulator_disable(st->vdd);
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused icp10100_resume(struct device *dev)
|
||||
{
|
||||
struct icp10100_state *st = iio_priv(dev_get_drvdata(dev));
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
ret = icp10100_enable_regulator(st);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
/* reset chip */
|
||||
ret = icp10100_send_cmd(st, &icp10100_cmd_soft_reset, NULL, 0);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static UNIVERSAL_DEV_PM_OPS(icp10100_pm, icp10100_suspend, icp10100_resume,
|
||||
NULL);
|
||||
|
||||
static const struct of_device_id icp10100_of_match[] = {
|
||||
{
|
||||
.compatible = "invensense,icp10100",
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, icp10100_of_match);
|
||||
|
||||
static const struct i2c_device_id icp10100_id[] = {
|
||||
{ "icp10100", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, icp10100_id);
|
||||
|
||||
static struct i2c_driver icp10100_driver = {
|
||||
.driver = {
|
||||
.name = "icp10100",
|
||||
.pm = &icp10100_pm,
|
||||
.of_match_table = of_match_ptr(icp10100_of_match),
|
||||
},
|
||||
.probe = icp10100_probe,
|
||||
.id_table = icp10100_id,
|
||||
};
|
||||
module_i2c_driver(icp10100_driver);
|
||||
|
||||
MODULE_AUTHOR("InvenSense, Inc.");
|
||||
MODULE_DESCRIPTION("InvenSense icp10100 driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -45,6 +45,7 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
@ -56,6 +57,7 @@ struct srf04_data {
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpiod_trig;
|
||||
struct gpio_desc *gpiod_echo;
|
||||
struct gpio_desc *gpiod_power;
|
||||
struct mutex lock;
|
||||
int irqnr;
|
||||
ktime_t ts_rising;
|
||||
@ -63,6 +65,7 @@ struct srf04_data {
|
||||
struct completion rising;
|
||||
struct completion falling;
|
||||
const struct srf04_cfg *cfg;
|
||||
int startup_time_ms;
|
||||
};
|
||||
|
||||
static const struct srf04_cfg srf04_cfg = {
|
||||
@ -97,6 +100,9 @@ static int srf04_read(struct srf04_data *data)
|
||||
u64 dt_ns;
|
||||
u32 time_ns, distance_mm;
|
||||
|
||||
if (data->gpiod_power)
|
||||
pm_runtime_get_sync(data->dev);
|
||||
|
||||
/*
|
||||
* just one read-echo-cycle can take place at a time
|
||||
* ==> lock against concurrent reading calls
|
||||
@ -110,6 +116,11 @@ static int srf04_read(struct srf04_data *data)
|
||||
udelay(data->cfg->trigger_pulse_us);
|
||||
gpiod_set_value(data->gpiod_trig, 0);
|
||||
|
||||
if (data->gpiod_power) {
|
||||
pm_runtime_mark_last_busy(data->dev);
|
||||
pm_runtime_put_autosuspend(data->dev);
|
||||
}
|
||||
|
||||
/* it should not take more than 20 ms until echo is rising */
|
||||
ret = wait_for_completion_killable_timeout(&data->rising, HZ/50);
|
||||
if (ret < 0) {
|
||||
@ -268,6 +279,22 @@ static int srf04_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(data->gpiod_echo);
|
||||
}
|
||||
|
||||
data->gpiod_power = devm_gpiod_get_optional(dev, "power",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(data->gpiod_power)) {
|
||||
dev_err(dev, "failed to get power-gpios: err=%ld\n",
|
||||
PTR_ERR(data->gpiod_power));
|
||||
return PTR_ERR(data->gpiod_power);
|
||||
}
|
||||
if (data->gpiod_power) {
|
||||
|
||||
if (of_property_read_u32(dev->of_node, "startup-time-ms",
|
||||
&data->startup_time_ms))
|
||||
data->startup_time_ms = 100;
|
||||
dev_dbg(dev, "using power gpio: startup-time-ms=%d\n",
|
||||
data->startup_time_ms);
|
||||
}
|
||||
|
||||
if (gpiod_cansleep(data->gpiod_echo)) {
|
||||
dev_err(data->dev, "cansleep-GPIOs not supported\n");
|
||||
return -ENODEV;
|
||||
@ -296,14 +323,81 @@ static int srf04_probe(struct platform_device *pdev)
|
||||
indio_dev->channels = srf04_chan_spec;
|
||||
indio_dev->num_channels = ARRAY_SIZE(srf04_chan_spec);
|
||||
|
||||
return devm_iio_device_register(dev, indio_dev);
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(data->dev, "iio_device_register: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (data->gpiod_power) {
|
||||
pm_runtime_set_autosuspend_delay(data->dev, 1000);
|
||||
pm_runtime_use_autosuspend(data->dev);
|
||||
|
||||
ret = pm_runtime_set_active(data->dev);
|
||||
if (ret) {
|
||||
dev_err(data->dev, "pm_runtime_set_active: %d\n", ret);
|
||||
iio_device_unregister(indio_dev);
|
||||
}
|
||||
|
||||
pm_runtime_enable(data->dev);
|
||||
pm_runtime_idle(data->dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int srf04_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct srf04_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (data->gpiod_power) {
|
||||
pm_runtime_disable(data->dev);
|
||||
pm_runtime_set_suspended(data->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused srf04_pm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = container_of(dev,
|
||||
struct platform_device, dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct srf04_data *data = iio_priv(indio_dev);
|
||||
|
||||
gpiod_set_value(data->gpiod_power, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused srf04_pm_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = container_of(dev,
|
||||
struct platform_device, dev);
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct srf04_data *data = iio_priv(indio_dev);
|
||||
|
||||
gpiod_set_value(data->gpiod_power, 1);
|
||||
msleep(data->startup_time_ms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops srf04_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(srf04_pm_runtime_suspend,
|
||||
srf04_pm_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver srf04_driver = {
|
||||
.probe = srf04_probe,
|
||||
.remove = srf04_remove,
|
||||
.driver = {
|
||||
.name = "srf04-gpio",
|
||||
.of_match_table = of_srf04_match,
|
||||
.pm = &srf04_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -75,14 +75,27 @@ static const void *stm32h7_valids_table[][MAX_VALIDS] = {
|
||||
{ }, /* timer 17 */
|
||||
};
|
||||
|
||||
struct stm32_timer_trigger_regs {
|
||||
u32 cr1;
|
||||
u32 cr2;
|
||||
u32 psc;
|
||||
u32 arr;
|
||||
u32 cnt;
|
||||
u32 smcr;
|
||||
};
|
||||
|
||||
struct stm32_timer_trigger {
|
||||
struct device *dev;
|
||||
struct regmap *regmap;
|
||||
struct clk *clk;
|
||||
bool enabled;
|
||||
u32 max_arr;
|
||||
const void *triggers;
|
||||
const void *valids;
|
||||
bool has_trgo2;
|
||||
struct mutex lock; /* concurrent sysfs configuration */
|
||||
struct list_head tr_list;
|
||||
struct stm32_timer_trigger_regs bak;
|
||||
};
|
||||
|
||||
struct stm32_timer_trigger_cfg {
|
||||
@ -106,7 +119,7 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv,
|
||||
{
|
||||
unsigned long long prd, div;
|
||||
int prescaler = 0;
|
||||
u32 ccer, cr1;
|
||||
u32 ccer;
|
||||
|
||||
/* Period and prescaler values depends of clock rate */
|
||||
div = (unsigned long long)clk_get_rate(priv->clk);
|
||||
@ -136,9 +149,11 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv,
|
||||
if (ccer & TIM_CCER_CCXE)
|
||||
return -EBUSY;
|
||||
|
||||
regmap_read(priv->regmap, TIM_CR1, &cr1);
|
||||
if (!(cr1 & TIM_CR1_CEN))
|
||||
mutex_lock(&priv->lock);
|
||||
if (!priv->enabled) {
|
||||
priv->enabled = true;
|
||||
clk_enable(priv->clk);
|
||||
}
|
||||
|
||||
regmap_write(priv->regmap, TIM_PSC, prescaler);
|
||||
regmap_write(priv->regmap, TIM_ARR, prd - 1);
|
||||
@ -157,22 +172,20 @@ static int stm32_timer_start(struct stm32_timer_trigger *priv,
|
||||
|
||||
/* Enable controller */
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, TIM_CR1_CEN);
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stm32_timer_stop(struct stm32_timer_trigger *priv)
|
||||
{
|
||||
u32 ccer, cr1;
|
||||
u32 ccer;
|
||||
|
||||
regmap_read(priv->regmap, TIM_CCER, &ccer);
|
||||
if (ccer & TIM_CCER_CCXE)
|
||||
return;
|
||||
|
||||
regmap_read(priv->regmap, TIM_CR1, &cr1);
|
||||
if (cr1 & TIM_CR1_CEN)
|
||||
clk_disable(priv->clk);
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
/* Stop timer */
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, 0);
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
|
||||
@ -181,6 +194,12 @@ static void stm32_timer_stop(struct stm32_timer_trigger *priv)
|
||||
|
||||
/* Make sure that registers are updated */
|
||||
regmap_update_bits(priv->regmap, TIM_EGR, TIM_EGR_UG, TIM_EGR_UG);
|
||||
|
||||
if (priv->enabled) {
|
||||
priv->enabled = false;
|
||||
clk_disable(priv->clk);
|
||||
}
|
||||
mutex_unlock(&priv->lock);
|
||||
}
|
||||
|
||||
static ssize_t stm32_tt_store_frequency(struct device *dev,
|
||||
@ -295,8 +314,15 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev,
|
||||
for (i = 0; i <= master_mode_max; i++) {
|
||||
if (!strncmp(master_mode_table[i], buf,
|
||||
strlen(master_mode_table[i]))) {
|
||||
mutex_lock(&priv->lock);
|
||||
if (!priv->enabled) {
|
||||
/* Clock should be enabled first */
|
||||
priv->enabled = true;
|
||||
clk_enable(priv->clk);
|
||||
}
|
||||
regmap_update_bits(priv->regmap, TIM_CR2, mask,
|
||||
i << shift);
|
||||
mutex_unlock(&priv->lock);
|
||||
return len;
|
||||
}
|
||||
}
|
||||
@ -354,11 +380,21 @@ static const struct attribute_group *stm32_trigger_attr_groups[] = {
|
||||
static const struct iio_trigger_ops timer_trigger_ops = {
|
||||
};
|
||||
|
||||
static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv)
|
||||
static void stm32_unregister_iio_triggers(struct stm32_timer_trigger *priv)
|
||||
{
|
||||
struct iio_trigger *tr;
|
||||
|
||||
list_for_each_entry(tr, &priv->tr_list, alloc_list)
|
||||
iio_trigger_unregister(tr);
|
||||
}
|
||||
|
||||
static int stm32_register_iio_triggers(struct stm32_timer_trigger *priv)
|
||||
{
|
||||
int ret;
|
||||
const char * const *cur = priv->triggers;
|
||||
|
||||
INIT_LIST_HEAD(&priv->tr_list);
|
||||
|
||||
while (cur && *cur) {
|
||||
struct iio_trigger *trig;
|
||||
bool cur_is_trgo = stm32_timer_is_trgo_name(*cur);
|
||||
@ -385,9 +421,13 @@ static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv)
|
||||
|
||||
iio_trigger_set_drvdata(trig, priv);
|
||||
|
||||
ret = devm_iio_trigger_register(priv->dev, trig);
|
||||
if (ret)
|
||||
ret = iio_trigger_register(trig);
|
||||
if (ret) {
|
||||
stm32_unregister_iio_triggers(priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_add_tail(&trig->alloc_list, &priv->tr_list);
|
||||
cur++;
|
||||
}
|
||||
|
||||
@ -434,7 +474,6 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
||||
u32 dat;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
@ -445,19 +484,23 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev,
|
||||
return -EINVAL;
|
||||
|
||||
case IIO_CHAN_INFO_ENABLE:
|
||||
mutex_lock(&priv->lock);
|
||||
if (val) {
|
||||
regmap_read(priv->regmap, TIM_CR1, &dat);
|
||||
if (!(dat & TIM_CR1_CEN))
|
||||
if (!priv->enabled) {
|
||||
priv->enabled = true;
|
||||
clk_enable(priv->clk);
|
||||
}
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN,
|
||||
TIM_CR1_CEN);
|
||||
} else {
|
||||
regmap_read(priv->regmap, TIM_CR1, &dat);
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN,
|
||||
0);
|
||||
if (dat & TIM_CR1_CEN)
|
||||
if (priv->enabled) {
|
||||
priv->enabled = false;
|
||||
clk_disable(priv->clk);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&priv->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -553,7 +596,6 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev,
|
||||
{
|
||||
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
|
||||
int sms = stm32_enable_mode2sms(mode);
|
||||
u32 val;
|
||||
|
||||
if (sms < 0)
|
||||
return sms;
|
||||
@ -561,11 +603,12 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev,
|
||||
* Triggered mode sets CEN bit automatically by hardware. So, first
|
||||
* enable counter clock, so it can use it. Keeps it in sync with CEN.
|
||||
*/
|
||||
if (sms == 6) {
|
||||
regmap_read(priv->regmap, TIM_CR1, &val);
|
||||
if (!(val & TIM_CR1_CEN))
|
||||
clk_enable(priv->clk);
|
||||
mutex_lock(&priv->lock);
|
||||
if (sms == 6 && !priv->enabled) {
|
||||
clk_enable(priv->clk);
|
||||
priv->enabled = true;
|
||||
}
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, sms);
|
||||
|
||||
@ -749,8 +792,9 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev)
|
||||
priv->triggers = triggers_table[index];
|
||||
priv->valids = cfg->valids_table[index];
|
||||
stm32_timer_detect_trgo2(priv);
|
||||
mutex_init(&priv->lock);
|
||||
|
||||
ret = stm32_setup_iio_triggers(priv);
|
||||
ret = stm32_register_iio_triggers(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -759,6 +803,77 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stm32_timer_trigger_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct stm32_timer_trigger *priv = platform_get_drvdata(pdev);
|
||||
u32 val;
|
||||
|
||||
/* Unregister triggers before everything can be safely turned off */
|
||||
stm32_unregister_iio_triggers(priv);
|
||||
|
||||
/* Check if nobody else use the timer, then disable it */
|
||||
regmap_read(priv->regmap, TIM_CCER, &val);
|
||||
if (!(val & TIM_CCER_CCXE))
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
|
||||
|
||||
if (priv->enabled)
|
||||
clk_disable(priv->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_timer_trigger_suspend(struct device *dev)
|
||||
{
|
||||
struct stm32_timer_trigger *priv = dev_get_drvdata(dev);
|
||||
|
||||
/* Only take care of enabled timer: don't disturb other MFD child */
|
||||
if (priv->enabled) {
|
||||
/* Backup registers that may get lost in low power mode */
|
||||
regmap_read(priv->regmap, TIM_CR1, &priv->bak.cr1);
|
||||
regmap_read(priv->regmap, TIM_CR2, &priv->bak.cr2);
|
||||
regmap_read(priv->regmap, TIM_PSC, &priv->bak.psc);
|
||||
regmap_read(priv->regmap, TIM_ARR, &priv->bak.arr);
|
||||
regmap_read(priv->regmap, TIM_CNT, &priv->bak.cnt);
|
||||
regmap_read(priv->regmap, TIM_SMCR, &priv->bak.smcr);
|
||||
|
||||
/* Disable the timer */
|
||||
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, 0);
|
||||
clk_disable(priv->clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused stm32_timer_trigger_resume(struct device *dev)
|
||||
{
|
||||
struct stm32_timer_trigger *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (priv->enabled) {
|
||||
ret = clk_enable(priv->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* restore master/slave modes */
|
||||
regmap_write(priv->regmap, TIM_SMCR, priv->bak.smcr);
|
||||
regmap_write(priv->regmap, TIM_CR2, priv->bak.cr2);
|
||||
|
||||
/* restore sampling_frequency (trgo / trgo2 triggers) */
|
||||
regmap_write(priv->regmap, TIM_PSC, priv->bak.psc);
|
||||
regmap_write(priv->regmap, TIM_ARR, priv->bak.arr);
|
||||
regmap_write(priv->regmap, TIM_CNT, priv->bak.cnt);
|
||||
|
||||
/* Also re-enables the timer */
|
||||
regmap_write(priv->regmap, TIM_CR1, priv->bak.cr1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(stm32_timer_trigger_pm_ops,
|
||||
stm32_timer_trigger_suspend,
|
||||
stm32_timer_trigger_resume);
|
||||
|
||||
static const struct stm32_timer_trigger_cfg stm32_timer_trg_cfg = {
|
||||
.valids_table = valids_table,
|
||||
.num_valids_table = ARRAY_SIZE(valids_table),
|
||||
@ -783,9 +898,11 @@ MODULE_DEVICE_TABLE(of, stm32_trig_of_match);
|
||||
|
||||
static struct platform_driver stm32_timer_trigger_driver = {
|
||||
.probe = stm32_timer_trigger_probe,
|
||||
.remove = stm32_timer_trigger_remove,
|
||||
.driver = {
|
||||
.name = "stm32-timer-trigger",
|
||||
.of_match_table = stm32_trig_of_match,
|
||||
.pm = &stm32_timer_trigger_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(stm32_timer_trigger_driver);
|
||||
|
@ -1,20 +0,0 @@
|
||||
What: /sys/.../iio:deviceX/ac_excitation_en
|
||||
KernelVersion: 3.1.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This attribute, if available, is used to enable the AC
|
||||
excitation mode found on some converters. In ac excitation mode,
|
||||
the polarity of the excitation voltage is reversed on
|
||||
alternate cycles, to eliminate DC errors.
|
||||
|
||||
What: /sys/.../iio:deviceX/bridge_switch_en
|
||||
KernelVersion: 3.1.0
|
||||
Contact: linux-iio@vger.kernel.org
|
||||
Description:
|
||||
This attribute, if available, is used to close or open the
|
||||
bridge power down switch found on some converters.
|
||||
In bridge applications, such as strain gauges and load cells,
|
||||
the bridge itself consumes the majority of the current in the
|
||||
system. To minimize the current consumption of the system,
|
||||
the bridge can be disconnected (when it is not being used
|
||||
using the bridge_switch_en attribute.
|
@ -1,10 +1,4 @@
|
||||
2018-04-15
|
||||
|
||||
All affected drivers:
|
||||
Convert all uses of the old GPIO API from <linux/gpio.h> to the
|
||||
GPIO descriptor API in <linux/gpio/consumer.h> and look up GPIO
|
||||
lines from device tree, ACPI or board files, board files should
|
||||
use <linux/gpio/machine.h>.
|
||||
2020-02-25
|
||||
|
||||
|
||||
ADI Drivers:
|
||||
|
@ -250,6 +250,7 @@ static const struct adis_data adis16203_data = {
|
||||
.diag_stat_reg = ADIS16203_DIAG_STAT,
|
||||
|
||||
.self_test_mask = ADIS16203_MSC_CTRL_SELF_TEST_EN,
|
||||
.self_test_reg = ADIS16203_MSC_CTRL,
|
||||
.self_test_no_autoclear = true,
|
||||
.timeouts = &adis16203_timeouts,
|
||||
|
||||
|
@ -373,6 +373,7 @@ static const struct adis_data adis16240_data = {
|
||||
.diag_stat_reg = ADIS16240_DIAG_STAT,
|
||||
|
||||
.self_test_mask = ADIS16240_MSC_CTRL_SELF_TEST_EN,
|
||||
.self_test_reg = ADIS16240_MSC_CTRL,
|
||||
.self_test_no_autoclear = true,
|
||||
.timeouts = &adis16240_timeouts,
|
||||
|
||||
|
@ -15,18 +15,6 @@ config AD7816
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7816.
|
||||
|
||||
config AD7192
|
||||
tristate "Analog Devices AD7190 AD7192 AD7193 AD7195 ADC driver"
|
||||
depends on SPI
|
||||
select AD_SIGMA_DELTA
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7190,
|
||||
AD7192, AD7193 or AD7195 SPI analog to digital converters (ADC).
|
||||
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 ad7192.
|
||||
|
||||
config AD7280
|
||||
tristate "Analog Devices AD7280A Lithium Ion Battery Monitoring System"
|
||||
depends on SPI
|
||||
|
@ -4,5 +4,4 @@
|
||||
#
|
||||
|
||||
obj-$(CONFIG_AD7816) += ad7816.o
|
||||
obj-$(CONFIG_AD7192) += ad7192.o
|
||||
obj-$(CONFIG_AD7280) += ad7280a.o
|
||||
|
@ -568,6 +568,8 @@ struct iio_dev {
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
struct dentry *debugfs_dentry;
|
||||
unsigned cached_reg_addr;
|
||||
char read_buf[20];
|
||||
unsigned int read_buf_len;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -41,9 +41,16 @@ struct adis_timeout {
|
||||
* @glob_cmd_reg: Register address of the GLOB_CMD register
|
||||
* @msc_ctrl_reg: Register address of the MSC_CTRL register
|
||||
* @diag_stat_reg: Register address of the DIAG_STAT register
|
||||
* @prod_id_reg: Register address of the PROD_ID register
|
||||
* @prod_id: Product ID code that should be expected when reading @prod_id_reg
|
||||
* @self_test_mask: Bitmask of supported self-test operations
|
||||
* @self_test_reg: Register address to request self test command
|
||||
* @self_test_no_autoclear: True if device's self-test needs clear of ctrl reg
|
||||
* @status_error_msgs: Array of error messgaes
|
||||
* @status_error_mask:
|
||||
* @status_error_mask: Bitmask of errors supported by the device
|
||||
* @timeouts: Chip specific delays
|
||||
* @enable_irq: Hook for ADIS devices that have a special IRQ enable/disable
|
||||
* @has_paging: True if ADIS device has paged registers
|
||||
*/
|
||||
struct adis_data {
|
||||
unsigned int read_delay;
|
||||
@ -53,8 +60,12 @@ struct adis_data {
|
||||
unsigned int glob_cmd_reg;
|
||||
unsigned int msc_ctrl_reg;
|
||||
unsigned int diag_stat_reg;
|
||||
unsigned int prod_id_reg;
|
||||
|
||||
unsigned int prod_id;
|
||||
|
||||
unsigned int self_test_mask;
|
||||
unsigned int self_test_reg;
|
||||
bool self_test_no_autoclear;
|
||||
const struct adis_timeout *timeouts;
|
||||
|
||||
@ -66,6 +77,20 @@ struct adis_data {
|
||||
bool has_paging;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adis - ADIS device instance data
|
||||
* @spi: Reference to SPI device which owns this ADIS IIO device
|
||||
* @trig: IIO trigger object data
|
||||
* @data: ADIS chip variant specific data
|
||||
* @burst: ADIS burst transfer information
|
||||
* @state_lock: Lock used by the device to protect state
|
||||
* @msg: SPI message object
|
||||
* @xfer: SPI transfer objects to be used for a @msg
|
||||
* @current_page: Some ADIS devices have registers, this selects current page
|
||||
* @buffer: Data buffer for information read from the device
|
||||
* @tx: DMA safe TX buffer for SPI transfers
|
||||
* @rx: DMA safe RX buffer for SPI transfers
|
||||
*/
|
||||
struct adis {
|
||||
struct spi_device *spi;
|
||||
struct iio_trigger *trig;
|
||||
@ -73,6 +98,17 @@ struct adis {
|
||||
const struct adis_data *data;
|
||||
struct adis_burst *burst;
|
||||
|
||||
/**
|
||||
* The state_lock is meant to be used during operations that require
|
||||
* a sequence of SPI R/W in order to protect the SPI transfer
|
||||
* information (fields 'xfer', 'msg' & 'current_page') between
|
||||
* potential concurrent accesses.
|
||||
* This lock is used by all "adis_{functions}" that have to read/write
|
||||
* registers. These functions also have unlocked variants
|
||||
* (see "__adis_{functions}"), which don't hold this lock.
|
||||
* This allows users of the ADIS library to group SPI R/W into
|
||||
* the drivers, but they also must manage this lock themselves.
|
||||
*/
|
||||
struct mutex state_lock;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer *xfer;
|
||||
@ -297,6 +333,7 @@ static inline int adis_read_reg_32(struct adis *adis, unsigned int reg,
|
||||
|
||||
int adis_enable_irq(struct adis *adis, bool enable);
|
||||
int __adis_check_status(struct adis *adis);
|
||||
int __adis_initial_startup(struct adis *adis);
|
||||
|
||||
static inline int adis_check_status(struct adis *adis)
|
||||
{
|
||||
@ -309,7 +346,17 @@ static inline int adis_check_status(struct adis *adis)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adis_initial_startup(struct adis *adis);
|
||||
/* locked version of __adis_initial_startup() */
|
||||
static inline int adis_initial_startup(struct adis *adis)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&adis->state_lock);
|
||||
ret = __adis_initial_startup(adis);
|
||||
mutex_unlock(&adis->state_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int adis_single_conversion(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int error_mask,
|
||||
|
Loading…
Reference in New Issue
Block a user